-
Notifications
You must be signed in to change notification settings - Fork 4.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(ElementType): new wrapper for all elements
- Loading branch information
1 parent
6ef5af5
commit 5633ae5
Showing
6 changed files
with
153 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import PropTypes from 'prop-types' | ||
import React, { Component } from 'react' | ||
|
||
import Ref from '../addons/Ref' | ||
import computeElementType from './computeElementType' | ||
import * as customPropTypes from './customPropTypes' | ||
|
||
export default class ElementType extends Component { | ||
static propTypes = { | ||
/** An element type to render as (string or function). */ | ||
as: customPropTypes.as, | ||
|
||
/** A default element type to render as (string or function). */ | ||
defaultType: customPropTypes.as, | ||
|
||
/** A function that returns a default element type. */ | ||
computeType: PropTypes.func, | ||
|
||
/** | ||
* Called when component did mount, returns an inner DOM node. | ||
* | ||
* @param {HTMLElement} node - Referred node. | ||
*/ | ||
innerRef: PropTypes.func, | ||
} | ||
|
||
render() { | ||
const { as, defaultType, computeType, innerRef, ...rest } = this.props | ||
const Element = computeElementType({ as, defaultType, ...rest}, computeType) | ||
|
||
// Heads up! We should handle common situations to gain performance. | ||
// We don't need to <Ref> when Element is string or we deal with our component. | ||
if(typeof Element === 'string') return <Element {...rest} ref={innerRef} /> | ||
if(Element._meta) return <Element {...rest} innerRef={innerRef} /> | ||
|
||
return ( | ||
<Ref innerRef={innerRef}> | ||
<Element {...rest} /> | ||
</Ref> | ||
) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
/** | ||
* Returns a createElement() type based on the passed props. | ||
* Useful for calculating what type a component should render as. | ||
* | ||
* @param {object} props A ReactElement props object | ||
* @param {function} [computeType] A function that returns a default element type. | ||
* @returns {string|function} A ReactElement type | ||
*/ | ||
const computeElementType = (props, computeType) => { | ||
const { as, defaultType, href } = props | ||
|
||
// ---------------------------------------- | ||
// user defined "as" element type | ||
|
||
if (as && as !== defaultType) return props.as | ||
|
||
// ---------------------------------------- | ||
// computed default element type | ||
|
||
if (computeType) { | ||
const computedDefault = computeType() | ||
if (computedDefault) return computedDefault | ||
} | ||
|
||
// ---------------------------------------- | ||
// infer anchor links | ||
|
||
if (href) return 'a' | ||
|
||
// ---------------------------------------- | ||
// use defaultProp or 'div' | ||
|
||
return defaultType || 'div' | ||
} | ||
|
||
export default computeElementType |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import React from 'react' | ||
|
||
import Ref from 'src/addons/Ref' | ||
import Container from 'src/elements/Container' | ||
import { ElementType } from 'src/lib' | ||
import { sandbox } from 'test/utils' | ||
|
||
describe('ElementType', () => { | ||
describe('children', () => { | ||
it('adds ref to a string element', () => { | ||
const innerRef = sandbox.spy() | ||
const wrapper = mount(<ElementType as='a' innerRef={innerRef} />) | ||
|
||
wrapper.childAt(0).type().should.equal('a') | ||
innerRef.should.have.been.calledOnce('a') | ||
innerRef.should.have.been.calledWithMatch({ tagName: 'A' }) | ||
}) | ||
|
||
it('adds innerRef to our element', () => { | ||
const innerRef = () => {} | ||
const wrapper = mount(<ElementType as={Container} innerRef={innerRef} />) | ||
const child = wrapper.childAt(0) | ||
|
||
child.type().should.equal(Container) | ||
child.should.have.prop('innerRef', innerRef) | ||
}) | ||
|
||
it('wraps with Ref a third-part element', () => { | ||
const Element = () => <div /> | ||
const innerRef = () => {} | ||
const wrapper = mount(<ElementType as={Element} innerRef={innerRef} />) | ||
const child = wrapper.childAt(0) | ||
|
||
child.type().should.equal(Ref) | ||
child.should.have.prop('innerRef', innerRef) | ||
child.childAt(0).type().should.equal(Element) | ||
}) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import faker from 'faker' | ||
import computeElementType from 'src/lib/computeElementType' | ||
|
||
describe('computeElementType', () => { | ||
it('returns user defined "as" element type', () => { | ||
computeElementType({ as: 'button' }) | ||
.should.equal('button') | ||
}) | ||
|
||
it('returns computed default element type', () => { | ||
computeElementType({}, () => 'button') | ||
.should.equal('button') | ||
}) | ||
|
||
it('returns default element type when compute failed', () => { | ||
computeElementType({}, () => false) | ||
.should.equal('div') | ||
}) | ||
|
||
it('returns "a" when has a "href" prop', () => { | ||
computeElementType({ href: faker.internet.url() }) | ||
.should.equal('a') | ||
}) | ||
|
||
it('returns "as" from defaultProps', () => { | ||
computeElementType({ defaultType: 'button' }) | ||
.should.equal('button') | ||
}) | ||
|
||
it('returns default element type', () => { | ||
computeElementType({}) | ||
.should.equal('div') | ||
}) | ||
}) |