Compare commits

...

5 Commits
0.0.7 ... 0.0.8

Author SHA1 Message Date
Nicolas Gallagher
a26033be2d 0.0.8 2015-10-18 17:56:05 -07:00
Nicolas Gallagher
fdb4ee4aae [change] StyleSheet docs 2015-10-18 17:49:35 -07:00
Nicolas Gallagher
08300f624f [change] remove 'component' prop; accessibility docs
- infer underlying HTML tag from 'accessibilityRole'
- move accessibility props to 'CoreComponent'
- remove the 'component' prop from exported Components

Fix gh-23
2015-10-18 15:24:59 -07:00
Nicolas Gallagher
7f5a2807e2 [fix] avoid eslint-plugin-react@3.6.0 bug 2015-10-18 11:23:29 -07:00
Nicolas Gallagher
292f045c52 [fix] README examples 2015-10-18 11:02:36 -07:00
20 changed files with 320 additions and 190 deletions

View File

@@ -6,7 +6,7 @@
[React Native][react-native-url] components and APIs for the Web.
~17.7 KB minified and gzipped.
* [Slack: reactiflux channel #react-native-web][slack-url]
* [Slack: #react-native-web on reactiflux][slack-url]
* [Gitter: react-native-web][gitter-url]
## Table of contents
@@ -16,6 +16,7 @@
* [APIs](#apis)
* [Components](#components)
* [Styling](#styling)
* [Accessibility](#accessibility)
* [Contributing](#contributing)
* [Thanks](#thanks)
* [License](#license)
@@ -96,7 +97,9 @@ const css = StyleSheet.renderToString();
const Html = () => (
<html>
<head>
<style id="react-stylesheet">{css}</style>
<meta charSet="utf-8" />
<meta content="initial-scale=1,width=device-width" name="viewport" />
<style id="react-stylesheet" dangerouslySetInnerHTML={{ __html: css } />
</head>
<body>
<div id="react-root" dangerouslySetInnerHTML={{ __html: html }} />
@@ -111,13 +114,13 @@ Render styles on the client:
// client.js
import App from './components/App'
import React, { StyleSheet } from 'react-native-web'
import ReactDOM from 'react-dom'
React.render(
<App />,
document.getElementById('react-root')
)
const reactRoot = document.getElementById('react-root')
const reactStyleSheet = document.getElementById('react-stylesheet')
document.getElementById('stylesheet').textContent = StyleSheet.renderToString()
ReactDOM.render(<App />, reactRoot)
reactStyleSheet.textContent = StyleSheet.renderToString()
```
## APIs
@@ -169,6 +172,19 @@ flexbox][flexbox-guide-url].
Styling components can be achieved with inline styles or the use of
[StyleSheet](docs/apis/StyleSheet.md).
## Accessibility
Major accessibility features are available through the following props:
`accessible`, `accessibilityLabel`, `accessibilityLiveRegion`, and
`accessibilityRole`. The `accessibilityRole` prop is used to determine the
rendered DOM element. For example:
* `<View accessibilityRole='banner' />` => `<header role='banner' />`.
* `<View accessibilityRole='button' />` => `<button type='button' role='button' />`.
* `<Text accessibilityRole='link' href='/' />` => `<a role='link' href='/' />`.
See the component documentation for more details.
## Contributing
Please read the [contribution guidelines][contributing-url]. Contributions are
@@ -177,7 +193,7 @@ welcome!
## Thanks
Thanks to current and past members of the React and React Native teams (in
particular Vjeux and Pete Hunt), and Tobias Koppers for Webpack and CSS loader.
particular Vjeux and Pete Hunt).
Thanks to [react-tappable](https://github.com/JedWatson/react-tappable) for
backing the current implementation of `Touchable`.

View File

@@ -1,11 +1,13 @@
var webpack = require('webpack')
var DedupePlugin = webpack.optimize.DedupePlugin
var EnvironmentPlugin = webpack.EnvironmentPlugin
var OccurenceOrderPlugin = webpack.optimize.OccurenceOrderPlugin
var UglifyJsPlugin = webpack.optimize.UglifyJsPlugin
var plugins = [
new DedupePlugin(),
new EnvironmentPlugin('NODE_ENV'),
new OccurenceOrderPlugin()
]

View File

@@ -1,21 +1,13 @@
# StyleSheet
React Native for Web will automatically vendor-prefix styles applied to the
libraries components. The `StyleSheet` abstraction converts predefined styles
library's components. The `StyleSheet` abstraction converts predefined styles
to CSS without a compile-time step. Some styles cannot be resolved outside of
the render loop and are applied as inline styles.
The `style`-to-`className` conversion strategy is optimized to minimize the
amount of CSS required. Unique declarations are defined using "atomic" CSS a
unique class name for a unique declaration.
React Native for Web includes a CSS reset to remove unwanted user agent styles
from elements and pseudo-elements beyond the reach of React (e.g., `html` and
`body`).
Create a new StyleSheet:
```
```js
const styles = StyleSheet.create({
container: {
borderRadius: 4,
@@ -55,17 +47,53 @@ StyleSheet.renderToString()
**create**(obj: {[key: string]: any})
**destroy**()
Clears all style information.
**renderToString**()
## Strategy
Renders a CSS Style Sheet.
Mapping entire `style` objects to CSS rules can lead to increasingly large CSS
files. Each new component adds new rules to the stylesheet.
## About
![](../static/styling-strategy.png)
### Strategy
React Native for Web uses an alternative strategy: mapping declarations to
declarations.
React Native for Web uses a `style`-to-`className` conversion strategy that is
designed to avoid issues arising from the [7 deadly sins of
CSS](https://speakerdeck.com/vjeux/react-css-in-js):
1. Global namespace
2. Dependency hell
3. Dead code elimination
4. Code minification
5. Sharing constants
6. Non-deterministic resolution
7. Breaking isolation
The strategy also minimizes the amount of generated CSS, making it more viable
to inline the style sheet when pre-rendering pages on the server. There is one
unique selector per unique style _declaration_.
```js
// definition
{
heading: {
color: 'gray',
fontSize: '2rem'
},
text: {
color: 'gray',
fontSize: '1.25rem'
}
}
// css
//
// .a { color: gray; }
// .b { font-size: 2rem; }
// .c { font-size: 1.25rem; }
```
For example:
@@ -102,11 +130,11 @@ In production the class names are obfuscated.
(CSS libraries like [Atomic CSS](http://acss.io/),
[Basscss](http://www.basscss.com/), [SUIT CSS](https://suitcss.github.io/), and
[tachyons](http://tachyons.io/) are attempts to limit style scope and limit
stylesheet growth in a similar way. But they're CSS utility libraries, each with a
style sheet growth in a similar way. But they're CSS utility libraries, each with a
particular set of classes and features to learn. All of them require developers
to manually connect CSS classes for given styles.)
## Media Queries, pseudo-classes, and pseudo-elements
### Media Queries, pseudo-classes, and pseudo-elements
Media Queries in JavaScript can be used to modify the render tree and styles.
This has the benefit of co-locating breakpoint-specific DOM and style changes.
@@ -115,3 +143,33 @@ Pseudo-classes like `:hover` and `:focus` can be replaced with JavaScript
events.
Pseudo-elements are not supported.
### Reset
React Native for Web includes a very small CSS reset taken from
[normalize.css](https://necolas.github.io/normalize.css/). It removes unwanted
User Agent styles from (pseudo-)elements beyond the reach of React (e.g.,
`html`, `body`) or inline styles (e.g., `::-moz-focus-inner`).
```css
html {
font-family: sans-serif;
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
}
body {
margin: 0;
}
button::-moz-focus-inner,
input::-moz-focus-inner {
border: 0;
padding: 0;
}
input[type="search"]::-webkit-search-cancel-button,
input[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
```

View File

@@ -23,6 +23,17 @@ NOTE: `Text` will transfer all other props to the rendered HTML element.
Defines the text available to assistive technologies upon interaction with the
element. (This is implemented using `aria-label`.)
(web) **accessibilityRole**: oneOf(roles)
Allows assistive technologies to present and support interaction with the view
in a manner that is consistent with user expectations for similar views of that
type. For example, marking a touchable view with an `accessibilityRole` of
`button`. (This is implemented using [ARIA roles](http://www.w3.org/TR/wai-aria/roles#role_definitions)).
Note: Avoid changing `accessibilityRole` values over time or after user
actions. Generally, accessibility APIs do not provide a means of notifying
assistive technologies of a `role` value change.
(web) **accessible**: bool = true
When `false`, the text is hidden from assistive technologies. (This is
@@ -32,10 +43,6 @@ implemented using `aria-hidden`.)
Child content.
(web) **component**: function | string = 'span'
Backing component.
**numberOfLines**: number
Truncates the text with an ellipsis after this many lines. Currently only supports `1`.
@@ -70,7 +77,7 @@ Used to locate this view in end-to-end tests.
## Examples
```js
import React, { Text } from 'react-native-web'
import React, { StyleSheet, Text } from 'react-native-web'
const { Component, PropTypes } = React
@@ -104,7 +111,7 @@ class PrettyText extends Component {
}
}
const localStyle = {
const localStyle = StyleSheet.create({
color: {
white: { color: 'white' },
gray: { color: 'gray' },
@@ -120,5 +127,5 @@ const localStyle = {
normal: { fontWeight: '400' },
bold: { fontWeight: '700' }
}
}
})
```

View File

@@ -54,10 +54,6 @@ assistive technologies of a `role` value change.
When `false`, the view is hidden from assistive technologies. (This is
implemented using `aria-hidden`.)
(web) **component**: function | string = 'div'
The React Component for this view.
**onLayout**: function
(TODO)
@@ -159,7 +155,7 @@ Used to locate this view in end-to-end tests.
## Examples
```js
import React, { View } from 'react-native-web'
import React, { StyleSheet, View } from 'react-native-web'
const { Component, PropTypes } = React
@@ -177,14 +173,14 @@ class Example extends Component {
}
}
const styles = {
const styles = StyleSheet.create({
row: {
flexDirection: 'row'
},
cell: {
flexGrow: 1
}
}
})
export default Example
```

View File

@@ -1,6 +1,6 @@
{
"name": "react-native-web",
"version": "0.0.7",
"version": "0.0.8",
"description": "React Native for Web",
"main": "dist/react-native-web.js",
"files": [
@@ -25,10 +25,10 @@
"babel-eslint": "^4.1.1",
"babel-loader": "^5.3.2",
"babel-runtime": "^5.8.20",
"eslint": "^1.3.1",
"eslint": "^1.7.1",
"eslint-config-standard": "^4.3.1",
"eslint-config-standard-react": "^1.0.4",
"eslint-plugin-react": "^3.3.1",
"eslint-plugin-react": "^3.6.0",
"eslint-plugin-standard": "^1.3.0",
"karma": "^0.13.9",
"karma-browserstack-launcher": "^0.1.5",

View File

@@ -0,0 +1,62 @@
/* eslint-env mocha */
import * as utils from '../../../modules/specHelpers'
import assert from 'assert'
import React from 'react'
import CoreComponent from '../'
suite('components/CoreComponent', () => {
test('prop "accessibilityLabel"', () => {
const accessibilityLabel = 'accessibilityLabel'
const dom = utils.renderToDOM(<CoreComponent accessibilityLabel={accessibilityLabel} />)
assert.equal(dom.getAttribute('aria-label'), accessibilityLabel)
})
test('prop "accessibilityLiveRegion"', () => {
const accessibilityLiveRegion = 'polite'
const dom = utils.renderToDOM(<CoreComponent accessibilityLiveRegion={accessibilityLiveRegion} />)
assert.equal(dom.getAttribute('aria-live'), accessibilityLiveRegion)
})
test('prop "accessibilityRole"', () => {
const accessibilityRole = 'banner'
let dom = utils.renderToDOM(<CoreComponent accessibilityRole={accessibilityRole} />)
assert.equal(dom.getAttribute('role'), accessibilityRole)
assert.equal((dom.tagName).toLowerCase(), 'header')
const button = 'button'
dom = utils.renderToDOM(<CoreComponent accessibilityRole={button} />)
assert.equal(dom.getAttribute('type'), button)
assert.equal((dom.tagName).toLowerCase(), button)
})
test('prop "accessible"', () => {
// accessible (implicit)
let dom = utils.renderToDOM(<CoreComponent />)
assert.equal(dom.getAttribute('aria-hidden'), null)
// accessible (explicit)
dom = utils.renderToDOM(<CoreComponent accessible />)
assert.equal(dom.getAttribute('aria-hidden'), null)
// not accessible
dom = utils.renderToDOM(<CoreComponent accessible={false} />)
assert.equal(dom.getAttribute('aria-hidden'), 'true')
})
test('prop "component"', () => {
const component = 'main'
const dom = utils.renderToDOM(<CoreComponent component={component} />)
const tagName = (dom.tagName).toLowerCase()
assert.equal(tagName, component)
})
test('prop "testID"', () => {
// no testID
let dom = utils.renderToDOM(<CoreComponent />)
assert.equal(dom.getAttribute('data-testid'), null)
// with testID
const testID = 'Example.testID'
dom = utils.renderToDOM(<CoreComponent testID={testID} />)
assert.equal(dom.getAttribute('data-testid'), testID)
})
})

View File

@@ -2,18 +2,38 @@ import React, { PropTypes } from 'react'
import StylePropTypes from '../../modules/StylePropTypes'
import StyleSheet from '../../modules/StyleSheet'
const roleComponents = {
article: 'article',
banner: 'header',
button: 'button',
complementary: 'aside',
contentinfo: 'footer',
form: 'form',
heading: 'h1',
link: 'a',
main: 'main',
navigation: 'nav',
region: 'section'
}
class CoreComponent extends React.Component {
static propTypes = {
accessibilityLabel: PropTypes.string,
accessibilityLiveRegion: PropTypes.oneOf(['assertive', 'off', 'polite']),
accessibilityRole: PropTypes.string,
accessible: PropTypes.bool,
className: PropTypes.string,
component: PropTypes.oneOfType([
PropTypes.func,
PropTypes.string
]),
style: PropTypes.object,
testID: PropTypes.string
testID: PropTypes.string,
type: PropTypes.string
}
static defaultProps = {
accessible: true,
component: 'div'
}
@@ -21,16 +41,28 @@ class CoreComponent extends React.Component {
render() {
const {
component: Component,
accessibilityLabel,
accessibilityLiveRegion,
accessibilityRole,
accessible,
component,
testID,
type,
...other
} = this.props
const Component = roleComponents[accessibilityRole] || 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}
/>
)
}

View File

@@ -1,6 +1,6 @@
/* eslint-env mocha */
import { assertProps, render, renderToDOM } from '../../../modules/specHelpers'
import * as utils from '../../../modules/specHelpers'
import assert from 'assert'
import React from 'react'
@@ -8,30 +8,34 @@ import Image from '../'
suite('components/Image', () => {
test('default accessibility', () => {
const dom = renderToDOM(<Image />)
const dom = utils.renderToDOM(<Image />)
assert.equal(dom.getAttribute('role'), 'img')
})
test('prop "accessibilityLabel"', () => {
assertProps.accessibilityLabel(Image)
const accessibilityLabel = 'accessibilityLabel'
const result = utils.shallowRender(<Image accessibilityLabel={accessibilityLabel} />)
assert.equal(result.props.accessibilityLabel, accessibilityLabel)
})
test('prop "accessible"', () => {
assertProps.accessible(Image)
const accessible = false
const result = utils.shallowRender(<Image accessible={accessible} />)
assert.equal(result.props.accessible, accessible)
})
test('prop "children"')
test('prop "defaultSource"', () => {
const defaultSource = { uri: 'https://google.com/favicon.ico' }
const dom = renderToDOM(<Image defaultSource={defaultSource} />)
const dom = utils.renderToDOM(<Image defaultSource={defaultSource} />)
const backgroundImage = dom.style.backgroundImage
assert(backgroundImage.indexOf(defaultSource.uri) > -1)
})
test('prop "onError"', function (done) {
this.timeout(5000)
render(<Image
utils.render(<Image
onError={onError}
source={{ uri: 'https://google.com/favicon.icox' }}
/>)
@@ -43,7 +47,7 @@ suite('components/Image', () => {
test('prop "onLoad"', function (done) {
this.timeout(5000)
render(<Image
utils.render(<Image
onLoad={onLoad}
source={{ uri: 'https://google.com/favicon.ico' }}
/>)
@@ -62,10 +66,12 @@ suite('components/Image', () => {
test('prop "source"')
test('prop "style"', () => {
assertProps.style(Image)
utils.assertProps.style(Image)
})
test('prop "testID"', () => {
assertProps.testID(Image)
const testID = 'testID'
const result = utils.shallowRender(<Image testID={testID} />)
assert.equal(result.props.testID, testID)
})
})

View File

@@ -55,18 +55,17 @@ const styles = StyleSheet.create({
class Image extends React.Component {
constructor(props, context) {
super(props, context)
const { uri } = props.source
// state
this.state = { status: props.source.uri ? STATUS_PENDING : STATUS_IDLE }
this.state = { status: uri ? STATUS_PENDING : STATUS_IDLE }
// autobinding
this._onError = this._onError.bind(this)
this._onLoad = this._onLoad.bind(this)
}
static propTypes = {
accessibilityLabel: PropTypes.string,
accessible: PropTypes.bool,
accessibilityLabel: CoreComponent.propTypes.accessibilityLabel,
accessible: CoreComponent.propTypes.accessible,
children: PropTypes.any,
defaultSource: PropTypes.object,
onError: PropTypes.func,
@@ -102,8 +101,8 @@ class Image extends React.Component {
_destroyImageLoader() {
if (this.image) {
this.image.onload = null
this.image.onerror = null
this.image.onload = null
this.image = null
}
}
@@ -124,8 +123,8 @@ class Image extends React.Component {
this._destroyImageLoader()
this.setState({ status: STATUS_LOADED })
this._onLoadEnd()
if (onLoad) onLoad(event)
this._onLoadEnd()
}
_onLoadEnd() {
@@ -194,7 +193,6 @@ class Image extends React.Component {
accessibilityLabel={accessibilityLabel}
accessibilityRole='img'
accessible={accessible}
component='div'
style={{
...styles.initial,
...resolvedStyle,

View File

@@ -1,6 +1,6 @@
/* eslint-env mocha */
import { assertProps, renderToDOM, shallowRender } from '../../../modules/specHelpers'
import * as utils from '../../../modules/specHelpers'
import assert from 'assert'
import React from 'react'
import ReactTestUtils from 'react-addons-test-utils'
@@ -9,27 +9,33 @@ import Text from '../'
suite('components/Text', () => {
test('prop "accessibilityLabel"', () => {
assertProps.accessibilityLabel(Text)
const accessibilityLabel = 'accessibilityLabel'
const result = utils.shallowRender(<Text accessibilityLabel={accessibilityLabel} />)
assert.equal(result.props.accessibilityLabel, accessibilityLabel)
})
test('prop "accessibilityRole"', () => {
const accessibilityRole = 'accessibilityRole'
const result = utils.shallowRender(<Text accessibilityRole={accessibilityRole} />)
assert.equal(result.props.accessibilityRole, accessibilityRole)
})
test('prop "accessible"', () => {
assertProps.accessible(Text)
const accessible = false
const result = utils.shallowRender(<Text accessible={accessible} />)
assert.equal(result.props.accessible, accessible)
})
test('prop "children"', () => {
const children = 'children'
const result = shallowRender(<Text>{children}</Text>)
const result = utils.shallowRender(<Text>{children}</Text>)
assert.equal(result.props.children, children)
})
test('prop "component"', () => {
assertProps.component(Text, 'span')
})
test('prop "numberOfLines"')
test('prop "onPress"', (done) => {
const dom = renderToDOM(<Text onPress={onPress} />)
const dom = utils.renderToDOM(<Text onPress={onPress} />)
ReactTestUtils.Simulate.click(dom)
function onPress(e) {
assert.ok(e.nativeEvent)
@@ -38,10 +44,12 @@ suite('components/Text', () => {
})
test('prop "style"', () => {
assertProps.style(Text)
utils.assertProps.style(Text)
})
test('prop "testID"', () => {
assertProps.testID(Text)
const testID = 'testID'
const result = utils.shallowRender(<Text testID={testID} />)
assert.equal(result.props.testID, testID)
})
})

View File

@@ -26,10 +26,10 @@ const styles = StyleSheet.create({
class Text extends React.Component {
static propTypes = {
_className: PropTypes.string, // escape-hatch for code migrations
accessibilityLabel: PropTypes.string,
accessible: PropTypes.bool,
accessibilityLabel: CoreComponent.propTypes.accessibilityLabel,
accessibilityRole: CoreComponent.propTypes.accessibilityRole,
accessible: CoreComponent.propTypes.accessible,
children: PropTypes.any,
component: CoreComponent.propTypes.component,
numberOfLines: PropTypes.number,
onPress: PropTypes.func,
style: PropTypes.shape(TextStylePropTypes),
@@ -41,7 +41,6 @@ class Text extends React.Component {
static defaultProps = {
_className: '',
accessible: true,
component: 'span',
style: styles.initial
}
@@ -52,14 +51,9 @@ class Text extends React.Component {
render() {
const {
_className,
accessibilityLabel,
accessible,
children,
component,
numberOfLines,
onPress,
style,
testID,
...other
} = this.props
@@ -69,18 +63,14 @@ class Text extends React.Component {
return (
<CoreComponent
{...other}
aria-hidden={accessible ? null : true}
aria-label={accessibilityLabel}
children={children}
className={className}
component={component}
component='span'
onClick={this._onPress.bind(this)}
style={{
...styles.initial,
...resolvedStyle,
...(numberOfLines === 1 && styles.singleLineStyle)
}}
testID={testID}
/>
)
}

View File

@@ -9,7 +9,9 @@ import TextInput from '../'
suite('components/TextInput', () => {
test('prop "accessibilityLabel"', () => {
utils.assertProps.accessibilityLabel(TextInput)
const accessibilityLabel = 'accessibilityLabel'
const result = utils.shallowRender(<TextInput accessibilityLabel={accessibilityLabel} />)
assert.equal(result.props.accessibilityLabel, accessibilityLabel)
})
test('prop "autoComplete"', () => {
@@ -208,7 +210,9 @@ suite('components/TextInput', () => {
})
test('prop "testID"', () => {
utils.assertProps.testID(TextInput)
const testID = 'testID'
const result = utils.shallowRender(<TextInput testID={testID} />)
assert.equal(result.props.testID, testID)
})
test('prop "value"', () => {

View File

@@ -23,7 +23,7 @@ const styles = StyleSheet.create({
class TextInput extends React.Component {
static propTypes = {
accessibilityLabel: PropTypes.string,
accessibilityLabel: CoreComponent.propTypes.accessibilityLabel,
autoComplete: PropTypes.bool,
autoFocus: PropTypes.bool,
clearTextOnFocus: PropTypes.bool,
@@ -136,7 +136,7 @@ class TextInput extends React.Component {
}
const propsCommon = {
'aria-label': accessibilityLabel,
accessibilityLabel,
autoComplete: autoComplete && 'on',
autoFocus,
className: 'TextInput',

View File

@@ -1,6 +1,6 @@
/* eslint-env mocha */
import { assertProps, shallowRender } from '../../../modules/specHelpers'
import * as utils from '../../../modules/specHelpers'
import assert from 'assert'
import React from 'react'
@@ -11,19 +11,25 @@ const requiredProps = { children }
suite('components/Touchable', () => {
test('prop "accessibilityLabel"', () => {
assertProps.accessibilityLabel(Touchable, requiredProps)
const accessibilityLabel = 'accessibilityLabel'
const result = utils.shallowRender(<Touchable {...requiredProps} accessibilityLabel={accessibilityLabel} />)
assert.equal(result.props.accessibilityLabel, accessibilityLabel)
})
test('prop "accessibilityRole"', () => {
assertProps.accessibilityRole(Touchable, requiredProps)
const accessibilityRole = 'accessibilityRole'
const result = utils.shallowRender(<Touchable {...requiredProps} accessibilityRole={accessibilityRole} />)
assert.equal(result.props.accessibilityRole, accessibilityRole)
})
test('prop "accessible"', () => {
assertProps.accessible(Touchable, requiredProps)
const accessible = false
const result = utils.shallowRender(<Touchable {...requiredProps} accessible={accessible} />)
assert.equal(result.props.accessible, accessible)
})
test('prop "children"', () => {
const result = shallowRender(<Touchable {...requiredProps} />)
const result = utils.shallowRender(<Touchable {...requiredProps} />)
assert.deepEqual(result.props.children, children)
})
})

View File

@@ -25,9 +25,9 @@ class Touchable extends React.Component {
}
static propTypes = {
accessibilityLabel: PropTypes.string,
accessibilityRole: PropTypes.string,
accessible: PropTypes.bool,
accessibilityLabel: View.propTypes.accessibilityLabel,
accessibilityRole: View.propTypes.accessibilityRole,
accessible: View.propTypes.accessible,
activeOpacity: PropTypes.number,
activeUnderlayColor: PropTypes.string,
children: PropTypes.element,
@@ -45,7 +45,6 @@ class Touchable extends React.Component {
accessibilityRole: 'button',
activeOpacity: 1,
activeUnderlayColor: 'transparent',
component: 'div',
delayLongPress: 1000,
delayPressIn: 0,
delayPressOut: 0,

View File

@@ -1,6 +1,6 @@
/* eslint-env mocha */
import { assertProps, shallowRender } from '../../../modules/specHelpers'
import * as utils from '../../../modules/specHelpers'
import assert from 'assert'
import React from 'react'
@@ -8,41 +8,47 @@ import View from '../'
suite('components/View', () => {
test('prop "accessibilityLabel"', () => {
assertProps.accessibilityLabel(View)
const accessibilityLabel = 'accessibilityLabel'
const result = utils.shallowRender(<View accessibilityLabel={accessibilityLabel} />)
assert.equal(result.props.accessibilityLabel, accessibilityLabel)
})
test('prop "accessibilityLiveRegion"', () => {
assertProps.accessibilityLiveRegion(View)
const accessibilityLiveRegion = 'polite'
const result = utils.shallowRender(<View accessibilityLiveRegion={accessibilityLiveRegion} />)
assert.equal(result.props.accessibilityLiveRegion, accessibilityLiveRegion)
})
test('prop "accessibilityRole"', () => {
assertProps.accessibilityRole(View)
const accessibilityRole = 'accessibilityRole'
const result = utils.shallowRender(<View accessibilityRole={accessibilityRole} />)
assert.equal(result.props.accessibilityRole, accessibilityRole)
})
test('prop "accessible"', () => {
assertProps.accessible(View)
const accessible = false
const result = utils.shallowRender(<View accessible={accessible} />)
assert.equal(result.props.accessible, accessible)
})
test('prop "children"', () => {
const children = 'children'
const result = shallowRender(<View>{children}</View>)
const result = utils.shallowRender(<View>{children}</View>)
assert.equal(result.props.children, children)
})
test('prop "component"', () => {
assertProps.component(View)
})
test('prop "pointerEvents"', () => {
const result = shallowRender(<View pointerEvents='box-only' />)
const result = utils.shallowRender(<View pointerEvents='box-only' />)
assert.equal(result.props.style.pointerEvents, 'box-only')
})
test('prop "style"', () => {
assertProps.style(View)
utils.assertProps.style(View)
})
test('prop "testID"', () => {
assertProps.testID(View)
const testID = 'testID'
const result = utils.shallowRender(<View testID={testID} />)
assert.equal(result.props.testID, testID)
})
})

View File

@@ -32,12 +32,11 @@ const styles = StyleSheet.create({
class View extends React.Component {
static propTypes = {
_className: PropTypes.string, // escape-hatch for code migrations
accessibilityLabel: PropTypes.string,
accessibilityLiveRegion: PropTypes.oneOf(['assertive', 'off', 'polite']),
accessibilityRole: PropTypes.string,
accessible: PropTypes.bool,
accessibilityLabel: CoreComponent.propTypes.accessibilityLabel,
accessibilityLiveRegion: CoreComponent.propTypes.accessibilityLiveRegion,
accessibilityRole: CoreComponent.propTypes.accessibilityRole,
accessible: CoreComponent.propTypes.accessible,
children: PropTypes.any,
component: CoreComponent.propTypes.component,
pointerEvents: PropTypes.oneOf(['auto', 'box-none', 'box-only', 'none']),
style: PropTypes.shape(ViewStylePropTypes),
testID: CoreComponent.propTypes.testID
@@ -48,20 +47,14 @@ class View extends React.Component {
static defaultProps = {
_className: '',
accessible: true,
component: 'div',
style: styles.initial
}
render() {
const {
_className,
accessibilityLabel,
accessibilityLiveRegion,
accessibilityRole,
accessible,
pointerEvents,
style,
testID,
...other
} = this.props
@@ -72,17 +65,12 @@ class View extends React.Component {
return (
<CoreComponent
{...other}
aria-hidden={accessible ? null : true}
aria-label={accessibilityLabel}
aria-live={accessibilityLiveRegion}
className={className}
role={accessibilityRole}
style={{
...styles.initial,
...resolvedStyle,
...pointerEventsStyle
}}
testID={testID}
/>
)
}

View File

@@ -1,10 +1,10 @@
import React, { Image, StyleSheet, Text, TextInput, Touchable, View } from '.'
import ReactDOM from 'react-dom'
const Heading = ({ children, level = '1', size = 'normal' }) => (
const Heading = ({ children, size = 'normal' }) => (
<Text
accessibilityRole='heading'
children={children}
component={`h${level}`}
style={headingStyles.size[size]}
/>
)
@@ -36,15 +36,15 @@ class Example extends React.Component {
render() {
return (
<View accessibilityRole='main' style={styles.root}>
<Heading level='1' size='xlarge'>React Native Web</Heading>
<Heading size='xlarge'>React Native Web</Heading>
<Text>React Native Web takes the core components from <Text
component='a' href='https://facebook.github.io/react-native/'>React
accessibilityRole='link' href='https://facebook.github.io/react-native/'>React
Native</Text> and brings them to the web. These components provide
simple building blocks touch handling, flexbox layout,
scroll views from which more complex components and apps can be
constructed.</Text>
<Heading level='2' size='large'>Image</Heading>
<Heading size='large'>Image</Heading>
<Image
accessibilityLabel='accessible image'
children={<Text>Inner content</Text>}
@@ -67,7 +67,7 @@ class Example extends React.Component {
testID='Example.image'
/>
<Heading level='2' size='large'>Text</Heading>
<Heading size='large'>Text</Heading>
<Text
onPress={(e) => { console.log('Text.onPress', e) }}
testID={'Example.text'}
@@ -92,7 +92,7 @@ class Example extends React.Component {
hendrerit consequat.
</Text>
<Heading level='2' size='large'>TextInput</Heading>
<Heading size='large'>TextInput</Heading>
<TextInput
keyboardType='default'
onBlur={(e) => { console.log('TextInput.onBlur', e) }}
@@ -114,7 +114,7 @@ class Example extends React.Component {
numberOfLines={5}
/>
<Heading level='2' size='large'>Touchable</Heading>
<Heading size='large'>Touchable</Heading>
<Touchable
accessibilityLabel={'Touchable element'}
activeHighlight='lightblue'
@@ -129,8 +129,8 @@ class Example extends React.Component {
</View>
</Touchable>
<Heading level='2' size='large'>View</Heading>
<Heading level='3'>Default layout</Heading>
<Heading size='large'>View</Heading>
<Heading>Default layout</Heading>
<View>
{[ 1, 2, 3, 4, 5, 6 ].map((item, i) => {
return (
@@ -141,7 +141,7 @@ class Example extends React.Component {
})}
</View>
<Heading level='3'>Row layout</Heading>
<Heading>Row layout</Heading>
<View style={styles.row}>
{[ 1, 2, 3, 4, 5, 6 ].map((item, i) => {
return (
@@ -152,13 +152,13 @@ class Example extends React.Component {
})}
</View>
<Heading level='3'>pointerEvents</Heading>
<Heading>pointerEvents</Heading>
<View style={styles.row}>
{['box-none', 'box-only', 'none'].map((value, i) => {
return (
<View
accessibilityRole='link'
children={value}
component='a'
href='https://google.com'
key={i}
pointerEvents={value}

View File

@@ -6,44 +6,6 @@ import ReactDOM from 'react-dom'
import ReactTestUtils from 'react-addons-test-utils'
export const assertProps = {
accessibilityLabel: function (Component, props) {
// with label
const accessibilityLabel = 'accessibilityLabel'
const dom = renderToDOM(<Component {...props} accessibilityLabel={accessibilityLabel} />)
assert.equal(dom.getAttribute('aria-label'), accessibilityLabel)
},
accessibilityLiveRegion: function (Component, props) {
const accessibilityLiveRegion = 'polite'
const dom = renderToDOM(<Component {...props} accessibilityLiveRegion={accessibilityLiveRegion} />)
assert.equal(dom.getAttribute('aria-live'), accessibilityLiveRegion)
},
accessibilityRole: function (Component, props) {
const accessibilityRole = 'main'
const dom = renderToDOM(<Component {...props} accessibilityRole={accessibilityRole} />)
assert.equal(dom.getAttribute('role'), accessibilityRole)
},
accessible: function (Component, props) {
// accessible (implicit)
let dom = renderToDOM(<Component {...props} />)
assert.equal(dom.getAttribute('aria-hidden'), null)
// accessible (explicit)
dom = renderToDOM(<Component {...props} accessible />)
assert.equal(dom.getAttribute('aria-hidden'), null)
// not accessible
dom = renderToDOM(<Component {...props} accessible={false} />)
assert.equal(dom.getAttribute('aria-hidden'), 'true')
},
component: function (Component, props) {
const component = 'main'
const dom = renderToDOM(<Component {...props} component={component} />)
const tagName = (dom.tagName).toLowerCase()
assert.equal(tagName, component)
},
style: function (Component, props) {
let shallow
// default styles
@@ -67,16 +29,6 @@ export const assertProps = {
shallow.props.style,
{ ...Component.defaultProps.style, ...styleToMerge }
)
},
testID: function (Component, props) {
// no testID
let dom = renderToDOM(<Component {...props} />)
assert.equal(dom.getAttribute('data-testid'), null)
// with testID
const testID = 'Example.testID'
dom = renderToDOM(<Component {...props} testID={testID} />)
assert.equal(dom.getAttribute('data-testid'), testID)
}
}