Compare commits

...

45 Commits

Author SHA1 Message Date
Nicolas Gallagher
5c482ef3be 0.0.56 2016-12-08 18:29:38 -08:00
Nicolas Gallagher
f51592f96e [change] TouchableOpacity without Animated
Fix #259
2016-12-08 18:22:25 -08:00
Nicolas Gallagher
6bffe1775f Fix lint error 2016-12-07 16:49:53 -08:00
Nicolas Gallagher
7e75d037f2 [fix] Image passes unknown props to underlying View
Fix #267
2016-12-07 16:37:24 -08:00
Nicolas Gallagher
7536508fe3 Update docs 2016-12-07 16:22:39 -08:00
Maxime Thirouin
945fff0015 Add source files to published package 2016-11-25 12:38:29 -08:00
Nicolas Gallagher
5032ed6fe1 Update AppRegistry docs 2016-11-25 12:36:05 -08:00
Nicolas Gallagher
8c7cdbf4be 0.0.55 2016-11-24 10:24:19 -08:00
Nicolas Gallagher
e5d8857bcc [fix] inject ReactDefaultInjection
Fixes a regression introduced in the following commit to avoid directly
depending on the 'react-dom' entry file:

d65c92eea9

Injecting ReactDefaultInjection adds ~25 KB back to the UMD build.

Fix #263
2016-11-24 10:21:40 -08:00
Nicolas Gallagher
cda8d05bb7 0.0.54 2016-11-24 08:42:53 -08:00
Nicolas Gallagher
049edc4611 [change] don't prefix HTML id's with underscore 2016-11-23 10:28:20 -08:00
Nicolas Gallagher
e76d5a4e6c [change] export createDOMElement helper
Fix #184
2016-11-23 10:25:00 -08:00
Nicolas Gallagher
f71dae7d93 [add] support for vendor-prefixed font-smoothing styles
Fix #240
2016-11-23 09:58:18 -08:00
Nicolas Gallagher
94d31beaf4 [change] ignore unsupported React Native props
Ignores RN props that RN packages commonly applied to elements without
scoping them to supported platforms.

Fix #252
2016-11-23 09:27:27 -08:00
Nicolas Gallagher
f5f9389728 0.0.53 2016-11-22 17:46:57 -08:00
Nicolas Gallagher
fdbd19a4af [fix] PropTypes production error
See: https://github.com/oliviertassinari/babel-plugin-transform-react-remove-prop-types/issues/68
2016-11-22 17:44:48 -08:00
Nicolas Gallagher
36eafbc2f5 0.0.52 2016-11-22 17:10:14 -08:00
Nicolas Gallagher
bca3398c1c Correct devDependencies 2016-11-22 17:08:04 -08:00
Nicolas Gallagher
722d77e8e5 Smaller production builds
Builds on the exclusion of PropTypes from production builds:

- Remove 'lodash' and use smaller modules for equivalent functions.
- Remove use of some unnecessary Facebook utilities.
- Remove 'TouchableBounce'; it isn't part of React Native anymore.
- Remove stray import of 'react-dom/server'.
- Exclude 'StyleSheetValidation' from production.

Measuring the UMD build (gzip)…

Before: ~100KB
After: ~60KB
2016-11-22 16:59:20 -08:00
Nicolas Gallagher
d65c92eea9 [change] prepare for react-dom@15.4.0
Don't depend directly on the 'react-dom' module as it will be prebuilt
in 15.4. Leave server-side rendering to 'react-dom/server'.
2016-11-22 16:57:28 -08:00
Nicolas Gallagher
8dd39c681c [change] allow propTypes to be removed in production builds
Fix #254
2016-11-22 16:57:22 -08:00
Nicolas Gallagher
0b1759363d [add] promote ScrollView content to new layer 2016-11-22 13:08:11 -08:00
Nicolas Gallagher
abd1227a94 [change] ScrollView props and event handling
- Update 'scrollEventThrottle' prop
- Filter non-DOM props
- Persist debounced scroll events.

Fix #209
2016-11-21 21:39:08 -08:00
Nicolas Gallagher
8352c7cbda Use yarn for dependency management 2016-11-21 17:10:50 -08:00
Nicolas Gallagher
89f5a13891 [change] TextInput uses DOM elements
This patch changes TextInput to use DOM inputs directly, rather than
trying to reimplement 'placeholder'. Removes support for
'placeholderTextColor'.

Fix #54
Fix #224
Fix #229
Fix #235
Fix #253
2016-11-21 16:52:40 -08:00
Nicolas Gallagher
4005f9ddde 0.0.51 2016-11-21 12:42:58 -08:00
Nicolas Gallagher
f192a9ba26 [fix] Depend on React@15.3
React@15.4 includes changes that prevent the ResponderEventPlugin from
being properly injected, which breaks Touchables and PanResponder.

