Refactor DOM element helper

This commit is contained in:
Nicolas Gallagher
2016-08-17 15:43:53 -07:00
parent acc7032d60
commit fdf4a88251
8 changed files with 99 additions and 103 deletions
+5 -6
View File
@@ -1,6 +1,7 @@
/* global window */
import applyNativeMethods from '../../modules/applyNativeMethods'
import createReactDOMComponent from '../../modules/createReactDOMComponent'
import BaseComponentPropTypes from '../../propTypes/BaseComponentPropTypes'
import createDOMElement from '../../modules/createDOMElement'
import ImageResizeMode from './ImageResizeMode'
import ImageStylePropTypes from './ImageStylePropTypes'
import resolveAssetSource from './resolveAssetSource'
@@ -26,8 +27,7 @@ class Image extends Component {
static displayName = 'Image'
static propTypes = {
accessibilityLabel: createReactDOMComponent.propTypes.accessibilityLabel,
accessible: createReactDOMComponent.propTypes.accessible,
...BaseComponentPropTypes,
children: PropTypes.any,
defaultSource: ImageSourcePropType,
onError: PropTypes.func,
@@ -37,8 +37,7 @@ class Image extends Component {
onLoadStart: PropTypes.func,
resizeMode: PropTypes.oneOf(['center', 'contain', 'cover', 'none', 'repeat', 'stretch']),
source: ImageSourcePropType,
style: StyleSheetPropType(ImageStylePropTypes),
testID: createReactDOMComponent.propTypes.testID
style: StyleSheetPropType(ImageStylePropTypes)
};
static defaultProps = {
@@ -121,7 +120,7 @@ class Image extends Component {
]}
testID={testID}
>
{createReactDOMComponent({ component: 'img', src: displayImage, style: styles.img })}
{createDOMElement('img', { src: displayImage, style: styles.img })}
{children ? (
<View children={children} pointerEvents='box-none' style={styles.children} />
) : null}
+5 -7
View File
@@ -1,6 +1,7 @@
import applyLayout from '../../modules/applyLayout'
import applyNativeMethods from '../../modules/applyNativeMethods'
import createReactDOMComponent from '../../modules/createReactDOMComponent'
import BaseComponentPropTypes from '../../propTypes/BaseComponentPropTypes'
import createDOMElement from '../../modules/createDOMElement'
import { Component, PropTypes } from 'react'
import StyleSheet from '../../apis/StyleSheet'
import StyleSheetPropType from '../../propTypes/StyleSheetPropType'
@@ -10,16 +11,14 @@ class Text extends Component {
static displayName = 'Text'
static propTypes = {
accessibilityLabel: createReactDOMComponent.propTypes.accessibilityLabel,
...BaseComponentPropTypes,
accessibilityRole: PropTypes.oneOf([ 'heading', 'link' ]),
accessible: createReactDOMComponent.propTypes.accessible,
children: PropTypes.any,
numberOfLines: PropTypes.number,
onLayout: PropTypes.func,
onPress: PropTypes.func,
selectable: PropTypes.bool,
style: StyleSheetPropType(TextStylePropTypes),
testID: createReactDOMComponent.propTypes.testID
style: StyleSheetPropType(TextStylePropTypes)
};
static defaultProps = {
@@ -37,9 +36,8 @@ class Text extends Component {
...other
} = this.props
return createReactDOMComponent({
return createDOMElement('span', {
...other,
component: 'span',
onClick: this._onPress,
style: [
styles.initial,
+4 -4
View File
@@ -1,5 +1,5 @@
import applyNativeMethods from '../../modules/applyNativeMethods'
import createReactDOMComponent from '../../modules/createReactDOMComponent'
import createDOMElement from '../../modules/createDOMElement'
import omit from 'lodash/omit'
import pick from 'lodash/pick'
import React, { Component, PropTypes } from 'react'
@@ -135,23 +135,23 @@ class TextInput extends Component {
onFocus: this._handleFocus,
onSelect: onSelectionChange && this._handleSelectionChange,
readOnly: !editable,
ref: 'input',
style: [ styles.input, textStyles, { outline: style.outline } ],
value
}
const propsMultiline = {
...propsCommon,
component: TextareaAutosize,
maxRows: maxNumberOfLines || numberOfLines,
minRows: numberOfLines
}
const propsSingleline = {
...propsCommon,
component: 'input',
type
}
const component = multiline ? TextareaAutosize : 'input'
const props = multiline ? propsMultiline : propsSingleline
const optionalPlaceholder = placeholder && this.state.showPlaceholder && (
@@ -176,7 +176,7 @@ class TextInput extends Component {
testID={testID}
>
<View style={styles.wrapper}>
{createReactDOMComponent({ ...props, ref: 'input' })}
{createDOMElement(component, props)}
{optionalPlaceholder}
</View>
</View>
+6 -9
View File
@@ -1,6 +1,7 @@
import applyLayout from '../../modules/applyLayout'
import applyNativeMethods from '../../modules/applyNativeMethods'
import createReactDOMComponent from '../../modules/createReactDOMComponent'
import BaseComponentPropTypes from '../../propTypes/BaseComponentPropTypes'
import createDOMElement from '../../modules/createDOMElement'
import EdgeInsetsPropType from '../../propTypes/EdgeInsetsPropType'
import normalizeNativeEvent from '../../modules/normalizeNativeEvent'
import { Component, PropTypes } from 'react'
@@ -35,10 +36,7 @@ class View extends Component {
static displayName = 'View'
static propTypes = {
accessibilityLabel: createReactDOMComponent.propTypes.accessibilityLabel,
accessibilityLiveRegion: createReactDOMComponent.propTypes.accessibilityLiveRegion,
accessibilityRole: createReactDOMComponent.propTypes.accessibilityRole,
accessible: createReactDOMComponent.propTypes.accessible,
...BaseComponentPropTypes,
children: PropTypes.any,
collapsable: PropTypes.bool,
hitSlop: EdgeInsetsPropType,
@@ -64,8 +62,7 @@ class View extends Component {
onTouchStart: PropTypes.func,
onTouchStartCapture: PropTypes.func,
pointerEvents: PropTypes.oneOf(['auto', 'box-none', 'box-only', 'none']),
style: StyleSheetPropType(ViewStylePropTypes),
testID: createReactDOMComponent.propTypes.testID
style: StyleSheetPropType(ViewStylePropTypes)
};
static defaultProps = {
@@ -110,10 +107,10 @@ class View extends Component {
return handlerProps
}, {})
const component = this.context.isInAButtonView ? 'span' : 'div'
const props = {
...other,
...normalizedEventHandlers,
component: this.context.isInAButtonView ? 'span' : 'div',
style: [
styles.initial,
style,
@@ -122,7 +119,7 @@ class View extends Component {
]
}
return createReactDOMComponent(props)
return createDOMElement(component, props)
}
_normalizeEventForHandler(handler, handlerName) {
@@ -1,61 +1,60 @@
/* eslint-env mocha */
import assert from 'assert'
import createReactDOMComponent from '..'
import createDOMElement from '..'
import { shallow } from 'enzyme'
suite('modules/createReactDOMComponent', () => {
suite('modules/createDOMElement', () => {
test('renders correct DOM element', () => {
let element = shallow(createDOMElement('span'))
assert.equal(element.is('span'), true)
element = shallow(createDOMElement('main'))
assert.equal(element.is('main'), true)
})
test('prop "accessibilityLabel"', () => {
const accessibilityLabel = 'accessibilityLabel'
const element = shallow(createReactDOMComponent({ accessibilityLabel }))
const element = shallow(createDOMElement('span', { accessibilityLabel }))
assert.equal(element.prop('aria-label'), accessibilityLabel)
})
test('prop "accessibilityLiveRegion"', () => {
const accessibilityLiveRegion = 'polite'
const element = shallow(createReactDOMComponent({ accessibilityLiveRegion }))
const element = shallow(createDOMElement('span', { accessibilityLiveRegion }))
assert.equal(element.prop('aria-live'), accessibilityLiveRegion)
})
test('prop "accessibilityRole"', () => {
const accessibilityRole = 'banner'
let element = shallow(createReactDOMComponent({ accessibilityRole }))
let element = shallow(createDOMElement('span', { accessibilityRole }))
assert.equal(element.prop('role'), accessibilityRole)
assert.equal(element.is('header'), true)
const button = 'button'
element = shallow(createReactDOMComponent({ accessibilityRole: 'button' }))
element = shallow(createDOMElement('span', { accessibilityRole: 'button' }))
assert.equal(element.prop('type'), button)
assert.equal(element.is('button'), true)
})
test('prop "accessible"', () => {
// accessible (implicit)
let element = shallow(createReactDOMComponent({}))
let element = shallow(createDOMElement('span', {}))
assert.equal(element.prop('aria-hidden'), null)
// accessible (explicit)
element = shallow(createReactDOMComponent({ accessible: true }))
element = shallow(createDOMElement('span', { accessible: true }))
assert.equal(element.prop('aria-hidden'), null)
// not accessible
element = shallow(createReactDOMComponent({ accessible: false }))
element = shallow(createDOMElement('span', { accessible: false }))
assert.equal(element.prop('aria-hidden'), true)
})
test('prop "component"', () => {
const component = 'main'
let element = shallow(createReactDOMComponent({}))
assert.equal(element.is('span'), true, 'Default element must be a "span"')
element = shallow(createReactDOMComponent({ component }))
assert.equal(element.is('main'), true)
})
test('prop "testID"', () => {
// no testID
let element = shallow(createReactDOMComponent({}))
let element = shallow(createDOMElement('span', {}))
assert.equal(element.prop('data-testid'), null)
// with testID
const testID = 'Example.testID'
element = shallow(createReactDOMComponent({ testID }))
element = shallow(createDOMElement('span', { testID }))
assert.equal(element.prop('data-testid'), testID)
})
})
+48
View File
@@ -0,0 +1,48 @@
import React from 'react'
import StyleSheet from '../../apis/StyleSheet'
const roleComponents = {
article: 'article',
banner: 'header',
button: 'button',
complementary: 'aside',
contentinfo: 'footer',
form: 'form',
heading: 'h1',
link: 'a',
list: 'ul',
listitem: 'li',
main: 'main',
navigation: 'nav',
region: 'section'
}
const createDOMElement = (component, rnProps = {}) => {
const {
accessibilityLabel,
accessibilityLiveRegion,
accessibilityRole,
accessible = true,
testID,
type,
...other
} = rnProps
const accessibilityComponent = accessibilityRole && roleComponents[accessibilityRole]
const Component = accessibilityComponent || component
return (
<Component
{...other}
{...StyleSheet.resolve(other)}
aria-hidden={accessible ? null : true}
aria-label={accessibilityLabel}
aria-live={accessibilityLiveRegion}
data-testid={testID}
role={accessibilityRole}
type={accessibilityRole === 'button' ? 'button' : type}
/>
)
}
module.exports = createDOMElement
@@ -1,58 +0,0 @@
import React, { PropTypes } from 'react'
import StyleSheet from '../../apis/StyleSheet'
const roleComponents = {
article: 'article',
banner: 'header',
button: 'button',
complementary: 'aside',
contentinfo: 'footer',
form: 'form',
heading: 'h1',
link: 'a',
list: 'ul',
listitem: 'li',
main: 'main',
navigation: 'nav',
region: 'section'
}
const createReactDOMComponent = ({
accessibilityLabel,
accessibilityLiveRegion,
accessibilityRole,
accessible = true,
component = 'span',
testID,
type,
...other
}) => {
const role = accessibilityRole
const Component = role && roleComponents[role] ? roleComponents[role] : component
return (
<Component
{...other}
{...StyleSheet.resolve(other)}
aria-hidden={accessible ? null : true}
aria-label={accessibilityLabel}
aria-live={accessibilityLiveRegion}
data-testid={testID}
role={role}
type={role === 'button' ? 'button' : type}
/>
)
}
createReactDOMComponent.propTypes = {
accessibilityLabel: PropTypes.string,
accessibilityLiveRegion: PropTypes.oneOf([ 'assertive', 'off', 'polite' ]),
accessibilityRole: PropTypes.string,
accessible: PropTypes.bool,
component: PropTypes.oneOfType([ PropTypes.func, PropTypes.string ]),
style: PropTypes.oneOfType([ PropTypes.array, PropTypes.object ]),
testID: PropTypes.string,
type: PropTypes.string
}
module.exports = createReactDOMComponent
+13
View File
@@ -0,0 +1,13 @@
import { PropTypes } from 'react'
const { array, bool, number, object, oneOf, oneOfType, string } = PropTypes
const BaseComponentPropTypes = {
accessibilityLabel: string,
accessibilityLiveRegion: oneOf([ 'assertive', 'off', 'polite' ]),
accessibilityRole: string,
accessible: bool,
style: oneOfType([ array, number, object ]),
testID: string
}
module.exports = BaseComponentPropTypes