Compare commits

..

58 Commits

Author SHA1 Message Date
Nicolas Gallagher
b08bfb9ad5 0.1.5 2017-10-04 11:49:22 -07:00
Nicolas Gallagher
5353011ee4 [fix] BackHandler export; remove BackAndroid
Fix #673
2017-10-04 11:45:28 -07:00
Nicolas Gallagher
5faa3af19a 0.1.4 2017-10-03 10:58:32 -07:00
Jhen
c8461c9c11 [fix] Babel plugin import paths for ViewPropTypes and TextPropTypes
Close #668
2017-10-03 10:53:55 -07:00
Nicolas Gallagher
3112e2ba56 0.1.3 2017-10-02 10:14:18 -07:00
Nicolas Gallagher
fee03e101c [fix] exports from module.js
Fix #666
2017-10-02 10:13:45 -07:00
Nicolas Gallagher
c730a20a26 Update formatter 2017-10-01 17:01:27 -07:00
Nicolas Gallagher
f7ed60ac67 Update test tools 2017-10-01 17:00:04 -07:00
Nicolas Gallagher
417716391a Update build tools 2017-10-01 16:59:00 -07:00
Nicolas Gallagher
ee5e3cb3ca Update eslint packages 2017-10-01 16:54:29 -07:00
Nicolas Gallagher
2298c5d6e3 0.1.2 2017-10-01 16:40:41 -07:00
Jirat Kijlerdpornpailoj
ca9f870ff6 [fix] Image.getSize failure callback
Fix #561
Close #665
2017-10-01 16:33:47 -07:00
Nicolas Gallagher
e9b2fd8bc4 0.1.1 2017-09-29 16:45:48 -07:00
Nicolas Gallagher
09a4985fd2 Remove markdown component and api docs 2017-09-28 14:49:51 -07:00
Nicolas Gallagher
a9e61b4cd5 Docs: allow AppText to inherit font-size 2017-09-28 14:49:51 -07:00
Charlie Croom
6ef19c3ccd [fix] latest NetInfo API
Update the NetInfo API to conform to the latest React Native
implementation. Web-only properties are also included now.

Fix #662
Close #663
2017-09-28 14:49:50 -07:00
Nicolas Gallagher
0d29458874 Remove links to unmaintained starter kits 2017-09-28 13:04:21 -07:00
Amogh Banta
1795bae8b5 Add re-start to starter kits 2017-09-27 09:06:45 -07:00
Nicolas Gallagher
c1152ee09a Update README install instructions 2017-09-26 10:30:48 -07:00
Nicolas Gallagher
182149aac7 0.1.0 2017-09-26 10:21:04 -07:00
Nicolas Gallagher
d6df440987 [change] React 16 support
Fix #364
2017-09-26 10:14:34 -07:00
Nicolas Gallagher
df16c24d68 0.0.130 2017-09-25 14:47:38 -07:00
Nicolas Gallagher
8a2c259235 Update inline-style-prefixer 2017-09-21 14:55:52 -07:00
Nicolas Gallagher
a7f265de11 Update test tools 2017-09-21 14:52:07 -07:00
Nicolas Gallagher
581529540a Update build tools 2017-09-21 14:50:42 -07:00
Nicolas Gallagher
8ad61d9061 Update formatter 2017-09-21 14:49:31 -07:00
Nicolas Gallagher
3d79861970 0.0.129 2017-09-21 14:20:37 -07:00
Nicolas Gallagher
0a84ccb299 [fix] IE10 flexbox support
Add prefixes for flexbox (and other) support in IE10.

Ref #650
2017-09-21 14:18:11 -07:00
Nicolas Gallagher
3aa37450a0 Improve error message for text nodes in View 2017-09-21 14:04:41 -07:00
Nicolas Gallagher
450722153d 0.0.128 2017-09-20 09:45:25 -07:00
Nicolas Gallagher
fbba32defb [fix] cross-browser flex styles
Fix #616
Close #648
2017-09-19 15:44:10 -07:00
Nicolas Gallagher
d762d64b49 Revert SSR Image change
Reverts 3c660e2ad7

See #543
2017-09-19 15:23:09 -07:00
Nicolas Gallagher
a5b1dda62d 0.0.127 2017-09-19 15:12:13 -07:00
Nicolas Gallagher
aad904b550 [fix] remove 'use strict' from warnValidStyle 2017-09-19 15:07:54 -07:00
Unknown
a01e895e30 [change] RefreshControl placeholder
Fix #638
Close #644
2017-09-19 13:44:31 -07:00
Nicolas Gallagher
52e5d41518 [fix] keyboard interaction with Touchable as link
Fix #643
Close #645
2017-09-19 13:40:57 -07:00
Nicolas Gallagher
77a40b6237 [fix] Easing import when using babel plugin
Fix #649
2017-09-19 13:37:12 -07:00
Nicolas Gallagher
4bbe1a40aa 0.0.126 2017-09-18 21:08:39 -07:00
Nicolas Gallagher
6942e4e417 Shallow render ActivityIndicator tests
These snapshots should be improved by flattening the React Native style
object.
2017-09-18 19:47:03 -07:00
Nicolas Gallagher
63daff7f80 Simplify Switch tests 2017-09-18 19:32:02 -07:00
Nicolas Gallagher
662b7c3d6e Simplify Image snapshot testing 2017-09-18 19:26:48 -07:00
Nicolas Gallagher
12fb588596 Simplify View snapshot testing 2017-09-18 19:26:11 -07:00
Nicolas Gallagher
5cb09b1a9e [fix] disallow text node children of View
Catch text nodes that are not the only child of View. Fixes a case
missing from 0ad6ab948b.
2017-09-18 13:28:51 -07:00
Nicolas Gallagher
c03cfdf8bd Add compile task to build task 2017-09-17 11:25:50 -07:00
Nicolas Gallagher
997b598de8 [fix] event normalization
Work better with simulated events, and avoid crashing if
'nativeEvent.target' isn't an element node.

Close #597
2017-09-15 15:16:40 -07:00
Nicolas Gallagher
6fe796f9da [add] accessibilityRole 'label' to Text 2017-09-15 11:29:44 -07:00
Nicolas Gallagher
0ad6ab948b [fix] disallow text node children of View
Fix #627
2017-09-15 11:29:41 -07:00
Nicolas Gallagher
32a23136af 0.0.125 2017-09-14 15:16:52 -07:00
Nicolas Gallagher
96f48226cb [fix] ColorPropType validation
Check color is of type string before using `indexOf`.

Fix #630
2017-09-14 13:19:13 -07:00
Peter Schussheim
f1ef0f21af Fix typo in NetInfo docs 2017-09-14 11:31:18 -07:00
Unknown
d42e8907ca [fix] return value of Keyboard.addListener
Close #631
Fix #632
2017-09-14 11:26:46 -07:00
Nicolas Gallagher
90724b2cef [fix] filter unsupported ScrollView props
Fix #633
2017-09-14 11:20:18 -07:00
Nicolas Gallagher
dd0f1de3d1 [fix] avoid prop types check on UnimplementedView
Fix #635
2017-09-14 11:08:36 -07:00
Nicolas Gallagher
fc751ed715 [fix] remove TouchalbeWithoutFeedback children prop type
Fix #634
2017-09-14 10:49:49 -07:00
Nicolas Gallagher
917b06a690 Update README 2017-09-13 11:27:36 -07:00
Nicolas Gallagher
80cb7baf82 0.0.124 2017-09-12 15:58:28 -07:00
Nicolas Gallagher
f08515b1f1 Avoid compiling tests 2017-09-12 15:58:14 -07:00
Nicolas Gallagher
8591bf7ce5 [fix] add AppState to Babel plugin list 2017-09-12 15:46:27 -07:00
72 changed files with 1627 additions and 2928 deletions

View File