Fix #257
2016-11-21 12:38:37 -08:00
Nicolas Gallagher
e688a949fb 0.0.50 2016-11-20 14:00:42 -08:00
Nicolas Gallagher
77605cb27c [add] Text accessibility roles
Fix #199
2016-11-20 13:56:49 -08:00
Nicolas Gallagher
4f71833aec [fix] Image rendering in Safari
The use of 'max-height:100%' on the inner image can cause extremely poor
render performance in Safari. Remove the inner image and simplify
`Image` to use a single view. This fixes the following additional bugs:

Fix #202
Fix #226
2016-11-20 13:51:16 -08:00
Nicolas Gallagher
fa14995a35 Use jest for testing
Thanks to @paularmstrong:
https://github.com/necolas/react-native-web/pull/249
2016-11-09 10:00:49 -08:00
Paul Armstrong
4beae0dd78 [fix] NetInfo event handler registration 2016-11-04 10:20:19 -07:00
Nicolas Gallagher
5598961d2c Move ResponderEventPlugin injection to View 2016-11-04 10:09:55 -07:00
Nicolas Gallagher
4613baf0e8 [fix] StyleSheet check 'transform' is an array 2016-11-04 10:09:27 -07:00
Nicolas Gallagher
3b661d8d6d 0.0.49 2016-11-03 09:16:00 -07:00
Nicolas Gallagher
22d20706e3 Add React Native TextInput examples 2016-11-03 08:56:26 -07:00
Nicolas Gallagher
0b2813b186 [fix] View when 'style' is not defined 2016-11-03 08:52:25 -07:00
Nicolas Gallagher
b248de552d Fix tests 2016-11-03 08:51:51 -07:00
Nicolas Gallagher
2b826dc7f4 [add] TextInput support for selection 2016-10-28 23:37:19 -07:00
Nicolas Gallagher
b46acd4f50 [fix] TextInputState focus management 2016-10-28 22:12:20 -07:00
Nicolas Gallagher
5a03cb25cb [add] TextInput support for blurOnSubmit and onSubmitEditing 2016-10-28 21:15:35 -07:00
Nicolas Gallagher
44e60d12e3 [change] TextInput support for autoCorrect and autoComplete 2016-10-28 10:51:05 -07:00
Nicolas Gallagher
fc60f8d332 [add] TextInput support for autoCapitalize 2016-10-28 10:36:06 -07:00
Nicolas Gallagher
2a65ca6fc0 [add] TextInput support for isFocused 2016-10-27 22:31:43 -07:00
Nicolas Gallagher
9db3bd7e67 [add] TextInput support for onKeyPress
Fix #215
2016-10-27 22:17:59 -07:00
101 changed files with 10311 additions and 1527 deletions

View File

@@ -1,5 +1,8 @@
{
"presets": [
"react-native"
],
"plugins": [
[ "transform-react-remove-prop-types", { "mode": "wrap" } ]
]
}

View File

@@ -5,5 +5,4 @@ before_script:
- export DISPLAY=:99.0
- sh -e /etc/init.d/xvfb start
script:
- npm run lint
- npm test

View File

@@ -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

View File

@@ -142,6 +142,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

View File

@@ -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>)

View File

@@ -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

View File

@@ -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);
```

View File

@@ -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())
})

View File

@@ -7,8 +7,8 @@ storiesOf('component: ScrollView', module)
<View style={styles.scrollViewContainer}>
<ScrollView
contentContainerStyle={styles.scrollViewContentContainerStyle}
onScroll={e => console.log('ScrollView.onScroll', e)}
scrollEventThrottle={1} // 1 event per second
onScroll={e => { console.log('ScrollView.onScroll', e); } }
scrollEventThrottle={1000} // 1 event per second
style={styles.scrollViewStyle}
>
{Array.from({ length: 50 }).map((item, i) => (

View File

@@ -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>

View File

@@ -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>
<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}
/>
<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())
});

View File

@@ -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>
);
}

View File

@@ -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
}
})
}

View File

@@ -1,10 +1,12 @@
{
"name": "react-native-web",
"version": "0.0.48",
"version": "0.0.56",
"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"
}
}

View File

@@ -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');
});
});

View File

@@ -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);
}
}

View File

@@ -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 };
}

View File

@@ -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();
});
});
});

View File

@@ -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' ];

View File

@@ -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,
},
}
`;

View File

@@ -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();
});
});

View File

@@ -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);

View File

@@ -1,5 +1,5 @@
/* eslint-env mocha */
/* eslint-env jasmine, jest */
suite('apis/Dimensions', () => {
describe('apis/Dimensions', () => {
test.skip('NO TEST COVERAGE', () => {});
});

View File

@@ -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';

View File

@@ -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);
});
});

View File

@@ -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.

View File

@@ -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;
});
});
});
});

