mirror of
https://github.com/zhigang1992/react-native-web.git
synced 2026-03-30 09:24:09 +08:00
Compare commits
63 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7cda89c5ce | ||
|
|
695eba45af | ||
|
|
92a2cb274a | ||
|
|
b1ca04d11e | ||
|
|
22ab70ea6f | ||
|
|
49f36d8eb1 | ||
|
|
80ba119b83 | ||
|
|
c30b67f6db | ||
|
|
4580f93199 | ||
|
|
4c46126ffe | ||
|
|
f8d5c15405 | ||
|
|
dc54e03625 | ||
|
|
4d5819ae28 | ||
|
|
5c482ef3be | ||
|
|
f51592f96e | ||
|
|
6bffe1775f | ||
|
|
7e75d037f2 | ||
|
|
7536508fe3 | ||
|
|
945fff0015 | ||
|
|
5032ed6fe1 | ||
|
|
8c7cdbf4be | ||
|
|
e5d8857bcc | ||
|
|
cda8d05bb7 | ||
|
|
049edc4611 | ||
|
|
e76d5a4e6c | ||
|
|
f71dae7d93 | ||
|
|
94d31beaf4 | ||
|
|
f5f9389728 | ||
|
|
fdbd19a4af | ||
|
|
36eafbc2f5 | ||
|
|
bca3398c1c | ||
|
|
722d77e8e5 | ||
|
|
d65c92eea9 | ||
|
|
8dd39c681c | ||
|
|
0b1759363d | ||
|
|
abd1227a94 | ||
|
|
8352c7cbda | ||
|
|
89f5a13891 | ||
|
|
4005f9ddde | ||
|
|
f192a9ba26 | ||
|
|
e688a949fb | ||
|
|
77605cb27c | ||
|
|
4f71833aec | ||
|
|
fa14995a35 | ||
|
|
4beae0dd78 | ||
|
|
5598961d2c | ||
|
|
4613baf0e8 | ||
|
|
3b661d8d6d | ||
|
|
22d20706e3 | ||
|
|
0b2813b186 | ||
|
|
b248de552d | ||
|
|
2b826dc7f4 | ||
|
|
b46acd4f50 | ||
|
|
5a03cb25cb | ||
|
|
44e60d12e3 | ||
|
|
fc60f8d332 | ||
|
|
2a65ca6fc0 | ||
|
|
9db3bd7e67 | ||
|
|
1963e9109a | ||
|
|
14072c7471 | ||
|
|
0af6dc00fc | ||
|
|
c9d401f09a | ||
|
|
8aeeed0ef7 |
3
.babelrc
3
.babelrc
@@ -1,5 +1,8 @@
|
||||
{
|
||||
"presets": [
|
||||
"react-native"
|
||||
],
|
||||
"plugins": [
|
||||
[ "transform-react-remove-prop-types", { "mode": "wrap" } ]
|
||||
]
|
||||
}
|
||||
|
||||
@@ -5,5 +5,4 @@ before_script:
|
||||
- export DISPLAY=:99.0
|
||||
- sh -e /etc/init.d/xvfb start
|
||||
script:
|
||||
- npm run lint
|
||||
- npm test
|
||||
|
||||
@@ -19,6 +19,12 @@ Fork, then clone the repo:
|
||||
git clone https://github.com/your-username/react-native-web.git
|
||||
```
|
||||
|
||||
Install dependencies (requires [yarn](https://yarnpkg.com/en/docs/install):
|
||||
|
||||
```
|
||||
yarn
|
||||
```
|
||||
|
||||
Run the examples:
|
||||
|
||||
```
|
||||
@@ -51,7 +57,7 @@ To continuously watch and run tests, run the following:
|
||||
npm run test:watch
|
||||
```
|
||||
|
||||
To perform linting, run the following:
|
||||
To perform only linting, run the following:
|
||||
|
||||
```
|
||||
npm run lint
|
||||
|
||||
@@ -27,7 +27,7 @@ online with [React Native for Web: Playground](http://codepen.io/necolas/pen/PZz
|
||||
To install in your app:
|
||||
|
||||
```
|
||||
npm install --save react react-native-web
|
||||
npm install --save react@15.3 react-native-web
|
||||
```
|
||||
|
||||
Read the [Client and Server rendering](docs/guides/rendering.md) guide.
|
||||
@@ -53,6 +53,7 @@ Exported modules:
|
||||
|
||||
* Components
|
||||
* [`ActivityIndicator`](docs/components/ActivityIndicator.md)
|
||||
* [`Button`](docs/components/Button.md)
|
||||
* [`Image`](docs/components/Image.md)
|
||||
* [`ListView`](docs/components/ListView.md)
|
||||
* [`ProgressBar`](docs/components/ProgressBar.md)
|
||||
@@ -69,6 +70,7 @@ Exported modules:
|
||||
* [`AppRegistry`](docs/apis/AppRegistry.md)
|
||||
* [`AppState`](docs/apis/AppState.md)
|
||||
* [`AsyncStorage`](docs/apis/AsyncStorage.md)
|
||||
* [`Clipboard`](docs/apis/Clipboard.md)
|
||||
* [`Dimensions`](docs/apis/Dimensions.md)
|
||||
* [`I18nManager`](docs/apis/I18nManager.md)
|
||||
* [`NativeMethods`](docs/apis/NativeMethods.md)
|
||||
@@ -142,6 +144,7 @@ AppRegistry.runApplication('MyApp', { rootTag: document.getElementById('react-ro
|
||||
* [react-native-web-player](https://github.com/dabbott/react-native-web-player)
|
||||
* [react-web](https://github.com/taobaofed/react-web)
|
||||
* [react-native-for-web](https://github.com/KodersLab/react-native-for-web)
|
||||
* [rhinos-app](https://github.com/rhinos-app/rhinos-app-dev)
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
`AppRegistry` is the control point for registering, running, prerendering, and
|
||||
unmounting all apps. App root components should register themselves with
|
||||
`AppRegistry.registerComponent`. Apps can be run by invoking
|
||||
`AppRegistry.runApplication`, and prerendered by invoking
|
||||
`AppRegistry.prerenderApplication` (see the [client and server rendering
|
||||
`AppRegistry.runApplication` (see the [client and server rendering
|
||||
guide](../guides/rendering.md) for more details).
|
||||
|
||||
To "stop" an application when a view should be destroyed, call
|
||||
@@ -13,14 +12,11 @@ into `runApplication`. These should always be used as a pair.
|
||||
|
||||
## Methods
|
||||
|
||||
(web) static **prerenderApplication**(appKey:string, appParameters: object)
|
||||
(web) static **getApplication**(appKey:string, appParameters: object)
|
||||
|
||||
Renders the given application to an HTML string. Use this for server-side
|
||||
rendering. Return object is of type `{ html: string; style: string;
|
||||
styleElement: ReactComponent }`. `html` is the prerendered HTML, `style` is the
|
||||
prerendered style sheet, and `styleElement` is a React Component. It's
|
||||
recommended that you use `styleElement` to render the style sheet in an app
|
||||
shell.
|
||||
Returns the given application element. Use this for server-side rendering.
|
||||
Return object is of type `{ element: ReactElement; stylesheet: ReactElement }`.
|
||||
It's recommended that you use `sheetsheet` to render the style sheet in an app
|
||||
|
||||
static **registerConfig**(config: Array<AppConfig>)
|
||||
|
||||
|
||||
16
docs/apis/Clipboard.md
Normal file
16
docs/apis/Clipboard.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# Clipboard
|
||||
|
||||
Clipboard gives you an interface for setting to the clipboard. (Getting
|
||||
clipboard content is not supported on web.)
|
||||
|
||||
## Methods
|
||||
|
||||
static **getString**()
|
||||
|
||||
Returns a `Promise` of an empty string.
|
||||
|
||||
static **setString**(content: string): boolean
|
||||
|
||||
Copies a string to the clipboard. On web, some browsers may not support copying
|
||||
to the clipboard, therefore, this function returns a boolean to indicate if the
|
||||
copy was successful.
|
||||
39
docs/components/Button.md
Normal file
39
docs/components/Button.md
Normal file
@@ -0,0 +1,39 @@
|
||||
# Button
|
||||
|
||||
A basic button component. Supports a minimal level of customization. You can
|
||||
build your own custom button using `TouchableOpacity` or
|
||||
`TouchableNativeFeedback`.
|
||||
|
||||
## Props
|
||||
|
||||
**accessibilityLabel**: string
|
||||
|
||||
Defines the text available to assistive technologies upon interaction with the
|
||||
element. (This is implemented using `aria-label`.)
|
||||
|
||||
**color**: string
|
||||
|
||||
Background color of the button.
|
||||
|
||||
**disabled**: bool = false
|
||||
|
||||
If true, disable all interactions for this component
|
||||
|
||||
**onPress**: function
|
||||
|
||||
This function is called on press.
|
||||
|
||||
**title**: string
|
||||
|
||||
Text to display inside the button.
|
||||
|
||||
## Examples
|
||||
|
||||
```js
|
||||
<Button
|
||||
accessibilityLabel="Learn more about this purple button"
|
||||
color="#841584"
|
||||
onPress={onPressLearnMore}
|
||||
title="Learn More"
|
||||
/>
|
||||
```
|
||||
@@ -38,6 +38,18 @@ which this `ScrollView` renders.
|
||||
Fires at most once per frame during scrolling. The frequency of the events can
|
||||
be contolled using the `scrollEventThrottle` prop.
|
||||
|
||||
Invoked on scroll with the following event:
|
||||
|
||||
```js
|
||||
{
|
||||
nativeEvent: {
|
||||
contentOffset: { x, y },
|
||||
contentSize: { height, width },
|
||||
layoutMeasurement: { height, width }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**refreshControl**: element
|
||||
|
||||
TODO
|
||||
@@ -51,8 +63,8 @@ When false, the content does not scroll.
|
||||
|
||||
**scrollEventThrottle**: number = 0
|
||||
|
||||
This controls how often the scroll event will be fired while scrolling (in
|
||||
events per seconds). A higher number yields better accuracy for code that is
|
||||
This controls how often the scroll event will be fired while scrolling (as a
|
||||
time interval in ms). A lower number yields better accuracy for code that is
|
||||
tracking the scroll position, but can lead to scroll performance problems. The
|
||||
default value is `0`, which means the scroll event will be sent only once each
|
||||
time the view is scrolled.
|
||||
@@ -104,7 +116,7 @@ export default class ScrollViewExample extends Component {
|
||||
contentContainerStyle={styles.container}
|
||||
horizontal
|
||||
onScroll={(e) => this.onScroll(e)}
|
||||
scrollEventThrottle={60}
|
||||
scrollEventThrottle={100}
|
||||
style={styles.root}
|
||||
/>
|
||||
)
|
||||
|
||||
@@ -6,12 +6,10 @@ such as auto-complete, auto-focus, placeholder text, and event callbacks.
|
||||
Note: some props are exclusive to or excluded from `multiline`.
|
||||
|
||||
Unsupported React Native props:
|
||||
`autoCapitalize`,
|
||||
`autoCorrect`,
|
||||
`onEndEditing`,
|
||||
`onSubmitEditing`,
|
||||
`clearButtonMode` (ios),
|
||||
`enablesReturnKeyAutomatically` (ios),
|
||||
`placeholderTextColor`,
|
||||
`returnKeyType` (ios),
|
||||
`selectionState` (ios),
|
||||
`underlineColorAndroid` (android)
|
||||
@@ -20,15 +18,37 @@ Unsupported React Native props:
|
||||
|
||||
[...View props](./View.md)
|
||||
|
||||
(web) **autoComplete**: bool = false
|
||||
**autoCapitalize**: oneOf('characters', 'none', 'sentences', 'words') = 'sentences'
|
||||
|
||||
Indicates whether the value of the control can be automatically completed by the browser.
|
||||
Automatically capitalize certain characters (only available in Chrome and iOS Safari).
|
||||
|
||||
* `characters`: Automatically capitalize all characters.
|
||||
* `none`: Completely disables automatic capitalization
|
||||
* `sentences`: Automatically capitalize the first letter of sentences.
|
||||
* `words`: Automatically capitalize the first letter of words.
|
||||
|
||||
(web) **autoComplete**: string
|
||||
|
||||
Indicates whether the value of the control can be automatically completed by
|
||||
the browser. [Accepted values](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input).
|
||||
|
||||
**autoCorrect**: bool = true
|
||||
|
||||
Automatically correct spelling mistakes (only available in iOS Safari).
|
||||
|
||||
**autoFocus**: bool = false
|
||||
|
||||
If true, focuses the input on `componentDidMount`. Only the first form element
|
||||
If `true`, focuses the input on `componentDidMount`. Only the first form element
|
||||
in a document with `autofocus` is focused.
|
||||
|
||||
**blurOnSubmit**: bool
|
||||
|
||||
If `true`, the text field will blur when submitted. The default value is `true`
|
||||
for single-line fields and `false` for multiline fields. Note, for multiline
|
||||
fields setting `blurOnSubmit` to `true` means that pressing return will blur
|
||||
the field and trigger the `onSubmitEditing` event instead of inserting a
|
||||
newline into the field.
|
||||
|
||||
**clearTextOnFocus**: bool = false
|
||||
|
||||
If `true`, clears the text field automatically when focused.
|
||||
@@ -87,29 +107,25 @@ as an argument to the callback handler.
|
||||
|
||||
Callback that is called when the text input is focused.
|
||||
|
||||
(web) **onSelectionChange**: function
|
||||
**onKeyPress**: function
|
||||
|
||||
Callback that is called when the text input's selection changes. The following
|
||||
object is passed as an argument to the callback handler.
|
||||
Callback that is called when a key is pressed. Pressed key value is passed as
|
||||
an argument to the callback handler. Fires before `onChange` callbacks.
|
||||
|
||||
```js
|
||||
{
|
||||
selectionDirection,
|
||||
selectionEnd,
|
||||
selectionStart,
|
||||
nativeEvent
|
||||
}
|
||||
```
|
||||
**onSelectionChange**: function
|
||||
|
||||
Callback that is called when the text input's selection changes. This will be called with
|
||||
`{ nativeEvent: { selection: { start, end } } }`.
|
||||
|
||||
**onSubmitEditing**: function
|
||||
|
||||
Callback that is called when the keyboard's submit button is pressed.
|
||||
|
||||
**placeholder**: string
|
||||
|
||||
The string that will be rendered in an empty `TextInput` before text has been
|
||||
entered.
|
||||
|
||||
**placeholderTextColor**: string
|
||||
|
||||
The text color of the placeholder string.
|
||||
|
||||
**secureTextEntry**: bool = false
|
||||
|
||||
If true, the text input obscures the text entered so that sensitive text like
|
||||
@@ -117,6 +133,10 @@ passwords stay secure.
|
||||
|
||||
(Not available when `multiline` is `true`.)
|
||||
|
||||
**selection**: { start: number, end: ?number }
|
||||
|
||||
The start and end of the text input's selection. Set start and end to the same value to position the cursor.
|
||||
|
||||
**selectTextOnFocus**: bool = false
|
||||
|
||||
If `true`, all text will automatically be selected on focus.
|
||||
@@ -152,6 +172,10 @@ Clear the text from the underlying DOM input.
|
||||
|
||||
Focus the underlying DOM input.
|
||||
|
||||
**isFocused()**
|
||||
|
||||
Returns `true` if the input is currently focused; `false` otherwise.
|
||||
|
||||
## Examples
|
||||
|
||||
```js
|
||||
|
||||
@@ -43,12 +43,7 @@ import ReactNative from 'react-native'
|
||||
// component that renders the app
|
||||
const AppHeaderContainer = (props) => { /* ... */ }
|
||||
|
||||
// DOM render
|
||||
ReactNative.render(<AppHeaderContainer />, document.getElementById('react-app-header'))
|
||||
|
||||
// Server render
|
||||
ReactNative.renderToString(<AppHeaderContainer />)
|
||||
ReactNative.renderToStaticMarkup(<AppHeaderContainer />)
|
||||
```
|
||||
|
||||
Rendering using the `AppRegistry`:
|
||||
@@ -63,12 +58,27 @@ const AppContainer = (props) => { /* ... */ }
|
||||
// register the app
|
||||
AppRegistry.registerComponent('App', () => AppContainer)
|
||||
|
||||
// DOM render
|
||||
AppRegistry.runApplication('App', {
|
||||
initialProps: {},
|
||||
rootTag: document.getElementById('react-app')
|
||||
})
|
||||
```
|
||||
|
||||
## Server-side rendering
|
||||
|
||||
Rendering using the `AppRegistry`:
|
||||
|
||||
```js
|
||||
import ReactDOMServer from 'react-dom/server'
|
||||
import ReactNative, { AppRegistry } from 'react-native'
|
||||
|
||||
// component that renders the app
|
||||
const AppContainer = (props) => { /* ... */ }
|
||||
|
||||
// register the app
|
||||
AppRegistry.registerComponent('App', () => AppContainer)
|
||||
|
||||
// prerender the app
|
||||
const { html, styleElement } = AppRegistry.prerenderApplication('App', { initialProps })
|
||||
const { element, stylesheet } = AppRegistry.getApplication('App', { initialProps });
|
||||
const initialHTML = ReactDOMServer.renderToString(element);
|
||||
```
|
||||
|
||||
33
examples/apis/Clipboard/ClipboardExample.js
Normal file
33
examples/apis/Clipboard/ClipboardExample.js
Normal file
@@ -0,0 +1,33 @@
|
||||
import { Clipboard, Text, TextInput, View } from 'react-native'
|
||||
import React, { Component } from 'react';
|
||||
import { action, storiesOf } from '@kadira/storybook';
|
||||
|
||||
class ClipboardExample extends Component {
|
||||
render() {
|
||||
return (
|
||||
<View style={{ minWidth: 300 }}>
|
||||
<Text onPress={this._handleSet}>Copy to clipboard</Text>
|
||||
<TextInput
|
||||
multiline={true}
|
||||
placeholder={'Try pasting here afterwards'}
|
||||
style={{ borderWidth: 1, height: 200, marginVertical: 20 }}
|
||||
/>
|
||||
<Text onPress={this._handleGet}>(Clipboard.getString returns a Promise that always resolves to an empty string on web)</Text>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
_handleGet() {
|
||||
Clipboard.getString().then((value) => { console.log(`Clipboard value: ${value}`) });
|
||||
}
|
||||
|
||||
_handleSet() {
|
||||
const success = Clipboard.setString('This text was copied to the clipboard by React Native');
|
||||
console.log(`Clipboard.setString success? ${success}`);
|
||||
}
|
||||
}
|
||||
|
||||
storiesOf('api: Clipboard', module)
|
||||
.add('setString', () => (
|
||||
<ClipboardExample />
|
||||
));
|
||||
@@ -1,8 +1,8 @@
|
||||
import { storiesOf } from '@kadira/storybook';
|
||||
import { I18nManager, StyleSheet, TouchableHighlight, Text, View } from 'react-native'
|
||||
import React, { Component } from 'react';
|
||||
import { storiesOf, action } from '@kadira/storybook';
|
||||
|
||||
class RTLExample extends Component {
|
||||
class I18nManagerExample extends Component {
|
||||
componentWillUnmount() {
|
||||
I18nManager.setPreferredLanguageRTL(false)
|
||||
}
|
||||
@@ -75,5 +75,5 @@ const styles = StyleSheet.create({
|
||||
|
||||
storiesOf('api: I18nManager', module)
|
||||
.add('RTL layout', () => (
|
||||
<RTLExample />
|
||||
<I18nManagerExample />
|
||||
))
|
||||
29
examples/apis/Linking/LinkingExample.js
Normal file
29
examples/apis/Linking/LinkingExample.js
Normal file
@@ -0,0 +1,29 @@
|
||||
import { Linking, StyleSheet, Text, View } from 'react-native'
|
||||
import React, { Component } from 'react';
|
||||
import { storiesOf, action } from '@kadira/storybook';
|
||||
|
||||
class LinkingExample extends Component {
|
||||
render() {
|
||||
return (
|
||||
<View>
|
||||
<Text onPress={() => { Linking.openURL('https://mathiasbynens.github.io/rel-noopener/malicious.html'); }} style={styles.text}>
|
||||
Linking.openURL (Expect: "The previous tab is safe and intact")
|
||||
</Text>
|
||||
<Text accessibilityRole='link' href='https://mathiasbynens.github.io/rel-noopener/malicious.html' style={styles.text} target='_blank'>
|
||||
target="_blank" (Expect: "The previous tab is safe and intact")
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
text: {
|
||||
marginVertical: 10
|
||||
}
|
||||
});
|
||||
|
||||
storiesOf('api: Linking', module)
|
||||
.add('Safe linking', () => (
|
||||
<LinkingExample />
|
||||
));
|
||||
80
examples/components/Button/ButtonExample.js
Normal file
80
examples/components/Button/ButtonExample.js
Normal file
@@ -0,0 +1,80 @@
|
||||
import React from 'react';
|
||||
import { action, storiesOf } from '@kadira/storybook';
|
||||
import { Button, StyleSheet, View } from 'react-native';
|
||||
|
||||
const onButtonPress = action('Button has been pressed!');
|
||||
|
||||
const examples = [
|
||||
{
|
||||
title: 'Simple Button',
|
||||
description: 'The title and onPress handler are required. It is ' +
|
||||
'recommended to set accessibilityLabel to help make your app usable by ' +
|
||||
'everyone.',
|
||||
render: function() {
|
||||
return (
|
||||
<Button
|
||||
onPress={onButtonPress}
|
||||
title="Press Me"
|
||||
accessibilityLabel="See an informative alert"
|
||||
/>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Adjusted color',
|
||||
description: 'Adjusts the color in a way that looks standard on each ' +
|
||||
'platform. On iOS, the color prop controls the color of the text. On ' +
|
||||
'Android, the color adjusts the background color of the button.',
|
||||
render: function() {
|
||||
return (
|
||||
<Button
|
||||
onPress={onButtonPress}
|
||||
title="Press Purple"
|
||||
color="#841584"
|
||||
accessibilityLabel="Learn more about purple"
|
||||
/>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Fit to text layout',
|
||||
description: 'This layout strategy lets the title define the width of ' +
|
||||
'the button',
|
||||
render: function() {
|
||||
return (
|
||||
<View style={{flexDirection: 'row', justifyContent: 'space-between'}}>
|
||||
<Button
|
||||
onPress={onButtonPress}
|
||||
title="This looks great!"
|
||||
accessibilityLabel="This sounds great!"
|
||||
/>
|
||||
<Button
|
||||
onPress={onButtonPress}
|
||||
title="Ok!"
|
||||
color="#841584"
|
||||
accessibilityLabel="Ok, Great!"
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Disabled Button',
|
||||
description: 'All interactions for the component are disabled.',
|
||||
render: function() {
|
||||
return (
|
||||
<Button
|
||||
disabled
|
||||
onPress={onButtonPress}
|
||||
title="I Am Disabled"
|
||||
accessibilityLabel="See an informative alert"
|
||||
/>
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
examples.forEach((example) => {
|
||||
storiesOf('component: Button', module)
|
||||
.add(example.title, () => example.render());
|
||||
});
|
||||
@@ -218,7 +218,7 @@ const examples = [
|
||||
render: function() {
|
||||
return (
|
||||
<Image
|
||||
source={{uri: 'http://facebook.github.io/react/img/logo_og.png'}}
|
||||
source={{ uri: 'http://facebook.github.io/react/img/logo_og.png', width: 1200, height: 630 }}
|
||||
style={styles.base}
|
||||
/>
|
||||
);
|
||||
@@ -652,6 +652,6 @@ var styles = StyleSheet.create({
|
||||
|
||||
examples.forEach((example) => {
|
||||
storiesOf('component: Image', module)
|
||||
.addDecorator((renderStory) => <View>{renderStory()}</View>)
|
||||
.addDecorator((renderStory) => <View style={{ width: '100%' }}>{renderStory()}</View>)
|
||||
.add(example.title, () => example.render())
|
||||
})
|
||||
|
||||
@@ -1,4 +1,80 @@
|
||||
import React from 'react';
|
||||
import { storiesOf, action } from '@kadira/storybook';
|
||||
import { ListView } from 'react-native'
|
||||
import { storiesOf } from '@kadira/storybook';
|
||||
import { ListView, StyleSheet, Text, View } from 'react-native';
|
||||
|
||||
const generateData = (length) => Array.from({ length }).map((item, i) => i);
|
||||
const dataSource = new ListView.DataSource({ rowHasChanged: (r1, r2) => r1 !== r2 });
|
||||
|
||||
storiesOf('component: ListView', module)
|
||||
.add('vertical', () => (
|
||||
<View style={styles.scrollViewContainer}>
|
||||
<ListView
|
||||
contentContainerStyle={styles.scrollViewContentContainerStyle}
|
||||
dataSource={dataSource.cloneWithRows(generateData(100))}
|
||||
initialListSize={100}
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onScroll={(e) => { console.log('ScrollView.onScroll', e); } }
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
renderRow={(row) => (
|
||||
<View><Text>{row}</Text></View>
|
||||
)}
|
||||
scrollEventThrottle={1000} // 1 event per second
|
||||
style={styles.scrollViewStyle}
|
||||
/>
|
||||
</View>
|
||||
))
|
||||
.add('incremental rendering - large pageSize', () => (
|
||||
<View style={styles.scrollViewContainer}>
|
||||
<ListView
|
||||
contentContainerStyle={styles.scrollViewContentContainerStyle}
|
||||
dataSource={dataSource.cloneWithRows(generateData(5000))}
|
||||
initialListSize={100}
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onScroll={(e) => { console.log('ScrollView.onScroll', e); } }
|
||||
pageSize={50}
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
renderRow={(row) => (
|
||||
<View><Text>{row}</Text></View>
|
||||
)}
|
||||
scrollEventThrottle={1000} // 1 event per second
|
||||
style={styles.scrollViewStyle}
|
||||
/>
|
||||
</View>
|
||||
))
|
||||
.add('incremental rendering - small pageSize', () => (
|
||||
<View style={styles.scrollViewContainer}>
|
||||
<ListView
|
||||
contentContainerStyle={styles.scrollViewContentContainerStyle}
|
||||
dataSource={dataSource.cloneWithRows(generateData(5000))}
|
||||
initialListSize={5}
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onScroll={(e) => { console.log('ScrollView.onScroll', e); } }
|
||||
pageSize={1}
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
renderRow={(row) => (
|
||||
<View><Text>{row}</Text></View>
|
||||
)}
|
||||
scrollEventThrottle={1000} // 1 event per second
|
||||
style={styles.scrollViewStyle}
|
||||
/>
|
||||
</View>
|
||||
));
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
box: {
|
||||
flexGrow: 1,
|
||||
justifyContent: 'center',
|
||||
borderWidth: 1
|
||||
},
|
||||
scrollViewContainer: {
|
||||
height: '200px',
|
||||
width: 300
|
||||
},
|
||||
scrollViewStyle: {
|
||||
borderWidth: '1px'
|
||||
},
|
||||
scrollViewContentContainerStyle: {
|
||||
backgroundColor: '#eee',
|
||||
padding: '10px'
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,19 +1,21 @@
|
||||
import React from 'react';
|
||||
import { storiesOf, action } from '@kadira/storybook';
|
||||
import { ScrollView, StyleSheet, Text, View } from 'react-native'
|
||||
import { action, storiesOf } from '@kadira/storybook';
|
||||
import { ScrollView, StyleSheet, Text, TouchableHighlight, View } from 'react-native'
|
||||
|
||||
const onScroll = action('ScrollView.onScroll');
|
||||
|
||||
storiesOf('component: ScrollView', module)
|
||||
.add('vertical', () => (
|
||||
<View style={styles.scrollViewContainer}>
|
||||
<ScrollView
|
||||
contentContainerStyle={styles.scrollViewContentContainerStyle}
|
||||
onScroll={e => console.log('ScrollView.onScroll', e)}
|
||||
scrollEventThrottle={1} // 1 event per second
|
||||
onScroll={onScroll}
|
||||
scrollEventThrottle={1000} // 1 event per second
|
||||
style={styles.scrollViewStyle}
|
||||
>
|
||||
{Array.from({ length: 50 }).map((item, i) => (
|
||||
<View key={i} style={styles.box}>
|
||||
<Text>{i}</Text>
|
||||
<TouchableHighlight onPress={() => {}}><Text>{i}</Text></TouchableHighlight>
|
||||
</View>
|
||||
))}
|
||||
</ScrollView>
|
||||
@@ -24,8 +26,8 @@ storiesOf('component: ScrollView', module)
|
||||
<ScrollView
|
||||
contentContainerStyle={styles.scrollViewContentContainerStyle}
|
||||
horizontal
|
||||
onScroll={e => console.log('ScrollView.onScroll', e)}
|
||||
scrollEventThrottle={1} // 1 event per second
|
||||
onScroll={onScroll}
|
||||
scrollEventThrottle={16} // ~60 events per second
|
||||
style={styles.scrollViewStyle}
|
||||
>
|
||||
{Array.from({ length: 50 }).map((item, i) => (
|
||||
@@ -39,7 +41,6 @@ storiesOf('component: ScrollView', module)
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
box: {
|
||||
alignItems: 'center',
|
||||
flexGrow: 1,
|
||||
justifyContent: 'center',
|
||||
borderWidth: 1
|
||||
@@ -49,10 +50,10 @@ const styles = StyleSheet.create({
|
||||
width: 300
|
||||
},
|
||||
scrollViewStyle: {
|
||||
borderWidth: '1px'
|
||||
borderWidth: 1
|
||||
},
|
||||
scrollViewContentContainerStyle: {
|
||||
backgroundColor: '#eee',
|
||||
padding: '10px'
|
||||
padding: 10
|
||||
}
|
||||
})
|
||||
|
||||
@@ -79,7 +79,7 @@ const examples = [
|
||||
title: 'Wrap',
|
||||
render: function() {
|
||||
return (
|
||||
<Text>
|
||||
<Text style={{ WebkitFontSmoothing: 'antialiased' }}>
|
||||
The text should wrap if it goes on multiple lines. See, this is going to
|
||||
the next line.
|
||||
</Text>
|
||||
|
||||
@@ -1,41 +1,867 @@
|
||||
import React from 'react';
|
||||
import { storiesOf, action } from '@kadira/storybook';
|
||||
import { StyleSheet, TextInput, View } from 'react-native'
|
||||
import { StyleSheet, Text, TextInput, View } from 'react-native'
|
||||
|
||||
storiesOf('component: TextInput', module)
|
||||
.add('tbd', () => (
|
||||
<View>
|
||||
<TextInput
|
||||
defaultValue='Default textInput'
|
||||
keyboardType='default'
|
||||
onBlur={(e) => { console.log('TextInput.onBlur', e) }}
|
||||
onChange={(e) => { console.log('TextInput.onChange', e) }}
|
||||
onChangeText={(e) => { console.log('TextInput.onChangeText', e) }}
|
||||
onFocus={(e) => { console.log('TextInput.onFocus', e) }}
|
||||
onSelectionChange={(e) => { console.log('TextInput.onSelectionChange', e) }}
|
||||
/>
|
||||
<TextInput keyboardType='search' style={styles.textInput} />
|
||||
<TextInput secureTextEntry style={styles.textInput} />
|
||||
<TextInput defaultValue='read only' editable={false} style={styles.textInput} />
|
||||
<TextInput
|
||||
style={[ styles.textInput, { flex:1, height: 60, padding: 20, fontSize: 20, textAlign: 'center' } ]}
|
||||
keyboardType='email-address' placeholder='you@domain.com' placeholderTextColor='red'
|
||||
/>
|
||||
<TextInput keyboardType='numeric' style={styles.textInput} />
|
||||
<TextInput keyboardType='phone-pad' style={styles.textInput} />
|
||||
<TextInput defaultValue='https://delete-me' keyboardType='url' placeholder='https://www.some-website.com' selectTextOnFocus style={styles.textInput} />
|
||||
<TextInput
|
||||
defaultValue='default value'
|
||||
maxNumberOfLines={10}
|
||||
multiline
|
||||
numberOfLines={5}
|
||||
style={styles.textInput}
|
||||
/>
|
||||
</View>
|
||||
))
|
||||
/**
|
||||
* Copyright (c) 2013-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* The examples provided by Facebook are for non-commercial testing and
|
||||
* evaluation purposes only.
|
||||
*
|
||||
* Facebook reserves all rights not expressly granted.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
|
||||
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
textInput: {
|
||||
borderWidth: 1
|
||||
class WithLabel extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<View style={styles.labelContainer}>
|
||||
<View style={styles.label}>
|
||||
<Text>{this.props.label}</Text>
|
||||
</View>
|
||||
{this.props.children}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
class TextEventsExample extends React.Component {
|
||||
state = {
|
||||
curText: '<No Event>',
|
||||
prevText: '<No Event>',
|
||||
prev2Text: '<No Event>',
|
||||
prev3Text: '<No Event>',
|
||||
};
|
||||
|
||||
updateText = (text) => {
|
||||
this.setState((state) => {
|
||||
return {
|
||||
curText: text,
|
||||
prevText: state.curText,
|
||||
prev2Text: state.prevText,
|
||||
prev3Text: state.prev2Text,
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View style={{ alignItems: 'center' }}>
|
||||
<TextInput
|
||||
autoCapitalize="none"
|
||||
placeholder="Enter text to see events"
|
||||
autoCorrect={false}
|
||||
onFocus={() => this.updateText('onFocus')}
|
||||
onBlur={() => this.updateText('onBlur')}
|
||||
onChange={(event) => this.updateText(
|
||||
'onChange text: ' + event.nativeEvent.text
|
||||
)}
|
||||
onEndEditing={(event) => this.updateText(
|
||||
'onEndEditing text: ' + event.nativeEvent.text
|
||||
)}
|
||||
onSubmitEditing={(event) => this.updateText(
|
||||
'onSubmitEditing text: ' + event.nativeEvent.text
|
||||
)}
|
||||
onSelectionChange={(event) => this.updateText(
|
||||
'onSelectionChange range: ' +
|
||||
event.nativeEvent.selection.start + ',' +
|
||||
event.nativeEvent.selection.end
|
||||
)}
|
||||
onKeyPress={(event) => {
|
||||
this.updateText('onKeyPress key: ' + event.nativeEvent.key);
|
||||
}}
|
||||
style={[ styles.default, { maxWidth: 200 } ]}
|
||||
/>
|
||||
<Text style={styles.eventLabel}>
|
||||
{this.state.curText}{'\n'}
|
||||
(prev: {this.state.prevText}){'\n'}
|
||||
(prev2: {this.state.prev2Text}){'\n'}
|
||||
(prev3: {this.state.prev3Text})
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class AutoExpandingTextInput extends React.Component {
|
||||
state: any;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
text: 'React Native enables you to build world-class application experiences on native platforms using a consistent developer experience based on JavaScript and React. The focus of React Native is on developer efficiency across all the platforms you care about — learn once, write anywhere. Facebook uses React Native in multiple production apps and will continue investing in React Native.',
|
||||
height: 0,
|
||||
};
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<TextInput
|
||||
{...this.props}
|
||||
multiline={true}
|
||||
onChangeText={(text) => {
|
||||
this.setState({text});
|
||||
}}
|
||||
onContentSizeChange={(event) => {
|
||||
this.setState({height: event.nativeEvent.contentSize.height});
|
||||
}}
|
||||
style={[styles.default, {height: Math.max(35, this.state.height)}]}
|
||||
value={this.state.text}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class RewriteExample extends React.Component {
|
||||
state: any;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {text: ''};
|
||||
}
|
||||
render() {
|
||||
var limit = 20;
|
||||
var remainder = limit - this.state.text.length;
|
||||
var remainderColor = remainder > 5 ? 'blue' : 'red';
|
||||
return (
|
||||
<View style={styles.rewriteContainer}>
|
||||
<TextInput
|
||||
multiline={false}
|
||||
maxLength={limit}
|
||||
onChangeText={(text) => {
|
||||
text = text.replace(/ /g, '_');
|
||||
this.setState({text});
|
||||
}}
|
||||
style={styles.default}
|
||||
value={this.state.text}
|
||||
/>
|
||||
<Text style={[styles.remainder, {color: remainderColor}]}>
|
||||
{remainder}
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class RewriteExampleInvalidCharacters extends React.Component {
|
||||
state: any;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {text: ''};
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<View style={styles.rewriteContainer}>
|
||||
<TextInput
|
||||
multiline={false}
|
||||
onChangeText={(text) => {
|
||||
this.setState({text: text.replace(/\s/g, '')});
|
||||
}}
|
||||
style={styles.default}
|
||||
value={this.state.text}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class TokenizedTextExample extends React.Component {
|
||||
state: any;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {text: 'Hello #World'};
|
||||
}
|
||||
render() {
|
||||
|
||||
//define delimiter
|
||||
let delimiter = /\s+/;
|
||||
|
||||
//split string
|
||||
let _text = this.state.text;
|
||||
let token, index, parts = [];
|
||||
while (_text) {
|
||||
delimiter.lastIndex = 0;
|
||||
token = delimiter.exec(_text);
|
||||
if (token === null) {
|
||||
break;
|
||||
}
|
||||
index = token.index;
|
||||
if (token[0].length === 0) {
|
||||
index = 1;
|
||||
}
|
||||
parts.push(_text.substr(0, index));
|
||||
parts.push(token[0]);
|
||||
index = index + token[0].length;
|
||||
_text = _text.slice(index);
|
||||
}
|
||||
parts.push(_text);
|
||||
|
||||
return (
|
||||
<View>
|
||||
<TextInput
|
||||
value={parts.join('')}
|
||||
multiline={true}
|
||||
style={styles.multiline}
|
||||
onChangeText={(text) => {
|
||||
this.setState({text});
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class BlurOnSubmitExample extends React.Component {
|
||||
focusNextField = (nextField) => {
|
||||
this.refs[nextField].focus();
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View>
|
||||
<TextInput
|
||||
ref="1"
|
||||
style={styles.default}
|
||||
placeholder="blurOnSubmit = false"
|
||||
returnKeyType="next"
|
||||
blurOnSubmit={false}
|
||||
onSubmitEditing={() => this.focusNextField('2')}
|
||||
/>
|
||||
<TextInput
|
||||
ref="2"
|
||||
style={styles.default}
|
||||
keyboardType="email-address"
|
||||
placeholder="blurOnSubmit = false"
|
||||
returnKeyType="next"
|
||||
blurOnSubmit={false}
|
||||
onSubmitEditing={() => this.focusNextField('3')}
|
||||
/>
|
||||
<TextInput
|
||||
ref="3"
|
||||
style={styles.default}
|
||||
keyboardType="url"
|
||||
placeholder="blurOnSubmit = false"
|
||||
returnKeyType="next"
|
||||
blurOnSubmit={false}
|
||||
onSubmitEditing={() => this.focusNextField('4')}
|
||||
/>
|
||||
<TextInput
|
||||
ref="4"
|
||||
style={styles.default}
|
||||
keyboardType="numeric"
|
||||
placeholder="blurOnSubmit = false"
|
||||
blurOnSubmit={false}
|
||||
onSubmitEditing={() => this.focusNextField('5')}
|
||||
/>
|
||||
<TextInput
|
||||
ref="5"
|
||||
style={styles.default}
|
||||
keyboardType="numeric"
|
||||
placeholder="blurOnSubmit = true"
|
||||
returnKeyType="done"
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
type SelectionExampleState = {
|
||||
selection: {
|
||||
start: number;
|
||||
end?: number;
|
||||
};
|
||||
value: string;
|
||||
};
|
||||
|
||||
class SelectionExample extends React.Component {
|
||||
state: SelectionExampleState;
|
||||
|
||||
_textInput: any;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
selection: {start: 0, end: 0},
|
||||
value: props.value
|
||||
};
|
||||
}
|
||||
|
||||
onSelectionChange({nativeEvent: {selection}}) {
|
||||
this.setState({selection});
|
||||
}
|
||||
|
||||
getRandomPosition() {
|
||||
var length = this.state.value.length;
|
||||
return Math.round(Math.random() * length);
|
||||
}
|
||||
|
||||
select(start, end) {
|
||||
this._textInput.focus();
|
||||
this.setState({selection: {start, end}});
|
||||
}
|
||||
|
||||
selectRandom() {
|
||||
var positions = [this.getRandomPosition(), this.getRandomPosition()].sort();
|
||||
this.select(...positions);
|
||||
}
|
||||
|
||||
placeAt(position) {
|
||||
this.select(position, position);
|
||||
}
|
||||
|
||||
placeAtRandom() {
|
||||
this.placeAt(this.getRandomPosition());
|
||||
}
|
||||
|
||||
render() {
|
||||
var length = this.state.value.length;
|
||||
|
||||
return (
|
||||
<View>
|
||||
<TextInput
|
||||
multiline={this.props.multiline}
|
||||
onChangeText={(value) => this.setState({value})}
|
||||
onSelectionChange={this.onSelectionChange.bind(this)}
|
||||
ref={textInput => (this._textInput = textInput)}
|
||||
selection={this.state.selection}
|
||||
style={this.props.style}
|
||||
value={this.state.value}
|
||||
/>
|
||||
<View>
|
||||
<Text>
|
||||
selection = {JSON.stringify(this.state.selection)}
|
||||
</Text>
|
||||
<Text onPress={this.placeAt.bind(this, 0)}>
|
||||
Place at Start (0, 0)
|
||||
</Text>
|
||||
<Text onPress={this.placeAt.bind(this, length)}>
|
||||
Place at End ({length}, {length})
|
||||
</Text>
|
||||
<Text onPress={this.placeAtRandom.bind(this)}>
|
||||
Place at Random
|
||||
</Text>
|
||||
<Text onPress={this.select.bind(this, 0, length)}>
|
||||
Select All
|
||||
</Text>
|
||||
<Text onPress={this.selectRandom.bind(this)}>
|
||||
Select Random
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
page: {
|
||||
paddingBottom: 300,
|
||||
},
|
||||
default: {
|
||||
height: 26,
|
||||
borderWidth: 0.5,
|
||||
borderColor: '#0f0f0f',
|
||||
flex: 1,
|
||||
fontSize: 13,
|
||||
padding: 4,
|
||||
},
|
||||
multiline: {
|
||||
borderWidth: 0.5,
|
||||
borderColor: '#0f0f0f',
|
||||
flex: 1,
|
||||
fontSize: 13,
|
||||
height: 50,
|
||||
padding: 4,
|
||||
marginBottom: 4,
|
||||
},
|
||||
multilineWithFontStyles: {
|
||||
color: 'blue',
|
||||
fontWeight: 'bold',
|
||||
fontSize: 18,
|
||||
fontFamily: 'Cochin',
|
||||
height: 60,
|
||||
},
|
||||
multilineChild: {
|
||||
width: 50,
|
||||
height: 40,
|
||||
position: 'absolute',
|
||||
right: 5,
|
||||
backgroundColor: 'red',
|
||||
},
|
||||
eventLabel: {
|
||||
margin: 3,
|
||||
fontSize: 12,
|
||||
},
|
||||
labelContainer: {
|
||||
flexDirection: 'row',
|
||||
marginVertical: 2,
|
||||
flex: 1,
|
||||
},
|
||||
label: {
|
||||
width: 115,
|
||||
alignItems: 'flex-end',
|
||||
marginRight: 10,
|
||||
paddingTop: 2,
|
||||
},
|
||||
rewriteContainer: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
},
|
||||
remainder: {
|
||||
textAlign: 'right',
|
||||
width: 24,
|
||||
},
|
||||
hashtag: {
|
||||
color: 'blue',
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
});
|
||||
|
||||
const examples = [
|
||||
{
|
||||
title: 'Auto-focus',
|
||||
render: function() {
|
||||
return (
|
||||
<View>
|
||||
<TextInput
|
||||
autoFocus={true}
|
||||
style={styles.default}
|
||||
accessibilityLabel="I am the accessibility label for text input"
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: "Live Re-Write (<sp> -> '_') + maxLength",
|
||||
render: function() {
|
||||
return <RewriteExample />;
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Live Re-Write (no spaces allowed)',
|
||||
render: function() {
|
||||
return <RewriteExampleInvalidCharacters />;
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Auto-capitalize',
|
||||
render: function() {
|
||||
return (
|
||||
<View>
|
||||
<WithLabel label="none">
|
||||
<TextInput
|
||||
autoCapitalize="none"
|
||||
style={styles.default}
|
||||
/>
|
||||
</WithLabel>
|
||||
<WithLabel label="sentences">
|
||||
<TextInput
|
||||
autoCapitalize="sentences"
|
||||
style={styles.default}
|
||||
/>
|
||||
</WithLabel>
|
||||
<WithLabel label="words">
|
||||
<TextInput
|
||||
autoCapitalize="words"
|
||||
style={styles.default}
|
||||
/>
|
||||
</WithLabel>
|
||||
<WithLabel label="characters">
|
||||
<TextInput
|
||||
autoCapitalize="characters"
|
||||
style={styles.default}
|
||||
/>
|
||||
</WithLabel>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Auto-correct',
|
||||
render: function() {
|
||||
return (
|
||||
<View>
|
||||
<WithLabel label="true">
|
||||
<TextInput autoCorrect={true} style={styles.default} />
|
||||
</WithLabel>
|
||||
<WithLabel label="false">
|
||||
<TextInput autoCorrect={false} style={styles.default} />
|
||||
</WithLabel>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Keyboard types',
|
||||
render: function() {
|
||||
var keyboardTypes = [
|
||||
'default',
|
||||
//'ascii-capable',
|
||||
//'numbers-and-punctuation',
|
||||
'url',
|
||||
'number-pad',
|
||||
'phone-pad',
|
||||
//'name-phone-pad',
|
||||
'email-address',
|
||||
//'decimal-pad',
|
||||
//'twitter',
|
||||
'web-search',
|
||||
'numeric',
|
||||
];
|
||||
var examples = keyboardTypes.map((type) => {
|
||||
return (
|
||||
<WithLabel key={type} label={type}>
|
||||
<TextInput
|
||||
keyboardType={type}
|
||||
style={styles.default}
|
||||
/>
|
||||
</WithLabel>
|
||||
);
|
||||
});
|
||||
return <View>{examples}</View>;
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Keyboard appearance',
|
||||
render: function() {
|
||||
var keyboardAppearance = [
|
||||
'default',
|
||||
'light',
|
||||
'dark',
|
||||
];
|
||||
var examples = keyboardAppearance.map((type) => {
|
||||
return (
|
||||
<WithLabel key={type} label={type}>
|
||||
<TextInput
|
||||
keyboardAppearance={type}
|
||||
style={styles.default}
|
||||
/>
|
||||
</WithLabel>
|
||||
);
|
||||
});
|
||||
return <View>{examples}</View>;
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Return key types',
|
||||
render: function() {
|
||||
var returnKeyTypes = [
|
||||
'default',
|
||||
'go',
|
||||
'google',
|
||||
'join',
|
||||
'next',
|
||||
'route',
|
||||
'search',
|
||||
'send',
|
||||
'yahoo',
|
||||
'done',
|
||||
'emergency-call',
|
||||
];
|
||||
var examples = returnKeyTypes.map((type) => {
|
||||
return (
|
||||
<WithLabel key={type} label={type}>
|
||||
<TextInput
|
||||
returnKeyType={type}
|
||||
style={styles.default}
|
||||
/>
|
||||
</WithLabel>
|
||||
);
|
||||
});
|
||||
return <View>{examples}</View>;
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Enable return key automatically',
|
||||
render: function() {
|
||||
return (
|
||||
<View>
|
||||
<WithLabel label="true">
|
||||
<TextInput enablesReturnKeyAutomatically={true} style={styles.default} />
|
||||
</WithLabel>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Secure text entry',
|
||||
render: function() {
|
||||
return (
|
||||
<View>
|
||||
<WithLabel label="true">
|
||||
<TextInput secureTextEntry={true} style={styles.default} defaultValue="abc" />
|
||||
</WithLabel>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Event handling',
|
||||
render: function(): React.Element<any> { return <TextEventsExample />; },
|
||||
},
|
||||
{
|
||||
title: 'Colored input text',
|
||||
render: function() {
|
||||
return (
|
||||
<View>
|
||||
<TextInput
|
||||
style={[styles.default, {color: 'blue'}]}
|
||||
defaultValue="Blue"
|
||||
/>
|
||||
<TextInput
|
||||
style={[styles.default, {color: 'green'}]}
|
||||
defaultValue="Green"
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Colored highlight/cursor for text input',
|
||||
render: function() {
|
||||
return (
|
||||
<View>
|
||||
<TextInput
|
||||
style={styles.default}
|
||||
selectionColor={"green"}
|
||||
defaultValue="Highlight me"
|
||||
/>
|
||||
<TextInput
|
||||
style={styles.default}
|
||||
selectionColor={"rgba(86, 76, 205, 1)"}
|
||||
defaultValue="Highlight me"
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Clear button mode',
|
||||
render: function () {
|
||||
return (
|
||||
<View>
|
||||
<WithLabel label="never">
|
||||
<TextInput
|
||||
style={styles.default}
|
||||
clearButtonMode="never"
|
||||
/>
|
||||
</WithLabel>
|
||||
<WithLabel label="while editing">
|
||||
<TextInput
|
||||
style={styles.default}
|
||||
clearButtonMode="while-editing"
|
||||
/>
|
||||
</WithLabel>
|
||||
<WithLabel label="unless editing">
|
||||
<TextInput
|
||||
style={styles.default}
|
||||
clearButtonMode="unless-editing"
|
||||
/>
|
||||
</WithLabel>
|
||||
<WithLabel label="always">
|
||||
<TextInput
|
||||
style={styles.default}
|
||||
clearButtonMode="always"
|
||||
/>
|
||||
</WithLabel>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Clear and select',
|
||||
render: function() {
|
||||
return (
|
||||
<View>
|
||||
<WithLabel label="clearTextOnFocus">
|
||||
<TextInput
|
||||
placeholder="text is cleared on focus"
|
||||
defaultValue="text is cleared on focus"
|
||||
style={styles.default}
|
||||
clearTextOnFocus={true}
|
||||
/>
|
||||
</WithLabel>
|
||||
<WithLabel label="selectTextOnFocus">
|
||||
<TextInput
|
||||
placeholder="text is selected on focus"
|
||||
defaultValue="text is selected on focus"
|
||||
style={styles.default}
|
||||
selectTextOnFocus={true}
|
||||
/>
|
||||
</WithLabel>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Blur on submit',
|
||||
render: function(): React.Element<any> { return <BlurOnSubmitExample />; },
|
||||
},
|
||||
{
|
||||
title: 'Multiline blur on submit',
|
||||
render: function() {
|
||||
return (
|
||||
<View>
|
||||
<TextInput
|
||||
style={styles.multiline}
|
||||
placeholder="blurOnSubmit = true"
|
||||
returnKeyType="next"
|
||||
blurOnSubmit={true}
|
||||
multiline={true}
|
||||
onSubmitEditing={event => alert(event.nativeEvent.text)}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Multiline',
|
||||
render: function() {
|
||||
return (
|
||||
<View>
|
||||
<TextInput
|
||||
placeholder="multiline text input"
|
||||
multiline={true}
|
||||
style={styles.multiline}
|
||||
/>
|
||||
<TextInput
|
||||
placeholder="multiline text input with font styles and placeholder"
|
||||
multiline={true}
|
||||
clearTextOnFocus={true}
|
||||
autoCorrect={true}
|
||||
autoCapitalize="words"
|
||||
placeholderTextColor="red"
|
||||
keyboardType="url"
|
||||
style={[styles.multiline, styles.multilineWithFontStyles]}
|
||||
/>
|
||||
<TextInput
|
||||
placeholder="multiline text input with max length"
|
||||
maxLength={5}
|
||||
multiline={true}
|
||||
style={styles.multiline}
|
||||
/>
|
||||
<TextInput
|
||||
placeholder="uneditable multiline text input"
|
||||
editable={false}
|
||||
multiline={true}
|
||||
style={styles.multiline}
|
||||
/>
|
||||
<TextInput
|
||||
defaultValue="uneditable multiline text input with phone number detection: 88888888."
|
||||
editable={false}
|
||||
multiline={true}
|
||||
style={styles.multiline}
|
||||
dataDetectorTypes="phoneNumber"
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Number of lines',
|
||||
render: function() {
|
||||
return (
|
||||
<View>
|
||||
<TextInput
|
||||
multiline={true}
|
||||
numberOfLines={4}
|
||||
style={[ styles.multiline, { height: 'auto' } ]}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Auto-expanding',
|
||||
render: function() {
|
||||
return (
|
||||
<View>
|
||||
<AutoExpandingTextInput
|
||||
placeholder="height increases with content"
|
||||
enablesReturnKeyAutomatically={true}
|
||||
returnKeyType="default"
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Attributed text',
|
||||
render: function() {
|
||||
return <TokenizedTextExample />;
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Text selection & cursor placement',
|
||||
render: function() {
|
||||
return (
|
||||
<View>
|
||||
<SelectionExample
|
||||
style={styles.default}
|
||||
value="text selection can be changed"
|
||||
/>
|
||||
<SelectionExample
|
||||
multiline
|
||||
style={styles.multiline}
|
||||
value={"multiline text selection\ncan also be changed"}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'TextInput maxLength',
|
||||
render: function() {
|
||||
return (
|
||||
<View>
|
||||
<WithLabel label="maxLength: 5">
|
||||
<TextInput
|
||||
maxLength={5}
|
||||
style={styles.default}
|
||||
/>
|
||||
</WithLabel>
|
||||
<WithLabel label="maxLength: 5 with placeholder">
|
||||
<TextInput
|
||||
maxLength={5}
|
||||
placeholder="ZIP code entry"
|
||||
style={styles.default}
|
||||
/>
|
||||
</WithLabel>
|
||||
<WithLabel label="maxLength: 5 with default value already set">
|
||||
<TextInput
|
||||
maxLength={5}
|
||||
defaultValue="94025"
|
||||
style={styles.default}
|
||||
/>
|
||||
</WithLabel>
|
||||
<WithLabel label="maxLength: 5 with very long default value already set">
|
||||
<TextInput
|
||||
maxLength={5}
|
||||
defaultValue="9402512345"
|
||||
style={styles.default}
|
||||
/>
|
||||
</WithLabel>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
examples.forEach((example) => {
|
||||
storiesOf('component: TextInput', module)
|
||||
.add(example.title, () => example.render())
|
||||
});
|
||||
|
||||
@@ -23,7 +23,7 @@ var {
|
||||
AppRegistry,
|
||||
StyleSheet,
|
||||
Text,
|
||||
TouchableBounce,
|
||||
TouchableOpacity,
|
||||
View,
|
||||
} = ReactNative;
|
||||
|
||||
@@ -139,9 +139,9 @@ class GameEndOverlay extends React.Component {
|
||||
return (
|
||||
<View style={styles.overlay}>
|
||||
<Text style={styles.overlayMessage}>{message}</Text>
|
||||
<TouchableBounce onPress={this.props.onRestart} style={styles.tryAgain}>
|
||||
<TouchableOpacity onPress={this.props.onRestart} style={styles.tryAgain}>
|
||||
<Text style={styles.tryAgainText}>Try Again?</Text>
|
||||
</TouchableBounce>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
const webpack = require('webpack')
|
||||
|
||||
const testEntry = 'src/tests.webpack.js'
|
||||
|
||||
module.exports = function (config) {
|
||||
config.set({
|
||||
browsers: process.env.TRAVIS ? [ 'Firefox' ] : [ 'Chrome' ],
|
||||
browserNoActivityTimeout: 60000,
|
||||
client: {
|
||||
captureConsole: true,
|
||||
mocha: { ui: 'tdd' },
|
||||
useIframe: true
|
||||
},
|
||||
files: [
|
||||
testEntry
|
||||
],
|
||||
frameworks: [ 'mocha' ],
|
||||
plugins: [
|
||||
'karma-chrome-launcher',
|
||||
'karma-firefox-launcher',
|
||||
'karma-mocha',
|
||||
'karma-mocha-reporter',
|
||||
'karma-sourcemap-loader',
|
||||
'karma-webpack'
|
||||
],
|
||||
preprocessors: {
|
||||
[testEntry]: [ 'webpack', 'sourcemap' ]
|
||||
},
|
||||
reporters: process.env.TRAVIS ? [ 'dots' ] : [ 'mocha' ],
|
||||
singleRun: true,
|
||||
webpack: {
|
||||
devtool: 'inline-source-map',
|
||||
// required by 'enzyme'
|
||||
externals: {
|
||||
'cheerio': 'window',
|
||||
'react/addons': true,
|
||||
'react/lib/ExecutionEnvironment': true,
|
||||
'react/lib/ReactContext': true
|
||||
},
|
||||
module: {
|
||||
loaders: [
|
||||
{
|
||||
test: /\.js$/,
|
||||
exclude: /node_modules/,
|
||||
loader: 'babel',
|
||||
query: { cacheDirectory: true }
|
||||
}
|
||||
]
|
||||
},
|
||||
plugins: [
|
||||
new webpack.DefinePlugin({
|
||||
'process.env': {
|
||||
NODE_ENV: JSON.stringify('test')
|
||||
}
|
||||
})
|
||||
]
|
||||
},
|
||||
webpackServer: {
|
||||
noInfo: true
|
||||
}
|
||||
})
|
||||
}
|
||||
44
package.json
44
package.json
@@ -1,10 +1,12 @@
|
||||
{
|
||||
"name": "react-native-web",
|
||||
"version": "0.0.47",
|
||||
"version": "0.0.60",
|
||||
"description": "React Native for Web",
|
||||
"main": "dist/index.js",
|
||||
"files": [
|
||||
"dist"
|
||||
"dist",
|
||||
"src",
|
||||
"!**/__tests__"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "del ./dist && mkdir dist && babel src -d dist --ignore **/__tests__",
|
||||
@@ -14,16 +16,19 @@
|
||||
"examples": "start-storybook -p 9001 -c ./examples/.storybook --dont-track",
|
||||
"lint": "eslint src",
|
||||
"prepublish": "npm run build && npm run build:umd",
|
||||
"test": "karma start karma.config.js",
|
||||
"test:watch": "npm run test -- --no-single-run"
|
||||
"test": "npm run lint && npm run test:jest",
|
||||
"test:jest": "jest",
|
||||
"test:watch": "npm run test:jest -- --watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"animated": "^0.1.3",
|
||||
"array-find-index": "^1.0.2",
|
||||
"babel-runtime": "^6.11.6",
|
||||
"debounce": "^1.0.0",
|
||||
"deep-assign": "^2.0.0",
|
||||
"fbjs": "^0.8.4",
|
||||
"inline-style-prefixer": "^2.0.1",
|
||||
"lodash": "^4.15.0",
|
||||
"react-dom": "^15.3.1",
|
||||
"react-dom": "~15.3.2",
|
||||
"react-textarea-autosize": "^4.0.4",
|
||||
"react-timer-mixin": "^0.13.3"
|
||||
},
|
||||
@@ -33,6 +38,7 @@
|
||||
"babel-core": "^6.14.0",
|
||||
"babel-eslint": "^6.1.2",
|
||||
"babel-loader": "^6.2.5",
|
||||
"babel-plugin-transform-react-remove-prop-types": "^0.2.11",
|
||||
"babel-preset-react-native": "^1.9.0",
|
||||
"del-cli": "^0.2.0",
|
||||
"enzyme": "^2.4.1",
|
||||
@@ -41,23 +47,17 @@
|
||||
"eslint-plugin-promise": "^2.0.1",
|
||||
"eslint-plugin-react": "^6.1.2",
|
||||
"file-loader": "^0.9.0",
|
||||
"karma": "^1.2.0",
|
||||
"karma-browserstack-launcher": "^1.0.1",
|
||||
"karma-chrome-launcher": "^2.0.0",
|
||||
"karma-firefox-launcher": "^1.0.0",
|
||||
"karma-mocha": "^1.1.1",
|
||||
"karma-mocha-reporter": "^2.1.0",
|
||||
"karma-sourcemap-loader": "^0.3.7",
|
||||
"karma-webpack": "^1.8.0",
|
||||
"mocha": "^3.0.2",
|
||||
"jest": "^16.0.2",
|
||||
"node-libs-browser": "^0.5.3",
|
||||
"react": "^15.3.1",
|
||||
"react-addons-test-utils": "^15.3.1",
|
||||
"react": "~15.3.2",
|
||||
"react-addons-test-utils": "~15.3.2",
|
||||
"react-test-renderer": "~15.3.2",
|
||||
"url-loader": "^0.5.7",
|
||||
"webpack": "^1.13.2"
|
||||
"webpack": "^1.13.2",
|
||||
"webpack-bundle-analyzer": "^1.5.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^15.3.1"
|
||||
"react": "~15.3.2"
|
||||
},
|
||||
"author": "Nicolas Gallagher",
|
||||
"license": "BSD-3-Clause",
|
||||
@@ -73,5 +73,9 @@
|
||||
"react-component",
|
||||
"react-native",
|
||||
"web"
|
||||
]
|
||||
],
|
||||
"jest": {
|
||||
"testEnvironment": "jsdom",
|
||||
"timers": "fake"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
/* eslint-env mocha */
|
||||
/* eslint-env jasmine, jest */
|
||||
|
||||
import assert from 'assert';
|
||||
import { prerenderApplication } from '../renderApplication';
|
||||
import { getApplication } from '../renderApplication';
|
||||
import React from 'react';
|
||||
|
||||
const component = () => <div />;
|
||||
|
||||
suite('apis/AppRegistry/renderApplication', () => {
|
||||
test('prerenderApplication', () => {
|
||||
const { html, styleElement } = prerenderApplication(component, {});
|
||||
describe('apis/AppRegistry/renderApplication', () => {
|
||||
test('getApplication', () => {
|
||||
const { element, stylesheet } = getApplication(component, {});
|
||||
|
||||
assert.ok(html.indexOf('<div ') > -1);
|
||||
assert.equal(styleElement.type, 'style');
|
||||
expect(element).toBeTruthy();
|
||||
expect(stylesheet.type).toEqual('style');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
|
||||
import { Component } from 'react';
|
||||
import invariant from 'fbjs/lib/invariant';
|
||||
import ReactDOM from 'react-dom';
|
||||
import renderApplication, { prerenderApplication } from './renderApplication';
|
||||
import { unmountComponentAtNode } from 'react/lib/ReactMount';
|
||||
import renderApplication, { getApplication } from './renderApplication';
|
||||
|
||||
const runnables = {};
|
||||
|
||||
@@ -29,20 +29,20 @@ class AppRegistry {
|
||||
return Object.keys(runnables);
|
||||
}
|
||||
|
||||
static prerenderApplication(appKey: string, appParameters?: Object): string {
|
||||
static getApplication(appKey: string, appParameters?: Object): string {
|
||||
invariant(
|
||||
runnables[appKey] && runnables[appKey].prerender,
|
||||
runnables[appKey] && runnables[appKey].getApplication,
|
||||
`Application ${appKey} has not been registered. ` +
|
||||
'This is either due to an import error during initialization or failure to call AppRegistry.registerComponent.'
|
||||
);
|
||||
|
||||
return runnables[appKey].prerender(appParameters);
|
||||
return runnables[appKey].getApplication(appParameters);
|
||||
}
|
||||
|
||||
static registerComponent(appKey: string, getComponentFunc: ComponentProvider): string {
|
||||
runnables[appKey] = {
|
||||
run: ({ initialProps, rootTag }) => renderApplication(getComponentFunc(), initialProps, rootTag),
|
||||
prerender: ({ initialProps } = {}) => prerenderApplication(getComponentFunc(), initialProps)
|
||||
getApplication: ({ initialProps } = {}) => getApplication(getComponentFunc(), initialProps),
|
||||
run: ({ initialProps, rootTag }) => renderApplication(getComponentFunc(), initialProps, rootTag)
|
||||
};
|
||||
return appKey;
|
||||
}
|
||||
@@ -85,7 +85,7 @@ class AppRegistry {
|
||||
}
|
||||
|
||||
static unmountApplicationComponentAtRootTag(rootTag) {
|
||||
ReactDOM.unmountComponentAtNode(rootTag);
|
||||
unmountComponentAtNode(rootTag);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,8 +7,7 @@
|
||||
*/
|
||||
|
||||
import invariant from 'fbjs/lib/invariant';
|
||||
import ReactDOM from 'react-dom';
|
||||
import ReactDOMServer from 'react-dom/server';
|
||||
import { render } from 'react/lib/ReactMount';
|
||||
import ReactNativeApp from './ReactNativeApp';
|
||||
import StyleSheet from '../../apis/StyleSheet';
|
||||
import React, { Component } from 'react';
|
||||
@@ -23,17 +22,16 @@ export default function renderApplication(RootComponent: Component, initialProps
|
||||
rootTag={rootTag}
|
||||
/>
|
||||
);
|
||||
ReactDOM.render(component, rootTag);
|
||||
render(component, rootTag);
|
||||
}
|
||||
|
||||
export function prerenderApplication(RootComponent: Component, initialProps: Object): string {
|
||||
const component = (
|
||||
export function getApplication(RootComponent: Component, initialProps: Object): Object {
|
||||
const element = (
|
||||
<ReactNativeApp
|
||||
initialProps={initialProps}
|
||||
rootComponent={RootComponent}
|
||||
/>
|
||||
);
|
||||
const html = ReactDOMServer.renderToString(component);
|
||||
const styleElement = StyleSheet.render();
|
||||
return { html, styleElement };
|
||||
const stylesheet = StyleSheet.render();
|
||||
return { element, stylesheet };
|
||||
}
|
||||
|
||||
@@ -1,31 +1,30 @@
|
||||
/* eslint-env mocha */
|
||||
/* eslint-env jasmine, jest */
|
||||
|
||||
import AppState from '..';
|
||||
import assert from 'assert';
|
||||
|
||||
suite('apis/AppState', () => {
|
||||
describe('apis/AppState', () => {
|
||||
const handler = () => {};
|
||||
|
||||
teardown(() => {
|
||||
afterEach(() => {
|
||||
try { AppState.removeEventListener('change', handler); } catch (e) {}
|
||||
});
|
||||
|
||||
suite('addEventListener', () => {
|
||||
describe('addEventListener', () => {
|
||||
test('throws if the provided "eventType" is not supported', () => {
|
||||
assert.throws(() => AppState.addEventListener('foo', handler));
|
||||
assert.doesNotThrow(() => AppState.addEventListener('change', handler));
|
||||
expect(() => AppState.addEventListener('foo', handler)).toThrow();
|
||||
expect(() => AppState.addEventListener('change', handler)).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
suite('removeEventListener', () => {
|
||||
describe('removeEventListener', () => {
|
||||
test('throws if the handler is not registered', () => {
|
||||
assert.throws(() => AppState.removeEventListener('change', handler));
|
||||
expect(() => AppState.removeEventListener('change', handler)).toThrow();
|
||||
});
|
||||
|
||||
test('throws if the provided "eventType" is not supported', () => {
|
||||
AppState.addEventListener('change', handler);
|
||||
assert.throws(() => AppState.removeEventListener('foo', handler));
|
||||
assert.doesNotThrow(() => AppState.removeEventListener('change', handler));
|
||||
expect(() => AppState.removeEventListener('foo', handler)).toThrow();
|
||||
expect(() => AppState.removeEventListener('change', handler)).not.toThrow();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import ExecutionEnvironment from 'fbjs/lib/ExecutionEnvironment';
|
||||
import findIndex from 'lodash/findIndex';
|
||||
import findIndex from 'array-find-index';
|
||||
import invariant from 'fbjs/lib/invariant';
|
||||
|
||||
const EVENT_TYPES = [ 'change' ];
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
exports[`apis/AsyncStorage mergeLocalStorageItem should have same behavior as react-native 1`] = `
|
||||
Object {
|
||||
"age": 31,
|
||||
"name": "Chris",
|
||||
"traits": Object {
|
||||
"eyes": "blue",
|
||||
"hair": "brown",
|
||||
"shoe_size": 10,
|
||||
},
|
||||
}
|
||||
`;
|
||||
@@ -1,5 +1,4 @@
|
||||
/* eslint-env mocha */
|
||||
import assert from 'assert';
|
||||
/* eslint-env jasmine, jest */
|
||||
import AsyncStorage from '..';
|
||||
|
||||
const waterfall = (fns, cb) => {
|
||||
@@ -20,9 +19,21 @@ const waterfall = (fns, cb) => {
|
||||
_waterfall();
|
||||
};
|
||||
|
||||
suite('apis/AsyncStorage', () => {
|
||||
suite('mergeLocalStorageItem', () => {
|
||||
const obj = {};
|
||||
const mockLocalStorage = {
|
||||
getItem(key) {
|
||||
return obj[key];
|
||||
},
|
||||
setItem(key, value) {
|
||||
obj[key] = value;
|
||||
}
|
||||
};
|
||||
const originalLocalStorage = window.localStorage;
|
||||
|
||||
describe('apis/AsyncStorage', () => {
|
||||
describe('mergeLocalStorageItem', () => {
|
||||
test('should have same behavior as react-native', (done) => {
|
||||
window.localStorage = mockLocalStorage;
|
||||
// https://facebook.github.io/react-native/docs/asyncstorage.html
|
||||
const UID123_object = {
|
||||
name: 'Chris',
|
||||
@@ -33,6 +44,7 @@ suite('apis/AsyncStorage', () => {
|
||||
age: 31,
|
||||
traits: { eyes: 'blue', shoe_size: 10 }
|
||||
};
|
||||
|
||||
waterfall([
|
||||
(cb) => {
|
||||
AsyncStorage.setItem('UID123', JSON.stringify(UID123_object))
|
||||
@@ -52,12 +64,9 @@ suite('apis/AsyncStorage', () => {
|
||||
.catch(cb);
|
||||
}
|
||||
], (err, result) => {
|
||||
assert.equal(err, null);
|
||||
assert.deepEqual(result, {
|
||||
'name': 'Chris', 'age': 31, 'traits': {
|
||||
'shoe_size': 10, 'hair': 'brown', 'eyes': 'blue'
|
||||
}
|
||||
});
|
||||
expect(err).toEqual(null);
|
||||
expect(result).toMatchSnapshot();
|
||||
window.localStorage = originalLocalStorage;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*/
|
||||
import merge from 'lodash/merge';
|
||||
import merge from 'deep-assign';
|
||||
|
||||
const mergeLocalStorageItem = (key, value) => {
|
||||
const oldValue = window.localStorage.getItem(key);
|
||||
|
||||
21
src/apis/Clipboard/index.js
Normal file
21
src/apis/Clipboard/index.js
Normal file
@@ -0,0 +1,21 @@
|
||||
class Clipboard {
|
||||
static getString() {
|
||||
return Promise.resolve('');
|
||||
}
|
||||
|
||||
static setString(text) {
|
||||
let success = false;
|
||||
const textField = document.createElement('textarea');
|
||||
textField.innerText = text;
|
||||
document.body.appendChild(textField);
|
||||
textField.select();
|
||||
try {
|
||||
document.execCommand('copy');
|
||||
success = true;
|
||||
} catch (e) {}
|
||||
document.body.removeChild(textField);
|
||||
return success;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Clipboard;
|
||||
@@ -1,5 +1,5 @@
|
||||
/* eslint-env mocha */
|
||||
/* eslint-env jasmine, jest */
|
||||
|
||||
suite('apis/Dimensions', () => {
|
||||
describe('apis/Dimensions', () => {
|
||||
test.skip('NO TEST COVERAGE', () => {});
|
||||
});
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import debounce from 'lodash/debounce';
|
||||
import debounce from 'debounce';
|
||||
import ExecutionEnvironment from 'fbjs/lib/ExecutionEnvironment';
|
||||
import invariant from 'fbjs/lib/invariant';
|
||||
|
||||
|
||||
@@ -1,45 +1,44 @@
|
||||
/* eslint-env mocha */
|
||||
/* eslint-env jasmine, jest */
|
||||
|
||||
import assert from 'assert';
|
||||
import I18nManager from '..';
|
||||
|
||||
suite('apis/I18nManager', () => {
|
||||
suite('when RTL not enabled', () => {
|
||||
setup(() => {
|
||||
describe('apis/I18nManager', () => {
|
||||
describe('when RTL not enabled', () => {
|
||||
beforeEach(() => {
|
||||
I18nManager.setPreferredLanguageRTL(false);
|
||||
});
|
||||
|
||||
test('is "false" by default', () => {
|
||||
assert.equal(I18nManager.isRTL, false);
|
||||
assert.equal(document.documentElement.getAttribute('dir'), 'ltr');
|
||||
expect(I18nManager.isRTL).toEqual(false);
|
||||
expect(document.documentElement.getAttribute('dir')).toEqual('ltr');
|
||||
});
|
||||
|
||||
test('is "true" when forced', () => {
|
||||
I18nManager.forceRTL(true);
|
||||
assert.equal(I18nManager.isRTL, true);
|
||||
assert.equal(document.documentElement.getAttribute('dir'), 'rtl');
|
||||
expect(I18nManager.isRTL).toEqual(true);
|
||||
expect(document.documentElement.getAttribute('dir')).toEqual('rtl');
|
||||
I18nManager.forceRTL(false);
|
||||
});
|
||||
});
|
||||
|
||||
suite('when RTL is enabled', () => {
|
||||
setup(() => {
|
||||
describe('when RTL is enabled', () => {
|
||||
beforeEach(() => {
|
||||
I18nManager.setPreferredLanguageRTL(true);
|
||||
});
|
||||
|
||||
teardown(() => {
|
||||
afterEach(() => {
|
||||
I18nManager.setPreferredLanguageRTL(false);
|
||||
});
|
||||
|
||||
test('is "true" by default', () => {
|
||||
assert.equal(I18nManager.isRTL, true);
|
||||
assert.equal(document.documentElement.getAttribute('dir'), 'rtl');
|
||||
expect(I18nManager.isRTL).toEqual(true);
|
||||
expect(document.documentElement.getAttribute('dir')).toEqual('rtl');
|
||||
});
|
||||
|
||||
test('is "false" when not allowed', () => {
|
||||
I18nManager.allowRTL(false);
|
||||
assert.equal(I18nManager.isRTL, false);
|
||||
assert.equal(document.documentElement.getAttribute('dir'), 'ltr');
|
||||
expect(I18nManager.isRTL).toEqual(false);
|
||||
expect(document.documentElement.getAttribute('dir')).toEqual('ltr');
|
||||
I18nManager.allowRTL(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -6,13 +6,12 @@
|
||||
*/
|
||||
|
||||
import invariant from 'fbjs/lib/invariant';
|
||||
import keyMirror from 'fbjs/lib/keyMirror';
|
||||
|
||||
const InteractionManager = {
|
||||
Events: keyMirror({
|
||||
interactionStart: true,
|
||||
interactionComplete: true
|
||||
}),
|
||||
Events: {
|
||||
interactionStart: 'interactionStart',
|
||||
interactionComplete: 'interactionComplete'
|
||||
},
|
||||
|
||||
/**
|
||||
* Schedule a function to run after all interactions have completed.
|
||||
|
||||
36
src/apis/Linking/index.js
Normal file
36
src/apis/Linking/index.js
Normal file
@@ -0,0 +1,36 @@
|
||||
const Linking = {
|
||||
addEventListener() {},
|
||||
removeEventListener() {},
|
||||
canOpenUrl() { return true; },
|
||||
getInitialUrl() { return ''; },
|
||||
openURL(url) {
|
||||
iframeOpen(url);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Tabs opened using JavaScript may redirect the parent tab using
|
||||
* `window.opener.location`, ignoring cross-origin restrictions and enabling
|
||||
* phishing attacks.
|
||||
*
|
||||
* Safari requires that we open the url by injecting a hidden iframe that calls
|
||||
* window.open(), then removes the iframe from the DOM.
|
||||
*
|
||||
* https://mathiasbynens.github.io/rel-noopener/
|
||||
*/
|
||||
const iframeOpen = (url) => {
|
||||
const iframe = document.createElement('iframe');
|
||||
iframe.style.display = 'none';
|
||||
document.body.appendChild(iframe);
|
||||
|
||||
const iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
|
||||
const script = iframeDoc.createElement('script');
|
||||
script.text = `
|
||||
window.parent = null; window.top = null; window.frameElement = null;
|
||||
var child = window.open("${url}"); child.opener = null;
|
||||
`;
|
||||
iframeDoc.body.appendChild(script);
|
||||
document.body.removeChild(iframe);
|
||||
};
|
||||
|
||||
module.exports = Linking;
|
||||
@@ -1,5 +1,32 @@
|
||||
/* eslint-env mocha */
|
||||
/* eslint-env jasmine, jest */
|
||||
|
||||
suite('apis/NetInfo', () => {
|
||||
test.skip('NO TEST COVERAGE', () => {});
|
||||
import NetInfo from '..';
|
||||
|
||||
describe('apis/NetInfo', () => {
|
||||
describe('isConnected', () => {
|
||||
const handler = () => {};
|
||||
|
||||
afterEach(() => {
|
||||
try { NetInfo.isConnected.removeEventListener('change', handler); } catch (e) {}
|
||||
});
|
||||
|
||||
describe('addEventListener', () => {
|
||||
test('throws if the provided "eventType" is not supported', () => {
|
||||
expect(() => NetInfo.isConnected.addEventListener('foo', handler)).toThrow();
|
||||
expect(() => NetInfo.isConnected.addEventListener('change', handler)).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('removeEventListener', () => {
|
||||
test('throws if the handler is not registered', () => {
|
||||
expect(() => NetInfo.isConnected.removeEventListener('change', handler)).toThrow;
|
||||
});
|
||||
|
||||
test('throws if the provided "eventType" is not supported', () => {
|
||||
NetInfo.isConnected.addEventListener('change', handler);
|
||||
expect(() => NetInfo.isConnected.removeEventListener('foo', handler)).toThrow;
|
||||
expect(() => NetInfo.isConnected.removeEventListener('change', handler)).not.toThrow;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
*/
|
||||
|
||||
import ExecutionEnvironment from 'fbjs/lib/ExecutionEnvironment';
|
||||
import findIndex from 'array-find-index';
|
||||
import invariant from 'fbjs/lib/invariant';
|
||||
|
||||
const connection = ExecutionEnvironment.canUseDOM && (
|
||||
@@ -17,6 +18,8 @@ const connection = ExecutionEnvironment.canUseDOM && (
|
||||
|
||||
const eventTypes = [ 'change' ];
|
||||
|
||||
const connectionListeners = [];
|
||||
|
||||
/**
|
||||
* Navigator online: https://developer.mozilla.org/en-US/docs/Web/API/NavigatorOnLine/onLine
|
||||
* Network Connection API: https://developer.mozilla.org/en-US/docs/Web/API/NetworkInformation
|
||||
@@ -56,8 +59,12 @@ const NetInfo = {
|
||||
isConnected: {
|
||||
addEventListener(type: string, handler: Function): { remove: () => void } {
|
||||
invariant(eventTypes.indexOf(type) !== -1, 'Trying to subscribe to unknown event: "%s"', type);
|
||||
window.addEventListener('online', handler.bind(null, true), false);
|
||||
window.addEventListener('offline', handler.bind(null, false), false);
|
||||
const onlineCallback = () => handler(true);
|
||||
const offlineCallback = () => handler(false);
|
||||
connectionListeners.push([ handler, onlineCallback, offlineCallback ]);
|
||||
|
||||
window.addEventListener('online', onlineCallback, false);
|
||||
window.addEventListener('offline', offlineCallback, false);
|
||||
|
||||
return {
|
||||
remove: () => NetInfo.isConnected.removeEventListener(type, handler)
|
||||
@@ -66,8 +73,15 @@ const NetInfo = {
|
||||
|
||||
removeEventListener(type: string, handler: Function): void {
|
||||
invariant(eventTypes.indexOf(type) !== -1, 'Trying to subscribe to unknown event: "%s"', type);
|
||||
window.removeEventListener('online', handler.bind(null, true), false);
|
||||
window.removeEventListener('offline', handler.bind(null, false), false);
|
||||
|
||||
const listenerIndex = findIndex(connectionListeners, (pair) => pair[0] === handler);
|
||||
invariant(listenerIndex !== -1, 'Trying to remove NetInfo connection listener for unregistered handler');
|
||||
const [ , onlineCallback, offlineCallback ] = connectionListeners[listenerIndex];
|
||||
|
||||
window.removeEventListener('online', onlineCallback, false);
|
||||
window.removeEventListener('offline', offlineCallback, false);
|
||||
|
||||
connectionListeners.splice(listenerIndex, 1);
|
||||
},
|
||||
|
||||
fetch(): Promise {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* eslint-env mocha */
|
||||
/* eslint-env jasmine, jest */
|
||||
|
||||
suite('apis/PixelRatio', () => {
|
||||
describe('apis/PixelRatio', () => {
|
||||
test.skip('NO TEST COVERAGE', () => {});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
exports[`apis/StyleSheet/expandStyle shortform -> longform 1`] = `
|
||||
Object {
|
||||
"borderBottomColor": "white",
|
||||
"borderBottomStyle": "solid",
|
||||
"borderBottomWidth": "1px",
|
||||
"borderLeftStyle": "solid",
|
||||
"borderLeftWidth": "0px",
|
||||
"borderRightStyle": "solid",
|
||||
"borderRightWidth": "0px",
|
||||
"borderTopStyle": "solid",
|
||||
"borderTopWidth": "0px",
|
||||
"boxSizing": "border-box",
|
||||
"marginBottom": "25px",
|
||||
"marginLeft": "10px",
|
||||
"marginRight": "10px",
|
||||
"marginTop": "50px",
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,107 @@
|
||||
exports[`apis/StyleSheet/i18nStyle LTR mode does not auto-flip 1`] = `
|
||||
Object {
|
||||
"borderBottomLeftRadius": 20,
|
||||
"borderBottomRightRadius": "2rem",
|
||||
"borderLeftColor": "red",
|
||||
"borderLeftStyle": "solid",
|
||||
"borderLeftWidth": 5,
|
||||
"borderRightColor": "blue",
|
||||
"borderRightStyle": "dotted",
|
||||
"borderRightWidth": 6,
|
||||
"borderTopLeftRadius": 10,
|
||||
"borderTopRightRadius": "1rem",
|
||||
"left": 1,
|
||||
"marginLeft": 7,
|
||||
"marginRight": 8,
|
||||
"paddingLeft": 9,
|
||||
"paddingRight": 10,
|
||||
"right": 2,
|
||||
"textAlign": "left",
|
||||
"textShadowOffset": Object {
|
||||
"height": 10,
|
||||
"width": "1rem",
|
||||
},
|
||||
"writingDirection": "ltr",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`apis/StyleSheet/i18nStyle LTR mode normalizes properties 1`] = `
|
||||
Object {
|
||||
"borderBottomLeftRadius": 20,
|
||||
"borderBottomRightRadius": "2rem",
|
||||
"borderLeftColor": "red",
|
||||
"borderLeftStyle": "solid",
|
||||
"borderLeftWidth": 5,
|
||||
"borderRightColor": "blue",
|
||||
"borderRightStyle": "dotted",
|
||||
"borderRightWidth": 6,
|
||||
"borderTopLeftRadius": 10,
|
||||
"borderTopRightRadius": "1rem",
|
||||
"left": 1,
|
||||
"marginLeft": 7,
|
||||
"marginRight": 8,
|
||||
"paddingLeft": 9,
|
||||
"paddingRight": 10,
|
||||
"right": 2,
|
||||
"textAlign": "left",
|
||||
"textShadowOffset": Object {
|
||||
"height": 10,
|
||||
"width": "1rem",
|
||||
},
|
||||
"writingDirection": "ltr",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`apis/StyleSheet/i18nStyle RTL mode does auto-flip 1`] = `
|
||||
Object {
|
||||
"borderBottomLeftRadius": "2rem",
|
||||
"borderBottomRightRadius": 20,
|
||||
"borderLeftColor": "blue",
|
||||
"borderLeftStyle": "dotted",
|
||||
"borderLeftWidth": 6,
|
||||
"borderRightColor": "red",
|
||||
"borderRightStyle": "solid",
|
||||
"borderRightWidth": 5,
|
||||
"borderTopLeftRadius": "1rem",
|
||||
"borderTopRightRadius": 10,
|
||||
"left": 2,
|
||||
"marginLeft": 8,
|
||||
"marginRight": 7,
|
||||
"paddingLeft": 10,
|
||||
"paddingRight": 9,
|
||||
"right": 1,
|
||||
"textAlign": "right",
|
||||
"textShadowOffset": Object {
|
||||
"height": 10,
|
||||
"width": "-1rem",
|
||||
},
|
||||
"writingDirection": "rtl",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`apis/StyleSheet/i18nStyle RTL mode normalizes properties 1`] = `
|
||||
Object {
|
||||
"borderBottomLeftRadius": 20,
|
||||
"borderBottomRightRadius": "2rem",
|
||||
"borderLeftColor": "red",
|
||||
"borderLeftStyle": "solid",
|
||||
"borderLeftWidth": 5,
|
||||
"borderRightColor": "blue",
|
||||
"borderRightStyle": "dotted",
|
||||
"borderRightWidth": 6,
|
||||
"borderTopLeftRadius": 10,
|
||||
"borderTopRightRadius": "1rem",
|
||||
"left": 1,
|
||||
"marginLeft": 7,
|
||||
"marginRight": 8,
|
||||
"paddingLeft": 9,
|
||||
"paddingRight": 10,
|
||||
"right": 2,
|
||||
"textAlign": "left",
|
||||
"textShadowOffset": Object {
|
||||
"height": 10,
|
||||
"width": "-1rem",
|
||||
},
|
||||
"writingDirection": "ltr",
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,10 @@
|
||||
exports[`apis/StyleSheet resolve 1`] = `
|
||||
Object {
|
||||
"className": "test __style_df __style_pebn",
|
||||
"style": Object {
|
||||
"display": null,
|
||||
"opacity": 1,
|
||||
"pointerEvents": null,
|
||||
},
|
||||
}
|
||||
`;
|
||||
@@ -1,13 +1,12 @@
|
||||
/* eslint-env mocha */
|
||||
/* eslint-env jasmine, jest */
|
||||
|
||||
import assert from 'assert';
|
||||
import createReactStyleObject from '../createReactStyleObject';
|
||||
|
||||
suite('apis/StyleSheet/createReactStyleObject', () => {
|
||||
describe('apis/StyleSheet/createReactStyleObject', () => {
|
||||
test('converts ReactNative style to ReactDOM style', () => {
|
||||
const reactNativeStyle = { display: 'flex', marginVertical: 0, opacity: 0 };
|
||||
const expectedStyle = { display: 'flex', marginTop: '0px', marginBottom: '0px', opacity: 0 };
|
||||
|
||||
assert.deepEqual(createReactStyleObject(reactNativeStyle), expectedStyle);
|
||||
expect(createReactStyleObject(reactNativeStyle)).toEqual(expectedStyle);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
/* eslint-env mocha */
|
||||
/* eslint-env jasmine, jest */
|
||||
|
||||
import assert from 'assert';
|
||||
import expandStyle from '../expandStyle';
|
||||
|
||||
suite('apis/StyleSheet/expandStyle', () => {
|
||||
describe('apis/StyleSheet/expandStyle', () => {
|
||||
test('shortform -> longform', () => {
|
||||
const initial = {
|
||||
const style = {
|
||||
borderStyle: 'solid',
|
||||
boxSizing: 'border-box',
|
||||
borderBottomColor: 'white',
|
||||
@@ -16,24 +15,7 @@ suite('apis/StyleSheet/expandStyle', () => {
|
||||
margin: 10
|
||||
};
|
||||
|
||||
const expected = {
|
||||
borderBottomStyle: 'solid',
|
||||
borderLeftStyle: 'solid',
|
||||
borderRightStyle: 'solid',
|
||||
boxSizing: 'border-box',
|
||||
borderBottomColor: 'white',
|
||||
borderTopStyle: 'solid',
|
||||
borderTopWidth: '0px',
|
||||
borderLeftWidth: '0px',
|
||||
borderRightWidth: '0px',
|
||||
borderBottomWidth: '1px',
|
||||
marginTop: '50px',
|
||||
marginBottom: '25px',
|
||||
marginLeft: '10px',
|
||||
marginRight: '10px'
|
||||
};
|
||||
|
||||
assert.deepEqual(expandStyle(initial), expected);
|
||||
expect(expandStyle(style)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('textAlignVertical', () => {
|
||||
@@ -45,7 +27,7 @@ suite('apis/StyleSheet/expandStyle', () => {
|
||||
verticalAlign: 'middle'
|
||||
};
|
||||
|
||||
assert.deepEqual(expandStyle(initial), expected);
|
||||
expect(expandStyle(initial)).toEqual(expected);
|
||||
});
|
||||
|
||||
test('flex', () => {
|
||||
@@ -61,6 +43,6 @@ suite('apis/StyleSheet/expandStyle', () => {
|
||||
flexBasis: 'auto'
|
||||
};
|
||||
|
||||
assert.deepEqual(expandStyle(initial), expected);
|
||||
expect(expandStyle(initial)).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
/* eslint-env mocha */
|
||||
/* eslint-env jasmine, jest */
|
||||
|
||||
import assert from 'assert';
|
||||
import I18nManager from '../../I18nManager';
|
||||
import i18nStyle from '../i18nStyle';
|
||||
|
||||
const initial = {
|
||||
const style = {
|
||||
borderLeftColor: 'red',
|
||||
borderRightColor: 'blue',
|
||||
borderTopLeftRadius: 10,
|
||||
@@ -26,66 +25,44 @@ const initial = {
|
||||
writingDirection: 'ltr'
|
||||
};
|
||||
|
||||
const initialNoI18n = Object.keys(initial).reduce((acc, prop) => {
|
||||
const styleNoI18n = Object.keys(style).reduce((acc, prop) => {
|
||||
const newProp = `${prop}$noI18n`;
|
||||
acc[newProp] = initial[prop];
|
||||
acc[newProp] = style[prop];
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
const expected = {
|
||||
borderLeftColor: 'blue',
|
||||
borderRightColor: 'red',
|
||||
borderTopLeftRadius: '1rem',
|
||||
borderTopRightRadius: 10,
|
||||
borderBottomLeftRadius: '2rem',
|
||||
borderBottomRightRadius: 20,
|
||||
borderLeftStyle: 'dotted',
|
||||
borderRightStyle: 'solid',
|
||||
borderLeftWidth: 6,
|
||||
borderRightWidth: 5,
|
||||
left: 2,
|
||||
marginLeft: 8,
|
||||
marginRight: 7,
|
||||
paddingLeft: 10,
|
||||
paddingRight: 9,
|
||||
right: 1,
|
||||
textAlign: 'right',
|
||||
textShadowOffset: { width: '-1rem', height: 10 },
|
||||
writingDirection: 'rtl'
|
||||
};
|
||||
|
||||
suite('apis/StyleSheet/i18nStyle', () => {
|
||||
suite('LTR mode', () => {
|
||||
setup(() => {
|
||||
describe('apis/StyleSheet/i18nStyle', () => {
|
||||
describe('LTR mode', () => {
|
||||
beforeEach(() => {
|
||||
I18nManager.allowRTL(false);
|
||||
});
|
||||
|
||||
teardown(() => {
|
||||
afterEach(() => {
|
||||
I18nManager.allowRTL(true);
|
||||
});
|
||||
|
||||
test('does not auto-flip', () => {
|
||||
assert.deepEqual(i18nStyle(initial), initial);
|
||||
expect(i18nStyle(style)).toMatchSnapshot();
|
||||
});
|
||||
test('normalizes properties', () => {
|
||||
assert.deepEqual(i18nStyle(initialNoI18n), initial);
|
||||
expect(i18nStyle(styleNoI18n)).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
suite('RTL mode', () => {
|
||||
setup(() => {
|
||||
describe('RTL mode', () => {
|
||||
beforeEach(() => {
|
||||
I18nManager.forceRTL(true);
|
||||
});
|
||||
|
||||
teardown(() => {
|
||||
afterEach(() => {
|
||||
I18nManager.forceRTL(false);
|
||||
});
|
||||
|
||||
test('does auto-flip', () => {
|
||||
assert.deepEqual(i18nStyle(initial), expected);
|
||||
expect(i18nStyle(style)).toMatchSnapshot();
|
||||
});
|
||||
test('normalizes properties', () => {
|
||||
assert.deepEqual(i18nStyle(initialNoI18n), initial);
|
||||
expect(i18nStyle(styleNoI18n)).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,71 +1,64 @@
|
||||
/* eslint-env mocha */
|
||||
/* eslint-env jasmine, jest */
|
||||
|
||||
import assert from 'assert';
|
||||
import { getDefaultStyleSheet } from '../css';
|
||||
import isPlainObject from 'lodash/isPlainObject';
|
||||
import StyleSheet from '..';
|
||||
|
||||
suite('apis/StyleSheet', () => {
|
||||
setup(() => {
|
||||
const isPlainObject = (x) => {
|
||||
const toString = Object.prototype.toString;
|
||||
let proto;
|
||||
/* eslint-disable */
|
||||
return (
|
||||
toString.call(x) === '[object Object]' &&
|
||||
(proto = Object.getPrototypeOf(x), proto === null || proto === Object.getPrototypeOf({}))
|
||||
);
|
||||
/* eslint-enable */
|
||||
};
|
||||
|
||||
describe('apis/StyleSheet', () => {
|
||||
beforeEach(() => {
|
||||
StyleSheet._reset();
|
||||
});
|
||||
|
||||
test('absoluteFill', () => {
|
||||
assert(Number.isInteger(StyleSheet.absoluteFill) === true);
|
||||
expect(Number.isInteger(StyleSheet.absoluteFill) === true).toBeTruthy();
|
||||
});
|
||||
|
||||
test('absoluteFillObject', () => {
|
||||
assert.ok(isPlainObject(StyleSheet.absoluteFillObject) === true);
|
||||
expect(isPlainObject(StyleSheet.absoluteFillObject) === true).toBeTruthy();
|
||||
});
|
||||
|
||||
suite('create', () => {
|
||||
describe('create', () => {
|
||||
test('replaces styles with numbers', () => {
|
||||
const style = StyleSheet.create({ root: { opacity: 1 } });
|
||||
assert(Number.isInteger(style.root) === true);
|
||||
expect(Number.isInteger(style.root) === true).toBeTruthy();
|
||||
});
|
||||
|
||||
test('renders a style sheet in the browser', () => {
|
||||
StyleSheet.create({ root: { color: 'red' } });
|
||||
assert.equal(
|
||||
document.getElementById('__react-native-style').textContent,
|
||||
getDefaultStyleSheet()
|
||||
);
|
||||
expect(document.getElementById('react-native-style__').textContent).toEqual(getDefaultStyleSheet());
|
||||
});
|
||||
});
|
||||
|
||||
test('flatten', () => {
|
||||
assert(typeof StyleSheet.flatten === 'function');
|
||||
expect(typeof StyleSheet.flatten === 'function').toBeTruthy();
|
||||
});
|
||||
|
||||
test('hairlineWidth', () => {
|
||||
assert(Number.isInteger(StyleSheet.hairlineWidth) === true);
|
||||
expect(Number.isInteger(StyleSheet.hairlineWidth) === true).toBeTruthy();
|
||||
});
|
||||
|
||||
test('render', () => {
|
||||
assert.equal(
|
||||
StyleSheet.render().props.dangerouslySetInnerHTML.__html,
|
||||
getDefaultStyleSheet()
|
||||
);
|
||||
expect(StyleSheet.render().props.dangerouslySetInnerHTML.__html).toEqual(getDefaultStyleSheet());
|
||||
});
|
||||
|
||||
test('resolve', () => {
|
||||
assert.deepEqual(
|
||||
StyleSheet.resolve({
|
||||
className: 'test',
|
||||
style: {
|
||||
display: 'flex',
|
||||
opacity: 1,
|
||||
pointerEvents: 'box-none'
|
||||
}
|
||||
}),
|
||||
{
|
||||
className: 'test __style_df __style_pebn',
|
||||
style: {
|
||||
display: null,
|
||||
opacity: 1,
|
||||
pointerEvents: null
|
||||
}
|
||||
expect(StyleSheet.resolve({
|
||||
className: 'test',
|
||||
style: {
|
||||
display: 'flex',
|
||||
opacity: 1,
|
||||
pointerEvents: 'box-none'
|
||||
}
|
||||
);
|
||||
})).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
/* eslint-env mocha */
|
||||
/* eslint-env jasmine, jest */
|
||||
|
||||
import assert from 'assert';
|
||||
import normalizeValue from '../normalizeValue';
|
||||
|
||||
suite('apis/StyleSheet/normalizeValue', () => {
|
||||
describe('apis/StyleSheet/normalizeValue', () => {
|
||||
test('normalizes property values requiring units', () => {
|
||||
assert.deepEqual(normalizeValue('margin', 0), '0px');
|
||||
expect(normalizeValue('margin', 0)).toEqual('0px');
|
||||
});
|
||||
test('ignores unitless property values', () => {
|
||||
assert.deepEqual(normalizeValue('flexGrow', 1), 1);
|
||||
assert.deepEqual(normalizeValue('scale', 2), 2);
|
||||
expect(normalizeValue('flexGrow', 1)).toEqual(1);
|
||||
expect(normalizeValue('scale', 2)).toEqual(2);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
/* eslint-env mocha */
|
||||
/* eslint-env jasmine, jest */
|
||||
|
||||
import assert from 'assert';
|
||||
import processTextShadow from '../processTextShadow';
|
||||
|
||||
suite('apis/StyleSheet/processTextShadow', () => {
|
||||
describe('apis/StyleSheet/processTextShadow', () => {
|
||||
test('textShadowOffset', () => {
|
||||
const style = {
|
||||
textShadowColor: 'red',
|
||||
@@ -11,14 +10,11 @@ suite('apis/StyleSheet/processTextShadow', () => {
|
||||
textShadowRadius: 5
|
||||
};
|
||||
|
||||
assert.deepEqual(
|
||||
processTextShadow(style),
|
||||
{
|
||||
textShadow: '2px 2px 5px red',
|
||||
textShadowColor: null,
|
||||
textShadowOffset: null,
|
||||
textShadowRadius: null
|
||||
}
|
||||
);
|
||||
expect(processTextShadow(style)).toEqual({
|
||||
textShadow: '2px 2px 5px red',
|
||||
textShadowColor: null,
|
||||
textShadowOffset: null,
|
||||
textShadowRadius: null
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
/* eslint-env mocha */
|
||||
/* eslint-env jasmine, jest */
|
||||
|
||||
import assert from 'assert';
|
||||
import processTransform from '../processTransform';
|
||||
|
||||
suite('apis/StyleSheet/processTransform', () => {
|
||||
describe('apis/StyleSheet/processTransform', () => {
|
||||
test('transform', () => {
|
||||
const style = {
|
||||
transform: [
|
||||
@@ -13,10 +12,7 @@ suite('apis/StyleSheet/processTransform', () => {
|
||||
]
|
||||
};
|
||||
|
||||
assert.deepEqual(
|
||||
processTransform(style),
|
||||
{ transform: 'scaleX(20) translateX(20px) rotate(20deg)' }
|
||||
);
|
||||
expect(processTransform(style)).toEqual({ transform: 'scaleX(20) translateX(20px) rotate(20deg)' });
|
||||
});
|
||||
|
||||
test('transformMatrix', () => {
|
||||
@@ -24,12 +20,9 @@ suite('apis/StyleSheet/processTransform', () => {
|
||||
transformMatrix: [ 1, 2, 3, 4, 5, 6 ]
|
||||
};
|
||||
|
||||
assert.deepEqual(
|
||||
processTransform(style),
|
||||
{
|
||||
transform: 'matrix3d(1,2,3,4,5,6)',
|
||||
transformMatrix: null
|
||||
}
|
||||
);
|
||||
expect(processTransform(style)).toEqual({
|
||||
transform: 'matrix3d(1,2,3,4,5,6)',
|
||||
transformMatrix: null
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,17 +1,13 @@
|
||||
/* eslint-env mocha */
|
||||
/* eslint-env jasmine, jest */
|
||||
|
||||
import assert from 'assert';
|
||||
import processVendorPrefixes from '../processVendorPrefixes';
|
||||
|
||||
suite('apis/StyleSheet/processVendorPrefixes', () => {
|
||||
describe('apis/StyleSheet/processVendorPrefixes', () => {
|
||||
test('handles array values', () => {
|
||||
const style = {
|
||||
display: [ '-webkit-flex', 'flex' ]
|
||||
};
|
||||
|
||||
assert.deepEqual(
|
||||
processVendorPrefixes(style),
|
||||
{ display: 'flex' }
|
||||
);
|
||||
expect(processVendorPrefixes(style)).toEqual({ display: 'flex' });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -4,12 +4,11 @@ import ExecutionEnvironment from 'fbjs/lib/ExecutionEnvironment';
|
||||
import flattenStyle from '../../modules/flattenStyle';
|
||||
import React from 'react';
|
||||
import ReactNativePropRegistry from '../../modules/ReactNativePropRegistry';
|
||||
import StyleSheetValidation from './StyleSheetValidation';
|
||||
|
||||
let styleElement;
|
||||
let shouldInsertStyleSheet = ExecutionEnvironment.canUseDOM;
|
||||
|
||||
const STYLE_SHEET_ID = '__react-native-style';
|
||||
const STYLE_SHEET_ID = 'react-native-style__';
|
||||
|
||||
const absoluteFillObject = { position: 'absolute', left: 0, right: 0, top: 0, bottom: 0 };
|
||||
|
||||
@@ -52,7 +51,9 @@ module.exports = {
|
||||
|
||||
const result = {};
|
||||
for (const key in styles) {
|
||||
StyleSheetValidation.validateStyle(key, styles);
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
require('./StyleSheetValidation').validateStyle(key, styles);
|
||||
}
|
||||
result[key] = ReactNativePropRegistry.register(styles[key]);
|
||||
}
|
||||
return result;
|
||||
|
||||
@@ -16,7 +16,7 @@ const convertTransformMatrix = (transformMatrix) => {
|
||||
|
||||
const processTransform = (style) => {
|
||||
if (style) {
|
||||
if (style.transform) {
|
||||
if (style.transform && Array.isArray(style.transform)) {
|
||||
style.transform = style.transform.map(mapTransform).join(' ');
|
||||
} else if (style.transformMatrix) {
|
||||
style.transform = convertTransformMatrix(style.transformMatrix);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
/* eslint-env mocha */
|
||||
/* eslint-env jasmine, jest */
|
||||
|
||||
import assert from 'assert';
|
||||
import UIManager from '..';
|
||||
|
||||
const createNode = (style = {}) => {
|
||||
@@ -13,47 +12,52 @@ const createNode = (style = {}) => {
|
||||
|
||||
let defaultBodyMargin;
|
||||
|
||||
suite('apis/UIManager', () => {
|
||||
setup(() => {
|
||||
describe('apis/UIManager', () => {
|
||||
beforeEach(() => {
|
||||
// remove default body margin so we can predict the measured offsets
|
||||
defaultBodyMargin = document.body.style.margin;
|
||||
document.body.style.margin = 0;
|
||||
});
|
||||
|
||||
teardown(() => {
|
||||
afterEach(() => {
|
||||
document.body.style.margin = defaultBodyMargin;
|
||||
});
|
||||
|
||||
suite('measure', () => {
|
||||
describe('measure', () => {
|
||||
test('provides correct layout to callback', () => {
|
||||
const node = createNode({ height: '5000px', left: '100px', position: 'relative', top: '100px', width: '5000px' });
|
||||
document.body.appendChild(node);
|
||||
|
||||
node.getBoundingClientRect = jest.fn(() => ({ width: 5000, height: 5000, top: 100, left: 100 }));
|
||||
|
||||
UIManager.measure(node, (x, y, width, height, pageX, pageY) => {
|
||||
assert.equal(x, 100);
|
||||
assert.equal(y, 100);
|
||||
assert.equal(width, 5000);
|
||||
assert.equal(height, 5000);
|
||||
assert.equal(pageX, 100);
|
||||
assert.equal(pageY, 100);
|
||||
expect(x).toEqual(100);
|
||||
expect(y).toEqual(100);
|
||||
expect(width).toEqual(5000);
|
||||
expect(height).toEqual(5000);
|
||||
expect(pageX).toEqual(100);
|
||||
expect(pageY).toEqual(100);
|
||||
});
|
||||
|
||||
// test values account for scroll position
|
||||
window.scrollTo(200, 200);
|
||||
node.getBoundingClientRect = jest.fn(() => ({ width: 5000, height: 5000, top: -100, left: -100 }));
|
||||
node.parentNode.getBoundingClientRect = jest.fn(() => ({ top: -200, left: -200 }));
|
||||
|
||||
UIManager.measure(node, (x, y, width, height, pageX, pageY) => {
|
||||
assert.equal(x, 100);
|
||||
assert.equal(y, 100);
|
||||
assert.equal(width, 5000);
|
||||
assert.equal(height, 5000);
|
||||
assert.equal(pageX, -100);
|
||||
assert.equal(pageY, -100);
|
||||
expect(x).toEqual(100);
|
||||
expect(y).toEqual(100);
|
||||
expect(width).toEqual(5000);
|
||||
expect(height).toEqual(5000);
|
||||
expect(pageX).toEqual(-100);
|
||||
expect(pageY).toEqual(-100);
|
||||
});
|
||||
|
||||
document.body.removeChild(node);
|
||||
});
|
||||
});
|
||||
|
||||
suite('measureLayout', () => {
|
||||
describe('measureLayout', () => {
|
||||
test('provides correct layout to onSuccess callback', () => {
|
||||
const node = createNode({ height: '10px', width: '10px' });
|
||||
const middle = createNode({ padding: '20px' });
|
||||
@@ -62,18 +66,25 @@ suite('apis/UIManager', () => {
|
||||
context.appendChild(middle);
|
||||
document.body.appendChild(context);
|
||||
|
||||
node.getBoundingClientRect = jest.fn(() => ({
|
||||
width: 10,
|
||||
height: 10,
|
||||
top: 40,
|
||||
left: 40
|
||||
}));
|
||||
|
||||
UIManager.measureLayout(node, context, () => {}, (x, y, width, height) => {
|
||||
assert.equal(x, 40);
|
||||
assert.equal(y, 40);
|
||||
assert.equal(width, 10);
|
||||
assert.equal(height, 10);
|
||||
expect(x).toEqual(40);
|
||||
expect(y).toEqual(40);
|
||||
expect(width).toEqual(10);
|
||||
expect(height).toEqual(10);
|
||||
});
|
||||
|
||||
document.body.removeChild(context);
|
||||
});
|
||||
});
|
||||
|
||||
suite('measureInWindow', () => {
|
||||
describe('measureInWindow', () => {
|
||||
test('provides correct layout to callback', () => {
|
||||
const node = createNode({ height: '10px', width: '10px' });
|
||||
const middle = createNode({ padding: '20px' });
|
||||
@@ -82,18 +93,25 @@ suite('apis/UIManager', () => {
|
||||
context.appendChild(middle);
|
||||
document.body.appendChild(context);
|
||||
|
||||
node.getBoundingClientRect = jest.fn(() => ({
|
||||
width: 10,
|
||||
height: 10,
|
||||
top: 40,
|
||||
left: 40
|
||||
}));
|
||||
|
||||
UIManager.measureInWindow(node, (x, y, width, height) => {
|
||||
assert.equal(x, 40);
|
||||
assert.equal(y, 40);
|
||||
assert.equal(width, 10);
|
||||
assert.equal(height, 10);
|
||||
expect(x).toEqual(40);
|
||||
expect(y).toEqual(40);
|
||||
expect(width).toEqual(10);
|
||||
expect(height).toEqual(10);
|
||||
});
|
||||
|
||||
document.body.removeChild(context);
|
||||
});
|
||||
});
|
||||
|
||||
suite('updateView', () => {
|
||||
describe('updateView', () => {
|
||||
const componentStub = {
|
||||
_reactInternalInstance: {
|
||||
_currentElement: { _owner: {} },
|
||||
@@ -106,14 +124,14 @@ suite('apis/UIManager', () => {
|
||||
node.className = 'existing';
|
||||
const props = { className: 'extra' };
|
||||
UIManager.updateView(node, props, componentStub);
|
||||
assert.equal(node.getAttribute('class'), 'existing extra');
|
||||
expect(node.getAttribute('class')).toEqual('existing extra');
|
||||
});
|
||||
|
||||
test('adds correct DOM styles to existing style', () => {
|
||||
const node = createNode({ color: 'red' });
|
||||
const props = { style: { marginVertical: 0, opacity: 0 } };
|
||||
UIManager.updateView(node, props, componentStub);
|
||||
assert.equal(node.getAttribute('style'), 'color: red; margin-top: 0px; margin-bottom: 0px; opacity: 0;');
|
||||
expect(node.getAttribute('style')).toEqual('color: red; margin-top: 0px; margin-bottom: 0px; opacity: 0;');
|
||||
});
|
||||
|
||||
test('replaces input and textarea text', () => {
|
||||
@@ -123,18 +141,18 @@ suite('apis/UIManager', () => {
|
||||
const valueProp = { value: 'expected-value' };
|
||||
|
||||
UIManager.updateView(node, textProp);
|
||||
assert.equal(node.value, 'expected-text');
|
||||
expect(node.value).toEqual('expected-text');
|
||||
|
||||
UIManager.updateView(node, valueProp);
|
||||
assert.equal(node.value, 'expected-value');
|
||||
expect(node.value).toEqual('expected-value');
|
||||
});
|
||||
|
||||
test('sets other attribute values', () => {
|
||||
const node = createNode();
|
||||
const props = { 'aria-level': '4', 'data-of-type': 'string' };
|
||||
UIManager.updateView(node, props);
|
||||
assert.equal(node.getAttribute('aria-level'), '4');
|
||||
assert.equal(node.getAttribute('data-of-type'), 'string');
|
||||
expect(node.getAttribute('aria-level')).toEqual('4');
|
||||
expect(node.getAttribute('data-of-type')).toEqual('string');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* eslint-env mocha */
|
||||
/* eslint-env jasmine, jest */
|
||||
|
||||
suite('components/ActivityIndicator', () => {
|
||||
describe('components/ActivityIndicator', () => {
|
||||
test.skip('NO TEST COVERAGE', () => {});
|
||||
});
|
||||
|
||||
5
src/components/Button/__tests__/index-test.js
Normal file
5
src/components/Button/__tests__/index-test.js
Normal file
@@ -0,0 +1,5 @@
|
||||
/* eslint-env jasmine, jest */
|
||||
|
||||
describe('components/Button', () => {
|
||||
test.skip('NO TEST COVERAGE', () => {});
|
||||
});
|
||||
66
src/components/Button/index.js
Normal file
66
src/components/Button/index.js
Normal file
@@ -0,0 +1,66 @@
|
||||
import ColorPropType from '../../propTypes/ColorPropType';
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import StyleSheet from '../../apis/StyleSheet';
|
||||
import TouchableOpacity from '../Touchable/TouchableOpacity';
|
||||
import Text from '../Text';
|
||||
|
||||
class Button extends Component {
|
||||
static propTypes = {
|
||||
accessibilityLabel: PropTypes.string,
|
||||
color: ColorPropType,
|
||||
disabled: PropTypes.bool,
|
||||
onPress: PropTypes.func.isRequired,
|
||||
title: PropTypes.string.isRequired
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
accessibilityLabel,
|
||||
color,
|
||||
disabled,
|
||||
onPress,
|
||||
title
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<TouchableOpacity
|
||||
accessibilityLabel={accessibilityLabel}
|
||||
accessibilityRole={'button'}
|
||||
disabled={disabled}
|
||||
onPress={onPress}
|
||||
style={[
|
||||
styles.button,
|
||||
color && { backgroundColor: color },
|
||||
disabled && styles.buttonDisabled
|
||||
]}>
|
||||
<Text style={[
|
||||
styles.text,
|
||||
disabled && styles.textDisabled
|
||||
]}>
|
||||
{title}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
button: {
|
||||
backgroundColor: '#2196F3',
|
||||
borderRadius: 2
|
||||
},
|
||||
text: {
|
||||
textAlign: 'center',
|
||||
color: '#fff',
|
||||
padding: 8,
|
||||
fontWeight: '500'
|
||||
},
|
||||
buttonDisabled: {
|
||||
backgroundColor: '#dfdfdf'
|
||||
},
|
||||
textDisabled: {
|
||||
color: '#a1a1a1'
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = Button;
|
||||
@@ -7,7 +7,7 @@ import TransformPropTypes from '../../propTypes/TransformPropTypes';
|
||||
|
||||
const hiddenOrVisible = PropTypes.oneOf([ 'hidden', 'visible' ]);
|
||||
|
||||
module.exports = {
|
||||
module.exports = process.env.NODE_ENV !== 'production' ? {
|
||||
...BorderPropTypes,
|
||||
...LayoutPropTypes,
|
||||
...TransformPropTypes,
|
||||
@@ -24,4 +24,4 @@ module.exports = {
|
||||
* @platform web
|
||||
*/
|
||||
visibility: hiddenOrVisible
|
||||
};
|
||||
} : {};
|
||||
|
||||
912
src/components/Image/__tests__/__snapshots__/index-test.js.snap
Normal file
912
src/components/Image/__tests__/__snapshots__/index-test.js.snap
Normal file
@@ -0,0 +1,912 @@
|
||||
exports[`components/Image passes other props through to underlying View 1`] = `
|
||||
<div
|
||||
className=" __style_df"
|
||||
onResponderGrant={[Function]}
|
||||
role="img"
|
||||
style={
|
||||
Object {
|
||||
"MozBoxSizing": "border-box",
|
||||
"WebkitAlignItems": "stretch",
|
||||
"WebkitBoxAlign": "stretch",
|
||||
"WebkitBoxDirection": "normal",
|
||||
"WebkitBoxOrient": "vertical",
|
||||
"WebkitFlexBasis": "auto",
|
||||
"WebkitFlexDirection": "column",
|
||||
"WebkitFlexShrink": 0,
|
||||
"alignItems": "stretch",
|
||||
"backgroundColor": "transparent",
|
||||
"backgroundPosition": "center",
|
||||
"backgroundRepeat": "no-repeat",
|
||||
"backgroundSize": "cover",
|
||||
"borderBottomStyle": "solid",
|
||||
"borderBottomWidth": "0px",
|
||||
"borderLeftStyle": "solid",
|
||||
"borderLeftWidth": "0px",
|
||||
"borderRightStyle": "solid",
|
||||
"borderRightWidth": "0px",
|
||||
"borderTopStyle": "solid",
|
||||
"borderTopWidth": "0px",
|
||||
"boxSizing": "border-box",
|
||||
"color": "inherit",
|
||||
"display": null,
|
||||
"flexBasis": "auto",
|
||||
"flexDirection": "column",
|
||||
"flexShrink": 0,
|
||||
"font": "inherit",
|
||||
"listStyle": "none",
|
||||
"marginBottom": "0px",
|
||||
"marginLeft": "0px",
|
||||
"marginRight": "0px",
|
||||
"marginTop": "0px",
|
||||
"minHeight": "0px",
|
||||
"minWidth": "0px",
|
||||
"msFlexAlign": "stretch",
|
||||
"msFlexDirection": "column",
|
||||
"msFlexNegative": 0,
|
||||
"msPreferredSize": "auto",
|
||||
"paddingBottom": "0px",
|
||||
"paddingLeft": "0px",
|
||||
"paddingRight": "0px",
|
||||
"paddingTop": "0px",
|
||||
"position": "relative",
|
||||
"textAlign": "inherit",
|
||||
"textDecoration": "none",
|
||||
}
|
||||
} />
|
||||
`;
|
||||
|
||||
exports[`components/Image prop "accessibilityLabel" 1`] = `
|
||||
<div
|
||||
aria-label="accessibilityLabel"
|
||||
className=" __style_df"
|
||||
role="img"
|
||||
style={
|
||||
Object {
|
||||
"MozBoxSizing": "border-box",
|
||||
"WebkitAlignItems": "stretch",
|
||||
"WebkitBoxAlign": "stretch",
|
||||
"WebkitBoxDirection": "normal",
|
||||
"WebkitBoxOrient": "vertical",
|
||||
"WebkitFlexBasis": "auto",
|
||||
"WebkitFlexDirection": "column",
|
||||
"WebkitFlexShrink": 0,
|
||||
"alignItems": "stretch",
|
||||
"backgroundColor": "transparent",
|
||||
"backgroundPosition": "center",
|
||||
"backgroundRepeat": "no-repeat",
|
||||
"backgroundSize": "cover",
|
||||
"borderBottomStyle": "solid",
|
||||
"borderBottomWidth": "0px",
|
||||
"borderLeftStyle": "solid",
|
||||
"borderLeftWidth": "0px",
|
||||
"borderRightStyle": "solid",
|
||||
"borderRightWidth": "0px",
|
||||
"borderTopStyle": "solid",
|
||||
"borderTopWidth": "0px",
|
||||
"boxSizing": "border-box",
|
||||
"color": "inherit",
|
||||
"display": null,
|
||||
"flexBasis": "auto",
|
||||
"flexDirection": "column",
|
||||
"flexShrink": 0,
|
||||
"font": "inherit",
|
||||
"listStyle": "none",
|
||||
"marginBottom": "0px",
|
||||
"marginLeft": "0px",
|
||||
"marginRight": "0px",
|
||||
"marginTop": "0px",
|
||||
"minHeight": "0px",
|
||||
"minWidth": "0px",
|
||||
"msFlexAlign": "stretch",
|
||||
"msFlexDirection": "column",
|
||||
"msFlexNegative": 0,
|
||||
"msPreferredSize": "auto",
|
||||
"paddingBottom": "0px",
|
||||
"paddingLeft": "0px",
|
||||
"paddingRight": "0px",
|
||||
"paddingTop": "0px",
|
||||
"position": "relative",
|
||||
"textAlign": "inherit",
|
||||
"textDecoration": "none",
|
||||
}
|
||||
} />
|
||||
`;
|
||||
|
||||
exports[`components/Image prop "accessible" 1`] = `
|
||||
<div
|
||||
aria-hidden={true}
|
||||
className=" __style_df"
|
||||
role="img"
|
||||
style={
|
||||
Object {
|
||||
"MozBoxSizing": "border-box",
|
||||
"WebkitAlignItems": "stretch",
|
||||
"WebkitBoxAlign": "stretch",
|
||||
"WebkitBoxDirection": "normal",
|
||||
"WebkitBoxOrient": "vertical",
|
||||
"WebkitFlexBasis": "auto",
|
||||
"WebkitFlexDirection": "column",
|
||||
"WebkitFlexShrink": 0,
|
||||
"alignItems": "stretch",
|
||||
"backgroundColor": "transparent",
|
||||
"backgroundPosition": "center",
|
||||
"backgroundRepeat": "no-repeat",
|
||||
"backgroundSize": "cover",
|
||||
"borderBottomStyle": "solid",
|
||||
"borderBottomWidth": "0px",
|
||||
"borderLeftStyle": "solid",
|
||||
"borderLeftWidth": "0px",
|
||||
"borderRightStyle": "solid",
|
||||
"borderRightWidth": "0px",
|
||||
"borderTopStyle": "solid",
|
||||
"borderTopWidth": "0px",
|
||||
"boxSizing": "border-box",
|
||||
"color": "inherit",
|
||||
"display": null,
|
||||
"flexBasis": "auto",
|
||||
"flexDirection": "column",
|
||||
"flexShrink": 0,
|
||||
"font": "inherit",
|
||||
"listStyle": "none",
|
||||
"marginBottom": "0px",
|
||||
"marginLeft": "0px",
|
||||
"marginRight": "0px",
|
||||
"marginTop": "0px",
|
||||
"minHeight": "0px",
|
||||
"minWidth": "0px",
|
||||
"msFlexAlign": "stretch",
|
||||
"msFlexDirection": "column",
|
||||
"msFlexNegative": 0,
|
||||
"msPreferredSize": "auto",
|
||||
"paddingBottom": "0px",
|
||||
"paddingLeft": "0px",
|
||||
"paddingRight": "0px",
|
||||
"paddingTop": "0px",
|
||||
"position": "relative",
|
||||
"textAlign": "inherit",
|
||||
"textDecoration": "none",
|
||||
}
|
||||
} />
|
||||
`;
|
||||
|
||||
exports[`components/Image prop "children" 1`] = `
|
||||
<div
|
||||
className=" __style_df"
|
||||
role="img"
|
||||
style={
|
||||
Object {
|
||||
"MozBoxSizing": "border-box",
|
||||
"WebkitAlignItems": "stretch",
|
||||
"WebkitBoxAlign": "stretch",
|
||||
"WebkitBoxDirection": "normal",
|
||||
"WebkitBoxOrient": "vertical",
|
||||
"WebkitFlexBasis": "auto",
|
||||
"WebkitFlexDirection": "column",
|
||||
"WebkitFlexShrink": 0,
|
||||
"alignItems": "stretch",
|
||||
"backgroundColor": "transparent",
|
||||
"backgroundPosition": "center",
|
||||
"backgroundRepeat": "no-repeat",
|
||||
"backgroundSize": "cover",
|
||||
"borderBottomStyle": "solid",
|
||||
"borderBottomWidth": "0px",
|
||||
"borderLeftStyle": "solid",
|
||||
"borderLeftWidth": "0px",
|
||||
"borderRightStyle": "solid",
|
||||
"borderRightWidth": "0px",
|
||||
"borderTopStyle": "solid",
|
||||
"borderTopWidth": "0px",
|
||||
"boxSizing": "border-box",
|
||||
"color": "inherit",
|
||||
"display": null,
|
||||
"flexBasis": "auto",
|
||||
"flexDirection": "column",
|
||||
"flexShrink": 0,
|
||||
"font": "inherit",
|
||||
"listStyle": "none",
|
||||
"marginBottom": "0px",
|
||||
"marginLeft": "0px",
|
||||
"marginRight": "0px",
|
||||
"marginTop": "0px",
|
||||
"minHeight": "0px",
|
||||
"minWidth": "0px",
|
||||
"msFlexAlign": "stretch",
|
||||
"msFlexDirection": "column",
|
||||
"msFlexNegative": 0,
|
||||
"msPreferredSize": "auto",
|
||||
"paddingBottom": "0px",
|
||||
"paddingLeft": "0px",
|
||||
"paddingRight": "0px",
|
||||
"paddingTop": "0px",
|
||||
"position": "relative",
|
||||
"textAlign": "inherit",
|
||||
"textDecoration": "none",
|
||||
}
|
||||
}>
|
||||
<div
|
||||
className="unique" />
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`components/Image prop "defaultSource" does not override "height" and "width" styles 1`] = `
|
||||
<div
|
||||
className=" __style_df"
|
||||
role="img"
|
||||
style={
|
||||
Object {
|
||||
"MozBoxSizing": "border-box",
|
||||
"WebkitAlignItems": "stretch",
|
||||
"WebkitBoxAlign": "stretch",
|
||||
"WebkitBoxDirection": "normal",
|
||||
"WebkitBoxOrient": "vertical",
|
||||
"WebkitFlexBasis": "auto",
|
||||
"WebkitFlexDirection": "column",
|
||||
"WebkitFlexShrink": 0,
|
||||
"alignItems": "stretch",
|
||||
"backgroundColor": "transparent",
|
||||
"backgroundImage": "url(\"https://google.com/favicon.ico\")",
|
||||
"backgroundPosition": "center",
|
||||
"backgroundRepeat": "no-repeat",
|
||||
"backgroundSize": "cover",
|
||||
"borderBottomStyle": "solid",
|
||||
"borderBottomWidth": "0px",
|
||||
"borderLeftStyle": "solid",
|
||||
"borderLeftWidth": "0px",
|
||||
"borderRightStyle": "solid",
|
||||
"borderRightWidth": "0px",
|
||||
"borderTopStyle": "solid",
|
||||
"borderTopWidth": "0px",
|
||||
"boxSizing": "border-box",
|
||||
"color": "inherit",
|
||||
"display": null,
|
||||
"flexBasis": "auto",
|
||||
"flexDirection": "column",
|
||||
"flexShrink": 0,
|
||||
"font": "inherit",
|
||||
"height": "20px",
|
||||
"listStyle": "none",
|
||||
"marginBottom": "0px",
|
||||
"marginLeft": "0px",
|
||||
"marginRight": "0px",
|
||||
"marginTop": "0px",
|
||||
"minHeight": "0px",
|
||||
"minWidth": "0px",
|
||||
"msFlexAlign": "stretch",
|
||||
"msFlexDirection": "column",
|
||||
"msFlexNegative": 0,
|
||||
"msPreferredSize": "auto",
|
||||
"paddingBottom": "0px",
|
||||
"paddingLeft": "0px",
|
||||
"paddingRight": "0px",
|
||||
"paddingTop": "0px",
|
||||
"position": "relative",
|
||||
"textAlign": "inherit",
|
||||
"textDecoration": "none",
|
||||
"width": "40px",
|
||||
}
|
||||
} />
|
||||
`;
|
||||
|
||||
exports[`components/Image prop "defaultSource" sets "height" and "width" styles if missing 1`] = `
|
||||
<div
|
||||
className=" __style_df"
|
||||
role="img"
|
||||
style={
|
||||
Object {
|
||||
"MozBoxSizing": "border-box",
|
||||
"WebkitAlignItems": "stretch",
|
||||
"WebkitBoxAlign": "stretch",
|
||||
"WebkitBoxDirection": "normal",
|
||||
"WebkitBoxOrient": "vertical",
|
||||
"WebkitFlexBasis": "auto",
|
||||
"WebkitFlexDirection": "column",
|
||||
"WebkitFlexShrink": 0,
|
||||
"alignItems": "stretch",
|
||||
"backgroundColor": "transparent",
|
||||
"backgroundImage": "url(\"https://google.com/favicon.ico\")",
|
||||
"backgroundPosition": "center",
|
||||
"backgroundRepeat": "no-repeat",
|
||||
"backgroundSize": "cover",
|
||||
"borderBottomStyle": "solid",
|
||||
"borderBottomWidth": "0px",
|
||||
"borderLeftStyle": "solid",
|
||||
"borderLeftWidth": "0px",
|
||||
"borderRightStyle": "solid",
|
||||
"borderRightWidth": "0px",
|
||||
"borderTopStyle": "solid",
|
||||
"borderTopWidth": "0px",
|
||||
"boxSizing": "border-box",
|
||||
"color": "inherit",
|
||||
"display": null,
|
||||
"flexBasis": "auto",
|
||||
"flexDirection": "column",
|
||||
"flexShrink": 0,
|
||||
"font": "inherit",
|
||||
"height": "10px",
|
||||
"listStyle": "none",
|
||||
"marginBottom": "0px",
|
||||
"marginLeft": "0px",
|
||||
"marginRight": "0px",
|
||||
"marginTop": "0px",
|
||||
"minHeight": "0px",
|
||||
"minWidth": "0px",
|
||||
"msFlexAlign": "stretch",
|
||||
"msFlexDirection": "column",
|
||||
"msFlexNegative": 0,
|
||||
"msPreferredSize": "auto",
|
||||
"paddingBottom": "0px",
|
||||
"paddingLeft": "0px",
|
||||
"paddingRight": "0px",
|
||||
"paddingTop": "0px",
|
||||
"position": "relative",
|
||||
"textAlign": "inherit",
|
||||
"textDecoration": "none",
|
||||
"width": "20px",
|
||||
}
|
||||
} />
|
||||
`;
|
||||
|
||||
exports[`components/Image prop "defaultSource" sets background image when value is a string 1`] = `
|
||||
<div
|
||||
className=" __style_df"
|
||||
role="img"
|
||||
style={
|
||||
Object {
|
||||
"MozBoxSizing": "border-box",
|
||||
"WebkitAlignItems": "stretch",
|
||||
"WebkitBoxAlign": "stretch",
|
||||
"WebkitBoxDirection": "normal",
|
||||
"WebkitBoxOrient": "vertical",
|
||||
"WebkitFlexBasis": "auto",
|
||||
"WebkitFlexDirection": "column",
|
||||
"WebkitFlexShrink": 0,
|
||||
"alignItems": "stretch",
|
||||
"backgroundColor": "transparent",
|
||||
"backgroundImage": "url(\"https://google.com/favicon.ico\")",
|
||||
"backgroundPosition": "center",
|
||||
"backgroundRepeat": "no-repeat",
|
||||
"backgroundSize": "cover",
|
||||
"borderBottomStyle": "solid",
|
||||
"borderBottomWidth": "0px",
|
||||
"borderLeftStyle": "solid",
|
||||
"borderLeftWidth": "0px",
|
||||
"borderRightStyle": "solid",
|
||||
"borderRightWidth": "0px",
|
||||
"borderTopStyle": "solid",
|
||||
"borderTopWidth": "0px",
|
||||
"boxSizing": "border-box",
|
||||
"color": "inherit",
|
||||
"display": null,
|
||||
"flexBasis": "auto",
|
||||
"flexDirection": "column",
|
||||
"flexShrink": 0,
|
||||
"font": "inherit",
|
||||
"listStyle": "none",
|
||||
"marginBottom": "0px",
|
||||
"marginLeft": "0px",
|
||||
"marginRight": "0px",
|
||||
"marginTop": "0px",
|
||||
"minHeight": "0px",
|
||||
"minWidth": "0px",
|
||||
"msFlexAlign": "stretch",
|
||||
"msFlexDirection": "column",
|
||||
"msFlexNegative": 0,
|
||||
"msPreferredSize": "auto",
|
||||
"paddingBottom": "0px",
|
||||
"paddingLeft": "0px",
|
||||
"paddingRight": "0px",
|
||||
"paddingTop": "0px",
|
||||
"position": "relative",
|
||||
"textAlign": "inherit",
|
||||
"textDecoration": "none",
|
||||
}
|
||||
} />
|
||||
`;
|
||||
|
||||
exports[`components/Image prop "defaultSource" sets background image when value is an object 1`] = `
|
||||
<div
|
||||
className=" __style_df"
|
||||
role="img"
|
||||
style={
|
||||
Object {
|
||||
"MozBoxSizing": "border-box",
|
||||
"WebkitAlignItems": "stretch",
|
||||
"WebkitBoxAlign": "stretch",
|
||||
"WebkitBoxDirection": "normal",
|
||||
"WebkitBoxOrient": "vertical",
|
||||
"WebkitFlexBasis": "auto",
|
||||
"WebkitFlexDirection": "column",
|
||||
"WebkitFlexShrink": 0,
|
||||
"alignItems": "stretch",
|
||||
"backgroundColor": "transparent",
|
||||
"backgroundImage": "url(\"https://google.com/favicon.ico\")",
|
||||
"backgroundPosition": "center",
|
||||
"backgroundRepeat": "no-repeat",
|
||||
"backgroundSize": "cover",
|
||||
"borderBottomStyle": "solid",
|
||||
"borderBottomWidth": "0px",
|
||||
"borderLeftStyle": "solid",
|
||||
"borderLeftWidth": "0px",
|
||||
"borderRightStyle": "solid",
|
||||
"borderRightWidth": "0px",
|
||||
"borderTopStyle": "solid",
|
||||
"borderTopWidth": "0px",
|
||||
"boxSizing": "border-box",
|
||||
"color": "inherit",
|
||||
"display": null,
|
||||
"flexBasis": "auto",
|
||||
"flexDirection": "column",
|
||||
"flexShrink": 0,
|
||||
"font": "inherit",
|
||||
"height": undefined,
|
||||
"listStyle": "none",
|
||||
"marginBottom": "0px",
|
||||
"marginLeft": "0px",
|
||||
"marginRight": "0px",
|
||||
"marginTop": "0px",
|
||||
"minHeight": "0px",
|
||||
"minWidth": "0px",
|
||||
"msFlexAlign": "stretch",
|
||||
"msFlexDirection": "column",
|
||||
"msFlexNegative": 0,
|
||||
"msPreferredSize": "auto",
|
||||
"paddingBottom": "0px",
|
||||
"paddingLeft": "0px",
|
||||
"paddingRight": "0px",
|
||||
"paddingTop": "0px",
|
||||
"position": "relative",
|
||||
"textAlign": "inherit",
|
||||
"textDecoration": "none",
|
||||
"width": undefined,
|
||||
}
|
||||
} />
|
||||
`;
|
||||
|
||||
exports[`components/Image prop "resizeMode" value "contain" 1`] = `
|
||||
<div
|
||||
className=" __style_df"
|
||||
role="img"
|
||||
style={
|
||||
Object {
|
||||
"MozBoxSizing": "border-box",
|
||||
"WebkitAlignItems": "stretch",
|
||||
"WebkitBoxAlign": "stretch",
|
||||
"WebkitBoxDirection": "normal",
|
||||
"WebkitBoxOrient": "vertical",
|
||||
"WebkitFlexBasis": "auto",
|
||||
"WebkitFlexDirection": "column",
|
||||
"WebkitFlexShrink": 0,
|
||||
"alignItems": "stretch",
|
||||
"backgroundColor": "transparent",
|
||||
"backgroundPosition": "center",
|
||||
"backgroundRepeat": "no-repeat",
|
||||
"backgroundSize": "contain",
|
||||
"borderBottomStyle": "solid",
|
||||
"borderBottomWidth": "0px",
|
||||
"borderLeftStyle": "solid",
|
||||
"borderLeftWidth": "0px",
|
||||
"borderRightStyle": "solid",
|
||||
"borderRightWidth": "0px",
|
||||
"borderTopStyle": "solid",
|
||||
"borderTopWidth": "0px",
|
||||
"boxSizing": "border-box",
|
||||
"color": "inherit",
|
||||
"display": null,
|
||||
"flexBasis": "auto",
|
||||
"flexDirection": "column",
|
||||
"flexShrink": 0,
|
||||
"font": "inherit",
|
||||
"listStyle": "none",
|
||||
"marginBottom": "0px",
|
||||
"marginLeft": "0px",
|
||||
"marginRight": "0px",
|
||||
"marginTop": "0px",
|
||||
"minHeight": "0px",
|
||||
"minWidth": "0px",
|
||||
"msFlexAlign": "stretch",
|
||||
"msFlexDirection": "column",
|
||||
"msFlexNegative": 0,
|
||||
"msPreferredSize": "auto",
|
||||
"paddingBottom": "0px",
|
||||
"paddingLeft": "0px",
|
||||
"paddingRight": "0px",
|
||||
"paddingTop": "0px",
|
||||
"position": "relative",
|
||||
"textAlign": "inherit",
|
||||
"textDecoration": "none",
|
||||
}
|
||||
} />
|
||||
`;
|
||||
|
||||
exports[`components/Image prop "resizeMode" value "cover" 1`] = `
|
||||
<div
|
||||
className=" __style_df"
|
||||
role="img"
|
||||
style={
|
||||
Object {
|
||||
"MozBoxSizing": "border-box",
|
||||
"WebkitAlignItems": "stretch",
|
||||
"WebkitBoxAlign": "stretch",
|
||||
"WebkitBoxDirection": "normal",
|
||||
"WebkitBoxOrient": "vertical",
|
||||
"WebkitFlexBasis": "auto",
|
||||
"WebkitFlexDirection": "column",
|
||||
"WebkitFlexShrink": 0,
|
||||
"alignItems": "stretch",
|
||||
"backgroundColor": "transparent",
|
||||
"backgroundPosition": "center",
|
||||
"backgroundRepeat": "no-repeat",
|
||||
"backgroundSize": "cover",
|
||||
"borderBottomStyle": "solid",
|
||||
"borderBottomWidth": "0px",
|
||||
"borderLeftStyle": "solid",
|
||||
"borderLeftWidth": "0px",
|
||||
"borderRightStyle": "solid",
|
||||
"borderRightWidth": "0px",
|
||||
"borderTopStyle": "solid",
|
||||
"borderTopWidth": "0px",
|
||||
"boxSizing": "border-box",
|
||||
"color": "inherit",
|
||||
"display": null,
|
||||
"flexBasis": "auto",
|
||||
"flexDirection": "column",
|
||||
"flexShrink": 0,
|
||||
"font": "inherit",
|
||||
"listStyle": "none",
|
||||
"marginBottom": "0px",
|
||||
"marginLeft": "0px",
|
||||
"marginRight": "0px",
|
||||
"marginTop": "0px",
|
||||
"minHeight": "0px",
|
||||
"minWidth": "0px",
|
||||
"msFlexAlign": "stretch",
|
||||
"msFlexDirection": "column",
|
||||
"msFlexNegative": 0,
|
||||
"msPreferredSize": "auto",
|
||||
"paddingBottom": "0px",
|
||||
"paddingLeft": "0px",
|
||||
"paddingRight": "0px",
|
||||
"paddingTop": "0px",
|
||||
"position": "relative",
|
||||
"textAlign": "inherit",
|
||||
"textDecoration": "none",
|
||||
}
|
||||
} />
|
||||
`;
|
||||
|
||||
exports[`components/Image prop "resizeMode" value "none" 1`] = `
|
||||
<div
|
||||
className=" __style_df"
|
||||
role="img"
|
||||
style={
|
||||
Object {
|
||||
"MozBoxSizing": "border-box",
|
||||
"WebkitAlignItems": "stretch",
|
||||
"WebkitBoxAlign": "stretch",
|
||||
"WebkitBoxDirection": "normal",
|
||||
"WebkitBoxOrient": "vertical",
|
||||
"WebkitFlexBasis": "auto",
|
||||
"WebkitFlexDirection": "column",
|
||||
"WebkitFlexShrink": 0,
|
||||
"alignItems": "stretch",
|
||||
"backgroundColor": "transparent",
|
||||
"backgroundPosition": "center",
|
||||
"backgroundRepeat": "no-repeat",
|
||||
"backgroundSize": "auto",
|
||||
"borderBottomStyle": "solid",
|
||||
"borderBottomWidth": "0px",
|
||||
"borderLeftStyle": "solid",
|
||||
"borderLeftWidth": "0px",
|
||||
"borderRightStyle": "solid",
|
||||
"borderRightWidth": "0px",
|
||||
"borderTopStyle": "solid",
|
||||
"borderTopWidth": "0px",
|
||||
"boxSizing": "border-box",
|
||||
"color": "inherit",
|
||||
"display": null,
|
||||
"flexBasis": "auto",
|
||||
"flexDirection": "column",
|
||||
"flexShrink": 0,
|
||||
"font": "inherit",
|
||||
"listStyle": "none",
|
||||
"marginBottom": "0px",
|
||||
"marginLeft": "0px",
|
||||
"marginRight": "0px",
|
||||
"marginTop": "0px",
|
||||
"minHeight": "0px",
|
||||
"minWidth": "0px",
|
||||
"msFlexAlign": "stretch",
|
||||
"msFlexDirection": "column",
|
||||
"msFlexNegative": 0,
|
||||
"msPreferredSize": "auto",
|
||||
"paddingBottom": "0px",
|
||||
"paddingLeft": "0px",
|
||||
"paddingRight": "0px",
|
||||
"paddingTop": "0px",
|
||||
"position": "relative",
|
||||
"textAlign": "inherit",
|
||||
"textDecoration": "none",
|
||||
}
|
||||
} />
|
||||
`;
|
||||
|
||||
exports[`components/Image prop "resizeMode" value "stretch" 1`] = `
|
||||
<div
|
||||
className=" __style_df"
|
||||
role="img"
|
||||
style={
|
||||
Object {
|
||||
"MozBoxSizing": "border-box",
|
||||
"WebkitAlignItems": "stretch",
|
||||
"WebkitBoxAlign": "stretch",
|
||||
"WebkitBoxDirection": "normal",
|
||||
"WebkitBoxOrient": "vertical",
|
||||
"WebkitFlexBasis": "auto",
|
||||
"WebkitFlexDirection": "column",
|
||||
"WebkitFlexShrink": 0,
|
||||
"alignItems": "stretch",
|
||||
"backgroundColor": "transparent",
|
||||
"backgroundPosition": "center",
|
||||
"backgroundRepeat": "no-repeat",
|
||||
"backgroundSize": "100% 100%",
|
||||
"borderBottomStyle": "solid",
|
||||
"borderBottomWidth": "0px",
|
||||
"borderLeftStyle": "solid",
|
||||
"borderLeftWidth": "0px",
|
||||
"borderRightStyle": "solid",
|
||||
"borderRightWidth": "0px",
|
||||
"borderTopStyle": "solid",
|
||||
"borderTopWidth": "0px",
|
||||
"boxSizing": "border-box",
|
||||
"color": "inherit",
|
||||
"display": null,
|
||||
"flexBasis": "auto",
|
||||
"flexDirection": "column",
|
||||
"flexShrink": 0,
|
||||
"font": "inherit",
|
||||
"listStyle": "none",
|
||||
"marginBottom": "0px",
|
||||
"marginLeft": "0px",
|
||||
"marginRight": "0px",
|
||||
"marginTop": "0px",
|
||||
"minHeight": "0px",
|
||||
"minWidth": "0px",
|
||||
"msFlexAlign": "stretch",
|
||||
"msFlexDirection": "column",
|
||||
"msFlexNegative": 0,
|
||||
"msPreferredSize": "auto",
|
||||
"paddingBottom": "0px",
|
||||
"paddingLeft": "0px",
|
||||
"paddingRight": "0px",
|
||||
"paddingTop": "0px",
|
||||
"position": "relative",
|
||||
"textAlign": "inherit",
|
||||
"textDecoration": "none",
|
||||
}
|
||||
} />
|
||||
`;
|
||||
|
||||
exports[`components/Image prop "resizeMode" value "undefined" 1`] = `
|
||||
<div
|
||||
className=" __style_df"
|
||||
role="img"
|
||||
style={
|
||||
Object {
|
||||
"MozBoxSizing": "border-box",
|
||||
"WebkitAlignItems": "stretch",
|
||||
"WebkitBoxAlign": "stretch",
|
||||
"WebkitBoxDirection": "normal",
|
||||
"WebkitBoxOrient": "vertical",
|
||||
"WebkitFlexBasis": "auto",
|
||||
"WebkitFlexDirection": "column",
|
||||
"WebkitFlexShrink": 0,
|
||||
"alignItems": "stretch",
|
||||
"backgroundColor": "transparent",
|
||||
"backgroundPosition": "center",
|
||||
"backgroundRepeat": "no-repeat",
|
||||
"backgroundSize": "cover",
|
||||
"borderBottomStyle": "solid",
|
||||
"borderBottomWidth": "0px",
|
||||
"borderLeftStyle": "solid",
|
||||
"borderLeftWidth": "0px",
|
||||
"borderRightStyle": "solid",
|
||||
"borderRightWidth": "0px",
|
||||
"borderTopStyle": "solid",
|
||||
"borderTopWidth": "0px",
|
||||
"boxSizing": "border-box",
|
||||
"color": "inherit",
|
||||
"display": null,
|
||||
"flexBasis": "auto",
|
||||
"flexDirection": "column",
|
||||
"flexShrink": 0,
|
||||
"font": "inherit",
|
||||
"listStyle": "none",
|
||||
"marginBottom": "0px",
|
||||
"marginLeft": "0px",
|
||||
"marginRight": "0px",
|
||||
"marginTop": "0px",
|
||||
"minHeight": "0px",
|
||||
"minWidth": "0px",
|
||||
"msFlexAlign": "stretch",
|
||||
"msFlexDirection": "column",
|
||||
"msFlexNegative": 0,
|
||||
"msPreferredSize": "auto",
|
||||
"paddingBottom": "0px",
|
||||
"paddingLeft": "0px",
|
||||
"paddingRight": "0px",
|
||||
"paddingTop": "0px",
|
||||
"position": "relative",
|
||||
"textAlign": "inherit",
|
||||
"textDecoration": "none",
|
||||
}
|
||||
} />
|
||||
`;
|
||||
|
||||
exports[`components/Image prop "style" correctly supports "resizeMode" property 1`] = `
|
||||
<div
|
||||
className=" __style_df"
|
||||
role="img"
|
||||
style={
|
||||
Object {
|
||||
"MozBoxSizing": "border-box",
|
||||
"WebkitAlignItems": "stretch",
|
||||
"WebkitBoxAlign": "stretch",
|
||||
"WebkitBoxDirection": "normal",
|
||||
"WebkitBoxOrient": "vertical",
|
||||
"WebkitFlexBasis": "auto",
|
||||
"WebkitFlexDirection": "column",
|
||||
"WebkitFlexShrink": 0,
|
||||
"alignItems": "stretch",
|
||||
"backgroundColor": "transparent",
|
||||
"backgroundPosition": "center",
|
||||
"backgroundRepeat": "no-repeat",
|
||||
"backgroundSize": "contain",
|
||||
"borderBottomStyle": "solid",
|
||||
"borderBottomWidth": "0px",
|
||||
"borderLeftStyle": "solid",
|
||||
"borderLeftWidth": "0px",
|
||||
"borderRightStyle": "solid",
|
||||
"borderRightWidth": "0px",
|
||||
"borderTopStyle": "solid",
|
||||
"borderTopWidth": "0px",
|
||||
"boxSizing": "border-box",
|
||||
"color": "inherit",
|
||||
"display": null,
|
||||
"flexBasis": "auto",
|
||||
"flexDirection": "column",
|
||||
"flexShrink": 0,
|
||||
"font": "inherit",
|
||||
"listStyle": "none",
|
||||
"marginBottom": "0px",
|
||||
"marginLeft": "0px",
|
||||
"marginRight": "0px",
|
||||
"marginTop": "0px",
|
||||
"minHeight": "0px",
|
||||
"minWidth": "0px",
|
||||
"msFlexAlign": "stretch",
|
||||
"msFlexDirection": "column",
|
||||
"msFlexNegative": 0,
|
||||
"msPreferredSize": "auto",
|
||||
"paddingBottom": "0px",
|
||||
"paddingLeft": "0px",
|
||||
"paddingRight": "0px",
|
||||
"paddingTop": "0px",
|
||||
"position": "relative",
|
||||
"textAlign": "inherit",
|
||||
"textDecoration": "none",
|
||||
}
|
||||
} />
|
||||
`;
|
||||
|
||||
exports[`components/Image prop "testID" 1`] = `
|
||||
<div
|
||||
className=" __style_df"
|
||||
data-testid="testID"
|
||||
role="img"
|
||||
style={
|
||||
Object {
|
||||
"MozBoxSizing": "border-box",
|
||||
"WebkitAlignItems": "stretch",
|
||||
"WebkitBoxAlign": "stretch",
|
||||
"WebkitBoxDirection": "normal",
|
||||
"WebkitBoxOrient": "vertical",
|
||||
"WebkitFlexBasis": "auto",
|
||||
"WebkitFlexDirection": "column",
|
||||
"WebkitFlexShrink": 0,
|
||||
"alignItems": "stretch",
|
||||
"backgroundColor": "transparent",
|
||||
"backgroundPosition": "center",
|
||||
"backgroundRepeat": "no-repeat",
|
||||
"backgroundSize": "cover",
|
||||
"borderBottomStyle": "solid",
|
||||
"borderBottomWidth": "0px",
|
||||
"borderLeftStyle": "solid",
|
||||
"borderLeftWidth": "0px",
|
||||
"borderRightStyle": "solid",
|
||||
"borderRightWidth": "0px",
|
||||
"borderTopStyle": "solid",
|
||||
"borderTopWidth": "0px",
|
||||
"boxSizing": "border-box",
|
||||
"color": "inherit",
|
||||
"display": null,
|
||||
"flexBasis": "auto",
|
||||
"flexDirection": "column",
|
||||
"flexShrink": 0,
|
||||
"font": "inherit",
|
||||
"listStyle": "none",
|
||||
"marginBottom": "0px",
|
||||
"marginLeft": "0px",
|
||||
"marginRight": "0px",
|
||||
"marginTop": "0px",
|
||||
"minHeight": "0px",
|
||||
"minWidth": "0px",
|
||||
"msFlexAlign": "stretch",
|
||||
"msFlexDirection": "column",
|
||||
"msFlexNegative": 0,
|
||||
"msPreferredSize": "auto",
|
||||
"paddingBottom": "0px",
|
||||
"paddingLeft": "0px",
|
||||
"paddingRight": "0px",
|
||||
"paddingTop": "0px",
|
||||
"position": "relative",
|
||||
"textAlign": "inherit",
|
||||
"textDecoration": "none",
|
||||
}
|
||||
} />
|
||||
`;
|
||||
|
||||
exports[`components/Image sets correct accessibility role" 1`] = `
|
||||
<div
|
||||
className=" __style_df"
|
||||
role="img"
|
||||
style={
|
||||
Object {
|
||||
"MozBoxSizing": "border-box",
|
||||
"WebkitAlignItems": "stretch",
|
||||
"WebkitBoxAlign": "stretch",
|
||||
"WebkitBoxDirection": "normal",
|
||||
"WebkitBoxOrient": "vertical",
|
||||
"WebkitFlexBasis": "auto",
|
||||
"WebkitFlexDirection": "column",
|
||||
"WebkitFlexShrink": 0,
|
||||
"alignItems": "stretch",
|
||||
"backgroundColor": "transparent",
|
||||
"backgroundPosition": "center",
|
||||
"backgroundRepeat": "no-repeat",
|
||||
"backgroundSize": "cover",
|
||||
"borderBottomStyle": "solid",
|
||||
"borderBottomWidth": "0px",
|
||||
"borderLeftStyle": "solid",
|
||||
"borderLeftWidth": "0px",
|
||||
"borderRightStyle": "solid",
|
||||
"borderRightWidth": "0px",
|
||||
"borderTopStyle": "solid",
|
||||
"borderTopWidth": "0px",
|
||||
"boxSizing": "border-box",
|
||||
"color": "inherit",
|
||||
"display": null,
|
||||
"flexBasis": "auto",
|
||||
"flexDirection": "column",
|
||||
"flexShrink": 0,
|
||||
"font": "inherit",
|
||||
"listStyle": "none",
|
||||
"marginBottom": "0px",
|
||||
"marginLeft": "0px",
|
||||
"marginRight": "0px",
|
||||
"marginTop": "0px",
|
||||
"minHeight": "0px",
|
||||
"minWidth": "0px",
|
||||
"msFlexAlign": "stretch",
|
||||
"msFlexDirection": "column",
|
||||
"msFlexNegative": 0,
|
||||
"msPreferredSize": "auto",
|
||||
"paddingBottom": "0px",
|
||||
"paddingLeft": "0px",
|
||||
"paddingRight": "0px",
|
||||
"paddingTop": "0px",
|
||||
"position": "relative",
|
||||
"textAlign": "inherit",
|
||||
"textDecoration": "none",
|
||||
}
|
||||
} />
|
||||
`;
|
||||
@@ -1,161 +1,100 @@
|
||||
/* eslint-env mocha */
|
||||
/* eslint-env jasmine, jest */
|
||||
|
||||
import assert from 'assert';
|
||||
import Image from '../';
|
||||
import React from 'react';
|
||||
import StyleSheet from '../../../apis/StyleSheet';
|
||||
import { mount, shallow } from 'enzyme';
|
||||
import renderer from 'react-test-renderer';
|
||||
|
||||
jest.mock('react-dom');
|
||||
|
||||
const originalImage = window.Image;
|
||||
|
||||
describe('components/Image', () => {
|
||||
beforeEach(() => {
|
||||
window.Image = jest.fn(() => ({}));
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
window.Image = originalImage;
|
||||
});
|
||||
|
||||
suite('components/Image', () => {
|
||||
test('sets correct accessibility role"', () => {
|
||||
const image = shallow(<Image />);
|
||||
assert.equal(image.prop('accessibilityRole'), 'img');
|
||||
const component = renderer.create(<Image />);
|
||||
expect(component.toJSON()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('prop "accessibilityLabel"', () => {
|
||||
const accessibilityLabel = 'accessibilityLabel';
|
||||
const image = shallow(<Image accessibilityLabel={accessibilityLabel} />);
|
||||
assert.equal(image.prop('accessibilityLabel'), accessibilityLabel);
|
||||
const component = renderer.create(<Image accessibilityLabel='accessibilityLabel' />);
|
||||
expect(component.toJSON()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('prop "accessible"', () => {
|
||||
const accessible = false;
|
||||
const image = shallow(<Image accessible={accessible} />);
|
||||
assert.equal(image.prop('accessible'), accessible);
|
||||
const component = renderer.create(<Image accessible={false} />);
|
||||
expect(component.toJSON()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('prop "children"', () => {
|
||||
const children = <div className='unique' />;
|
||||
const wrapper = shallow(<Image>{children}</Image>);
|
||||
assert.equal(wrapper.contains(children), true);
|
||||
const component = renderer.create(<Image children={children} />);
|
||||
expect(component.toJSON()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
suite('prop "defaultSource"', () => {
|
||||
describe('prop "defaultSource"', () => {
|
||||
test('sets background image when value is an object', () => {
|
||||
const defaultSource = { uri: 'https://google.com/favicon.ico' };
|
||||
const image = shallow(<Image defaultSource={defaultSource} />);
|
||||
const backgroundImage = StyleSheet.flatten(image.prop('style')).backgroundImage;
|
||||
assert(backgroundImage.indexOf(defaultSource.uri) > -1);
|
||||
const component = renderer.create(<Image defaultSource={defaultSource} />);
|
||||
expect(component.toJSON()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('sets background image when value is a string', () => {
|
||||
// emulate require-ed asset
|
||||
const defaultSource = 'https://google.com/favicon.ico';
|
||||
const image = shallow(<Image defaultSource={defaultSource} />);
|
||||
const backgroundImage = StyleSheet.flatten(image.prop('style')).backgroundImage;
|
||||
assert(backgroundImage.indexOf(defaultSource) > -1);
|
||||
const component = renderer.create(<Image defaultSource={defaultSource} />);
|
||||
expect(component.toJSON()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('sets "height" and "width" styles if missing', () => {
|
||||
const defaultSource = { uri: 'https://google.com/favicon.ico', height: 10, width: 20 };
|
||||
const component = renderer.create(<Image defaultSource={defaultSource} />);
|
||||
expect(component.toJSON()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('does not override "height" and "width" styles', () => {
|
||||
const defaultSource = { uri: 'https://google.com/favicon.ico', height: 10, width: 20 };
|
||||
const component = renderer.create(<Image defaultSource={defaultSource} style={{ height: 20, width: 40 }} />);
|
||||
expect(component.toJSON()).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
test('prop "onError"', function (done) {
|
||||
this.timeout(5000);
|
||||
mount(<Image onError={onError} source={{ uri: 'https://google.com/favicon.icox' }} />);
|
||||
function onError(e) {
|
||||
assert.equal(e.nativeEvent.type, 'error');
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
test('prop "onLoad"', function (done) {
|
||||
this.timeout(5000);
|
||||
const image = mount(<Image onLoad={onLoad} source={{ uri: 'https://google.com/favicon.ico' }} />);
|
||||
function onLoad(e) {
|
||||
assert.equal(e.nativeEvent.type, 'load');
|
||||
const hasBackgroundImage = (image.html()).indexOf('url("https://google.com/favicon.ico")') > -1;
|
||||
assert.equal(hasBackgroundImage, true);
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
test('prop "onLoadEnd"', function (done) {
|
||||
this.timeout(5000);
|
||||
const image = mount(<Image onLoadEnd={onLoadEnd} source={{ uri: 'https://google.com/favicon.ico' }} />);
|
||||
function onLoadEnd() {
|
||||
assert.ok(true);
|
||||
const hasBackgroundImage = (image.html()).indexOf('url("https://google.com/favicon.ico")') > -1;
|
||||
assert.equal(hasBackgroundImage, true);
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
test('prop "onLoadStart"', function (done) {
|
||||
this.timeout(5000);
|
||||
mount(<Image onLoadStart={onLoadStart} source={{ uri: 'https://google.com/favicon.ico' }} />);
|
||||
function onLoadStart() {
|
||||
assert.ok(true);
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
suite('prop "resizeMode"', () => {
|
||||
const getBackgroundSize = (image) => StyleSheet.flatten(image.prop('style')).backgroundSize;
|
||||
|
||||
test('value "contain"', () => {
|
||||
const image = shallow(<Image resizeMode={Image.resizeMode.contain} />);
|
||||
assert.equal(getBackgroundSize(image), 'contain');
|
||||
});
|
||||
|
||||
test('value "cover"', () => {
|
||||
const image = shallow(<Image resizeMode={Image.resizeMode.cover} />);
|
||||
assert.equal(getBackgroundSize(image), 'cover');
|
||||
});
|
||||
|
||||
test('value "none"', () => {
|
||||
const image = shallow(<Image resizeMode={Image.resizeMode.none} />);
|
||||
assert.equal(getBackgroundSize(image), 'auto');
|
||||
});
|
||||
|
||||
test('value "stretch"', () => {
|
||||
const image = shallow(<Image resizeMode={Image.resizeMode.stretch} />);
|
||||
assert.equal(getBackgroundSize(image), '100% 100%');
|
||||
});
|
||||
|
||||
test('no value', () => {
|
||||
const image = shallow(<Image />);
|
||||
assert.equal(getBackgroundSize(image), 'cover');
|
||||
describe('prop "resizeMode"', () => {
|
||||
[
|
||||
Image.resizeMode.contain,
|
||||
Image.resizeMode.cover,
|
||||
Image.resizeMode.none,
|
||||
Image.resizeMode.stretch,
|
||||
undefined
|
||||
].forEach((resizeMode) => {
|
||||
test(`value "${resizeMode}"`, () => {
|
||||
const component = renderer.create(<Image resizeMode={resizeMode} />);
|
||||
expect(component.toJSON()).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
suite('prop "source"', function () {
|
||||
this.timeout(5000);
|
||||
|
||||
test('sets background image when value is an object', (done) => {
|
||||
const source = { uri: 'https://google.com/favicon.ico' };
|
||||
mount(<Image onLoad={onLoad} source={source} />);
|
||||
function onLoad(e) {
|
||||
const src = e.nativeEvent.target.src;
|
||||
assert.equal(src, source.uri);
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
test('sets background image when value is a string', (done) => {
|
||||
// emulate require-ed asset
|
||||
const source = 'https://google.com/favicon.ico';
|
||||
mount(<Image onLoad={onLoad} source={source} />);
|
||||
function onLoad(e) {
|
||||
const src = e.nativeEvent.target.src;
|
||||
assert.equal(src, source);
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
suite('prop "style"', () => {
|
||||
test('converts "resizeMode" property', () => {
|
||||
const image = shallow(<Image style={{ resizeMode: Image.resizeMode.contain }} />);
|
||||
assert.equal(StyleSheet.flatten(image.prop('style')).backgroundSize, 'contain');
|
||||
});
|
||||
|
||||
test('removes "resizeMode" property', () => {
|
||||
const image = shallow(<Image style={{ resizeMode: Image.resizeMode.contain }} />);
|
||||
assert.equal(StyleSheet.flatten(image.prop('style')).resizeMode, undefined);
|
||||
describe('prop "style"', () => {
|
||||
test('correctly supports "resizeMode" property', () => {
|
||||
const component = renderer.create(<Image style={{ resizeMode: Image.resizeMode.contain }} />);
|
||||
expect(component.toJSON()).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
test('prop "testID"', () => {
|
||||
const testID = 'testID';
|
||||
const image = shallow(<Image testID={testID} />);
|
||||
assert.equal(image.prop('testID'), testID);
|
||||
const component = renderer.create(<Image testID='testID' />);
|
||||
expect(component.toJSON()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('passes other props through to underlying View', () => {
|
||||
const fn = () => {};
|
||||
const component = renderer.create(<Image onResponderGrant={fn} />);
|
||||
expect(component.toJSON()).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
/* global window */
|
||||
import applyNativeMethods from '../../modules/applyNativeMethods';
|
||||
import BaseComponentPropTypes from '../../propTypes/BaseComponentPropTypes';
|
||||
import createDOMElement from '../../modules/createDOMElement';
|
||||
import ImageResizeMode from './ImageResizeMode';
|
||||
import ImageStylePropTypes from './ImageStylePropTypes';
|
||||
import requestAnimationFrame from 'fbjs/lib/requestAnimationFrame';
|
||||
import StyleSheet from '../../apis/StyleSheet';
|
||||
import StyleSheetPropType from '../../propTypes/StyleSheetPropType';
|
||||
import View from '../View';
|
||||
@@ -17,11 +16,20 @@ const STATUS_IDLE = 'IDLE';
|
||||
|
||||
const ImageSourcePropType = PropTypes.oneOfType([
|
||||
PropTypes.shape({
|
||||
uri: PropTypes.string.isRequired
|
||||
height: PropTypes.number,
|
||||
uri: PropTypes.string.isRequired,
|
||||
width: PropTypes.number
|
||||
}),
|
||||
PropTypes.string
|
||||
]);
|
||||
|
||||
const resolveAssetDimensions = (source) => {
|
||||
if (typeof source === 'object') {
|
||||
const { height, width } = source;
|
||||
return { height, width };
|
||||
}
|
||||
};
|
||||
|
||||
const resolveAssetSource = (source) => {
|
||||
return ((typeof source === 'object') ? source.uri : source) || null;
|
||||
};
|
||||
@@ -30,7 +38,7 @@ class Image extends Component {
|
||||
static displayName = 'Image';
|
||||
|
||||
static propTypes = {
|
||||
...BaseComponentPropTypes,
|
||||
...View.propTypes,
|
||||
children: PropTypes.any,
|
||||
defaultSource: ImageSourcePropType,
|
||||
onError: PropTypes.func,
|
||||
@@ -44,7 +52,6 @@ class Image extends Component {
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
accessible: true,
|
||||
style: {}
|
||||
};
|
||||
|
||||
@@ -55,12 +62,14 @@ class Image extends Component {
|
||||
this.state = { isLoaded: false };
|
||||
const uri = resolveAssetSource(props.source);
|
||||
this._imageState = uri ? STATUS_PENDING : STATUS_IDLE;
|
||||
this._isMounted = false;
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
if (this._imageState === STATUS_PENDING) {
|
||||
this._createImageLoader();
|
||||
}
|
||||
this._isMounted = true;
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
@@ -78,6 +87,7 @@ class Image extends Component {
|
||||
|
||||
componentWillUnmount() {
|
||||
this._destroyImageLoader();
|
||||
this._isMounted = false;
|
||||
}
|
||||
|
||||
render() {
|
||||
@@ -89,44 +99,40 @@ class Image extends Component {
|
||||
defaultSource,
|
||||
onLayout,
|
||||
source,
|
||||
testID
|
||||
testID,
|
||||
/* eslint-disable */
|
||||
resizeMode,
|
||||
/* eslint-enable */
|
||||
...other
|
||||
} = this.props;
|
||||
|
||||
const displayImage = resolveAssetSource(!isLoaded ? defaultSource : source);
|
||||
const imageSizeStyle = resolveAssetDimensions(!isLoaded ? defaultSource : source);
|
||||
const backgroundImage = displayImage ? `url("${displayImage}")` : null;
|
||||
let style = StyleSheet.flatten(this.props.style);
|
||||
const originalStyle = StyleSheet.flatten(this.props.style);
|
||||
const finalResizeMode = resizeMode || originalStyle.resizeMode || ImageResizeMode.cover;
|
||||
|
||||
const resizeMode = this.props.resizeMode || style.resizeMode || ImageResizeMode.cover;
|
||||
// remove 'resizeMode' style, as it is not supported by View (N.B. styles are frozen in dev)
|
||||
style = process.env.NODE_ENV !== 'production' ? { ...style } : style;
|
||||
const style = StyleSheet.flatten([
|
||||
styles.initial,
|
||||
imageSizeStyle,
|
||||
originalStyle,
|
||||
backgroundImage && { backgroundImage },
|
||||
resizeModeStyles[finalResizeMode]
|
||||
]);
|
||||
// View doesn't support 'resizeMode' as a style
|
||||
delete style.resizeMode;
|
||||
|
||||
/**
|
||||
* Image is a non-stretching View. The image is displayed as a background
|
||||
* image to support `resizeMode`. The HTML image is hidden but used to
|
||||
* provide the correct responsive image dimensions, and to support the
|
||||
* image context menu. Child content is rendered into an element absolutely
|
||||
* positioned over the image.
|
||||
*/
|
||||
return (
|
||||
<View
|
||||
{...other}
|
||||
accessibilityLabel={accessibilityLabel}
|
||||
accessibilityRole='img'
|
||||
accessible={accessible}
|
||||
children={children}
|
||||
onLayout={onLayout}
|
||||
style={[
|
||||
styles.initial,
|
||||
style,
|
||||
backgroundImage && { backgroundImage },
|
||||
resizeModeStyles[resizeMode]
|
||||
]}
|
||||
style={style}
|
||||
testID={testID}
|
||||
>
|
||||
{createDOMElement('img', { src: displayImage, style: styles.img })}
|
||||
{children ? (
|
||||
<View children={children} pointerEvents='box-none' style={styles.children} />
|
||||
) : null}
|
||||
</View>
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -149,14 +155,18 @@ class Image extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
_onError = (e) => {
|
||||
const { onError } = this.props;
|
||||
const event = { nativeEvent: e };
|
||||
|
||||
_onError = () => {
|
||||
const { onError, source } = this.props;
|
||||
this._destroyImageLoader();
|
||||
this._updateImageState(STATUS_ERRORED);
|
||||
this._onLoadEnd();
|
||||
if (onError) { onError(event); }
|
||||
this._updateImageState(STATUS_ERRORED);
|
||||
if (onError) {
|
||||
onError({
|
||||
nativeEvent: {
|
||||
error: `Failed to load resource ${resolveAssetSource(source)} (404)`
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
_onLoad = (e) => {
|
||||
@@ -184,32 +194,21 @@ class Image extends Component {
|
||||
this._imageState = status;
|
||||
const isLoaded = this._imageState === STATUS_LOADED;
|
||||
if (isLoaded !== this.state.isLoaded) {
|
||||
this.setState({ isLoaded });
|
||||
requestAnimationFrame(() => {
|
||||
if (this._isMounted) {
|
||||
this.setState({ isLoaded });
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
initial: {
|
||||
alignSelf: 'flex-start',
|
||||
backgroundColor: 'transparent',
|
||||
backgroundPosition: 'center',
|
||||
backgroundRepeat: 'no-repeat',
|
||||
backgroundSize: 'cover'
|
||||
},
|
||||
img: {
|
||||
borderWidth: 0,
|
||||
height: 'auto',
|
||||
maxHeight: '100%',
|
||||
maxWidth: '100%',
|
||||
opacity: 0
|
||||
},
|
||||
children: {
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
position: 'absolute',
|
||||
right: 0,
|
||||
top: 0
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* eslint-env mocha */
|
||||
/* eslint-env jasmine, jest */
|
||||
|
||||
suite('components/ListView', () => {
|
||||
describe('components/ListView', () => {
|
||||
test('NO TEST COVERAGE');
|
||||
});
|
||||
|
||||
@@ -1,22 +1,28 @@
|
||||
import applyNativeMethods from '../../modules/applyNativeMethods';
|
||||
import ListViewDataSource from './ListViewDataSource';
|
||||
import ListViewPropTypes from './ListViewPropTypes';
|
||||
import pick from 'lodash/pick';
|
||||
import ScrollView from '../ScrollView';
|
||||
import View from '../View';
|
||||
import React, { Component } from 'react';
|
||||
import StaticRenderer from '../StaticRenderer';
|
||||
import React, { Component, isEmpty, merge } from 'react';
|
||||
import requestAnimationFrame from 'fbjs/lib/requestAnimationFrame';
|
||||
|
||||
const scrollViewProps = Object.keys(ScrollView.propTypes);
|
||||
const DEFAULT_PAGE_SIZE = 1;
|
||||
const DEFAULT_INITIAL_ROWS = 10;
|
||||
const DEFAULT_SCROLL_RENDER_AHEAD = 1000;
|
||||
const DEFAULT_END_REACHED_THRESHOLD = 1000;
|
||||
const DEFAULT_SCROLL_CALLBACK_THROTTLE = 50;
|
||||
|
||||
class ListView extends Component {
|
||||
static propTypes = ListViewPropTypes;
|
||||
|
||||
static defaultProps = {
|
||||
initialListSize: 10,
|
||||
pageSize: 1,
|
||||
initialListSize: DEFAULT_INITIAL_ROWS,
|
||||
pageSize: DEFAULT_PAGE_SIZE,
|
||||
renderScrollComponent: (props) => <ScrollView {...props} />,
|
||||
scrollRenderAheadDistance: 1000,
|
||||
onEndReachedThreshold: 1000,
|
||||
scrollRenderAheadDistance: DEFAULT_SCROLL_RENDER_AHEAD,
|
||||
onEndReachedThreshold: DEFAULT_END_REACHED_THRESHOLD,
|
||||
scrollEventThrottle: DEFAULT_SCROLL_CALLBACK_THROTTLE,
|
||||
removeClippedSubviews: true,
|
||||
stickyHeaderIndices: []
|
||||
};
|
||||
|
||||
@@ -29,6 +35,34 @@ class ListView extends Component {
|
||||
highlightedRow: {}
|
||||
};
|
||||
this.onRowHighlighted = (sectionId, rowId) => this._onRowHighlighted(sectionId, rowId);
|
||||
this.scrollProperties = {};
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
// this data should never trigger a render pass, so don't put in state
|
||||
this.scrollProperties = {
|
||||
visibleLength: null,
|
||||
contentLength: null,
|
||||
offset: 0
|
||||
};
|
||||
this._childFrames = [];
|
||||
this._visibleRows = {};
|
||||
this._prevRenderedRowsCount = 0;
|
||||
this._sentEndForContentLength = null;
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
// do this in animation frame until componentDidMount actually runs after
|
||||
// the component is laid out
|
||||
requestAnimationFrame(() => {
|
||||
this._measureAndUpdateScrollProps();
|
||||
});
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
requestAnimationFrame(() => {
|
||||
this._measureAndUpdateScrollProps();
|
||||
});
|
||||
}
|
||||
|
||||
getScrollResponder() {
|
||||
@@ -43,60 +77,313 @@ class ListView extends Component {
|
||||
return this._scrollViewRef && this._scrollViewRef.setNativeProps(props);
|
||||
}
|
||||
|
||||
_onRowHighlighted(sectionId, rowId) {
|
||||
_onRowHighlighted = (sectionId, rowId) => {
|
||||
this.setState({ highlightedRow: { sectionId, rowId } });
|
||||
}
|
||||
|
||||
renderSectionHeaderFn = (data, sectionID) => {
|
||||
return () => this.props.renderSectionHeader(data, sectionID);
|
||||
}
|
||||
|
||||
renderRowFn = (data, sectionID, rowID) => {
|
||||
return () => this.props.renderRow(data, sectionID, rowID, this._onRowHighlighted);
|
||||
}
|
||||
|
||||
render() {
|
||||
const dataSource = this.props.dataSource;
|
||||
const header = this.props.renderHeader ? this.props.renderHeader() : undefined;
|
||||
const footer = this.props.renderFooter ? this.props.renderFooter() : undefined;
|
||||
|
||||
// render sections and rows
|
||||
const children = [];
|
||||
const sections = dataSource.rowIdentities;
|
||||
const renderRow = this.props.renderRow;
|
||||
const renderSectionHeader = this.props.renderSectionHeader;
|
||||
const renderSeparator = this.props.renderSeparator;
|
||||
for (let sectionIdx = 0, sectionCnt = sections.length; sectionIdx < sectionCnt; sectionIdx++) {
|
||||
const rows = sections[sectionIdx];
|
||||
const sectionId = dataSource.sectionIdentities[sectionIdx];
|
||||
|
||||
// render optional section header
|
||||
if (renderSectionHeader) {
|
||||
const section = dataSource.getSectionHeaderData(sectionIdx);
|
||||
const key = `s_${sectionId}`;
|
||||
const child = <View key={key}>{renderSectionHeader(section, sectionId)}</View>;
|
||||
children.push(child);
|
||||
const dataSource = this.props.dataSource;
|
||||
const allRowIDs = dataSource.rowIdentities;
|
||||
let rowCount = 0;
|
||||
const sectionHeaderIndices = [];
|
||||
|
||||
const header = this.props.renderHeader && this.props.renderHeader();
|
||||
const footer = this.props.renderFooter && this.props.renderFooter();
|
||||
let totalIndex = header ? 1 : 0;
|
||||
|
||||
for (let sectionIdx = 0; sectionIdx < allRowIDs.length; sectionIdx++) {
|
||||
const sectionID = dataSource.sectionIdentities[sectionIdx];
|
||||
const rowIDs = allRowIDs[sectionIdx];
|
||||
if (rowIDs.length === 0) {
|
||||
if (this.props.enableEmptySections === undefined) {
|
||||
const warning = require('fbjs/lib/warning');
|
||||
warning(false, 'In next release empty section headers will be rendered.' +
|
||||
' In this release you can use \'enableEmptySections\' flag to render empty section headers.');
|
||||
continue;
|
||||
} else {
|
||||
const invariant = require('fbjs/lib/invariant');
|
||||
invariant(
|
||||
this.props.enableEmptySections,
|
||||
'In next release \'enableEmptySections\' flag will be deprecated,' +
|
||||
' empty section headers will always be rendered. If empty section headers' +
|
||||
' are not desirable their indices should be excluded from sectionIDs object.' +
|
||||
' In this release \'enableEmptySections\' may only have value \'true\'' +
|
||||
' to allow empty section headers rendering.');
|
||||
}
|
||||
}
|
||||
|
||||
// render rows
|
||||
for (let rowIdx = 0, rowCnt = rows.length; rowIdx < rowCnt; rowIdx++) {
|
||||
const rowId = rows[rowIdx];
|
||||
const row = dataSource.getRowData(sectionIdx, rowIdx);
|
||||
const key = `r_${sectionId}_${rowId}`;
|
||||
const child = <View key={key}>{renderRow(row, sectionId, rowId, this.onRowHighlighted)}</View>;
|
||||
children.push(child);
|
||||
if (this.props.renderSectionHeader) {
|
||||
const shouldUpdateHeader = rowCount >= this._prevRenderedRowsCount &&
|
||||
dataSource.sectionHeaderShouldUpdate(sectionIdx);
|
||||
children.push(
|
||||
<StaticRenderer
|
||||
key={`s_${sectionID}`}
|
||||
render={this.renderSectionHeaderFn(
|
||||
dataSource.getSectionHeaderData(sectionIdx),
|
||||
sectionID
|
||||
)}
|
||||
shouldUpdate={!!shouldUpdateHeader}
|
||||
/>
|
||||
);
|
||||
sectionHeaderIndices.push(totalIndex++);
|
||||
}
|
||||
|
||||
// render optional separator
|
||||
if (renderSeparator && ((rowIdx !== rows.length - 1) || (sectionIdx === sections.length - 1))) {
|
||||
for (let rowIdx = 0; rowIdx < rowIDs.length; rowIdx++) {
|
||||
const rowID = rowIDs[rowIdx];
|
||||
const comboID = `${sectionID}_${rowID}`;
|
||||
const shouldUpdateRow = rowCount >= this._prevRenderedRowsCount &&
|
||||
dataSource.rowShouldUpdate(sectionIdx, rowIdx);
|
||||
const row =
|
||||
<StaticRenderer
|
||||
key={`r_${comboID}`}
|
||||
render={this.renderRowFn(
|
||||
dataSource.getRowData(sectionIdx, rowIdx),
|
||||
sectionID,
|
||||
rowID
|
||||
)}
|
||||
shouldUpdate={!!shouldUpdateRow}
|
||||
/>;
|
||||
children.push(row);
|
||||
totalIndex++;
|
||||
|
||||
if (this.props.renderSeparator &&
|
||||
(rowIdx !== rowIDs.length - 1 || sectionIdx === allRowIDs.length - 1)) {
|
||||
const adjacentRowHighlighted =
|
||||
this.state.highlightedRow.sectionID === sectionId && (
|
||||
this.state.highlightedRow.rowID === rowId ||
|
||||
this.state.highlightedRow.rowID === rows[rowIdx + 1]);
|
||||
const separator = renderSeparator(sectionId, rowId, adjacentRowHighlighted);
|
||||
children.push(separator);
|
||||
this.state.highlightedRow.sectionID === sectionID && (
|
||||
this.state.highlightedRow.rowID === rowID ||
|
||||
this.state.highlightedRow.rowID === rowIDs[rowIdx + 1]
|
||||
);
|
||||
const separator = this.props.renderSeparator(
|
||||
sectionID,
|
||||
rowID,
|
||||
adjacentRowHighlighted
|
||||
);
|
||||
if (separator) {
|
||||
children.push(separator);
|
||||
totalIndex++;
|
||||
}
|
||||
}
|
||||
if (++rowCount === this.state.curRenderedRowsCount) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (rowCount >= this.state.curRenderedRowsCount) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const props = pick(this.props, scrollViewProps);
|
||||
const {
|
||||
renderScrollComponent,
|
||||
...props
|
||||
} = this.props;
|
||||
Object.assign(props, {
|
||||
onScroll: this._onScroll,
|
||||
stickyHeaderIndices: this.props.stickyHeaderIndices.concat(sectionHeaderIndices),
|
||||
|
||||
return React.cloneElement(this.props.renderScrollComponent(props), {
|
||||
ref: this._setScrollViewRef
|
||||
// Do not pass these events downstream to ScrollView since they will be
|
||||
// registered in ListView's own ScrollResponder.Mixin
|
||||
onKeyboardWillShow: undefined,
|
||||
onKeyboardWillHide: undefined,
|
||||
onKeyboardDidShow: undefined,
|
||||
onKeyboardDidHide: undefined
|
||||
});
|
||||
|
||||
return React.cloneElement(renderScrollComponent(props), {
|
||||
ref: this._setScrollViewRef,
|
||||
onContentSizeChange: this._onContentSizeChange,
|
||||
onLayout: this._onLayout
|
||||
}, header, children, footer);
|
||||
}
|
||||
|
||||
_measureAndUpdateScrollProps() {
|
||||
const scrollComponent = this.getScrollResponder();
|
||||
if (!scrollComponent || !scrollComponent.getInnerViewNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._updateVisibleRows();
|
||||
}
|
||||
|
||||
_onLayout = (event: Object) => {
|
||||
const { width, height } = event.nativeEvent.layout;
|
||||
const visibleLength = !this.props.horizontal ? height : width;
|
||||
if (visibleLength !== this.scrollProperties.visibleLength) {
|
||||
this.scrollProperties.visibleLength = visibleLength;
|
||||
this._updateVisibleRows();
|
||||
this._renderMoreRowsIfNeeded();
|
||||
}
|
||||
this.props.onLayout && this.props.onLayout(event);
|
||||
}
|
||||
|
||||
_updateVisibleRows(updatedFrames?: Array<Object>) {
|
||||
if (!this.props.onChangeVisibleRows) {
|
||||
return; // No need to compute visible rows if there is no callback
|
||||
}
|
||||
if (updatedFrames) {
|
||||
updatedFrames.forEach((newFrame) => {
|
||||
this._childFrames[newFrame.index] = merge(newFrame);
|
||||
});
|
||||
}
|
||||
const isVertical = !this.props.horizontal;
|
||||
const dataSource = this.props.dataSource;
|
||||
const visibleMin = this.scrollProperties.offset;
|
||||
const visibleMax = visibleMin + this.scrollProperties.visibleLength;
|
||||
const allRowIDs = dataSource.rowIdentities;
|
||||
|
||||
const header = this.props.renderHeader && this.props.renderHeader();
|
||||
let totalIndex = header ? 1 : 0;
|
||||
let visibilityChanged = false;
|
||||
const changedRows = {};
|
||||
for (let sectionIdx = 0; sectionIdx < allRowIDs.length; sectionIdx++) {
|
||||
const rowIDs = allRowIDs[sectionIdx];
|
||||
if (rowIDs.length === 0) {
|
||||
continue;
|
||||
}
|
||||
const sectionID = dataSource.sectionIdentities[sectionIdx];
|
||||
if (this.props.renderSectionHeader) {
|
||||
totalIndex++;
|
||||
}
|
||||
let visibleSection = this._visibleRows[sectionID];
|
||||
if (!visibleSection) {
|
||||
visibleSection = {};
|
||||
}
|
||||
for (let rowIdx = 0; rowIdx < rowIDs.length; rowIdx++) {
|
||||
const rowID = rowIDs[rowIdx];
|
||||
const frame = this._childFrames[totalIndex];
|
||||
totalIndex++;
|
||||
if (this.props.renderSeparator &&
|
||||
(rowIdx !== rowIDs.length - 1 || sectionIdx === allRowIDs.length - 1)) {
|
||||
totalIndex++;
|
||||
}
|
||||
if (!frame) {
|
||||
break;
|
||||
}
|
||||
const rowVisible = visibleSection[rowID];
|
||||
const min = isVertical ? frame.y : frame.x;
|
||||
const max = min + (isVertical ? frame.height : frame.width);
|
||||
if ((!min && !max) || (min === max)) {
|
||||
break;
|
||||
}
|
||||
if (min > visibleMax || max < visibleMin) {
|
||||
if (rowVisible) {
|
||||
visibilityChanged = true;
|
||||
delete visibleSection[rowID];
|
||||
if (!changedRows[sectionID]) {
|
||||
changedRows[sectionID] = {};
|
||||
}
|
||||
changedRows[sectionID][rowID] = false;
|
||||
}
|
||||
} else if (!rowVisible) {
|
||||
visibilityChanged = true;
|
||||
visibleSection[rowID] = true;
|
||||
if (!changedRows[sectionID]) {
|
||||
changedRows[sectionID] = {};
|
||||
}
|
||||
changedRows[sectionID][rowID] = true;
|
||||
}
|
||||
}
|
||||
if (!isEmpty(visibleSection)) {
|
||||
this._visibleRows[sectionID] = visibleSection;
|
||||
} else if (this._visibleRows[sectionID]) {
|
||||
delete this._visibleRows[sectionID];
|
||||
}
|
||||
}
|
||||
visibilityChanged && this.props.onChangeVisibleRows(this._visibleRows, changedRows);
|
||||
}
|
||||
|
||||
_onContentSizeChange = (width: number, height: number) => {
|
||||
const contentLength = !this.props.horizontal ? height : width;
|
||||
if (contentLength !== this.scrollProperties.contentLength) {
|
||||
this.scrollProperties.contentLength = contentLength;
|
||||
this._updateVisibleRows();
|
||||
this._renderMoreRowsIfNeeded();
|
||||
}
|
||||
this.props.onContentSizeChange && this.props.onContentSizeChange(width, height);
|
||||
}
|
||||
|
||||
_getDistanceFromEnd(scrollProperties: Object) {
|
||||
return scrollProperties.contentLength - scrollProperties.visibleLength - scrollProperties.offset;
|
||||
}
|
||||
|
||||
_maybeCallOnEndReached(event?: Object) {
|
||||
if (this.props.onEndReached &&
|
||||
this.scrollProperties.contentLength !== this._sentEndForContentLength &&
|
||||
this._getDistanceFromEnd(this.scrollProperties) < this.props.onEndReachedThreshold &&
|
||||
this.state.curRenderedRowsCount === (this.props.enableEmptySections ?
|
||||
this.props.dataSource.getRowAndSectionCount() : this.props.dataSource.getRowCount())) {
|
||||
this._sentEndForContentLength = this.scrollProperties.contentLength;
|
||||
this.props.onEndReached(event);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
_renderMoreRowsIfNeeded() {
|
||||
if (this.scrollProperties.contentLength === null ||
|
||||
this.scrollProperties.visibleLength === null ||
|
||||
this.state.curRenderedRowsCount === (this.props.enableEmptySections ?
|
||||
this.props.dataSource.getRowAndSectionCount() : this.props.dataSource.getRowCount())) {
|
||||
this._maybeCallOnEndReached();
|
||||
return;
|
||||
}
|
||||
|
||||
const distanceFromEnd = this._getDistanceFromEnd(this.scrollProperties);
|
||||
if (distanceFromEnd < this.props.scrollRenderAheadDistance) {
|
||||
this._pageInNewRows();
|
||||
}
|
||||
}
|
||||
|
||||
_pageInNewRows() {
|
||||
this.setState((state, props) => {
|
||||
const rowsToRender = Math.min(
|
||||
state.curRenderedRowsCount + props.pageSize,
|
||||
(props.enableEmptySections ? props.dataSource.getRowAndSectionCount() : props.dataSource.getRowCount())
|
||||
);
|
||||
this._prevRenderedRowsCount = state.curRenderedRowsCount;
|
||||
return {
|
||||
curRenderedRowsCount: rowsToRender
|
||||
};
|
||||
}, () => {
|
||||
this._measureAndUpdateScrollProps();
|
||||
this._prevRenderedRowsCount = this.state.curRenderedRowsCount;
|
||||
});
|
||||
}
|
||||
|
||||
_onScroll = (e: Object) => {
|
||||
const isVertical = !this.props.horizontal;
|
||||
this.scrollProperties.visibleLength = e.nativeEvent.layoutMeasurement[
|
||||
isVertical ? 'height' : 'width'
|
||||
];
|
||||
this.scrollProperties.contentLength = e.nativeEvent.contentSize[
|
||||
isVertical ? 'height' : 'width'
|
||||
];
|
||||
this.scrollProperties.offset = e.nativeEvent.contentOffset[
|
||||
isVertical ? 'y' : 'x'
|
||||
];
|
||||
this._updateVisibleRows(e.nativeEvent.updatedChildFrames);
|
||||
if (!this._maybeCallOnEndReached(e)) {
|
||||
this._renderMoreRowsIfNeeded();
|
||||
}
|
||||
|
||||
if (this.props.onEndReached &&
|
||||
this._getDistanceFromEnd(this.scrollProperties) > this.props.onEndReachedThreshold) {
|
||||
// Scrolled out of the end zone, so it should be able to trigger again.
|
||||
this._sentEndForContentLength = null;
|
||||
}
|
||||
|
||||
this.props.onScroll && this.props.onScroll(e);
|
||||
};
|
||||
|
||||
_setScrollViewRef = (component) => {
|
||||
this._scrollViewRef = component;
|
||||
}
|
||||
|
||||
@@ -1,20 +1,19 @@
|
||||
/* eslint-env mocha */
|
||||
/* eslint-env jasmine, jest */
|
||||
|
||||
import assert from 'assert';
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import ProgressBar from '..';
|
||||
|
||||
suite('components/ProgressBar', () => {
|
||||
suite('progress', () => {
|
||||
test('value as percentage is set to "aria-valuenow"', () => {
|
||||
describe('components/ProgressBar', () => {
|
||||
describe('progress', () => {
|
||||
it('value as percentage is set to "aria-valuenow"', () => {
|
||||
const component = shallow(<ProgressBar progress={0.5} />);
|
||||
assert(component.prop('aria-valuenow') === 50);
|
||||
expect(component.prop('aria-valuenow') === 50).toBeTruthy();
|
||||
});
|
||||
|
||||
test('is ignored when "indeterminate" is "true"', () => {
|
||||
it('is ignored when "indeterminate" is "true"', () => {
|
||||
const component = shallow(<ProgressBar indeterminate progress={0.5} />);
|
||||
assert(component.prop('aria-valuenow') === null);
|
||||
expect(component.prop('aria-valuenow') === null).toBeTruthy();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -6,10 +6,39 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import debounce from 'lodash/debounce';
|
||||
import debounce from 'debounce';
|
||||
import View from '../View';
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
|
||||
const normalizeScrollEvent = (e) => ({
|
||||
nativeEvent: {
|
||||
contentOffset: {
|
||||
get x() {
|
||||
return e.target.scrollLeft;
|
||||
},
|
||||
get y() {
|
||||
return e.target.scrollTop;
|
||||
}
|
||||
},
|
||||
contentSize: {
|
||||
get height() {
|
||||
return e.target.scrollHeight;
|
||||
},
|
||||
get width() {
|
||||
return e.target.scrollWidth;
|
||||
}
|
||||
},
|
||||
layoutMeasurement: {
|
||||
get height() {
|
||||
return e.target.offsetHeight;
|
||||
},
|
||||
get width() {
|
||||
return e.target.offsetWidth;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Encapsulates the Web-specific scroll throttling and disabling logic
|
||||
*/
|
||||
@@ -23,12 +52,16 @@ export default class ScrollViewBase extends Component {
|
||||
onScrollEndDrag: PropTypes.func,
|
||||
onTouchMove: PropTypes.func,
|
||||
onWheel: PropTypes.func,
|
||||
removeClippedSubviews: PropTypes.bool,
|
||||
scrollEnabled: PropTypes.bool,
|
||||
scrollEventThrottle: PropTypes.number
|
||||
scrollEventThrottle: PropTypes.number,
|
||||
showsHorizontalScrollIndicator: PropTypes.bool,
|
||||
showsVerticalScrollIndicator: PropTypes.bool
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
scrollEnabled: true
|
||||
scrollEnabled: true,
|
||||
scrollEventThrottle: 0
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
@@ -48,6 +81,7 @@ export default class ScrollViewBase extends Component {
|
||||
}
|
||||
|
||||
_handleScroll = (e) => {
|
||||
e.persist();
|
||||
const { scrollEventThrottle } = this.props;
|
||||
// A scroll happened, so the scroll bumps the debounce.
|
||||
this._debouncedOnScrollEnd(e);
|
||||
@@ -70,23 +104,33 @@ export default class ScrollViewBase extends Component {
|
||||
_handleScrollTick(e) {
|
||||
const { onScroll } = this.props;
|
||||
this._state.scrollLastTick = Date.now();
|
||||
if (onScroll) { onScroll(e); }
|
||||
if (onScroll) { onScroll(normalizeScrollEvent(e)); }
|
||||
}
|
||||
|
||||
_handleScrollEnd(e) {
|
||||
const { onScroll } = this.props;
|
||||
this._state.isScrolling = false;
|
||||
if (onScroll) { onScroll(e); }
|
||||
if (onScroll) { onScroll(normalizeScrollEvent(e)); }
|
||||
}
|
||||
|
||||
_shouldEmitScrollEvent(lastTick, eventThrottle) {
|
||||
const timeSinceLastTick = Date.now() - lastTick;
|
||||
return (eventThrottle > 0 && timeSinceLastTick >= (1000 / eventThrottle));
|
||||
return (eventThrottle > 0 && timeSinceLastTick >= eventThrottle);
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
onMomentumScrollBegin, onMomentumScrollEnd, onScrollBeginDrag, onScrollEndDrag, scrollEnabled, scrollEventThrottle, // eslint-disable-line
|
||||
/* eslint-disable */
|
||||
onMomentumScrollBegin,
|
||||
onMomentumScrollEnd,
|
||||
onScrollBeginDrag,
|
||||
onScrollEndDrag,
|
||||
removeClippedSubviews,
|
||||
scrollEnabled,
|
||||
scrollEventThrottle,
|
||||
showsHorizontalScrollIndicator,
|
||||
showsVerticalScrollIndicator,
|
||||
/* eslint-enable */
|
||||
...other
|
||||
} = this.props;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* eslint-env mocha */
|
||||
/* eslint-env jasmine, jest */
|
||||
|
||||
suite('components/ScrollView', () => {
|
||||
describe('components/ScrollView', () => {
|
||||
test('NO TEST COVERAGE');
|
||||
});
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
*/
|
||||
|
||||
import dismissKeyboard from '../../modules/dismissKeyboard';
|
||||
import findNodeHandle from '../../modules/findNodeHandle';
|
||||
import invariant from 'fbjs/lib/invariant';
|
||||
import ReactDOM from 'react-dom';
|
||||
import ScrollResponder from '../../modules/ScrollResponder';
|
||||
import ScrollViewBase from './ScrollViewBase';
|
||||
import StyleSheet from '../../apis/StyleSheet';
|
||||
@@ -21,12 +21,12 @@ import React, { Component, PropTypes } from 'react';
|
||||
const ScrollView = React.createClass({
|
||||
propTypes: {
|
||||
...View.propTypes,
|
||||
children: View.propTypes.children,
|
||||
contentContainerStyle: StyleSheetPropType(ViewStylePropTypes),
|
||||
horizontal: PropTypes.bool,
|
||||
keyboardDismissMode: PropTypes.oneOf([ 'none', 'interactive', 'on-drag' ]),
|
||||
onContentSizeChange: PropTypes.func,
|
||||
onScroll: PropTypes.func,
|
||||
pagingEnabled: PropTypes.bool,
|
||||
refreshControl: PropTypes.element,
|
||||
scrollEnabled: PropTypes.bool,
|
||||
scrollEventThrottle: PropTypes.number,
|
||||
@@ -54,11 +54,11 @@ const ScrollView = React.createClass({
|
||||
},
|
||||
|
||||
getScrollableNode(): any {
|
||||
return ReactDOM.findDOMNode(this._scrollViewRef);
|
||||
return findNodeHandle(this._scrollViewRef);
|
||||
},
|
||||
|
||||
getInnerViewNode(): any {
|
||||
return ReactDOM.findDOMNode(this._innerViewRef);
|
||||
return findNodeHandle(this._innerViewRef);
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -97,10 +97,13 @@ const ScrollView = React.createClass({
|
||||
const {
|
||||
contentContainerStyle,
|
||||
horizontal,
|
||||
keyboardDismissMode, // eslint-disable-line
|
||||
onContentSizeChange,
|
||||
onScroll, // eslint-disable-line
|
||||
refreshControl,
|
||||
/* eslint-disable */
|
||||
keyboardDismissMode,
|
||||
onScroll,
|
||||
pagingEnabled,
|
||||
/* eslint-enable */
|
||||
...other
|
||||
} = this.props;
|
||||
|
||||
@@ -233,7 +236,7 @@ const styles = StyleSheet.create({
|
||||
overflowY: 'hidden'
|
||||
},
|
||||
contentContainer: {
|
||||
flexGrow: 1
|
||||
transform: [ { translateZ: 0 } ]
|
||||
},
|
||||
contentContainerHorizontal: {
|
||||
flexDirection: 'row'
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* eslint-env mocha */
|
||||
/* eslint-env jasmine, jest */
|
||||
|
||||
suite('components/StaticContainer', () => {
|
||||
describe('components/StaticContainer', () => {
|
||||
test.skip('NO TEST COVERAGE', () => {});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* eslint-env mocha */
|
||||
/* eslint-env jasmine, jest */
|
||||
|
||||
suite('components/StaticRenderer', () => {
|
||||
describe('components/StaticRenderer', () => {
|
||||
test.skip('NO TEST COVERAGE', () => {});
|
||||
});
|
||||
|
||||
855
src/components/Switch/__tests__/__snapshots__/index-test.js.snap
Normal file
855
src/components/Switch/__tests__/__snapshots__/index-test.js.snap
Normal file
@@ -0,0 +1,855 @@
|
||||
exports[`components/Switch disabled when "false" a default checkbox is rendered 1`] = `
|
||||
<div
|
||||
className=" __style_df"
|
||||
style={
|
||||
Object {
|
||||
"MozBoxSizing": "border-box",
|
||||
"MozUserSelect": "none",
|
||||
"WebkitAlignItems": "stretch",
|
||||
"WebkitBoxAlign": "stretch",
|
||||
"WebkitBoxDirection": "normal",
|
||||
"WebkitBoxOrient": "vertical",
|
||||
"WebkitFlexBasis": "auto",
|
||||
"WebkitFlexDirection": "column",
|
||||
"WebkitFlexShrink": 0,
|
||||
"WebkitUserSelect": "none",
|
||||
"alignItems": "stretch",
|
||||
"backgroundColor": "transparent",
|
||||
"borderBottomStyle": "solid",
|
||||
"borderBottomWidth": "0px",
|
||||
"borderLeftStyle": "solid",
|
||||
"borderLeftWidth": "0px",
|
||||
"borderRightStyle": "solid",
|
||||
"borderRightWidth": "0px",
|
||||
"borderTopStyle": "solid",
|
||||
"borderTopWidth": "0px",
|
||||
"boxSizing": "border-box",
|
||||
"color": "inherit",
|
||||
"cursor": "pointer",
|
||||
"display": null,
|
||||
"flexBasis": "auto",
|
||||
"flexDirection": "column",
|
||||
"flexShrink": 0,
|
||||
"font": "inherit",
|
||||
"height": "20px",
|
||||
"listStyle": "none",
|
||||
"marginBottom": "0px",
|
||||
"marginLeft": "0px",
|
||||
"marginRight": "0px",
|
||||
"marginTop": "0px",
|
||||
"minHeight": "0px",
|
||||
"minWidth": "0px",
|
||||
"msFlexAlign": "stretch",
|
||||
"msFlexDirection": "column",
|
||||
"msFlexNegative": 0,
|
||||
"msPreferredSize": "auto",
|
||||
"msUserSelect": "none",
|
||||
"paddingBottom": "0px",
|
||||
"paddingLeft": "0px",
|
||||
"paddingRight": "0px",
|
||||
"paddingTop": "0px",
|
||||
"position": "relative",
|
||||
"textAlign": "inherit",
|
||||
"textDecoration": "none",
|
||||
"userSelect": "none",
|
||||
"width": "40px",
|
||||
}
|
||||
}>
|
||||
<div
|
||||
className=" __style_df"
|
||||
style={
|
||||
Object {
|
||||
"MozBoxSizing": "border-box",
|
||||
"WebkitAlignItems": "stretch",
|
||||
"WebkitBoxAlign": "stretch",
|
||||
"WebkitBoxDirection": "normal",
|
||||
"WebkitBoxOrient": "vertical",
|
||||
"WebkitFlexBasis": "auto",
|
||||
"WebkitFlexDirection": "column",
|
||||
"WebkitFlexShrink": 0,
|
||||
"WebkitTransition": "0.1s",
|
||||
"alignItems": "stretch",
|
||||
"backgroundColor": "#939393",
|
||||
"borderBottomLeftRadius": "10px",
|
||||
"borderBottomRightRadius": "10px",
|
||||
"borderBottomStyle": "solid",
|
||||
"borderBottomWidth": "0px",
|
||||
"borderLeftStyle": "solid",
|
||||
"borderLeftWidth": "0px",
|
||||
"borderRightStyle": "solid",
|
||||
"borderRightWidth": "0px",
|
||||
"borderTopLeftRadius": "10px",
|
||||
"borderTopRightRadius": "10px",
|
||||
"borderTopStyle": "solid",
|
||||
"borderTopWidth": "0px",
|
||||
"bottom": "0px",
|
||||
"boxSizing": "border-box",
|
||||
"color": "inherit",
|
||||
"display": null,
|
||||
"flexBasis": "auto",
|
||||
"flexDirection": "column",
|
||||
"flexShrink": 0,
|
||||
"font": "inherit",
|
||||
"height": "70%",
|
||||
"left": "0px",
|
||||
"listStyle": "none",
|
||||
"marginBottom": "auto",
|
||||
"marginLeft": "auto",
|
||||
"marginRight": "auto",
|
||||
"marginTop": "auto",
|
||||
"minHeight": "0px",
|
||||
"minWidth": "0px",
|
||||
"msFlexAlign": "stretch",
|
||||
"msFlexDirection": "column",
|
||||
"msFlexNegative": 0,
|
||||
"msPreferredSize": "auto",
|
||||
"paddingBottom": "0px",
|
||||
"paddingLeft": "0px",
|
||||
"paddingRight": "0px",
|
||||
"paddingTop": "0px",
|
||||
"position": "absolute",
|
||||
"right": "0px",
|
||||
"textAlign": "inherit",
|
||||
"textDecoration": "none",
|
||||
"top": "0px",
|
||||
"transition": "0.1s",
|
||||
"width": "90%",
|
||||
}
|
||||
} />
|
||||
<div
|
||||
className=" __style_df"
|
||||
style={
|
||||
Object {
|
||||
"MozBoxSizing": "border-box",
|
||||
"WebkitAlignItems": "stretch",
|
||||
"WebkitAlignSelf": "flex-start",
|
||||
"WebkitBoxAlign": "stretch",
|
||||
"WebkitBoxDirection": "normal",
|
||||
"WebkitBoxOrient": "vertical",
|
||||
"WebkitFlexBasis": "auto",
|
||||
"WebkitFlexDirection": "column",
|
||||
"WebkitFlexShrink": 0,
|
||||
"WebkitTransform": "translateX(0%)",
|
||||
"WebkitTransition": "0.1s",
|
||||
"alignItems": "stretch",
|
||||
"alignSelf": "flex-start",
|
||||
"backgroundColor": "#FAFAFA",
|
||||
"borderBottomLeftRadius": "100%",
|
||||
"borderBottomRightRadius": "100%",
|
||||
"borderBottomStyle": "solid",
|
||||
"borderBottomWidth": "0px",
|
||||
"borderLeftStyle": "solid",
|
||||
"borderLeftWidth": "0px",
|
||||
"borderRightStyle": "solid",
|
||||
"borderRightWidth": "0px",
|
||||
"borderTopLeftRadius": "100%",
|
||||
"borderTopRightRadius": "100%",
|
||||
"borderTopStyle": "solid",
|
||||
"borderTopWidth": "0px",
|
||||
"boxShadow": "0px 1px 3px rgba(0,0,0,0.5)",
|
||||
"boxSizing": "border-box",
|
||||
"color": "inherit",
|
||||
"display": null,
|
||||
"flexBasis": "auto",
|
||||
"flexDirection": "column",
|
||||
"flexShrink": 0,
|
||||
"font": "inherit",
|
||||
"height": "20px",
|
||||
"listStyle": "none",
|
||||
"marginBottom": "0px",
|
||||
"marginLeft": "0px",
|
||||
"marginRight": "0px",
|
||||
"marginTop": "0px",
|
||||
"minHeight": "0px",
|
||||
"minWidth": "0px",
|
||||
"msFlexAlign": "stretch",
|
||||
"msFlexDirection": "column",
|
||||
"msFlexItemAlign": "start",
|
||||
"msFlexNegative": 0,
|
||||
"msPreferredSize": "auto",
|
||||
"msTransform": "translateX(0%)",
|
||||
"paddingBottom": "0px",
|
||||
"paddingLeft": "0px",
|
||||
"paddingRight": "0px",
|
||||
"paddingTop": "0px",
|
||||
"position": "relative",
|
||||
"textAlign": "inherit",
|
||||
"textDecoration": "none",
|
||||
"transform": "translateX(0%)",
|
||||
"transition": "0.1s",
|
||||
"width": "20px",
|
||||
}
|
||||
} />
|
||||
<input
|
||||
checked={false}
|
||||
className=""
|
||||
disabled={false}
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
onFocus={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"bottom": "0px",
|
||||
"cursor": "inherit",
|
||||
"height": "100%",
|
||||
"left": "0px",
|
||||
"marginBottom": "0px",
|
||||
"marginLeft": "0px",
|
||||
"marginRight": "0px",
|
||||
"marginTop": "0px",
|
||||
"opacity": 0,
|
||||
"paddingBottom": "0px",
|
||||
"paddingLeft": "0px",
|
||||
"paddingRight": "0px",
|
||||
"paddingTop": "0px",
|
||||
"position": "absolute",
|
||||
"right": "0px",
|
||||
"top": "0px",
|
||||
"width": "100%",
|
||||
}
|
||||
}
|
||||
type="checkbox" />
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`components/Switch disabled when "true" a disabled checkbox is rendered 1`] = `
|
||||
<div
|
||||
className=" __style_df"
|
||||
style={
|
||||
Object {
|
||||
"MozBoxSizing": "border-box",
|
||||
"MozUserSelect": "none",
|
||||
"WebkitAlignItems": "stretch",
|
||||
"WebkitBoxAlign": "stretch",
|
||||
"WebkitBoxDirection": "normal",
|
||||
"WebkitBoxOrient": "vertical",
|
||||
"WebkitFlexBasis": "auto",
|
||||
"WebkitFlexDirection": "column",
|
||||
"WebkitFlexShrink": 0,
|
||||
"WebkitUserSelect": "none",
|
||||
"alignItems": "stretch",
|
||||
"backgroundColor": "transparent",
|
||||
"borderBottomStyle": "solid",
|
||||
"borderBottomWidth": "0px",
|
||||
"borderLeftStyle": "solid",
|
||||
"borderLeftWidth": "0px",
|
||||
"borderRightStyle": "solid",
|
||||
"borderRightWidth": "0px",
|
||||
"borderTopStyle": "solid",
|
||||
"borderTopWidth": "0px",
|
||||
"boxSizing": "border-box",
|
||||
"color": "inherit",
|
||||
"cursor": "default",
|
||||
"display": null,
|
||||
"flexBasis": "auto",
|
||||
"flexDirection": "column",
|
||||
"flexShrink": 0,
|
||||
"font": "inherit",
|
||||
"height": "20px",
|
||||
"listStyle": "none",
|
||||
"marginBottom": "0px",
|
||||
"marginLeft": "0px",
|
||||
"marginRight": "0px",
|
||||
"marginTop": "0px",
|
||||
"minHeight": "0px",
|
||||
"minWidth": "0px",
|
||||
"msFlexAlign": "stretch",
|
||||
"msFlexDirection": "column",
|
||||
"msFlexNegative": 0,
|
||||
"msPreferredSize": "auto",
|
||||
"msUserSelect": "none",
|
||||
"paddingBottom": "0px",
|
||||
"paddingLeft": "0px",
|
||||
"paddingRight": "0px",
|
||||
"paddingTop": "0px",
|
||||
"position": "relative",
|
||||
"textAlign": "inherit",
|
||||
"textDecoration": "none",
|
||||
"userSelect": "none",
|
||||
"width": "40px",
|
||||
}
|
||||
}>
|
||||
<div
|
||||
className=" __style_df"
|
||||
style={
|
||||
Object {
|
||||
"MozBoxSizing": "border-box",
|
||||
"WebkitAlignItems": "stretch",
|
||||
"WebkitBoxAlign": "stretch",
|
||||
"WebkitBoxDirection": "normal",
|
||||
"WebkitBoxOrient": "vertical",
|
||||
"WebkitFlexBasis": "auto",
|
||||
"WebkitFlexDirection": "column",
|
||||
"WebkitFlexShrink": 0,
|
||||
"WebkitTransition": "0.1s",
|
||||
"alignItems": "stretch",
|
||||
"backgroundColor": "#D5D5D5",
|
||||
"borderBottomLeftRadius": "10px",
|
||||
"borderBottomRightRadius": "10px",
|
||||
"borderBottomStyle": "solid",
|
||||
"borderBottomWidth": "0px",
|
||||
"borderLeftStyle": "solid",
|
||||
"borderLeftWidth": "0px",
|
||||
"borderRightStyle": "solid",
|
||||
"borderRightWidth": "0px",
|
||||
"borderTopLeftRadius": "10px",
|
||||
"borderTopRightRadius": "10px",
|
||||
"borderTopStyle": "solid",
|
||||
"borderTopWidth": "0px",
|
||||
"bottom": "0px",
|
||||
"boxSizing": "border-box",
|
||||
"color": "inherit",
|
||||
"display": null,
|
||||
"flexBasis": "auto",
|
||||
"flexDirection": "column",
|
||||
"flexShrink": 0,
|
||||
"font": "inherit",
|
||||
"height": "70%",
|
||||
"left": "0px",
|
||||
"listStyle": "none",
|
||||
"marginBottom": "auto",
|
||||
"marginLeft": "auto",
|
||||
"marginRight": "auto",
|
||||
"marginTop": "auto",
|
||||
"minHeight": "0px",
|
||||
"minWidth": "0px",
|
||||
"msFlexAlign": "stretch",
|
||||
"msFlexDirection": "column",
|
||||
"msFlexNegative": 0,
|
||||
"msPreferredSize": "auto",
|
||||
"paddingBottom": "0px",
|
||||
"paddingLeft": "0px",
|
||||
"paddingRight": "0px",
|
||||
"paddingTop": "0px",
|
||||
"position": "absolute",
|
||||
"right": "0px",
|
||||
"textAlign": "inherit",
|
||||
"textDecoration": "none",
|
||||
"top": "0px",
|
||||
"transition": "0.1s",
|
||||
"width": "90%",
|
||||
}
|
||||
} />
|
||||
<div
|
||||
className=" __style_df"
|
||||
style={
|
||||
Object {
|
||||
"MozBoxSizing": "border-box",
|
||||
"WebkitAlignItems": "stretch",
|
||||
"WebkitAlignSelf": "flex-start",
|
||||
"WebkitBoxAlign": "stretch",
|
||||
"WebkitBoxDirection": "normal",
|
||||
"WebkitBoxOrient": "vertical",
|
||||
"WebkitFlexBasis": "auto",
|
||||
"WebkitFlexDirection": "column",
|
||||
"WebkitFlexShrink": 0,
|
||||
"WebkitTransform": "translateX(0%)",
|
||||
"WebkitTransition": "0.1s",
|
||||
"alignItems": "stretch",
|
||||
"alignSelf": "flex-start",
|
||||
"backgroundColor": "#BDBDBD",
|
||||
"borderBottomLeftRadius": "100%",
|
||||
"borderBottomRightRadius": "100%",
|
||||
"borderBottomStyle": "solid",
|
||||
"borderBottomWidth": "0px",
|
||||
"borderLeftStyle": "solid",
|
||||
"borderLeftWidth": "0px",
|
||||
"borderRightStyle": "solid",
|
||||
"borderRightWidth": "0px",
|
||||
"borderTopLeftRadius": "100%",
|
||||
"borderTopRightRadius": "100%",
|
||||
"borderTopStyle": "solid",
|
||||
"borderTopWidth": "0px",
|
||||
"boxShadow": "0px 1px 3px rgba(0,0,0,0.5)",
|
||||
"boxSizing": "border-box",
|
||||
"color": "inherit",
|
||||
"display": null,
|
||||
"flexBasis": "auto",
|
||||
"flexDirection": "column",
|
||||
"flexShrink": 0,
|
||||
"font": "inherit",
|
||||
"height": "20px",
|
||||
"listStyle": "none",
|
||||
"marginBottom": "0px",
|
||||
"marginLeft": "0px",
|
||||
"marginRight": "0px",
|
||||
"marginTop": "0px",
|
||||
"minHeight": "0px",
|
||||
"minWidth": "0px",
|
||||
"msFlexAlign": "stretch",
|
||||
"msFlexDirection": "column",
|
||||
"msFlexItemAlign": "start",
|
||||
"msFlexNegative": 0,
|
||||
"msPreferredSize": "auto",
|
||||
"msTransform": "translateX(0%)",
|
||||
"paddingBottom": "0px",
|
||||
"paddingLeft": "0px",
|
||||
"paddingRight": "0px",
|
||||
"paddingTop": "0px",
|
||||
"position": "relative",
|
||||
"textAlign": "inherit",
|
||||
"textDecoration": "none",
|
||||
"transform": "translateX(0%)",
|
||||
"transition": "0.1s",
|
||||
"width": "20px",
|
||||
}
|
||||
} />
|
||||
<input
|
||||
checked={false}
|
||||
className=""
|
||||
disabled={true}
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
onFocus={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"bottom": "0px",
|
||||
"cursor": "inherit",
|
||||
"height": "100%",
|
||||
"left": "0px",
|
||||
"marginBottom": "0px",
|
||||
"marginLeft": "0px",
|
||||
"marginRight": "0px",
|
||||
"marginTop": "0px",
|
||||
"opacity": 0,
|
||||
"paddingBottom": "0px",
|
||||
"paddingLeft": "0px",
|
||||
"paddingRight": "0px",
|
||||
"paddingTop": "0px",
|
||||
"position": "absolute",
|
||||
"right": "0px",
|
||||
"top": "0px",
|
||||
"width": "100%",
|
||||
}
|
||||
}
|
||||
type="checkbox" />
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`components/Switch value when "false" an unchecked checkbox is rendered 1`] = `
|
||||
<div
|
||||
className=" __style_df"
|
||||
style={
|
||||
Object {
|
||||
"MozBoxSizing": "border-box",
|
||||
"MozUserSelect": "none",
|
||||
"WebkitAlignItems": "stretch",
|
||||
"WebkitBoxAlign": "stretch",
|
||||
"WebkitBoxDirection": "normal",
|
||||
"WebkitBoxOrient": "vertical",
|
||||
"WebkitFlexBasis": "auto",
|
||||
"WebkitFlexDirection": "column",
|
||||
"WebkitFlexShrink": 0,
|
||||
"WebkitUserSelect": "none",
|
||||
"alignItems": "stretch",
|
||||
"backgroundColor": "transparent",
|
||||
"borderBottomStyle": "solid",
|
||||
"borderBottomWidth": "0px",
|
||||
"borderLeftStyle": "solid",
|
||||
"borderLeftWidth": "0px",
|
||||
"borderRightStyle": "solid",
|
||||
"borderRightWidth": "0px",
|
||||
"borderTopStyle": "solid",
|
||||
"borderTopWidth": "0px",
|
||||
"boxSizing": "border-box",
|
||||
"color": "inherit",
|
||||
"cursor": "pointer",
|
||||
"display": null,
|
||||
"flexBasis": "auto",
|
||||
"flexDirection": "column",
|
||||
"flexShrink": 0,
|
||||
"font": "inherit",
|
||||
"height": "20px",
|
||||
"listStyle": "none",
|
||||
"marginBottom": "0px",
|
||||
"marginLeft": "0px",
|
||||
"marginRight": "0px",
|
||||
"marginTop": "0px",
|
||||
"minHeight": "0px",
|
||||
"minWidth": "0px",
|
||||
"msFlexAlign": "stretch",
|
||||
"msFlexDirection": "column",
|
||||
"msFlexNegative": 0,
|
||||
"msPreferredSize": "auto",
|
||||
"msUserSelect": "none",
|
||||
"paddingBottom": "0px",
|
||||
"paddingLeft": "0px",
|
||||
"paddingRight": "0px",
|
||||
"paddingTop": "0px",
|
||||
"position": "relative",
|
||||
"textAlign": "inherit",
|
||||
"textDecoration": "none",
|
||||
"userSelect": "none",
|
||||
"width": "40px",
|
||||
}
|
||||
}>
|
||||
<div
|
||||
className=" __style_df"
|
||||
style={
|
||||
Object {
|
||||
"MozBoxSizing": "border-box",
|
||||
"WebkitAlignItems": "stretch",
|
||||
"WebkitBoxAlign": "stretch",
|
||||
"WebkitBoxDirection": "normal",
|
||||
"WebkitBoxOrient": "vertical",
|
||||
"WebkitFlexBasis": "auto",
|
||||
"WebkitFlexDirection": "column",
|
||||
"WebkitFlexShrink": 0,
|
||||
"WebkitTransition": "0.1s",
|
||||
"alignItems": "stretch",
|
||||
"backgroundColor": "#939393",
|
||||
"borderBottomLeftRadius": "10px",
|
||||
"borderBottomRightRadius": "10px",
|
||||
"borderBottomStyle": "solid",
|
||||
"borderBottomWidth": "0px",
|
||||
"borderLeftStyle": "solid",
|
||||
"borderLeftWidth": "0px",
|
||||
"borderRightStyle": "solid",
|
||||
"borderRightWidth": "0px",
|
||||
"borderTopLeftRadius": "10px",
|
||||
"borderTopRightRadius": "10px",
|
||||
"borderTopStyle": "solid",
|
||||
"borderTopWidth": "0px",
|
||||
"bottom": "0px",
|
||||
"boxSizing": "border-box",
|
||||
"color": "inherit",
|
||||
"display": null,
|
||||
"flexBasis": "auto",
|
||||
"flexDirection": "column",
|
||||
"flexShrink": 0,
|
||||
"font": "inherit",
|
||||
"height": "70%",
|
||||
"left": "0px",
|
||||
"listStyle": "none",
|
||||
"marginBottom": "auto",
|
||||
"marginLeft": "auto",
|
||||
"marginRight": "auto",
|
||||
"marginTop": "auto",
|
||||
"minHeight": "0px",
|
||||
"minWidth": "0px",
|
||||
"msFlexAlign": "stretch",
|
||||
"msFlexDirection": "column",
|
||||
"msFlexNegative": 0,
|
||||
"msPreferredSize": "auto",
|
||||
"paddingBottom": "0px",
|
||||
"paddingLeft": "0px",
|
||||
"paddingRight": "0px",
|
||||
"paddingTop": "0px",
|
||||
"position": "absolute",
|
||||
"right": "0px",
|
||||
"textAlign": "inherit",
|
||||
"textDecoration": "none",
|
||||
"top": "0px",
|
||||
"transition": "0.1s",
|
||||
"width": "90%",
|
||||
}
|
||||
} />
|
||||
<div
|
||||
className=" __style_df"
|
||||
style={
|
||||
Object {
|
||||
"MozBoxSizing": "border-box",
|
||||
"WebkitAlignItems": "stretch",
|
||||
"WebkitAlignSelf": "flex-start",
|
||||
"WebkitBoxAlign": "stretch",
|
||||
"WebkitBoxDirection": "normal",
|
||||
"WebkitBoxOrient": "vertical",
|
||||
"WebkitFlexBasis": "auto",
|
||||
"WebkitFlexDirection": "column",
|
||||
"WebkitFlexShrink": 0,
|
||||
"WebkitTransform": "translateX(0%)",
|
||||
"WebkitTransition": "0.1s",
|
||||
"alignItems": "stretch",
|
||||
"alignSelf": "flex-start",
|
||||
"backgroundColor": "#FAFAFA",
|
||||
"borderBottomLeftRadius": "100%",
|
||||
"borderBottomRightRadius": "100%",
|
||||
"borderBottomStyle": "solid",
|
||||
"borderBottomWidth": "0px",
|
||||
"borderLeftStyle": "solid",
|
||||
"borderLeftWidth": "0px",
|
||||
"borderRightStyle": "solid",
|
||||
"borderRightWidth": "0px",
|
||||
"borderTopLeftRadius": "100%",
|
||||
"borderTopRightRadius": "100%",
|
||||
"borderTopStyle": "solid",
|
||||
"borderTopWidth": "0px",
|
||||
"boxShadow": "0px 1px 3px rgba(0,0,0,0.5)",
|
||||
"boxSizing": "border-box",
|
||||
"color": "inherit",
|
||||
"display": null,
|
||||
"flexBasis": "auto",
|
||||
"flexDirection": "column",
|
||||
"flexShrink": 0,
|
||||
"font": "inherit",
|
||||
"height": "20px",
|
||||
"listStyle": "none",
|
||||
"marginBottom": "0px",
|
||||
"marginLeft": "0px",
|
||||
"marginRight": "0px",
|
||||
"marginTop": "0px",
|
||||
"minHeight": "0px",
|
||||
"minWidth": "0px",
|
||||
"msFlexAlign": "stretch",
|
||||
"msFlexDirection": "column",
|
||||
"msFlexItemAlign": "start",
|
||||
"msFlexNegative": 0,
|
||||
"msPreferredSize": "auto",
|
||||
"msTransform": "translateX(0%)",
|
||||
"paddingBottom": "0px",
|
||||
"paddingLeft": "0px",
|
||||
"paddingRight": "0px",
|
||||
"paddingTop": "0px",
|
||||
"position": "relative",
|
||||
"textAlign": "inherit",
|
||||
"textDecoration": "none",
|
||||
"transform": "translateX(0%)",
|
||||
"transition": "0.1s",
|
||||
"width": "20px",
|
||||
}
|
||||
} />
|
||||
<input
|
||||
checked={false}
|
||||
className=""
|
||||
disabled={false}
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
onFocus={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"bottom": "0px",
|
||||
"cursor": "inherit",
|
||||
"height": "100%",
|
||||
"left": "0px",
|
||||
"marginBottom": "0px",
|
||||
"marginLeft": "0px",
|
||||
"marginRight": "0px",
|
||||
"marginTop": "0px",
|
||||
"opacity": 0,
|
||||
"paddingBottom": "0px",
|
||||
"paddingLeft": "0px",
|
||||
"paddingRight": "0px",
|
||||
"paddingTop": "0px",
|
||||
"position": "absolute",
|
||||
"right": "0px",
|
||||
"top": "0px",
|
||||
"width": "100%",
|
||||
}
|
||||
}
|
||||
type="checkbox" />
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`components/Switch value when "true" a checked checkbox is rendered 1`] = `
|
||||
<div
|
||||
className=" __style_df"
|
||||
style={
|
||||
Object {
|
||||
"MozBoxSizing": "border-box",
|
||||
"MozUserSelect": "none",
|
||||
"WebkitAlignItems": "stretch",
|
||||
"WebkitBoxAlign": "stretch",
|
||||
"WebkitBoxDirection": "normal",
|
||||
"WebkitBoxOrient": "vertical",
|
||||
"WebkitFlexBasis": "auto",
|
||||
"WebkitFlexDirection": "column",
|
||||
"WebkitFlexShrink": 0,
|
||||
"WebkitUserSelect": "none",
|
||||
"alignItems": "stretch",
|
||||
"backgroundColor": "transparent",
|
||||
"borderBottomStyle": "solid",
|
||||
"borderBottomWidth": "0px",
|
||||
"borderLeftStyle": "solid",
|
||||
"borderLeftWidth": "0px",
|
||||
"borderRightStyle": "solid",
|
||||
"borderRightWidth": "0px",
|
||||
"borderTopStyle": "solid",
|
||||
"borderTopWidth": "0px",
|
||||
"boxSizing": "border-box",
|
||||
"color": "inherit",
|
||||
"cursor": "pointer",
|
||||
"display": null,
|
||||
"flexBasis": "auto",
|
||||
"flexDirection": "column",
|
||||
"flexShrink": 0,
|
||||
"font": "inherit",
|
||||
"height": "20px",
|
||||
"listStyle": "none",
|
||||
"marginBottom": "0px",
|
||||
"marginLeft": "0px",
|
||||
"marginRight": "0px",
|
||||
"marginTop": "0px",
|
||||
"minHeight": "0px",
|
||||
"minWidth": "0px",
|
||||
"msFlexAlign": "stretch",
|
||||
"msFlexDirection": "column",
|
||||
"msFlexNegative": 0,
|
||||
"msPreferredSize": "auto",
|
||||
"msUserSelect": "none",
|
||||
"paddingBottom": "0px",
|
||||
"paddingLeft": "0px",
|
||||
"paddingRight": "0px",
|
||||
"paddingTop": "0px",
|
||||
"position": "relative",
|
||||
"textAlign": "inherit",
|
||||
"textDecoration": "none",
|
||||
"userSelect": "none",
|
||||
"width": "40px",
|
||||
}
|
||||
}>
|
||||
<div
|
||||
className=" __style_df"
|
||||
style={
|
||||
Object {
|
||||
"MozBoxSizing": "border-box",
|
||||
"WebkitAlignItems": "stretch",
|
||||
"WebkitBoxAlign": "stretch",
|
||||
"WebkitBoxDirection": "normal",
|
||||
"WebkitBoxOrient": "vertical",
|
||||
"WebkitFlexBasis": "auto",
|
||||
"WebkitFlexDirection": "column",
|
||||
"WebkitFlexShrink": 0,
|
||||
"WebkitTransition": "0.1s",
|
||||
"alignItems": "stretch",
|
||||
"backgroundColor": "#A3D3CF",
|
||||
"borderBottomLeftRadius": "10px",
|
||||
"borderBottomRightRadius": "10px",
|
||||
"borderBottomStyle": "solid",
|
||||
"borderBottomWidth": "0px",
|
||||
"borderLeftStyle": "solid",
|
||||
"borderLeftWidth": "0px",
|
||||
"borderRightStyle": "solid",
|
||||
"borderRightWidth": "0px",
|
||||
"borderTopLeftRadius": "10px",
|
||||
"borderTopRightRadius": "10px",
|
||||
"borderTopStyle": "solid",
|
||||
"borderTopWidth": "0px",
|
||||
"bottom": "0px",
|
||||
"boxSizing": "border-box",
|
||||
"color": "inherit",
|
||||
"display": null,
|
||||
"flexBasis": "auto",
|
||||
"flexDirection": "column",
|
||||
"flexShrink": 0,
|
||||
"font": "inherit",
|
||||
"height": "70%",
|
||||
"left": "0px",
|
||||
"listStyle": "none",
|
||||
"marginBottom": "auto",
|
||||
"marginLeft": "auto",
|
||||
"marginRight": "auto",
|
||||
"marginTop": "auto",
|
||||
"minHeight": "0px",
|
||||
"minWidth": "0px",
|
||||
"msFlexAlign": "stretch",
|
||||
"msFlexDirection": "column",
|
||||
"msFlexNegative": 0,
|
||||
"msPreferredSize": "auto",
|
||||
"paddingBottom": "0px",
|
||||
"paddingLeft": "0px",
|
||||
"paddingRight": "0px",
|
||||
"paddingTop": "0px",
|
||||
"position": "absolute",
|
||||
"right": "0px",
|
||||
"textAlign": "inherit",
|
||||
"textDecoration": "none",
|
||||
"top": "0px",
|
||||
"transition": "0.1s",
|
||||
"width": "90%",
|
||||
}
|
||||
} />
|
||||
<div
|
||||
className=" __style_df"
|
||||
style={
|
||||
Object {
|
||||
"MozBoxSizing": "border-box",
|
||||
"WebkitAlignItems": "stretch",
|
||||
"WebkitAlignSelf": "flex-start",
|
||||
"WebkitBoxAlign": "stretch",
|
||||
"WebkitBoxDirection": "normal",
|
||||
"WebkitBoxOrient": "vertical",
|
||||
"WebkitFlexBasis": "auto",
|
||||
"WebkitFlexDirection": "column",
|
||||
"WebkitFlexShrink": 0,
|
||||
"WebkitTransform": "translateX(100%)",
|
||||
"WebkitTransition": "0.1s",
|
||||
"alignItems": "stretch",
|
||||
"alignSelf": "flex-start",
|
||||
"backgroundColor": "#009688",
|
||||
"borderBottomLeftRadius": "100%",
|
||||
"borderBottomRightRadius": "100%",
|
||||
"borderBottomStyle": "solid",
|
||||
"borderBottomWidth": "0px",
|
||||
"borderLeftStyle": "solid",
|
||||
"borderLeftWidth": "0px",
|
||||
"borderRightStyle": "solid",
|
||||
"borderRightWidth": "0px",
|
||||
"borderTopLeftRadius": "100%",
|
||||
"borderTopRightRadius": "100%",
|
||||
"borderTopStyle": "solid",
|
||||
"borderTopWidth": "0px",
|
||||
"boxShadow": "0px 1px 3px rgba(0,0,0,0.5)",
|
||||
"boxSizing": "border-box",
|
||||
"color": "inherit",
|
||||
"display": null,
|
||||
"flexBasis": "auto",
|
||||
"flexDirection": "column",
|
||||
"flexShrink": 0,
|
||||
"font": "inherit",
|
||||
"height": "20px",
|
||||
"listStyle": "none",
|
||||
"marginBottom": "0px",
|
||||
"marginLeft": "0px",
|
||||
"marginRight": "0px",
|
||||
"marginTop": "0px",
|
||||
"minHeight": "0px",
|
||||
"minWidth": "0px",
|
||||
"msFlexAlign": "stretch",
|
||||
"msFlexDirection": "column",
|
||||
"msFlexItemAlign": "start",
|
||||
"msFlexNegative": 0,
|
||||
"msPreferredSize": "auto",
|
||||
"msTransform": "translateX(100%)",
|
||||
"paddingBottom": "0px",
|
||||
"paddingLeft": "0px",
|
||||
"paddingRight": "0px",
|
||||
"paddingTop": "0px",
|
||||
"position": "relative",
|
||||
"textAlign": "inherit",
|
||||
"textDecoration": "none",
|
||||
"transform": "translateX(100%)",
|
||||
"transition": "0.1s",
|
||||
"width": "20px",
|
||||
}
|
||||
} />
|
||||
<input
|
||||
checked={true}
|
||||
className=""
|
||||
disabled={false}
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
onFocus={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"bottom": "0px",
|
||||
"cursor": "inherit",
|
||||
"height": "100%",
|
||||
"left": "0px",
|
||||
"marginBottom": "0px",
|
||||
"marginLeft": "0px",
|
||||
"marginRight": "0px",
|
||||
"marginTop": "0px",
|
||||
"opacity": 0,
|
||||
"paddingBottom": "0px",
|
||||
"paddingLeft": "0px",
|
||||
"paddingRight": "0px",
|
||||
"paddingTop": "0px",
|
||||
"position": "absolute",
|
||||
"right": "0px",
|
||||
"top": "0px",
|
||||
"width": "100%",
|
||||
}
|
||||
}
|
||||
type="checkbox" />
|
||||
</div>
|
||||
`;
|
||||
@@ -1,46 +1,50 @@
|
||||
/* eslint-env mocha */
|
||||
/* eslint-env jasmine, jest */
|
||||
|
||||
import assert from 'assert';
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import renderer from 'react-test-renderer';
|
||||
// import { shallow } from 'enzyme';
|
||||
import Switch from '..';
|
||||
|
||||
suite('components/Switch', () => {
|
||||
suite('disabled', () => {
|
||||
jest.mock('react-dom');
|
||||
|
||||
describe('components/Switch', () => {
|
||||
describe('disabled', () => {
|
||||
test('when "false" a default checkbox is rendered', () => {
|
||||
const component = shallow(<Switch />);
|
||||
assert(component.find('input').length === 1);
|
||||
const component = renderer.create(<Switch />);
|
||||
expect(component.toJSON()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('when "true" a disabled checkbox is rendered', () => {
|
||||
const component = shallow(<Switch disabled />);
|
||||
assert(component.find('input').prop('disabled') === true);
|
||||
const component = renderer.create(<Switch disabled />);
|
||||
expect(component.toJSON()).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
suite('onValueChange', () => {
|
||||
/*
|
||||
describe('onValueChange', () => {
|
||||
test('when value is "false" it receives "true"', () => {
|
||||
const handleValueChange = (value) => assert(value === true);
|
||||
const handleValueChange = (value) => expect(value === true).toBeTruthy();
|
||||
const component = shallow(<Switch onValueChange={handleValueChange} value={false} />);
|
||||
component.find('input').simulate('click');
|
||||
});
|
||||
|
||||
test('when value is "true" it receives "false"', () => {
|
||||
const handleValueChange = (value) => assert(value === false);
|
||||
const handleValueChange = (value) => expect(value === false).toBeTruthy();
|
||||
const component = shallow(<Switch onValueChange={handleValueChange} value />);
|
||||
component.find('input').simulate('click');
|
||||
});
|
||||
});
|
||||
*/
|
||||
|
||||
suite('value', () => {
|
||||
describe('value', () => {
|
||||
test('when "false" an unchecked checkbox is rendered', () => {
|
||||
const component = shallow(<Switch value={false} />);
|
||||
assert(component.find('input').prop('checked') === false);
|
||||
const component = renderer.create(<Switch value={false} />);
|
||||
expect(component.toJSON()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('when "true" a checked checkbox is rendered', () => {
|
||||
const component = shallow(<Switch value />);
|
||||
assert(component.find('input').prop('checked') === true);
|
||||
const component = renderer.create(<Switch value />);
|
||||
expect(component.toJSON()).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -88,9 +88,9 @@ class Switch extends Component {
|
||||
const thumbStyle = [
|
||||
styles.thumb,
|
||||
{
|
||||
alignSelf: value ? 'flex-end' : 'flex-start',
|
||||
backgroundColor: thumbCurrentColor,
|
||||
height: thumbHeight,
|
||||
transform: [ { translateX: value ? '100%' : '0%' } ],
|
||||
width: thumbWidth
|
||||
},
|
||||
disabled && styles.disabledThumb
|
||||
@@ -151,16 +151,17 @@ const styles = StyleSheet.create({
|
||||
...StyleSheet.absoluteFillObject,
|
||||
height: '70%',
|
||||
margin: 'auto',
|
||||
transition: 'background-color 0.1s',
|
||||
transition: '0.1s',
|
||||
width: '90%'
|
||||
},
|
||||
disabledTrack: {
|
||||
backgroundColor: '#D5D5D5'
|
||||
},
|
||||
thumb: {
|
||||
alignSelf: 'flex-start',
|
||||
borderRadius: '100%',
|
||||
boxShadow: thumbDefaultBoxShadow,
|
||||
transition: 'background-color 0.1s'
|
||||
transition: '0.1s'
|
||||
},
|
||||
disabledThumb: {
|
||||
backgroundColor: '#BDBDBD'
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import TextPropTypes from '../../propTypes/TextPropTypes';
|
||||
import ViewStylePropTypes from '../View/ViewStylePropTypes';
|
||||
|
||||
module.exports = {
|
||||
module.exports = process.env.NODE_ENV !== 'production' ? {
|
||||
...ViewStylePropTypes,
|
||||
...TextPropTypes
|
||||
};
|
||||
} : {};
|
||||
|
||||
113
src/components/Text/__tests__/__snapshots__/index-test.js.snap
Normal file
113
src/components/Text/__tests__/__snapshots__/index-test.js.snap
Normal file
@@ -0,0 +1,113 @@
|
||||
exports[`components/Text prop "children" 1`] = `
|
||||
<span
|
||||
className=""
|
||||
style={
|
||||
Object {
|
||||
"borderBottomWidth": "0px",
|
||||
"borderLeftWidth": "0px",
|
||||
"borderRightWidth": "0px",
|
||||
"borderTopWidth": "0px",
|
||||
"color": "inherit",
|
||||
"display": "inline",
|
||||
"font": "inherit",
|
||||
"marginBottom": "0px",
|
||||
"marginLeft": "0px",
|
||||
"marginRight": "0px",
|
||||
"marginTop": "0px",
|
||||
"paddingBottom": "0px",
|
||||
"paddingLeft": "0px",
|
||||
"paddingRight": "0px",
|
||||
"paddingTop": "0px",
|
||||
"textDecoration": "none",
|
||||
"wordWrap": "break-word",
|
||||
}
|
||||
}>
|
||||
children
|
||||
</span>
|
||||
`;
|
||||
|
||||
exports[`components/Text prop "onPress" 1`] = `
|
||||
<span
|
||||
className=""
|
||||
onClick={[Function]}
|
||||
onKeyDown={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"borderBottomWidth": "0px",
|
||||
"borderLeftWidth": "0px",
|
||||
"borderRightWidth": "0px",
|
||||
"borderTopWidth": "0px",
|
||||
"color": "inherit",
|
||||
"cursor": "pointer",
|
||||
"display": "inline",
|
||||
"font": "inherit",
|
||||
"marginBottom": "0px",
|
||||
"marginLeft": "0px",
|
||||
"marginRight": "0px",
|
||||
"marginTop": "0px",
|
||||
"paddingBottom": "0px",
|
||||
"paddingLeft": "0px",
|
||||
"paddingRight": "0px",
|
||||
"paddingTop": "0px",
|
||||
"textDecoration": "none",
|
||||
"wordWrap": "break-word",
|
||||
}
|
||||
}
|
||||
tabIndex={0} />
|
||||
`;
|
||||
|
||||
exports[`components/Text prop "selectable" 1`] = `
|
||||
<span
|
||||
className=""
|
||||
style={
|
||||
Object {
|
||||
"borderBottomWidth": "0px",
|
||||
"borderLeftWidth": "0px",
|
||||
"borderRightWidth": "0px",
|
||||
"borderTopWidth": "0px",
|
||||
"color": "inherit",
|
||||
"display": "inline",
|
||||
"font": "inherit",
|
||||
"marginBottom": "0px",
|
||||
"marginLeft": "0px",
|
||||
"marginRight": "0px",
|
||||
"marginTop": "0px",
|
||||
"paddingBottom": "0px",
|
||||
"paddingLeft": "0px",
|
||||
"paddingRight": "0px",
|
||||
"paddingTop": "0px",
|
||||
"textDecoration": "none",
|
||||
"wordWrap": "break-word",
|
||||
}
|
||||
} />
|
||||
`;
|
||||
|
||||
exports[`components/Text prop "selectable" 2`] = `
|
||||
<span
|
||||
className=""
|
||||
style={
|
||||
Object {
|
||||
"MozUserSelect": "none",
|
||||
"WebkitUserSelect": "none",
|
||||
"borderBottomWidth": "0px",
|
||||
"borderLeftWidth": "0px",
|
||||
"borderRightWidth": "0px",
|
||||
"borderTopWidth": "0px",
|
||||
"color": "inherit",
|
||||
"display": "inline",
|
||||
"font": "inherit",
|
||||
"marginBottom": "0px",
|
||||
"marginLeft": "0px",
|
||||
"marginRight": "0px",
|
||||
"marginTop": "0px",
|
||||
"msUserSelect": "none",
|
||||
"paddingBottom": "0px",
|
||||
"paddingLeft": "0px",
|
||||
"paddingRight": "0px",
|
||||
"paddingTop": "0px",
|
||||
"textDecoration": "none",
|
||||
"userSelect": "none",
|
||||
"wordWrap": "break-word",
|
||||
}
|
||||
} />
|
||||
`;
|
||||
@@ -1,41 +1,29 @@
|
||||
/* eslint-env mocha */
|
||||
/* eslint-env jasmine, jest */
|
||||
|
||||
import assert from 'assert';
|
||||
import React from 'react';
|
||||
import renderer from 'react-test-renderer';
|
||||
import Text from '../';
|
||||
import { mount, shallow } from 'enzyme';
|
||||
|
||||
suite('components/Text', () => {
|
||||
jest.mock('react-dom');
|
||||
|
||||
describe('components/Text', () => {
|
||||
test('prop "children"', () => {
|
||||
const children = 'children';
|
||||
const text = shallow(<Text>{children}</Text>);
|
||||
assert.equal(text.prop('children'), children);
|
||||
const component = renderer.create(<Text>children</Text>);
|
||||
expect(component.toJSON()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('prop "numberOfLines"');
|
||||
|
||||
test('prop "onLayout"', (done) => {
|
||||
mount(<Text onLayout={onLayout} />);
|
||||
function onLayout(e) {
|
||||
const { layout } = e.nativeEvent;
|
||||
assert.deepEqual(layout, { x: 0, y: 0, width: 0, height: 0 });
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
test('prop "onPress"', (done) => {
|
||||
const text = mount(<Text onPress={onPress} />);
|
||||
text.simulate('click');
|
||||
function onPress(e) {
|
||||
assert.ok(e.nativeEvent);
|
||||
done();
|
||||
}
|
||||
test('prop "onPress"', () => {
|
||||
const onPress = (e) => {};
|
||||
const component = renderer.create(<Text onPress={onPress} />);
|
||||
expect(component.toJSON()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('prop "selectable"', () => {
|
||||
let text = shallow(<Text />);
|
||||
assert.equal(text.prop('style').userSelect, undefined);
|
||||
text = shallow(<Text selectable={false} />);
|
||||
assert.equal(text.prop('style').userSelect, 'none');
|
||||
let component = renderer.create(<Text />);
|
||||
expect(component.toJSON()).toMatchSnapshot();
|
||||
component = renderer.create(<Text selectable={false} />);
|
||||
expect(component.toJSON()).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -12,7 +12,7 @@ class Text extends Component {
|
||||
|
||||
static propTypes = {
|
||||
...BaseComponentPropTypes,
|
||||
accessibilityRole: PropTypes.oneOf([ 'heading', 'link' ]),
|
||||
accessibilityRole: PropTypes.oneOf([ 'button', 'heading', 'link', 'listitem' ]),
|
||||
children: PropTypes.any,
|
||||
numberOfLines: PropTypes.number,
|
||||
onLayout: PropTypes.func,
|
||||
@@ -29,27 +29,44 @@ class Text extends Component {
|
||||
render() {
|
||||
const {
|
||||
numberOfLines,
|
||||
onLayout, // eslint-disable-line
|
||||
onPress, // eslint-disable-line
|
||||
onPress,
|
||||
selectable,
|
||||
style,
|
||||
/* eslint-disable */
|
||||
adjustsFontSizeToFit,
|
||||
allowFontScaling,
|
||||
ellipsizeMode,
|
||||
minimumFontScale,
|
||||
onLayout,
|
||||
suppressHighlighting,
|
||||
/* eslint-enable */
|
||||
...other
|
||||
} = this.props;
|
||||
|
||||
if (onPress) {
|
||||
other.onClick = onPress;
|
||||
other.onKeyDown = this._createEnterHandler(onPress);
|
||||
other.tabIndex = 0;
|
||||
}
|
||||
|
||||
return createDOMElement('span', {
|
||||
...other,
|
||||
onClick: this._onPress,
|
||||
style: [
|
||||
styles.initial,
|
||||
style,
|
||||
!selectable && styles.notSelectable,
|
||||
numberOfLines === 1 && styles.singleLineStyle
|
||||
numberOfLines === 1 && styles.singleLineStyle,
|
||||
onPress && styles.pressable
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
_onPress = (e) => {
|
||||
if (this.props.onPress) { this.props.onPress(e); }
|
||||
_createEnterHandler(fn) {
|
||||
return (e) => {
|
||||
if (e.keyCode === 13) {
|
||||
fn && fn(e);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,6 +84,9 @@ const styles = StyleSheet.create({
|
||||
notSelectable: {
|
||||
userSelect: 'none'
|
||||
},
|
||||
pressable: {
|
||||
cursor: 'pointer'
|
||||
},
|
||||
singleLineStyle: {
|
||||
maxWidth: '100%',
|
||||
overflow: 'hidden',
|
||||
|
||||
@@ -24,6 +24,9 @@ const TextInputState = {
|
||||
* If no text field is focused it returns null
|
||||
*/
|
||||
currentlyFocusedField(): ?Object {
|
||||
if (document.activeElement !== this._currentlyFocusedNode) {
|
||||
this._currentlyFocusedNode = null;
|
||||
}
|
||||
return this._currentlyFocusedNode;
|
||||
},
|
||||
|
||||
@@ -33,7 +36,7 @@ const TextInputState = {
|
||||
* noop if the text field was already focused
|
||||
*/
|
||||
focusTextInput(textFieldNode: ?Object) {
|
||||
if (this._currentlyFocusedNode !== textFieldNode && textFieldNode !== null) {
|
||||
if (document.activeElement !== textFieldNode && textFieldNode !== null) {
|
||||
this._currentlyFocusedNode = textFieldNode;
|
||||
UIManager.focus(textFieldNode);
|
||||
}
|
||||
@@ -45,7 +48,7 @@ const TextInputState = {
|
||||
* noop if it wasn't focused
|
||||
*/
|
||||
blurTextInput(textFieldNode: ?Object) {
|
||||
if (this._currentlyFocusedNode === textFieldNode && textFieldNode !== null) {
|
||||
if (document.activeElement === textFieldNode && textFieldNode !== null) {
|
||||
this._currentlyFocusedNode = null;
|
||||
UIManager.blur(textFieldNode);
|
||||
}
|
||||
|
||||
@@ -1,16 +1,12 @@
|
||||
/* eslint-env mocha */
|
||||
/* eslint-env jasmine, jest */
|
||||
|
||||
import assert from 'assert';
|
||||
import React from 'react';
|
||||
import StyleSheet from '../../../apis/StyleSheet';
|
||||
import TextareaAutosize from 'react-textarea-autosize';
|
||||
import TextInput from '..';
|
||||
import { mount, shallow } from 'enzyme';
|
||||
|
||||
const placeholderText = 'placeholderText';
|
||||
const findNativeInput = (wrapper) => wrapper.find('input');
|
||||
const findNativeTextarea = (wrapper) => wrapper.find(TextareaAutosize);
|
||||
const findPlaceholder = (wrapper) => wrapper.find({ children: placeholderText });
|
||||
|
||||
const testIfDocumentIsFocused = (message, fn) => {
|
||||
if (document.hasFocus && document.hasFocus()) {
|
||||
@@ -20,23 +16,23 @@ const testIfDocumentIsFocused = (message, fn) => {
|
||||
}
|
||||
};
|
||||
|
||||
suite('components/TextInput', () => {
|
||||
describe('components/TextInput', () => {
|
||||
test('prop "autoComplete"', () => {
|
||||
// off
|
||||
let input = findNativeInput(shallow(<TextInput />));
|
||||
assert.equal(input.prop('autoComplete'), undefined);
|
||||
// on
|
||||
input = findNativeInput(shallow(<TextInput autoComplete />));
|
||||
assert.equal(input.prop('autoComplete'), 'on');
|
||||
let input = findNativeInput(shallow(<TextInput />));
|
||||
expect(input.prop('autoComplete')).toEqual('on');
|
||||
// off
|
||||
input = findNativeInput(shallow(<TextInput autoComplete='off' />));
|
||||
expect(input.prop('autoComplete')).toEqual('off');
|
||||
});
|
||||
|
||||
test('prop "autoFocus"', () => {
|
||||
// false
|
||||
let input = findNativeInput(mount(<TextInput />));
|
||||
assert.equal(input.prop('autoFocus'), undefined);
|
||||
expect(input.prop('autoFocus')).toEqual(undefined);
|
||||
// true
|
||||
input = findNativeInput(mount(<TextInput autoFocus />));
|
||||
assert.equal(input.prop('autoFocus'), true);
|
||||
expect(input.prop('autoFocus')).toEqual(true);
|
||||
});
|
||||
|
||||
testIfDocumentIsFocused('prop "clearTextOnFocus"', () => {
|
||||
@@ -44,53 +40,53 @@ suite('components/TextInput', () => {
|
||||
// false
|
||||
let input = findNativeInput(mount(<TextInput defaultValue={defaultValue} />));
|
||||
input.simulate('focus');
|
||||
assert.equal(input.node.value, defaultValue);
|
||||
expect(input.node.value).toEqual(defaultValue);
|
||||
// true
|
||||
input = findNativeInput(mount(<TextInput clearTextOnFocus defaultValue={defaultValue} />));
|
||||
input.simulate('focus');
|
||||
assert.equal(input.node.value, '');
|
||||
expect(input.node.value).toEqual('');
|
||||
});
|
||||
|
||||
test('prop "defaultValue"', () => {
|
||||
const defaultValue = 'defaultValue';
|
||||
const input = findNativeInput(shallow(<TextInput defaultValue={defaultValue} />));
|
||||
assert.equal(input.prop('defaultValue'), defaultValue);
|
||||
expect(input.prop('defaultValue')).toEqual(defaultValue);
|
||||
});
|
||||
|
||||
test('prop "editable"', () => {
|
||||
// true
|
||||
let input = findNativeInput(shallow(<TextInput />));
|
||||
assert.equal(input.prop('readOnly'), false);
|
||||
expect(input.prop('readOnly')).toEqual(false);
|
||||
// false
|
||||
input = findNativeInput(shallow(<TextInput editable={false} />));
|
||||
assert.equal(input.prop('readOnly'), true);
|
||||
expect(input.prop('readOnly')).toEqual(true);
|
||||
});
|
||||
|
||||
test('prop "keyboardType"', () => {
|
||||
// default
|
||||
let input = findNativeInput(shallow(<TextInput />));
|
||||
assert.equal(input.prop('type'), 'text');
|
||||
expect(input.prop('type')).toEqual('text');
|
||||
input = findNativeInput(shallow(<TextInput keyboardType='default' />));
|
||||
assert.equal(input.prop('type'), 'text');
|
||||
expect(input.prop('type')).toEqual('text');
|
||||
// email-address
|
||||
input = findNativeInput(shallow(<TextInput keyboardType='email-address' />));
|
||||
assert.equal(input.prop('type'), 'email');
|
||||
expect(input.prop('type')).toEqual('email');
|
||||
// numeric
|
||||
input = findNativeInput(shallow(<TextInput keyboardType='numeric' />));
|
||||
assert.equal(input.prop('type'), 'number');
|
||||
expect(input.prop('type')).toEqual('number');
|
||||
// phone-pad
|
||||
input = findNativeInput(shallow(<TextInput keyboardType='phone-pad' />));
|
||||
assert.equal(input.prop('type'), 'tel');
|
||||
expect(input.prop('type')).toEqual('tel');
|
||||
// url
|
||||
input = findNativeInput(shallow(<TextInput keyboardType='url' />));
|
||||
assert.equal(input.prop('type'), 'url');
|
||||
expect(input.prop('type')).toEqual('url');
|
||||
});
|
||||
|
||||
test('prop "maxLength"', () => {
|
||||
let input = findNativeInput(shallow(<TextInput />));
|
||||
assert.equal(input.prop('maxLength'), undefined);
|
||||
expect(input.prop('maxLength')).toEqual(undefined);
|
||||
input = findNativeInput(shallow(<TextInput maxLength={10} />));
|
||||
assert.equal(input.prop('maxLength'), '10');
|
||||
expect(input.prop('maxLength')).toEqual(10);
|
||||
});
|
||||
|
||||
test('prop "maxNumberOfLines"', () => {
|
||||
@@ -107,25 +103,25 @@ suite('components/TextInput', () => {
|
||||
value={generateValue()}
|
||||
/>
|
||||
));
|
||||
assert.equal(input.prop('maxRows'), 3);
|
||||
expect(input.prop('maxRows')).toEqual(3);
|
||||
});
|
||||
|
||||
test('prop "multiline"', () => {
|
||||
// false
|
||||
let input = findNativeInput(shallow(<TextInput />));
|
||||
assert.equal(input.length, 1);
|
||||
expect(input.length).toEqual(1);
|
||||
// true
|
||||
input = findNativeTextarea(shallow(<TextInput multiline />));
|
||||
assert.equal(input.length, 1);
|
||||
expect(input.length).toEqual(1);
|
||||
});
|
||||
|
||||
test('prop "numberOfLines"', () => {
|
||||
// missing multiline
|
||||
let input = findNativeInput(shallow(<TextInput numberOfLines={2} />));
|
||||
assert.equal(input.length, 1);
|
||||
expect(input.length).toEqual(1);
|
||||
// with multiline
|
||||
input = findNativeTextarea(shallow(<TextInput multiline numberOfLines={2} />));
|
||||
assert.equal(input.length, 1);
|
||||
expect(input.length).toEqual(1);
|
||||
|
||||
input = findNativeTextarea(shallow(
|
||||
<TextInput
|
||||
@@ -133,14 +129,14 @@ suite('components/TextInput', () => {
|
||||
numberOfLines={3}
|
||||
/>
|
||||
));
|
||||
assert.equal(input.prop('minRows'), 3);
|
||||
expect(input.prop('minRows')).toEqual(3);
|
||||
});
|
||||
|
||||
test('prop "onBlur"', (done) => {
|
||||
const input = findNativeInput(mount(<TextInput onBlur={onBlur} />));
|
||||
input.simulate('blur');
|
||||
function onBlur(e) {
|
||||
assert.ok(e);
|
||||
expect(e).toBeTruthy();
|
||||
done();
|
||||
}
|
||||
});
|
||||
@@ -149,7 +145,7 @@ suite('components/TextInput', () => {
|
||||
const input = findNativeInput(mount(<TextInput onChange={onChange} />));
|
||||
input.simulate('change');
|
||||
function onChange(e) {
|
||||
assert.ok(e);
|
||||
expect(e).toBeTruthy();
|
||||
done();
|
||||
}
|
||||
});
|
||||
@@ -159,7 +155,7 @@ suite('components/TextInput', () => {
|
||||
const input = findNativeInput(mount(<TextInput onChangeText={onChangeText} />));
|
||||
input.simulate('change', { target: { value: newText } });
|
||||
function onChangeText(text) {
|
||||
assert.equal(text, newText);
|
||||
expect(text).toEqual(newText);
|
||||
done();
|
||||
}
|
||||
});
|
||||
@@ -168,7 +164,7 @@ suite('components/TextInput', () => {
|
||||
const input = findNativeInput(mount(<TextInput onFocus={onFocus} />));
|
||||
input.simulate('focus');
|
||||
function onFocus(e) {
|
||||
assert.ok(e);
|
||||
expect(e).toBeTruthy();
|
||||
done();
|
||||
}
|
||||
});
|
||||
@@ -179,36 +175,18 @@ suite('components/TextInput', () => {
|
||||
const input = findNativeInput(mount(<TextInput defaultValue='12345' onSelectionChange={onSelectionChange} />));
|
||||
input.simulate('select', { target: { selectionStart: 0, selectionEnd: 3 } });
|
||||
function onSelectionChange(e) {
|
||||
assert.equal(e.selectionEnd, 3);
|
||||
assert.equal(e.selectionStart, 0);
|
||||
expect(e.nativeEvent.selection.end).toEqual(3);
|
||||
expect(e.nativeEvent.selection.start).toEqual(0);
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
test('prop "placeholder"', () => {
|
||||
let textInput = shallow(<TextInput />);
|
||||
assert.equal(findPlaceholder(textInput).length, 0);
|
||||
|
||||
textInput = shallow(<TextInput placeholder={placeholderText} />);
|
||||
assert.equal(findPlaceholder(textInput).length, 1);
|
||||
});
|
||||
|
||||
test('prop "placeholderTextColor"', () => {
|
||||
let placeholderElement = findPlaceholder(shallow(<TextInput placeholder={placeholderText} />));
|
||||
assert.equal(StyleSheet.flatten(placeholderElement.prop('style')).color, 'darkgray');
|
||||
|
||||
placeholderElement = findPlaceholder(
|
||||
shallow(<TextInput placeholder={placeholderText} placeholderTextColor='red' />)
|
||||
);
|
||||
assert.equal(StyleSheet.flatten(placeholderElement.prop('style')).color, 'red');
|
||||
});
|
||||
|
||||
test('prop "secureTextEntry"', () => {
|
||||
let input = findNativeInput(shallow(<TextInput secureTextEntry />));
|
||||
assert.equal(input.prop('type'), 'password');
|
||||
expect(input.prop('type')).toEqual('password');
|
||||
// ignored for multiline
|
||||
input = findNativeTextarea(shallow(<TextInput multiline secureTextEntry />));
|
||||
assert.equal(input.prop('type'), undefined);
|
||||
expect(input.prop('type')).toEqual(undefined);
|
||||
});
|
||||
|
||||
testIfDocumentIsFocused('prop "selectTextOnFocus"', () => {
|
||||
@@ -216,8 +194,8 @@ suite('components/TextInput', () => {
|
||||
// false
|
||||
let input = findNativeInput(mount(<TextInput defaultValue={text} />));
|
||||
input.node.focus();
|
||||
assert.equal(input.node.selectionEnd, 4);
|
||||
assert.equal(input.node.selectionStart, 4);
|
||||
expect(input.node.selectionEnd).toEqual(4);
|
||||
expect(input.node.selectionStart).toEqual(4);
|
||||
// true
|
||||
input = findNativeInput(mount(<TextInput defaultValue={text} selectTextOnFocus />));
|
||||
// input.node.focus()
|
||||
@@ -225,23 +203,9 @@ suite('components/TextInput', () => {
|
||||
// assert.equal(input.node.selectionStart, 0)
|
||||
});
|
||||
|
||||
test('prop "style"', () => {
|
||||
const styles = StyleSheet.create({
|
||||
root: {
|
||||
borderWidth: 1,
|
||||
textAlign: 'center'
|
||||
}
|
||||
});
|
||||
const textInput = shallow(<TextInput style={styles.root} />);
|
||||
const input = findNativeInput(textInput);
|
||||
const borderWidth = StyleSheet.flatten(textInput.prop('style')).borderWidth;
|
||||
assert.equal(borderWidth, 1, 'expected View styles to be applied to the "View"');
|
||||
assert.equal(input.prop('style').textAlign, 'center', 'expected Text styles to be applied to the "input"');
|
||||
});
|
||||
|
||||
test('prop "value"', () => {
|
||||
const value = 'value';
|
||||
const input = findNativeInput(shallow(<TextInput value={value} />));
|
||||
assert.equal(input.prop('value'), value);
|
||||
expect(input.prop('value')).toEqual(value);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,31 +1,66 @@
|
||||
import applyLayout from '../../modules/applyLayout';
|
||||
import applyNativeMethods from '../../modules/applyNativeMethods';
|
||||
import createDOMElement from '../../modules/createDOMElement';
|
||||
import omit from 'lodash/omit';
|
||||
import pick from 'lodash/pick';
|
||||
import ReactDOM from 'react-dom';
|
||||
import findNodeHandle from '../../modules/findNodeHandle';
|
||||
import StyleSheet from '../../apis/StyleSheet';
|
||||
import Text from '../Text';
|
||||
import TextareaAutosize from 'react-textarea-autosize';
|
||||
import TextInputState from './TextInputState';
|
||||
import UIManager from '../../apis/UIManager';
|
||||
import View from '../View';
|
||||
import ViewStylePropTypes from '../View/ViewStylePropTypes';
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import { Component, PropTypes } from 'react';
|
||||
|
||||
const viewStyleProps = Object.keys(ViewStylePropTypes);
|
||||
/**
|
||||
* React Native events differ from W3C events.
|
||||
*/
|
||||
const normalizeEventHandler = (handler) => (e) => {
|
||||
if (handler) {
|
||||
e.nativeEvent.text = e.target.value;
|
||||
return handler(e);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Determines whether a 'selection' prop differs from a node's existing
|
||||
* selection state.
|
||||
*/
|
||||
const isSelectionStale = (node, selection) => {
|
||||
if (node && selection) {
|
||||
const { selectionEnd, selectionStart } = node;
|
||||
const { start, end } = selection;
|
||||
return start !== selectionStart || end !== selectionEnd;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Certain input types do no support 'selectSelectionRange' and will throw an
|
||||
* error.
|
||||
*/
|
||||
const setSelection = (node, selection) => {
|
||||
try {
|
||||
if (isSelectionStale(node, selection)) {
|
||||
const { start, end } = selection;
|
||||
node.setSelectionRange(start, end || start);
|
||||
}
|
||||
} catch (e) {}
|
||||
};
|
||||
|
||||
class TextInput extends Component {
|
||||
static displayName = 'TextInput';
|
||||
|
||||
static propTypes = {
|
||||
...View.propTypes,
|
||||
autoComplete: PropTypes.bool,
|
||||
autoCapitalize: PropTypes.oneOf([ 'characters', 'none', 'sentences', 'words' ]),
|
||||
autoComplete: PropTypes.string,
|
||||
autoCorrect: PropTypes.bool,
|
||||
autoFocus: PropTypes.bool,
|
||||
blurOnSubmit: PropTypes.bool,
|
||||
clearTextOnFocus: PropTypes.bool,
|
||||
defaultValue: PropTypes.string,
|
||||
editable: PropTypes.bool,
|
||||
keyboardType: PropTypes.oneOf([
|
||||
'default', 'email-address', 'numeric', 'phone-pad', 'search', 'url', 'web-search'
|
||||
'default', 'email-address', 'number-pad', 'numeric', 'phone-pad', 'search', 'url', 'web-search'
|
||||
]),
|
||||
maxLength: PropTypes.number,
|
||||
maxNumberOfLines: PropTypes.number,
|
||||
@@ -35,17 +70,24 @@ class TextInput extends Component {
|
||||
onChange: PropTypes.func,
|
||||
onChangeText: PropTypes.func,
|
||||
onFocus: PropTypes.func,
|
||||
onKeyPress: PropTypes.func,
|
||||
onSelectionChange: PropTypes.func,
|
||||
placeholder: PropTypes.string,
|
||||
placeholderTextColor: PropTypes.string,
|
||||
secureTextEntry: PropTypes.bool,
|
||||
selectTextOnFocus: PropTypes.bool,
|
||||
selection: PropTypes.shape({
|
||||
start: PropTypes.number.isRequired,
|
||||
end: PropTypes.number
|
||||
}),
|
||||
style: Text.propTypes.style,
|
||||
testID: Text.propTypes.testID,
|
||||
value: PropTypes.string
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
autoCapitalize: 'sentences',
|
||||
autoComplete: 'on',
|
||||
autoCorrect: true,
|
||||
editable: true,
|
||||
keyboardType: 'default',
|
||||
multiline: false,
|
||||
@@ -54,47 +96,63 @@ class TextInput extends Component {
|
||||
style: {}
|
||||
};
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
this.state = { showPlaceholder: !props.value && !props.defaultValue };
|
||||
}
|
||||
|
||||
blur() {
|
||||
TextInputState.blurTextInput(ReactDOM.findDOMNode(this._inputRef));
|
||||
TextInputState.blurTextInput(this._node);
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.setNativeProps({ text: '' });
|
||||
this._node.value = '';
|
||||
}
|
||||
|
||||
focus() {
|
||||
TextInputState.focusTextInput(ReactDOM.findDOMNode(this._inputRef));
|
||||
TextInputState.focusTextInput(this._node);
|
||||
}
|
||||
|
||||
isFocused() {
|
||||
return TextInputState.currentlyFocusedField() === this._node;
|
||||
}
|
||||
|
||||
setNativeProps(props) {
|
||||
UIManager.updateView(this._inputRef, props, this);
|
||||
UIManager.updateView(this._node, props, this);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
setSelection(this._node, this.props.selection);
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
setSelection(this._node, this.props.selection);
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
accessibilityLabel, // eslint-disable-line
|
||||
autoComplete,
|
||||
autoFocus,
|
||||
defaultValue,
|
||||
autoCorrect,
|
||||
editable,
|
||||
keyboardType,
|
||||
maxLength,
|
||||
maxNumberOfLines,
|
||||
multiline,
|
||||
numberOfLines,
|
||||
onLayout,
|
||||
onSelectionChange,
|
||||
placeholder,
|
||||
placeholderTextColor,
|
||||
secureTextEntry,
|
||||
style,
|
||||
testID,
|
||||
value
|
||||
/* eslint-disable */
|
||||
blurOnSubmit,
|
||||
clearTextOnFocus,
|
||||
dataDetectorTypes,
|
||||
enablesReturnKeyAutomatically,
|
||||
keyboardAppearance,
|
||||
onChangeText,
|
||||
onContentSizeChange,
|
||||
onEndEditing,
|
||||
onLayout,
|
||||
onSelectionChange,
|
||||
onSubmitEditing,
|
||||
placeholderTextColor,
|
||||
returnKeyType,
|
||||
selection,
|
||||
selectionColor,
|
||||
selectTextOnFocus,
|
||||
/* eslint-enable */
|
||||
...other
|
||||
} = this.props;
|
||||
|
||||
let type;
|
||||
@@ -103,6 +161,7 @@ class TextInput extends Component {
|
||||
case 'email-address':
|
||||
type = 'email';
|
||||
break;
|
||||
case 'number-pad':
|
||||
case 'numeric':
|
||||
type = 'number';
|
||||
break;
|
||||
@@ -124,160 +183,96 @@ class TextInput extends Component {
|
||||
type = 'password';
|
||||
}
|
||||
|
||||
// In order to support 'Text' styles on 'TextInput', we split the 'Text'
|
||||
// and 'View' styles and apply them to the 'Text' and 'View' components
|
||||
// used in the implementation
|
||||
const flattenedStyle = StyleSheet.flatten(style);
|
||||
const rootStyles = pick(flattenedStyle, viewStyleProps);
|
||||
const textStyles = omit(flattenedStyle, viewStyleProps);
|
||||
|
||||
const propsCommon = {
|
||||
autoComplete: autoComplete && 'on',
|
||||
autoFocus,
|
||||
defaultValue,
|
||||
maxLength,
|
||||
onBlur: this._handleBlur,
|
||||
onChange: this._handleChange,
|
||||
onFocus: this._handleFocus,
|
||||
onSelect: onSelectionChange && this._handleSelectionChange,
|
||||
readOnly: !editable,
|
||||
ref: this._setInputRef,
|
||||
style: [ styles.input, textStyles, { outline: style.outline } ],
|
||||
value
|
||||
};
|
||||
|
||||
const propsMultiline = {
|
||||
...propsCommon,
|
||||
maxRows: maxNumberOfLines || numberOfLines,
|
||||
minRows: numberOfLines
|
||||
};
|
||||
|
||||
const propsSingleline = {
|
||||
...propsCommon,
|
||||
type
|
||||
};
|
||||
|
||||
const component = multiline ? TextareaAutosize : 'input';
|
||||
const props = multiline ? propsMultiline : propsSingleline;
|
||||
|
||||
const optionalPlaceholder = placeholder && this.state.showPlaceholder && (
|
||||
<View pointerEvents='none' style={styles.placeholder}>
|
||||
<Text
|
||||
children={placeholder}
|
||||
style={[
|
||||
styles.placeholderText,
|
||||
textStyles,
|
||||
placeholderTextColor && { color: placeholderTextColor }
|
||||
]}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
const props = {
|
||||
...other,
|
||||
autoCorrect: autoCorrect ? 'on' : 'off',
|
||||
onBlur: normalizeEventHandler(this._handleBlur),
|
||||
onChange: normalizeEventHandler(this._handleChange),
|
||||
onFocus: normalizeEventHandler(this._handleFocus),
|
||||
onKeyPress: normalizeEventHandler(this._handleKeyPress),
|
||||
onSelect: normalizeEventHandler(this._handleSelectionChange),
|
||||
readOnly: !editable,
|
||||
ref: this._setNode,
|
||||
style: [
|
||||
styles.initial,
|
||||
style
|
||||
]
|
||||
};
|
||||
|
||||
return (
|
||||
<View
|
||||
accessibilityLabel={accessibilityLabel}
|
||||
onClick={this._handleClick}
|
||||
onLayout={onLayout}
|
||||
style={[ styles.initial, rootStyles ]}
|
||||
testID={testID}
|
||||
>
|
||||
<View style={styles.wrapper}>
|
||||
{createDOMElement(component, props)}
|
||||
{optionalPlaceholder}
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
if (multiline) {
|
||||
props.maxRows = maxNumberOfLines || numberOfLines;
|
||||
props.minRows = numberOfLines;
|
||||
} else {
|
||||
props.type = type;
|
||||
}
|
||||
|
||||
return createDOMElement(component, props);
|
||||
}
|
||||
|
||||
_handleBlur = (e) => {
|
||||
const { onBlur } = this.props;
|
||||
const text = e.target.value;
|
||||
this.setState({ showPlaceholder: text === '' });
|
||||
this.blur();
|
||||
if (onBlur) { onBlur(e); }
|
||||
}
|
||||
|
||||
_handleChange = (e) => {
|
||||
const { onChange, onChangeText } = this.props;
|
||||
const text = e.target.value;
|
||||
this.setState({ showPlaceholder: text === '' });
|
||||
const { text } = e.nativeEvent;
|
||||
if (onChange) { onChange(e); }
|
||||
if (onChangeText) { onChangeText(text); }
|
||||
if (!this._inputRef) {
|
||||
// calling `this.props.onChange` or `this.props.onChangeText`
|
||||
// may clean up the input itself. Exits here.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_handleClick = (e) => {
|
||||
this.focus();
|
||||
}
|
||||
|
||||
_handleFocus = (e) => {
|
||||
const { clearTextOnFocus, onFocus, selectTextOnFocus } = this.props;
|
||||
const node = ReactDOM.findDOMNode(this._inputRef);
|
||||
const text = e.target.value;
|
||||
const node = this._node;
|
||||
if (onFocus) { onFocus(e); }
|
||||
if (clearTextOnFocus) { this.clear(); }
|
||||
if (selectTextOnFocus) { node.select(); }
|
||||
this.setState({ showPlaceholder: text === '' });
|
||||
if (selectTextOnFocus) { node && node.select(); }
|
||||
}
|
||||
|
||||
_handleKeyPress = (e) => {
|
||||
const { blurOnSubmit, multiline, onKeyPress, onSubmitEditing } = this.props;
|
||||
const blurOnSubmitDefault = !multiline;
|
||||
const shouldBlurOnSubmit = blurOnSubmit == null ? blurOnSubmitDefault : blurOnSubmit;
|
||||
if (onKeyPress) { onKeyPress(e); }
|
||||
if (!e.isDefaultPrevented() && e.which === 13) {
|
||||
if (onSubmitEditing) { onSubmitEditing(e); }
|
||||
if (shouldBlurOnSubmit) { this.blur(); }
|
||||
}
|
||||
}
|
||||
|
||||
_handleSelectionChange = (e) => {
|
||||
const { onSelectionChange } = this.props;
|
||||
try {
|
||||
const { selectionDirection, selectionEnd, selectionStart } = e.target;
|
||||
const event = {
|
||||
selectionDirection,
|
||||
selectionEnd,
|
||||
selectionStart,
|
||||
nativeEvent: e.nativeEvent
|
||||
};
|
||||
if (onSelectionChange) { onSelectionChange(event); }
|
||||
} catch (e) {}
|
||||
const { onSelectionChange, selection = {} } = this.props;
|
||||
if (onSelectionChange) {
|
||||
try {
|
||||
const node = e.target;
|
||||
if (isSelectionStale(node, selection)) {
|
||||
const { selectionStart, selectionEnd } = node;
|
||||
e.nativeEvent.selection = { start: selectionStart, end: selectionEnd };
|
||||
onSelectionChange(e);
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
}
|
||||
|
||||
_setInputRef = (component) => {
|
||||
this._inputRef = component;
|
||||
_setNode = (component) => {
|
||||
this._node = findNodeHandle(component);
|
||||
}
|
||||
}
|
||||
|
||||
applyNativeMethods(TextInput);
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
initial: {
|
||||
borderColor: 'black'
|
||||
},
|
||||
wrapper: {
|
||||
flex: 1
|
||||
},
|
||||
input: {
|
||||
appearance: 'none',
|
||||
backgroundColor: 'transparent',
|
||||
borderColor: 'black',
|
||||
borderRadius: 0,
|
||||
borderWidth: 0,
|
||||
boxSizing: 'border-box',
|
||||
color: 'inherit',
|
||||
flex: 1,
|
||||
font: 'inherit',
|
||||
minHeight: '100%', // center small inputs (fix #139)
|
||||
padding: 0,
|
||||
zIndex: 1
|
||||
},
|
||||
placeholder: {
|
||||
bottom: 0,
|
||||
justifyContent: 'center',
|
||||
left: 0,
|
||||
position: 'absolute',
|
||||
right: 0,
|
||||
top: 0
|
||||
},
|
||||
placeholderText: {
|
||||
color: 'darkgray',
|
||||
overflow: 'hidden',
|
||||
whiteSpace: 'pre'
|
||||
padding: 0
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = TextInput;
|
||||
module.exports = applyLayout(applyNativeMethods(TextInput));
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
|
||||
/* @edit start */
|
||||
const BoundingDimensions = require('./BoundingDimensions');
|
||||
const keyMirror = require('fbjs/lib/keyMirror');
|
||||
const normalizeColor = require('../../modules/normalizeColor');
|
||||
const Position = require('./Position');
|
||||
const React = require('react');
|
||||
@@ -111,16 +110,16 @@ const View = require('../../components/View');
|
||||
/**
|
||||
* Touchable states.
|
||||
*/
|
||||
var States = keyMirror({
|
||||
NOT_RESPONDER: null, // Not the responder
|
||||
RESPONDER_INACTIVE_PRESS_IN: null, // Responder, inactive, in the `PressRect`
|
||||
RESPONDER_INACTIVE_PRESS_OUT: null, // Responder, inactive, out of `PressRect`
|
||||
RESPONDER_ACTIVE_PRESS_IN: null, // Responder, active, in the `PressRect`
|
||||
RESPONDER_ACTIVE_PRESS_OUT: null, // Responder, active, out of `PressRect`
|
||||
RESPONDER_ACTIVE_LONG_PRESS_IN: null, // Responder, active, in the `PressRect`, after long press threshold
|
||||
RESPONDER_ACTIVE_LONG_PRESS_OUT: null, // Responder, active, out of `PressRect`, after long press threshold
|
||||
ERROR: null
|
||||
});
|
||||
var States = {
|
||||
NOT_RESPONDER: 'NOT_RESPONDER', // Not the responder
|
||||
RESPONDER_INACTIVE_PRESS_IN: 'RESPONDER_INACTIVE_PRESS_IN', // Responder, inactive, in the `PressRect`
|
||||
RESPONDER_INACTIVE_PRESS_OUT: 'RESPONDER_INACTIVE_PRESS_OUT', // Responder, inactive, out of `PressRect`
|
||||
RESPONDER_ACTIVE_PRESS_IN: 'RESPONDER_ACTIVE_PRESS_IN', // Responder, active, in the `PressRect`
|
||||
RESPONDER_ACTIVE_PRESS_OUT: 'RESPONDER_ACTIVE_PRESS_OUT', // Responder, active, out of `PressRect`
|
||||
RESPONDER_ACTIVE_LONG_PRESS_IN: 'RESPONDER_ACTIVE_LONG_PRESS_IN', // Responder, active, in the `PressRect`, after long press threshold
|
||||
RESPONDER_ACTIVE_LONG_PRESS_OUT: 'RESPONDER_ACTIVE_LONG_PRESS_OUT', // Responder, active, out of `PressRect`, after long press threshold
|
||||
ERROR: 'ERROR'
|
||||
};
|
||||
|
||||
/**
|
||||
* Quick lookup map for states that are considered to be "active"
|
||||
@@ -147,15 +146,15 @@ var IsLongPressingIn = {
|
||||
/**
|
||||
* Inputs to the state machine.
|
||||
*/
|
||||
var Signals = keyMirror({
|
||||
DELAY: null,
|
||||
RESPONDER_GRANT: null,
|
||||
RESPONDER_RELEASE: null,
|
||||
RESPONDER_TERMINATED: null,
|
||||
ENTER_PRESS_RECT: null,
|
||||
LEAVE_PRESS_RECT: null,
|
||||
LONG_PRESS_DETECTED: null,
|
||||
});
|
||||
var Signals = {
|
||||
DELAY: 'DELAY',
|
||||
RESPONDER_GRANT: 'RESPONDER_GRANT',
|
||||
RESPONDER_RELEASE: 'RESPONDER_RELEASE',
|
||||
RESPONDER_TERMINATED: 'RESPONDER_TERMINATED',
|
||||
ENTER_PRESS_RECT: 'ENTER_PRESS_RECT',
|
||||
LEAVE_PRESS_RECT: 'LEAVE_PRESS_RECT',
|
||||
LONG_PRESS_DETECTED: 'LONG_PRESS_DETECTED',
|
||||
};
|
||||
|
||||
/**
|
||||
* Mapping from States x Signals => States
|
||||
|
||||
@@ -1,164 +0,0 @@
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule TouchableBounce
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var Animated = require('../../apis/Animated');
|
||||
var EdgeInsetsPropType = require('../../propTypes/EdgeInsetsPropType');
|
||||
var NativeMethodsMixin = require('../../modules/NativeMethodsMixin');
|
||||
var React = require('react');
|
||||
var StyleSheet = require('../../apis/StyleSheet');
|
||||
var Touchable = require('./Touchable');
|
||||
|
||||
type Event = Object;
|
||||
|
||||
type State = {
|
||||
animationID: ?number;
|
||||
scale: Animated.Value;
|
||||
};
|
||||
|
||||
var PRESS_RETENTION_OFFSET = {top: 20, left: 20, right: 20, bottom: 30};
|
||||
|
||||
/**
|
||||
* Example of using the `TouchableMixin` to play well with other responder
|
||||
* locking views including `ScrollView`. `TouchableMixin` provides touchable
|
||||
* hooks (`this.touchableHandle*`) that we forward events to. In turn,
|
||||
* `TouchableMixin` expects us to implement some abstract methods to handle
|
||||
* interesting interactions such as `handleTouchablePress`.
|
||||
*/
|
||||
var TouchableBounce = React.createClass({
|
||||
mixins: [Touchable.Mixin, NativeMethodsMixin],
|
||||
|
||||
propTypes: {
|
||||
onPress: React.PropTypes.func,
|
||||
onPressIn: React.PropTypes.func,
|
||||
onPressOut: React.PropTypes.func,
|
||||
// The function passed takes a callback to start the animation which should
|
||||
// be run after this onPress handler is done. You can use this (for example)
|
||||
// to update UI before starting the animation.
|
||||
onPressWithCompletion: React.PropTypes.func,
|
||||
// the function passed is called after the animation is complete
|
||||
onPressAnimationComplete: React.PropTypes.func,
|
||||
/**
|
||||
* When the scroll view is disabled, this defines how far your touch may
|
||||
* move off of the button, before deactivating the button. Once deactivated,
|
||||
* try moving it back and you'll see that the button is once again
|
||||
* reactivated! Move it back and forth several times while the scroll view
|
||||
* is disabled. Ensure you pass in a constant to reduce memory allocations.
|
||||
*/
|
||||
pressRetentionOffset: EdgeInsetsPropType,
|
||||
/**
|
||||
* This defines how far your touch can start away from the button. This is
|
||||
* added to `pressRetentionOffset` when moving off of the button.
|
||||
* ** NOTE **
|
||||
* The touch area never extends past the parent view bounds and the Z-index
|
||||
* of sibling views always takes precedence if a touch hits two overlapping
|
||||
* views.
|
||||
*/
|
||||
hitSlop: EdgeInsetsPropType,
|
||||
},
|
||||
|
||||
getInitialState: function(): State {
|
||||
return {
|
||||
...this.touchableGetInitialState(),
|
||||
scale: new Animated.Value(1),
|
||||
};
|
||||
},
|
||||
|
||||
bounceTo: function(
|
||||
value: number,
|
||||
velocity: number,
|
||||
bounciness: number,
|
||||
callback?: ?Function
|
||||
) {
|
||||
Animated.spring(this.state.scale, {
|
||||
toValue: value,
|
||||
velocity,
|
||||
bounciness,
|
||||
}).start(callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* `Touchable.Mixin` self callbacks. The mixin will invoke these if they are
|
||||
* defined on your component.
|
||||
*/
|
||||
touchableHandleActivePressIn: function(e: Event) {
|
||||
this.bounceTo(0.93, 0.1, 0);
|
||||
this.props.onPressIn && this.props.onPressIn(e);
|
||||
},
|
||||
|
||||
touchableHandleActivePressOut: function(e: Event) {
|
||||
this.bounceTo(1, 0.4, 0);
|
||||
this.props.onPressOut && this.props.onPressOut(e);
|
||||
},
|
||||
|
||||
touchableHandlePress: function(e: Event) {
|
||||
var onPressWithCompletion = this.props.onPressWithCompletion;
|
||||
if (onPressWithCompletion) {
|
||||
onPressWithCompletion(() => {
|
||||
this.state.scale.setValue(0.93);
|
||||
this.bounceTo(1, 10, 10, this.props.onPressAnimationComplete);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this.bounceTo(1, 10, 10, this.props.onPressAnimationComplete);
|
||||
this.props.onPress && this.props.onPress(e);
|
||||
},
|
||||
|
||||
touchableGetPressRectOffset: function(): typeof PRESS_RETENTION_OFFSET {
|
||||
return this.props.pressRetentionOffset || PRESS_RETENTION_OFFSET;
|
||||
},
|
||||
|
||||
touchableGetHitSlop: function(): ?Object {
|
||||
return this.props.hitSlop;
|
||||
},
|
||||
|
||||
touchableGetHighlightDelayMS: function(): number {
|
||||
return 0;
|
||||
},
|
||||
|
||||
render: function(): ReactElement {
|
||||
const scaleTransform = [{ scale: this.state.scale }];
|
||||
const propsTransform = this.props.style.transform;
|
||||
const transform = propsTransform && Array.isArray(propsTransform) ? propsTransform.concat(scaleTransform) : scaleTransform;
|
||||
|
||||
return (
|
||||
<Animated.View
|
||||
accessible={true}
|
||||
accessibilityLabel={this.props.accessibilityLabel}
|
||||
accessibilityRole={this.props.accessibilityRole || 'button'}
|
||||
testID={this.props.testID}
|
||||
hitSlop={this.props.hitSlop}
|
||||
onStartShouldSetResponder={this.touchableHandleStartShouldSetResponder}
|
||||
onResponderTerminationRequest={this.touchableHandleResponderTerminationRequest}
|
||||
onResponderGrant={this.touchableHandleResponderGrant}
|
||||
onResponderMove={this.touchableHandleResponderMove}
|
||||
onResponderRelease={this.touchableHandleResponderRelease}
|
||||
onResponderTerminate={this.touchableHandleResponderTerminate}
|
||||
style={[styles.root, this.props.style, { transform }]}
|
||||
tabIndex='0'
|
||||
>
|
||||
{this.props.children}
|
||||
</Animated.View>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
root: {
|
||||
cursor: 'pointer',
|
||||
userSelect: 'none'
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = TouchableBounce;
|
||||
@@ -18,15 +18,16 @@ var ColorPropType = require('../../propTypes/ColorPropType');
|
||||
var NativeMethodsMixin = require('../../modules/NativeMethodsMixin');
|
||||
var React = require('react');
|
||||
var StyleSheet = require('../../apis/StyleSheet');
|
||||
var StyleSheetPropType = require('../../propTypes/StyleSheetPropType');
|
||||
var TimerMixin = require('react-timer-mixin');
|
||||
var Touchable = require('./Touchable');
|
||||
var TouchableWithoutFeedback = require('./TouchableWithoutFeedback');
|
||||
var View = require('../View');
|
||||
var ViewStylePropTypes = require('../View/ViewStylePropTypes');
|
||||
|
||||
var ensureComponentIsNative = require('./ensureComponentIsNative');
|
||||
var ensurePositiveDelayProps = require('./ensurePositiveDelayProps');
|
||||
var keyOf = require('fbjs/lib/keyOf');
|
||||
var merge = require('../../modules/merge');
|
||||
|
||||
type Event = Object;
|
||||
|
||||
@@ -78,7 +79,7 @@ var TouchableHighlight = React.createClass({
|
||||
* active.
|
||||
*/
|
||||
underlayColor: ColorPropType,
|
||||
style: View.propTypes.style,
|
||||
style: StyleSheetPropType(ViewStylePropTypes),
|
||||
/**
|
||||
* Called immediately after the underlay is shown
|
||||
*/
|
||||
@@ -115,7 +116,7 @@ var TouchableHighlight = React.createClass({
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return merge(this.touchableGetInitialState(), this.computeSyntheticState(this.props))
|
||||
return { ...this.touchableGetInitialState(), ...this.computeSyntheticState(this.props) }
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
|
||||
@@ -14,13 +14,13 @@
|
||||
|
||||
// Note (avik): add @flow when Flow supports spread properties in propTypes
|
||||
|
||||
var Animated = require('../../apis/Animated');
|
||||
var NativeMethodsMixin = require('../../modules/NativeMethodsMixin');
|
||||
var React = require('react');
|
||||
var StyleSheet = require('../../apis/StyleSheet');
|
||||
var TimerMixin = require('react-timer-mixin');
|
||||
var Touchable = require('./Touchable');
|
||||
var TouchableWithoutFeedback = require('./TouchableWithoutFeedback');
|
||||
var View = require('../View');
|
||||
|
||||
var ensurePositiveDelayProps = require('./ensurePositiveDelayProps');
|
||||
var flattenStyle = StyleSheet.flatten
|
||||
@@ -70,10 +70,7 @@ var TouchableOpacity = React.createClass({
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
...this.touchableGetInitialState(),
|
||||
anim: new Animated.Value(1),
|
||||
};
|
||||
return this.touchableGetInitialState();
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
@@ -85,10 +82,11 @@ var TouchableOpacity = React.createClass({
|
||||
},
|
||||
|
||||
setOpacityTo: function(value) {
|
||||
Animated.timing(
|
||||
this.state.anim,
|
||||
{toValue: value, duration: 150}
|
||||
).start();
|
||||
this.setNativeProps({
|
||||
style: {
|
||||
opacity: value
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -166,7 +164,7 @@ var TouchableOpacity = React.createClass({
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<Animated.View
|
||||
<View
|
||||
accessible={this.props.accessible !== false}
|
||||
accessibilityLabel={this.props.accessibilityLabel}
|
||||
accessibilityRole={this.props.accessibilityRole}
|
||||
@@ -174,8 +172,7 @@ var TouchableOpacity = React.createClass({
|
||||
style={[
|
||||
styles.root,
|
||||
this.props.disabled && styles.disabled,
|
||||
this.props.style,
|
||||
{opacity: this.state.anim}
|
||||
this.props.style
|
||||
]}
|
||||
testID={this.props.testID}
|
||||
onLayout={this.props.onLayout}
|
||||
@@ -192,7 +189,7 @@ var TouchableOpacity = React.createClass({
|
||||
tabIndex={this.props.disabled ? null : '0'}
|
||||
>
|
||||
{this.props.children}
|
||||
</Animated.View>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
});
|
||||
@@ -200,6 +197,7 @@ var TouchableOpacity = React.createClass({
|
||||
var styles = StyleSheet.create({
|
||||
root: {
|
||||
cursor: 'pointer',
|
||||
transition: 'opacity 0.15s',
|
||||
userSelect: 'none'
|
||||
},
|
||||
disabled: {
|
||||
|
||||
@@ -16,7 +16,6 @@ var EdgeInsetsPropType = require('../../propTypes/EdgeInsetsPropType');
|
||||
var React = require('react');
|
||||
var TimerMixin = require('react-timer-mixin');
|
||||
var Touchable = require('./Touchable');
|
||||
var View = require('../View');
|
||||
var ensurePositiveDelayProps = require('./ensurePositiveDelayProps');
|
||||
var warning = require('fbjs/lib/warning');
|
||||
var StyleSheet = require('../../apis/StyleSheet');
|
||||
@@ -38,9 +37,9 @@ const TouchableWithoutFeedback = React.createClass({
|
||||
mixins: [TimerMixin, Touchable.Mixin],
|
||||
|
||||
propTypes: {
|
||||
accessible: View.propTypes.accessible,
|
||||
accessibilityLabel: View.propTypes.accessibilityLabel,
|
||||
accessibilityRole: View.propTypes.accessibilityRole,
|
||||
accessible: React.PropTypes.bool,
|
||||
accessibilityLabel: React.PropTypes.string,
|
||||
accessibilityRole: React.PropTypes.string,
|
||||
/**
|
||||
* If true, disable all interactions for this component.
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* eslint-env mocha */
|
||||
/* eslint-env jasmine, jest */
|
||||
|
||||
suite('components/Touchable', () => {
|
||||
describe('components/Touchable', () => {
|
||||
test.skip('NO TEST COVERAGE', () => {});
|
||||
});
|
||||
|
||||
@@ -8,7 +8,7 @@ const { number, oneOf, string } = PropTypes;
|
||||
const autoOrHiddenOrVisible = oneOf([ 'auto', 'hidden', 'visible' ]);
|
||||
const hiddenOrVisible = oneOf([ 'hidden', 'visible' ]);
|
||||
|
||||
module.exports = {
|
||||
module.exports = process.env.NODE_ENV !== 'production' ? {
|
||||
...BorderPropTypes,
|
||||
...LayoutPropTypes,
|
||||
...TransformPropTypes,
|
||||
@@ -36,4 +36,4 @@ module.exports = {
|
||||
userSelect: string,
|
||||
visibility: hiddenOrVisible,
|
||||
WebkitOverflowScrolling: oneOf([ 'auto', 'touch' ])
|
||||
};
|
||||
} : {};
|
||||
|
||||
525
src/components/View/__tests__/__snapshots__/index-test.js.snap
Normal file
525
src/components/View/__tests__/__snapshots__/index-test.js.snap
Normal file
@@ -0,0 +1,525 @@
|
||||
exports[`components/View prop "children" 1`] = `
|
||||
<div
|
||||
className=" __style_df"
|
||||
style={
|
||||
Object {
|
||||
"MozBoxSizing": "border-box",
|
||||
"WebkitAlignItems": "stretch",
|
||||
"WebkitBoxAlign": "stretch",
|
||||
"WebkitBoxDirection": "normal",
|
||||
"WebkitBoxOrient": "vertical",
|
||||
"WebkitFlexBasis": "auto",
|
||||
"WebkitFlexDirection": "column",
|
||||
"WebkitFlexShrink": 0,
|
||||
"alignItems": "stretch",
|
||||
"backgroundColor": "transparent",
|
||||
"borderBottomStyle": "solid",
|
||||
"borderBottomWidth": "0px",
|
||||
"borderLeftStyle": "solid",
|
||||
"borderLeftWidth": "0px",
|
||||
"borderRightStyle": "solid",
|
||||
"borderRightWidth": "0px",
|
||||
"borderTopStyle": "solid",
|
||||
"borderTopWidth": "0px",
|
||||
"boxSizing": "border-box",
|
||||
"color": "inherit",
|
||||
"display": null,
|
||||
"flexBasis": "auto",
|
||||
"flexDirection": "column",
|
||||
"flexShrink": 0,
|
||||
"font": "inherit",
|
||||
"listStyle": "none",
|
||||
"marginBottom": "0px",
|
||||
"marginLeft": "0px",
|
||||
"marginRight": "0px",
|
||||
"marginTop": "0px",
|
||||
"minHeight": "0px",
|
||||
"minWidth": "0px",
|
||||
"msFlexAlign": "stretch",
|
||||
"msFlexDirection": "column",
|
||||
"msFlexNegative": 0,
|
||||
"msPreferredSize": "auto",
|
||||
"paddingBottom": "0px",
|
||||
"paddingLeft": "0px",
|
||||
"paddingRight": "0px",
|
||||
"paddingTop": "0px",
|
||||
"position": "relative",
|
||||
"textAlign": "inherit",
|
||||
"textDecoration": "none",
|
||||
}
|
||||
}>
|
||||
<div
|
||||
className=" __style_df"
|
||||
data-testid="1"
|
||||
style={
|
||||
Object {
|
||||
"MozBoxSizing": "border-box",
|
||||
"WebkitAlignItems": "stretch",
|
||||
"WebkitBoxAlign": "stretch",
|
||||
"WebkitBoxDirection": "normal",
|
||||
"WebkitBoxOrient": "vertical",
|
||||
"WebkitFlexBasis": "auto",
|
||||
"WebkitFlexDirection": "column",
|
||||
"WebkitFlexShrink": 0,
|
||||
"alignItems": "stretch",
|
||||
"backgroundColor": "transparent",
|
||||
"borderBottomStyle": "solid",
|
||||
"borderBottomWidth": "0px",
|
||||
"borderLeftStyle": "solid",
|
||||
"borderLeftWidth": "0px",
|
||||
"borderRightStyle": "solid",
|
||||
"borderRightWidth": "0px",
|
||||
"borderTopStyle": "solid",
|
||||
"borderTopWidth": "0px",
|
||||
"boxSizing": "border-box",
|
||||
"color": "inherit",
|
||||
"display": null,
|
||||
"flexBasis": "auto",
|
||||
"flexDirection": "column",
|
||||
"flexShrink": 0,
|
||||
"font": "inherit",
|
||||
"listStyle": "none",
|
||||
"marginBottom": "0px",
|
||||
"marginLeft": "0px",
|
||||
"marginRight": "0px",
|
||||
"marginTop": "0px",
|
||||
"minHeight": "0px",
|
||||
"minWidth": "0px",
|
||||
"msFlexAlign": "stretch",
|
||||
"msFlexDirection": "column",
|
||||
"msFlexNegative": 0,
|
||||
"msPreferredSize": "auto",
|
||||
"paddingBottom": "0px",
|
||||
"paddingLeft": "0px",
|
||||
"paddingRight": "0px",
|
||||
"paddingTop": "0px",
|
||||
"position": "relative",
|
||||
"textAlign": "inherit",
|
||||
"textDecoration": "none",
|
||||
}
|
||||
} />
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`components/View prop "pointerEvents" 1`] = `
|
||||
<div
|
||||
className=" __style_df __style_pebo"
|
||||
style={
|
||||
Object {
|
||||
"MozBoxSizing": "border-box",
|
||||
"WebkitAlignItems": "stretch",
|
||||
"WebkitBoxAlign": "stretch",
|
||||
"WebkitBoxDirection": "normal",
|
||||
"WebkitBoxOrient": "vertical",
|
||||
"WebkitFlexBasis": "auto",
|
||||
"WebkitFlexDirection": "column",
|
||||
"WebkitFlexShrink": 0,
|
||||
"alignItems": "stretch",
|
||||
"backgroundColor": "transparent",
|
||||
"borderBottomStyle": "solid",
|
||||
"borderBottomWidth": "0px",
|
||||
"borderLeftStyle": "solid",
|
||||
"borderLeftWidth": "0px",
|
||||
"borderRightStyle": "solid",
|
||||
"borderRightWidth": "0px",
|
||||
"borderTopStyle": "solid",
|
||||
"borderTopWidth": "0px",
|
||||
"boxSizing": "border-box",
|
||||
"color": "inherit",
|
||||
"display": null,
|
||||
"flexBasis": "auto",
|
||||
"flexDirection": "column",
|
||||
"flexShrink": 0,
|
||||
"font": "inherit",
|
||||
"listStyle": "none",
|
||||
"marginBottom": "0px",
|
||||
"marginLeft": "0px",
|
||||
"marginRight": "0px",
|
||||
"marginTop": "0px",
|
||||
"minHeight": "0px",
|
||||
"minWidth": "0px",
|
||||
"msFlexAlign": "stretch",
|
||||
"msFlexDirection": "column",
|
||||
"msFlexNegative": 0,
|
||||
"msPreferredSize": "auto",
|
||||
"paddingBottom": "0px",
|
||||
"paddingLeft": "0px",
|
||||
"paddingRight": "0px",
|
||||
"paddingTop": "0px",
|
||||
"pointerEvents": null,
|
||||
"position": "relative",
|
||||
"textAlign": "inherit",
|
||||
"textDecoration": "none",
|
||||
}
|
||||
} />
|
||||
`;
|
||||
|
||||
exports[`components/View prop "style" 1`] = `
|
||||
<div
|
||||
className=" __style_df"
|
||||
style={
|
||||
Object {
|
||||
"MozBoxSizing": "border-box",
|
||||
"WebkitAlignItems": "stretch",
|
||||
"WebkitBoxAlign": "stretch",
|
||||
"WebkitBoxDirection": "normal",
|
||||
"WebkitBoxOrient": "vertical",
|
||||
"WebkitFlexBasis": "auto",
|
||||
"WebkitFlexDirection": "column",
|
||||
"WebkitFlexShrink": 0,
|
||||
"alignItems": "stretch",
|
||||
"backgroundColor": "transparent",
|
||||
"borderBottomStyle": "solid",
|
||||
"borderBottomWidth": "0px",
|
||||
"borderLeftStyle": "solid",
|
||||
"borderLeftWidth": "0px",
|
||||
"borderRightStyle": "solid",
|
||||
"borderRightWidth": "0px",
|
||||
"borderTopStyle": "solid",
|
||||
"borderTopWidth": "0px",
|
||||
"boxSizing": "border-box",
|
||||
"color": "inherit",
|
||||
"display": null,
|
||||
"flexBasis": "auto",
|
||||
"flexDirection": "column",
|
||||
"flexShrink": 0,
|
||||
"font": "inherit",
|
||||
"listStyle": "none",
|
||||
"marginBottom": "0px",
|
||||
"marginLeft": "0px",
|
||||
"marginRight": "0px",
|
||||
"marginTop": "0px",
|
||||
"minHeight": "0px",
|
||||
"minWidth": "0px",
|
||||
"msFlexAlign": "stretch",
|
||||
"msFlexDirection": "column",
|
||||
"msFlexNegative": 0,
|
||||
"msPreferredSize": "auto",
|
||||
"paddingBottom": "0px",
|
||||
"paddingLeft": "0px",
|
||||
"paddingRight": "0px",
|
||||
"paddingTop": "0px",
|
||||
"position": "relative",
|
||||
"textAlign": "inherit",
|
||||
"textDecoration": "none",
|
||||
}
|
||||
} />
|
||||
`;
|
||||
|
||||
exports[`components/View prop "style" 2`] = `
|
||||
<div
|
||||
className=" __style_df"
|
||||
style={
|
||||
Object {
|
||||
"MozBoxSizing": "border-box",
|
||||
"WebkitAlignItems": "stretch",
|
||||
"WebkitBoxAlign": "stretch",
|
||||
"WebkitBoxDirection": "normal",
|
||||
"WebkitBoxOrient": "vertical",
|
||||
"WebkitFlexBasis": "auto",
|
||||
"WebkitFlexDirection": "column",
|
||||
"WebkitFlexGrow": 1,
|
||||
"WebkitFlexShrink": 1,
|
||||
"alignItems": "stretch",
|
||||
"backgroundColor": "transparent",
|
||||
"borderBottomStyle": "solid",
|
||||
"borderBottomWidth": "0px",
|
||||
"borderLeftStyle": "solid",
|
||||
"borderLeftWidth": "0px",
|
||||
"borderRightStyle": "solid",
|
||||
"borderRightWidth": "0px",
|
||||
"borderTopStyle": "solid",
|
||||
"borderTopWidth": "0px",
|
||||
"boxSizing": "border-box",
|
||||
"color": "inherit",
|
||||
"display": null,
|
||||
"flexBasis": "auto",
|
||||
"flexDirection": "column",
|
||||
"flexGrow": 1,
|
||||
"flexShrink": 1,
|
||||
"font": "inherit",
|
||||
"listStyle": "none",
|
||||
"marginBottom": "0px",
|
||||
"marginLeft": "0px",
|
||||
"marginRight": "0px",
|
||||
"marginTop": "0px",
|
||||
"minHeight": "0px",
|
||||
"minWidth": "0px",
|
||||
"msFlexAlign": "stretch",
|
||||
"msFlexDirection": "column",
|
||||
"msFlexNegative": 1,
|
||||
"msFlexPositive": 1,
|
||||
"msPreferredSize": "auto",
|
||||
"paddingBottom": "0px",
|
||||
"paddingLeft": "0px",
|
||||
"paddingRight": "0px",
|
||||
"paddingTop": "0px",
|
||||
"position": "relative",
|
||||
"textAlign": "inherit",
|
||||
"textDecoration": "none",
|
||||
}
|
||||
} />
|
||||
`;
|
||||
|
||||
exports[`components/View prop "style" 3`] = `
|
||||
<div
|
||||
className=" __style_df"
|
||||
style={
|
||||
Object {
|
||||
"MozBoxSizing": "border-box",
|
||||
"WebkitAlignItems": "stretch",
|
||||
"WebkitBoxAlign": "stretch",
|
||||
"WebkitBoxDirection": "normal",
|
||||
"WebkitBoxOrient": "vertical",
|
||||
"WebkitFlexBasis": "auto",
|
||||
"WebkitFlexDirection": "column",
|
||||
"WebkitFlexShrink": 1,
|
||||
"alignItems": "stretch",
|
||||
"backgroundColor": "transparent",
|
||||
"borderBottomStyle": "solid",
|
||||
"borderBottomWidth": "0px",
|
||||
"borderLeftStyle": "solid",
|
||||
"borderLeftWidth": "0px",
|
||||
"borderRightStyle": "solid",
|
||||
"borderRightWidth": "0px",
|
||||
"borderTopStyle": "solid",
|
||||
"borderTopWidth": "0px",
|
||||
"boxSizing": "border-box",
|
||||
"color": "inherit",
|
||||
"display": null,
|
||||
"flexBasis": "auto",
|
||||
"flexDirection": "column",
|
||||
"flexShrink": 1,
|
||||
"font": "inherit",
|
||||
"listStyle": "none",
|
||||
"marginBottom": "0px",
|
||||
"marginLeft": "0px",
|
||||
"marginRight": "0px",
|
||||
"marginTop": "0px",
|
||||
"minHeight": "0px",
|
||||
"minWidth": "0px",
|
||||
"msFlexAlign": "stretch",
|
||||
"msFlexDirection": "column",
|
||||
"msFlexNegative": 1,
|
||||
"msPreferredSize": "auto",
|
||||
"paddingBottom": "0px",
|
||||
"paddingLeft": "0px",
|
||||
"paddingRight": "0px",
|
||||
"paddingTop": "0px",
|
||||
"position": "relative",
|
||||
"textAlign": "inherit",
|
||||
"textDecoration": "none",
|
||||
}
|
||||
} />
|
||||
`;
|
||||
|
||||
exports[`components/View prop "style" 4`] = `
|
||||
<div
|
||||
className=" __style_df"
|
||||
style={
|
||||
Object {
|
||||
"MozBoxSizing": "border-box",
|
||||
"WebkitAlignItems": "stretch",
|
||||
"WebkitBoxAlign": "stretch",
|
||||
"WebkitBoxDirection": "normal",
|
||||
"WebkitBoxOrient": "vertical",
|
||||
"WebkitFlexBasis": "auto",
|
||||
"WebkitFlexDirection": "column",
|
||||
"WebkitFlexGrow": 1,
|
||||
"WebkitFlexShrink": 2,
|
||||
"alignItems": "stretch",
|
||||
"backgroundColor": "transparent",
|
||||
"borderBottomStyle": "solid",
|
||||
"borderBottomWidth": "0px",
|
||||
"borderLeftStyle": "solid",
|
||||
"borderLeftWidth": "0px",
|
||||
"borderRightStyle": "solid",
|
||||
"borderRightWidth": "0px",
|
||||
"borderTopStyle": "solid",
|
||||
"borderTopWidth": "0px",
|
||||
"boxSizing": "border-box",
|
||||
"color": "inherit",
|
||||
"display": null,
|
||||
"flexBasis": "auto",
|
||||
"flexDirection": "column",
|
||||
"flexGrow": 1,
|
||||
"flexShrink": 2,
|
||||
"font": "inherit",
|
||||
"listStyle": "none",
|
||||
"marginBottom": "0px",
|
||||
"marginLeft": "0px",
|
||||
"marginRight": "0px",
|
||||
"marginTop": "0px",
|
||||
"minHeight": "0px",
|
||||
"minWidth": "0px",
|
||||
"msFlexAlign": "stretch",
|
||||
"msFlexDirection": "column",
|
||||
"msFlexNegative": 2,
|
||||
"msFlexPositive": 1,
|
||||
"msPreferredSize": "auto",
|
||||
"paddingBottom": "0px",
|
||||
"paddingLeft": "0px",
|
||||
"paddingRight": "0px",
|
||||
"paddingTop": "0px",
|
||||
"position": "relative",
|
||||
"textAlign": "inherit",
|
||||
"textDecoration": "none",
|
||||
}
|
||||
} />
|
||||
`;
|
||||
|
||||
exports[`components/View rendered element is a "div" by default 1`] = `
|
||||
<div
|
||||
className=" __style_df"
|
||||
style={
|
||||
Object {
|
||||
"MozBoxSizing": "border-box",
|
||||
"WebkitAlignItems": "stretch",
|
||||
"WebkitBoxAlign": "stretch",
|
||||
"WebkitBoxDirection": "normal",
|
||||
"WebkitBoxOrient": "vertical",
|
||||
"WebkitFlexBasis": "auto",
|
||||
"WebkitFlexDirection": "column",
|
||||
"WebkitFlexShrink": 0,
|
||||
"alignItems": "stretch",
|
||||
"backgroundColor": "transparent",
|
||||
"borderBottomStyle": "solid",
|
||||
"borderBottomWidth": "0px",
|
||||
"borderLeftStyle": "solid",
|
||||
"borderLeftWidth": "0px",
|
||||
"borderRightStyle": "solid",
|
||||
"borderRightWidth": "0px",
|
||||
"borderTopStyle": "solid",
|
||||
"borderTopWidth": "0px",
|
||||
"boxSizing": "border-box",
|
||||
"color": "inherit",
|
||||
"display": null,
|
||||
"flexBasis": "auto",
|
||||
"flexDirection": "column",
|
||||
"flexShrink": 0,
|
||||
"font": "inherit",
|
||||
"listStyle": "none",
|
||||
"marginBottom": "0px",
|
||||
"marginLeft": "0px",
|
||||
"marginRight": "0px",
|
||||
"marginTop": "0px",
|
||||
"minHeight": "0px",
|
||||
"minWidth": "0px",
|
||||
"msFlexAlign": "stretch",
|
||||
"msFlexDirection": "column",
|
||||
"msFlexNegative": 0,
|
||||
"msPreferredSize": "auto",
|
||||
"paddingBottom": "0px",
|
||||
"paddingLeft": "0px",
|
||||
"paddingRight": "0px",
|
||||
"paddingTop": "0px",
|
||||
"position": "relative",
|
||||
"textAlign": "inherit",
|
||||
"textDecoration": "none",
|
||||
}
|
||||
} />
|
||||
`;
|
||||
|
||||
exports[`components/View rendered element is a "span" when inside <View accessibilityRole="button" /> 1`] = `
|
||||
<button
|
||||
className=" __style_df"
|
||||
role="button"
|
||||
style={
|
||||
Object {
|
||||
"MozBoxSizing": "border-box",
|
||||
"WebkitAlignItems": "stretch",
|
||||
"WebkitBoxAlign": "stretch",
|
||||
"WebkitBoxDirection": "normal",
|
||||
"WebkitBoxOrient": "vertical",
|
||||
"WebkitFlexBasis": "auto",
|
||||
"WebkitFlexDirection": "column",
|
||||
"WebkitFlexShrink": 0,
|
||||
"alignItems": "stretch",
|
||||
"backgroundColor": "transparent",
|
||||
"borderBottomStyle": "solid",
|
||||
"borderBottomWidth": "0px",
|
||||
"borderLeftStyle": "solid",
|
||||
"borderLeftWidth": "0px",
|
||||
"borderRightStyle": "solid",
|
||||
"borderRightWidth": "0px",
|
||||
"borderTopStyle": "solid",
|
||||
"borderTopWidth": "0px",
|
||||
"boxSizing": "border-box",
|
||||
"color": "inherit",
|
||||
"display": null,
|
||||
"flexBasis": "auto",
|
||||
"flexDirection": "column",
|
||||
"flexShrink": 0,
|
||||
"font": "inherit",
|
||||
"listStyle": "none",
|
||||
"marginBottom": "0px",
|
||||
"marginLeft": "0px",
|
||||
"marginRight": "0px",
|
||||
"marginTop": "0px",
|
||||
"minHeight": "0px",
|
||||
"minWidth": "0px",
|
||||
"msFlexAlign": "stretch",
|
||||
"msFlexDirection": "column",
|
||||
"msFlexNegative": 0,
|
||||
"msPreferredSize": "auto",
|
||||
"paddingBottom": "0px",
|
||||
"paddingLeft": "0px",
|
||||
"paddingRight": "0px",
|
||||
"paddingTop": "0px",
|
||||
"position": "relative",
|
||||
"textAlign": "inherit",
|
||||
"textDecoration": "none",
|
||||
}
|
||||
}
|
||||
type="button">
|
||||
<span
|
||||
className=" __style_df"
|
||||
style={
|
||||
Object {
|
||||
"MozBoxSizing": "border-box",
|
||||
"WebkitAlignItems": "stretch",
|
||||
"WebkitBoxAlign": "stretch",
|
||||
"WebkitBoxDirection": "normal",
|
||||
"WebkitBoxOrient": "vertical",
|
||||
"WebkitFlexBasis": "auto",
|
||||
"WebkitFlexDirection": "column",
|
||||
"WebkitFlexShrink": 0,
|
||||
"alignItems": "stretch",
|
||||
"backgroundColor": "transparent",
|
||||
"borderBottomStyle": "solid",
|
||||
"borderBottomWidth": "0px",
|
||||
"borderLeftStyle": "solid",
|
||||
"borderLeftWidth": "0px",
|
||||
"borderRightStyle": "solid",
|
||||
"borderRightWidth": "0px",
|
||||
"borderTopStyle": "solid",
|
||||
"borderTopWidth": "0px",
|
||||
"boxSizing": "border-box",
|
||||
"color": "inherit",
|
||||
"display": null,
|
||||
"flexBasis": "auto",
|
||||
"flexDirection": "column",
|
||||
"flexShrink": 0,
|
||||
"font": "inherit",
|
||||
"listStyle": "none",
|
||||
"marginBottom": "0px",
|
||||
"marginLeft": "0px",
|
||||
"marginRight": "0px",
|
||||
"marginTop": "0px",
|
||||
"minHeight": "0px",
|
||||
"minWidth": "0px",
|
||||
"msFlexAlign": "stretch",
|
||||
"msFlexDirection": "column",
|
||||
"msFlexNegative": 0,
|
||||
"msPreferredSize": "auto",
|
||||
"paddingBottom": "0px",
|
||||
"paddingLeft": "0px",
|
||||
"paddingRight": "0px",
|
||||
"paddingTop": "0px",
|
||||
"position": "relative",
|
||||
"textAlign": "inherit",
|
||||
"textDecoration": "none",
|
||||
}
|
||||
} />
|
||||
</button>
|
||||
`;
|
||||
@@ -1,55 +1,46 @@
|
||||
/* eslint-env mocha */
|
||||
/* eslint-env jasmine, jest */
|
||||
|
||||
import assert from 'assert';
|
||||
import includes from 'lodash/includes';
|
||||
import React from 'react';
|
||||
import renderer from 'react-test-renderer';
|
||||
import View from '../';
|
||||
import { mount, shallow } from 'enzyme';
|
||||
|
||||
suite('components/View', () => {
|
||||
suite('rendered element', () => {
|
||||
jest.mock('react-dom');
|
||||
|
||||
describe('components/View', () => {
|
||||
describe('rendered element', () => {
|
||||
test('is a "div" by default', () => {
|
||||
const view = shallow(<View />);
|
||||
assert.equal(view.is('div'), true);
|
||||
const component = renderer.create(<View />);
|
||||
expect(component.toJSON()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('is a "span" when inside <View accessibilityRole="button" />', () => {
|
||||
const view = mount(<View accessibilityRole='button'><View /></View>);
|
||||
assert.equal(view.find('span').length, 1);
|
||||
const component = renderer.create(<View accessibilityRole='button'><View /></View>);
|
||||
expect(component.toJSON()).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
test('prop "children"', () => {
|
||||
const children = <View testID='1' />;
|
||||
const view = shallow(<View>{children}</View>);
|
||||
assert.equal(view.prop('children'), children);
|
||||
});
|
||||
|
||||
test('prop "onLayout"', (done) => {
|
||||
mount(<View onLayout={onLayout} />);
|
||||
function onLayout(e) {
|
||||
const { layout } = e.nativeEvent;
|
||||
assert.deepEqual(layout, { x: 0, y: 0, width: 0, height: 0 });
|
||||
done();
|
||||
}
|
||||
const component = renderer.create(<View>{children}</View>);
|
||||
expect(component.toJSON()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('prop "pointerEvents"', () => {
|
||||
const view = shallow(<View pointerEvents='box-only' />);
|
||||
assert.ok(includes(view.prop('className'), '__style_pebo') === true);
|
||||
const component = renderer.create(<View pointerEvents='box-only' />);
|
||||
expect(component.toJSON()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('prop "style"', () => {
|
||||
const view = shallow(<View />);
|
||||
assert.equal(view.prop('style').flexShrink, 0);
|
||||
let component = renderer.create(<View />);
|
||||
expect(component.toJSON()).toMatchSnapshot();
|
||||
|
||||
const flexView = shallow(<View style={{ flex: 1 }} />);
|
||||
assert.equal(flexView.prop('style').flexShrink, 1);
|
||||
component = renderer.create(<View style={{ flex: 1 }} />);
|
||||
expect(component.toJSON()).toMatchSnapshot();
|
||||
|
||||
const flexShrinkView = shallow(<View style={{ flexShrink: 1 }} />);
|
||||
assert.equal(flexShrinkView.prop('style').flexShrink, 1);
|
||||
component = renderer.create(<View style={{ flexShrink: 1 }} />);
|
||||
expect(component.toJSON()).toMatchSnapshot();
|
||||
|
||||
const flexAndShrinkView = shallow(<View style={{ flex: 1, flexShrink: 2 }} />);
|
||||
assert.equal(flexAndShrinkView.prop('style').flexShrink, 2);
|
||||
component = renderer.create(<View style={{ flex: 1, flexShrink: 2 }} />);
|
||||
expect(component.toJSON()).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import '../../modules/injectResponderEventPlugin';
|
||||
|
||||
import applyLayout from '../../modules/applyLayout';
|
||||
import applyNativeMethods from '../../modules/applyNativeMethods';
|
||||
import BaseComponentPropTypes from '../../propTypes/BaseComponentPropTypes';
|
||||
@@ -66,8 +68,7 @@ class View extends Component {
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
accessible: true,
|
||||
style: {}
|
||||
accessible: true
|
||||
};
|
||||
|
||||
static childContextTypes = {
|
||||
@@ -86,18 +87,25 @@ class View extends Component {
|
||||
|
||||
render() {
|
||||
const {
|
||||
collapsable, // eslint-disable-line
|
||||
hitSlop, // eslint-disable-line
|
||||
onLayout, // eslint-disable-line
|
||||
pointerEvents,
|
||||
style,
|
||||
/* eslint-disable */
|
||||
accessibilityComponentType,
|
||||
accessibilityTraits,
|
||||
collapsable,
|
||||
hitSlop,
|
||||
onAccessibilityTap,
|
||||
onLayout,
|
||||
onMagicTap,
|
||||
removeClippedSubviews,
|
||||
/* eslint-enable */
|
||||
...other
|
||||
} = this.props;
|
||||
|
||||
const flattenedStyle = StyleSheet.flatten(style);
|
||||
const pointerEventsStyle = pointerEvents && { pointerEvents };
|
||||
// 'View' needs to set 'flexShrink:0' only when there is no 'flex' or 'flexShrink' style provided
|
||||
const needsFlexReset = flattenedStyle.flex == null && flattenedStyle.flexShrink == null;
|
||||
const needsFlexReset = !flattenedStyle || (flattenedStyle.flex == null && flattenedStyle.flexShrink == null);
|
||||
|
||||
const normalizedEventHandlers = eventHandlerNames.reduce((handlerProps, handlerName) => {
|
||||
const handler = this.props[handlerName];
|
||||
@@ -124,10 +132,10 @@ class View extends Component {
|
||||
|
||||
_normalizeEventForHandler(handler, handlerName) {
|
||||
// Browsers fire mouse events after touch events. This causes the
|
||||
// ResponderEvents and their handlers to fire twice for Touchables.
|
||||
// 'onResponderRelease' handler to be called twice for Touchables.
|
||||
// Auto-fix this issue by calling 'preventDefault' to cancel the mouse
|
||||
// events.
|
||||
const shouldCancelEvent = handlerName.indexOf('onResponder') === 0;
|
||||
const shouldCancelEvent = handlerName === 'onResponderRelease';
|
||||
|
||||
return (e) => {
|
||||
e.nativeEvent = normalizeNativeEvent(e.nativeEvent);
|
||||
|
||||
18
src/core.js
18
src/core.js
@@ -1,8 +1,8 @@
|
||||
import './modules/injectResponderEventPlugin';
|
||||
|
||||
import findNodeHandle from './modules/findNodeHandle';
|
||||
import ReactDOM from 'react-dom';
|
||||
import ReactDOMServer from 'react-dom/server';
|
||||
import ReactDefaultInjection from 'react/lib/ReactDefaultInjection';
|
||||
import { render, unmountComponentAtNode } from 'react/lib/ReactMount';
|
||||
|
||||
ReactDefaultInjection.inject();
|
||||
|
||||
// APIs
|
||||
import I18nManager from './apis/I18nManager';
|
||||
@@ -14,12 +14,14 @@ import Text from './components/Text';
|
||||
import TextInput from './components/TextInput';
|
||||
import View from './components/View';
|
||||
|
||||
// modules
|
||||
import createDOMElement from './modules/createDOMElement';
|
||||
|
||||
const ReactNativeCore = {
|
||||
createDOMElement,
|
||||
findNodeHandle,
|
||||
render: ReactDOM.render,
|
||||
renderToStaticMarkup: ReactDOMServer.renderToStaticMarkup,
|
||||
renderToString: ReactDOMServer.renderToString,
|
||||
unmountComponentAtNode: ReactDOM.unmountComponentAtNode,
|
||||
render,
|
||||
unmountComponentAtNode,
|
||||
// APIs
|
||||
I18nManager,
|
||||
StyleSheet,
|
||||
|
||||
26
src/index.js
26
src/index.js
@@ -1,18 +1,20 @@
|
||||
import './modules/injectResponderEventPlugin';
|
||||
|
||||
import findNodeHandle from './modules/findNodeHandle';
|
||||
import ReactDOM from 'react-dom';
|
||||
import ReactDOMServer from 'react-dom/server';
|
||||
import ReactDefaultInjection from 'react/lib/ReactDefaultInjection';
|
||||
import { render, unmountComponentAtNode } from 'react/lib/ReactMount';
|
||||
|
||||
ReactDefaultInjection.inject();
|
||||
|
||||
// APIs
|
||||
import Animated from './apis/Animated';
|
||||
import AppRegistry from './apis/AppRegistry';
|
||||
import AppState from './apis/AppState';
|
||||
import AsyncStorage from './apis/AsyncStorage';
|
||||
import Clipboard from './apis/Clipboard';
|
||||
import Dimensions from './apis/Dimensions';
|
||||
import Easing from 'animated/lib/Easing';
|
||||
import I18nManager from './apis/I18nManager';
|
||||
import InteractionManager from './apis/InteractionManager';
|
||||
import Linking from './apis/Linking';
|
||||
import NetInfo from './apis/NetInfo';
|
||||
import PanResponder from './apis/PanResponder';
|
||||
import PixelRatio from './apis/PixelRatio';
|
||||
@@ -23,6 +25,7 @@ import Vibration from './apis/Vibration';
|
||||
|
||||
// components
|
||||
import ActivityIndicator from './components/ActivityIndicator';
|
||||
import Button from './components/Button';
|
||||
import Image from './components/Image';
|
||||
import ListView from './components/ListView';
|
||||
import ProgressBar from './components/ProgressBar';
|
||||
@@ -31,17 +34,16 @@ import Switch from './components/Switch';
|
||||
import Text from './components/Text';
|
||||
import TextInput from './components/TextInput';
|
||||
import Touchable from './components/Touchable/Touchable';
|
||||
import TouchableBounce from './components/Touchable/TouchableBounce';
|
||||
import TouchableHighlight from './components/Touchable/TouchableHighlight';
|
||||
import TouchableOpacity from './components/Touchable/TouchableOpacity';
|
||||
import TouchableWithoutFeedback from './components/Touchable/TouchableWithoutFeedback';
|
||||
import View from './components/View';
|
||||
|
||||
// modules
|
||||
import createDOMElement from './modules/createDOMElement';
|
||||
import NativeModules from './modules/NativeModules';
|
||||
|
||||
// propTypes
|
||||
|
||||
import ColorPropType from './propTypes/ColorPropType';
|
||||
import EdgeInsetsPropType from './propTypes/EdgeInsetsPropType';
|
||||
import PointPropType from './propTypes/PointPropType';
|
||||
@@ -49,21 +51,20 @@ import PointPropType from './propTypes/PointPropType';
|
||||
const ReactNative = {
|
||||
// top-level API
|
||||
findNodeHandle,
|
||||
render: ReactDOM.render,
|
||||
unmountComponentAtNode: ReactDOM.unmountComponentAtNode,
|
||||
// web-only
|
||||
renderToStaticMarkup: ReactDOMServer.renderToStaticMarkup,
|
||||
renderToString: ReactDOMServer.renderToString,
|
||||
render,
|
||||
unmountComponentAtNode,
|
||||
|
||||
// APIs
|
||||
Animated,
|
||||
AppRegistry,
|
||||
AppState,
|
||||
AsyncStorage,
|
||||
Clipboard,
|
||||
Dimensions,
|
||||
Easing,
|
||||
I18nManager,
|
||||
InteractionManager,
|
||||
Linking,
|
||||
NetInfo,
|
||||
PanResponder,
|
||||
PixelRatio,
|
||||
@@ -74,6 +75,7 @@ const ReactNative = {
|
||||
|
||||
// components
|
||||
ActivityIndicator,
|
||||
Button,
|
||||
Image,
|
||||
ListView,
|
||||
ProgressBar,
|
||||
@@ -82,13 +84,13 @@ const ReactNative = {
|
||||
Text,
|
||||
TextInput,
|
||||
Touchable,
|
||||
TouchableBounce,
|
||||
TouchableHighlight,
|
||||
TouchableOpacity,
|
||||
TouchableWithoutFeedback,
|
||||
View,
|
||||
|
||||
// modules
|
||||
createDOMElement,
|
||||
NativeModules,
|
||||
|
||||
// propTypes
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
import { Component } from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import findNodeHandle from '../findNodeHandle';
|
||||
import UIManager from '../../apis/UIManager';
|
||||
|
||||
type MeasureInWindowOnSuccessCallback = (
|
||||
@@ -38,7 +38,7 @@ const NativeMethodsMixin = {
|
||||
* Removes focus from an input or view. This is the opposite of `focus()`.
|
||||
*/
|
||||
blur() {
|
||||
UIManager.blur(ReactDOM.findDOMNode(this));
|
||||
UIManager.blur(findNodeHandle(this));
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -46,7 +46,7 @@ const NativeMethodsMixin = {
|
||||
* The exact behavior triggered will depend the type of view.
|
||||
*/
|
||||
focus() {
|
||||
UIManager.focus(ReactDOM.findDOMNode(this));
|
||||
UIManager.focus(findNodeHandle(this));
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -54,7 +54,7 @@ const NativeMethodsMixin = {
|
||||
*/
|
||||
measure(callback: MeasureOnSuccessCallback) {
|
||||
UIManager.measure(
|
||||
ReactDOM.findDOMNode(this),
|
||||
findNodeHandle(this),
|
||||
mountSafeCallback(this, callback)
|
||||
);
|
||||
},
|
||||
@@ -76,7 +76,7 @@ const NativeMethodsMixin = {
|
||||
*/
|
||||
measureInWindow(callback: MeasureInWindowOnSuccessCallback) {
|
||||
UIManager.measureInWindow(
|
||||
ReactDOM.findDOMNode(this),
|
||||
findNodeHandle(this),
|
||||
mountSafeCallback(this, callback)
|
||||
);
|
||||
},
|
||||
@@ -90,7 +90,7 @@ const NativeMethodsMixin = {
|
||||
onFail: () => void /* currently unused */
|
||||
) {
|
||||
UIManager.measureLayout(
|
||||
ReactDOM.findDOMNode(this),
|
||||
findNodeHandle(this),
|
||||
relativeToNativeNode,
|
||||
mountSafeCallback(this, onFail),
|
||||
mountSafeCallback(this, onSuccess)
|
||||
@@ -102,7 +102,7 @@ const NativeMethodsMixin = {
|
||||
*/
|
||||
setNativeProps(nativeProps: Object) {
|
||||
UIManager.updateView(
|
||||
ReactDOM.findDOMNode(this),
|
||||
findNodeHandle(this),
|
||||
nativeProps,
|
||||
this
|
||||
);
|
||||
|
||||
@@ -13,9 +13,9 @@
|
||||
'use strict';
|
||||
|
||||
var Dimensions = require('../../apis/Dimensions');
|
||||
var findNodeHandle = require('../findNodeHandle');
|
||||
var Platform = require('../../apis/Platform');
|
||||
var React = require('react');
|
||||
var ReactDOM = require('react-dom');
|
||||
// var Subscribable = require('../Subscribable');
|
||||
var TextInputState = require('../../components/TextInput/TextInputState');
|
||||
var UIManager = require('../../apis/UIManager');
|
||||
@@ -356,7 +356,7 @@ var ScrollResponderMixin = {
|
||||
scrollResponderGetScrollableNode: function(): any {
|
||||
return this.getScrollableNode ?
|
||||
this.getScrollableNode() :
|
||||
ReactDOM.findDOMNode(this);
|
||||
findNodeHandle(this);
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -423,7 +423,7 @@ var ScrollResponderMixin = {
|
||||
this.preventNegativeScrollOffset = !!preventNegativeScrollOffset;
|
||||
UIManager.measureLayout(
|
||||
nodeHandle,
|
||||
ReactDOM.findDOMNode(this.getInnerViewNode()),
|
||||
findNodeHandle(this.getInnerViewNode()),
|
||||
this.scrollResponderTextInputFocusError,
|
||||
this.scrollResponderInputMeasureAndScrollToKeyboard
|
||||
);
|
||||
|
||||
@@ -12,13 +12,13 @@ const applyLayout = (Component) => {
|
||||
const componentDidUpdate = Component.prototype.componentDidUpdate || emptyFunction;
|
||||
|
||||
Component.prototype.componentDidMount = function () {
|
||||
componentDidMount();
|
||||
componentDidMount.call(this);
|
||||
this._layoutState = {};
|
||||
this._handleLayout();
|
||||
};
|
||||
|
||||
Component.prototype.componentDidUpdate = function () {
|
||||
componentDidUpdate();
|
||||
componentDidUpdate.call(this);
|
||||
this._handleLayout();
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
exports[`modules/createDOMElement prop "accessibilityLabel" 1`] = `
|
||||
<span
|
||||
aria-label="accessibilityLabel"
|
||||
className=""
|
||||
style={Object {}} />
|
||||
`;
|
||||
|
||||
exports[`modules/createDOMElement prop "accessibilityLiveRegion" 1`] = `
|
||||
<span
|
||||
aria-live="polite"
|
||||
className=""
|
||||
style={Object {}} />
|
||||
`;
|
||||
|
||||
exports[`modules/createDOMElement prop "accessibilityRole" button 1`] = `
|
||||
<button
|
||||
className=""
|
||||
role="button"
|
||||
style={Object {}}
|
||||
type="button" />
|
||||
`;
|
||||
|
||||
exports[`modules/createDOMElement prop "accessibilityRole" link and target="_blank" 1`] = `
|
||||
<a
|
||||
className=""
|
||||
rel=" noopener noreferrer"
|
||||
role="link"
|
||||
style={Object {}}
|
||||
target="_blank" />
|
||||
`;
|
||||
|
||||
exports[`modules/createDOMElement prop "accessibilityRole" roles 1`] = `
|
||||
<header
|
||||
className=""
|
||||
role="banner"
|
||||
style={Object {}} />
|
||||
`;
|
||||
|
||||
exports[`modules/createDOMElement prop "accessible" 1`] = `
|
||||
<span
|
||||
className=""
|
||||
style={Object {}} />
|
||||
`;
|
||||
|
||||
exports[`modules/createDOMElement prop "accessible" 2`] = `
|
||||
<span
|
||||
className=""
|
||||
style={Object {}} />
|
||||
`;
|
||||
|
||||
exports[`modules/createDOMElement prop "accessible" 3`] = `
|
||||
<span
|
||||
aria-hidden={true}
|
||||
className=""
|
||||
style={Object {}} />
|
||||
`;
|
||||
|
||||
exports[`modules/createDOMElement prop "testID" 1`] = `
|
||||
<span
|
||||
className=""
|
||||
data-testid="Example.testID"
|
||||
style={Object {}} />
|
||||
`;
|
||||
|
||||
exports[`modules/createDOMElement renders correct DOM element 1`] = `
|
||||
<span
|
||||
className=""
|
||||
style={Object {}} />
|
||||
`;
|
||||
|
||||
exports[`modules/createDOMElement renders correct DOM element 2`] = `
|
||||
<main
|
||||
className=""
|
||||
style={Object {}} />
|
||||
`;
|
||||
@@ -1,60 +1,59 @@
|
||||
/* eslint-env mocha */
|
||||
/* eslint-env jasmine, jest */
|
||||
|
||||
import assert from 'assert';
|
||||
import createDOMElement from '..';
|
||||
import { shallow } from 'enzyme';
|
||||
import renderer from 'react-test-renderer';
|
||||
|
||||
suite('modules/createDOMElement', () => {
|
||||
describe('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);
|
||||
let component = renderer.create(createDOMElement('span'));
|
||||
expect(component.toJSON()).toMatchSnapshot();
|
||||
component = renderer.create(createDOMElement('main'));
|
||||
expect(component.toJSON()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('prop "accessibilityLabel"', () => {
|
||||
const accessibilityLabel = 'accessibilityLabel';
|
||||
const element = shallow(createDOMElement('span', { accessibilityLabel }));
|
||||
assert.equal(element.prop('aria-label'), accessibilityLabel);
|
||||
const component = renderer.create(createDOMElement('span', { accessibilityLabel }));
|
||||
expect(component.toJSON()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('prop "accessibilityLiveRegion"', () => {
|
||||
const accessibilityLiveRegion = 'polite';
|
||||
const element = shallow(createDOMElement('span', { accessibilityLiveRegion }));
|
||||
assert.equal(element.prop('aria-live'), accessibilityLiveRegion);
|
||||
const component = renderer.create(createDOMElement('span', { accessibilityLiveRegion }));
|
||||
expect(component.toJSON()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('prop "accessibilityRole"', () => {
|
||||
const accessibilityRole = 'banner';
|
||||
let element = shallow(createDOMElement('span', { accessibilityRole }));
|
||||
assert.equal(element.prop('role'), accessibilityRole);
|
||||
assert.equal(element.is('header'), true);
|
||||
describe('prop "accessibilityRole"', () => {
|
||||
test('roles', () => {
|
||||
const component = renderer.create(createDOMElement('span', { accessibilityRole: 'banner' }));
|
||||
expect(component.toJSON()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
const button = 'button';
|
||||
element = shallow(createDOMElement('span', { accessibilityRole: 'button' }));
|
||||
assert.equal(element.prop('type'), button);
|
||||
assert.equal(element.is('button'), true);
|
||||
test('button', () => {
|
||||
const component = renderer.create(createDOMElement('span', { accessibilityRole: 'button' }));
|
||||
expect(component.toJSON()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('link and target="_blank"', () => {
|
||||
const component = renderer.create(createDOMElement('span', { accessibilityRole: 'link', target: '_blank' }));
|
||||
expect(component.toJSON()).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
test('prop "accessible"', () => {
|
||||
// accessible (implicit)
|
||||
let element = shallow(createDOMElement('span', {}));
|
||||
assert.equal(element.prop('aria-hidden'), null);
|
||||
let component = renderer.create(createDOMElement('span', {}));
|
||||
expect(component.toJSON()).toMatchSnapshot();
|
||||
// accessible (explicit)
|
||||
element = shallow(createDOMElement('span', { accessible: true }));
|
||||
assert.equal(element.prop('aria-hidden'), null);
|
||||
component = renderer.create(createDOMElement('span', { accessible: true }));
|
||||
expect(component.toJSON()).toMatchSnapshot();
|
||||
// not accessible
|
||||
element = shallow(createDOMElement('span', { accessible: false }));
|
||||
assert.equal(element.prop('aria-hidden'), true);
|
||||
component = renderer.create(createDOMElement('span', { accessible: false }));
|
||||
expect(component.toJSON()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('prop "testID"', () => {
|
||||
// no testID
|
||||
let element = shallow(createDOMElement('span', {}));
|
||||
assert.equal(element.prop('data-testid'), null);
|
||||
// with testID
|
||||
const testID = 'Example.testID';
|
||||
element = shallow(createDOMElement('span', { testID }));
|
||||
assert.equal(element.prop('data-testid'), testID);
|
||||
const component = renderer.create(createDOMElement('span', { testID: 'Example.testID' }));
|
||||
expect(component.toJSON()).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -30,18 +30,26 @@ const createDOMElement = (component, rnProps = {}) => {
|
||||
|
||||
const accessibilityComponent = accessibilityRole && roleComponents[accessibilityRole];
|
||||
const Component = accessibilityComponent || component;
|
||||
const domProps = {
|
||||
...other,
|
||||
...StyleSheet.resolve(other)
|
||||
};
|
||||
if (!accessible) { domProps['aria-hidden'] = true; }
|
||||
if (accessibilityLabel) { domProps['aria-label'] = accessibilityLabel; }
|
||||
if (accessibilityLiveRegion) { domProps['aria-live'] = accessibilityLiveRegion; }
|
||||
if (testID) { domProps['data-testid'] = testID; }
|
||||
if (accessibilityRole) {
|
||||
domProps.role = accessibilityRole;
|
||||
if (accessibilityRole === 'button') {
|
||||
domProps.type = 'button';
|
||||
} else if (accessibilityRole === 'link' && domProps.target === '_blank') {
|
||||
domProps.rel = `${domProps.rel || ''} noopener noreferrer`;
|
||||
}
|
||||
}
|
||||
if (type) { domProps.type = type; }
|
||||
|
||||
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}
|
||||
/>
|
||||
<Component {...domProps} />
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
import ReactDOM from 'react-dom';
|
||||
const findNodeHandle = ReactDOM.findDOMNode;
|
||||
import findNodeHandle from 'react/lib/findDOMNode';
|
||||
export default findNodeHandle;
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
exports[`modules/flattenStyle should merge style objects 1`] = `
|
||||
Object {
|
||||
"opacity": 1,
|
||||
"order": 2,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`modules/flattenStyle should override style properties 1`] = `
|
||||
Object {
|
||||
"backgroundColor": "#023c69",
|
||||
"order": null,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`modules/flattenStyle should overwrite properties with \`undefined\` 1`] = `
|
||||
Object {
|
||||
"backgroundColor": undefined,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`modules/flattenStyle should recursively flatten arrays 1`] = `
|
||||
Object {
|
||||
"opacity": 1,
|
||||
"order": 3,
|
||||
}
|
||||
`;
|
||||
@@ -1,4 +1,4 @@
|
||||
/* eslint-env mocha */
|
||||
/* eslint-env jasmine, jest */
|
||||
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
@@ -9,43 +9,44 @@
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
import assert from 'assert';
|
||||
import flattenStyle from '..';
|
||||
|
||||
suite('modules/flattenStyle', () => {
|
||||
describe('modules/flattenStyle', () => {
|
||||
test('should merge style objects', () => {
|
||||
const style1 = { opacity: 1 };
|
||||
const style2 = { order: 2 };
|
||||
const flatStyle = flattenStyle([ style1, style2 ]);
|
||||
assert.equal(flatStyle.opacity, 1);
|
||||
assert.equal(flatStyle.order, 2);
|
||||
const style = flattenStyle([
|
||||
{ opacity: 1 },
|
||||
{ order: 2 }
|
||||
]);
|
||||
expect(style).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should override style properties', () => {
|
||||
const style1 = { backgroundColor: '#000', order: 1 };
|
||||
const style2 = { backgroundColor: '#023c69', order: null };
|
||||
const flatStyle = flattenStyle([ style1, style2 ]);
|
||||
assert.equal(flatStyle.backgroundColor, '#023c69');
|
||||
assert.strictEqual(flatStyle.order, null);
|
||||
const style = flattenStyle([
|
||||
{ backgroundColor: '#000', order: 1 },
|
||||
{ backgroundColor: '#023c69', order: null }
|
||||
]);
|
||||
expect(style).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should overwrite properties with `undefined`', () => {
|
||||
const style1 = { backgroundColor: '#000' };
|
||||
const style2 = { backgroundColor: undefined };
|
||||
const flatStyle = flattenStyle([ style1, style2 ]);
|
||||
assert.strictEqual(flatStyle.backgroundColor, undefined);
|
||||
const style = flattenStyle([
|
||||
{ backgroundColor: '#000' },
|
||||
{ backgroundColor: undefined }
|
||||
]);
|
||||
expect(style).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should not fail on falsy values', () => {
|
||||
assert.doesNotThrow(() => flattenStyle([ null, false, undefined ]));
|
||||
expect(() => flattenStyle([ null, false, undefined ])).not.toThrow();
|
||||
});
|
||||
|
||||
test('should recursively flatten arrays', () => {
|
||||
const style1 = { order: 2 };
|
||||
const style2 = { opacity: 1 };
|
||||
const style3 = { order: 3 };
|
||||
const flatStyle = flattenStyle([ null, [], [ style1, style2 ], style3 ]);
|
||||
assert.equal(flatStyle.order, 3);
|
||||
assert.equal(flatStyle.opacity, 1);
|
||||
const style = flattenStyle([
|
||||
null,
|
||||
[],
|
||||
[ { order: 2 }, { opacity: 1 } ],
|
||||
{ order: 3 }
|
||||
]);
|
||||
expect(style).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user