@@ -3,36 +3,56 @@
[![Build Status][travis-image]][travis-url]
[![npm version][npm-image]][npm-url]
"React Native for Web" brings [React Native][react-native-url]'s
building blocks and touch handling to the Web.
"React Native for Web" brings the platform-agnostic Components and APIs of
[React Native][react-native-url] to the Web.
* [UI Explorer and documentation](https://necolas.github.io/react-native-web/storybook/).
* [React Native for Web: Playground](https://glitch.com/edit/#!/react-native-web-playground) using create-react-app.
Browse the [interactive
documentation](https://necolas.github.io/react-native-web/storybook/) or [try
it out](https://glitch.com/edit/#!/react-native-web-playground) on Glitch.
Browser support: Chrome, Firefox, Safari >= 7, IE 10, Edge.
## Features
[npm-image]: https://badge.fury.io/js/react-native-web.svg
[npm-url]: https://npmjs.org/package/react-native-web
[react-native-url]: https://facebook.github.io/react-native/
[travis-image]: https://travis-ci.org/necolas/react-native-web.svg?branch=master
[travis-url]: https://travis-ci.org/necolas/react-native-web
* Interoperability with ReactDOM components.
* Native-like touch handling.
* Built-in integration with web accessibility APIs.
* Built-in support for LTR and RTL layouts.
* Built-in expressive and reliable subset of CSS.
* Optimized, vendor-prefixed CSS with [good runtime performance](benchmarks/README.md).
* Server-side rendering of HTML and critical CSS.
* Browser support: Chrome, Firefox, Safari >= 7, IE 10, Edge.
## Quick start
NOTE: React Native for Web supports React/ReactDOM 15.4, 15.5, or 15.6.
Install in your existing app using `yarn` or `npm`:
```
yarn add react@15.6 react-dom@15.6 react-native-web
yarn add react react-dom react-native-web
```
Then read the [Getting Started](docs/guides/getting-started.md) guide.
Add the `react-native-web/babel` plugin to your Babel configuration. This will
alias `react-native` to `react-native-web` and exclude any modules not required
by the app.
```json
{
"plugins": [
"react-native-web/babel"
],
"presets": [
"react-native"
]
}
```
(For React/ReactDOM 15.4 15.6 support, install `react-native-web@<0.1.0`)
See the [Getting Started](docs/guides/getting-started.md) guide for more details.
## Documentation
The [UI Explorer](https://necolas.github.io/react-native-web/storybook/)
interactively documents all the supported APIs and Components.
The [interactive
documentation](https://necolas.github.io/react-native-web/storybook/) shows all
the supported APIs and Components.
Guides:
@@ -44,12 +64,6 @@ Guides:
* [Advanced use](docs/guides/advanced.md)
* [Known issues](docs/guides/known-issues.md)
## Starter kits
* [create-react-app](https://github.com/facebookincubator/create-react-app) ([on Glitch](https://glitch.com/edit/#!/react-native-web-playground))
* [react-native-web-starter](https://github.com/grabcode/react-native-web-starter)
* [react-native-web-webpack](https://github.com/ndbroadbent/react-native-web-webpack)
## Example code
```js
@@ -89,6 +103,12 @@ AppRegistry.registerComponent('MyApp', () => App)
AppRegistry.runApplication('MyApp', { rootTag: document.getElementById('react-root') })
```
## Starter kits
* [Glitch](https://glitch.com/edit/#!/react-native-web-playground)
* [create-react-app](https://github.com/facebookincubator/create-react-app)
* [re-start](https://github.com/react-everywhere/re-start)
## Related projects
* [react-primitives](https://github.com/lelandrichardson/react-primitives/)
@@ -99,3 +119,9 @@ AppRegistry.runApplication('MyApp', { rootTag: document.getElementById('react-ro
## License
React Native for Web is [BSD licensed](LICENSE).
[npm-image]: https://badge.fury.io/js/react-native-web.svg
[npm-url]: https://npmjs.org/package/react-native-web
[react-native-url]: https://facebook.github.io/react-native/
[travis-image]: https://travis-ci.org/necolas/react-native-web.svg?branch=master
[travis-url]: https://travis-ci.org/necolas/react-native-web

View File

@@ -12,13 +12,14 @@ import View from 'react-native-web/dist/components/View';
exports[`2. Rewrite react-native paths for react-native-web 1`] = `
"
import { Switch, Text, View as MyView } from 'react-native';
import { Switch, Text, View as MyView, ViewPropTypes } from 'react-native';
↓ ↓ ↓ ↓ ↓ ↓
import Switch from 'react-native-web/dist/components/Switch';
import Text from 'react-native-web/dist/components/Text';
import MyView from 'react-native-web/dist/components/View';
import ViewPropTypes from 'react-native-web/dist/components/View/ViewPropTypes';
"
`;

View File

@@ -7,7 +7,7 @@ pluginTester({
tests: [
// import react-native
"import { View } from 'react-native';",
"import { Switch, Text, View as MyView } from 'react-native';",
"import { Switch, Text, View as MyView, ViewPropTypes } from 'react-native';",
"import { createElement, Switch, StyleSheet } from 'react-native';",
"import { InvalidThing, TouchableOpacity } from 'react-native';",
"import * as RNW from 'react-native';",

View File

@@ -5,8 +5,9 @@ const getDistLocation = importName => {
// apis
case 'Animated':
case 'AppRegistry':
case 'AppState':
case 'AsyncStorage':
case 'BackAndroid':
case 'BackHandler':
case 'Clipboard':
case 'Dimensions':
case 'Easing':
@@ -68,11 +69,15 @@ const getDistLocation = importName => {
// propTypes
case 'ColorPropType':
case 'EdgeInsetsPropType':
case 'PointPropType':
case 'TextPropTypes':
case 'ViewPropTypes': {
case 'PointPropType': {
return `${root}/propTypes/${importName}`;
}
case 'TextPropTypes': {
return `${root}/components/Text/${importName}`;
}
case 'ViewPropTypes': {
return `${root}/components/View/${importName}`;
}
default:
return;

View File

@@ -1,60 +0,0 @@
# AppRegistry
`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` (see the [getting started guide](../guides/getting-started.md) for more details).
To "stop" an application when a view should be destroyed, call
`AppRegistry.unmountApplicationComponentAtRootTag` with the tag that was passed
into `runApplication`. These should always be used as a pair.
## Methods
(web) static **getApplication**(appKey:string, appParameters: object)
Returns the given application element. Use this for server-side rendering.
Return object is of type `{ element: ReactElement; stylesheets: [ ReactElement ] }`.
static **registerConfig**(config: Array<AppConfig>)
Registry multiple applications. `AppConfig` is of type `{ appKey: string;
component: ComponentProvider; run?: Function }`.
static **registerComponent**(appKey: string, getComponentFunc: ComponentProvider)
Register a component provider under the given `appKey`.
static **registerRunnable**(appKey: string, run: Function)
Register a custom render function for an application. The function will receive
the `appParameters` passed to `runApplication`.
static **getAppKeys**()
Returns all registered app keys.
static **runApplication**(appKey: string, appParameters?: object)
Runs the application that was registered under `appKey`. The `appParameters`
must include the `rootTag` into which the application is rendered, and
optionally any `initialProps`.
On web, if the `rootTag` is a sub-section of your application it should be
styled as `position:relative` and given an explicit height.
static **unmountApplicationComponentAtRootTag**(rootTag: HTMLElement)
To "stop" an application when a view should be destroyed, call
`AppRegistry.unmountApplicationComponentAtRootTag` with the tag that was passed
into `runApplication`
## Example
```js
AppRegistry.registerComponent('MyApp', () => AppComponent)
AppRegistry.runApplication('MyApp', {
initialProps: {},
rootTag: document.getElementById('react-root')
})
```

View File

@@ -1,60 +0,0 @@
## AppState
`AppState` can tell you if the app is in the foreground or background, and
notify you when the state changes.
States
* `active` - The app is running in the foreground
* `background` - The app is running in the background (i.e., the user has not focused the app's tab).
## Properties
static **currentState**
Returns the current state of the app: `active` or `background`.
## Methods
static **addEventListener**(type: string, handler: Function)
Add a handler to `AppState` changes by listening to the `change` event type and
providing the `handler`. The handler is called with the app state value.
static **removeEventListener**(type: string, handler: Function)
Remove a handler by passing the change event `type` and the `handler`.
## Examples
To see the current state, you can check `AppState.currentState`, which will be
kept up-to-date. This example will only ever appear to say "Current state is:
active" because the app is only visible to the user when in the `active` state,
and the null state will happen only momentarily.
```js
class Example extends React.Component {
constructor(props) {
super(props)
this.state = { currentAppState: AppState.currentState }
}
componentDidMount() {
AppState.addEventListener('change', this._handleAppStateChange);
}
componentWillUnmount() {
AppState.removeEventListener('change', this._handleAppStateChange);
}
_handleAppStateChange = (currentAppState) => {
this.setState({ currentAppState });
}
render() {
return (
<Text>Current state is: {this.state.currentAppState}</Text>
)
}
}
```

View File

@@ -1,71 +0,0 @@
# AsyncStorage
`AsyncStorage` is a simple, asynchronous, persistent, key-value storage system
that is global to the domain. It's a facade over, and should be used instead of
`window.localStorage` to provide an asynchronous API and multi functions. Each
method returns a `Promise` object.
It is recommended that you use an abstraction on top of `AsyncStorage` instead
of `AsyncStorage` directly for anything more than light usage since it operates
globally.
The batched functions are useful for executing a lot of operations at once,
allowing for optimizations to provide the convenience of a single promise after
all operations are complete.
## Methods
static **clear**()
Erases all AsyncStorage. You probably don't want to call this - use
`removeItem` or `multiRemove` to clear only your own keys instead. Returns a
Promise object.
static **getAllKeys**()
Gets all known keys. Returns a Promise object.
static **getItem**(key: string)
Fetches the value of the given key. Returns a Promise object.
static **mergeItem**(key: string, value: string)
Merges existing value with input value, assuming they are stringified JSON.
Returns a Promise object.
static **multiGet**(keys: Array<string>)
`multiGet` results in an array of key-value pair arrays that matches the input
format of `multiSet`. Returns a Promise object.
```js
multiGet(['k1', 'k2']) -> [['k1', 'val1'], ['k2', 'val2']]
```
static **multiMerge**(keyValuePairs: Array<Array<string>>)
multiMerge takes an array of key-value array pairs that match the output of
`multiGet`. It merges existing values with input values, assuming they are
stringified JSON. Returns a Promise object.
static **multiRemove**(keys: Array<string>)
Delete all the keys in the keys array. Returns a Promise object.
static **multiSet**(keyValuePairs: Array<Array<string>>)
`multiSet` takes an array of key-value array pairs that match the output of
`multiGet`. Returns a Promise object.
```js
multiSet([['k1', 'val1'], ['k2', 'val2']]);
```
static **removeItem**(key: string)
Removes the value of the given key. Returns a Promise object.
static **setItem**(key: string, value: string)
Sets the value of the given key. Returns a Promise object.

View File

@@ -1,16 +0,0 @@
# Clipboard
Clipboard gives you an interface for setting to the clipboard. (Getting
clipboard content is not supported on web.)
## Methods
static **getString**()
Returns a `Promise` of an empty string.
static **setString**(content: string): boolean
Copies a string to the clipboard. On web, some browsers may not support copying
to the clipboard, therefore, this function returns a boolean to indicate if the
copy was successful.

View File

@@ -1,13 +0,0 @@
# Dimensions
Note: dimensions may change (e.g due to device rotation) so any rendering logic
or styles that depend on these constants should try to call this function on
every render, rather than caching the value.
## Methods
static **get**(dimension: string)
Get a dimension (e.g., `"window"` or `"screen"`).
Example: `const { height, width } = Dimensions.get('window')`

View File

@@ -1,25 +0,0 @@
# I18nManager
Control and set the layout and writing direction of the application.
## Properties
**isRTL**: bool = false
Whether the application is currently in RTL mode.
## Methods
static **allowRTL**(allowRTL: bool)
Allow the application to display in RTL mode.
static **forceRTL**(forceRTL: bool)
Force the application to display in RTL mode.
static **setPreferredLanguageRTL**(isRTL: bool)
Set the application's preferred writing direction to RTL. You will need to
determine the user's preferred locale server-side (from HTTP headers) and
decide whether it's an RTL language.

View File

@@ -1,77 +0,0 @@
# NetInfo
`NetInfo` asynchronously determines the online/offline status of the
application.
Connection types:
* `bluetooth` - The user agent is using a Bluetooth connection.
* `cellular` - The user agent is using a cellular connection (e.g., EDGE, HSPA, LTE, etc.).
* `ethernet` - The user agent is using an Ethernet connection.
* `mixed` - The user agent is using multiple connection types.
* `none` - The user agent will not contact the network (offline).
* `other` - The user agent is using a connection type that is not one of enumerated connection types.
* `unknown` - The user agent has established a network connection, but is unable to determine what is the underlying connection technology.
* `wifi` - The user agent is using a Wi-Fi connection.
* `wimax` - The user agent is using a WiMAX connection.
## Methods
Note that support for retrieving the connection type depends upon browswer
support (and is limited to mobile browsers). It will default to `unknown` when
support is missing.
static **addEventListener**(eventName: ChangeEventName, handler: Function)
static **fetch**(): Promise
static **removeEventListener**(eventName: ChangeEventName, handler: Function)
## Properties
**isConnected**: bool = true
Available on all user agents. Asynchronously fetch a boolean to determine
internet connectivity. Use this if you are only interested with whether the device has internet connectivity.
**isConnected.addEventListener**(eventName: ChangeEventName, handler: Function)
**isConnected.fetch**(): Promise
**isConnected.removeEventListener**(eventName: ChangeEventName, handler: Function)
## Examples
Fetching the connection type:
```js
NetInfo.fetch().then((connectionType) => {
console.log('Connection type:', connectionType);
});
```
Subscribing to changes in the connection type:
```js
const handleConnectivityTypeChange = (connectionType) => {
console.log('Current connection type:', connectionType);
}
NetInfo.addEventListener('change', handleConnectivityTypeChange);
```
Fetching the connection status:
```js
NetInfo.isConnected.fetch().then((isConnected) => {
console.log('Connection status:', (isConnected ? 'online' : 'offline'));
});
```
Subscribing to changes in the connection status:
```js
const handleConnectivityStatusChange = (isConnected) => {
console.log('Current connection status:', (isConnected ? 'online' : 'offline'));
}
NetInfo.isConnected.addEventListener('change', handleConnectivityStatusChange);
```

View File

@@ -1,51 +0,0 @@
# PixelRatio
`PixelRatio` gives access to the device pixel density.
## Methods
static **get**()
Returns the device pixel density. Some examples:
* PixelRatio.get() === 1
* mdpi Android devices (160 dpi)
* PixelRatio.get() === 1.5
* hdpi Android devices (240 dpi)
* PixelRatio.get() === 2
* iPhone 4, 4S
* iPhone 5, 5c, 5s
* iPhone 6
* xhdpi Android devices (320 dpi)
* PixelRatio.get() === 3
* iPhone 6 plus
* xxhdpi Android devices (480 dpi)
* PixelRatio.get() === 3.5
* Nexus 6
static **getPixelSizeForLayoutSize**(layoutSize: number)
Converts a layout size (dp) to pixel size (px). Guaranteed to return an integer
number.
static **roundToNearestPixel**(layoutSize: number)
Rounds a layout size (dp) to the nearest layout size that corresponds to an
integer number of pixels. For example, on a device with a PixelRatio of 3,
`PixelRatio.roundToNearestPixel(8.4)` = `8.33`, which corresponds to exactly
`(8.33 * 3)` = `25` pixels.
## Examples
Fetching a correctly sized image. You should get a higher resolution image if
you are on a high pixel density device. A good rule of thumb is to multiply the
size of the image you display by the pixel ratio.
```js
const image = getImage({
width: PixelRatio.getPixelSizeForLayoutSize(200),
height: PixelRatio.getPixelSizeForLayoutSize(100),
});
<Image source={image} style={{ width: 200, height: 100 }} />
```

View File

@@ -1,45 +0,0 @@
# Platform
Detect what is the platform in which the app is running. This piece of
functionality can be useful when only small parts of a component are platform
specific.
## Properties
**OS**: string
`Platform.OS` will be `web` when running in a Web browser.
```js
import { Platform } from 'react-native';
const styles = StyleSheet.create({
height: (Platform.OS === 'web') ? 200 : 100,
});
```
## Methods
**select**(object): any
`Platform.select` takes an object containing `Platform.OS` as keys and returns
the value for the platform you are currently running on.
```js
import { Platform } from 'react-native';
const containerStyles = {
flex: 1,
...Platform.select({
android: {
backgroundColor: 'blue'
},
ios: {
backgroundColor: 'red'
},
web: {
backgroundColor: 'green'
}
})
});
```

View File

@@ -1,80 +0,0 @@
# StyleSheet
The `StyleSheet` abstraction converts predefined styles to (vendor-prefixed)
CSS without requiring a compile-time step. Styles that cannot be resolved
outside of the render loop (e.g., dynamic positioning) are usually applied as
inline styles. Read more about [how to style your
application](../guides/style.md).
## Methods
**create**(obj: {[key: string]: any})
Each key of the object passed to `create` must define a style object.
**flatten**: function
Flattens an array of styles into a single style object.
(web) **getStyleSheets**: function
Returns an array of stylesheets (`{ id, textContent }`). Useful for
compile-time or server-side rendering.
## Properties
**absoluteFill**: number
A very common pattern is to create overlays with position absolute and zero positioning,
so `absoluteFill` can be used for convenience and to reduce duplication of these repeated
styles.
```js
<View style={StyleSheet.absoluteFill} />
```
**absoluteFillObject**: object
Sometimes you may want `absoluteFill` but with a couple tweaks - `absoluteFillObject` can be
used to create a customized entry in a `StyleSheet`, e.g.:
```js
const styles = StyleSheet.create({
wrapper: {
...StyleSheet.absoluteFillObject,
backgroundColor: 'transparent',
top: 10
}
})
```
**hairlineWidth**: number
## Example
```js
<View style={styles.container}>
<Text
children={'Title text'}
style={[
styles.title,
this.props.isActive && styles.activeTitle
]}
/>
</View>
const styles = StyleSheet.create({
container: {
borderRadius: 4,
borderWidth: 0.5,
borderColor: '#d6d7da',
},
title: {
fontSize: 19,
fontWeight: 'bold',
},
activeTitle: {
color: 'red',
}
})
```

View File

@@ -1,35 +0,0 @@
# Vibration
Vibration is described as a pattern of on-off pulses, which may be of varying
lengths. The pattern may consist of either a single integer, describing the
number of milliseconds to vibrate, or an array of integers describing a pattern
of vibrations and pauses. Vibration is controlled with a single method:
`Vibration.vibrate()`.
The vibration is asynchronous so this method will return immediately. There
will be no effect on devices that do not support vibration.
## Methods
static **cancel**()
Stop the vibration.
static **vibrate**(pattern)
Start the vibration pattern.
## Examples
Vibrate once for 200ms:
```js
Vibration.vibrate(200);
Vibration.vibrate([200]);
```
Vibrate for 200ms, pause for 100ms, vibrate for 200ms:
```js
Vibration.vibrate([200, 100, 200]);
```

View File

@@ -1,70 +0,0 @@
# ActivityIndicator
## Props
[...View props](./View.md)
**animating**: boolean = true
Whether to show the indicator or hide it.
**color**: ?color = '#1976D2'
The foreground color of the spinner.
**hidesWhenStopped**: ?boolean = true
Whether the indicator should hide when not animating.
**size**: ?enum('small, 'large') | number = 'small'
Size of the indicator. Small has a height of `20`, large has a height of `36`.
## Examples
```js
import React, { Component } from 'react'
import { ActivityIndicator, StyleSheet, View } from 'react-native'
class ToggleAnimatingActivityIndicator extends Component {
constructor(props) {
super(props)
this.state = { animating: true }
}
componentDidMount: function() {
this.setToggleTimeout();
}
render() {
return (
<ActivityIndicator
animating={this.state.animating}
size="large"
style={[styles.centering, { height: 80 }]}
/>
);
}
_setToggleTimeout() {
setTimeout(() => {
this.setState({ animating: !this.state.animating })
this._setToggleTimeout()
}, 1200)
}
})
const styles = StyleSheet.create({
centering: {
alignItems: 'center',
justifyContent: 'center'
},
gray: {
backgroundColor: '#cccccc'
},
horizontal: {
flexDirection: 'row',
justifyContent: 'space-around'
}
})
```

View File

@@ -1,43 +0,0 @@
# Button
A basic button component. Supports a minimal level of customization. You can
build your own custom button using `TouchableOpacity` or
`TouchableNativeFeedback`.
## Props
**accessibilityLabel**: ?string
Overrides the text that's read by a screen reader when the user interacts
with the element.
**color**: ?string
Background color of the button.
**disabled**: ?boolean
If `true`, disable all interactions for this element.
**onPress**: function
This function is called on press.
**testID**: ?string
Used to locate this view in end-to-end tests.
**title**: string
Text to display inside the button.
## Examples
```js
<Button
accessibilityLabel="Learn more about this purple button"
color="#841584"
onPress={onPressLearnMore}
title="Learn More"
/>
```

View File

@@ -1,167 +0,0 @@
# Image
An accessibile image component with support for image resizing, default image,
and child content.
Unsupported React Native props:
`capInsets` (ios),
`onProgress` (ios)
## Props
**accessibilityLabel**: ?string
The text that's read by a screenreader when someone interacts with the image.
**accessible**: ?boolean
When `true`, indicates the image is an accessibility element.
**children**: ?any
Content to display over the image.
**defaultSource**: ?object
An image to display as a placeholder while downloading the final image off the
network. `{ uri: string, width, height }`
**onError**: ?function
Invoked on load error with `{nativeEvent: {error}}`.
**onLayout**: ?function
Invoked on mount and layout changes with `{ nativeEvent: { layout: { x, y, width,
height } } }`, where `x` and `y` are the offsets from the parent node.
**onLoad**: ?function
Invoked when load completes successfully.
**onLoadEnd**: ?function
Invoked when load either succeeds or fails,
**onLoadStart**: ?function
Invoked on load start.
**resizeMode**: ?enum('center', 'contain', 'cover', 'none', 'repeat', 'stretch') = 'cover'
Determines how to resize the image when the frame doesn't match the raw image
dimensions.
**source**: ?object
`uri` is a string representing the resource identifier for the image, which
could be an http address or a base64 encoded image. `{ uri: string, width, height }`
**style**: ?style
+ ...[View#style](./View.md)
+ `resizeMode`
**testID**: ?string
Used to locate a view in end-to-end tests.
## Properties
static **resizeMode**: object
Example usage:
```
<Image resizeMode={Image.resizeMode.contain} />
```
## Methods
static **getSize**(uri: string, success: (width, height) => {}, failure: function)
Retrieve the width and height (in pixels) of an image prior to displaying it.
This method can fail if the image cannot be found, or fails to download.
(In order to retrieve the image dimensions, the image may first need to be
loaded or downloaded, after which it will be cached. This means that in
principle you could use this method to preload images, however it is not
optimized for that purpose, and may in future be implemented in a way that does
not fully load/download the image data.)
static **prefetch**(url: string): Promise
Prefetches a remote image for later use by downloading it.
## Examples
```js
import placeholderAvatar from './placeholderAvatar.png'
import React, { Component } from 'react'
import { Image, PropTypes, StyleSheet } from 'react-native'
export default class ImageExample extends Component {
constructor(props, context) {
super(props, context)
this.state = { loading: true }
}
static propTypes = {
size: PropTypes.oneOf(['small', 'normal', 'large']),
testID: Image.propTypes.testID,
user: PropTypes.object
}
static defaultProps = {
size: 'normal'
}
_onLoad(e) {
console.log('Avatar.onLoad', e)
this.setState({ loading: false })
}
render() {
const { size, testID, user } = this.props
const loadingStyle = this.state.loading ? { styles.loading } : { }
return (
<Image
accessibilityLabel={`${user.name}'s profile picture`}
defaultSource={{ uri: placeholderAvatar }}
onLoad={this._onLoad.bind(this)}
resizeMode='cover'
source={{ uri: user.avatarUrl }}
style={[
styles.base,
styles[size],
loadingStyle
]}
/>
)
}
}
const styles = StyleSheet.create({
base: {
borderColor: 'white',
borderRadius: 5,
borderWidth: 5
},
loading: {
opacity: 0.5
},
small: {
height: 32,
width: 32
},
normal: {
height: 48,
width: 48
},
large: {
height: 64,
width: 64
}
})
```

View File

@@ -1,27 +0,0 @@
# ProgressBar
Display an activity progress bar.
## Props
[...View props](./View.md)
**color**: ?string = '#1976D2'
Color of the progress bar.
**indeterminate**: ?boolean = true
Whether the progress bar will show indeterminate progress.
**progress**: ?number
The progress value (between 0 and 1).
**testID**: ?string
Used to locate this view in end-to-end tests.
(web) **trackColor**: ?string = 'transparent'
Color of the track bar.

View File

@@ -1,134 +0,0 @@
# ScrollView
A scrollable `View` that provides itegration with the touch-locking "responder"
system. `ScrollView`'s must have a bounded height: either set the height of the
view directly (discouraged) or make sure all parent views have bounded height
(e.g., transfer `{ flex: 1}` down the view stack).
## Props
[...View props](./View.md)
**contentContainerStyle**: ?style
These styles will be applied to the scroll view content container which wraps
all of the child views.
**horizontal**: ?boolean = false
When `true`, the scroll view's children are arranged horizontally in a row
instead of vertically in a column.
**keyboardDismissMode**: ?enum('none', 'on-drag') = 'none'
Determines whether the keyboard gets dismissed in response to a scroll drag.
* `none` (the default), drags do not dismiss the keyboard.
* `on-drag`, the keyboard is dismissed when a drag begins.
* `interactive` (not supported on web; same as `none`)
**onContentSizeChange**: ?function
Called when scrollable content view of the `ScrollView` changes. It's
implemented using the `onLayout` handler attached to the content container
which this `ScrollView` renders.
**onScroll**: ?function
Fires at most once per frame during scrolling. The frequency of the events can
be contolled using the `scrollEventThrottle` prop.
Invoked on scroll with the following event:
```js
{
nativeEvent: {
contentOffset: { x, y },
contentSize: { height, width },
layoutMeasurement: { height, width }
}
}
```
**refreshControl**: ?element
TODO
A [RefreshControl](../RefreshControl) component, used to provide
pull-to-refresh functionality for the `ScrollView`.
**scrollEnabled**: ?boolean = true
When false, the content does not scroll.
**scrollEventThrottle**: ?number = 0
This controls how often the scroll event will be fired while scrolling (as a
time interval in ms). A lower number yields better accuracy for code that is
tracking the scroll position, but can lead to scroll performance problems. The
default value is `0`, which means the scroll event will be sent only once each
time the view is scrolled.
## Instance methods
**getInnerViewNode()**: any
Returns a reference to the underlying content container DOM node within the `ScrollView`.
**getScrollableNode()**: any
Returns a reference to the underlying scrollable DOM node.
**getScrollResponder()**: Component
Returns a reference to the underlying scroll responder, which supports
operations like `scrollTo`. All `ScrollView`-like components should implement
this method so that they can be composed while providing access to the
underlying scroll responder's methods.
**scrollTo(options: { x: number = 0; y: number = 0; animated: boolean = true })**
Scrolls to a given `x`, `y` offset (animation is not currently supported).
## Examples
```js
import React, { Component } from 'react'
import { ScrollView, StyleSheet } from 'react-native'
import Item from './Item'
export default class ScrollViewExample extends Component {
constructor(props, context) {
super(props, context)
this.state = {
items: Array.from(new Array(20)).map((_, i) => ({ id: i }))
}
}
onScroll(e) {
console.log(e)
}
render() {
return (
<ScrollView
children={this.state.items.map((item) => <Item {...item} />)}
contentContainerStyle={styles.container}
horizontal
onScroll={(e) => this.onScroll(e)}
scrollEventThrottle={100}
style={styles.root}
/>
)
}
}
const styles = StyleSheet.create({
root: {
borderWidth: 1
},
container: {
padding: 10
}
})
```

View File

@@ -1,76 +0,0 @@
# Switch
This is a controlled component that requires an `onValueChange` callback that
updates the value prop in order for the component to reflect user actions. If
the `value` prop is not updated, the component will continue to render the
supplied `value` prop instead of the expected result of any user actions.
## Props
[...View props](./View.md)
**disabled**: ?boolean = false
If `true` the user won't be able to interact with the switch.
**onValueChange**: ?function
Invoked with the new value when the value changes.
**value**: ?boolean = false
The value of the switch. If `true` the switch will be turned on.
(web) **activeThumbColor**: ?color = #009688
The color of the thumb grip when the switch is turned on.
(web) **activeTrackColor**: ?color = #A3D3CF
The color of the track when the switch is turned on.
(web) **thumbColor**: ?color = #FAFAFA
The color of the thumb grip when the switch is turned off.
(web) **trackColor**: ?color = #939393
The color of the track when the switch is turned off.
## Examples
```js
import React, { Component } from 'react'
import { Switch, View } from 'react-native'
class ColorSwitchExample extends Component {
constructor(props) {
super(props)
this.state = {
colorTrueSwitchIsOn: true,
colorFalseSwitchIsOn: false
}
}
render() {
return (
<View>
<Switch
activeThumbColor='#428BCA'
activeTrackColor='#A0C4E3'
onValueChange={(value) => this.setState({ colorFalseSwitchIsOn: value })}
value={this.state.colorFalseSwitchIsOn}
/>
<Switch
activeThumbColor='#5CB85C'
activeTrackColor='#ADDAAD'
onValueChange={(value) => this.setState({ colorTrueSwitchIsOn: value })}
thumbColor='#EBA9A7'
trackColor='#D9534F'
value={this.state.colorTrueSwitchIsOn}
/>
</View>
)
}
}
```

View File

@@ -1,161 +0,0 @@
# Text
`Text` is component for displaying text. It supports style, basic touch
handling, and inherits typographic styles from ancestor elements.
The `Text` is unique relative to layout: child elements use text layout
(`inline`) rather than flexbox layout. This means that elements inside of a
`Text` are not rectangles, as they wrap when reaching the edge of their
container.
Unsupported React Native props:
`allowFontScaling` (ios),
`suppressHighlighting` (ios)
## Props
NOTE: `Text` will transfer all other props to the rendered HTML element.
(web) **accessibilityLabel**: ?string
Overrides the text that's read by a screen reader when the user interacts
with the element. (This is implemented using `aria-label`.)
See the [Accessibility guide](../guides/accessibility) for more information.
(web) **accessibilityRole**: ?oneOf(roles)
Allows assistive technologies to present and support interaction with the view
in a manner that is consistent with user expectations for similar views of that
type. For example, marking a touchable view with an `accessibilityRole` of
`button`. (This is implemented using [ARIA roles](http://www.w3.org/TR/wai-aria/roles#role_definitions)).
See the [Accessibility guide](../guides/accessibility) for more information.
**accessible**: ?boolean
When `true`, indicates that the view is an accessibility element (i.e.,
focusable) and groups its child content. By default, all the touchable elements
and elements with `accessibilityRole` of `button` and `link` are accessible.
(This is implemented using `tabindex`.)
See the [Accessibility guide](../guides/accessibility) for more information.
**children**: ?any
Child content.
**importantForAccessibility**: ?enum('auto', 'no-hide-descendants', 'yes')
A value of `no` will remove the element from the tab flow.
A value of `no-hide-descendants` will hide the element and its children from
assistive technologies. (This is implemented using `aria-hidden`.)
See the [Accessibility guide](../guides/accessibility) for more information.
**numberOfLines**: ?number
Truncates the text with an ellipsis after this many lines. Currently only supports `1`.
**onLayout**: ?function
Invoked on mount and layout changes with `{ nativeEvent: { layout: { x, y, width,
height } } }`, where `x` and `y` are the offsets from the parent node.
**onPress**: ?function
This function is called on press.
**selectable**: ?boolean
When `false`, the text is not selectable.
**style**: ?style
+ ...[View#style](View.md)
+ `color`
+ `fontFamily`
+ `fontFeatureSettings`
+ `fontSize`
+ `fontStyle`
+ `fontWeight`
+ `letterSpacing`
+ `lineHeight`
+ `textAlign`
+ `textAlignVertical`
+ `textDecorationLine`
+ `textIndent`
+ `textOverflow`
+ `textRendering`
+ `textShadowColor`
+ `textShadowOffset`
+ `textShadowRadius`
+ `textTransform`
+ `unicodeBidi`
+ `whiteSpace`
+ `wordWrap`
+ `writingDirection`
‡ web only.
**testID**: ?string
Used to locate this view in end-to-end tests.
## Examples
```js
import React, { Component, PropTypes } from 'react'
import { StyleSheet, Text } from 'react-native'
export default class PrettyText extends Component {
static propTypes = {
...Text.propTypes,
color: PropTypes.oneOf(['white', 'gray', 'red']),
size: PropTypes.oneOf(['small', 'normal', 'large']),
weight: PropTypes.oneOf(['light', 'normal', 'bold'])
};
static defaultProps = {
...Text.defaultProps,
color: 'gray',
size: 'normal',
weight: 'normal'
};
render() {
const { color, size, style, weight, ...other } = this.props;
return (
<Text
...other
style={[
style,
colorStyles[color],
sizeStyles[size],
weightStyles[weight]
]}
/>
);
}
}
const colorStyles = StyleSheet.create({
white: { color: 'white' },
gray: { color: 'gray' },
red: { color: 'red' }
})
const sizeStyles = StyleSheet.create({
small: { fontSize: '0.85rem', padding: '0.5rem' },
normal: { fontSize: '1rem', padding: '0.75rem' },
large: { fontSize: '1.5rem', padding: '1rem' }
})
const weightStyles = StyleSheet.create({
light: { fontWeight: '300' },
normal: { fontWeight: '400' },
bold: { fontWeight: '700' }
})
```

View File

@@ -1,226 +0,0 @@
# TextInput
Accessible single- and multi-line text input via a keyboard. Supports features
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:
`onEndEditing`,
`clearButtonMode` (ios),
`enablesReturnKeyAutomatically` (ios),
`placeholderTextColor`,
`returnKeyType` (ios),
`selectionState` (ios),
`underlineColorAndroid` (android)
## Props
[...View props](./View.md)
**autoCapitalize**: ?enum('characters', 'none', 'sentences', 'words') = 'sentences'
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**: ?boolean = true
Automatically correct spelling mistakes (only available in iOS Safari).
**autoFocus**: ?boolean = false
If `true`, focuses the input on `componentDidMount`. Only the first form element
in a document with `autofocus` is focused.
**blurOnSubmit**: ?boolean
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**: ?boolean = false
If `true`, clears the text field automatically when focused.
**defaultValue**: ?string
Provides an initial value that will change when the user starts typing. Useful
for simple use-cases where you don't want to deal with listening to events and
updating the `value` prop to keep the controlled state in sync.
**editable**: ?boolean = true
If `false`, text is not editable (i.e., read-only).
**keyboardType**: enum('default', 'email-address', 'numeric', 'phone-pad', 'search', 'url', 'web-search') = 'default'
Determines which keyboard to open. (NOTE: Safari iOS requires an ancestral
`<form action>` element to display the `search` keyboard).
(Not available when `multiline` is `true`.)
**maxLength**: ?number
Limits the maximum number of characters that can be entered.
**multiline**: ?boolean = false
If true, the text input can be multiple lines.
**numberOfLines**: ?number = 2
Sets the number of lines for a multiline `TextInput`.
(Requires `multiline` to be `true`.)
**onBlur**: ?function
Callback that is called when the text input is blurred.
**onChange**: ?function
Callback that is called when the text input's text changes.
**onChangeText**: ?function
Callback that is called when the text input's text changes. The text is passed
as an argument to the callback handler.
**onFocus**: ?function
Callback that is called when the text input is focused.
**onKeyPress**: ?function
Callback that is called when a key is pressed. This will be called with `{
nativeEvent: { key: keyValue } }` where keyValue is 'Enter` or 'Backspace' for
respective keys and the typed-in character otherwise including ' ' for space.
Modifier keys are also included in the nativeEvent. Fires before onChange
callbacks.
**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.
**secureTextEntry**: ?boolean = false
If true, the text input obscures the text entered so that sensitive text like
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**: ?boolean = false
If `true`, all text will automatically be selected on focus.
**style**: ?style
+ ...[Text#style](./Text.md)
+ `resize` ‡
‡ web only.
**testID**: ?string
Used to locate this view in end-to-end tests.
**value**: ?string
The value to show for the text input. `TextInput` is a controlled component,
which means the native `value` will be forced to match this prop if provided.
Read about how [React form
components](https://facebook.github.io/react/docs/forms.html) work. To prevent
user edits to the value set `editable={false}`.
## Instance methods
**blur()**
Blur the underlying DOM input.
**clear()**
Clear the text from the underlying DOM input.
**focus()**
Focus the underlying DOM input.
**isFocused()**
Returns `true` if the input is currently focused; `false` otherwise.
## Examples
```js
import React, { Component } from 'react'
import { StyleSheet, TextInput } from 'react-native'
export default class TextInputExample extends Component {
constructor(props, context) {
super(props, context)
this.state = { isFocused: false }
}
_onBlur(e) {
this.setState({ isFocused: false })
}
_onFocus(e) {
this.setState({ isFocused: true })
}
render() {
return (
<TextInput
accessibilityLabel='Write a status update'
maxNumberOfLines={5}
multiline
numberOfLines={2}
onBlur={this._onBlur.bind(this)}
onFocus={this._onFocus.bind(this)}
placeholder={`What's happening?`}
style={[
styles.default
this.state.isFocused && styles.focused
]}
/>
);
}
}
const styles = StyleSheet.create({
default: {
borderColor: 'gray',
borderBottomWidth: 2
},
focused: {
borderColor: 'blue'
}
})
```

View File

@@ -1,46 +0,0 @@
# TouchableWithoutFeedback
Do not use unless you have a very good reason. All the elements that respond to
press should have a visual feedback when touched. This is one of the primary
reason a "web" app doesn't feel "native".
**NOTE: `TouchableWithoutFeedback` supports only one child**. If you wish to have
several child components, wrap them in a View.
## Props
[...View props](./View.md)
**delayLongPress**: ?number
Delay in ms, from `onPressIn`, before `onLongPress` is called.
**delayPressIn**: ?number
Delay in ms, from the start of the touch, before `onPressIn` is called.
**delayPressOut**: ?number
Delay in ms, from the release of the touch, before `onPressOut` is called.
**disabled**: ?boolean
If `true`, disable all interactions for this component.
**onLongPress**: ?function
**onPress**: ?function
Called when the touch is released, but not if cancelled (e.g. by a scroll that steals the responder lock).
**onPressIn**: ?function
**onPressOut**: ?function
**pressRetentionOffset**: ?`{top: number, left: number, bottom: number, right: number}`
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.

View File

@@ -1,317 +0,0 @@
# View
`View` is the fundamental UI building block. It is a component that supports
style, layout with flexbox, and accessibility controls. It can be nested
inside another `View` and has 0-to-many children of any type.
Also, refer to React Native's documentation about the [Gesture Responder
System](http://facebook.github.io/react-native/releases/0.22/docs/gesture-responder-system.html).
Unsupported React Native props: `collapsable`, `onAccessibilityTap`, `onMagicTap`.
## Props
NOTE: `View` will transfer all other props to the rendered HTML element.
**accessibilityLabel**: ?string
Overrides the text that's read by a screen reader when the user interacts
with the element. (This is implemented using `aria-label`.)
See the [Accessibility guide](../guides/accessibility.md) for more information.
**accessibilityLiveRegion**: ?enum('assertive', 'none', 'polite')
Indicates to assistive technologies whether to notify the user when the view
changes. The values of this attribute are expressed in degrees of importance.
When regions are specified as `polite` (recommended), updates take low
priority. When regions are specified as `assertive`, assistive technologies
will interrupt and immediately notify the user. (This is implemented using
[`aria-live`](http://www.w3.org/TR/wai-aria/states_and_properties#aria-live).)
See the [Accessibility guide](../guides/accessibility.md) for more information.
(web) **accessibilityRole**: ?enum(roles)
Allows assistive technologies to present and support interaction with the view
in a manner that is consistent with user expectations for similar views of that
type. For example, marking a touchable view with an `accessibilityRole` of
`button`. (This is implemented using [ARIA roles](http://www.w3.org/TR/wai-aria/roles#role_definitions)).
See the [Accessibility guide](../guides/accessibility.md) for more information.
**accessible**: ?boolean
When `true`, indicates that the view is an accessibility element (i.e.,
focusable) and groups its child content. By default, all the touchable elements
and elements with `accessibilityRole` of `button` and `link` are accessible.
(This is implemented using `tabindex`.)
See the [Accessibility guide](../guides/accessibility.md) for more information.
**children**: ?element
Child content.
**hitSlop**: ?object
This defines how far a touch event can start away from the view (in pixels).
Typical interface guidelines recommend touch targets that are at least 30 - 40
points/density-independent pixels.
For example, if a touchable view has a height of `20` the touchable height can
be extended to `40` with `hitSlop={{top: 10, bottom: 10, left: 0, right: 0}}`.
**importantForAccessibility**: ?enum('auto', 'no', 'no-hide-descendants', 'yes')
A value of `no` will remove the element from the tab flow.
A value of `no-hide-descendants` will hide the element and its children from
assistive technologies. (This is implemented using `aria-hidden`.)
See the [Accessibility guide](../guides/accessibility.md) for more information.
**onLayout**: ?function
Invoked on mount and layout changes with `{ nativeEvent: { layout: { x, y, width,
height } } }`, where `x` and `y` are the offsets from the parent node.
**onMoveShouldSetResponder**: ?function => boolean
Does this view want to "claim" touch responsiveness? This is called for every
touch move on the `View` when it is not the responder.
**onMoveShouldSetResponderCapture**: ?function => boolean
If a parent `View` wants to prevent a child `View` from becoming responder on a
move, it should have this handler return `true`.
**onResponderGrant**: ?function
The `View` is now responding to touch events. This is the time to highlight and
show the user what is happening. For most touch interactions, you'll simply
want to wrap your component in `TouchableHighlight` or `TouchableOpacity`.
**onResponderMove**: ?function
The user is moving their finger.
**onResponderReject**: ?function
Another responder is already active and will not release it to the `View`
asking to be the responder.
**onResponderRelease**: ?function
Fired at the end of the touch.
**onResponderTerminate**: ?function
The responder has been taken from the `View`.
**onResponderTerminationRequest**: ?function
Some other `View` wants to become responder and is asking this `View` to
release its responder. Returning `true` allows its release.
**onStartShouldSetResponder**: ?function => boolean
Does this view want to become responder on the start of a touch?
**onStartShouldSetResponderCapture**: ?function => boolean
If a parent `View` wants to prevent a child `View` from becoming the responder
on a touch start, it should have this handler return `true`.
**pointerEvents**: ?enum('auto', 'box-only', 'box-none', 'none') = 'auto'
Controls whether the View can be the target of touch events. The enhanced
`pointerEvents` modes provided are not part of the CSS spec, therefore,
`pointerEvents` is excluded from `style`.
`box-none` is the equivalent of:
```css
.box-none { pointer-events: none !important; }
.box-none > * { pointer-events: auto; }
```
`box-only` is the equivalent of:
```css
.box-only { pointer-events: auto !important; }
.box-only > * { pointer-events: none; }
```
**style**: ?style
+ `alignContent`
+ `alignItems`
+ `alignSelf`
+ `animationDelay`
+ `animationDirection`
+ `animationDuration`
+ `animationFillMode`
+ `animationIterationCount`
+ `animationName`
+ `animationPlayState`
+ `animationTimingFunction`
+ `backfaceVisibility`
+ `backgroundAttachment`
+ `backgroundBlendMode`
+ `backgroundClip`
+ `backgroundColor`
+ `backgroundImage`
+ `backgroundOrigin`
+ `backgroundPosition`
+ `backgroundRepeat`
+ `backgroundSize`
+ `borderColor` (single value)
+ `borderTopColor`
+ `borderBottomColor`
+ `borderRightColor`
+ `borderLeftColor`
+ `borderRadius` (single value)
+ `borderTopLeftRadius`
+ `borderTopRightRadius`
+ `borderBottomLeftRadius`
+ `borderBottomRightRadius`
+ `borderStyle` (single value)
+ `borderTopStyle`
+ `borderRightStyle`
+ `borderBottomStyle`
+ `borderLeftStyle`
+ `borderWidth` (single value)
+ `borderBottomWidth`
+ `borderLeftWidth`
+ `borderRightWidth`
+ `borderTopWidth`
+ `bottom`
+ `boxShadow`
+ `boxSizing`
+ `clip`
+ `cursor`
+ `display`
+ `filter`
+ `flex` (number)
+ `flexBasis`
+ `flexDirection`
+ `flexGrow`
+ `flexShrink`
+ `flexWrap`
+ `gridAutoColumns`
+ `gridAutoFlow`
+ `gridAutoRows`
+ `gridColumnEnd`
+ `gridColumnGap`
+ `gridColumnStart`
+ `gridRowEnd`
+ `gridRowGap`
+ `gridRowStart`
+ `gridTemplateColumns`
+ `gridTemplateRows`
+ `gridTemplateAreas`
+ `height`
+ `justifyContent`
+ `left`
+ `margin` (single value)
+ `marginBottom`
+ `marginHorizontal`
+ `marginLeft`
+ `marginRight`
+ `marginTop`
+ `marginVertical`
+ `maxHeight`
+ `maxWidth`
+ `minHeight`
+ `minWidth`
+ `opacity`
+ `order`
+ `outline`
+ `outlineColor`
+ `overflow`
+ `overflowX`
+ `overflowY`
+ `padding` (single value)
+ `paddingBottom`
+ `paddingHorizontal`
+ `paddingLeft`
+ `paddingRight`
+ `paddingTop`
+ `paddingVertical`
+ `perspective`
+ `perspectiveOrigin`
+ `position`
+ `right`
+ `shadowColor`
+ `shadowOffset`
+ `shadowOpacity`
+ `shadowRadius`
+ `top`
+ `transform`
+ `transformOrigin`
+ `transitionDelay`
+ `transitionDuration`
+ `transitionProperty`
+ `transitionTimingFunction`
+ `userSelect`
+ `visibility`
+ `width`
+ `willChange`
+ `zIndex`
‡ web only.
Default:
```js
{
alignItems: 'stretch',
borderWidth: 0,
borderStyle: 'solid',
boxSizing: 'border-box',
display: 'flex',
flexBasis: 'auto',
flexDirection: 'column',
flexShrink: 0,
margin: 0,
padding: 0,
position: 'relative'
};
```
(See [facebook/yoga](https://github.com/facebook/yoga)).
**testID**: ?string
Used to locate this view in end-to-end tests.
## Examples
```js
import React, { Component, PropTypes } from 'react'
import { StyleSheet, View } from 'react-native'
export default class ViewExample extends Component {
render() {
return (
<View style={styles.row}>
{
['1', '2', '3', '4', '5'].map((value, i) => {
return <View children={value} key={i} style={styles.cell} />
})
}
</View>
);
}
}
const styles = StyleSheet.create({
row: {
flexDirection: 'row'
},
cell: {
flexGrow: 1
}
})
```

View File

@@ -127,6 +127,7 @@ import AppHeader from './src/AppHeader';
import React from 'react';
import ReactNative from 'react-native';
// use .hydrate if hydrating a SSR app
ReactNative.render(<AppHeader />, document.getElementById('react-app-header'))
```

View File

@@ -8,61 +8,94 @@ import UIExplorer, {
Code,
Description,
DocItem,
ExternalLink,
Section,
storiesOf,
TextList
storiesOf
} from '../../ui-explorer';
const NetInfoScreen = () => (
<UIExplorer title="NetInfo" url="2-apis/NetInfo">
<Description>
<AppText>
NetInfo asynchronously determines the online/offline status of the application.
NetInfo asynchronously determines the online/offline status and additional connection
information (where available) of the application.
</AppText>
<AppText>
Note that support for retrieving the connection type depends upon browswer support (and is
limited to mobile browsers). It will default to <Code>unknown</Code> when support is
missing.
Note that connection type information is limited to how well the browser supports the{' '}
<ExternalLink href="https://developer.mozilla.org/en-US/docs/Web/API/NetworkInformation">
NetworkInformation API
</ExternalLink>. Connection types will be <Code>unknown</Code> when support is missing.
</AppText>
</Description>
<Section title="Types">
<DocItem
description={
<AppText>
One of <Code>slow-2g</Code>, <Code>2g</Code>, <Code>3g</Code>, <Code>4g</Code>,{' '}
<Code>unknown</Code>.
</AppText>
}
name="ConnectionType"
/>
<DocItem
description={
<AppText>
One of <Code>bluebooth</Code>, <Code>cellular</Code>, <Code>ethernet</Code>,{' '}
<Code>mixed</Code>, <Code>mixed</Code>, <Code>none</Code>, <Code>other</Code>,{' '}
<Code>unknown</Code>, <Code>wifi</Code>, <Code>wimax</Code>
</AppText>
}
name="EffectiveConnectionType"
/>
<DocItem
description={
<Code>{`{
effectiveType: EffectiveConnectionType;
type: ConnectionType;
downlink?: number;
downlinkMax?: number;
rtt?: number;
}`}</Code>
}
name="ConnectionEventType"
/>
</Section>
<Section title="Methods">
<DocItem
description={[
description={
<AppText>
Invokes the listener whenever network status changes. The listener receives one of the
following connectivity types (from the DOM connection API):
</AppText>,
<TextList
items={[
'bluetooth',
'cellular',
'ethernet',
'mixed',
'none',
'other',
'unknown',
'wifi',
'wimax'
]}
/>
]}
Adds an event handler. The <Code>connectionChange</Code> event fires when the network
status changes. The argument to the event handler is an object of type{' '}
<Code>ConnectionEventType</Code>.
</AppText>
}
example={{
code: "NetInfo.addEventListener('change', (connectionType) => {})"
code: `NetInfo.addEventListener('connectionChange', ({ effectiveType, type }) => {
console.log('Effective connection type:', effectiveType);
console.log('Connection type:', type);
})`
}}
name="static addEventListener"
typeInfo="(eventName, handler) => void"
/>
<DocItem
description="Returns a promise that resolves with one of the connectivity types listed above."
description={
<AppText>
Returns a promise that resolves with an object of type <Code>ConnectionEventType</Code>.
</AppText>
}
example={{
code: `NetInfo.fetch().then((connectionType) => {
console.log('Connection type:', connectionType);
code: `NetInfo.getConnectionInfo().then(({ effectiveType, type }) => {
console.log('Effective connection type:', effectiveType);
console.log('Connection type:', type);
});`
}}
name="static fetch"
typeInfo="() => Promise<string>"
name="static getConnectionInfo"
typeInfo="() => Promise<ConnectionEventType>"
/>
<DocItem
@@ -76,7 +109,7 @@ const NetInfoScreen = () => (
<DocItem
description="An object with the same methods as above but the listener receives a boolean which represents the internet connectivity. Use this if you are only interested with whether the device has internet connectivity."
example={{
code: `NetInfo.isConnected.fetch().then((isConnected) => {
code: `NetInfo.isConnected.getConnectionInfo().then((isConnected) => {
console.log('Connection status:', (isConnected ? 'online' : 'offline'));
});`
}}

View File

@@ -14,7 +14,6 @@
* @providesModule Game2048
* @flow
*/
'use strict';
import { any, func, object } from 'prop-types';
import GameBoard from './GameBoard';

View File

@@ -14,7 +14,6 @@
* @providesModule GameBoard
* @flow
*/
'use strict';
// NB: Taken straight from: https://github.com/IvanVergiliev/2048-react/blob/master/src/board.js
// with no modification except to format it for CommonJS and fix lint/flow errors

View File

@@ -4,16 +4,27 @@
* @flow
*/
import { bool } from 'prop-types';
import React from 'react';
import { StyleSheet, Text } from 'react-native';
const AppText = ({ style, ...rest }) => (
<Text
{...rest}
accessibilityRole={rest.href ? 'link' : undefined}
style={[styles.baseText, style, rest.href && styles.link]}
/>
);
class AppText extends React.PureComponent {
static contextTypes = {
isInAParentText: bool
};
render() {
const { style, ...rest } = this.props;
const isInAParentText = this.context;
return (
<Text
{...rest}
accessibilityRole={rest.href ? 'link' : undefined}
style={[!isInAParentText && styles.baseText, style, rest.href && styles.link]}
/>
);
}
}
export default AppText;

4
jest-setup-framework.js Normal file
View File

@@ -0,0 +1,4 @@
import Enzyme from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
Enzyme.configure({ adapter: new Adapter() });

View File

@@ -1,8 +1,9 @@
{
"name": "react-native-web",
"version": "0.0.123",
"version": "0.1.5",
"description": "React Native for Web",
"main": "dist/index.js",
"module": "dist/module.js",
"files": [
"babel",
"dist",
@@ -11,8 +12,9 @@
],
"scripts": {
"benchmark": "cd benchmarks && yarn && webpack && open index.html",
"build": "webpack --config webpack.config.js --sort-assets-by --progress",
"compile": "del ./dist && mkdir dist && babel src -d dist --ignore **/__tests__",
"build": "yarn compile && webpack --config webpack.config.js --sort-assets-by --progress",
"clean-dist": "del ./dist && mkdir dist",
"compile": "babel src -d dist --ignore *-test.js",
"docs:build": "cd docs && yarn build",
"docs:start": "cd docs && yarn && yarn start",
"docs:release": "cd docs && yarn release",
@@ -24,7 +26,7 @@
"lint": "yarn lint:cmd -- babel benchmarks docs src",
"lint:cmd": "eslint --ignore-path .gitignore --fix",
"precommit": "lint-staged",
"release": "yarn lint && yarn test && yarn compile && yarn build && npm publish",
"release": "yarn clean-dist && yarn lint && yarn test && yarn build && npm publish",
"test": "flow && jest"
},
"babel": {
@@ -43,8 +45,12 @@
"jest": {
"testEnvironment": "jsdom",
"timers": "fake",
"setupFiles": [
"raf/polyfill"
],
"setupTestFrameworkScriptFile": "<rootDir>/jest-setup-framework.js",
"snapshotSerializers": [
"<rootDir>/node_modules/enzyme-to-json/serializer"
"enzyme-to-json/serializer"
]
},
"lint-staged": {
@@ -58,14 +64,14 @@
"animated": "^0.2.0",
"array-find-index": "^1.0.2",
"babel-runtime": "^6.26.0",
"create-react-class": "^15.6.0",
"create-react-class": "^15.6.2",
"debounce": "1.0.2",
"deep-assign": "^2.0.0",
"fbjs": "^0.8.14",
"fbjs": "^0.8.16",
"hyphenate-style-name": "^1.0.2",
"inline-style-prefixer": "^3.0.7",
"inline-style-prefixer": "^3.0.8",
"normalize-css-color": "^1.0.2",
"prop-types": "^15.5.10",
"prop-types": "^15.6.0",
"react-timer-mixin": "^0.13.3"
},
"devDependencies": {
@@ -74,31 +80,33 @@
"babel-eslint": "^7.2.3",
"babel-loader": "^7.1.2",
"babel-plugin-tester": "^4.0.0",
"babel-plugin-transform-react-remove-prop-types": "^0.4.8",
"babel-preset-react-native": "^3.0.2",
"babel-plugin-transform-react-remove-prop-types": "^0.4.9",
"babel-preset-react-native": "^4.0.0",
"caniuse-api": "^2.0.0",
"del-cli": "^1.1.0",
"enzyme": "^2.9.1",
"enzyme-to-json": "^1.5.1",
"enzyme": "^3.0.0",
"enzyme-adapter-react-16": "^1.0.0",
"enzyme-to-json": "^3.0.1",
"eslint": "^4.6.1",
"eslint-config-prettier": "^2.4.0",
"eslint-config-prettier": "^2.6.0",
"eslint-plugin-promise": "^3.5.0",
"eslint-plugin-react": "^7.3.0",
"file-loader": "^0.11.2",
"eslint-plugin-react": "^7.4.0",
"file-loader": "^1.1.4",
"flow-bin": "^0.49.1",
"jest": "^21.0.2",
"jest": "^21.2.1",
"lint-staged": "^4.1.3",
"prettier": "^1.6.1",
"react": "^15.6.1",
"react-dom": "^15.6.1",
"react-test-renderer": "^15.6.1",
"prettier": "^1.7.3",
"raf": "^3.3.2",
"react": "^16.0.0",
"react-dom": "^16.0.0",
"react-test-renderer": "^16.0.0",
"url-loader": "^0.5.9",
"webpack": "^3.5.6",
"webpack": "^3.6.0",
"webpack-bundle-analyzer": "^2.9.0"
},
"peerDependencies": {
"react": "15.4.x || 15.5.x || 15.6.x",
"react-dom": "15.4.x || 15.5.x || 15.6.x"
"react": "16.x.x",
"react-dom": "16.x.x"
},
"author": "Nicolas Gallagher",
"license": "BSD-3-Clause",

View File

@@ -7,7 +7,7 @@ const browserList = {
firefox: 40,
ios_saf: 7,
safari: 7,
ie: 11,
ie: 10,
ie_mob: 11,
edge: 12,
opera: 16,

View File

@@ -38,7 +38,7 @@ exports[`apis/AppRegistry/renderApplication getApplication 3`] = `
.rn-textAlign-1ttztb7{text-align:inherit}
.rn-textDecoration-bauka4{text-decoration:none}
.rn-listStyle-1ebb2ja{list-style:none}
.rn-alignItems-1oszu61{-webkit-align-items:stretch;-webkit-box-align:stretch;align-items:stretch}
.rn-alignItems-1oszu61{-ms-flex-align:stretch;-webkit-align-items:stretch;-webkit-box-align:stretch;align-items:stretch}
.rn-borderTopStyle-1efd50x{border-top-style:solid}
.rn-borderRightStyle-14skgim{border-right-style:solid}
.rn-borderBottomStyle-rull8r{border-bottom-style:solid}
@@ -50,11 +50,10 @@ exports[`apis/AppRegistry/renderApplication getApplication 3`] = `
.rn-boxSizing-deolkf{box-sizing:border-box}
.rn-display-6koalj{display:-webkit-box;display:-moz-box;display:-ms-flexbox;display:-webkit-flex;display:flex}
.rn-display-xoduu5{display:-webkit-inline-box;display:-moz-inline-box;display:-ms-inline-flexbox;display:-webkit-inline-flex;display:inline-flex}
.rn-flexShrink-1qe8dj5{-webkit-flex-shrink:0;flex-shrink:0}
.rn-flexShrink-1wbh5a2{-webkit-flex-shrink:1;flex-shrink:1}
.rn-flexBasis-1mlwlqe{-webkit-flex-basis:auto;flex-basis:auto}
.rn-flexBasis-1ro0kt6{-webkit-flex-basis:0%;flex-basis:0%}
.rn-flexDirection-eqz5dr{-webkit-box-direction:normal;-webkit-box-orient:vertical;-webkit-flex-direction:column;flex-direction:column}
.rn-flexShrink-1pxmb3b{-ms-flex-negative:0 !important;-webkit-flex-shrink:0 !important;flex-shrink:0 !important}
.rn-flexShrink-1awmn5t{-ms-flex-negative:1 !important;-webkit-flex-shrink:1 !important;flex-shrink:1 !important}
.rn-flexBasis-7vfszb{-ms-flex-preferred-size:auto !important;-webkit-flex-basis:auto !important;flex-basis:auto !important}
.rn-flexDirection-eqz5dr{-ms-flex-direction:column;-webkit-box-direction:normal;-webkit-box-orient:vertical;-webkit-flex-direction:column;flex-direction:column}
.rn-marginTop-1mnahxq{margin-top:0px}
.rn-marginRight-61z16t{margin-right:0px}
.rn-marginBottom-p1pxzi{margin-bottom:0px}
@@ -67,5 +66,6 @@ exports[`apis/AppRegistry/renderApplication getApplication 3`] = `
.rn-paddingLeft-gy4na3{padding-left:0px}
.rn-zIndex-1lgpqti{z-index:0}
.rn-zIndex-1wyyakw{z-index:-1}
.rn-flexGrow-16y2uox{-webkit-flex-grow:1;flex-grow:1}</style>"
.rn-flex-13awgt0{-ms-flex:1;-webkit-flex:1;flex:1}
.rn-flexGrow-1m1wadx{-ms-flex-positive:1 !important;-webkit-flex-grow:1 !important;flex-grow:1 !important}</style>"
`;

View File

@@ -12,7 +12,7 @@
function emptyFunction() {}
const BackAndroid = {
const BackHandler = {
exitApp: emptyFunction,
addEventListener() {
return {
@@ -22,4 +22,4 @@ const BackAndroid = {
removeEventListener: emptyFunction
};
export default BackAndroid;
export default BackHandler;

13
src/apis/Easing/index.js Normal file
View File

@@ -0,0 +1,13 @@
/**
* Copyright (c) 2016-present, Nicolas Gallagher.
* 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.
*
* @providesModule Easing
* @noflow
*/
import Easing from 'animated/lib/Easing';
export default Easing;

View File

@@ -13,7 +13,9 @@
import dismissKeyboard from '../../modules/dismissKeyboard';
const Keyboard = {
addListener() {},
addListener() {
return { remove: () => {} };
},
dismiss() {
dismissKeyboard();
},

View File

@@ -3,31 +3,44 @@
import NetInfo from '..';
describe('apis/NetInfo', () => {
describe('getConnectionInfo', () => {
test('fills out basic fields', done => {
NetInfo.getConnectionInfo().then(result => {
expect(result.effectiveType).toBeDefined();
expect(result.type).toBeDefined();
done();
});
});
});
describe('isConnected', () => {
const handler = () => {};
afterEach(() => {
try {
NetInfo.isConnected.removeEventListener('change', handler);
NetInfo.isConnected.removeEventListener('connectionChange', 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();
expect(() =>
NetInfo.isConnected.addEventListener('connectionChange', handler)
).not.toThrow();
});
});
describe('removeEventListener', () => {
test('throws if the handler is not registered', () => {
expect(() => NetInfo.isConnected.removeEventListener('change', handler)).toThrow;
expect(() => NetInfo.isConnected.removeEventListener('connectionChange', handler)).toThrow;
});
test('throws if the provided "eventType" is not supported', () => {
NetInfo.isConnected.addEventListener('change', handler);
NetInfo.isConnected.addEventListener('connectionChange', handler);
expect(() => NetInfo.isConnected.removeEventListener('foo', handler)).toThrow;
expect(() => NetInfo.isConnected.removeEventListener('change', handler)).not.toThrow;
expect(() => NetInfo.isConnected.removeEventListener('connectionChange', handler)).not
.toThrow;
});
});
});

View File

@@ -20,7 +20,27 @@ const connection =
window.navigator.mozConnection ||
window.navigator.webkitConnection);
const eventTypes = ['change'];
// Prevent the underlying event handlers from leaking and include additional
// properties available in browsers
const getConnectionInfoObject = () => {
const result = {};
if (!connection) {
return result;
}
for (const prop in connection) {
if (typeof connection[prop] !== 'function') {
result[prop] = connection[prop];
}
}
return result;
};
// Map React Native events to browser equivalents
const eventTypesMap = {
change: 'change',
connectionChange: 'change'
};
const eventTypes = Object.keys(eventTypesMap);
const connectionListeners = [];
@@ -31,6 +51,9 @@ const connectionListeners = [];
const NetInfo = {
addEventListener(type: string, handler: Function): { remove: () => void } {
invariant(eventTypes.indexOf(type) !== -1, 'Trying to subscribe to unknown event: "%s"', type);
if (type === 'change') {
console.warn('Listening to event `change` is deprecated. Use `connectionChange` instead.');
}
if (!connection) {
console.error(
'Network Connection API is not supported. Not listening for connection type changes.'
@@ -40,21 +63,25 @@ const NetInfo = {
};
}
connection.addEventListener(type, handler);
connection.addEventListener(eventTypesMap[type], handler);
return {
remove: () => NetInfo.removeEventListener(type, handler)
remove: () => NetInfo.removeEventListener(eventTypesMap[type], handler)
};
},
removeEventListener(type: string, handler: Function): void {
invariant(eventTypes.indexOf(type) !== -1, 'Trying to subscribe to unknown event: "%s"', type);
if (type === 'change') {
console.warn('Listening to event `change` is deprecated. Use `connectionChange` instead.');
}
if (!connection) {
return;
}
connection.removeEventListener(type, handler);
connection.removeEventListener(eventTypesMap[type], handler);
},
fetch(): Promise<any> {
console.warn('`fetch` is deprecated. Use `getConnectionInfo` instead.');
return new Promise((resolve, reject) => {
try {
resolve(connection.type);
@@ -64,6 +91,16 @@ const NetInfo = {
});
},
getConnectionInfo(): Promise<Object> {
return new Promise((resolve, reject) => {
resolve({
effectiveType: 'unknown',
type: 'unknown',
...getConnectionInfoObject()
});
});
},
isConnected: {
addEventListener(type: string, handler: Function): { remove: () => void } {
invariant(
@@ -71,6 +108,10 @@ const NetInfo = {
'Trying to subscribe to unknown event: "%s"',
type
);
if (type === 'change') {
console.warn('Listening to event `change` is deprecated. Use `connectionChange` instead.');
}
const onlineCallback = () => handler(true);
const offlineCallback = () => handler(false);
connectionListeners.push([handler, onlineCallback, offlineCallback]);
@@ -79,7 +120,7 @@ const NetInfo = {
window.addEventListener('offline', offlineCallback, false);
return {
remove: () => NetInfo.isConnected.removeEventListener(type, handler)
remove: () => NetInfo.isConnected.removeEventListener(eventTypesMap[type], handler)
};
},
@@ -89,6 +130,9 @@ const NetInfo = {
'Trying to subscribe to unknown event: "%s"',
type
);
if (type === 'change') {
console.warn('Listening to event `change` is deprecated. Use `connectionChange` instead.');
}
const listenerIndex = findIndex(connectionListeners, pair => pair[0] === handler);
invariant(
@@ -103,7 +147,12 @@ const NetInfo = {
connectionListeners.splice(listenerIndex, 1);
},
fetch(): Promise<any> {
fetch(): Promise<boolean> {
console.warn('`fetch` is deprecated. Use `getConnectionInfo` instead.');
return NetInfo.isConnected.getConnectionInfo();
},
getConnectionInfo(): Promise<boolean> {
return new Promise((resolve, reject) => {
try {
resolve(window.navigator.onLine);

View File

@@ -26,44 +26,44 @@ describe('apis/StyleSheet/createReactDOMStyle', () => {
test('flex defaults', () => {
expect(createReactDOMStyle({ display: 'flex' })).toEqual({
display: 'flex',
flexShrink: 0,
flexBasis: 'auto'
flexShrink: '0 !important',
flexBasis: 'auto !important'
});
});
test('flex: -1', () => {
expect(createReactDOMStyle({ display: 'flex', flex: -1 })).toEqual({
display: 'flex',
flexGrow: 0,
flexShrink: 1,
flexBasis: 'auto'
flexGrow: '0 !important',
flexShrink: '1 !important',
flexBasis: 'auto !important'
});
});
test('flex: 0', () => {
expect(createReactDOMStyle({ display: 'flex', flex: 0 })).toEqual({
display: 'flex',
flexGrow: 0,
flexShrink: 0,
flexBasis: 'auto'
flexGrow: '0 !important',
flexShrink: '0 !important',
flexBasis: 'auto !important'
});
});
test('flex: 1', () => {
expect(createReactDOMStyle({ display: 'flex', flex: 1 })).toEqual({
display: 'flex',
flexGrow: 1,
flexShrink: 1,
flexBasis: '0%'
flex: 1,
flexGrow: '1 !important',
flexShrink: '1 !important'
});
});
test('flex: 10', () => {
expect(createReactDOMStyle({ display: 'flex', flex: 10 })).toEqual({
display: 'flex',
flexGrow: 10,
flexShrink: 1,
flexBasis: '0%'
flex: 10,
flexGrow: '10 !important',
flexShrink: '1 !important'
});
});
@@ -71,16 +71,17 @@ describe('apis/StyleSheet/createReactDOMStyle', () => {
// is flex-basis applied?
expect(createReactDOMStyle({ display: 'flex', flexBasis: '25%' })).toEqual({
display: 'flex',
flexShrink: 0,
flexBasis: '25%'
flexShrink: '0 !important',
flexBasis: '25% !important'
});
// can flex-basis override the 'flex' expansion?
expect(createReactDOMStyle({ display: 'flex', flex: 1, flexBasis: '25%' })).toEqual({
display: 'flex',
flexGrow: 1,
flexShrink: 1,
flexBasis: '25%'
flex: 1,
flexGrow: '1 !important',
flexShrink: '1 !important',
flexBasis: '25% !important'
});
});
@@ -88,16 +89,16 @@ describe('apis/StyleSheet/createReactDOMStyle', () => {
// is flex-shrink applied?
expect(createReactDOMStyle({ display: 'flex', flexShrink: 1 })).toEqual({
display: 'flex',
flexShrink: 1,
flexBasis: 'auto'
flexShrink: '1 !important',
flexBasis: 'auto !important'
});
// can flex-shrink override the 'flex' expansion?
expect(createReactDOMStyle({ display: 'flex', flex: 1, flexShrink: 2 })).toEqual({
display: 'flex',
flexGrow: 1,
flexShrink: 2,
flexBasis: '0%'
flex: 1,
flexGrow: '1 !important',
flexShrink: '2 !important'
});
});
});

View File

@@ -158,29 +158,56 @@ const createReducer = (style, styleProps) => {
case 'display': {
resolvedStyle.display = value;
// defaults of 'flexBasis:auto' and 'flexShrink:0' have lowest precedence
if (style.display === 'flex') {
// A flex container in React Native has these defaults which should be
// set only if there is no otherwise supplied flex style.
if (style.display === 'flex' && style.flex == null) {
if (style.flexShrink == null) {
resolvedStyle.flexShrink = 0;
resolvedStyle.flexShrink = '0 !important';
}
if (style.flexBasis == null) {
resolvedStyle.flexBasis = 'auto';
resolvedStyle.flexBasis = 'auto !important';
}
}
break;
}
// The 'flex' property value in React Native must be a positive integer,
// 0, or -1.
//
// On the web, a positive integer value for 'flex' is complicated by
// browser differences. Although browsers render styles like 'flex:2'
// consistently, they don't all set the same value for the resulting
// 'flexBasis' (See #616). Expanding 'flex' in 'StyleSheet' would mean
// setting different values for different browsers.
//
// This fix instead relies on the browser expanding 'flex' itself. And
// because the 'flex' style is not being expanded the generated CSS is
// likely to contain source order "conflicts". To avoid the browser
// relying on source order to resolve the styles, all the longhand flex
// property values must use '!important'.
case 'flex': {
if (value > 0) {
resolvedStyle.flexGrow = value;
resolvedStyle.flexShrink = 1;
resolvedStyle.flexBasis = '0%';
resolvedStyle.flex = value;
resolvedStyle.flexGrow = `${value} !important`;
resolvedStyle.flexShrink = '1 !important';
} else if (value === 0) {
resolvedStyle.flexGrow = 0;
resolvedStyle.flexShrink = 0;
resolvedStyle.flexGrow = '0 !important';
resolvedStyle.flexShrink = '0 !important';
resolvedStyle.flexBasis = 'auto !important';
} else if (value === -1) {
resolvedStyle.flexGrow = 0;
resolvedStyle.flexShrink = 1;
resolvedStyle.flexGrow = '0 !important';
resolvedStyle.flexShrink = '1 !important';
resolvedStyle.flexBasis = 'auto !important';
}
break;
}
case 'flexGrow':
case 'flexShrink':
case 'flexBasis': {
if (value != null) {
const hasImportant = `${value}`.indexOf('!important') > -1;
resolvedStyle[prop] = hasImportant ? value : `${value} !important`;
}
break;
}

View File

@@ -1,18 +1,30 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`components/ActivityIndicator prop "animating" is "false" 1`] = `
<div
<View
accessibilityRole="progressbar"
aria-valuemax="1"
aria-valuemin="0"
class="rn-alignItems-1awozwy rn-borderTopStyle-1efd50x rn-borderRightStyle-14skgim rn-borderBottomStyle-rull8r rn-borderLeftStyle-mm0ijv rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxSizing-deolkf rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-justifyContent-1777fci rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim"
role="progressbar"
style={
Array [
15,
undefined,
]
}
>
<div
class="rn-alignItems-1oszu61 rn-animationDuration-17bb2tj rn-animationIterationCount-1muvv40 rn-animationName-dozj4v rn-animationPlayState-1abnn5w rn-animationTimingFunction-1ldzwu0 rn-borderTopStyle-1efd50x rn-borderRightStyle-14skgim rn-borderBottomStyle-rull8r rn-borderLeftStyle-mm0ijv rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxSizing-deolkf rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-height-z80fyv rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim rn-visibility-11j9u27 rn-width-19wmn03"
<View
style={
Array [
19,
17,
18,
16,
]
}
>
<svg
height="100%"
viewbox="0 0 32 32"
viewBox="0 0 32 32"
width="100%"
>
<circle
@@ -20,35 +32,58 @@ exports[`components/ActivityIndicator prop "animating" is "false" 1`] = `
cy="16"
fill="none"
r="14"
stroke-width="4"
style="stroke:#1976D2;opacity:0.2;"
strokeWidth="4"
style={
Object {
"opacity": 0.2,
"stroke": "#1976D2",
}
}
/>
<circle
cx="16"
cy="16"
fill="none"
r="14"
stroke-width="4"
style="stroke:#1976D2;stroke-dasharray:80;stroke-dashoffset:60;"
strokeWidth="4"
style={
Object {
"stroke": "#1976D2",
"strokeDasharray": 80,
"strokeDashoffset": 60,
}
}
/>
</svg>
</div>
</div>
</View>
</View>
`;
exports[`components/ActivityIndicator prop "animating" is "true" 1`] = `
<div
<View
accessibilityRole="progressbar"
aria-valuemax="1"
aria-valuemin="0"
class="rn-alignItems-1awozwy rn-borderTopStyle-1efd50x rn-borderRightStyle-14skgim rn-borderBottomStyle-rull8r rn-borderLeftStyle-mm0ijv rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxSizing-deolkf rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-justifyContent-1777fci rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim"
role="progressbar"
style={
Array [
15,
undefined,
]
}
>
<div
class="rn-alignItems-1oszu61 rn-animationDuration-17bb2tj rn-animationIterationCount-1muvv40 rn-animationName-dozj4v rn-animationTimingFunction-1ldzwu0 rn-borderTopStyle-1efd50x rn-borderRightStyle-14skgim rn-borderBottomStyle-rull8r rn-borderLeftStyle-mm0ijv rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxSizing-deolkf rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-height-z80fyv rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim rn-width-19wmn03"
<View
style={
Array [
19,
17,
false,
false,
]
}
>
<svg
height="100%"
viewbox="0 0 32 32"
viewBox="0 0 32 32"
width="100%"
>
<circle
@@ -56,71 +91,94 @@ exports[`components/ActivityIndicator prop "animating" is "true" 1`] = `
cy="16"
fill="none"
r="14"
stroke-width="4"
style="stroke:#1976D2;opacity:0.2;"
strokeWidth="4"
style={
Object {
"opacity": 0.2,
"stroke": "#1976D2",
}
}
/>
<circle
cx="16"
cy="16"
fill="none"
r="14"
stroke-width="4"
style="stroke:#1976D2;stroke-dasharray:80;stroke-dashoffset:60;"
strokeWidth="4"
style={
Object {
"stroke": "#1976D2",
"strokeDasharray": 80,
"strokeDashoffset": 60,
}
}
/>
</svg>
</div>
</div>
</View>
</View>
`;
exports[`components/ActivityIndicator prop "color" 1`] = `
<div
aria-valuemax="1"
aria-valuemin="0"
class="rn-alignItems-1awozwy rn-borderTopStyle-1efd50x rn-borderRightStyle-14skgim rn-borderBottomStyle-rull8r rn-borderLeftStyle-mm0ijv rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxSizing-deolkf rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-justifyContent-1777fci rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim"
role="progressbar"
<svg
height="100%"
viewBox="0 0 32 32"
width="100%"
>
<div
class="rn-alignItems-1oszu61 rn-animationDuration-17bb2tj rn-animationIterationCount-1muvv40 rn-animationName-dozj4v rn-animationTimingFunction-1ldzwu0 rn-borderTopStyle-1efd50x rn-borderRightStyle-14skgim rn-borderBottomStyle-rull8r rn-borderLeftStyle-mm0ijv rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxSizing-deolkf rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-height-z80fyv rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim rn-width-19wmn03"
>
<svg
height="100%"
viewbox="0 0 32 32"
width="100%"
>
<circle
cx="16"
cy="16"
fill="none"
r="14"
stroke-width="4"
style="stroke:red;opacity:0.2;"
/>
<circle
cx="16"
cy="16"
fill="none"
r="14"
stroke-width="4"
style="stroke:red;stroke-dasharray:80;stroke-dashoffset:60;"
/>
</svg>
</div>
</div>
<circle
cx="16"
cy="16"
fill="none"
r="14"
strokeWidth="4"
style={
Object {
"opacity": 0.2,
"stroke": "red",
}
}
/>
<circle
cx="16"
cy="16"
fill="none"
r="14"
strokeWidth="4"
style={
Object {
"stroke": "red",
"strokeDasharray": 80,
"strokeDashoffset": 60,
}
}
/>
</svg>
`;
exports[`components/ActivityIndicator prop "hidesWhenStopped" is "false" 1`] = `
<div
<View
accessibilityRole="progressbar"
aria-valuemax="1"
aria-valuemin="0"
class="rn-alignItems-1awozwy rn-borderTopStyle-1efd50x rn-borderRightStyle-14skgim rn-borderBottomStyle-rull8r rn-borderLeftStyle-mm0ijv rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxSizing-deolkf rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-justifyContent-1777fci rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim"
role="progressbar"
style={
Array [
15,
undefined,
]
}
>
<div
class="rn-alignItems-1oszu61 rn-animationDuration-17bb2tj rn-animationIterationCount-1muvv40 rn-animationName-dozj4v rn-animationPlayState-1abnn5w rn-animationTimingFunction-1ldzwu0 rn-borderTopStyle-1efd50x rn-borderRightStyle-14skgim rn-borderBottomStyle-rull8r rn-borderLeftStyle-mm0ijv rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxSizing-deolkf rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-height-z80fyv rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim rn-width-19wmn03"
<View
style={
Array [
19,
17,
18,
false,
]
}
>
<svg
height="100%"
viewbox="0 0 32 32"
viewBox="0 0 32 32"
width="100%"
>
<circle
@@ -128,35 +186,58 @@ exports[`components/ActivityIndicator prop "hidesWhenStopped" is "false" 1`] = `
cy="16"
fill="none"
r="14"
stroke-width="4"
style="stroke:#1976D2;opacity:0.2;"
strokeWidth="4"
style={
Object {
"opacity": 0.2,
"stroke": "#1976D2",
}
}
/>
<circle
cx="16"
cy="16"
fill="none"
r="14"
stroke-width="4"
style="stroke:#1976D2;stroke-dasharray:80;stroke-dashoffset:60;"
strokeWidth="4"
style={
Object {
"stroke": "#1976D2",
"strokeDasharray": 80,
"strokeDashoffset": 60,
}
}
/>
</svg>
</div>
</div>
</View>
</View>
`;
exports[`components/ActivityIndicator prop "hidesWhenStopped" is "true" 1`] = `
<div
<View
accessibilityRole="progressbar"
aria-valuemax="1"
aria-valuemin="0"
class="rn-alignItems-1awozwy rn-borderTopStyle-1efd50x rn-borderRightStyle-14skgim rn-borderBottomStyle-rull8r rn-borderLeftStyle-mm0ijv rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxSizing-deolkf rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-justifyContent-1777fci rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim"
role="progressbar"
style={
Array [
15,
undefined,
]
}
>
<div
class="rn-alignItems-1oszu61 rn-animationDuration-17bb2tj rn-animationIterationCount-1muvv40 rn-animationName-dozj4v rn-animationPlayState-1abnn5w rn-animationTimingFunction-1ldzwu0 rn-borderTopStyle-1efd50x rn-borderRightStyle-14skgim rn-borderBottomStyle-rull8r rn-borderLeftStyle-mm0ijv rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxSizing-deolkf rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-height-z80fyv rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim rn-visibility-11j9u27 rn-width-19wmn03"
<View
style={
Array [
19,
17,
18,
16,
]
}
>
<svg
height="100%"
viewbox="0 0 32 32"
viewBox="0 0 32 32"
width="100%"
>
<circle
@@ -164,35 +245,58 @@ exports[`components/ActivityIndicator prop "hidesWhenStopped" is "true" 1`] = `
cy="16"
fill="none"
r="14"
stroke-width="4"
style="stroke:#1976D2;opacity:0.2;"
strokeWidth="4"
style={
Object {
"opacity": 0.2,
"stroke": "#1976D2",
}
}
/>
<circle
cx="16"
cy="16"
fill="none"
r="14"
stroke-width="4"
style="stroke:#1976D2;stroke-dasharray:80;stroke-dashoffset:60;"
strokeWidth="4"
style={
Object {
"stroke": "#1976D2",
"strokeDasharray": 80,
"strokeDashoffset": 60,
}
}
/>
</svg>
</div>
</div>
</View>
</View>
`;
exports[`components/ActivityIndicator prop "size" is "large" 1`] = `
<div
<View
accessibilityRole="progressbar"
aria-valuemax="1"
aria-valuemin="0"
class="rn-alignItems-1awozwy rn-borderTopStyle-1efd50x rn-borderRightStyle-14skgim rn-borderBottomStyle-rull8r rn-borderLeftStyle-mm0ijv rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxSizing-deolkf rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-justifyContent-1777fci rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim"
role="progressbar"
style={
Array [
15,
undefined,
]
}
>
<div
class="rn-alignItems-1oszu61 rn-animationDuration-17bb2tj rn-animationIterationCount-1muvv40 rn-animationName-dozj4v rn-animationTimingFunction-1ldzwu0 rn-borderTopStyle-1efd50x rn-borderRightStyle-14skgim rn-borderBottomStyle-rull8r rn-borderLeftStyle-mm0ijv rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxSizing-deolkf rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-height-1r8g8re rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim rn-width-1acpoxo"
<View
style={
Array [
20,
17,
false,
false,
]
}
>
<svg
height="100%"
viewbox="0 0 32 32"
viewBox="0 0 32 32"
width="100%"
>
<circle
@@ -200,36 +304,61 @@ exports[`components/ActivityIndicator prop "size" is "large" 1`] = `
cy="16"
fill="none"
r="14"
stroke-width="4"
style="stroke:#1976D2;opacity:0.2;"
strokeWidth="4"
style={
Object {
"opacity": 0.2,
"stroke": "#1976D2",
}
}
/>
<circle
cx="16"
cy="16"
fill="none"
r="14"
stroke-width="4"
style="stroke:#1976D2;stroke-dasharray:80;stroke-dashoffset:60;"
strokeWidth="4"
style={
Object {
"stroke": "#1976D2",
"strokeDasharray": 80,
"strokeDashoffset": 60,
}
}
/>
</svg>
</div>
</div>
</View>
</View>
`;
exports[`components/ActivityIndicator prop "size" is a number 1`] = `
<div
<View
accessibilityRole="progressbar"
aria-valuemax="1"
aria-valuemin="0"
class="rn-alignItems-1awozwy rn-borderTopStyle-1efd50x rn-borderRightStyle-14skgim rn-borderBottomStyle-rull8r rn-borderLeftStyle-mm0ijv rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxSizing-deolkf rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-justifyContent-1777fci rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim"
role="progressbar"
style={
Array [
15,
undefined,
]
}
>
<div
class="rn-alignItems-1oszu61 rn-animationDuration-17bb2tj rn-animationIterationCount-1muvv40 rn-animationName-dozj4v rn-animationTimingFunction-1ldzwu0 rn-borderTopStyle-1efd50x rn-borderRightStyle-14skgim rn-borderBottomStyle-rull8r rn-borderLeftStyle-mm0ijv rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxSizing-deolkf rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim"
style="height:30px;width:30px;"
<View
style={
Array [
Object {
"height": 30,
"width": 30,
},
17,
false,
false,
]
}
>
<svg
height="100%"
viewbox="0 0 32 32"
viewBox="0 0 32 32"
width="100%"
>
<circle
@@ -237,18 +366,29 @@ exports[`components/ActivityIndicator prop "size" is a number 1`] = `
cy="16"
fill="none"
r="14"
stroke-width="4"
style="stroke:#1976D2;opacity:0.2;"
strokeWidth="4"
style={
Object {
"opacity": 0.2,
"stroke": "#1976D2",
}
}
/>
<circle
cx="16"
cy="16"
fill="none"
r="14"
stroke-width="4"
style="stroke:#1976D2;stroke-dasharray:80;stroke-dashoffset:60;"
strokeWidth="4"
style={
Object {
"stroke": "#1976D2",
"strokeDasharray": 80,
"strokeDashoffset": 60,
}
}
/>
</svg>
</div>
</div>
</View>
</View>
`;

View File

@@ -2,46 +2,46 @@
import ActivityIndicator from '..';
import React from 'react';
import { render } from 'enzyme';
import { shallow } from 'enzyme';
describe('components/ActivityIndicator', () => {
describe('prop "animating"', () => {
test('is "true"', () => {
const component = render(<ActivityIndicator animating={true} />);
const component = shallow(<ActivityIndicator animating={true} />);
expect(component).toMatchSnapshot();
});
test('is "false"', () => {
const component = render(<ActivityIndicator animating={false} />);
const component = shallow(<ActivityIndicator animating={false} />);
expect(component).toMatchSnapshot();
});
});
test('prop "color"', () => {
const component = render(<ActivityIndicator color="red" />);
const component = shallow(<ActivityIndicator color="red" />).find('svg');
expect(component).toMatchSnapshot();
});
describe('prop "hidesWhenStopped"', () => {
test('is "true"', () => {
const component = render(<ActivityIndicator animating={false} hidesWhenStopped={true} />);
const component = shallow(<ActivityIndicator animating={false} hidesWhenStopped={true} />);
expect(component).toMatchSnapshot();
});
test('is "false"', () => {
const component = render(<ActivityIndicator animating={false} hidesWhenStopped={false} />);
const component = shallow(<ActivityIndicator animating={false} hidesWhenStopped={false} />);
expect(component).toMatchSnapshot();
});
});
describe('prop "size"', () => {
test('is "large"', () => {
const component = render(<ActivityIndicator size="large" />);
const component = shallow(<ActivityIndicator size="large" />);
expect(component).toMatchSnapshot();
});
test('is a number', () => {
const component = render(<ActivityIndicator size={30} />);
const component = shallow(<ActivityIndicator size={30} />);
expect(component).toMatchSnapshot();
});
});

View File

@@ -1,138 +1,17 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`components/Image passes other props through to underlying View 1`] = `
<div
class="rn-alignItems-1oszu61 rn-backgroundColor-wib322 rn-backgroundPosition-vvn4in rn-backgroundRepeat-u6sd8q rn-backgroundSize-4gszlv rn-borderTopStyle-1efd50x rn-borderRightStyle-14skgim rn-borderBottomStyle-rull8r rn-borderLeftStyle-mm0ijv rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxSizing-deolkf rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim rn-zIndex-1lgpqti"
/>
`;
exports[`components/Image prop "defaultSource" sets background image when value is a string 1`] = `"url(\\"https://google.com/favicon.ico\\")"`;
exports[`components/Image prop "accessibilityLabel" 1`] = `
<div
aria-label="accessibilityLabel"
class="rn-alignItems-1oszu61 rn-backgroundColor-wib322 rn-backgroundPosition-vvn4in rn-backgroundRepeat-u6sd8q rn-backgroundSize-4gszlv rn-borderTopStyle-1efd50x rn-borderRightStyle-14skgim rn-borderBottomStyle-rull8r rn-borderLeftStyle-mm0ijv rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxSizing-deolkf rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim rn-zIndex-1lgpqti"
style="background-image:url(\\"https://google.com/favicon.ico\\");"
>
<img
alt="accessibilityLabel"
class="rn-bottom-1p0dtai rn-height-1pi2tsx rn-left-1d2f490 rn-opacity-1272l3b rn-position-u8s1d rn-right-zchlnj rn-top-ipm5af rn-width-13qz1uu rn-zIndex-1wyyakw"
src="https://google.com/favicon.ico"
/>
</div>
`;
exports[`components/Image prop "defaultSource" sets background image when value is an object 1`] = `"url(\\"https://google.com/favicon.ico\\")"`;
exports[`components/Image prop "accessible" 1`] = `
<div
class="rn-alignItems-1oszu61 rn-backgroundColor-wib322 rn-backgroundPosition-vvn4in rn-backgroundRepeat-u6sd8q rn-backgroundSize-4gszlv rn-borderTopStyle-1efd50x rn-borderRightStyle-14skgim rn-borderBottomStyle-rull8r rn-borderLeftStyle-mm0ijv rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxSizing-deolkf rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim rn-zIndex-1lgpqti"
/>
`;
exports[`components/Image prop "resizeMode" value "contain" 1`] = `"contain"`;
exports[`components/Image prop "children" 1`] = `
<div
class="rn-alignItems-1oszu61 rn-backgroundColor-wib322 rn-backgroundPosition-vvn4in rn-backgroundRepeat-u6sd8q rn-backgroundSize-4gszlv rn-borderTopStyle-1efd50x rn-borderRightStyle-14skgim rn-borderBottomStyle-rull8r rn-borderLeftStyle-mm0ijv rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxSizing-deolkf rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim rn-zIndex-1lgpqti"
>
<div
class="unique"
/>
</div>
`;
exports[`components/Image prop "resizeMode" value "cover" 1`] = `"cover"`;
exports[`components/Image prop "defaultSource" does not override "height" and "width" styles 1`] = `
<div
class="rn-alignItems-1oszu61 rn-backgroundColor-wib322 rn-backgroundPosition-vvn4in rn-backgroundRepeat-u6sd8q rn-backgroundSize-4gszlv rn-borderTopStyle-1efd50x rn-borderRightStyle-14skgim rn-borderBottomStyle-rull8r rn-borderLeftStyle-mm0ijv rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxSizing-deolkf rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim rn-zIndex-1lgpqti"
style="background-image:url(\\"https://google.com/favicon.ico\\");height:20px;width:40px;"
>
<img
alt=""
class="rn-bottom-1p0dtai rn-height-1pi2tsx rn-left-1d2f490 rn-opacity-1272l3b rn-position-u8s1d rn-right-zchlnj rn-top-ipm5af rn-width-13qz1uu rn-zIndex-1wyyakw"
src="https://google.com/favicon.ico"
/>
</div>
`;
exports[`components/Image prop "resizeMode" value "none" 1`] = `"auto"`;
exports[`components/Image prop "defaultSource" sets "height" and "width" styles if missing 1`] = `
<div
class="rn-alignItems-1oszu61 rn-backgroundColor-wib322 rn-backgroundPosition-vvn4in rn-backgroundRepeat-u6sd8q rn-backgroundSize-4gszlv rn-borderTopStyle-1efd50x rn-borderRightStyle-14skgim rn-borderBottomStyle-rull8r rn-borderLeftStyle-mm0ijv rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxSizing-deolkf rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim rn-zIndex-1lgpqti"
style="background-image:url(\\"https://google.com/favicon.ico\\");height:10px;width:20px;"
>
<img
alt=""
class="rn-bottom-1p0dtai rn-height-1pi2tsx rn-left-1d2f490 rn-opacity-1272l3b rn-position-u8s1d rn-right-zchlnj rn-top-ipm5af rn-width-13qz1uu rn-zIndex-1wyyakw"
src="https://google.com/favicon.ico"
/>
</div>
`;
exports[`components/Image prop "resizeMode" value "stretch" 1`] = `"100% 100%"`;
exports[`components/Image prop "defaultSource" sets background image when value is a string 1`] = `
<div
class="rn-alignItems-1oszu61 rn-backgroundColor-wib322 rn-backgroundPosition-vvn4in rn-backgroundRepeat-u6sd8q rn-backgroundSize-4gszlv rn-borderTopStyle-1efd50x rn-borderRightStyle-14skgim rn-borderBottomStyle-rull8r rn-borderLeftStyle-mm0ijv rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxSizing-deolkf rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim rn-zIndex-1lgpqti"
style="background-image:url(\\"https://google.com/favicon.ico\\");"
>
<img
alt=""
class="rn-bottom-1p0dtai rn-height-1pi2tsx rn-left-1d2f490 rn-opacity-1272l3b rn-position-u8s1d rn-right-zchlnj rn-top-ipm5af rn-width-13qz1uu rn-zIndex-1wyyakw"
src="https://google.com/favicon.ico"
/>
</div>
`;
exports[`components/Image prop "resizeMode" value "undefined" 1`] = `"cover"`;
exports[`components/Image prop "defaultSource" sets background image when value is an object 1`] = `
<div
class="rn-alignItems-1oszu61 rn-backgroundColor-wib322 rn-backgroundPosition-vvn4in rn-backgroundRepeat-u6sd8q rn-backgroundSize-4gszlv rn-borderTopStyle-1efd50x rn-borderRightStyle-14skgim rn-borderBottomStyle-rull8r rn-borderLeftStyle-mm0ijv rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxSizing-deolkf rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim rn-zIndex-1lgpqti"
style="background-image:url(\\"https://google.com/favicon.ico\\");"
>
<img
alt=""
class="rn-bottom-1p0dtai rn-height-1pi2tsx rn-left-1d2f490 rn-opacity-1272l3b rn-position-u8s1d rn-right-zchlnj rn-top-ipm5af rn-width-13qz1uu rn-zIndex-1wyyakw"
src="https://google.com/favicon.ico"
/>
</div>
`;
exports[`components/Image prop "resizeMode" value "contain" 1`] = `
<div
class="rn-alignItems-1oszu61 rn-backgroundColor-wib322 rn-backgroundPosition-vvn4in rn-backgroundRepeat-u6sd8q rn-backgroundSize-ehq7j7 rn-borderTopStyle-1efd50x rn-borderRightStyle-14skgim rn-borderBottomStyle-rull8r rn-borderLeftStyle-mm0ijv rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxSizing-deolkf rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim rn-zIndex-1lgpqti"
/>
`;
exports[`components/Image prop "resizeMode" value "cover" 1`] = `
<div
class="rn-alignItems-1oszu61 rn-backgroundColor-wib322 rn-backgroundPosition-vvn4in rn-backgroundRepeat-u6sd8q rn-backgroundSize-4gszlv rn-borderTopStyle-1efd50x rn-borderRightStyle-14skgim rn-borderBottomStyle-rull8r rn-borderLeftStyle-mm0ijv rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxSizing-deolkf rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim rn-zIndex-1lgpqti"
/>
`;
exports[`components/Image prop "resizeMode" value "none" 1`] = `
<div
class="rn-alignItems-1oszu61 rn-backgroundColor-wib322 rn-backgroundPosition-vvn4in rn-backgroundRepeat-u6sd8q rn-backgroundSize-1sxrcry rn-borderTopStyle-1efd50x rn-borderRightStyle-14skgim rn-borderBottomStyle-rull8r rn-borderLeftStyle-mm0ijv rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxSizing-deolkf rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim rn-zIndex-1lgpqti"
/>
`;
exports[`components/Image prop "resizeMode" value "stretch" 1`] = `
<div
class="rn-alignItems-1oszu61 rn-backgroundColor-wib322 rn-backgroundPosition-vvn4in rn-backgroundRepeat-u6sd8q rn-backgroundSize-x3cy2q rn-borderTopStyle-1efd50x rn-borderRightStyle-14skgim rn-borderBottomStyle-rull8r rn-borderLeftStyle-mm0ijv rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxSizing-deolkf rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim rn-zIndex-1lgpqti"
/>
`;
exports[`components/Image prop "resizeMode" value "undefined" 1`] = `
<div
class="rn-alignItems-1oszu61 rn-backgroundColor-wib322 rn-backgroundPosition-vvn4in rn-backgroundRepeat-u6sd8q rn-backgroundSize-4gszlv rn-borderTopStyle-1efd50x rn-borderRightStyle-14skgim rn-borderBottomStyle-rull8r rn-borderLeftStyle-mm0ijv rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxSizing-deolkf rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim rn-zIndex-1lgpqti"
/>
`;
exports[`components/Image prop "style" correctly supports "resizeMode" property 1`] = `
<div
class="rn-alignItems-1oszu61 rn-backgroundColor-wib322 rn-backgroundPosition-vvn4in rn-backgroundRepeat-u6sd8q rn-backgroundSize-ehq7j7 rn-borderTopStyle-1efd50x rn-borderRightStyle-14skgim rn-borderBottomStyle-rull8r rn-borderLeftStyle-mm0ijv rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxSizing-deolkf rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim rn-zIndex-1lgpqti"
/>
`;
exports[`components/Image prop "testID" 1`] = `
<div
class="rn-alignItems-1oszu61 rn-backgroundColor-wib322 rn-backgroundPosition-vvn4in rn-backgroundRepeat-u6sd8q rn-backgroundSize-4gszlv rn-borderTopStyle-1efd50x rn-borderRightStyle-14skgim rn-borderBottomStyle-rull8r rn-borderLeftStyle-mm0ijv rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxSizing-deolkf rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim rn-zIndex-1lgpqti"
data-testid="testID"
/>
`;
exports[`components/Image sets correct accessibility role" 1`] = `
<div
class="rn-alignItems-1oszu61 rn-backgroundColor-wib322 rn-backgroundPosition-vvn4in rn-backgroundRepeat-u6sd8q rn-backgroundSize-4gszlv rn-borderTopStyle-1efd50x rn-borderRightStyle-14skgim rn-borderBottomStyle-rull8r rn-borderLeftStyle-mm0ijv rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxSizing-deolkf rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim rn-zIndex-1lgpqti"
/>
`;
exports[`components/Image prop "style" correctly supports "resizeMode" property 1`] = `"contain"`;

View File

@@ -1,12 +1,10 @@
/* eslint-env jasmine, jest */
import ExecutionEnvironment from 'fbjs/lib/ExecutionEnvironment';
import Image from '../';
import ImageUriCache from '../ImageUriCache';
import React from 'react';
import { mount, render, shallow } from 'enzyme';
import { mount, shallow } from 'enzyme';
const originalCanUseDOM = ExecutionEnvironment.canUseDOM;
const originalImage = window.Image;
describe('components/Image', () => {
@@ -18,42 +16,39 @@ describe('components/Image', () => {
window.Image = originalImage;
});
test('sets correct accessibility role"', () => {
const component = render(<Image />);
expect(component).toMatchSnapshot();
});
test('prop "accessibilityLabel"', () => {
const defaultSource = { uri: 'https://google.com/favicon.ico' };
const component = render(
const component = shallow(
<Image accessibilityLabel="accessibilityLabel" defaultSource={defaultSource} />
);
expect(component).toMatchSnapshot();
const img = component.find('img');
expect(component.prop('accessibilityLabel')).toBe('accessibilityLabel');
expect(img.prop('alt')).toBe('accessibilityLabel');
});
test('prop "accessible"', () => {
const component = render(<Image accessible={false} />);
expect(component).toMatchSnapshot();
const component = shallow(<Image accessible={false} />);
expect(component.prop('accessible')).toBe(false);
});
test('prop "children"', () => {
const children = <div className="unique" />;
const component = render(<Image children={children} />);
expect(component).toMatchSnapshot();
const component = shallow(<Image children={children} />);
expect(component.find('.unique').length).toBe(1);
});
describe('prop "defaultSource"', () => {
test('sets background image when value is an object', () => {
const defaultSource = { uri: 'https://google.com/favicon.ico' };
const component = render(<Image defaultSource={defaultSource} />);
expect(component).toMatchSnapshot();
const component = shallow(<Image defaultSource={defaultSource} />);
expect(component.prop('style').backgroundImage).toMatchSnapshot();
});
test('sets background image when value is a string', () => {
// emulate require-ed asset
const defaultSource = 'https://google.com/favicon.ico';
const component = render(<Image defaultSource={defaultSource} />);
expect(component).toMatchSnapshot();
const component = shallow(<Image defaultSource={defaultSource} />);
expect(component.prop('style').backgroundImage).toMatchSnapshot();
});
test('sets "height" and "width" styles if missing', () => {
@@ -62,8 +57,10 @@ describe('components/Image', () => {
height: 10,
width: 20
};
const component = render(<Image defaultSource={defaultSource} />);
expect(component).toMatchSnapshot();
const component = shallow(<Image defaultSource={defaultSource} />);
const { height, width } = component.prop('style');
expect(height).toBe(10);
expect(width).toBe(20);
});
test('does not override "height" and "width" styles', () => {
@@ -72,10 +69,12 @@ describe('components/Image', () => {
height: 10,
width: 20
};
const component = render(
const component = shallow(
<Image defaultSource={defaultSource} style={{ height: 20, width: 40 }} />
);
expect(component).toMatchSnapshot();
const { height, width } = component.prop('style');
expect(height).toBe(20);
expect(width).toBe(40);
});
});
@@ -96,8 +95,8 @@ describe('components/Image', () => {
undefined
].forEach(resizeMode => {
test(`value "${resizeMode}"`, () => {
const component = render(<Image resizeMode={resizeMode} />);
expect(component).toMatchSnapshot();
const component = shallow(<Image resizeMode={resizeMode} />);
expect(component.prop('style').backgroundSize).toMatchSnapshot();
});
});
});
@@ -106,7 +105,7 @@ describe('components/Image', () => {
test('is not set immediately if the image has not already been loaded', () => {
const uri = 'https://google.com/favicon.ico';
const source = { uri };
const component = render(<Image source={source} />);
const component = shallow(<Image source={source} />);
expect(component.find('img')).toBeUndefined;
});
@@ -136,21 +135,12 @@ describe('components/Image', () => {
.attr('src')
).toBe(uriTwo);
});
test('is set immediately when rendered on the server', () => {
ExecutionEnvironment.canUseDOM = false;
const uri = 'https://google.com/favicon.ico';
const component = render(<Image source={{ uri }} />);
expect(component.find('img').attr('src')).toBe(uri);
expect(ImageUriCache.has(uri)).toBe(true);
ExecutionEnvironment.canUseDOM = originalCanUseDOM;
});
});
describe('prop "style"', () => {
test('correctly supports "resizeMode" property', () => {
const component = render(<Image style={{ resizeMode: Image.resizeMode.contain }} />);
expect(component).toMatchSnapshot();
const component = shallow(<Image style={{ resizeMode: Image.resizeMode.contain }} />);
expect(component.prop('style').backgroundSize).toMatchSnapshot();
});
test('removes other unsupported View styles', () => {
@@ -161,13 +151,13 @@ describe('components/Image', () => {
});
test('prop "testID"', () => {
const component = render(<Image testID="testID" />);
expect(component).toMatchSnapshot();
const component = shallow(<Image testID="testID" />);
expect(component.prop('testID')).toBe('testID');
});
test('passes other props through to underlying View', () => {
const fn = () => {};
const component = render(<Image onResponderGrant={fn} />);
expect(component).toMatchSnapshot();
const component = shallow(<Image onResponderGrant={fn} />);
expect(component.prop('onResponderGrant')).toBe(fn);
});
});

View File

@@ -11,7 +11,6 @@
*/
import applyNativeMethods from '../../modules/applyNativeMethods';
import { canUseDOM } from 'fbjs/lib/ExecutionEnvironment';
import createElement from '../../modules/createElement';
import { getAssetByID } from '../../modules/AssetRegistry';
import ImageLoader from '../../modules/ImageLoader';
@@ -131,7 +130,7 @@ class Image extends Component {
super(props, context);
// If an image has been loaded before, render it immediately
const uri = resolveAssetSource(props.source);
const shouldDisplaySource = ImageUriCache.has(uri) || !canUseDOM;
const shouldDisplaySource = ImageUriCache.has(uri);
this.state = { shouldDisplaySource };
this._imageState = getImageState(uri, shouldDisplaySource);
shouldDisplaySource && ImageUriCache.add(uri);

View File

@@ -1,2 +1,55 @@
import UnimplementedView from '../UnimplementedView';
export default UnimplementedView;
/**
* Copyright (c) 2017-present, Nicolas Gallagher.
* 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.
*
* @providesModule RefreshControl
* @flow
*/
import ColorPropType from '../../propTypes/ColorPropType';
import View from '../View';
import ViewPropTypes from '../View/ViewPropTypes';
import { arrayOf, bool, func, number, oneOf, string } from 'prop-types';
import React, { Component } from 'react';
class RefreshControl extends Component {
static propTypes = {
...ViewPropTypes,
colors: arrayOf(ColorPropType),
enabled: bool,
onRefresh: func,
progressBackgroundColor: ColorPropType,
progressViewOffset: number,
refreshing: bool.isRequired,
size: oneOf([0, 1]),
tintColor: ColorPropType,
title: string,
titleColor: ColorPropType
};
render() {
const {
/* eslint-disable */
colors,
enabled,
onRefresh,
progressBackgroundColor,
progressViewOffset,
refreshing,
size,
tintColor,
title,
titleColor,
/* eslint-enable */
...rest
} = this.props;
return <View {...rest} />;
}
}
export default RefreshControl;

View File

@@ -134,14 +134,39 @@ export default class ScrollViewBase extends Component {
scrollEnabled,
style,
/* eslint-disable */
alwaysBounceHorizontal,
alwaysBounceVertical,
automaticallyAdjustContentInsets,
bounces,
bouncesZoom,
canCancelContentTouches,
centerContent,
contentInset,
contentInsetAdjustmentBehavior,
contentOffset,
decelerationRate,
directionalLockEnabled,
endFillColor,
indicatorStyle,
keyboardShouldPersistTaps,
maximumZoomScale,
minimumZoomScale,
onMomentumScrollBegin,
onMomentumScrollEnd,
onScrollBeginDrag,
onScrollEndDrag,
overScrollMode,
pinchGestureEnabled,
removeClippedSubviews,
scrollEventThrottle,
scrollIndicatorInsets,
scrollPerfTag,
scrollsToTop,
showsHorizontalScrollIndicator,
showsVerticalScrollIndicator,
snapToInterval,
snapToAlignment,
zoomScale,
/* eslint-enable */
...other
} = this.props;

View File

@@ -137,6 +137,7 @@ const ScrollView = createReactClass({
keyboardDismissMode,
onScroll,
pagingEnabled,
stickyHeaderIndices,
/* eslint-enable */
...other
} = this.props;

View File

@@ -1,84 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`components/Switch disabled when "false" a default checkbox is rendered 1`] = `
<div
class="rn-alignItems-1oszu61 rn-borderTopStyle-1efd50x rn-borderRightStyle-14skgim rn-borderBottomStyle-rull8r rn-borderLeftStyle-mm0ijv rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxSizing-deolkf rn-cursor-1loqt21 rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim rn-userSelect-lrvibr"
style="height:20px;width:40px;"
>
<div
class="rn-alignItems-1oszu61 rn-borderTopStyle-1efd50x rn-borderRightStyle-14skgim rn-borderBottomStyle-rull8r rn-borderLeftStyle-mm0ijv rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-bottom-1p0dtai rn-boxSizing-deolkf rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-height-1dernwh rn-left-1d2f490 rn-marginTop-1t01tom rn-marginRight-lchren rn-marginBottom-1qahzrx rn-marginLeft-1jj8364 rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-u8s1d rn-right-zchlnj rn-top-ipm5af rn-transitionDuration-13tjlyg rn-width-13qz1uu"
style="background-color:rgba(147,147,147,1);border-top-left-radius:10px;border-top-right-radius:10px;border-bottom-right-radius:10px;border-bottom-left-radius:10px;"
/>
<div
class="rn-alignItems-1oszu61 rn-alignSelf-k200y rn-borderTopLeftRadius-jt3ufn rn-borderTopRightRadius-1e868j9 rn-borderBottomRightRadius-ujv9e3 rn-borderBottomLeftRadius-1hakmuk rn-borderTopStyle-1efd50x rn-borderRightStyle-14skgim rn-borderBottomStyle-rull8r rn-borderLeftStyle-mm0ijv rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxShadow-1ewcgjf rn-boxSizing-deolkf rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-left-1fe0xdi rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim rn-transform-emqnss rn-transitionDuration-13tjlyg"
style="background-color:rgba(250,250,250,1);height:20px;width:20px;"
/>
<input
class="rn-bottom-1p0dtai rn-cursor-1ei5mc7 rn-height-1pi2tsx rn-left-1d2f490 rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-opacity-1272l3b rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-u8s1d rn-right-zchlnj rn-top-ipm5af rn-width-13qz1uu"
type="checkbox"
/>
</div>
`;
exports[`components/Switch disabled when "true" a disabled checkbox is rendered 1`] = `
<div
class="rn-alignItems-1oszu61 rn-borderTopStyle-1efd50x rn-borderRightStyle-14skgim rn-borderBottomStyle-rull8r rn-borderLeftStyle-mm0ijv rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxSizing-deolkf rn-cursor-7q8q6z rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim rn-userSelect-lrvibr"
style="height:20px;width:40px;"
>
<div
class="rn-alignItems-1oszu61 rn-backgroundColor-1hj8efq rn-borderTopStyle-1efd50x rn-borderRightStyle-14skgim rn-borderBottomStyle-rull8r rn-borderLeftStyle-mm0ijv rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-bottom-1p0dtai rn-boxSizing-deolkf rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-height-1dernwh rn-left-1d2f490 rn-marginTop-1t01tom rn-marginRight-lchren rn-marginBottom-1qahzrx rn-marginLeft-1jj8364 rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-u8s1d rn-right-zchlnj rn-top-ipm5af rn-transitionDuration-13tjlyg rn-width-13qz1uu"
style="border-top-left-radius:10px;border-top-right-radius:10px;border-bottom-right-radius:10px;border-bottom-left-radius:10px;"
/>
<div
class="rn-alignItems-1oszu61 rn-alignSelf-k200y rn-backgroundColor-1bgzomc rn-borderTopLeftRadius-jt3ufn rn-borderTopRightRadius-1e868j9 rn-borderBottomRightRadius-ujv9e3 rn-borderBottomLeftRadius-1hakmuk rn-borderTopStyle-1efd50x rn-borderRightStyle-14skgim rn-borderBottomStyle-rull8r rn-borderLeftStyle-mm0ijv rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxShadow-1ewcgjf rn-boxSizing-deolkf rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-left-1fe0xdi rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim rn-transform-emqnss rn-transitionDuration-13tjlyg"
style="height:20px;width:20px;"
/>
<input
aria-disabled="true"
class="rn-bottom-1p0dtai rn-cursor-1ei5mc7 rn-height-1pi2tsx rn-left-1d2f490 rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-opacity-1272l3b rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-u8s1d rn-right-zchlnj rn-top-ipm5af rn-width-13qz1uu"
disabled=""
type="checkbox"
/>
</div>
`;
exports[`components/Switch value when "false" an unchecked checkbox is rendered 1`] = `
<div
class="rn-alignItems-1oszu61 rn-borderTopStyle-1efd50x rn-borderRightStyle-14skgim rn-borderBottomStyle-rull8r rn-borderLeftStyle-mm0ijv rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxSizing-deolkf rn-cursor-1loqt21 rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim rn-userSelect-lrvibr"
style="height:20px;width:40px;"
>
<div
class="rn-alignItems-1oszu61 rn-borderTopStyle-1efd50x rn-borderRightStyle-14skgim rn-borderBottomStyle-rull8r rn-borderLeftStyle-mm0ijv rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-bottom-1p0dtai rn-boxSizing-deolkf rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-height-1dernwh rn-left-1d2f490 rn-marginTop-1t01tom rn-marginRight-lchren rn-marginBottom-1qahzrx rn-marginLeft-1jj8364 rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-u8s1d rn-right-zchlnj rn-top-ipm5af rn-transitionDuration-13tjlyg rn-width-13qz1uu"
style="background-color:rgba(147,147,147,1);border-top-left-radius:10px;border-top-right-radius:10px;border-bottom-right-radius:10px;border-bottom-left-radius:10px;"
/>
<div
class="rn-alignItems-1oszu61 rn-alignSelf-k200y rn-borderTopLeftRadius-jt3ufn rn-borderTopRightRadius-1e868j9 rn-borderBottomRightRadius-ujv9e3 rn-borderBottomLeftRadius-1hakmuk rn-borderTopStyle-1efd50x rn-borderRightStyle-14skgim rn-borderBottomStyle-rull8r rn-borderLeftStyle-mm0ijv rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxShadow-1ewcgjf rn-boxSizing-deolkf rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-left-1fe0xdi rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim rn-transform-emqnss rn-transitionDuration-13tjlyg"
style="background-color:rgba(250,250,250,1);height:20px;width:20px;"
/>
<input
class="rn-bottom-1p0dtai rn-cursor-1ei5mc7 rn-height-1pi2tsx rn-left-1d2f490 rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-opacity-1272l3b rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-u8s1d rn-right-zchlnj rn-top-ipm5af rn-width-13qz1uu"
type="checkbox"
/>
</div>
`;
exports[`components/Switch value when "true" a checked checkbox is rendered 1`] = `
<div
class="rn-alignItems-1oszu61 rn-borderTopStyle-1efd50x rn-borderRightStyle-14skgim rn-borderBottomStyle-rull8r rn-borderLeftStyle-mm0ijv rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxSizing-deolkf rn-cursor-1loqt21 rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim rn-userSelect-lrvibr"
style="height:20px;width:40px;"
>
<div
class="rn-alignItems-1oszu61 rn-borderTopStyle-1efd50x rn-borderRightStyle-14skgim rn-borderBottomStyle-rull8r rn-borderLeftStyle-mm0ijv rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-bottom-1p0dtai rn-boxSizing-deolkf rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-height-1dernwh rn-left-1d2f490 rn-marginTop-1t01tom rn-marginRight-lchren rn-marginBottom-1qahzrx rn-marginLeft-1jj8364 rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-u8s1d rn-right-zchlnj rn-top-ipm5af rn-transitionDuration-13tjlyg rn-width-13qz1uu"
style="background-color:rgba(163,211,207,1);border-top-left-radius:10px;border-top-right-radius:10px;border-bottom-right-radius:10px;border-bottom-left-radius:10px;"
/>
<div
class="rn-alignItems-1oszu61 rn-alignSelf-k200y rn-borderTopLeftRadius-jt3ufn rn-borderTopRightRadius-1e868j9 rn-borderBottomRightRadius-ujv9e3 rn-borderBottomLeftRadius-1hakmuk rn-borderTopStyle-1efd50x rn-borderRightStyle-14skgim rn-borderBottomStyle-rull8r rn-borderLeftStyle-mm0ijv rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxShadow-1ewcgjf rn-boxSizing-deolkf rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-left-7b7h2f rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim rn-transform-emqnss rn-transitionDuration-13tjlyg"
style="background-color:rgba(0,150,136,1);height:20px;margin-left:-20px;width:20px;"
/>
<input
checked=""
class="rn-bottom-1p0dtai rn-cursor-1ei5mc7 rn-height-1pi2tsx rn-left-1d2f490 rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-opacity-1272l3b rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-u8s1d rn-right-zchlnj rn-top-ipm5af rn-width-13qz1uu"
type="checkbox"
/>
</div>
`;

View File

@@ -1,19 +1,21 @@
/* eslint-env jasmine, jest */
import React from 'react';
import { render } from 'enzyme';
import { shallow } from 'enzyme';
import Switch from '..';
const checkboxSelector = 'input[type="checkbox"]';
describe('components/Switch', () => {
describe('disabled', () => {
test('when "false" a default checkbox is rendered', () => {
const component = render(<Switch />);
expect(component).toMatchSnapshot();
const component = shallow(<Switch />);
expect(component.find(checkboxSelector).prop('disabled')).toBe(false);
});
test('when "true" a disabled checkbox is rendered', () => {
const component = render(<Switch disabled />);
expect(component).toMatchSnapshot();
const component = shallow(<Switch disabled />);
expect(component.find(checkboxSelector).prop('disabled')).toBe(true);
});
});
@@ -35,13 +37,13 @@ describe('components/Switch', () => {
describe('value', () => {
test('when "false" an unchecked checkbox is rendered', () => {
const component = render(<Switch value={false} />);
expect(component).toMatchSnapshot();
const component = shallow(<Switch value={false} />);
expect(component.find(checkboxSelector).prop('checked')).toBe(false);
});
test('when "true" a checked checkbox is rendered', () => {
const component = render(<Switch value />);
expect(component).toMatchSnapshot();
const component = shallow(<Switch value />);
expect(component.find(checkboxSelector).prop('checked')).toBe(true);
});
});
});

View File

@@ -17,7 +17,7 @@ import { any, bool, func, number, oneOf } from 'prop-types';
const TextPropTypes = {
...BaseComponentPropTypes,
accessibilityRole: oneOf(['button', 'heading', 'link', 'listitem']),
accessibilityRole: oneOf(['button', 'heading', 'label', 'link', 'listitem']),
children: any,
numberOfLines: number,
onLayout: func,

View File

@@ -366,12 +366,12 @@ describe('components/TextInput', () => {
);
// default selection is 0
expect(inputDefaultSelection.node.selectionStart).toEqual(0);
expect(inputDefaultSelection.node.selectionEnd).toEqual(0);
expect(inputDefaultSelection.instance().selectionStart).toEqual(0);
expect(inputDefaultSelection.instance().selectionEnd).toEqual(0);
// custom selection sets cursor at custom position
expect(inputCustomSelection.node.selectionStart).toEqual(cursorLocation.start);
expect(inputCustomSelection.node.selectionEnd).toEqual(cursorLocation.end);
expect(inputCustomSelection.instance().selectionStart).toEqual(cursorLocation.start);
expect(inputCustomSelection.instance().selectionEnd).toEqual(cursorLocation.end);
});
});
});

View File

@@ -12,6 +12,7 @@
* @flow
*/
import AccessibilityUtil from '../../modules/AccessibilityUtil';
import BoundingDimensions from './BoundingDimensions';
import findNodeHandle from '../../modules/findNodeHandle';
import normalizeColor from 'normalize-css-color';
@@ -819,7 +820,11 @@ const TouchableMixin = {
}
}
e.stopPropagation();
e.preventDefault();
// prevent the default behaviour unless the Touchable functions as a link
// and Enter is pressed
if (!(which === ENTER && AccessibilityUtil.propsToAriaRole(this.props) === 'link')) {
e.preventDefault();
}
}
}
};

View File

@@ -19,7 +19,7 @@ import StyleSheet from '../../apis/StyleSheet';
import TimerMixin from 'react-timer-mixin';
import Touchable from './Touchable';
import warning from 'fbjs/lib/warning';
import { bool, element, func, number, string } from 'prop-types';
import { bool, func, number, string } from 'prop-types';
type Event = Object;
@@ -33,7 +33,7 @@ const PRESS_RETENTION_OFFSET = { top: 20, left: 20, right: 20, bottom: 30 };
* If you wish to have several child components, wrap them in a View.
*/
/* eslint-disable react/prefer-es6-class */
/* eslint-disable react/prefer-es6-class, react/prop-types */
const TouchableWithoutFeedback = createReactClass({
displayName: 'TouchableWithoutFeedback',
mixins: [TimerMixin, Touchable.Mixin],
@@ -44,7 +44,6 @@ const TouchableWithoutFeedback = createReactClass({
accessibilityRole: BaseComponentPropTypes.accessibilityRole,
accessibilityTraits: BaseComponentPropTypes.accessibilityTraits,
accessible: bool,
children: element,
/**
* Delay in ms, from onPressIn, before onLongPress is called.
*/
@@ -161,6 +160,7 @@ const TouchableWithoutFeedback = createReactClass({
} = this.props;
// Note(avik): remove dynamic typecast once Flow has been upgraded
// $FlowFixMe
const child = React.Children.only(this.props.children);
let children = child.props.children;
warning(

View File

@@ -11,18 +11,13 @@
*/
import View from '../View';
import { any, object } from 'prop-types';
import React, { Component } from 'react';
/**
* Common implementation for a simple stubbed view.
*/
/* eslint-disable react/prop-types */
class UnimplementedView extends Component {
static propTypes = {
children: any,
style: object
};
setNativeProps() {
// Do nothing.
// This method is required in order to use this view as a Touchable* child.

View File

@@ -1,29 +1,16 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`components/View prop "hitSlop" handles partial offsets 1`] = `
<div
class="rn-alignItems-1oszu61 rn-borderTopStyle-1efd50x rn-borderRightStyle-14skgim rn-borderBottomStyle-rull8r rn-borderLeftStyle-mm0ijv rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxSizing-deolkf rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim rn-zIndex-1lgpqti"
>
<span
class="rn-bottom-1p0dtai rn-left-1d2f490 rn-position-u8s1d rn-right-zchlnj rn-zIndex-1wyyakw"
style="top:-10px;"
/>
</div>
Object {
"top": "-10px",
}
`;
exports[`components/View prop "hitSlop" renders a span with negative position offsets 1`] = `
<div
class="rn-alignItems-1oszu61 rn-borderTopStyle-1efd50x rn-borderRightStyle-14skgim rn-borderBottomStyle-rull8r rn-borderLeftStyle-mm0ijv rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxSizing-deolkf rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim rn-zIndex-1lgpqti"
>
<span
class="rn-position-u8s1d rn-zIndex-1wyyakw"
style="bottom:-10px;left:-5px;right:-5px;top:-10px;"
/>
</div>
`;
exports[`components/View prop "pointerEvents" 1`] = `
<div
class="rn-alignItems-1oszu61 rn-borderTopStyle-1efd50x rn-borderRightStyle-14skgim rn-borderBottomStyle-rull8r rn-borderLeftStyle-mm0ijv rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxSizing-deolkf rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-pointerEvents-ah5dr5 rn-position-bnwqim"
/>
Object {
"bottom": "-10px",
"left": "-5px",
"right": "-5px",
"top": "-10px",
}
`;

View File

@@ -1,7 +1,7 @@
/* eslint-env jasmine, jest */
import React from 'react';
import { render, shallow } from 'enzyme';
import { shallow } from 'enzyme';
import View from '../';
describe('components/View', () => {
@@ -12,26 +12,48 @@ describe('components/View', () => {
});
});
test('prop "children"', () => {
const children = <View testID="1" />;
const component = shallow(<View>{children}</View>);
expect(component.contains(children)).toEqual(true);
describe('prop "children"', () => {
test('text node throws error (single)', () => {
const render = () => shallow(<View>'hello'</View>);
expect(render).toThrow();
});
test('text node throws error (array)', () => {
const render = () =>
shallow(
<View>
<View />
'hello'
<View />
</View>
);
expect(render).toThrow();
});
test('non-text is rendered', () => {
const children = <View testID="1" />;
const component = shallow(<View>{children}</View>);
expect(component.contains(children)).toEqual(true);
});
});
describe('prop "hitSlop"', () => {
it('renders a span with negative position offsets', () => {
const component = render(<View hitSlop={{ top: 10, bottom: 10, right: 5, left: 5 }} />);
expect(component).toMatchSnapshot();
const component = shallow(<View hitSlop={{ top: 10, bottom: 10, right: 5, left: 5 }} />);
const span = component.find('span');
expect(span.length).toBe(1);
expect(span.prop('style')).toMatchSnapshot();
});
it('handles partial offsets', () => {
const component = render(<View hitSlop={{ top: 10 }} />);
expect(component).toMatchSnapshot();
const component = shallow(<View hitSlop={{ top: 10 }} />);
const span = component.find('span');
expect(span.prop('style')).toMatchSnapshot();
});
});
test('prop "pointerEvents"', () => {
const component = render(<View pointerEvents="box-only" />);
expect(component).toMatchSnapshot();
const component = shallow(<View pointerEvents="box-only" />);
expect(component.prop('className').indexOf('pointerEvents') > -1).toBe(true);
});
});

View File

@@ -11,6 +11,7 @@ import applyLayout from '../../modules/applyLayout';
import applyNativeMethods from '../../modules/applyNativeMethods';
import { bool } from 'prop-types';
import createElement from '../../modules/createElement';
import invariant from 'fbjs/lib/invariant';
import StyleSheet from '../../apis/StyleSheet';
import ViewPropTypes from './ViewPropTypes';
import React, { Component } from 'react';
@@ -49,6 +50,15 @@ class View extends Component {
...otherProps
} = this.props;
if (process.env.NODE_ENV !== 'production') {
React.Children.toArray(this.props.children).forEach(item => {
invariant(
typeof item !== 'string',
`Unexpected text node: ${item}. A text node cannot be a child of a <View>.`
);
});
}
const { isInAParentText } = this.context;
otherProps.style = [styles.initial, isInAParentText && styles.inline, style];

View File

@@ -1,134 +1,2 @@
import {
// top-level API
findNodeHandle,
render,
unmountComponentAtNode,
// modules
createElement,
NativeModules,
processColor,
// APIs
Animated,
AppRegistry,
AppState,
AsyncStorage,
BackAndroid,
Clipboard,
Dimensions,
Easing,
I18nManager,
InteractionManager,
Keyboard,
Linking,
NetInfo,
PanResponder,
PixelRatio,
Platform,
StyleSheet,
UIManager,
Vibration,
// components
ActivityIndicator,
Button,
FlatList,
Image,
KeyboardAvoidingView,
ListView,
Modal,
Picker,
ProgressBar,
RefreshControl,
ScrollView,
SectionList,
Slider,
StatusBar,
Switch,
Text,
TextInput,
Touchable,
TouchableHighlight,
TouchableNativeFeedback,
TouchableOpacity,
TouchableWithoutFeedback,
View,
VirtualizedList,
// propTypes
ColorPropType,
EdgeInsetsPropType,
PointPropType,
TextPropTypes,
ViewPropTypes
} from './module';
const ReactNative = {
// top-level API
findNodeHandle,
render,
unmountComponentAtNode,
// modules
createElement,
NativeModules,
processColor,
// APIs
Animated,
AppRegistry,
AppState,
AsyncStorage,
BackAndroid,
BackHandler: BackAndroid,
Clipboard,
Dimensions,
Easing,
I18nManager,
InteractionManager,
Keyboard,
Linking,
NetInfo,
PanResponder,
PixelRatio,
Platform,
StyleSheet,
UIManager,
Vibration,
// components
ActivityIndicator,
Button,
FlatList,
Image,
KeyboardAvoidingView,
ListView,
Modal,
Picker,
ProgressBar,
RefreshControl,
ScrollView,
SectionList,
Slider,
StatusBar,
Switch,
Text,
TextInput,
Touchable,
TouchableHighlight,
TouchableNativeFeedback,
TouchableOpacity,
TouchableWithoutFeedback,
View,
VirtualizedList,
// propTypes
ColorPropType,
EdgeInsetsPropType,
PointPropType,
TextPropTypes,
ViewPropTypes
};
import ReactNative from './module';
module.exports = ReactNative;

View File

@@ -1,61 +1,191 @@
export { default as createElement } from './modules/createElement';
export { default as findNodeHandle } from './modules/findNodeHandle';
export { default as NativeModules } from './modules/NativeModules';
export { default as processColor } from './modules/processColor';
export { render, unmountComponentAtNode } from 'react-dom';
import createElement from './modules/createElement';
import findNodeHandle from './modules/findNodeHandle';
import NativeModules from './modules/NativeModules';
import processColor from './modules/processColor';
import { hydrate, render, unmountComponentAtNode } from 'react-dom';
// APIs
export { default as Animated } from './apis/Animated';
export { default as AppRegistry } from './apis/AppRegistry';
export { default as AppState } from './apis/AppState';
export { default as AsyncStorage } from './apis/AsyncStorage';
export { default as BackAndroid } from './apis/BackAndroid';
export { default as Clipboard } from './apis/Clipboard';
export { default as Dimensions } from './apis/Dimensions';
export { default as Easing } from 'animated/lib/Easing';
export { default as I18nManager } from './apis/I18nManager';
export { default as Keyboard } from './apis/Keyboard';
export { default as InteractionManager } from './apis/InteractionManager';
export { default as Linking } from './apis/Linking';
export { default as NetInfo } from './apis/NetInfo';
export { default as PanResponder } from './apis/PanResponder';
export { default as PixelRatio } from './apis/PixelRatio';
export { default as Platform } from './apis/Platform';
export { default as StyleSheet } from './apis/StyleSheet';
export { default as UIManager } from './apis/UIManager';
export { default as Vibration } from './apis/Vibration';
import Animated from './apis/Animated';
import AppRegistry from './apis/AppRegistry';
import AppState from './apis/AppState';
import AsyncStorage from './apis/AsyncStorage';
import BackHandler from './apis/BackHandler';
import Clipboard from './apis/Clipboard';
import Dimensions from './apis/Dimensions';
import Easing from './apis/Easing';
import I18nManager from './apis/I18nManager';
import Keyboard from './apis/Keyboard';
import InteractionManager from './apis/InteractionManager';
import Linking from './apis/Linking';
import NetInfo from './apis/NetInfo';
import PanResponder from './apis/PanResponder';
import PixelRatio from './apis/PixelRatio';
import Platform from './apis/Platform';
import StyleSheet from './apis/StyleSheet';
import UIManager from './apis/UIManager';
import Vibration from './apis/Vibration';
// components
export { default as ActivityIndicator } from './components/ActivityIndicator';
export { default as Button } from './components/Button';
export { default as FlatList } from './components/FlatList';
export { default as Image } from './components/Image';
export { default as KeyboardAvoidingView } from './components/KeyboardAvoidingView';
export { default as ListView } from './components/ListView';
export { default as Modal } from './components/Modal';
export { default as Picker } from './components/Picker';
export { default as ProgressBar } from './components/ProgressBar';
export { default as RefreshControl } from './components/RefreshControl';
export { default as ScrollView } from './components/ScrollView';
export { default as SectionList } from './components/SectionList';
export { default as Slider } from './components/Slider';
export { default as StatusBar } from './components/StatusBar';
export { default as Switch } from './components/Switch';
export { default as Text } from './components/Text';
export { default as TextInput } from './components/TextInput';
export { default as Touchable } from './components/Touchable/Touchable';
export { default as TouchableHighlight } from './components/Touchable/TouchableHighlight';
export { default as TouchableNativeFeedback } from './components/Touchable/TouchableNativeFeedback';
export { default as TouchableOpacity } from './components/Touchable/TouchableOpacity';
export {
default as TouchableWithoutFeedback
} from './components/Touchable/TouchableWithoutFeedback';
export { default as View } from './components/View';
export { default as VirtualizedList } from './components/VirtualizedList';
import ActivityIndicator from './components/ActivityIndicator';
import Button from './components/Button';
import FlatList from './components/FlatList';
import Image from './components/Image';
import KeyboardAvoidingView from './components/KeyboardAvoidingView';
import ListView from './components/ListView';
import Modal from './components/Modal';
import Picker from './components/Picker';
import ProgressBar from './components/ProgressBar';
import RefreshControl from './components/RefreshControl';
import ScrollView from './components/ScrollView';
import SectionList from './components/SectionList';
import Slider from './components/Slider';
import StatusBar from './components/StatusBar';
import Switch from './components/Switch';
import Text from './components/Text';
import TextInput from './components/TextInput';
import Touchable from './components/Touchable/Touchable';
import TouchableHighlight from './components/Touchable/TouchableHighlight';
import TouchableNativeFeedback from './components/Touchable/TouchableNativeFeedback';
import TouchableOpacity from './components/Touchable/TouchableOpacity';
import TouchableWithoutFeedback from './components/Touchable/TouchableWithoutFeedback';
import View from './components/View';
import VirtualizedList from './components/VirtualizedList';
// propTypes
export { default as ColorPropType } from './propTypes/ColorPropType';
export { default as EdgeInsetsPropType } from './propTypes/EdgeInsetsPropType';
export { default as PointPropType } from './propTypes/PointPropType';
export { default as TextPropTypes } from './components/Text/TextPropTypes';
export { default as ViewPropTypes } from './components/View/ViewPropTypes';
import ColorPropType from './propTypes/ColorPropType';
import EdgeInsetsPropType from './propTypes/EdgeInsetsPropType';
import PointPropType from './propTypes/PointPropType';
import TextPropTypes from './components/Text/TextPropTypes';
import ViewPropTypes from './components/View/ViewPropTypes';
export {
// top-level API
findNodeHandle,
hydrate,
render,
unmountComponentAtNode,
// modules
createElement,
NativeModules,
processColor,
// APIs
Animated,
AppRegistry,
AppState,
AsyncStorage,
BackHandler,
Clipboard,
Dimensions,
Easing,
I18nManager,
InteractionManager,
Keyboard,
Linking,
NetInfo,
PanResponder,
PixelRatio,
Platform,
StyleSheet,
UIManager,
Vibration,
// components
ActivityIndicator,
Button,
FlatList,
Image,
KeyboardAvoidingView,
ListView,
Modal,
Picker,
ProgressBar,
RefreshControl,
ScrollView,
SectionList,
Slider,
StatusBar,
Switch,
Text,
TextInput,
Touchable,
TouchableHighlight,
TouchableNativeFeedback,
TouchableOpacity,
TouchableWithoutFeedback,
View,
VirtualizedList,
// propTypes
ColorPropType,
EdgeInsetsPropType,
PointPropType,
TextPropTypes,
ViewPropTypes
};
const ReactNative = {
// top-level API
findNodeHandle,
hydrate,
render,
unmountComponentAtNode,
// modules
createElement,
NativeModules,
processColor,
// APIs
Animated,
AppRegistry,
AppState,
AsyncStorage,
BackHandler,
Clipboard,
Dimensions,
Easing,
I18nManager,
InteractionManager,
Keyboard,
Linking,
NetInfo,
PanResponder,
PixelRatio,
Platform,
StyleSheet,
UIManager,
Vibration,
// components
ActivityIndicator,
Button,
FlatList,
Image,
KeyboardAvoidingView,
ListView,
Modal,
Picker,
ProgressBar,
RefreshControl,
ScrollView,
SectionList,
Slider,
StatusBar,
Switch,
Text,
TextInput,
Touchable,
TouchableHighlight,
TouchableNativeFeedback,
TouchableOpacity,
TouchableWithoutFeedback,
View,
VirtualizedList,
// propTypes
ColorPropType,
EdgeInsetsPropType,
PointPropType,
TextPropTypes,
ViewPropTypes
};
export default ReactNative;

View File

@@ -22,7 +22,7 @@ const ImageLoader = {
getSize(uri, success, failure) {
let complete = false;
const interval = setInterval(callback, 16);
const requestId = ImageLoader.load(uri, callback, callback);
const requestId = ImageLoader.load(uri, callback, errorCallback);
function callback() {
const image = requests[`${requestId}`];
@@ -38,6 +38,14 @@ const ImageLoader = {
clearInterval(interval);
}
}
function errorCallback() {
if (typeof failure === 'function') {
failure();
}
ImageLoader.abort(requestId);
clearInterval(interval);
}
},
load(uri, onLoad, onError): number {
id += 1;

View File

@@ -4,6 +4,9 @@ exports[`modules/createElement it normalizes event.nativeEvent 1`] = `
Object {
"_normalized": true,
"changedTouches": Array [],
"identifier": undefined,
"locationX": undefined,
"locationY": undefined,
"pageX": undefined,
"pageY": undefined,
"preventDefault": [Function],

View File

@@ -19,11 +19,7 @@ describe('modules/createElement', () => {
};
const component = shallow(createElement('span', { onClick }));
component.find('span').simulate('click', {
nativeEvent: {
preventDefault() {},
stopImmediatePropagation() {},
stopPropagation() {}
}
nativeEvent: {}
});
});
@@ -38,11 +34,7 @@ describe('modules/createElement', () => {
);
component.find('span').simulate('keyPress', {
isDefaultPrevented() {},
nativeEvent: {
preventDefault() {},
stopImmediatePropagation() {},
stopPropagation() {}
},
nativeEvent: {},
preventDefault() {},
which
});

View File

@@ -1,9 +1,11 @@
// based on https://github.com/facebook/react/pull/4303/files
import EventPluginHub from 'react-dom/lib/EventPluginHub';
import normalizeNativeEvent from '../normalizeNativeEvent';
import ResponderEventPlugin from 'react-dom/lib/ResponderEventPlugin';
import ResponderTouchHistoryStore from 'react-dom/lib/ResponderTouchHistoryStore';
import ReactDOM from 'react-dom';
import ReactDOMUnstableNativeDependencies from 'react-dom/unstable-native-dependencies';
const { EventPluginHub } = ReactDOM.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
const { ResponderEventPlugin, ResponderTouchHistoryStore } = ReactDOMUnstableNativeDependencies;
const topMouseDown = 'topMouseDown';
const topMouseMove = 'topMouseMove';

View File

@@ -0,0 +1,124 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`modules/normalizeNativeEvent mouse events simulated event 1`] = `
Object {
"_normalized": true,
"changedTouches": Array [
Object {
"_normalized": true,
"clientX": undefined,
"clientY": undefined,
"force": undefined,
"identifier": 0,
"locationX": undefined,
"locationY": undefined,
"pageX": undefined,
"pageY": undefined,
"screenX": undefined,
"screenY": undefined,
"target": undefined,
"timestamp": 1496876171255,
},
],
"identifier": 0,
"locationX": undefined,
"locationY": undefined,
"pageX": undefined,
"pageY": undefined,
"preventDefault": [Function],
"stopImmediatePropagation": [Function],
"stopPropagation": [Function],
"target": undefined,
"timestamp": 1496876171255,
"touches": Array [],
}
`;
exports[`modules/normalizeNativeEvent mouse events synthetic event 1`] = `
Object {
"_normalized": true,
"changedTouches": Array [
Object {
"_normalized": true,
"clientX": 100,
"clientY": 100,
"force": false,
"identifier": 0,
"locationX": 100,
"locationY": 100,
"pageX": 300,
"pageY": 300,
"screenX": 400,
"screenY": 400,
"target": undefined,
"timestamp": 1496876171255,
},
],
"identifier": 0,
"locationX": 200,
"locationY": 200,
"pageX": 300,
"pageY": 300,
"preventDefault": [Function],
"stopImmediatePropagation": [Function],
"stopPropagation": [Function],
"target": undefined,
"timestamp": 1496876171255,
"touches": Array [],
}
`;
exports[`modules/normalizeNativeEvent touch events simulated event 1`] = `
Object {
"_normalized": true,
"changedTouches": Array [],
"identifier": undefined,
"locationX": undefined,
"locationY": undefined,
"pageX": undefined,
"pageY": undefined,
"preventDefault": [Function],
"stopImmediatePropagation": [Function],
"stopPropagation": [Function],
"target": undefined,
"timestamp": 1496876171255,
"touches": Array [],
}
`;
exports[`modules/normalizeNativeEvent touch events synthetic event 1`] = `
Object {
"_normalized": true,
"changedTouches": Array [
Object {
"_normalized": true,
"clientX": 100,
"clientY": 100,
"force": false,
"identifier": undefined,
"locationX": undefined,
"locationY": undefined,
"pageX": 300,
"pageY": 300,
"radiusX": 10,
"radiusY": 10,
"rotationAngle": 45,
"screenX": 400,
"screenY": 400,
"target": undefined,
"timestamp": 1496876171255,
},
],
"identifier": undefined,
"locationX": undefined,
"locationY": undefined,
"pageX": 300,
"pageY": 300,
"preventDefault": [Function],
"stopImmediatePropagation": [Function],
"stopPropagation": [Function],
"target": undefined,
"timestamp": 1496876171255,
"touches": Array [],
}
`;

View File

@@ -0,0 +1,82 @@
/* eslint-env jasmine, jest */
import normalizeNativeEvent from '..';
const normalizeEvent = nativeEvent => {
const result = normalizeNativeEvent(nativeEvent);
result.timestamp = 1496876171255;
if (result.changedTouches && result.changedTouches[0]) {
result.changedTouches[0].timestamp = 1496876171255;
}
if (result.touches && result.touches[0]) {
result.touches[0].timestamp = 1496876171255;
}
return result;
};
describe('modules/normalizeNativeEvent', () => {
describe('mouse events', () => {
test('simulated event', () => {
const nativeEvent = {
type: 'mouseup'
};
const result = normalizeEvent(nativeEvent);
expect(result).toMatchSnapshot();
});
test('synthetic event', () => {
const nativeEvent = {
type: 'mouseup',
clientX: 100,
clientY: 100,
force: false,
offsetX: 200,
offsetY: 200,
pageX: 300,
pageY: 300,
screenX: 400,
screenY: 400
};
const result = normalizeEvent(nativeEvent);
expect(result).toMatchSnapshot();
});
});
describe('touch events', () => {
test('simulated event', () => {
const nativeEvent = {
type: 'touchstart'
};
const result = normalizeEvent(nativeEvent);
expect(result).toMatchSnapshot();
});
test('synthetic event', () => {
const nativeEvent = {
type: 'touchstart',
changedTouches: [
{
clientX: 100,
clientY: 100,
force: false,
pageX: 300,
pageY: 300,
radiusX: 10,
radiusY: 10,
rotationAngle: 45,
screenX: 400,
screenY: 400
}
],
pageX: 300,
pageY: 300
};
const result = normalizeEvent(nativeEvent);
expect(result).toMatchSnapshot();
});
});
});

View File

@@ -5,19 +5,31 @@
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*
* @noflow
* @flow
*/
const emptyArray = [];
const emptyFunction = () => {};
// Mobile Safari re-uses touch objects, so we copy the properties we want and normalize the identifier
const normalizeTouches = (touches = emptyArray) =>
Array.prototype.slice.call(touches).map(touch => {
const identifier = touch.identifier > 20 ? touch.identifier % 20 : touch.identifier;
const normalizeTouches = touches => {
if (!touches) {
return emptyArray;
}
const rect = touch.target && touch.target.getBoundingClientRect();
const locationX = touch.pageX - rect.left;
const locationY = touch.pageY - rect.top;
return Array.prototype.slice.call(touches).map(touch => {
const identifier = touch.identifier > 20 ? touch.identifier % 20 : touch.identifier;
let locationX, locationY;
const node = touch.target;
if (node) {
const isElement = node.nodeType === 1 /* Node.ELEMENT_NODE */;
if (isElement && typeof node.getBoundingClientRect === 'function') {
const rect = node.getBoundingClientRect();
locationX = touch.pageX - rect.left;
locationY = touch.pageY - rect.top;
}
}
return {
_normalized: true,
@@ -40,19 +52,36 @@ const normalizeTouches = (touches = emptyArray) =>
timestamp: Date.now()
};
});
};
function normalizeTouchEvent(nativeEvent) {
const changedTouches = normalizeTouches(nativeEvent.changedTouches);
const touches = normalizeTouches(nativeEvent.touches);
const preventDefault =
typeof nativeEvent.preventDefault === 'function'
? nativeEvent.preventDefault.bind(nativeEvent)
: emptyFunction;
const stopImmediatePropagation =
typeof nativeEvent.stopImmediatePropagation === 'function'
? nativeEvent.stopImmediatePropagation.bind(nativeEvent)
: emptyFunction;
const stopPropagation =
typeof nativeEvent.stopPropagation === 'function'
? nativeEvent.stopPropagation.bind(nativeEvent)
: emptyFunction;
const event = {
_normalized: true,
changedTouches,
identifier: undefined,
locationX: undefined,
locationY: undefined,
pageX: nativeEvent.pageX,
pageY: nativeEvent.pageY,
preventDefault: nativeEvent.preventDefault.bind(nativeEvent),
stopImmediatePropagation: nativeEvent.stopImmediatePropagation.bind(nativeEvent),
stopPropagation: nativeEvent.stopPropagation.bind(nativeEvent),
preventDefault,
stopImmediatePropagation,
stopPropagation,
target: nativeEvent.target,
// normalize the timestamp
// https://stackoverflow.com/questions/26177087/ios-8-mobile-safari-wrong-timestamp-on-touch-events
@@ -89,6 +118,20 @@ function normalizeMouseEvent(nativeEvent) {
timestamp: Date.now()
}
];
const preventDefault =
typeof nativeEvent.preventDefault === 'function'
? nativeEvent.preventDefault.bind(nativeEvent)
: emptyFunction;
const stopImmediatePropagation =
typeof nativeEvent.stopImmediatePropagation === 'function'
? nativeEvent.stopImmediatePropagation.bind(nativeEvent)
: emptyFunction;
const stopPropagation =
typeof nativeEvent.stopPropagation === 'function'
? nativeEvent.stopPropagation.bind(nativeEvent)
: emptyFunction;
return {
_normalized: true,
changedTouches: touches,
@@ -97,22 +140,27 @@ function normalizeMouseEvent(nativeEvent) {
locationY: nativeEvent.offsetY,
pageX: nativeEvent.pageX,
pageY: nativeEvent.pageY,
preventDefault: nativeEvent.preventDefault.bind(nativeEvent),
stopImmediatePropagation: nativeEvent.stopImmediatePropagation.bind(nativeEvent),
stopPropagation: nativeEvent.stopPropagation.bind(nativeEvent),
preventDefault,
stopImmediatePropagation,
stopPropagation,
target: nativeEvent.target,
timestamp: touches[0].timestamp,
touches: nativeEvent.type === 'mouseup' ? emptyArray : touches
};
}
function normalizeNativeEvent(nativeEvent) {
if (nativeEvent._normalized) {
// TODO: how to best handle keyboard events?
function normalizeNativeEvent(nativeEvent: Object) {
if (!nativeEvent || nativeEvent._normalized) {
return nativeEvent;
}
const eventType = nativeEvent.type || '';
const mouse = eventType.indexOf('mouse') >= 0;
return mouse ? normalizeMouseEvent(nativeEvent) : normalizeTouchEvent(nativeEvent);
if (mouse) {
return normalizeMouseEvent(nativeEvent);
} else {
return normalizeTouchEvent(nativeEvent);
}
}
export default normalizeNativeEvent;

View File

@@ -2,6 +2,7 @@ import crossFade from 'inline-style-prefixer/static/plugins/crossFade';
import cursor from 'inline-style-prefixer/static/plugins/cursor';
import filter from 'inline-style-prefixer/static/plugins/filter';
import flex from 'inline-style-prefixer/static/plugins/flex';
import flexboxIE from 'inline-style-prefixer/static/plugins/flexboxIE';
import flexboxOld from 'inline-style-prefixer/static/plugins/flexboxOld';
import gradient from 'inline-style-prefixer/static/plugins/gradient';
import imageSet from 'inline-style-prefixer/static/plugins/imageSet';
@@ -21,6 +22,7 @@ export default {
cursor,
filter,
flex,
flexboxIE,
flexboxOld,
gradient,
imageSet,
@@ -82,13 +84,13 @@ export default {
columns: wm,
columnSpan: wm,
columnWidth: wm,
flex: w,
flex: wms,
flexBasis: w,
flexDirection: w,
flexDirection: wms,
flexGrow: w,
flexFlow: w,
flexFlow: wms,
flexShrink: w,
flexWrap: w,
flexWrap: wms,
alignContent: w,
alignItems: w,
alignSelf: w,
@@ -122,6 +124,7 @@ export default {
wrapFlow: ms,
wrapThrough: ms,
wrapMargin: ms,
touchAction: ms,
gridTemplateColumns: ms,
gridTemplateRows: ms,
gridTemplateAreas: ms,

View File

@@ -10,6 +10,9 @@
* @flow
*/
const isWebColor = (color: string) =>
color === 'currentcolor' || color === 'inherit' || color.indexOf('var(') === 0;
const colorPropType = function(isRequired, props, propName, componentName, location, propFullName) {
const normalizeColor = require('normalize-css-color');
const color = props[propName];
@@ -35,8 +38,8 @@ const colorPropType = function(isRequired, props, propName, componentName, locat
return;
}
// Web supports additional color keywords and custom property values
if (color === 'currentcolor' || color === 'inherit' || color.indexOf('var(') === 0) {
if (typeof color === 'string' && isWebColor(color)) {
// Web supports additional color keywords and custom property values. Ignore them.
return;
}

View File

@@ -11,8 +11,6 @@
* @providesModule warnValidStyle
*/
'use strict';
var emptyFunction = require('fbjs/lib/emptyFunction');
var warnValidStyle = emptyFunction;

674
yarn.lock

File diff suppressed because it is too large Load Diff