View File

@@ -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 {

View File

@@ -1,5 +1,5 @@
/* eslint-env mocha */
/* eslint-env jasmine, jest */
suite('apis/PixelRatio', () => {
describe('apis/PixelRatio', () => {
test.skip('NO TEST COVERAGE', () => {});
});

View File

@@ -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",
}
`;

View File

@@ -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",
}
`;

View File

@@ -0,0 +1,10 @@
exports[`apis/StyleSheet resolve 1`] = `
Object {
"className": "test __style_df __style_pebn",
"style": Object {
"display": null,
"opacity": 1,
"pointerEvents": null,
},
}
`;

View File

@@ -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);
});
});

View File

@@ -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);
});
});

View File

@@ -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();
});
});
});

View File

@@ -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();
});
});

View File

@@ -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);
});
});

View File

@@ -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
});
});
});

View File

@@ -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
});
});
});

View File

@@ -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' });
});
});

View File

@@ -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;

View File

@@ -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);

View File

@@ -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');
});
});
});

View File

@@ -1,5 +1,5 @@
/* eslint-env mocha */
/* eslint-env jasmine, jest */
suite('components/ActivityIndicator', () => {
describe('components/ActivityIndicator', () => {
test.skip('NO TEST COVERAGE', () => {});
});

View File

@@ -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
};
} : {};

View 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",
}
} />
`;

View File

@@ -1,182 +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 style = StyleSheet.flatten(image.prop('style'));
assert(style.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 image = mount(<Image defaultSource={defaultSource} />);
const html = image.html();
assert(html.indexOf('height: 10px') > -1);
assert(html.indexOf('width: 20px') > -1);
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 image = mount(<Image defaultSource={defaultSource} style={{ height: 20, width: 40 }} />);
const html = image.html();
assert(html.indexOf('height: 20px') > -1);
assert(html.indexOf('width: 40px') > -1);
const component = renderer.create(<Image defaultSource={defaultSource} style={{ height: 20, width: 40 }} />);
expect(component.toJSON()).toMatchSnapshot();
});
});
test('prop "onError"', function (done) {
this.timeout(5000);
const image = mount(<Image onError={onError} source={{ uri: 'https://google.com/favicon.icox' }} />);
function onError(e) {
assert.ok(e.nativeEvent.error);
image.unmount();
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(&quot;https://google.com/favicon.ico&quot;)') > -1;
assert.equal(hasBackgroundImage, true);
image.unmount();
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(&quot;https://google.com/favicon.ico&quot;)') > -1;
assert.equal(hasBackgroundImage, true);
image.unmount();
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' };
const image = mount(<Image onLoad={onLoad} source={source} />);
function onLoad(e) {
const src = e.nativeEvent.target.src;
assert.equal(src, source.uri);
image.unmount();
done();
}
});
test('sets background image when value is a string', (done) => {
// emulate require-ed asset
const source = 'https://google.com/favicon.ico';
const image = mount(<Image onLoad={onLoad} source={source} />);
function onLoad(e) {
const src = e.nativeEvent.target.src;
assert.equal(src, source);
image.unmount();
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();
});
});

View File

@@ -1,7 +1,5 @@
/* 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 StyleSheet from '../../apis/StyleSheet';
@@ -39,7 +37,7 @@ class Image extends Component {
static displayName = 'Image';
static propTypes = {
...BaseComponentPropTypes,
...View.propTypes,
children: PropTypes.any,
defaultSource: ImageSourcePropType,
onError: PropTypes.func,
@@ -53,7 +51,6 @@ class Image extends Component {
};
static defaultProps = {
accessible: true,
style: {}
};
@@ -64,12 +61,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() {
@@ -87,6 +86,7 @@ class Image extends Component {
componentWillUnmount() {
this._destroyImageLoader();
this._isMounted = false;
}
render() {
@@ -98,45 +98,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;
const originalStyle = StyleSheet.flatten(this.props.style);
const resizeMode = this.props.resizeMode || originalStyle.resizeMode || ImageResizeMode.cover;
const finalResizeMode = resizeMode || originalStyle.resizeMode || ImageResizeMode.cover;
const style = StyleSheet.flatten([
styles.initial,
imageSizeStyle,
originalStyle,
backgroundImage && { backgroundImage },
resizeModeStyles[resizeMode]
resizeModeStyles[finalResizeMode]
]);
// View doesn't support 'resizeMode' as a style
delete style.resizeMode;
/**
* 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={style}
testID={testID}
>
{createDOMElement('img', { src: displayImage, style: styles.img })}
{children ? (
<View children={children} pointerEvents='box-none' style={styles.children} />
) : null}
</View>
/>
);
}
@@ -198,7 +193,11 @@ class Image extends Component {
this._imageState = status;
const isLoaded = this._imageState === STATUS_LOADED;
if (isLoaded !== this.state.isLoaded) {
this.setState({ isLoaded });
window.requestAnimationFrame(() => {
if (this._isMounted) {
this.setState({ isLoaded });
}
});
}
}
}
@@ -209,20 +208,6 @@ const styles = StyleSheet.create({
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
}
});

View File

@@ -1,5 +1,5 @@
/* eslint-env mocha */
/* eslint-env jasmine, jest */
suite('components/ListView', () => {
describe('components/ListView', () => {
test('NO TEST COVERAGE');
});

View File

@@ -1,13 +1,10 @@
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';
const scrollViewProps = Object.keys(ScrollView.propTypes);
class ListView extends Component {
static propTypes = ListViewPropTypes;
@@ -90,9 +87,7 @@ class ListView extends Component {
}
}
const props = pick(this.props, scrollViewProps);
return React.cloneElement(this.props.renderScrollComponent(props), {
return React.cloneElement(this.props.renderScrollComponent(this.props), {
ref: this._setScrollViewRef
}, header, children, footer);
}

View File

@@ -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();
});
});
});

View File

@@ -6,7 +6,7 @@
* @flow
*/
import debounce from 'lodash/debounce';
import debounce from 'debounce';
import View from '../View';
import React, { Component, PropTypes } from 'react';
@@ -23,12 +23,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 +52,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);
@@ -81,12 +86,22 @@ export default class ScrollViewBase extends Component {
_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;

View File

@@ -1,5 +1,5 @@
/* eslint-env mocha */
/* eslint-env jasmine, jest */
suite('components/ScrollView', () => {
describe('components/ScrollView', () => {
test('NO TEST COVERAGE');
});

View File

@@ -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,8 @@ const styles = StyleSheet.create({
overflowY: 'hidden'
},
contentContainer: {
flexGrow: 1
flexGrow: 1,
transform: [ { translateZ: 0 } ]
},
contentContainerHorizontal: {
flexDirection: 'row'

View File

@@ -1,5 +1,5 @@
/* eslint-env mocha */
/* eslint-env jasmine, jest */
suite('components/StaticContainer', () => {
describe('components/StaticContainer', () => {
test.skip('NO TEST COVERAGE', () => {});
});

View File

@@ -1,5 +1,5 @@
/* eslint-env mocha */
/* eslint-env jasmine, jest */
suite('components/StaticRenderer', () => {
describe('components/StaticRenderer', () => {
test.skip('NO TEST COVERAGE', () => {});
});

View File

@@ -0,0 +1,843 @@
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": "background-color 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": "background-color 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,
"WebkitTransition": "background-color 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",
"paddingBottom": "0px",
"paddingLeft": "0px",
"paddingRight": "0px",
"paddingTop": "0px",
"position": "relative",
"textAlign": "inherit",
"textDecoration": "none",
"transition": "background-color 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": "background-color 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": "background-color 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,
"WebkitTransition": "background-color 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",
"paddingBottom": "0px",
"paddingLeft": "0px",
"paddingRight": "0px",
"paddingTop": "0px",
"position": "relative",
"textAlign": "inherit",
"textDecoration": "none",
"transition": "background-color 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": "background-color 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": "background-color 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,
"WebkitTransition": "background-color 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",
"paddingBottom": "0px",
"paddingLeft": "0px",
"paddingRight": "0px",
"paddingTop": "0px",
"position": "relative",
"textAlign": "inherit",
"textDecoration": "none",
"transition": "background-color 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": "background-color 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": "background-color 0.1s",
"width": "90%",
}
} />
<div
className=" __style_df"
style={
Object {
"MozBoxSizing": "border-box",
"WebkitAlignItems": "stretch",
"WebkitAlignSelf": "flex-end",
"WebkitBoxAlign": "stretch",
"WebkitBoxDirection": "normal",
"WebkitBoxOrient": "vertical",
"WebkitFlexBasis": "auto",
"WebkitFlexDirection": "column",
"WebkitFlexShrink": 0,
"WebkitTransition": "background-color 0.1s",
"alignItems": "stretch",
"alignSelf": "flex-end",
"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": "end",
"msFlexNegative": 0,
"msPreferredSize": "auto",
"paddingBottom": "0px",
"paddingLeft": "0px",
"paddingRight": "0px",
"paddingTop": "0px",
"position": "relative",
"textAlign": "inherit",
"textDecoration": "none",
"transition": "background-color 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>
`;

View File

@@ -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();
});
});
});

View File

@@ -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
};
} : {};

View File

@@ -0,0 +1,86 @@
exports[`components/Text prop "children" 1`] = `
<span
className=""
onClick={[Function]}
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 "selectable" 1`] = `
<span
className=""
onClick={[Function]}
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=""
onClick={[Function]}
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",
}
} />
`;

View File

@@ -1,41 +1,23 @@
/* 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 "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();
});
});

View File

@@ -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,10 +29,17 @@ class Text extends Component {
render() {
const {
numberOfLines,
onLayout, // eslint-disable-line
onPress, // eslint-disable-line
selectable,
style,
/* eslint-disable */
adjustsFontSizeToFit,
allowFontScaling,
ellipsizeMode,
minimumFontScale,
onLayout,
onPress,
suppressHighlighting,
/* eslint-enable */
...other
} = this.props;

View File

@@ -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);
}

View File

@@ -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);
});
});

View File

@@ -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,97 @@ 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));

View File

@@ -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

View File

@@ -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;

View File

@@ -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() {

View File

@@ -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: {

View File

@@ -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.
*/

View File

@@ -1,5 +1,5 @@
/* eslint-env mocha */
/* eslint-env jasmine, jest */
suite('components/Touchable', () => {
describe('components/Touchable', () => {
test.skip('NO TEST COVERAGE', () => {});
});

View File

@@ -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' ])
};
} : {};

View 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>
`;

View File

@@ -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();
});
});

View File

@@ -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];

View File

@@ -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,

View File

@@ -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 Animated from './apis/Animated';
@@ -31,17 +31,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,11 +48,8 @@ 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,
@@ -82,13 +78,13 @@ const ReactNative = {
Text,
TextInput,
Touchable,
TouchableBounce,
TouchableHighlight,
TouchableOpacity,
TouchableWithoutFeedback,
View,
// modules
createDOMElement,
NativeModules,
// propTypes

View File

@@ -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
);

View File

@@ -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
);

View File

@@ -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();
};

View File

@@ -0,0 +1,66 @@
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" 1`] = `
<header
className=""
role="banner"
style={Object {}} />
`;
exports[`modules/createDOMElement prop "accessibilityRole" 2`] = `
<button
className=""
role="button"
style={Object {}}
type="button" />
`;
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 {}} />
`;

View File

@@ -1,60 +1,50 @@
/* 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);
const button = 'button';
element = shallow(createDOMElement('span', { accessibilityRole: 'button' }));
assert.equal(element.prop('type'), button);
assert.equal(element.is('button'), true);
let component = renderer.create(createDOMElement('span', { accessibilityRole }));
expect(component.toJSON()).toMatchSnapshot();
component = renderer.create(createDOMElement('span', { accessibilityRole: 'button' }));
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();
});
});

View File

@@ -30,18 +30,24 @@ 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';
}
}
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} />
);
};

View File

@@ -1,3 +1,2 @@
import ReactDOM from 'react-dom';
const findNodeHandle = ReactDOM.findDOMNode;
import findNodeHandle from 'react/lib/findDOMNode';
export default findNodeHandle;

View File

@@ -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,
}
`;

View File

@@ -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();
});
});

View File

@@ -1,222 +0,0 @@
/* eslint-disable */
/**
* @generated SignedSource<<b68d78236d45828b3f7f7fcc740782a9>>
*
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* !! This file is a check-in of a static_upstream project! !!
* !! !!
* !! You should not modify this file directly. Instead: !!
* !! 1) Use `fjs use-upstream` to temporarily replace this with !!
* !! the latest version from upstream. !!
* !! 2) Make your changes, test them, etc. !!
* !! 3) Use `fjs push-upstream` to copy your changes back to !!
* !! static_upstream. !!
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
*
* Copyright 2013-2014 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* @providesModule mergeHelpers
*
* requiresPolyfills: Array.isArray
*/
"use strict";
var invariant = require('fbjs/lib/invariant');
var keyMirror = require('fbjs/lib/keyMirror');
/**
* Maximum number of levels to traverse. Will catch circular structures.
* @const
*/
var MAX_MERGE_DEPTH = 36;
/**
* We won't worry about edge cases like new String('x') or new Boolean(true).
* Functions are considered terminals, and arrays are not.
* @param {*} o The item/object/value to test.
* @return {boolean} true iff the argument is a terminal.
*/
var isTerminal = function(o) {
return typeof o !== 'object' || o === null;
};
var mergeHelpers = {
MAX_MERGE_DEPTH: MAX_MERGE_DEPTH,
isTerminal: isTerminal,
/**
* Converts null/undefined values into empty object.
*
* @param {?Object=} arg Argument to be normalized (nullable optional)
* @return {!Object}
*/
normalizeMergeArg: function(arg) {
return arg === undefined || arg === null ? {} : arg;
},
/**
* If merging Arrays, a merge strategy *must* be supplied. If not, it is
* likely the caller's fault. If this function is ever called with anything
* but `one` and `two` being `Array`s, it is the fault of the merge utilities.
*
* @param {*} one Array to merge into.
* @param {*} two Array to merge from.
*/
checkMergeArrayArgs: function(one, two) {
invariant(
Array.isArray(one) && Array.isArray(two),
'Tried to merge arrays, instead got %s and %s.',
one,
two
);
},
/**
* @param {*} one Object to merge into.
* @param {*} two Object to merge from.
*/
checkMergeObjectArgs: function(one, two) {
mergeHelpers.checkMergeObjectArg(one);
mergeHelpers.checkMergeObjectArg(two);
},
/**
* @param {*} arg
*/
checkMergeObjectArg: function(arg) {
invariant(
!isTerminal(arg) && !Array.isArray(arg),
'Tried to merge an object, instead got %s.',
arg
);
},
/**
* @param {*} arg
*/
checkMergeIntoObjectArg: function(arg) {
invariant(
(!isTerminal(arg) || typeof arg === 'function') && !Array.isArray(arg),
'Tried to merge into an object, instead got %s.',
arg
);
},
/**
* Checks that a merge was not given a circular object or an object that had
* too great of depth.
*
* @param {number} Level of recursion to validate against maximum.
*/
checkMergeLevel: function(level) {
invariant(
level < MAX_MERGE_DEPTH,
'Maximum deep merge depth exceeded. You may be attempting to merge ' +
'circular structures in an unsupported way.'
);
},
/**
* Checks that the supplied merge strategy is valid.
*
* @param {string} Array merge strategy.
*/
checkArrayStrategy: function(strategy) {
invariant(
strategy === undefined || strategy in mergeHelpers.ArrayStrategies,
'You must provide an array strategy to deep merge functions to ' +
'instruct the deep merge how to resolve merging two arrays.'
);
},
/**
* Set of possible behaviors of merge algorithms when encountering two Arrays
* that must be merged together.
* - `clobber`: The left `Array` is ignored.
* - `indexByIndex`: The result is achieved by recursively deep merging at
* each index. (not yet supported.)
*/
ArrayStrategies: keyMirror({
Clobber: true,
IndexByIndex: true
})
};
/**
* @generated SignedSource<<d3caa35be27b17ea4dd4c76bef72d1ab>>
*
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* !! This file is a check-in of a static_upstream project! !!
* !! !!
* !! You should not modify this file directly. Instead: !!
* !! 1) Use `fjs use-upstream` to temporarily replace this with !!
* !! the latest version from upstream. !!
* !! 2) Make your changes, test them, etc. !!
* !! 3) Use `fjs push-upstream` to copy your changes back to !!
* !! static_upstream. !!
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
*
* Copyright 2013-2014 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* @providesModule mergeInto
* @typechecks static-only
*/
var checkMergeObjectArg = mergeHelpers.checkMergeObjectArg;
var checkMergeIntoObjectArg = mergeHelpers.checkMergeIntoObjectArg;
/**
* Shallow merges two structures by mutating the first parameter.
*
* @param {object|function} one Object to be merged into.
* @param {?object} two Optional object with properties to merge from.
*/
function mergeInto(one, two) {
checkMergeIntoObjectArg(one);
if (two != null) {
checkMergeObjectArg(two);
for (var key in two) {
if (!two.hasOwnProperty(key)) {
continue;
}
one[key] = two[key];
}
}
}
var merge = function(one, two) {
var result = {};
mergeInto(result, one);
mergeInto(result, two);
return result;
};
module.exports = merge;

View File

@@ -1,19 +1,18 @@
/* eslint-env mocha */
/* eslint-env jasmine, jest */
import assert from 'assert';
import multiplyStyleLengthValue from '..';
suite('modules/multiplyStyleLengthValue', () => {
describe('modules/multiplyStyleLengthValue', () => {
test('number', () => {
assert.equal(multiplyStyleLengthValue(2, -1), -2);
assert.equal(multiplyStyleLengthValue(2, 2), 4);
assert.equal(multiplyStyleLengthValue(2.5, 2), 5);
expect(multiplyStyleLengthValue(2, -1)).toEqual(-2);
expect(multiplyStyleLengthValue(2, 2)).toEqual(4);
expect(multiplyStyleLengthValue(2.5, 2)).toEqual(5);
});
test('length', () => {
assert.equal(multiplyStyleLengthValue('2rem', -1), '-2rem');
assert.equal(multiplyStyleLengthValue('2px', 2), '4px');
assert.equal(multiplyStyleLengthValue('2.5em', 2), '5em');
assert.equal(multiplyStyleLengthValue('2e3px', 2), '4000px');
expect(multiplyStyleLengthValue('2rem', -1)).toEqual('-2rem');
expect(multiplyStyleLengthValue('2px', 2)).toEqual('4px');
expect(multiplyStyleLengthValue('2.5em', 2)).toEqual('5em');
expect(multiplyStyleLengthValue('2e3px', 2)).toEqual('4000px');
});
});

View File

@@ -1,13 +1,13 @@
import { PropTypes } from 'react';
const { array, bool, number, object, oneOf, oneOfType, string } = PropTypes;
const BaseComponentPropTypes = {
const BaseComponentPropTypes = process.env.NODE_ENV !== 'production' ? {
accessibilityLabel: string,
accessibilityLiveRegion: oneOf([ 'assertive', 'off', 'polite' ]),
accessibilityRole: string,
accessible: bool,
style: oneOfType([ array, number, object ]),
testID: string
};
} : {};
module.exports = BaseComponentPropTypes;

View File

@@ -4,7 +4,7 @@ import { PropTypes } from 'react';
const numberOrString = PropTypes.oneOfType([ PropTypes.number, PropTypes.string ]);
const BorderStylePropType = PropTypes.oneOf([ 'solid', 'dotted', 'dashed' ]);
const BorderPropTypes = {
const BorderPropTypes = process.env.NODE_ENV !== 'production' ? {
borderColor: ColorPropType,
borderTopColor: ColorPropType,
borderRightColor: ColorPropType,
@@ -29,6 +29,6 @@ const BorderPropTypes = {
borderBottomRightRadius$noI18n: numberOrString,
borderLeftStyle$noI18n: BorderStylePropType,
borderRightStyle$noI18n: BorderStylePropType
};
} : {};
module.exports = BorderPropTypes;

View File

@@ -11,11 +11,10 @@
*/
import { PropTypes } from 'react'
import ReactPropTypeLocationNames from 'react/lib/ReactPropTypeLocationNames'
var normalizeColor = require('../modules/normalizeColor');
var colorPropType = function(isRequired, props, propName, componentName, location, propFullName) {
var normalizeColor = require('../modules/normalizeColor');
var ReactPropTypeLocationNames = require('react/lib/ReactPropTypeLocationNames');
var color = props[propName];
if (color === undefined || color === null) {
if (isRequired) {
@@ -56,7 +55,11 @@ var colorPropType = function(isRequired, props, propName, componentName, locatio
}
};
var ColorPropType = colorPropType.bind(null, false /* isRequired */);
ColorPropType.isRequired = colorPropType.bind(null, true /* isRequired */);
if (process.env.NODE_ENV !== 'production') {
var ColorPropType = colorPropType.bind(null, false /* isRequired */);
ColorPropType.isRequired = colorPropType.bind(null, true /* isRequired */);
} else {
var ColorPropType = function () {}
}
module.exports = ColorPropType

View File

@@ -14,13 +14,11 @@
var PropTypes = require('react').PropTypes;
var createStrictShapeTypeChecker = require('./createStrictShapeTypeChecker');
var EdgeInsetsPropType = createStrictShapeTypeChecker({
var EdgeInsetsPropType = process.env.NODE_ENV !== 'production' ? require('./createStrictShapeTypeChecker')({
top: PropTypes.number,
left: PropTypes.number,
bottom: PropTypes.number,
right: PropTypes.number,
});
}) : function () {};
module.exports = EdgeInsetsPropType;

View File

@@ -3,7 +3,7 @@ import { PropTypes } from 'react';
const { number, oneOf, oneOfType, string } = PropTypes;
const numberOrString = oneOfType([ number, string ]);
const LayoutPropTypes = {
const LayoutPropTypes = process.env.NODE_ENV !== 'production' ? {
// box model
borderWidth: numberOrString,
borderBottomWidth: numberOrString,
@@ -58,6 +58,6 @@ const LayoutPropTypes = {
paddingLeft$noI18n: numberOrString,
paddingRight$noI18n: numberOrString,
right$noI18n: numberOrString
};
} : {};
module.exports = LayoutPropTypes;

View File

@@ -14,11 +14,9 @@
var PropTypes = require('react').PropTypes;
var createStrictShapeTypeChecker = require('./createStrictShapeTypeChecker');
var PointPropType = createStrictShapeTypeChecker({
var PointPropType = process.env.NODE_ENV !== 'production' ? require('./createStrictShapeTypeChecker')({
x: PropTypes.number,
y: PropTypes.number,
});
}) : function () {};
module.exports = PointPropType;

View File

@@ -5,10 +5,10 @@
* @flow
*/
import createStrictShapeTypeChecker from './createStrictShapeTypeChecker';
import flattenStyle from '../modules/flattenStyle';
module.exports = process.env.NODE_ENV !== 'production' ? function StyleSheetPropType(shape) {
const createStrictShapeTypeChecker = require('./createStrictShapeTypeChecker');
const flattenStyle = require('../modules/flattenStyle');
module.exports = function StyleSheetPropType(shape) {
const shapePropType = createStrictShapeTypeChecker(shape);
return function (props, propName, componentName, location?) {
let newProps = props;
@@ -19,4 +19,4 @@ module.exports = function StyleSheetPropType(shape) {
}
return shapePropType(newProps, propName, componentName, location);
};
};
} : function () {};

View File

@@ -8,7 +8,7 @@ const ShadowOffsetPropType = shape({ width: number, height: number });
const TextAlignPropType = oneOf([ 'center', 'inherit', 'justify', 'justify-all', 'left', 'right' ]);
const WritingDirectionPropType = oneOf([ 'auto', 'ltr', 'rtl' ]);
const TextPropTypes = {
const TextPropTypes = process.env.NODE_ENV !== 'production' ? {
// box model
color: ColorPropType,
fontFamily: string,
@@ -20,26 +20,23 @@ const TextPropTypes = {
textAlign: TextAlignPropType,
textAlignVertical: oneOf([ 'auto', 'bottom', 'center', 'top' ]),
textDecorationLine: string,
/* @platform web */
textOverflow: string,
/* @platform web */
textRendering: oneOf([ 'auto', 'geometricPrecision', 'optimizeLegibility', 'optimizeSpeed' ]),
textShadowColor: ColorPropType,
textShadowOffset: ShadowOffsetPropType,
textShadowRadius: number,
/* @platform web */
textTransform: oneOf([ 'capitalize', 'lowercase', 'none', 'uppercase' ]),
/* @platform web */
unicodeBidi: oneOf([ 'normal', 'bidi-override', 'embed', 'isolate', 'isolate-override', 'plaintext' ]),
/* @platform web */
whiteSpace: string,
/* @platform web */
wordWrap: string,
writingDirection: WritingDirectionPropType,
/* @platform web */
textOverflow: string,
textRendering: oneOf([ 'auto', 'geometricPrecision', 'optimizeLegibility', 'optimizeSpeed' ]),
textTransform: oneOf([ 'capitalize', 'lowercase', 'none', 'uppercase' ]),
unicodeBidi: oneOf([ 'normal', 'bidi-override', 'embed', 'isolate', 'isolate-override', 'plaintext' ]),
whiteSpace: string,
wordWrap: string,
MozOsxFontSmoothing: string,
WebkitFontSmoothing: string,
// opt-out of RTL flipping
textAlign$noI18n: TextAlignPropType,
textShadowOffset$noI18n: ShadowOffsetPropType,
writingDirection$noI18n: WritingDirectionPropType
};
} : {};
module.exports = TextPropTypes;

View File

@@ -10,7 +10,7 @@ import { PropTypes } from 'react';
const { arrayOf, number, oneOfType, shape, string } = PropTypes;
const numberOrString = oneOfType([ number, string ]);
const TransformPropTypes = {
const TransformPropTypes = process.env.NODE_ENV !== 'production' ? {
transform: arrayOf(
oneOfType([
shape({ perspective: numberOrString }),
@@ -29,6 +29,6 @@ const TransformPropTypes = {
shape({ translate3d: string })
])
)
};
} : {};
module.exports = TransformPropTypes;

View File

@@ -12,7 +12,6 @@
*/
import invariant from 'fbjs/lib/invariant'
import merge from '../modules/merge'
import ReactPropTypeLocationNames from 'react/lib/ReactPropTypeLocationNames'
import ReactPropTypesSecret from 'react/lib/ReactPropTypesSecret'
@@ -43,7 +42,7 @@ function createStrictShapeTypeChecker(
}
// We need to check all keys in case some are required but missing from
// props.
var allKeys = merge(props[propName], shapeTypes);
var allKeys = { ...props[propName], ...shapeTypes };
for (var key in allKeys) {
var checker = shapeTypes[key];
if (!checker) {

View File

@@ -1,5 +1,6 @@
const path = require('path')
const webpack = require('webpack')
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const DIST_DIRECTORY = './dist'
@@ -7,6 +8,16 @@ module.exports = {
entry: {
main: DIST_DIRECTORY
},
externals: [
{
react: {
root: 'React',
commonjs2: 'react',
commonjs: 'react',
amd: 'react'
}
}
],
output: {
filename: 'ReactNative.js',
library: 'ReactNative',
@@ -14,12 +25,16 @@ module.exports = {
path: DIST_DIRECTORY
},
plugins: [
new BundleAnalyzerPlugin({
analyzerMode: 'static',
openAnalyzer: false
}),
new webpack.DefinePlugin({ 'process.env.NODE_ENV': JSON.stringify('production') }),
new webpack.optimize.DedupePlugin(),
// https://github.com/animatedjs/animated/issues/40
new webpack.NormalModuleReplacementPlugin(
/es6-set/,
path.join(__dirname, 'src/modules/polyfills/Set.js')
path.join(__dirname, 'dist/modules/polyfills/Set.js')
),
new webpack.optimize.OccurenceOrderPlugin(),
new webpack.optimize.UglifyJsPlugin({

Some files were not shown because too many files have changed in this diff Show More