Compare commits

...

100 Commits
0.3.3 ... 0.6.0

Author SHA1 Message Date
Nicolas Gallagher
83a8758f68 0.6.0 2018-04-15 15:56:10 -07:00
MoOx
e022d166dd Update Phenomic integration link to official example 2018-04-15 15:26:24 -07:00
Nicolas Gallagher
1a225bc449 [fix] Text onPress event propagation
The press event should not propagate.

Fix #897
2018-04-15 15:23:32 -07:00
Nicolas Gallagher
cf2612663b [fix] createElement uses provided function component
If the component provided to 'createElement' is not a string alias for a
DOM component, it will no longer attempt to replace that component with
a DOM component inferred from the 'accessibility{Component,Role,Traits}'
prop.

Fix #895
2018-04-15 15:03:34 -07:00
Nicolas Gallagher
1aec803086 [fix] Picker.Item support for 'color' prop
Not well supported by browsers.

Fix #810
2018-04-08 11:43:20 -07:00
Nicolas Gallagher
2050730b77 [fix] consistency of nativeEvent.location{X,Y} between touch and mouse
Calculate `location{X,Y}` in the same way for both touch and mouse
events. Also defer the call to `getBoundingClientRect` to avoid
unnecessary DOM queries when the data is not used.
2018-04-08 11:05:12 -07:00
Nicolas Gallagher
a67bf0f490 [change] synchronous layout measurement 2018-04-08 10:38:31 -07:00
Nicolas Gallagher
4529a4ac0a Update benchmarks dependencies 2018-04-07 17:20:01 -07:00
Giuseppe Gurgone
5a04d07a35 [change] call 'onLayout' when elements are resized
Uses ResizeObserver to monitor layout changes. Falls back to
window.onresize (with initial firing).

Fix #60
Close #848
2018-04-07 17:03:17 -07:00
Nicolas Gallagher
9427eea293 [add] VirtualizedList support
Make changes needed to run VirtualizedList using React Native for Web.

Close #659
2018-04-07 10:25:15 -07:00
Nicolas Gallagher
b9803e1e07 Add snapshot of VirtualizedList from React Native
Exact source code of the module and its dependencies, for the given SHA.
2018-04-07 10:25:15 -07:00
slorber
7a3a9a5c3f [add] AppRegistry support for React render callback
Close #858

Co-authored-by: Nicolas Gallagher <nicolasgallagher@gmail.com>
2018-04-07 10:15:17 -07:00
Nicolas Gallagher
4c59343fd3 [fix] SafeAreaView inset padding for Safari
See https://webkit.org/blog/7929/designing-websites-for-iphone-x/

Fix #859
2018-04-02 14:32:15 -07:00
Nicolas Gallagher
ce89b7e3ec [change] move 'react-art' to peerDependencies 2018-04-02 14:31:26 -07:00
Nicolas Gallagher
23fa663a6e [fix] Text font styling
Don't use 'font' shorthand internally to reset font styles, as the
framework's internal styles may be injected after application styles,
causing 'font' to override 'font-*' properties.

Fix #881
2018-04-02 11:37:59 -07:00
Nicolas Gallagher
b96dd668d3 [add] AppRegistry provider methods
Adds support for the following methods in React Native:
- setComponentProviderInstrumentationHook
- setWrapperComponentProvider
2018-04-01 10:29:27 -07:00
Nicolas Gallagher
a9cacb2ef5 [change] AppRegistry container is not absolutely positioned
Aligned with latest layout of AppContainer in React Native.

Fix #871
2018-04-01 09:47:02 -07:00
Nicolas Gallagher
c122814591 0.5.4 2018-04-01 09:34:51 -07:00
Bruno Lemos
efe18f1b7e [fix] Linking.openURL on Firefox
Fix #883
Close #884
2018-04-01 09:25:16 -07:00
Nicolas Gallagher
e1b576e427 [add] StyleSheet support for 'System' in font stack
Allow 'System' to be used in a font-family stack, e.g., "Noto, System".

Update the 'System' font stack to include "system-ui", which is the
latest keyword for system fonts.
2018-03-31 14:57:29 -07:00
Nicolas Gallagher
5d77d6e30f [fix] TextInput focus management
Defer to the browser's native handling of 'blur' and 'focus'; directly
update the internal state of TextInputState. Fixes an issue with
preserving focus when the tab is backgrounded.

Also ensure the TextInputState is correctly set when a component mounts.
When 'autoFocus' is true, 'onFocus' can be called before the internal
ref is set.

Fix #880
2018-03-31 14:01:19 -07:00
Jesús Darío
8fb9a88ee6 Add webpack-cli to getting started guide 2018-03-29 15:49:43 -07:00
Kyle Goggin
a7cda988ef [fix] ignore 'tvParallaxProperties' prop
Close #867
2018-03-29 15:47:39 -07:00
Nicolas Gallagher
b239cfb04d [fix] update InteractionManager stub
Fix #875
2018-03-29 15:32:38 -07:00
Nicolas Gallagher
8e94d858b2 [fix] Picker prop types
Fix #879
2018-03-29 15:11:16 -07:00
Nicolas Gallagher
aa22b06359 0.5.3 2018-03-15 13:32:24 -07:00
Paul Armstrong
2aa565c7c3 [fix] Picker default fontFamily
Close #860
2018-03-15 13:08:23 -07:00
Sakamoto, Kazunori
7b9b57960d Update getting-started.md
Fix __DEV__ value in example web/webpack.config.js

Close #862
2018-03-15 13:06:58 -07:00
Nicolas Gallagher
eae3ee9dca 0.5.2 2018-03-07 19:56:46 -08:00
Nicolas Gallagher
74e1a196b6 [fix] Linking.openURL works with nonce use
`Linking` no longer uses an iframe hack to add 'noopener' support to
older Safari. Instead it relies on browsers having support for
'noopener'.

Fix #837
Close #846
2018-03-05 11:50:08 -08:00
Nicolas Gallagher
48da9814e7 Update benchmark libraries 2018-03-05 10:40:02 -08:00
Nicolas Gallagher
4d391ef57c Link to example integrations in README 2018-03-05 10:22:26 -08:00
Nicolas Gallagher
780df69a80 Update link to Glitch starter 2018-03-01 19:37:12 -08:00
Nicolas Gallagher
6c229da01f Minor improvement to AppRegistry console.log message 2018-02-26 15:20:35 -08:00
Nicolas Gallagher
ae7aa818fb 0.5.1 2018-02-20 17:48:12 -08:00
Nicolas Gallagher
306cf67932 [add] Image style support for animations, interactions, and filters 2018-02-20 17:45:19 -08:00
Nicolas Gallagher
619c2048be [add] support for 'transformStyle' style prop 2018-02-20 17:44:22 -08:00
Nicolas Gallagher
b9f9a4f8d7 Move some View style propTypes to InteractionPropTypes 2018-02-20 17:43:22 -08:00
Nicolas Gallagher
58bc18c2f5 Group 'transition' style propTypes with animations 2018-02-20 17:42:19 -08:00
Nicolas Gallagher
073940fc4e Group 'perspective' style propType with transforms 2018-02-20 17:40:57 -08:00
Nicolas Gallagher
5c462303de Inline some values in TextStylePropTypes 2018-02-20 17:32:45 -08:00
Nicolas Gallagher
5fb92da317 [fix] Switch layout when left/right do not flip
The I18nManager can now disable the automatic BiDi flipping of
left/right, which caused the Switch layout to break in RTL mode. Change
the styles to use start/end.
2018-02-20 16:44:46 -08:00
Nicolas Gallagher
cafe10d851 Improve View 'pointerEvents' example 2018-02-19 18:48:31 -08:00
Nicolas Gallagher
b28581f44e [fix] Touchable 'touchAction' value 2018-02-19 18:27:49 -08:00
Nicolas Gallagher
9333e7e887 0.5.0 2018-02-19 15:48:05 -08:00
Nicolas Gallagher
b28cbbb37e [fix] TextInput default font styles
Match React Native (and web) expected defaults.
2018-02-19 15:29:05 -08:00
Nicolas Gallagher
a53372ceb3 [fix] babel-plugin only rewrites paths for known modules
Don't rewrite import paths for non-existent modules or types. They will
attempt to be imported from the package's main export. This change
currently requires a module map to be generated for the babel-plugin to
use. The map is automatically regenerated for any commit that alters the
entry file of react-native-web.

Fix #822
2018-02-19 13:05:42 -08:00
Nicolas Gallagher
239a43978f Update yarn.lock 2018-02-18 17:43:22 -08:00
Nicolas Gallagher
b4e4bfbb3c Fix the event handlers with event normalization
Events are now normalized within the responder system, so those handlers
don't need to be listed anymore. Recently added events (blur,
contextmenu, focus) are now also normalized.
2018-02-18 17:42:02 -08:00
Nicolas Gallagher
893963a799 [fix] PanResponder grant firing twice on touch devices
Touch events can produce trailing mouse events, which cause unwanted
events to fire in the responder event system.  This issue is avoided in
`Touchable` by cancelling the event in the responder release callback.
To fix the issue in other areas, like the PaneResponder, this hack is
moved into `createElement` and applied to event `onResponderRelease`
callback.

Fix #802
2018-02-18 17:41:48 -08:00
Nicolas Gallagher
e5adc5a37c Add 'coveragePathIgnorePatterns' to jest config 2018-02-17 18:03:36 -08:00
Nicolas Gallagher
6d908189a7 [fix] Text support for 'fontVariant' style
Fix #824
Close #825
2018-02-17 18:03:31 -08:00
Nicolas Gallagher
31db333ba3 [fix] Image support for SVG base64 data
The previous fix to support inline SVG data in utf-8 format broke images
that were rendering base64 SVGs.
2018-02-17 15:55:45 -08:00
Nicolas Gallagher
9fe089ca21 [add] development warning about using '!important' in styles
React Native for Web may use '!important' as part of the internal
resolving of styles. But user styles should never be allowed to include
'!important' in the value. Print a warning to the console when they do.
2018-02-17 14:46:31 -08:00
Nicolas Gallagher
a314d5b2e4 [change] MIT license
Now possible to license under MIT following the change to React Native's
license.

26684cf3ad

Fix #828
2018-02-17 12:23:45 -08:00
Nicolas Gallagher
fb845ebf44 Update benchmarks dependencies 2018-02-16 19:08:19 -08:00
Nicolas Gallagher
0d0c7e6e27 Document 'onBlur', 'onFocus', and 'onContextMenu' props 2018-02-16 18:53:33 -08:00
Nicolas Gallagher
f37003a079 Add end/start to BorderPropTypes 2018-02-15 19:08:34 -08:00
Nicolas Gallagher
f1fc2a9e37 Rewrite I18nManager and i18nStyle unit tests 2018-02-15 18:46:08 -08:00
Nicolas Gallagher
92794cdc9f [add] I18nManager and StyleSheet support for RTL without left/right flip
I18nManager supports `doLeftAndRightSwapInRTL` and
`swapLeftAndRightInRTL` to query and control the BiDi-flipping of
left/right properties and values. For example, you may choose to use
`end`/`start` for positioning that flips with writing direction, and
then disable `left`/`right` swapping in RTL so that `left` will always
be `left`.

The StyleSheet resolver cache must also account for the third "direction"
variant: RTL with no swapping of left/right.
2018-02-15 18:45:45 -08:00
Nicolas Gallagher
b754776373 [add] StyleSheet support for start/end properties and values
Add support for new style properties and values that automatically
account for the writing direction (as introduced in React Native
0.51.0). The start/end variants are automatically resolved to match the
global writing direction, as defined by I18nManager.isRTL. Start/End
take precedence over Left/Right.

Adds support for the following properties:

* `borderTop{End,Start}Radius`
* `borderBottom{End,Start}Radius`
* `border{End,Start}Color`
* `border{End,Start}Style`
* `border{End,Start}Width`
* `end`
* `margin{End,Start}`
* `padding{End,Start}`
* `start`

And values:

* `clear: "end" | "start"`
* `float: "end" | "start"`
* `textAlign: "end" | "start"`
2018-02-15 18:43:09 -08:00
Nicolas Gallagher
155b34e495 Fix unit test timeouts on Travis CI
Jest recommends using 'runInBand' for Travis CI. It runs all tests
serially in the current process, rather than creating a worker pool of
child processes that run tests.
2018-02-14 14:26:03 -08:00
Nicolas Gallagher
00c9dc4236 [fix] setNativeProps can apply flex styles
Updates the 'setValueForStyle' implementation to support style values
that contain "!important". This allows the 'flex{Basis,Grow,Shrink}'
values created by the style resolver to be applied. They currently use
the important priority as a work-around for browser-inconsistencies in
the 'flex' shorthand.

Upstream fix: https://github.com/facebook/react/pull/12181

Ref #798
Close #813
2018-02-14 13:51:00 -08:00
Nicolas Gallagher
b66aba1a06 website: fix ImageBackground source code link
Close #820
2018-02-14 13:39:14 -08:00
Nicolas Gallagher
17f8a674b8 [fix] ResponderEventPlugin skips 'mouseup' when no touch is active
Prevent the responder system recording 'mouseup' events if there is no
active 'touch'.

Fix #816
2018-02-13 15:02:09 -08:00
Nicolas Gallagher
b8080ba775 [fix] Touchable press events regression in 9ee89bc
Fix a regression introduced by "ResponderEvent event filtering"
9ee89bc7f7. Touchable press events were
firing twice after the event normalization was moved up into
'extractEvents'. The reason is:

1) events were previously not normalized throughout the responder system
2) once they were normalized, the fix introduced by a535c558d8 stopped working
3) because normalized nativeEvent did not include event data like 'cancelable'.

This patch adds more of the original nativeEvent fields to the
normalized event, so that React's synthetic event can return the correct
information for 'bubbles', 'cancelable', 'isDefaultPrevented()', etc.
2018-02-13 13:34:17 -08:00
Nicolas Gallagher
7265736545 [fix] StyleSheet accepts 'space-evenly' value for 'justifyContent' 2018-02-12 10:35:30 -08:00
Bulat Kidasov
399f465e59 [fix] NativeMethodsMixin measure/measureInWindow with scroll
Account for scroll offsets when calculating measurements.

Fix #702
Fix #805
Close #806
2018-02-12 10:27:47 -08:00
Nicolas Gallagher
9ee89bc7f7 [fix] ResponderEventPlugin event filtering
Exclude middle, wheel, and right click mouse events from the responder
system. This fixes the Touchables incorrectly triggering 'onPress' in
response to these events.

Filter mousemove events in the 'extractEvents' methods, and check for
active touches rather than the length of the touch bank. This fixes the
PanResponder not functioning after the first touch in Firefox.

Fix #719
Fix #729
Close #804
2018-02-12 10:08:44 -08:00
Jakub Rimal
748b2d0f3f [fix] Image 'source' prop update when missing in initial render
Close #811
2018-02-12 08:27:48 -08:00
Nicolas Gallagher
fb4635e013 Update README's 2018-02-08 16:09:56 -08:00
Nicolas Gallagher
73b459e770 [add] TextInput onKeyPress support for arrow keys
Close #791
Close #792
2018-02-08 10:57:32 -08:00
Nicolas Gallagher
a41af0f65f 0.4.0 2018-02-06 16:35:20 -08:00
Nicolas Gallagher
96eecc0da3 Replace lerna with custom script
Each package version is now updated with each release.

Fix #783
2018-02-06 16:30:03 -08:00
Nicolas Gallagher
69d5373222 [fix] Firefox rendering of TextInput
Removes UA controls from numeric inputs in Firefox, and makes the
default border solid in all browsers.

Fix #789
2018-02-06 11:17:38 -08:00
Nicolas Gallagher
538ab88eda Separate modules for StyleSheet and related side-effects 2018-02-05 12:18:43 -08:00
Nicolas Gallagher
21b3f39c0b [fix] babel-plugin require call for compiled modules
require('module') => require('module').default

Fix #786
2018-02-04 10:42:21 -08:00
Nicolas Gallagher
998e275e65 [add] CSS keyframes support via 'animationName' style
Keyframes can be defined using an array of objects as the value of
'animationName'. Each keyframe is transformed into a CSS keyframe rule.
2018-02-03 11:48:57 -08:00
Nicolas Gallagher
31d428a649 [fix] add support for TextInput placeholderTextColor prop
This change also ensures that potential object-styles (user-provided or
placeholderTextColor) are group together at the end of the style array.

Fix #560
2018-01-31 10:55:45 -08:00
Nicolas Gallagher
240cf7e05f [change] AppRegistry and StyleSheet APIs to fix SSR
SSR was not working correctly. Styles would accumulate in the style
cache and the styles returned for each 'getApplication' call would not
represent the styles needed to render a given tree, but rather all the
styles needed to render every tree that has been rendered by that server
instance.

This is now fixed by reinitializing the style resolver after a call to
'getStyleSheet' on the server. The return type of
'AppRegistry.getApplication' is changed – { element, getStyleElement }.
The 'getStyleElement' function must be called after the component tree
has been rendered.  Note that if 'getStyleElement' is not called for a
response, then its styles may leak into the next response's styles (but
will not affect the UX).

This patch also removes the 'StyleSheet.getStyleSheets' (web-only) API
and requires SSR to be done using 'AppRegistry.getApplication'.

Fix #778
2018-01-31 10:55:45 -08:00
Nicolas Gallagher
2ad710d83a [change] StyleSheet does not eagerly resolve and inject styles
Resolving styles at the same time as they are registered is problematic.
This work doesn't need to be complete until the moment a component is
rendered, rather than being done at startup.
2018-01-31 10:55:45 -08:00
Nicolas Gallagher
dcce72b66e Reorganize style resolver
The style resolver is reorganized to remove React Native style
registration, and contain all the stateful resolver logic (previously
partially found in NativeMethodsMixin).

Style registration is done in 'StyleSheet.create', and will subsequently
be decoupled from eager style resolution.

The stateful resolver is fully contained in the
ReactNativeStyleResolver. The options are removed from the resolver as
they aren't needed to implement the correct i18n-ified styles. This
functionality is also covered by unit tests now.
2018-01-31 10:55:44 -08:00
Nicolas Gallagher
083769d642 [fix] resolve inline-style pointerEvents to classNames
pointerEvents styles are now always resolved to classNames, even if
working with dynamic styles. In practice, inline-style pointerEvents
have always resolved to classNames as an accident of 'createElement'
registering each pointerEvent style. However, this patch ensures that
the style resolver produces correct output, and introduces the basic
mechanism by which 'placeholderTextColor' (for which classNames cannot
be precomputed) could function similarly.
2018-01-31 10:55:44 -08:00
Nicolas Gallagher
a53dba8c62 [change] simplify CSS rule injection
WebStyleSheet handles injecting CSS rules and returning the style
sheet's text content.

All CSS, including the reset, is now added using WebStyleSheet and the DOM API 'insertRule'.
Browsers will throw a parse error on unrecognized vendor-prefixes in
selectors, so rules with unsafe selectors are wrapped in '@media all {}'
before being passed to 'insertRule'. (This will be relevant when
attempting to support 'placeholderTextColor'. And it is a way to
batch-insert rules with a single call to 'insertRule'.)

The html tag 'font-family' CSS reset is removed as it's not needed.
2018-01-31 10:55:44 -08:00
Nicolas Gallagher
670d43ba04 Refactor how pointerEvents styles are managed 2018-01-31 10:55:44 -08:00
Nicolas Gallagher
73a731f2da Rename StyleManager and StyleRegistry 2018-01-31 10:55:44 -08:00
Nicolas Gallagher
1542f1f369 0.3.4 2018-01-31 10:31:50 -08:00
Nicolas Gallagher
6f58d7abe7 [fix] TextInput support for spellCheck prop
Fix #501
2018-01-31 10:27:08 -08:00
Nicolas Gallagher
7e0fbf9691 Fix yarn.lock
Yarn didn't make these changes to yarn.lock after updating packages.
2018-01-29 12:46:12 -08:00
Maximilian Stoiber
865034e8f7 Update benchmark instructions 2018-01-29 12:40:37 -08:00
Nicolas Gallagher
6e96ee4f3c Update benchmarked libraries 2018-01-29 08:59:18 -08:00
Maxime Thirouin
16d98b49f0 [fix] ignore native-only Image props
Close #788
2018-01-26 14:59:59 -08:00
Nicolas Gallagher
d04721c75a Update yarn.lock 2018-01-23 13:01:48 -08:00
Nicolas Gallagher
8512709251 Fix benchmark picker touch target size in Safari 2018-01-23 12:24:56 -08:00
Nicolas Gallagher
efeaea70a9 Benchmarks include forced layout time
This change to 'benchmarks' reports the time taken to perform a forced
layout after mounting the tree. Adding a forced layout to the stress
tests can surface how different approaches to styling may affect browser
render timings.

The total time displayed is now the sum of "scripting time" (previously
total time) and "layout time". The layout time is a reflection of the
time the browser takes to perform a style recalculation and relayout of
the document.

The Benchmark component now has a 'forceLayout' prop. When it is 'true'
a forced layout is triggered on componentDidUpdate. The time taken is
added to the sample's timing data.
2018-01-23 12:24:18 -08:00
Nicolas Gallagher
a403244e67 Update benchmarks library dependencies 2018-01-23 09:49:48 -08:00
Oleg Slobodskoi
985c1d63b6 Add benchmarks update test for react-jss
Close #785
2018-01-23 09:11:38 -08:00
Nicolas Gallagher
9d8d4057f6 Benchmark app component optimizations 2018-01-22 15:52:37 -08:00
Nicolas Gallagher
ec8843fe90 Compute benchmark props before each iteration
Fix a bug in the logic that was meant to perform component prop
computation in-between cycles.
2018-01-22 13:24:41 -08:00
Nicolas Gallagher
935970156c Benchmarks report total run time 2018-01-21 17:02:21 -08:00
242 changed files with 6427 additions and 3574 deletions

View File

@@ -3,7 +3,6 @@
[ignore]
<PROJECT_ROOT>/.*/__tests__/.*
<PROJECT_ROOT>/packages/benchmarks/.*
<PROJECT_ROOT>/packages/.*/dist/.*
<PROJECT_ROOT>/website/.*
.*/node_modules/babel-plugin-transform-react-remove-prop-types/*

View File

@@ -85,10 +85,18 @@ yarn compile --watch
## Benchmarks
To run the performance benchmarks in a browser (opening `./packages/benchmarks/index.html`):
To run the benchmarks locally:
```
yarn benchmarks
open ./packages/benchmarks/dist/index.html
```
To develop against these benchmarks:
```
yarn compile --watch
yarn benchmarks --watch
```
### New Features

45
LICENSE
View File

@@ -1,31 +1,22 @@
BSD License
MIT License
For React Native software
Copyright (c) 2015-present, Nicolas Gallagher.
Copyright (c) 2015-present, Facebook, Inc.
Copyright (c) 2015-present, Nicolas Gallagher. All rights reserved.
Copyright (c) 2015-present, Facebook, Inc. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name Facebook nor the names of its contributors may be used to
endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -19,21 +19,19 @@ rewriting existing code. React Native for Web can also render to HTML and
critical CSS on the server using Node.js.
Who is using React Native for Web? [Twitter](https://mobile.twitter.com),
[Major League Soccer](https://matchcenter.mlssoccer.com), [The
[Major League Soccer](https://matchcenter.mlssoccer.com),
[Flipkart](https://www.flipkart.com/), Playstation, Uber, [The
Times](https://github.com/newsuk/times-components), [React Native's
documentation](http://facebook.github.io/react-native/).
Browser support: Chrome, Firefox, Safari >= 7, IE 10, Edge.
Browser support: Chrome, Firefox, Edge, Safari 7+, IE 10+.
## Quick start
The easiest way to get started with React Native for Web is to use this
[ready-to-go project on Glitch](https://glitch.com/edit/#!/react-native-web-playground).
[ready-to-go project on Glitch](https://glitch.com/edit/#!/react-native).
You dont need to install anything to try it out.
If you are unfamiliar with setting up a React web project, please follow the
recommendations in the [React documentation](https://reactjs.org/).
## Documentation
You can find the React Native for Web API documentation [on the
@@ -53,6 +51,12 @@ yarn add react react-dom react-native-web
yarn add --dev babel-plugin-react-native-web
```
And if you need to use `ART`:
```
yarn add react-art
```
### Guides
* [Getting started](https://github.com/necolas/react-native-web/blob/master/website/guides/getting-started.md)
@@ -97,6 +101,16 @@ You'll notice that there is no reference to `react-dom`; the `App` component is
defined using the platform-agnostic APIs and Components introduced by React
Native. This allows the app to be rendered to web and native platforms.
## Integrations
Examples of using React Native for Web with other web tools:
* [Next.js](https://github.com/zeit/next.js/tree/master/examples/with-react-native-web)
* [Storybook](https://github.com/necolas/react-native-web/tree/0.5.1/website/storybook/.storybook)
* [Razzle](https://github.com/jaredpalmer/razzle/tree/master/examples/with-react-native-web)
* [Phenomic](https://github.com/phenomic/phenomic/tree/master/examples/react-native-web-app)
* [Styleguidist](https://github.com/styleguidist/react-styleguidist/tree/v6.2.6/examples/react-native)
## Contributing
The main purpose of this repository is to help evolve React web and native
@@ -126,7 +140,9 @@ relatively limited scope. This is a great place to get started.
## License
React Native for Web is [BSD licensed](./LICENSE).
React Native for Web is [MIT licensed](./LICENSE). By contributing to React
Native for Web, you agree that your contributions will be licensed under its
MIT license.
[package-badge]: https://img.shields.io/npm/v/react-native-web.svg?style=flat
[package-url]: https://yarnpkg.com/en/package/react-native-web

View File

@@ -1,10 +0,0 @@
{
"lerna": "2.5.1",
"version": "0.3.3",
"npmClient": "yarn",
"useWorkspaces": true,
"packages": [
"packages/*",
"website"
]
}

View File

@@ -1,5 +1,6 @@
{
"private": true,
"version": "0.6.0",
"name": "react-native-web-monorepo",
"scripts": {
"clean": "del ./packages/*/dist",
@@ -18,7 +19,7 @@
"prerelease": "yarn test && yarn compile",
"release": "node ./scripts/release/publish.js",
"postrelease": "yarn website:release && yarn benchmarks:release",
"test": "yarn flow && yarn lint:check && yarn jest"
"test": "yarn flow && yarn lint:check && yarn jest --runInBand"
},
"devDependencies": {
"babel-cli": "^6.26.0",
@@ -42,13 +43,14 @@
"eslint-plugin-promise": "^3.6.0",
"eslint-plugin-react": "^7.5.1",
"flow-bin": "^0.63.1",
"glob": "^7.1.2",
"husky": "^0.14.3",
"jest": "^21.2.1",
"lerna": "^2.6.0",
"lint-staged": "^6.0.0",
"prettier": "^1.8.2",
"raf": "^3.4.0",
"react": "^16.2.0",
"react-art": "^16.3.0",
"react-dom": "^16.2.0",
"react-test-renderer": "^16.2.0"
},
@@ -61,6 +63,9 @@
"fmt:cmd",
"git update-index --again",
"eslint"
],
"packages/react-native-web/src/index.js": [
"node ./scripts/babel/createModuleMap.js"
]
},
"prettier": {
@@ -68,5 +73,5 @@
"singleQuote": true
},
"author": "Nicolas Gallagher",
"license": "BSD-3-Clause"
"license": "MIT"
}

View File

@@ -1,6 +1,6 @@
{
"name": "babel-plugin-react-native-web",
"version": "0.3.3",
"version": "0.6.0",
"description": "Babel plugin for React Native for Web",
"main": "index.js",
"devDependencies": {

View File

@@ -41,7 +41,7 @@ import * as ReactNativeModules from 'react-native';
import ReactNative from 'react-native-web/dist/index';
import View from 'react-native-web/dist/exports/View';
import Invalid from 'react-native-web/dist/exports/Invalid';
import { Invalid } from 'react-native-web/dist/index';
import MyView from 'react-native-web/dist/exports/View';
import ViewPropTypes from 'react-native-web/dist/exports/ViewPropTypes';
import * as ReactNativeModules from 'react-native-web/dist/index';
@@ -74,13 +74,13 @@ const { StyleSheet, TouchableOpacity } = require('react-native');
↓ ↓ ↓ ↓ ↓ ↓
const ReactNative = require('react-native-web/dist/index');
const ReactNative = require('react-native-web/dist/index').default;
const View = require('react-native-web/dist/exports/View');
const View = require('react-native-web/dist/exports/View').default;
const StyleSheet = require('react-native-web/dist/exports/StyleSheet');
const StyleSheet = require('react-native-web/dist/exports/StyleSheet').default;
const TouchableOpacity = require('react-native-web/dist/exports/TouchableOpacity');
const TouchableOpacity = require('react-native-web/dist/exports/TouchableOpacity').default;
"
`;
@@ -92,18 +92,18 @@ const { ColorPropType, StyleSheet, View, TouchableOpacity, processColor } = requ
↓ ↓ ↓ ↓ ↓ ↓
const ReactNative = require('react-native-web/dist/index');
const ReactNative = require('react-native-web/dist/index').default;
const createElement = require('react-native-web/dist/exports/createElement');
const createElement = require('react-native-web/dist/exports/createElement').default;
const ColorPropType = require('react-native-web/dist/exports/ColorPropType');
const ColorPropType = require('react-native-web/dist/exports/ColorPropType').default;
const StyleSheet = require('react-native-web/dist/exports/StyleSheet');
const StyleSheet = require('react-native-web/dist/exports/StyleSheet').default;
const View = require('react-native-web/dist/exports/View');
const View = require('react-native-web/dist/exports/View').default;
const TouchableOpacity = require('react-native-web/dist/exports/TouchableOpacity');
const TouchableOpacity = require('react-native-web/dist/exports/TouchableOpacity').default;
const processColor = require('react-native-web/dist/exports/processColor');
const processColor = require('react-native-web/dist/exports/processColor').default;
"
`;

View File

@@ -1,5 +1,7 @@
const moduleMap = require('./moduleMap');
const getDistLocation = importName =>
importName ? `react-native-web/dist/exports/${importName}` : undefined;
importName && moduleMap[importName] ? `react-native-web/dist/exports/${importName}` : undefined;
const isReactNativeRequire = (t, node) => {
const { declarations } = node;
@@ -92,7 +94,10 @@ module.exports = function({ types: t }) {
return t.variableDeclaration(path.node.kind, [
t.variableDeclarator(
t.identifier(identifier.value.name),
t.callExpression(t.identifier('require'), [t.stringLiteral(distLocation)])
t.memberExpression(
t.callExpression(t.identifier('require'), [t.stringLiteral(distLocation)]),
t.identifier('default')
)
)
]);
}
@@ -105,9 +110,12 @@ module.exports = function({ types: t }) {
const importIndex = t.variableDeclaration(path.node.kind, [
t.variableDeclarator(
t.identifier(name),
t.callExpression(t.identifier('require'), [
t.stringLiteral('react-native-web/dist/index')
])
t.memberExpression(
t.callExpression(t.identifier('require'), [
t.stringLiteral('react-native-web/dist/index')
]),
t.identifier('default')
)
)
]);

View File

@@ -0,0 +1,61 @@
// THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT.
module.exports = {
ART: true,
ActivityIndicator: true,
Animated: true,
AppRegistry: true,
AppState: true,
AsyncStorage: true,
BackHandler: true,
Button: true,
CheckBox: true,
Clipboard: true,
ColorPropType: true,
Dimensions: true,
Easing: true,
EdgeInsetsPropType: true,
FlatList: true,
I18nManager: true,
Image: true,
ImageBackground: true,
InteractionManager: true,
Keyboard: true,
KeyboardAvoidingView: true,
Linking: true,
ListView: true,
Modal: true,
NativeModules: true,
NetInfo: true,
PanResponder: true,
Picker: true,
PixelRatio: true,
Platform: true,
PointPropType: true,
ProgressBar: true,
RefreshControl: true,
SafeAreaView: true,
ScrollView: true,
SectionList: true,
Slider: true,
StatusBar: true,
StyleSheet: true,
Switch: true,
Text: true,
TextInput: true,
TextPropTypes: true,
Touchable: true,
TouchableHighlight: true,
TouchableNativeFeedback: true,
TouchableOpacity: true,
TouchableWithoutFeedback: true,
UIManager: true,
Vibration: true,
View: true,
ViewPropTypes: true,
VirtualizedList: true,
createElement: true,
findNodeHandle: true,
processColor: true,
render: true,
unmountComponentAtNode: true
};

View File

@@ -5,7 +5,7 @@ Try the [benchmarks app](https://necolas.github.io/react-native-web/benchmarks)
To run the benchmarks locally:
```
yarn benchmark
yarn benchmarks
open ./packages/benchmarks/dist/index.html
```
@@ -13,18 +13,15 @@ Develop against these benchmarks:
```
yarn compile --watch
yarn benchmark --watch
yarn benchmarks --watch
```
## Notes
These benchmarks are approximations of extreme cases that libraries may
encounter. The deep and wide tree cases look at the performance of mounting and
rendering large trees of styled elements. The dynamic case looks at the
performance of repeated style updates to a large mounted tree. Some libraries
must inject new styles for each "dynamic style", whereas others may not.
Libraries without support for dynamic styles (i.e., they rely on user-authored
inline styles) do not include a corresponding benchmark.
encounter. Their purpose is to provide an early-warning signal for performance
regressions. Each test report includes the mean and standard deviation of the
timings, and approximations of the time spent in scripting (S) and layout (L).
The components used in the render benchmarks are simple enough to be
implemented by multiple UI or style libraries. The benchmark implementations
@@ -33,60 +30,41 @@ functionality_.
No benchmark will run for more than 20 seconds.
### Mount deep/wide tree
These cases look at the performance of mounting and rendering large trees of
elements that use static styles.
### Update dynamic styles
This case looks at the performance of repeated style updates to a large mounted
tree. Some libraries choose to inject new styles for each "dynamic style",
whereas others choose to use inline styles. Libraries without built-in support
for dynamic styles (i.e., they rely on user-authored inline styles) are not
included.
## Example results
### MacBook Pro (2011)
MacBook Pro (13-inch, Early 2011); 2.3 GHz Intel Core i5; 8 GB 1333 MHz DDR3 RAM. Google Chrome 63.
Typical render timings*: mean ± standard deviations.
Typical render timings: mean ± standard deviations.
| Implementation | Mount deep tree (ms) | Mount wide tree (ms) | Dynamic update (ms) |
| Implementation | Mount deep tree (ms) | Mount wide tree (ms) | Dynamic update (ms) |
| :--- | ---: | ---: | ---: |
| `css-modules` | `15.23` `±04.31` | `21.27` `±07.03` | - |
| `react-native-web@0.3.2` | `17.52` `±04.44` | `24.14` `±04.39` | `15.03` `±02.22` |
| `inline-styles` | `50.06` `±06.70` | `76.38` `±09.58` | `06.43` `±02.02` |
Other libraries
| Implementation | Mount deep tree (ms) | Mount wide tree (ms) | Dynamic update (ms) |
| :--- | ---: | ---: | ---: |
| `aphrodite@1.2.5` | `17.27` `±05.96` | `24.89` `±08.36` | - |
| `glamor@2.20.40` | `21.59` `±05.38` | `27.93` `±07.56` | ‡ |
| `emotion@8.0.12` | `21.07` `±04.16` | `31.40` `±09.40` | ‡ `19.80` `±13.56` |
| `styletron-react@3.0.3` | `23.55` `±05.14` | `34.26` `±07.58` | `10.39` `±02.94` |
| `react-fela@5.0.0` | `27.58` `±04.26` | `39.54` `±05.46` | `10.93` `±01.69` |
| `react-jss@8.2.1` | `27.31` `±07.87` | `40.74` `±10.67` | - |
| `styled-jsx@2.2.1` | `27.46` `±07.85` | `41.47` `±11.53` | `29.16` `±09.98` |
| `styled-components@2.4.0` | `43.89` `±06.99` | `63.26` `±09.02` | `16.17` `±03.71` |
| `reactxp@0.51.0-alpha.9` | `51.86` `±07.21` | `78.80` `±11.85` | `15.04` `±03.92` |
| `radium@0.21.0` | `101.06` `±13.00` | `144.46` `±16.94` | `17.44` `±03.59` |
| `css-modules` | `30.19` `±04.84` | `38.25` `±04.85` | - |
| `react-native-web@0.4.0` | `36.40` `±04.98` | `51.28` `±05.58` | `19.36` `±02.56` |
| `inline-styles` | `64.12` `±07.69` | `94.49` `±11.34` | `09.84` `±02.36` |
### Moto G4
Moto G4 (Android 7); Octa-core (4x1.5 GHz & 4x1.2 Ghz); 2 GB RAM. Google Chrome 63
Moto G4 (Android 7); Octa-core (4x1.5 GHz & 4x1.2 Ghz); 2 GB RAM. Google Chrome 63.
Typical render timings*: mean ± standard deviations.
Typical render timings: mean ± standard deviations.
| Implementation | Mount deep tree (ms) | Mount wide tree (ms) | Dynamic update (ms) |
| Implementation | Mount deep tree (ms) | Mount wide tree (ms) | Dynamic update (ms) |
| :--- | ---: | ---: | ---: |
| `css-modules` | `56.18` 19.54` | `75.95` `±23.55` | - |
| `react-native-web@0.3.2` | `68.53` 21.00` | `101.03` 25.32` | `60.57` `±09.07` |
| `inline-styles` | `140.32` `±23.91` | `208.55` 35.25` | `20.36` `±04.92` |
Other libraries
| Implementation | Mount deep tree (ms) | Mount wide tree (ms) | Dynamic update (ms) |
| :--- | ---: | ---: | ---: |
| `aphrodite@1.2.5` | `58.77` `±19.73` | `85.83` `±24.64` | - |
| `glamor@2.20.40` | `81.05` `±15.87` | `104.02` `±20.92` | ‡ |
| `emotion@8.0.12` | `77.12` `±19.61` | `112.04` `±24.43` | ‡ `80.40` `±40.56` |
| `styletron-react@3.0.3` | `91.00` `±17.95` | `130.49` `±20.06` | `39.70` `±06.85` |
| `react-fela@5.0.0` | `101.36` `±19.55` | `142.18` `±21.87` | `43.64` `±12.24` |
| `styled-jsx@2.2.1` | `101.60` `±25.26` | `144.12` `±30.79` | `115.63` `±32.77` |
| `react-jss@8.2.1` | `112.46` `±32.07` | `165.96` `±42.54` | - |
| `styled-components@2.4.0` | `159.85` `±24.30` | `231.00` `±31.34` | `53.86` `±13.40` |
| `reactxp@0.51.0-alpha.9` | `182.05` `±30.72` | `261.25` `±35.54` | `58.20` `±08.62` |
| `radium@0.21.0` | `323.93` `±41.46` | `464.70` `±53.93` | `59.13` `±09.76` |
‡Glamor essentially crashes the browser tab. Emotion gets slower every iteration.
| `css-modules` | `98.24` 20.26` | `143.75` `±25.50` | - |
| `react-native-web@0.4.0` | `131.46` 18.96` | `174.70` 14.88` | `60.87` `±06.32` |
| `inline-styles` | `184.58` `±26.23` | `273.86` 26.23` | `30.28` `±07.44` |

View File

@@ -1,35 +1,36 @@
{
"private": true,
"name": "benchmarks",
"version": "0.3.3",
"version": "0.6.0",
"scripts": {
"build": "mkdir -p dist && cp -f index.html dist/index.html && webpack --config ./webpack.config.js",
"release": "yarn build && git checkout gh-pages && rm -rf ../../benchmarks && mv dist ../../benchmarks && git add -A && git commit -m \"Benchmarks deploy\" && git push origin gh-pages && git checkout -"
},
"dependencies": {
"aphrodite": "1.2.5",
"aphrodite": "^2.1.0",
"classnames": "^2.2.5",
"d3-scale-chromatic": "^1.1.1",
"emotion": "8.0.12",
"fela": "5.0.0",
"d3-scale-chromatic": "^1.2.0",
"emotion": "^9.1.1",
"fela": "^6.1.7",
"glamor": "2.20.40",
"radium": "0.21.0",
"radium": "^0.24.0",
"react": "^16.2.0",
"react-component-benchmark": "^0.0.4",
"react-dom": "^16.2.0",
"react-fela": "5.0.0",
"react-jss": "8.2.1",
"react-native-web": "^0.3.2",
"reactxp": "0.51.0-alpha.9",
"styled-components": "2.4.0",
"styled-jsx": "2.2.1",
"styletron-client": "3.0.2",
"styletron-react": "3.0.3"
"react-fela": "^7.2.0",
"react-jss": "^8.4.0",
"react-native-web": "0.6.0",
"reactxp": "^1.1.0",
"styled-components": "^3.2.5",
"styled-jsx": "^2.2.6",
"styletron-engine-atomic": "^1.0.4",
"styletron-react": "^4.0.3"
},
"devDependencies": {
"babel-plugin-react-native-web": "^0.3.3",
"css-loader": "^0.28.9",
"style-loader": "^0.19.1",
"babel-plugin-react-native-web": "0.6.0",
"css-loader": "^0.28.11",
"style-loader": "^0.20.3",
"url-loader": "^1.0.1",
"webpack": "^3.10.0",
"webpack-bundle-analyzer": "^2.9.2"
}

View File

@@ -100,6 +100,9 @@ export default class App extends Component {
libraryName={r.libraryName}
libraryVersion={r.libraryVersion}
mean={r.mean}
meanLayout={r.meanLayout}
meanScripting={r.meanScripting}
runTime={r.runTime}
sampleCount={r.sampleCount}
stdDev={r.stdDev}
/>
@@ -129,6 +132,7 @@ export default class App extends Component {
<View ref={this._setBenchWrapperRef}>
<Benchmark
component={Component}
forceLayout={true}
getComponentProps={getComponentProps}
onComplete={this._createHandleComplete({
sampleCount,
@@ -282,6 +286,7 @@ const styles = StyleSheet.create({
},
picker: {
...StyleSheet.absoluteFillObject,
appearance: 'none',
opacity: 0,
width: '100%'
},

View File

@@ -1,5 +1,7 @@
// @flow
/* global $Values */
/**
* @flow
*/
import * as Timing from './timing';
import React, { Component } from 'react';
import { getMean, getMedian, getStdDev } from './math';
@@ -12,8 +14,6 @@ export const BenchmarkType = {
UNMOUNT: 'unmount'
};
const emptyObject = {};
const shouldRender = (cycle: number, type: $Values<typeof BenchmarkType>): boolean => {
switch (type) {
// Render every odd iteration (first, third, etc)
@@ -66,7 +66,8 @@ const sortNumbers = (a: number, b: number): number => a - b;
type BenchmarkPropsType = {
component: typeof React.Component,
getComponentProps?: Function,
forceLayout?: boolean,
getComponentProps: Function,
onComplete: (x: BenchResultsType) => void,
sampleCount: number,
timeout: number,
@@ -74,7 +75,7 @@ type BenchmarkPropsType = {
};
type BenchmarkStateType = {
getComponentProps: Function,
componentProps: Object,
cycle: number,
running: boolean
};
@@ -84,13 +85,13 @@ type BenchmarkStateType = {
* TODO: documentation
*/
export default class Benchmark extends Component<BenchmarkPropsType, BenchmarkStateType> {
_raf: ?Function;
_startTime: number;
_samples: Array<SampleTimingType>;
static displayName = 'Benchmark';
static defaultProps = {
getComponentProps: () => emptyObject,
sampleCount: 50,
timeout: 10000, // 10 seconds
type: BenchmarkType.MOUNT
@@ -100,9 +101,11 @@ export default class Benchmark extends Component<BenchmarkPropsType, BenchmarkSt
constructor(props: BenchmarkPropsType, context?: {}) {
super(props, context);
const cycle = 0;
const componentProps = props.getComponentProps({ cycle });
this.state = {
getComponentProps: props.getComponentProps,
cycle: 0,
componentProps,
cycle,
running: false
};
this._startTime = 0;
@@ -110,7 +113,9 @@ export default class Benchmark extends Component<BenchmarkPropsType, BenchmarkSt
}
componentWillReceiveProps(nextProps: BenchmarkPropsType) {
this.setState(state => ({ getComponentProps: nextProps.getComponentProps }));
if (nextProps) {
this.setState(state => ({ componentProps: nextProps.getComponentProps(state.cycle) }));
}
}
componentWillUpdate(nextProps: BenchmarkPropsType, nextState: BenchmarkStateType) {
@@ -120,11 +125,20 @@ export default class Benchmark extends Component<BenchmarkPropsType, BenchmarkSt
}
componentDidUpdate() {
const { sampleCount, timeout, type } = this.props;
const { forceLayout, sampleCount, timeout, type } = this.props;
const { cycle, running } = this.state;
if (running && shouldRecord(cycle, type)) {
this._samples[cycle].end = Timing.now();
this._samples[cycle].scriptingEnd = Timing.now();
// force style recalc that would otherwise happen before the next frame
if (forceLayout) {
this._samples[cycle].layoutStart = Timing.now();
if (document.body) {
document.body.offsetWidth;
}
this._samples[cycle].layoutEnd = Timing.now();
}
}
if (running) {
@@ -138,20 +152,18 @@ export default class Benchmark extends Component<BenchmarkPropsType, BenchmarkSt
}
componentWillUnmount() {
if (this.raf) {
window.cancelAnimationFrame(this.raf);
if (this._raf) {
window.cancelAnimationFrame(this._raf);
}
}
render() {
const { component: Component, type } = this.props;
const { getComponentProps, cycle, running } = this.state;
const { componentProps, cycle, running } = this.state;
if (running && shouldRecord(cycle, type)) {
this._samples[cycle] = { start: Timing.now() };
this._samples[cycle] = { scriptingStart: Timing.now() };
}
return running && shouldRender(cycle, type) ? (
<Component {...getComponentProps({ cycle })} />
) : null;
return running && shouldRender(cycle, type) ? <Component {...componentProps} /> : null;
}
start() {
@@ -163,17 +175,21 @@ export default class Benchmark extends Component<BenchmarkPropsType, BenchmarkSt
const { getComponentProps, type } = this.props;
const { cycle } = this.state;
// Calculate the component props outside of the time recording (render)
// so that it doesn't skew results
const getNextProps =
type === BenchmarkType.UPDATE
? obj => ({ ...getComponentProps(obj), 'data-test': cycle })
: getComponentProps;
let componentProps;
if (getComponentProps) {
// Calculate the component props outside of the time recording (render)
// so that it doesn't skew results
componentProps = getComponentProps({ cycle });
// make sure props always change for update tests
if (type === BenchmarkType.UPDATE) {
componentProps['data-test'] = cycle;
}
}
this.raf = window.requestAnimationFrame(() => {
this._raf = window.requestAnimationFrame(() => {
this.setState((state: BenchmarkStateType) => ({
getComponentProps: getNextProps,
cycle: state.cycle + 1
cycle: state.cycle + 1,
componentProps
}));
});
}
@@ -182,10 +198,16 @@ export default class Benchmark extends Component<BenchmarkPropsType, BenchmarkSt
return this._samples.reduce(
(
memo: Array<FullSampleTimingType>,
{ start, end: endTime }: SampleTimingType
{ scriptingStart, scriptingEnd, layoutStart, layoutEnd }: SampleTimingType
): Array<FullSampleTimingType> => {
const end = endTime || 0;
memo.push({ start, end, elapsed: end - start });
memo.push({
start: scriptingStart,
end: layoutEnd || scriptingEnd || 0,
scriptingStart,
scriptingEnd: scriptingEnd || 0,
layoutStart,
layoutEnd
});
return memo;
},
[]
@@ -199,11 +221,13 @@ export default class Benchmark extends Component<BenchmarkPropsType, BenchmarkSt
this.setState(() => ({ running: false, cycle: 0 }));
const runTime = endTime - this._startTime;
const sortedElapsedTimes = samples
.map(({ elapsed }: { elapsed: number }): number => elapsed)
const sortedElapsedTimes = samples.map(({ start, end }) => end - start).sort(sortNumbers);
const sortedScriptingElapsedTimes = samples
.map(({ scriptingStart, scriptingEnd }) => scriptingEnd - scriptingStart)
.sort(sortNumbers);
const sortedLayoutElapsedTimes = samples
.map(({ layoutStart, layoutEnd }) => (layoutEnd || 0) - (layoutStart || 0))
.sort(sortNumbers);
const mean = getMean(sortedElapsedTimes);
const stdDev = getStdDev(sortedElapsedTimes);
onComplete({
startTime: this._startTime,
@@ -214,8 +238,10 @@ export default class Benchmark extends Component<BenchmarkPropsType, BenchmarkSt
max: sortedElapsedTimes[sortedElapsedTimes.length - 1],
min: sortedElapsedTimes[0],
median: getMedian(sortedElapsedTimes),
mean,
stdDev
mean: getMean(sortedElapsedTimes),
stdDev: getStdDev(sortedElapsedTimes),
meanLayout: getMean(sortedLayoutElapsedTimes),
meanScripting: getMean(sortedScriptingElapsedTimes)
});
}
}

View File

@@ -1,4 +1,6 @@
// @flow
/**
* @flow
*/
export type BenchResultsType = {
startTime: number,
endTime: number,
@@ -9,20 +11,21 @@ export type BenchResultsType = {
min: number,
median: number,
mean: number,
stdDev: number,
p70: number,
p95: number,
p99: number
stdDev: number
};
export type SampleTimingType = {
start: number,
end?: number,
elapsed?: number
scriptingStart: number,
scriptingEnd?: number,
layoutStart?: number,
layoutEnd?: number
};
export type FullSampleTimingType = {
start: number,
end: number,
elapsed: number
scriptingStart: number,
scriptingEnd: number,
layoutStart?: number,
layoutEnd?: number
};

View File

@@ -19,11 +19,13 @@ export default class Layout extends Component {
const { widescreen } = this.state;
return (
<View onLayout={this._handleLayout} style={[styles.root, widescreen && styles.row]}>
<View style={widescreen ? styles.grow : styles.stackPanel}>{viewPanel}</View>
<View style={[widescreen ? styles.grow : styles.stackPanel, styles.layer]}>
{viewPanel}
</View>
<View style={styles.grow}>
<View style={styles.grow}>{listPanel}</View>
<View style={[styles.grow, styles.layer]}>{listPanel}</View>
<View style={styles.divider} />
<View>{actionPanel}</View>
<View style={styles.layer}>{actionPanel}</View>
</View>
</View>
);
@@ -59,5 +61,8 @@ const styles = StyleSheet.create({
},
stackPanel: {
height: '33.33%'
},
layer: {
transform: [{ translateZ: '0' }]
}
});

View File

@@ -8,37 +8,49 @@ const fmt = (time: number) => {
return 10 / i > 1 ? `0${i}` : i;
};
const ReportCard = ({ benchmarkName, libraryName, sampleCount, mean, stdDev, libraryVersion }) => {
const sampleCountText = sampleCount != null ? `(${sampleCount})` : '';
class ReportCard extends React.PureComponent {
render() {
const {
benchmarkName,
libraryName,
sampleCount,
mean,
meanLayout,
meanScripting,
stdDev,
libraryVersion
} = this.props;
return (
<View style={styles.root}>
<View style={styles.left}>
<Text numberOfLines={1} style={styles.bold}>
{`${libraryName}${libraryVersion ? '@' + libraryVersion : ''}`}
</Text>
<Text numberOfLines={1}>
{benchmarkName} {sampleCountText}
</Text>
const sampleCountText = sampleCount != null ? `(${sampleCount})` : '';
return (
<View style={styles.root}>
<View style={styles.left}>
<Text numberOfLines={1} style={styles.bold}>
{`${libraryName}${libraryVersion ? '@' + libraryVersion : ''}`}
</Text>
<Text numberOfLines={1}>
{benchmarkName} {sampleCountText}
</Text>
</View>
<View style={styles.right}>
{mean ? (
<Fragment>
<Text style={[styles.bold, styles.monoFont]}>
{fmt(mean)} ±{fmt(stdDev)} ms
</Text>
<Text style={[styles.smallText, styles.monoFont]}>
(S/L) {fmt(meanScripting)}/{fmt(meanLayout)} ms
</Text>
</Fragment>
) : (
<Text style={styles.bold}>In progress</Text>
)}
</View>
</View>
<View style={styles.right}>
{mean ? (
<Fragment>
<Text style={[styles.bold, styles.monoFont]}>
{fmt(mean)} ±{fmt(stdDev)} ms
</Text>
<Text style={[styles.monoFont, styles.centerText]}>
<Text style={styles.smallText}>Σ = </Text>
<Text>{Math.round(mean * sampleCount)} ms</Text>
</Text>
</Fragment>
) : (
<Text style={styles.bold}>In progress</Text>
)}
</View>
</View>
);
};
);
}
}
const styles = StyleSheet.create({
root: {

View File

@@ -1,9 +1,5 @@
/* eslint-disable react/prop-types */
/**
* @flow
*/
import { bool } from 'prop-types';
import React from 'react';
import { StyleSheet, Text } from 'react-native';

View File

@@ -53,35 +53,34 @@ class SierpinskiTriangle extends React.Component {
s /= 2;
return [
<SierpinskiTriangle
components={components}
depth={1}
key={1}
renderCount={renderCount}
s={s}
x={x}
y={y - s / 2}
/>,
<SierpinskiTriangle
components={components}
depth={2}
key={2}
renderCount={renderCount}
s={s}
x={x - s}
y={y + s / 2}
/>,
<SierpinskiTriangle
components={components}
depth={3}
key={3}
renderCount={renderCount}
s={s}
x={x + s}
y={y + s / 2}
/>
];
return (
<React.Fragment>
<SierpinskiTriangle
components={components}
depth={1}
renderCount={renderCount}
s={s}
x={x}
y={y - s / 2}
/>
<SierpinskiTriangle
components={components}
depth={2}
renderCount={renderCount}
s={s}
x={x - s}
y={y + s / 2}
/>
<SierpinskiTriangle
components={components}
depth={3}
renderCount={renderCount}
s={s}
x={x + s}
y={y + s / 2}
/>
</React.Fragment>
);
} else {
return <span style={{ color: 'white' }}>No implementation available</span>;
}

View File

@@ -1,5 +1,3 @@
/* @flow */
import { type Component } from 'react';
import packageJson from '../package.json';

View File

@@ -0,0 +1,26 @@
/* eslint-disable react/prop-types */
import injectSheet from 'react-jss';
import React from 'react';
const Dot = ({ classes, children }) => <div className={classes.root}>{children}</div>;
const styles = {
root: {
position: 'absolute',
cursor: 'pointer',
width: 0,
height: 0,
borderColor: 'transparent',
borderStyle: 'solid',
borderTopWidth: 0,
transform: 'translate(50%, 50%)',
borderBottomColor: ({ color }) => color,
borderRightWidth: ({ size }) => size / 2,
borderBottomWidth: ({ size }) => size / 2,
borderLeftWidth: ({ size }) => size / 2,
marginLeft: ({ x }) => x,
marginTop: ({ y }) => y
}
};
export default injectSheet(styles)(Dot);

View File

@@ -1,9 +1,11 @@
import Box from './Box';
import Dot from './Dot';
import Provider from './Provider';
import View from './View';
export default {
Box,
Dot,
Provider,
View
};

View File

@@ -1,8 +1,8 @@
/* eslint-disable react/prop-types */
import { styled } from 'styletron-react';
import { withStyle } from 'styletron-react';
import View from './View';
const Box = styled(
const Box = withStyle(
View,
({ color, fixed = false, layout = 'column', outer = false, ...other }) => ({
...styles[`color${color}`],

View File

@@ -1,7 +1,7 @@
/* eslint-disable react/prop-types */
import React from 'react';
import Styletron from 'styletron-client';
import { StyletronProvider } from 'styletron-react';
import { Client as Styletron } from 'styletron-engine-atomic';
import { Provider as StyletronProvider } from 'styletron-react';
import View from './View';
const styletron = new Styletron();
@@ -9,7 +9,7 @@ const styletron = new Styletron();
class Provider extends React.Component {
render() {
return (
<StyletronProvider styletron={styletron}>
<StyletronProvider value={styletron}>
<View>{this.props.children}</View>
</StyletronProvider>
);

View File

@@ -49,7 +49,7 @@ module.exports = {
new webpack.optimize.UglifyJsPlugin({
compress: {
dead_code: true,
drop_console: true,
drop_console: false,
screw_ie8: true,
warnings: false
}

View File

@@ -39,7 +39,7 @@ exports[`enzyme.mount complex 1`] = `
}
>
<div
className="rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxSizing-deolkf rn-color-1bodaif rn-display-1471scf rn-font-1lw9tu2 rn-fontFamily-10u92zi rn-fontSize-ubezar rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-verticalAlign-9iso6 rn-textDecoration-bauka4 rn-whiteSpace-q42fyq rn-wordWrap-qvutc0"
className="rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxSizing-deolkf rn-color-1bodaif rn-display-1471scf rn-fontFamily-14xgk7a rn-fontSize-ubezar rn-fontStyle-o11vmf rn-fontVariant-ebii48 rn-fontWeight-gul640 rn-lineHeight-t9a87b rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-verticalAlign-9iso6 rn-textDecoration-bauka4 rn-whiteSpace-q42fyq rn-wordWrap-qvutc0"
dir="auto"
>
Hello World
@@ -63,7 +63,7 @@ exports[`enzyme.mount complex 1`] = `
</View>
<Text>
<div
className="rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxSizing-deolkf rn-color-homxoj rn-display-1471scf rn-font-1lw9tu2 rn-fontFamily-10u92zi rn-fontSize-1b43r93 rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-textDecoration-bauka4 rn-whiteSpace-q42fyq rn-wordWrap-qvutc0"
className="rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxSizing-deolkf rn-color-homxoj rn-display-1471scf rn-fontFamily-14xgk7a rn-fontSize-1b43r93 rn-fontStyle-o11vmf rn-fontVariant-ebii48 rn-fontWeight-gul640 rn-lineHeight-t9a87b rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-textDecoration-bauka4 rn-whiteSpace-q42fyq rn-wordWrap-qvutc0"
dir="auto"
>
Nested
@@ -117,7 +117,7 @@ exports[`enzyme.mount nested 1`] = `
}
>
<div
className="rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxSizing-deolkf rn-color-1bodaif rn-display-1471scf rn-font-1lw9tu2 rn-fontFamily-10u92zi rn-fontSize-ubezar rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-verticalAlign-9iso6 rn-textDecoration-bauka4 rn-whiteSpace-q42fyq rn-wordWrap-qvutc0"
className="rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxSizing-deolkf rn-color-1bodaif rn-display-1471scf rn-fontFamily-14xgk7a rn-fontSize-ubezar rn-fontStyle-o11vmf rn-fontVariant-ebii48 rn-fontWeight-gul640 rn-lineHeight-t9a87b rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-verticalAlign-9iso6 rn-textDecoration-bauka4 rn-whiteSpace-q42fyq rn-wordWrap-qvutc0"
dir="auto"
>
Hello World
@@ -142,7 +142,7 @@ exports[`enzyme.render complex 1`] = `
class="rn-alignItems-1oszu61 rn-backgroundColor-1mjtqww 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-1pxmb3b rn-flexBasis-7vfszb rn-flexDirection-eqz5dr rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-m611by rn-paddingRight-1qfoi16 rn-paddingBottom-1mi0q7o rn-paddingLeft-1hfyk0a rn-position-bnwqim rn-zIndex-1lgpqti"
>
<div
class="rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxSizing-deolkf rn-color-1bodaif rn-display-1471scf rn-font-1lw9tu2 rn-fontFamily-10u92zi rn-fontSize-ubezar rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-verticalAlign-9iso6 rn-textDecoration-bauka4 rn-whiteSpace-q42fyq rn-wordWrap-qvutc0"
class="rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxSizing-deolkf rn-color-1bodaif rn-display-1471scf rn-fontFamily-14xgk7a rn-fontSize-ubezar rn-fontStyle-o11vmf rn-fontVariant-ebii48 rn-fontWeight-gul640 rn-lineHeight-t9a87b rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-verticalAlign-9iso6 rn-textDecoration-bauka4 rn-whiteSpace-q42fyq rn-wordWrap-qvutc0"
dir="auto"
>
Hello World
@@ -154,7 +154,7 @@ exports[`enzyme.render complex 1`] = `
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-1pxmb3b rn-flexBasis-7vfszb rn-flexDirection-eqz5dr rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-1knelpx rn-paddingRight-1ah4tor rn-paddingBottom-k8qxaj rn-paddingLeft-b5h31w rn-position-bnwqim rn-zIndex-1lgpqti"
/>
<div
class="rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxSizing-deolkf rn-color-homxoj rn-display-1471scf rn-font-1lw9tu2 rn-fontFamily-10u92zi rn-fontSize-1b43r93 rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-textDecoration-bauka4 rn-whiteSpace-q42fyq rn-wordWrap-qvutc0"
class="rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxSizing-deolkf rn-color-homxoj rn-display-1471scf rn-fontFamily-14xgk7a rn-fontSize-1b43r93 rn-fontStyle-o11vmf rn-fontVariant-ebii48 rn-fontWeight-gul640 rn-lineHeight-t9a87b rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-textDecoration-bauka4 rn-whiteSpace-q42fyq rn-wordWrap-qvutc0"
dir="auto"
>
Nested
@@ -174,7 +174,7 @@ exports[`enzyme.render nested 1`] = `
class="rn-alignItems-1oszu61 rn-backgroundColor-1mjtqww 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-1pxmb3b rn-flexBasis-7vfszb rn-flexDirection-eqz5dr rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-m611by rn-paddingRight-1qfoi16 rn-paddingBottom-1mi0q7o rn-paddingLeft-1hfyk0a rn-position-bnwqim rn-zIndex-1lgpqti"
>
<div
class="rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxSizing-deolkf rn-color-1bodaif rn-display-1471scf rn-font-1lw9tu2 rn-fontFamily-10u92zi rn-fontSize-ubezar rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-verticalAlign-9iso6 rn-textDecoration-bauka4 rn-whiteSpace-q42fyq rn-wordWrap-qvutc0"
class="rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxSizing-deolkf rn-color-1bodaif rn-display-1471scf rn-fontFamily-14xgk7a rn-fontSize-ubezar rn-fontStyle-o11vmf rn-fontVariant-ebii48 rn-fontWeight-gul640 rn-lineHeight-t9a87b rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-verticalAlign-9iso6 rn-textDecoration-bauka4 rn-whiteSpace-q42fyq rn-wordWrap-qvutc0"
dir="auto"
>
Hello World
@@ -252,7 +252,7 @@ exports[`react-test-renderer complex 1`] = `
className="rn-alignItems-1oszu61 rn-backgroundColor-1mjtqww 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-1pxmb3b rn-flexBasis-7vfszb rn-flexDirection-eqz5dr rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-m611by rn-paddingRight-1qfoi16 rn-paddingBottom-1mi0q7o rn-paddingLeft-1hfyk0a rn-position-bnwqim rn-zIndex-1lgpqti"
>
<div
className="rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxSizing-deolkf rn-color-1bodaif rn-display-1471scf rn-font-1lw9tu2 rn-fontFamily-10u92zi rn-fontSize-ubezar rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-verticalAlign-9iso6 rn-textDecoration-bauka4 rn-whiteSpace-q42fyq rn-wordWrap-qvutc0"
className="rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxSizing-deolkf rn-color-1bodaif rn-display-1471scf rn-fontFamily-14xgk7a rn-fontSize-ubezar rn-fontStyle-o11vmf rn-fontVariant-ebii48 rn-fontWeight-gul640 rn-lineHeight-t9a87b rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-verticalAlign-9iso6 rn-textDecoration-bauka4 rn-whiteSpace-q42fyq rn-wordWrap-qvutc0"
dir="auto"
>
Hello World
@@ -264,7 +264,7 @@ exports[`react-test-renderer complex 1`] = `
className="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-1pxmb3b rn-flexBasis-7vfszb rn-flexDirection-eqz5dr rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-1knelpx rn-paddingRight-1ah4tor rn-paddingBottom-k8qxaj rn-paddingLeft-b5h31w rn-position-bnwqim rn-zIndex-1lgpqti"
/>
<div
className="rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxSizing-deolkf rn-color-homxoj rn-display-1471scf rn-font-1lw9tu2 rn-fontFamily-10u92zi rn-fontSize-1b43r93 rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-textDecoration-bauka4 rn-whiteSpace-q42fyq rn-wordWrap-qvutc0"
className="rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxSizing-deolkf rn-color-homxoj rn-display-1471scf rn-fontFamily-14xgk7a rn-fontSize-1b43r93 rn-fontStyle-o11vmf rn-fontVariant-ebii48 rn-fontWeight-gul640 rn-lineHeight-t9a87b rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-textDecoration-bauka4 rn-whiteSpace-q42fyq rn-wordWrap-qvutc0"
dir="auto"
>
Nested
@@ -284,7 +284,7 @@ exports[`react-test-renderer nested 1`] = `
className="rn-alignItems-1oszu61 rn-backgroundColor-1mjtqww 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-1pxmb3b rn-flexBasis-7vfszb rn-flexDirection-eqz5dr rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-m611by rn-paddingRight-1qfoi16 rn-paddingBottom-1mi0q7o rn-paddingLeft-1hfyk0a rn-position-bnwqim rn-zIndex-1lgpqti"
>
<div
className="rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxSizing-deolkf rn-color-1bodaif rn-display-1471scf rn-font-1lw9tu2 rn-fontFamily-10u92zi rn-fontSize-ubezar rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-verticalAlign-9iso6 rn-textDecoration-bauka4 rn-whiteSpace-q42fyq rn-wordWrap-qvutc0"
className="rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxSizing-deolkf rn-color-1bodaif rn-display-1471scf rn-fontFamily-14xgk7a rn-fontSize-ubezar rn-fontStyle-o11vmf rn-fontVariant-ebii48 rn-fontWeight-gul640 rn-lineHeight-t9a87b rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-verticalAlign-9iso6 rn-textDecoration-bauka4 rn-whiteSpace-q42fyq rn-wordWrap-qvutc0"
dir="auto"
>
Hello World

View File

@@ -1,6 +1,6 @@
{
"name": "react-native-web",
"version": "0.3.2",
"version": "0.6.0",
"description": "React Native for Web",
"main": "dist/index.js",
"files": [
@@ -19,15 +19,15 @@
"inline-style-prefixer": "^4.0.0",
"normalize-css-color": "^1.0.2",
"prop-types": "^15.6.0",
"react-art": "^16.2.0",
"react-timer-mixin": "^0.13.3"
},
"peerDependencies": {
"react": "16.x.x",
"react-art": "16.x.x",
"react-dom": "16.x.x"
},
"author": "Nicolas Gallagher",
"license": "BSD-3-Clause",
"license": "MIT",
"repository": {
"type": "git",
"url": "git://github.com/necolas/react-native-web.git"

View File

@@ -17,7 +17,24 @@ exports[`components/ActivityIndicator prop "animating" is "false" 1`] = `
Object {
"animationDuration": "0.75s",
"animationIterationCount": "infinite",
"animationName": "rn-ActivityIndicator-animation",
"animationName": Array [
Object {
"0%": Object {
"transform": Array [
Object {
"rotate": "0deg",
},
],
},
"100%": Object {
"transform": Array [
Object {
"rotate": "360deg",
},
],
},
},
],
"animationPlayState": "paused",
"animationTimingFunction": "linear",
"height": 20,
@@ -80,7 +97,24 @@ exports[`components/ActivityIndicator prop "animating" is "true" 1`] = `
Object {
"animationDuration": "0.75s",
"animationIterationCount": "infinite",
"animationName": "rn-ActivityIndicator-animation",
"animationName": Array [
Object {
"0%": Object {
"transform": Array [
Object {
"rotate": "0deg",
},
],
},
"100%": Object {
"transform": Array [
Object {
"rotate": "360deg",
},
],
},
},
],
"animationTimingFunction": "linear",
"height": 20,
"width": 20,
@@ -177,7 +211,24 @@ exports[`components/ActivityIndicator prop "hidesWhenStopped" is "false" 1`] = `
Object {
"animationDuration": "0.75s",
"animationIterationCount": "infinite",
"animationName": "rn-ActivityIndicator-animation",
"animationName": Array [
Object {
"0%": Object {
"transform": Array [
Object {
"rotate": "0deg",
},
],
},
"100%": Object {
"transform": Array [
Object {
"rotate": "360deg",
},
],
},
},
],
"animationPlayState": "paused",
"animationTimingFunction": "linear",
"height": 20,
@@ -239,7 +290,24 @@ exports[`components/ActivityIndicator prop "hidesWhenStopped" is "true" 1`] = `
Object {
"animationDuration": "0.75s",
"animationIterationCount": "infinite",
"animationName": "rn-ActivityIndicator-animation",
"animationName": Array [
Object {
"0%": Object {
"transform": Array [
Object {
"rotate": "0deg",
},
],
},
"100%": Object {
"transform": Array [
Object {
"rotate": "360deg",
},
],
},
},
],
"animationPlayState": "paused",
"animationTimingFunction": "linear",
"height": 20,
@@ -302,7 +370,24 @@ exports[`components/ActivityIndicator prop "size" is "large" 1`] = `
Object {
"animationDuration": "0.75s",
"animationIterationCount": "infinite",
"animationName": "rn-ActivityIndicator-animation",
"animationName": Array [
Object {
"0%": Object {
"transform": Array [
Object {
"rotate": "0deg",
},
],
},
"100%": Object {
"transform": Array [
Object {
"rotate": "360deg",
},
],
},
},
],
"animationTimingFunction": "linear",
"height": 36,
"width": 36,
@@ -363,7 +448,24 @@ exports[`components/ActivityIndicator prop "size" is a number 1`] = `
Object {
"animationDuration": "0.75s",
"animationIterationCount": "infinite",
"animationName": "rn-ActivityIndicator-animation",
"animationName": Array [
Object {
"0%": Object {
"transform": Array [
Object {
"rotate": "0deg",
},
],
},
"100%": Object {
"transform": Array [
Object {
"rotate": "360deg",
},
],
},
},
],
"animationTimingFunction": "linear",
"height": 30,
"width": 30,

View File

@@ -1,9 +1,8 @@
/**
* Copyright (c) 2016-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
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule ActivityIndicator
@@ -88,7 +87,12 @@ const styles = StyleSheet.create({
},
animation: {
animationDuration: '0.75s',
animationName: 'rn-ActivityIndicator-animation',
animationName: [
{
'0%': { transform: [{ rotate: '0deg' }] },
'100%': { transform: [{ rotate: '360deg' }] }
}
],
animationTimingFunction: 'linear',
animationIterationCount: 'infinite'
},

View File

@@ -1,10 +1,8 @@
/**
* 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. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule Animated
* @flow

View File

@@ -1,9 +1,8 @@
/**
* Copyright (c) 2016-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
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
@@ -12,13 +11,14 @@
import StyleSheet from '../StyleSheet';
import View from '../View';
import { any, node } from 'prop-types';
import React, { Component } from 'react';
import React, { Component, type ComponentType } from 'react';
type Context = {
rootTag: any
};
type Props = {
WrapperComponent?: ?ComponentType<*>,
// $FlowFixMe
children?: React.Children,
rootTag: any
@@ -36,6 +36,7 @@ export default class AppContainer extends Component<Props, State> {
};
static propTypes = {
WrapperComponent: any,
children: node,
rootTag: any.isRequired
};
@@ -47,23 +48,29 @@ export default class AppContainer extends Component<Props, State> {
}
render() {
const { children, WrapperComponent } = this.props;
let innerView = (
<View
children={children}
key={this.state.mainKey}
pointerEvents="box-none"
style={styles.appContainer}
/>
);
if (WrapperComponent) {
innerView = <WrapperComponent>{innerView}</WrapperComponent>;
}
return (
<View pointerEvents="box-none" style={[styles.appContainer, StyleSheet.absoluteFill]}>
<View
children={this.props.children}
key={this.state.mainKey}
pointerEvents="box-none"
style={styles.appContainer}
/>
<View pointerEvents="box-none" style={styles.appContainer}>
{innerView}
</View>
);
}
}
const styles = StyleSheet.create({
/**
* Ensure that the application covers the whole screen.
*/
appContainer: {
flex: 1
}

View File

@@ -0,0 +1,68 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Additional CSS for styled app 1`] = `
"
.rn-backgroundColor-1e4kli0{background-color:purple}
.rn-borderTopWidth-10pzpfo{border-top-width:1234px}
.rn-borderRightWidth-1y24uml{border-right-width:1234px}
.rn-borderBottomWidth-98wxn4{border-bottom-width:1234px}
.rn-borderLeftWidth-150mub4{border-left-width:1234px}"
`;
exports[`AppRegistry getApplication returns "element" and "getStyleElement" 1`] = `
<AppContainer
WrapperComponent={undefined}
rootTag={Object {}}
>
<RootComponent />
</AppContainer>
`;
exports[`AppRegistry getApplication returns "element" and "getStyleElement" 2`] = `
"<style id=\\"react-native-stylesheet\\">@media all{
html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:rgba(0,0,0,0);}
body{margin:0;}
button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0;}
input::-webkit-inner-spin-button,input::-webkit-outer-spin-button,input::-webkit-search-cancel-button,input::-webkit-search-decoration,input::-webkit-search-results-button,input::-webkit-search-results-decoration{display:none;}
}</style>"
`;
exports[`CSS for an unstyled app 1`] = `
"@media all{
html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:rgba(0,0,0,0);}
body{margin:0;}
button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0;}
input::-webkit-inner-spin-button,input::-webkit-outer-spin-button,input::-webkit-search-cancel-button,input::-webkit-search-decoration,input::-webkit-search-results-button,input::-webkit-search-results-decoration{display:none;}
}
.rn-pointerEvents-12vffkv > *{pointer-events:auto}
.rn-pointerEvents-12vffkv{pointer-events:none !important}
.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}
.rn-borderLeftStyle-mm0ijv{border-left-style:solid}
.rn-borderTopWidth-13yce4e{border-top-width:0px}
.rn-borderRightWidth-fnigne{border-right-width:0px}
.rn-borderBottomWidth-ndvcnb{border-bottom-width:0px}
.rn-borderLeftWidth-gxnn5r{border-left-width:0px}
.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-flexShrink-1pxmb3b{-ms-flex-negative:0 !important;-webkit-flex-shrink:0 !important;flex-shrink:0 !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}
.rn-marginLeft-11wrixw{margin-left:0px}
.rn-minHeight-ifefl9{min-height:0px}
.rn-minWidth-bcqeeo{min-width:0px}
.rn-paddingTop-wk8lta{padding-top:0px}
.rn-paddingRight-9aemit{padding-right:0px}
.rn-paddingBottom-1mdbw0j{padding-bottom:0px}
.rn-paddingLeft-gy4na3{padding-left:0px}
.rn-position-bnwqim{position:relative}
.rn-zIndex-1lgpqti{z-index:0}
.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}
.rn-flexShrink-1awmn5t{-ms-flex-negative:1 !important;-webkit-flex-shrink:1 !important;flex-shrink:1 !important}"
`;

View File

@@ -1,188 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`apis/AppRegistry/renderApplication getApplication 1`] = `
<AppContainer
rootTag={Object {}}
>
<RootComponent />
</AppContainer>
`;
exports[`apis/AppRegistry/renderApplication getApplication 2`] = `
"<style id=\\"react-native-stylesheet-static\\">html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:rgba(0,0,0,0);}
body{margin:0;}
button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0;}
input::-webkit-inner-spin-button,input::-webkit-outer-spin-button,input::-webkit-search-cancel-button,input::-webkit-search-decoration,input::-webkit-search-results-button,input::-webkit-search-results-decoration{display:none;}
@keyframes rn-ActivityIndicator-animation{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg);}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg);}}
@keyframes rn-ProgressBar-animation{0%{-webkit-transform:translateX(-100%);transform:translateX(-100%);}100%{-webkit-transform:translateX(400%);transform:translateX(400%);}}
.rn-pointerEvents-105ug2t{pointer-events:auto !important;}
.rn-pointerEvents-ah5dr5{pointer-events:auto !important;}
.rn-pointerEvents-633pao{pointer-events:none !important;}
.rn-pointerEvents-12vffkv{pointer-events:none !important;}
.rn-pointerEvents-12vffkv > *{pointer-events:auto;}
.rn-pointerEvents-ah5dr5 > *{pointer-events:none;}</style>"
`;
exports[`apis/AppRegistry/renderApplication getApplication 3`] = `
"<style id=\\"react-native-stylesheet\\">.rn-bottom-1p0dtai{bottom:0px}
.rn-left-1d2f490{left:0px}
.rn-left-1fe0xdi{left:0%}
.rn-left-7b7h2f{left:100%}
.rn-position-u8s1d{position:absolute}
.rn-position-bnwqim{position:relative}
.rn-right-zchlnj{right:0px}
.rn-top-ipm5af{top:0px}
.rn-cursor-1loqt21{cursor:pointer}
.rn-cursor-7q8q6z{cursor:default}
.rn-cursor-1ei5mc7{cursor:inherit}
.rn-appearance-30o5oe{-moz-appearance:none;-webkit-appearance:none;appearance:none}
.rn-backgroundColor-wib322{background-color:transparent}
.rn-backgroundColor-8ndhhv{background-color:rgba(33,150,243,1)}
.rn-backgroundColor-15al3ab{background-color:rgba(223,223,223,1)}
.rn-backgroundColor-44z8sh{background-color:rgba(255,255,255,1)}
.rn-backgroundColor-5itogg{background-color:rgba(0,150,136,1)}
.rn-backgroundColor-1v82r4u{background-color:rgba(170,184,194,1)}
.rn-backgroundColor-1hj8efq{background-color:rgba(213,213,213,1)}
.rn-backgroundColor-1bgzomc{background-color:rgba(189,189,189,1)}
.rn-color-homxoj{color:inherit}
.rn-color-1qtguxu{color:rgba(255,255,255,1)}
.rn-color-istcb5{color:rgba(161,161,161,1)}
.rn-font-1lw9tu2{font:inherit}
.rn-textAlign-1ttztb7{text-align:inherit}
.rn-textAlign-q4m81j{text-align:center}
.rn-textDecoration-bauka4{text-decoration:none}
.rn-listStyle-1ebb2ja{list-style:none}
.rn-alignItems-1oszu61{-ms-flex-align:stretch;-webkit-align-items:stretch;-webkit-box-align:stretch;align-items:stretch}
.rn-alignItems-1awozwy{-ms-flex-align:center;-webkit-align-items:center;-webkit-box-align:center;align-items:center}
.rn-borderTopStyle-1efd50x{border-top-style:solid}
.rn-borderRightStyle-14skgim{border-right-style:solid}
.rn-borderBottomStyle-rull8r{border-bottom-style:solid}
.rn-borderLeftStyle-mm0ijv{border-left-style:solid}
.rn-borderTopWidth-13yce4e{border-top-width:0px}
.rn-borderTopWidth-1jxfwug{border-top-width:2px}
.rn-borderRightWidth-fnigne{border-right-width:0px}
.rn-borderRightWidth-18p6if4{border-right-width:2px}
.rn-borderBottomWidth-ndvcnb{border-bottom-width:0px}
.rn-borderBottomWidth-wgabs5{border-bottom-width:2px}
.rn-borderLeftWidth-gxnn5r{border-left-width:0px}
.rn-borderLeftWidth-dwliz8{border-left-width:2px}
.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-display-1471scf{display:inline}
.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-flexDirection-18u37iz{-ms-flex-direction:row;-webkit-box-direction:normal;-webkit-box-orient:horizontal;-webkit-flex-direction:row;flex-direction:row}
.rn-marginTop-1mnahxq{margin-top:0px}
.rn-marginTop-1t01tom{margin-top:auto}
.rn-marginRight-61z16t{margin-right:0px}
.rn-marginRight-lchren{margin-right:auto}
.rn-marginBottom-p1pxzi{margin-bottom:0px}
.rn-marginBottom-1qahzrx{margin-bottom:auto}
.rn-marginLeft-11wrixw{margin-left:0px}
.rn-marginLeft-1jj8364{margin-left:auto}
.rn-minHeight-ifefl9{min-height:0px}
.rn-minWidth-bcqeeo{min-width:0px}
.rn-paddingTop-wk8lta{padding-top:0px}
.rn-paddingTop-tskmnb{padding-top:8px}
.rn-paddingRight-9aemit{padding-right:0px}
.rn-paddingRight-1pyaxff{padding-right:8px}
.rn-paddingBottom-1mdbw0j{padding-bottom:0px}
.rn-paddingBottom-xd6kpl{padding-bottom:8px}
.rn-paddingLeft-gy4na3{padding-left:0px}
.rn-paddingLeft-1m04atk{padding-left:8px}
.rn-zIndex-1lgpqti{z-index:0}
.rn-zIndex-1wyyakw{z-index:-1}
.rn-backgroundPosition-vvn4in{background-position:center}
.rn-backgroundRepeat-u6sd8q{background-repeat:no-repeat}
.rn-backgroundRepeat-17leim2{background-repeat:repeat}
.rn-backgroundSize-4gszlv{background-size:cover}
.rn-backgroundSize-1sxrcry{background-size:auto}
.rn-backgroundSize-ehq7j7{background-size:contain}
.rn-backgroundSize-x3cy2q{background-size:100% 100%}
.rn-height-1pi2tsx{height:100%}
.rn-height-z80fyv{height:20px}
.rn-height-1r8g8re{height:36px}
.rn-height-10ptun7{height:16px}
.rn-height-4v7adb{height:5px}
.rn-height-1dernwh{height:70%}
.rn-opacity-1272l3b{opacity:0}
.rn-opacity-6dt33c{opacity:1}
.rn-width-13qz1uu{width:100%}
.rn-width-19wmn03{width:20px}
.rn-width-1acpoxo{width:36px}
.rn-width-1janqcz{width:16px}
.rn-touchAction-19z077z{-ms-touch-action:none;touch-action:none}
.rn-touchAction-1gvxusu{-ms-touch-action:manipulate;touch-action:manipulate}
.rn-WebkitOverflowScrolling-150rngu{-webkit-overflow-scrolling:touch}
.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}
.rn-overflowX-11yh6sk{overflow-x:hidden}
.rn-overflowX-lltvgl{overflow-x:auto}
.rn-overflowY-1rnoaur{overflow-y:auto}
.rn-overflowY-buy8e9{overflow-y:hidden}
.rn-transform-emqnss{-webkit-transform:translateZ(0px);transform:translateZ(0px)}
.rn-fontFamily-10u92zi{font-family:-apple-system, BlinkMacSystemFont, \\"Segoe UI\\", Roboto, Ubuntu, \\"Helvetica Neue\\", sans-serif}
.rn-fontFamily-poiln3{font-family:inherit}
.rn-fontSize-1b43r93{font-size:14px}
.rn-fontSize-7cikom{font-size:inherit}
.rn-whiteSpace-q42fyq{white-space:pre-wrap}
.rn-whiteSpace-irrty{white-space:inherit}
.rn-whiteSpace-3s2u2q{white-space:nowrap}
.rn-wordWrap-qvutc0{word-wrap:break-word}
.rn-userSelect-lrvibr{-moz-user-select:none;-ms-user-select:none;-webkit-user-select:none;user-select:none}
.rn-maxWidth-dnmrzs{max-width:100%}
.rn-textOverflow-1udbk01{text-overflow:ellipsis}
.rn-justifyContent-1777fci{-ms-flex-pack:center;-webkit-box-pack:center;-webkit-justify-content:center;justify-content:center}
.rn-visibility-11j9u27{visibility:hidden}
.rn-animationDuration-17bb2tj{-webkit-animation-duration:0.75s;animation-duration:0.75s}
.rn-animationDuration-1ay1djp{-webkit-animation-duration:1s;animation-duration:1s}
.rn-animationIterationCount-1muvv40{-webkit-animation-iteration-count:infinite;animation-iteration-count:infinite}
.rn-animationName-dozj4v{-webkit-animation-name:rn-ActivityIndicator-animation;animation-name:rn-ActivityIndicator-animation}
.rn-animationName-141g9a{-webkit-animation-name:rn-ProgressBar-animation;animation-name:rn-ProgressBar-animation}
.rn-animationTimingFunction-1ldzwu0{-webkit-animation-timing-function:linear;animation-timing-function:linear}
.rn-animationPlayState-1abnn5w{-webkit-animation-play-state:paused;animation-play-state:paused}
.rn-transitionDuration-eafdt9{-webkit-transition-duration:0.15s;transition-duration:0.15s}
.rn-transitionDuration-13tjlyg{-webkit-transition-duration:0.1s;transition-duration:0.1s}
.rn-transitionProperty-1i6wzkk{-moz-transition-property:opacity;-webkit-transition-property:opacity;transition-property:opacity}
.rn-borderTopLeftRadius-1iymjk7{border-top-left-radius:2px}
.rn-borderTopLeftRadius-jt3ufn{border-top-left-radius:100%}
.rn-borderTopLeftRadius-ou6ah9{border-top-left-radius:0px}
.rn-borderTopRightRadius-s2skl2{border-top-right-radius:2px}
.rn-borderTopRightRadius-1e868j9{border-top-right-radius:100%}
.rn-borderTopRightRadius-t12b5v{border-top-right-radius:0px}
.rn-borderBottomRightRadius-l5bh9y{border-bottom-right-radius:2px}
.rn-borderBottomRightRadius-ujv9e3{border-bottom-right-radius:100%}
.rn-borderBottomRightRadius-zmljjp{border-bottom-right-radius:0px}
.rn-borderBottomLeftRadius-101sy47{border-bottom-left-radius:2px}
.rn-borderBottomLeftRadius-1hakmuk{border-bottom-left-radius:100%}
.rn-borderBottomLeftRadius-pm2fo{border-bottom-left-radius:0px}
.rn-fontWeight-majxgm{font-weight:500}
.rn-textTransform-tsynxw{text-transform:uppercase}
.rn-borderTopColor-j4x2lb{border-top-color:rgba(101,119,134,1)}
.rn-borderTopColor-gj2eto{border-top-color:rgba(0,150,136,1)}
.rn-borderTopColor-1j7vz2b{border-top-color:rgba(204,214,221,1)}
.rn-borderTopColor-2dclza{border-top-color:rgba(170,184,194,1)}
.rn-borderTopColor-kqr9px{border-top-color:black}
.rn-borderRightColor-12i18q1{border-right-color:rgba(101,119,134,1)}
.rn-borderRightColor-31ud7z{border-right-color:rgba(0,150,136,1)}
.rn-borderRightColor-10fg1ub{border-right-color:rgba(204,214,221,1)}
.rn-borderRightColor-8jf312{border-right-color:rgba(170,184,194,1)}
.rn-borderRightColor-q0dj5p{border-right-color:black}
.rn-borderBottomColor-mg3rfb{border-bottom-color:rgba(101,119,134,1)}
.rn-borderBottomColor-1bgnb8i{border-bottom-color:rgba(0,150,136,1)}
.rn-borderBottomColor-zxuuv6{border-bottom-color:rgba(204,214,221,1)}
.rn-borderBottomColor-1yeakrt{border-bottom-color:rgba(170,184,194,1)}
.rn-borderBottomColor-1ah7hsa{border-bottom-color:black}
.rn-borderLeftColor-vnhemr{border-left-color:rgba(101,119,134,1)}
.rn-borderLeftColor-tbzcuz{border-left-color:rgba(0,150,136,1)}
.rn-borderLeftColor-1p34dw6{border-left-color:rgba(204,214,221,1)}
.rn-borderLeftColor-bluj2i{border-left-color:rgba(170,184,194,1)}
.rn-borderLeftColor-137uh4u{border-left-color:black}
.rn-backgroundImage-rs94m5{background-image:url(\\"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgdmVyc2lvbj0iMS4xIgogICB2aWV3Qm94PSIwIDAgMSAxIgogICBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWluWU1pbiBtZWV0Ij4KICA8cGF0aAogICAgIGQ9Ik0gMC4wNDAzODA1OSwwLjYyNjc3NjcgMC4xNDY0NDY2MSwwLjUyMDcxMDY4IDAuNDI5Mjg5MzIsMC44MDM1NTMzOSAwLjMyMzIyMzMsMC45MDk2MTk0MSB6IE0gMC4yMTcxNTcyOSwwLjgwMzU1MzM5IDAuODUzNTUzMzksMC4xNjcxNTcyOSAwLjk1OTYxOTQxLDAuMjczMjIzMyAwLjMyMzIyMzMsMC45MDk2MTk0MSB6IgogICAgIGlkPSJyZWN0Mzc4MCIKICAgICBzdHlsZT0iZmlsbDojZmZmZmZmO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lIiAvPgo8L3N2Zz4K\\")}
.rn-alignSelf-k200y{-ms-flex-item-align:start;-webkit-align-self:flex-start;align-self:flex-start}
.rn-boxShadow-1ewcgjf{box-shadow:0px 1px 3px rgba(0,0,0,0.5)}
.rn-resize-1dz5y72{resize:none}</style>"
`;

View File

@@ -0,0 +1,80 @@
/* eslint-env jasmine, jest */
import AppRegistry from '..';
import ExecutionEnvironment from 'fbjs/lib/ExecutionEnvironment';
import React from 'react';
import ReactDOMServer from 'react-dom/server';
import { render } from 'enzyme';
import StyleSheet from '../../StyleSheet';
import View from '../../View';
const RootComponent = () => <div />;
describe('AppRegistry', () => {
describe('getApplication', () => {
const canUseDOM = ExecutionEnvironment.canUseDOM;
beforeEach(() => {
ExecutionEnvironment.canUseDOM = false;
});
afterEach(() => {
ExecutionEnvironment.canUseDOM = canUseDOM;
});
test('returns "element" and "getStyleElement"', () => {
AppRegistry.registerComponent('App', () => RootComponent);
const { element, getStyleElement } = AppRegistry.getApplication('App', {});
expect(element).toMatchSnapshot();
expect(ReactDOMServer.renderToStaticMarkup(getStyleElement())).toMatchSnapshot();
});
test('"getStyleElement" produces styles that are a function of rendering "element"', () => {
const getTextContent = getStyleElement =>
getStyleElement().props.dangerouslySetInnerHTML.__html;
// First "RootComponent" render
AppRegistry.registerComponent('App1', () => RootComponent);
let app = AppRegistry.getApplication('App1', {});
render(app.element);
const first = getTextContent(app.getStyleElement);
// Next render is a different tree; the style sheet should be different
const styles = StyleSheet.create({ root: { borderWidth: 1234, backgroundColor: 'purple' } });
const Component = () => <View style={styles.root} />;
AppRegistry.registerComponent('App2', () => Component);
app = AppRegistry.getApplication('App2', {});
render(app.element);
const second = getTextContent(app.getStyleElement);
const diff = second.split(first)[1];
expect(first).toMatchSnapshot('CSS for an unstyled app');
expect(diff).toMatchSnapshot('Additional CSS for styled app');
expect(first).not.toEqual(second);
// Final render is once again "RootComponent"; the style sheet should not
// be polluted by earlier rendering of a different tree
app = AppRegistry.getApplication('App1', {});
render(app.element);
const third = getTextContent(app.getStyleElement);
expect(first).toEqual(third);
});
});
describe('runApplication', () => {
test('callback after render', () => {
AppRegistry.registerComponent('App', () => RootComponent);
const callback = jest.fn();
const rootTag = document.createElement('div');
rootTag.id = 'react-root';
document.body.appendChild(rootTag);
AppRegistry.runApplication('App', { initialProps: {}, rootTag, callback });
expect(callback).toHaveBeenCalledTimes(1);
document.body.removeChild(rootTag);
});
});
});

View File

@@ -1,19 +0,0 @@
/* eslint-env jasmine, jest */
import { getApplication } from '../renderApplication';
import React from 'react';
import ReactDOMServer from 'react-dom/server';
const RootComponent = () => <div />;
describe('apis/AppRegistry/renderApplication', () => {
test('getApplication', () => {
const { element, stylesheets } = getApplication(RootComponent, {});
expect(element).toMatchSnapshot();
stylesheets.forEach(sheet => {
const result = ReactDOMServer.renderToStaticMarkup(sheet);
expect(result).toMatchSnapshot();
});
});
});

View File

@@ -1,9 +1,8 @@
/**
* Copyright (c) 2015-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
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule AppRegistry
@@ -19,6 +18,15 @@ const emptyObject = {};
const runnables = {};
export type ComponentProvider = () => ComponentType<any>;
export type ComponentProviderInstrumentationHook = (
component: ComponentProvider
) => ComponentType<any>;
export type WrapperComponentProvider = any => ComponentType<*>;
let componentProviderInstrumentationHook: ComponentProviderInstrumentationHook = (
component: ComponentProvider
) => component();
let wrapperComponentProvider: ?WrapperComponentProvider;
export type AppConfig = {
appKey: string,
@@ -45,12 +53,22 @@ export default class AppRegistry {
return runnables[appKey].getApplication(appParameters);
}
static registerComponent(appKey: string, getComponentFunc: ComponentProvider): string {
static registerComponent(appKey: string, componentProvider: ComponentProvider): string {
runnables[appKey] = {
getApplication: ({ initialProps } = emptyObject) =>
getApplication(getComponentFunc(), initialProps),
run: ({ initialProps = emptyObject, rootTag }) =>
renderApplication(getComponentFunc(), initialProps, rootTag)
getApplication: appParameters =>
getApplication(
componentProviderInstrumentationHook(componentProvider),
appParameters.initialProps || emptyObject,
wrapperComponentProvider && wrapperComponentProvider(appParameters)
),
run: appParameters =>
renderApplication(
componentProviderInstrumentationHook(componentProvider),
appParameters.initialProps || emptyObject,
appParameters.rootTag,
wrapperComponentProvider && wrapperComponentProvider(appParameters),
appParameters.callback
)
};
return appKey;
}
@@ -78,9 +96,9 @@ export default class AppRegistry {
params.rootTag = `#${params.rootTag.id}`;
console.log(
`Running application "${appKey}" with appParams: ${JSON.stringify(params)}. ` +
`development-level warnings are ${isDevelopment ? 'ON' : 'OFF'}, ` +
`performance optimizations are ${isDevelopment ? 'OFF' : 'ON'}`
`Running application "${appKey}" with appParams: ${JSON.stringify(params)}.\n` +
`Development-level warnings: ${isDevelopment ? 'ON' : 'OFF'}.\n` +
`Performance optimizations: ${isDevelopment ? 'OFF' : 'ON'}.`
);
invariant(
@@ -92,6 +110,14 @@ export default class AppRegistry {
runnables[appKey].run(appParameters);
}
static setComponentProviderInstrumentationHook(hook: ComponentProviderInstrumentationHook) {
componentProviderInstrumentationHook = hook;
}
static setWrapperComponentProvider(provider: WrapperComponentProvider) {
wrapperComponentProvider = provider;
}
static unmountApplicationComponentAtRootTag(rootTag: Object) {
unmountComponentAtNode(rootTag);
}

View File

@@ -1,9 +1,8 @@
/**
* Copyright (c) 2015-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
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
@@ -13,7 +12,7 @@ import AppContainer from './AppContainer';
import invariant from 'fbjs/lib/invariant';
import hydrate from '../../modules/hydrate';
import render from '../render';
import StyleSheet from '../StyleSheet';
import styleResolver from '../StyleSheet/styleResolver';
import React, { type ComponentType } from 'react';
const renderFn = process.env.NODE_ENV !== 'production' ? render : hydrate;
@@ -21,27 +20,35 @@ const renderFn = process.env.NODE_ENV !== 'production' ? render : hydrate;
export default function renderApplication<Props: Object>(
RootComponent: ComponentType<Props>,
initialProps: Props,
rootTag: any
rootTag: any,
WrapperComponent?: ?ComponentType<*>,
callback?: () => void
) {
invariant(rootTag, 'Expect to have a valid rootTag, instead got ', rootTag);
renderFn(
<AppContainer rootTag={rootTag}>
<AppContainer WrapperComponent={WrapperComponent} rootTag={rootTag}>
<RootComponent {...initialProps} />
</AppContainer>,
rootTag
rootTag,
callback
);
}
export function getApplication(RootComponent: ComponentType<Object>, initialProps: Object): Object {
export function getApplication(
RootComponent: ComponentType<Object>,
initialProps: Object,
WrapperComponent?: ?ComponentType<*>
): Object {
const element = (
<AppContainer rootTag={{}}>
<AppContainer WrapperComponent={WrapperComponent} rootTag={{}}>
<RootComponent {...initialProps} />
</AppContainer>
);
const stylesheets = StyleSheet.getStyleSheets().map(sheet => (
// ensure that CSS text is not escaped
<style dangerouslySetInnerHTML={{ __html: sheet.textContent }} id={sheet.id} key={sheet.id} />
));
return { element, stylesheets };
// Don't escape CSS text
const getStyleElement = () => {
const sheet = styleResolver.getStyleSheet();
return <style dangerouslySetInnerHTML={{ __html: sheet.textContent }} id={sheet.id} />;
};
return { element, getStyleElement };
}

View File

@@ -1,9 +1,8 @@
/**
* Copyright (c) 2016-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
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule AppState

View File

@@ -1,9 +1,8 @@
/**
* Copyright (c) 2015-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
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule AsyncStorage

View File

@@ -1,9 +1,8 @@
/**
* Copyright (c) 2016-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
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule BackHandler

View File

@@ -1,9 +1,7 @@
/**
* Copyright (c) 2016-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
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule Button

View File

@@ -1,9 +1,8 @@
/**
* Copyright (c) 2017-present, Nicolas Gallagher.
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule CheckBox

View File

@@ -1,9 +1,8 @@
/**
* Copyright (c) 2016-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
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule Clipboard

View File

@@ -1,10 +1,8 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule ColorPropType
* @noflow

View File

@@ -1,9 +1,8 @@
/**
* Copyright (c) 2015-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
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule Dimensions

View File

@@ -1,8 +1,7 @@
/**
* Copyright (c) 2016-present, Nicolas Gallagher.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow

View File

@@ -1,10 +1,8 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule EdgeInsetsPropType
* @flow

View File

@@ -2,44 +2,100 @@
import I18nManager from '..';
const getDocumentDir = () => document.documentElement.getAttribute('dir');
describe('apis/I18nManager', () => {
describe('when RTL not enabled', () => {
describe('preferred language is LTR', () => {
beforeEach(() => {
I18nManager.setPreferredLanguageRTL(false);
});
test('is "false" by default', () => {
expect(I18nManager.isRTL).toEqual(false);
expect(document.documentElement.getAttribute('dir')).toEqual('ltr');
describe('isRTL', () => {
test('is false', () => {
expect(I18nManager.isRTL).toBe(false);
expect(getDocumentDir()).toEqual('ltr');
});
});
test('is "true" when forced', () => {
I18nManager.forceRTL(true);
expect(I18nManager.isRTL).toEqual(true);
expect(document.documentElement.getAttribute('dir')).toEqual('rtl');
I18nManager.forceRTL(false);
describe('forceRTL', () => {
test('when set to false, "isRTL" is false', () => {
I18nManager.forceRTL(false);
expect(I18nManager.isRTL).toBe(false);
expect(getDocumentDir()).toEqual('ltr');
});
test('when set to true, "isRTL" is true', () => {
I18nManager.forceRTL(true);
expect(I18nManager.isRTL).toBe(true);
expect(getDocumentDir()).toEqual('rtl');
I18nManager.forceRTL(false);
});
});
describe('swapLeftAndRightInRTL', () => {
test('when set to false, "doLeftAndRightSwapInRTL" is false', () => {
I18nManager.swapLeftAndRightInRTL(false);
expect(I18nManager.doLeftAndRightSwapInRTL).toBe(false);
});
test('when set to true, "doLeftAndRightSwapInRTL" is true', () => {
I18nManager.swapLeftAndRightInRTL(true);
expect(I18nManager.doLeftAndRightSwapInRTL).toBe(true);
});
});
});
describe('when RTL is enabled', () => {
describe('preferred language is RTL', () => {
beforeEach(() => {
I18nManager.setPreferredLanguageRTL(true);
});
afterEach(() => {
afterAll(() => {
I18nManager.setPreferredLanguageRTL(false);
});
test('is "true" by default', () => {
expect(I18nManager.isRTL).toEqual(true);
expect(document.documentElement.getAttribute('dir')).toEqual('rtl');
describe('isRTL', () => {
test('is true', () => {
expect(I18nManager.isRTL).toBe(true);
expect(getDocumentDir()).toEqual('rtl');
});
});
test('is "false" when not allowed', () => {
I18nManager.allowRTL(false);
expect(I18nManager.isRTL).toEqual(false);
expect(document.documentElement.getAttribute('dir')).toEqual('ltr');
I18nManager.allowRTL(true);
describe('allowRTL', () => {
test('when set to false, "isRTL" is false', () => {
I18nManager.allowRTL(false);
expect(I18nManager.isRTL).toBe(false);
expect(getDocumentDir()).toEqual('ltr');
I18nManager.allowRTL(true);
});
test('when set to true, "isRTL" is true', () => {
I18nManager.allowRTL(true);
expect(I18nManager.isRTL).toBe(true);
expect(getDocumentDir()).toEqual('rtl');
});
});
describe('forceRTL', () => {
test('when set to false, "isRTL" is true', () => {
I18nManager.forceRTL(false);
expect(I18nManager.isRTL).toBe(true);
expect(getDocumentDir()).toEqual('rtl');
});
test('when set to true, "isRTL" is true', () => {
I18nManager.forceRTL(true);
expect(I18nManager.isRTL).toBe(true);
expect(getDocumentDir()).toEqual('rtl');
I18nManager.forceRTL(false);
});
});
describe('swapLeftAndRightInRTL', () => {
test('when set to false, "doLeftAndRightSwapInRTL" is false', () => {
I18nManager.swapLeftAndRightInRTL(false);
expect(I18nManager.doLeftAndRightSwapInRTL).toBe(false);
});
test('when set to true, "doLeftAndRightSwapInRTL" is true', () => {
I18nManager.swapLeftAndRightInRTL(true);
expect(I18nManager.doLeftAndRightSwapInRTL).toBe(true);
});
});
});
});

View File

@@ -1,9 +1,8 @@
/**
* Copyright (c) 2016-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
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule I18nManager
@@ -14,11 +13,14 @@ import ExecutionEnvironment from 'fbjs/lib/ExecutionEnvironment';
type I18nManagerStatus = {
allowRTL: (allowRTL: boolean) => void,
doLeftAndRightSwapInRTL: boolean,
forceRTL: (forceRTL: boolean) => void,
isRTL: boolean,
setPreferredLanguageRTL: (setRTL: boolean) => void,
isRTL: boolean
swapLeftAndRightInRTL: (flipStyles: boolean) => void
};
let doLeftAndRightSwapInRTL = true;
let isPreferredLanguageRTL = false;
let isRTLAllowed = true;
let isRTLForced = false;
@@ -30,7 +32,7 @@ const isRTL = () => {
return isRTLAllowed && isPreferredLanguageRTL;
};
const onChange = () => {
const onDirectionChange = () => {
if (ExecutionEnvironment.canUseDOM) {
if (document.documentElement && document.documentElement.setAttribute) {
document.documentElement.setAttribute('dir', isRTL() ? 'rtl' : 'ltr');
@@ -41,15 +43,21 @@ const onChange = () => {
const I18nManager: I18nManagerStatus = {
allowRTL(bool) {
isRTLAllowed = bool;
onChange();
onDirectionChange();
},
forceRTL(bool) {
isRTLForced = bool;
onChange();
onDirectionChange();
},
setPreferredLanguageRTL(bool) {
isPreferredLanguageRTL = bool;
onChange();
onDirectionChange();
},
swapLeftAndRightInRTL(bool) {
doLeftAndRightSwapInRTL = bool;
},
get doLeftAndRightSwapInRTL() {
return doLeftAndRightSwapInRTL;
},
get isRTL() {
return isRTL();

View File

@@ -2,16 +2,20 @@
* @flow
*/
import AnimationPropTypes from '../../modules/AnimationPropTypes';
import BorderPropTypes from '../../modules/BorderPropTypes';
import ColorPropType from '../ColorPropType';
import ImageResizeMode from './ImageResizeMode';
import InteractionPropTypes from '../../modules/InteractionPropTypes';
import LayoutPropTypes from '../../modules/LayoutPropTypes';
import ShadowPropTypes from '../../modules/ShadowPropTypes';
import TransformPropTypes from '../../modules/TransformPropTypes';
import { number, oneOf, string } from 'prop-types';
const ImageStylePropTypes = {
...AnimationPropTypes,
...BorderPropTypes,
...InteractionPropTypes,
...LayoutPropTypes,
...ShadowPropTypes,
...TransformPropTypes,
@@ -26,7 +30,8 @@ const ImageStylePropTypes = {
/**
* @platform web
*/
boxShadow: string
boxShadow: string,
filter: string
};
export default ImageStylePropTypes;

View File

@@ -180,6 +180,20 @@ describe('components/Image', () => {
.attr('src')
).toBe(uriTwo);
});
test('is correctly updated when missing in initial render', () => {
jest.useFakeTimers();
const uri = 'https://testing.com/img.jpg';
const component = mount(<Image />);
component.setProps({ source: { uri } });
jest.runOnlyPendingTimers();
expect(
component
.render()
.find('img')
.attr('src')
).toBe(uri);
});
});
describe('prop "style"', () => {

View File

@@ -1,9 +1,8 @@
/**
* Copyright (c) 2016-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
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule Image
@@ -57,7 +56,7 @@ const resolveAssetDimensions = source => {
}
};
const svgDataUriPattern = /^data:image\/svg\+xml;/;
const svgDataUriPattern = /^(data:image\/svg\+xml;utf8,)(.*)/;
const resolveAssetSource = source => {
let uri;
if (typeof source === 'number') {
@@ -72,12 +71,12 @@ const resolveAssetSource = source => {
uri = source || '';
}
// SVG data may contain characters (e.g., #, ") that need to be escaped
if (svgDataUriPattern.test(uri)) {
const parts = uri.split('<svg');
const [prefix, ...svgFragment] = parts;
const svg = encodeURIComponent(`<svg${svgFragment.join('<svg')}`);
return `${prefix}${svg}`;
const match = uri.match(svgDataUriPattern);
// inline SVG markup may contain characters (e.g., #, ") that need to be escaped
if (match) {
const [, prefix, svg] = match;
const encodedSvg = encodeURIComponent(svg);
return `${prefix}${encodedSvg}`;
}
return uri;
@@ -108,7 +107,13 @@ class Image extends Component<*, State> {
onLoadStart: func,
resizeMode: oneOf(Object.keys(ImageResizeMode)),
source: ImageSourcePropType,
style: StyleSheetPropType(ImageStylePropTypes)
style: StyleSheetPropType(ImageStylePropTypes),
// compatibility with React Native
/* eslint-disable react/sort-prop-types */
blurRadius: number,
capInsets: shape({ top: number, left: number, bottom: number, right: number }),
resizeMethod: oneOf(['auto', 'resize', 'scale'])
/* eslint-enable react/sort-prop-types */
};
static defaultProps = {
@@ -162,8 +167,8 @@ class Image extends Component<*, State> {
if (uri !== nextUri) {
ImageUriCache.remove(uri);
const isPreviouslyLoaded = ImageUriCache.has(nextUri);
isPreviouslyLoaded && ImageUriCache.add(uri);
this._updateImageState(getImageState(uri, isPreviouslyLoaded));
isPreviouslyLoaded && ImageUriCache.add(nextUri);
this._updateImageState(getImageState(nextUri, isPreviouslyLoaded));
}
}
@@ -184,10 +189,13 @@ class Image extends Component<*, State> {
source,
testID,
/* eslint-disable */
blurRadius,
capInsets,
onError,
onLoad,
onLoadEnd,
onLoadStart,
resizeMethod,
resizeMode,
/* eslint-enable */
...other

View File

@@ -1,10 +1,8 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/

View File

@@ -1,9 +1,8 @@
/**
* Copyright (c) 2016-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
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule InteractionManager
@@ -21,9 +20,18 @@ const InteractionManager = {
/**
* Schedule a function to run after all interactions have completed.
*/
runAfterInteractions(callback: Function) {
invariant(typeof callback === 'function', 'Must specify a function to schedule.');
callback();
runAfterInteractions(task: ?Function): { then: Function, done: Function, cancel: Function } {
console.warn('InteractionManager is not supported on web');
const promise = new Promise(resolve => {
if (task) {
resolve(task());
}
});
return {
then: promise.then.bind(promise),
done: () => {},
cancel: () => {}
};
},
/**

View File

@@ -1,9 +1,8 @@
/**
* Copyright (c) 2016-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
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule Keyboard

View File

@@ -1,9 +1,8 @@
/**
* 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
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule KeyboardAvoidingView

View File

@@ -1,9 +1,8 @@
/**
* Copyright (c) 2016-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
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule Linking
@@ -25,7 +24,7 @@ const Linking = {
},
openURL(url: string): Promise<Object | void> {
try {
iframeOpen(url);
open(url);
return Promise.resolve();
} catch (e) {
return Promise.reject(e);
@@ -33,36 +32,16 @@ const Linking = {
}
};
/**
* Tabs opened using JavaScript may redirect the parent tab using
* `window.opener.location`, ignoring cross-origin restrictions and enabling
* phishing attacks.
*
* Safari requires that we open the url by injecting a hidden iframe that calls
* window.open(), then removes the iframe from the DOM.
*
* https://mathiasbynens.github.io/rel-noopener/
*/
const iframeOpen = url => {
const noOpener = url.indexOf('mailto:') !== 0;
const open = url => {
const anchor = document.createElement('a');
anchor.target = '_blank'; // :(
anchor.rel = 'noopener';
anchor.href = url;
const body = document.body;
if (body) {
const iframe = document.createElement('iframe');
iframe.style.display = 'none';
body.appendChild(iframe);
const iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
const iframeBody = iframeDoc.body;
if (iframeBody) {
const script = iframeDoc.createElement('script');
const openerExpression = noOpener ? 'child.opener = null' : '';
script.text = `
window.parent = null; window.top = null; window.frameElement = null;
var child = window.open("${url}"); ${openerExpression};
`;
iframeBody.appendChild(script);
}
body.removeChild(iframe);
body.appendChild(anchor);
anchor.click();
body.removeChild(anchor);
}
};

View File

@@ -1,9 +1,8 @@
/**
* Copyright (c) 2015-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
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule NetInfo

View File

@@ -19,7 +19,8 @@ export default class PickerItem extends Component<Props> {
};
render() {
const { label, testID, value } = this.props;
return createElement('option', { testID, value }, label);
const { color, label, testID, value } = this.props;
const style = { color };
return createElement('option', { style, testID, value }, label);
}
}

View File

@@ -1,9 +1,8 @@
/**
* 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
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow

View File

@@ -1,9 +1,8 @@
/**
* 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
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow

View File

@@ -10,7 +10,7 @@ exports[`components/Picker prop "children" items 1`] = `
exports[`components/Picker prop "children" renders items 1`] = `
<select
className="rn-fontFamily-poiln3 rn-fontSize-7cikom rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw"
className="rn-fontFamily-14xgk7a rn-fontSize-7cikom rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw"
onChange={[Function]}
>
<PickerItem

View File

@@ -1,9 +1,8 @@
/**
* 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
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule Picker
@@ -24,7 +23,7 @@ import { arrayOf, bool, func, number, oneOfType, string } from 'prop-types';
const pickerStyleType = StyleSheetPropType(PickerStylePropTypes);
type Props = {
children?: Array<typeof PickerItem>,
children?: PickerItem | Array<typeof PickerItem>,
enabled?: boolean,
onValueChange?: Function,
selectedValue?: number | string,
@@ -38,7 +37,7 @@ type Props = {
class Picker extends Component<Props> {
static propTypes = {
children: arrayOf(PickerItemPropType),
children: oneOfType([PickerItemPropType, arrayOf(PickerItemPropType)]),
enabled: bool,
onValueChange: func,
selectedValue: oneOfType([number, string]),
@@ -83,7 +82,7 @@ class Picker extends Component<Props> {
const styles = StyleSheet.create({
initial: {
fontFamily: 'inherit',
fontFamily: 'System',
fontSize: 'inherit',
margin: 0
}

View File

@@ -1,9 +1,8 @@
/**
* Copyright (c) 2015-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
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule PixelRatio

View File

@@ -1,9 +1,8 @@
/**
* Copyright (c) 2016-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
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule Platform

View File

@@ -1,10 +1,8 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule PointPropType
* @flow

View File

@@ -1,8 +1,7 @@
/**
* Copyright (c) 2016-present, Nicolas Gallagher.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule ProgressBar
@@ -96,7 +95,12 @@ const styles = StyleSheet.create({
},
animation: {
animationDuration: '1s',
animationName: 'rn-ProgressBar-animation',
animationName: [
{
'0%': { transform: [{ translateX: '-100%' }] },
'100%': { transform: [{ translateX: '400%' }] }
}
],
animationTimingFunction: 'linear',
animationIterationCount: 'infinite'
}

View File

@@ -1,9 +1,8 @@
/**
* 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
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule RefreshControl

View File

@@ -1,14 +1,38 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule SafeAreaView
* @flow
*/
import React from 'react';
import StyleSheet from '../StyleSheet';
import View from '../View';
export default View;
import ViewPropTypes, { type ViewProps } from '../ViewPropTypes';
class SafeAreaView extends React.Component<ViewProps> {
static displayName = 'SafeAreaView';
static propTypes = {
...ViewPropTypes
};
render() {
const { style, ...rest } = this.props;
return <View {...rest} style={StyleSheet.compose(styles.root, style)} />;
}
}
const styles = StyleSheet.create({
root: {
paddingTop: 'env(safe-area-inset-top)',
paddingRight: 'env(safe-area-inset-right)',
paddingBottom: 'env(safe-area-inset-bottom)',
paddingLeft: 'env(safe-area-inset-left)'
}
});
export default SafeAreaView;

View File

@@ -1,8 +1,7 @@
/**
* Copyright (c) 2016-present, Nicolas Gallagher.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow

View File

@@ -1,9 +1,8 @@
/**
* Copyright (c) 2016-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
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule ScrollView

View File

@@ -1,8 +1,7 @@
/**
* Copyright (c) 2016-present, Nicolas Gallagher.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule StatusBar

View File

@@ -0,0 +1,244 @@
/**
* Copyright (c) 2016-present, Nicolas Gallagher.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @noflow
*/
/**
* WARNING: changes to this file in particular can cause significant changes to
* the results of render performance benchmarks.
*/
import { canUseDOM } from 'fbjs/lib/ExecutionEnvironment';
import createReactDOMStyle from './createReactDOMStyle';
import flattenArray from '../../modules/flattenArray';
import flattenStyle from './flattenStyle';
import I18nManager from '../I18nManager';
import i18nStyle from './i18nStyle';
import { prefixInlineStyles } from '../../modules/prefixStyles';
import StyleSheetManager from './StyleSheetManager';
const emptyObject = {};
export default class ReactNativeStyleResolver {
_init() {
this.cache = { ltr: {}, rtl: {}, rtlNoSwap: {} };
this.injectedCache = { ltr: {}, rtl: {}, rtlNoSwap: {} };
this.styleSheetManager = new StyleSheetManager();
}
constructor() {
this._init();
}
getStyleSheet() {
// reset state on the server so critical css is always the result
const sheet = this.styleSheetManager.getStyleSheet();
if (!canUseDOM) {
this._init();
}
return sheet;
}
_injectRegisteredStyle(id) {
const { doLeftAndRightSwapInRTL, isRTL } = I18nManager;
const dir = isRTL ? (doLeftAndRightSwapInRTL ? 'rtl' : 'rtlNoSwap') : 'ltr';
if (!this.injectedCache[dir][id]) {
const style = flattenStyle(id);
const domStyle = createReactDOMStyle(i18nStyle(style));
Object.keys(domStyle).forEach(styleProp => {
const value = domStyle[styleProp];
if (value != null) {
this.styleSheetManager.injectDeclaration(styleProp, value);
}
});
this.injectedCache[dir][id] = true;
}
}
/**
* Resolves a React Native style object to DOM attributes
*/
resolve(style) {
if (!style) {
return emptyObject;
}
// fast and cachable
if (typeof style === 'number') {
this._injectRegisteredStyle(style);
const key = createCacheKey(style);
return this._resolveStyleIfNeeded(style, key);
}
// resolve a plain RN style object
if (!Array.isArray(style)) {
return this._resolveStyleIfNeeded(style);
}
// flatten the style array
// cache resolved props when all styles are registered
// otherwise fallback to resolving
const flatArray = flattenArray(style);
let isArrayOfNumbers = true;
for (let i = 0; i < flatArray.length; i++) {
const id = flatArray[i];
if (typeof id !== 'number') {
isArrayOfNumbers = false;
} else {
this._injectRegisteredStyle(id);
}
}
const key = isArrayOfNumbers ? createCacheKey(flatArray.join('-')) : null;
return this._resolveStyleIfNeeded(flatArray, key);
}
/**
* Resolves a React Native style object to DOM attributes, accounting for
* the existing styles applied to the DOM node.
*
* To determine the next style, some of the existing DOM state must be
* converted back into React Native styles.
*/
resolveWithNode(rnStyleNext, node) {
const { classList: rdomClassList, style: rdomStyle } = getDOMStyleInfo(node);
// Convert the DOM classList back into a React Native form
// Preserves unrecognized class names.
const { classList: rnClassList, style: rnStyle } = rdomClassList.reduce(
(styleProps, className) => {
const { prop, value } = this.styleSheetManager.getDeclaration(className);
if (prop) {
styleProps.style[prop] = value;
} else {
styleProps.classList.push(className);
}
return styleProps;
},
{ classList: [], style: {} }
);
// Create next DOM style props from current and next RN styles
const { classList: rdomClassListNext, style: rdomStyleNext } = this.resolve([
i18nStyle(rnStyle),
rnStyleNext
]);
// Final className
// Add the current class names not managed by React Native
const className = classListToString(rdomClassListNext.concat(rnClassList));
// Final style
// Next class names take priority over current inline styles
const style = { ...rdomStyle };
rdomClassListNext.forEach(className => {
const { prop } = this.styleSheetManager.getDeclaration(className);
if (style[prop]) {
style[prop] = '';
}
});
// Next inline styles take priority over current inline styles
Object.assign(style, rdomStyleNext);
return { className, style };
}
/**
* Resolves a React Native style object
*/
_resolveStyle(style) {
const flatStyle = flattenStyle(style);
const domStyle = createReactDOMStyle(i18nStyle(flatStyle));
const props = Object.keys(domStyle).reduce(
(props, styleProp) => {
const value = domStyle[styleProp];
if (value != null) {
const className = this.styleSheetManager.getClassName(styleProp, value);
if (className) {
props.classList.push(className);
} else {
// Certain properties and values are not transformed by 'createReactDOMStyle' as they
// require more complex transforms into multiple CSS rules. Here we assume that StyleManager
// can bind these styles to a className, and prevent them becoming invalid inline-styles.
if (
styleProp === 'pointerEvents' ||
styleProp === 'placeholderTextColor' ||
styleProp === 'animationName'
) {
const className = this.styleSheetManager.injectDeclaration(styleProp, value);
if (className) {
props.classList.push(className);
}
} else {
if (!props.style) {
props.style = {};
}
// 4x slower render
props.style[styleProp] = value;
}
}
}
return props;
},
{ classList: [] }
);
props.className = classListToString(props.classList);
if (props.style) {
props.style = prefixInlineStyles(props.style);
}
return props;
}
/**
* Caching layer over 'resolveStyle'
*/
_resolveStyleIfNeeded(style, key) {
if (key) {
const { doLeftAndRightSwapInRTL, isRTL } = I18nManager;
const dir = isRTL ? (doLeftAndRightSwapInRTL ? 'rtl' : 'rtlNoSwap') : 'ltr';
if (!this.cache[dir][key]) {
// slow: convert style object to props and cache
this.cache[dir][key] = this._resolveStyle(style);
}
return this.cache[dir][key];
}
return this._resolveStyle(style);
}
}
/**
* Misc helpers
*/
const createCacheKey = id => {
const prefix = 'rn';
return `${prefix}-${id}`;
};
const classListToString = list => list.join(' ').trim();
/**
* Copies classList and style data from a DOM node
*/
const hyphenPattern = /-([a-z])/g;
const toCamelCase = str => str.replace(hyphenPattern, m => m[1].toUpperCase());
const getDOMStyleInfo = node => {
const nodeStyle = node.style;
const classList = Array.prototype.slice.call(node.classList);
const style = {};
// DOM style is a CSSStyleDeclaration
// https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleDeclaration
for (let i = 0; i < nodeStyle.length; i += 1) {
const property = nodeStyle.item(i);
if (property) {
// DOM style uses hyphenated prop names and may include vendor prefixes
// Transform back into React DOM style.
style[toCamelCase(property)] = nodeStyle.getPropertyValue(property);
}
}
return { classList, style };
};

View File

@@ -1,161 +0,0 @@
/**
* 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.
*
* @noflow
*/
import { canUseDOM } from 'fbjs/lib/ExecutionEnvironment';
import generateCss from './generateCss';
import hash from '../../vendor/hash';
import staticCss from './staticCss';
const emptyObject = {};
const STYLE_ELEMENT_ID = 'react-native-stylesheet';
const createClassName = (prop, value) => {
const hashed = hash(prop + value);
return process.env.NODE_ENV !== 'production' ? `rn-${prop}-${hashed}` : `rn-${hashed}`;
};
const createCssRule = (className, prop, value) => {
const css = generateCss({ [prop]: value });
const selector = `.${className}`;
return `${selector}{${css}}`;
};
const pointerEvents = {
auto: createClassName('pointerEvents', 'auto'),
boxNone: createClassName('pointerEvents', 'box-none'),
boxOnly: createClassName('pointerEvents', 'box-only'),
none: createClassName('pointerEvents', 'none')
};
// See #513
const pointerEventsCss =
`.${pointerEvents.auto}{pointer-events:auto !important;}\n` +
`.${pointerEvents.boxOnly}{pointer-events:auto !important;}\n` +
`.${pointerEvents.none}{pointer-events:none !important;}\n` +
`.${pointerEvents.boxNone}{pointer-events:none !important;}\n` +
`.${pointerEvents.boxNone} > *{pointer-events:auto;}\n` +
`.${pointerEvents.boxOnly} > *{pointer-events:none;}`;
export default class StyleManager {
cache = null;
mainSheet = null;
constructor() {
// custom pointer event values are implemented using descendent selectors,
// so we manually create the CSS and pre-register the declarations
const pointerEventsPropName = 'pointerEvents';
this.cache = {
byClassName: {
[pointerEvents.auto]: { prop: pointerEventsPropName, value: 'auto' },
[pointerEvents.boxNone]: {
prop: pointerEventsPropName,
value: 'box-none'
},
[pointerEvents.boxOnly]: {
prop: pointerEventsPropName,
value: 'box-only'
},
[pointerEvents.none]: { prop: pointerEventsPropName, value: 'none' }
},
byProp: {
pointerEvents: {
auto: pointerEvents.auto,
'box-none': pointerEvents.boxNone,
'box-only': pointerEvents.boxOnly,
none: pointerEvents.none
}
}
};
// on the client we check for an existing style sheet before injecting style sheets
if (canUseDOM) {
const prerenderedStyleSheet = document.getElementById(STYLE_ELEMENT_ID);
if (prerenderedStyleSheet) {
this.mainSheet = prerenderedStyleSheet;
} else {
document.head.insertAdjacentHTML('afterbegin', this.getStyleSheetHtml());
this.mainSheet = document.getElementById(STYLE_ELEMENT_ID);
}
}
}
getClassName(prop, value) {
const cache = this.cache.byProp;
return cache[prop] && cache[prop].hasOwnProperty(value) && cache[prop][value];
}
getDeclaration(className) {
const cache = this.cache.byClassName;
return cache[className] || emptyObject;
}
getStyleSheetHtml() {
const styleSheets = this.getStyleSheets();
return styleSheets
.map(sheet => {
return `<style id="${sheet.id}">\n${sheet.textContent}\n</style>`;
})
.join('\n');
}
getStyleSheets() {
const cache = this.cache.byProp;
const mainSheetTextContext = Object.keys(cache)
.reduce((rules, prop) => {
if (prop !== 'pointerEvents') {
Object.keys(cache[prop]).forEach(value => {
const className = this.getClassName(prop, value);
const rule = createCssRule(className, prop, value);
rules.push(rule);
});
}
return rules;
}, [])
.join('\n');
return [
{
id: 'react-native-stylesheet-static',
textContent: `${staticCss}\n${pointerEventsCss}`
},
{
id: STYLE_ELEMENT_ID,
textContent: `${mainSheetTextContext}`
}
];
}
setDeclaration(prop, value) {
let className = this.getClassName(prop, value);
if (!className) {
className = createClassName(prop, value);
this._addToCache(className, prop, value);
if (canUseDOM) {
const sheet = this.mainSheet.sheet;
// avoid injecting if the rule already exists (e.g., server rendered, hot reload)
if (this.mainSheet.textContent.indexOf(className) === -1) {
const rule = createCssRule(className, prop, value);
sheet.insertRule(rule, sheet.cssRules.length);
}
}
}
return className;
}
_addToCache(className, prop, value) {
const cache = this.cache;
if (!cache.byProp[prop]) {
cache.byProp[prop] = {};
}
cache.byProp[prop][value] = className;
cache.byClassName[className] = { prop, value };
}
}

View File

@@ -1,198 +0,0 @@
/**
* 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.
*
* WARNING: changes to this file in particular can cause significant changes to
* the results of render performance benchmarks.
*
* @noflow
*/
import createReactDOMStyle from './createReactDOMStyle';
import flattenArray from '../../modules/flattenArray';
import flattenStyle from './flattenStyle';
import I18nManager from '../I18nManager';
import i18nStyle from './i18nStyle';
import { prefixInlineStyles } from '../../modules/prefixStyles';
import ReactNativePropRegistry from '../../modules/ReactNativePropRegistry';
import StyleManager from './StyleManager';
const emptyObject = {};
const createCacheKey = id => {
const prefix = 'rn';
return `${prefix}-${id}`;
};
const classListToString = list => list.join(' ').trim();
export default class StyleRegistry {
cache = { ltr: {}, rtl: {} };
styleManager = new StyleManager();
getStyleSheets() {
return this.styleManager.getStyleSheets();
}
/**
* Registers and precaches a React Native style object to HTML class names
*/
register(flatStyle) {
const id = ReactNativePropRegistry.register(flatStyle);
this._registerById(id);
return id;
}
_registerById(id) {
const dir = I18nManager.isRTL ? 'rtl' : 'ltr';
if (!this.cache[dir][id]) {
const style = flattenStyle(id);
const domStyle = createReactDOMStyle(i18nStyle(style));
Object.keys(domStyle).forEach(styleProp => {
const value = domStyle[styleProp];
if (value != null) {
this.styleManager.setDeclaration(styleProp, value);
}
});
this.cache[dir][id] = true;
}
}
/**
* Resolves a React Native style object to DOM attributes
*/
resolve(reactNativeStyle, options = emptyObject) {
if (!reactNativeStyle) {
return emptyObject;
}
// fast and cachable
if (typeof reactNativeStyle === 'number') {
this._registerById(reactNativeStyle);
const key = createCacheKey(reactNativeStyle);
return this._resolveStyleIfNeeded(reactNativeStyle, options, key);
}
// resolve a plain RN style object
if (!Array.isArray(reactNativeStyle)) {
return this._resolveStyle(reactNativeStyle, options);
}
// flatten the style array
// cache resolved props when all styles are registered
// otherwise fallback to resolving
const flatArray = flattenArray(reactNativeStyle);
let isArrayOfNumbers = true;
for (let i = 0; i < flatArray.length; i++) {
const id = flatArray[i];
if (typeof id !== 'number') {
isArrayOfNumbers = false;
} else {
this._registerById(id);
}
}
const key = isArrayOfNumbers ? createCacheKey(flatArray.join('-')) : null;
return this._resolveStyleIfNeeded(flatArray, options, key);
}
/**
* Resolves a React Native style object to DOM attributes, accounting for
* the existing styles applied to the DOM node.
*
* To determine the next style, some of the existing DOM state must be
* converted back into React Native styles.
*/
resolveStateful(rnStyleNext, domStyleProps, options) {
const { classList: rdomClassList, style: rdomStyle } = domStyleProps;
// Convert the DOM classList back into a React Native form
// Preserves unrecognized class names.
const { classList: rnClassList, style: rnStyle } = rdomClassList.reduce(
(styleProps, className) => {
const { prop, value } = this.styleManager.getDeclaration(className);
if (prop) {
styleProps.style[prop] = value;
} else {
styleProps.classList.push(className);
}
return styleProps;
},
{ classList: [], style: {} }
);
// Create next DOM style props from current and next RN styles
const { classList: rdomClassListNext, style: rdomStyleNext } = this.resolve(
[rnStyle, rnStyleNext],
options
);
// Next class names take priority over current inline styles
const style = { ...rdomStyle };
rdomClassListNext.forEach(className => {
const { prop } = this.styleManager.getDeclaration(className);
if (style[prop]) {
style[prop] = '';
}
});
// Next inline styles take priority over current inline styles
Object.assign(style, rdomStyleNext);
// Add the current class names not managed by React Native
const className = classListToString(rdomClassListNext.concat(rnClassList));
return { className, style };
}
/**
* Resolves a React Native style object
*/
_resolveStyle(reactNativeStyle, options) {
const flatStyle = flattenStyle(reactNativeStyle);
const domStyle = createReactDOMStyle(options.i18n === false ? flatStyle : i18nStyle(flatStyle));
const props = Object.keys(domStyle).reduce(
(props, styleProp) => {
const value = domStyle[styleProp];
if (value != null) {
const className = this.styleManager.getClassName(styleProp, value);
if (className) {
props.classList.push(className);
} else {
if (!props.style) {
props.style = {};
}
// 4x slower render
props.style[styleProp] = value;
}
}
return props;
},
{ classList: [] }
);
props.className = classListToString(props.classList);
if (props.style) {
props.style = prefixInlineStyles(props.style);
}
return props;
}
/**
* Caching layer over 'resolveStyle'
*/
_resolveStyleIfNeeded(style, options, key) {
const dir = I18nManager.isRTL ? 'rtl' : 'ltr';
if (key) {
if (!this.cache[dir][key]) {
// slow: convert style object to props and cache
this.cache[dir][key] = this._resolveStyle(style, options);
}
return this.cache[dir][key];
}
return this._resolveStyle(style, options);
}
}

View File

@@ -0,0 +1,50 @@
/**
* Copyright (c) 2016-present, Nicolas Gallagher.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule StyleSheet
* @noflow
*/
import flattenStyle from './flattenStyle';
import getHairlineWidth from './getHairlineWidth';
import ReactNativePropRegistry from '../../modules/ReactNativePropRegistry';
const absoluteFillObject = {
position: 'absolute',
left: 0,
right: 0,
top: 0,
bottom: 0
};
const absoluteFill = ReactNativePropRegistry.register(absoluteFillObject);
const StyleSheet = {
absoluteFill,
absoluteFillObject,
compose(style1, style2) {
if (style1 && style2) {
return [style1, style2];
} else {
return style1 || style2;
}
},
create(styles) {
const result = {};
Object.keys(styles).forEach(key => {
if (process.env.NODE_ENV !== 'production') {
const StyleSheetValidation = require('./StyleSheetValidation').default;
StyleSheetValidation.validateStyle(key, styles);
}
const id = styles[key] && ReactNativePropRegistry.register(styles[key]);
result[key] = id;
});
return result;
},
flatten: flattenStyle,
hairlineWidth: getHairlineWidth()
};
export default StyleSheet;

View File

@@ -0,0 +1,84 @@
/**
* Copyright (c) 2016-present, Nicolas Gallagher.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @noflow
*/
import createAtomicRules from './createAtomicRules';
import hash from '../../vendor/hash';
import initialRules from './initialRules';
import WebStyleSheet from './WebStyleSheet';
const emptyObject = {};
const STYLE_ELEMENT_ID = 'react-native-stylesheet';
const createClassName = (prop, value) => {
const hashed = hash(prop + normalizeValue(value));
return process.env.NODE_ENV !== 'production' ? `rn-${prop}-${hashed}` : `rn-${hashed}`;
};
const normalizeValue = value => (typeof value === 'object' ? JSON.stringify(value) : value);
export default class StyleSheetManager {
_cache = {
byClassName: {},
byProp: {}
};
constructor() {
this._sheet = new WebStyleSheet(STYLE_ELEMENT_ID);
initialRules.forEach(rule => {
this._sheet.insertRuleOnce(rule);
});
}
getClassName(prop, value) {
const val = normalizeValue(value);
const cache = this._cache.byProp;
return cache[prop] && cache[prop].hasOwnProperty(val) && cache[prop][val];
}
getDeclaration(className) {
const cache = this._cache.byClassName;
return cache[className] || emptyObject;
}
getStyleSheet() {
const { cssText } = this._sheet;
return {
id: STYLE_ELEMENT_ID,
textContent: cssText
};
}
injectDeclaration(prop, value): string {
const val = normalizeValue(value);
let className = this.getClassName(prop, val);
if (!className) {
className = createClassName(prop, val);
this._addToCache(className, prop, val);
const rules = createAtomicRules(`.${className}`, prop, value);
rules.forEach(rule => {
this._sheet.insertRuleOnce(rule);
});
}
return className;
}
injectKeyframe(): string {
// return identifier;
}
_addToCache(className, prop, value) {
const cache = this._cache;
if (!cache.byProp[prop]) {
cache.byProp[prop] = {};
}
cache.byProp[prop][value] = className;
cache.byClassName[className] = { prop, value };
}
}

View File

@@ -1,9 +1,8 @@
/**
* Copyright (c) 2016-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
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule StyleSheetValidation
@@ -26,6 +25,8 @@ const ReactPropTypesSecret = 'SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED';
export default class StyleSheetValidation {
static validateStyleProp(prop: string, style: Object, caller: string) {
if (process.env.NODE_ENV !== 'production') {
const value = style[prop];
const isCustomProperty = prop.indexOf('--') === 0;
if (isCustomProperty) return;
@@ -35,6 +36,12 @@ export default class StyleSheetValidation {
'\nValid style props: ' +
JSON.stringify(Object.keys(allStylePropTypes).sort(), null, ' ');
styleError(message1, style, caller, message2);
} else if (typeof value === 'string' && value.indexOf('!important') > -1) {
styleError(
`Invalid value of "${value}" set on prop "${prop}". Values cannot include "!important"`,
style,
caller
);
} else {
const error = allStylePropTypes[prop](
style,
@@ -92,9 +99,10 @@ StyleSheetValidation.addValidStylePropTypes({
clear: string,
cursor: string,
fill: string,
float: oneOf(['left', 'none', 'right']),
font: string /* @private */,
float: oneOf(['end', 'left', 'none', 'right', 'start']),
listStyle: string,
pointerEvents: string,
tableLayout: string
tableLayout: string,
/* @private */
MozAppearance: string
});

View File

@@ -0,0 +1,60 @@
/**
* Copyright (c) 2016-present, Nicolas Gallagher.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/
import { canUseDOM } from 'fbjs/lib/ExecutionEnvironment';
export default class WebStyleSheet {
_cssRules = [];
_sheet = null;
_textContent = '';
constructor(id: string) {
let domStyleElement;
// on the client we check for an existing style sheet before injecting
if (canUseDOM) {
domStyleElement = document.getElementById(id);
if (!domStyleElement) {
const html = `<style id="${id}"></style>`;
if (document.head) {
document.head.insertAdjacentHTML('afterbegin', html);
domStyleElement = document.getElementById(id);
}
}
if (domStyleElement) {
// $FlowFixMe
this._sheet = domStyleElement.sheet;
this._textContent = domStyleElement.textContent;
}
}
}
containsRule(rule: string): boolean {
return this._cssRules.indexOf(rule) > -1;
}
get cssText(): string {
return this._cssRules.join('\n');
}
insertRuleOnce(rule: string, position: ?number) {
// Reduce chance of duplicate rules
if (!this.containsRule(rule)) {
this._cssRules.push(rule);
// Check whether a rule was part of any prerendered styles (textContent
// doesn't include styles injected via 'insertRule')
if (this._textContent.indexOf(rule) === -1 && this._sheet) {
const pos = position || this._sheet.cssRules.length;
this._sheet.insertRule(rule, pos);
}
}
}
}

View File

@@ -0,0 +1,147 @@
/* eslint-env jasmine, jest */
import I18nManager from '../../I18nManager';
import ReactNativePropRegistry from '../../../modules/ReactNativePropRegistry';
import ReactNativeStyleResolver from '../ReactNativeStyleResolver';
let styleResolver;
describe('StyleSheet/ReactNativeStyleResolver', () => {
beforeEach(() => {
styleResolver = new ReactNativeStyleResolver();
});
describe('resolve', () => {
const styleA = { borderWidth: 0, borderColor: 'red', width: 100 };
const styleB = {
position: 'absolute',
left: 50,
opacity: 0.5
};
const styleC = { width: 200 };
const testResolve = (a, b, c) => {
// no common properties, different resolving order, same result
const resolve1 = styleResolver.resolve([a, b]);
expect(resolve1).toMatchSnapshot();
const resolve2 = styleResolver.resolve([b, a]);
expect(resolve1).toEqual(resolve2);
// common properties, different resolving order, different result
const resolve3 = styleResolver.resolve([a, b, c]);
expect(resolve3).toMatchSnapshot();
const resolve4 = styleResolver.resolve([c, a, b]);
expect(resolve4).toMatchSnapshot();
expect(resolve3).not.toEqual(resolve4);
};
test('with register, resolves to className', () => {
const a = ReactNativePropRegistry.register(styleA);
const b = ReactNativePropRegistry.register(styleB);
const c = ReactNativePropRegistry.register(styleC);
testResolve(a, b, c);
});
test('with register before RTL, resolves to correct className', () => {
const a = ReactNativePropRegistry.register({ left: '12.34%' });
const b = ReactNativePropRegistry.register({ textAlign: 'left' });
const c = ReactNativePropRegistry.register({ marginLeft: 10 });
I18nManager.forceRTL(true);
const resolved1 = styleResolver.resolve([a, b, c]);
expect(resolved1).toMatchSnapshot();
I18nManager.swapLeftAndRightInRTL(false);
const resolved2 = styleResolver.resolve([a, b, c]);
expect(resolved2).toMatchSnapshot();
I18nManager.swapLeftAndRightInRTL(true);
I18nManager.forceRTL(false);
});
test('with register, resolves to mixed', () => {
const a = styleA;
const b = ReactNativePropRegistry.register(styleB);
const c = ReactNativePropRegistry.register(styleC);
testResolve(a, b, c);
});
test('without register, resolves to inline styles', () => {
testResolve(styleA, styleB, styleC);
});
test('resolves inline-style pointerEvents to classname', () => {
expect(styleResolver.resolve({ pointerEvents: 'box-none' })).toMatchSnapshot();
});
});
describe('resolveWithNode', () => {
let node;
beforeEach(() => {
node = document.createElement('div');
});
test('preserves unrelated class names', () => {
node.classList.add('unknown-class-1', 'unknown-class-2');
const resolved = styleResolver.resolveWithNode({}, node);
expect(resolved).toMatchSnapshot();
});
test('preserves unrelated inline styles', () => {
node.style.cssText = 'font-size: 20px;';
const resolved = styleResolver.resolveWithNode({ opacity: 1 }, node);
expect(resolved).toMatchSnapshot();
});
test('next class names have priority over current inline styles', () => {
node.style.cssText = 'opacity: 0.5;';
const nextStyle = ReactNativePropRegistry.register({ opacity: 1 });
const resolved = styleResolver.resolveWithNode(nextStyle, node);
expect(resolved).toMatchSnapshot();
});
test('next inline styles have priority over current inline styles', () => {
// note: this also checks for correctly uppercasing the first letter of DOM vendor prefixes
node.style.cssText = 'opacity: 0.5; transform: scale(1);';
const style = { opacity: 1, transform: [{ scale: 2 }] };
const resolved = styleResolver.resolveWithNode(style, node);
expect(resolved).toMatchSnapshot();
});
test('when isRTL=true, resolves to flipped inline styles', () => {
// note: DOM state resolved from { marginLeft: 5, left: 5 } in RTL mode
node.style.cssText = 'margin-right: 5px; right: 5px;';
I18nManager.forceRTL(true);
const resolved = styleResolver.resolveWithNode({ marginLeft: 10, right: 10 }, node);
I18nManager.forceRTL(false);
expect(resolved).toMatchSnapshot();
});
test('when isRTL=true, resolves to flipped classNames', () => {
// note: DOM state resolved from { marginLeft: 5, left: 5 }
node.style.cssText = 'margin-right: 5px; right: 5px;';
const nextStyle = ReactNativePropRegistry.register({ marginLeft: 10, right: 1 });
I18nManager.forceRTL(true);
const resolved = styleResolver.resolveWithNode(nextStyle, node);
I18nManager.forceRTL(false);
expect(resolved).toMatchSnapshot();
});
test('when isRTL=true & doLeftAndRightSwapInRTL=false, resolves to non-flipped inline styles', () => {
// note: DOM state resolved from { marginRight 5, right: 5, paddingEnd: 5 }
node.style.cssText = 'margin-right: 5px; right: 5px; padding-left: 5px';
I18nManager.forceRTL(true);
I18nManager.swapLeftAndRightInRTL(false);
const resolved = styleResolver.resolveWithNode(
{ marginRight: 10, right: 10, paddingEnd: 10 },
node
);
I18nManager.forceRTL(false);
I18nManager.swapLeftAndRightInRTL(true);
expect(resolved).toMatchSnapshot();
});
});
});

View File

@@ -1,39 +0,0 @@
/* eslint-env jasmine, jest */
import StyleManager from '../StyleManager';
let styleManager;
describe('apis/StyleSheet/StyleManager', () => {
beforeEach(() => {
styleManager = new StyleManager();
});
test('getClassName', () => {
expect(styleManager.getClassName('pointerEvents', 'box-only')).toMatchSnapshot();
const className = styleManager.setDeclaration('width', '100px');
expect(styleManager.getClassName('width', '100px')).toEqual(className);
});
test('getDeclaration', () => {
const className = styleManager.setDeclaration('width', '100px');
expect(styleManager.getDeclaration(className)).toEqual({
prop: 'width',
value: '100px'
});
});
test('getStyleSheetHtml', () => {
expect(styleManager.getStyleSheetHtml()).toMatchSnapshot();
styleManager.setDeclaration('width', '100px');
expect(styleManager.getStyleSheetHtml()).toMatchSnapshot();
});
test('setDeclaration', () => {
styleManager.mainSheet.sheet.insertRule = (rule, position) => {
// check for regressions in CSS write path (e.g., 0 => 0px)
expect(rule.indexOf('-webkit-flex-shrink:0;')).not.toEqual(-1);
};
styleManager.setDeclaration('flexShrink', 0);
});
});

View File

@@ -1,105 +0,0 @@
/* eslint-env jasmine, jest */
import I18nManager from '../../I18nManager';
import StyleRegistry from '../StyleRegistry';
let styleRegistry;
describe('apis/StyleSheet/StyleRegistry', () => {
beforeEach(() => {
styleRegistry = new StyleRegistry();
});
test('register', () => {
const style = { opacity: 0 };
const id = styleRegistry.register(style);
expect(typeof id === 'number').toBe(true);
});
describe('resolve', () => {
const styleA = { borderWidth: 0, borderColor: 'red', width: 100 };
const styleB = {
position: 'absolute',
left: 50,
pointerEvents: 'box-only'
};
const styleC = { width: 200 };
const testResolve = (a, b, c) => {
// no common properties, different resolving order, same result
const resolve1 = styleRegistry.resolve([a, b]);
expect(resolve1).toMatchSnapshot();
const resolve2 = styleRegistry.resolve([b, a]);
expect(resolve1).toEqual(resolve2);
// common properties, different resolving order, different result
const resolve3 = styleRegistry.resolve([a, b, c]);
expect(resolve3).toMatchSnapshot();
const resolve4 = styleRegistry.resolve([c, a, b]);
expect(resolve4).toMatchSnapshot();
expect(resolve3).not.toEqual(resolve4);
};
test('with register, resolves to className', () => {
const a = styleRegistry.register(styleA);
const b = styleRegistry.register(styleB);
const c = styleRegistry.register(styleC);
testResolve(a, b, c);
});
test('with register before RTL, resolves to className', () => {
const a = styleRegistry.register({ left: '12.34%' });
const b = styleRegistry.register({ textAlign: 'left' });
const c = styleRegistry.register({ marginLeft: 10 });
I18nManager.forceRTL(true);
const resolved = styleRegistry.resolve([a, b, c]);
I18nManager.forceRTL(false);
expect(resolved).toMatchSnapshot();
});
test('with register, resolves to mixed', () => {
const a = styleA;
const b = styleRegistry.register(styleB);
const c = styleRegistry.register(styleC);
testResolve(a, b, c);
});
test('without register, resolves to inline styles', () => {
testResolve(styleA, styleB, styleC);
});
});
describe('resolveStateful', () => {
test('preserves unrelated class names', () => {
const domStyleProps = { classList: ['unknown-class-1', 'unknown-class-2'], style: {} };
const domStyleNextProps = styleRegistry.resolveStateful({}, domStyleProps);
expect(domStyleNextProps).toMatchSnapshot();
});
test('preserves unrelated inline styles', () => {
const domStyleProps = { classList: [], style: { fontSize: '20px' } };
const domStyleNextProps = styleRegistry.resolveStateful({ opacity: 1 }, domStyleProps);
expect(domStyleNextProps).toMatchSnapshot();
});
test('next class names have priority over current inline styles', () => {
const domStyleProps = { classList: [], style: { opacity: 0.5 } };
const nextStyle = styleRegistry.register({ opacity: 1 });
const domStyleNextProps = styleRegistry.resolveStateful(nextStyle, domStyleProps);
expect(domStyleNextProps).toMatchSnapshot();
});
test('next inline styles have priority over current inline styles', () => {
// note: this also checks for correctly uppercasing the first letter of DOM vendor prefixes
const domStyleProps = {
classList: [],
style: { opacity: 0.5, WebkitTransform: 'scale(1)', transform: 'scale(1)' }
};
const domStyleNextProps = styleRegistry.resolveStateful(
{ opacity: 1, transform: [{ scale: 2 }] },
domStyleProps
);
expect(domStyleNextProps).toMatchSnapshot();
});
});
});

View File

@@ -0,0 +1,38 @@
/* eslint-env jasmine, jest */
import StyleSheetManager from '../StyleSheetManager';
let styleSheetManager;
describe('StyleSheet/StyleSheetManager', () => {
beforeEach(() => {
styleSheetManager = new StyleSheetManager();
});
test('getClassName', () => {
expect(styleSheetManager.getClassName('pointerEvents', 'box-only')).toMatchSnapshot();
const className = styleSheetManager.injectDeclaration('width', '100px');
expect(styleSheetManager.getClassName('width', '100px')).toEqual(className);
});
test('getDeclaration', () => {
const className = styleSheetManager.injectDeclaration('width', '100px');
expect(styleSheetManager.getDeclaration(className)).toEqual({
prop: 'width',
value: '100px'
});
});
test('getStyleSheet', () => {
styleSheetManager.injectDeclaration('--test-property', 'test-value');
expect(styleSheetManager.getStyleSheet()).toMatchSnapshot();
});
test('injectDeclaration', () => {
styleSheetManager._sheet.insertRuleOnce = (rule, position) => {
// check for regressions in CSS write path (e.g., 0 => 0px)
expect(rule.indexOf('-webkit-flex-shrink:0;')).not.toEqual(-1);
};
styleSheetManager.injectDeclaration('flexShrink', 0);
});
});

View File

@@ -1,6 +1,15 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`apis/StyleSheet/StyleRegistry resolve with register before RTL, resolves to className 1`] = `
exports[`StyleSheet/ReactNativeStyleResolver resolve resolves inline-style pointerEvents to classname 1`] = `
Object {
"classList": Array [
"rn-pointerEvents-12vffkv",
],
"className": "rn-pointerEvents-12vffkv",
}
`;
exports[`StyleSheet/ReactNativeStyleResolver resolve with register before RTL, resolves to correct className 1`] = `
Object {
"classList": Array [
"rn-marginRight-zso239",
@@ -11,27 +20,18 @@ Object {
}
`;
exports[`apis/StyleSheet/StyleRegistry resolve with register, resolves to className 1`] = `
exports[`StyleSheet/ReactNativeStyleResolver resolve with register before RTL, resolves to correct className 2`] = `
Object {
"classList": Array [
"rn-borderTopColor-1gxhl28",
"rn-borderRightColor-knoah9",
"rn-borderBottomColor-1ani3fp",
"rn-borderLeftColor-ribj9x",
"rn-borderTopWidth-13yce4e",
"rn-borderRightWidth-fnigne",
"rn-borderBottomWidth-ndvcnb",
"rn-borderLeftWidth-gxnn5r",
"rn-left-1tsx3h3",
"rn-pointerEvents-ah5dr5",
"rn-position-u8s1d",
"rn-width-b8lwoo",
"rn-left-2s0hu9",
"rn-marginLeft-1n0xq6e",
"rn-textAlign-fdjqy7",
],
"className": "rn-borderTopColor-1gxhl28 rn-borderRightColor-knoah9 rn-borderBottomColor-1ani3fp rn-borderLeftColor-ribj9x rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-left-1tsx3h3 rn-pointerEvents-ah5dr5 rn-position-u8s1d rn-width-b8lwoo",
"className": "rn-left-2s0hu9 rn-marginLeft-1n0xq6e rn-textAlign-fdjqy7",
}
`;
exports[`apis/StyleSheet/StyleRegistry resolve with register, resolves to className 2`] = `
exports[`StyleSheet/ReactNativeStyleResolver resolve with register, resolves to className 1`] = `
Object {
"classList": Array [
"rn-borderTopColor-1gxhl28",
@@ -43,15 +43,35 @@ Object {
"rn-borderBottomWidth-ndvcnb",
"rn-borderLeftWidth-gxnn5r",
"rn-left-1tsx3h3",
"rn-pointerEvents-ah5dr5",
"rn-opacity-icoktb",
"rn-position-u8s1d",
"rn-width-b8lwoo",
],
"className": "rn-borderTopColor-1gxhl28 rn-borderRightColor-knoah9 rn-borderBottomColor-1ani3fp rn-borderLeftColor-ribj9x rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-left-1tsx3h3 rn-opacity-icoktb rn-position-u8s1d rn-width-b8lwoo",
}
`;
exports[`StyleSheet/ReactNativeStyleResolver resolve with register, resolves to className 2`] = `
Object {
"classList": Array [
"rn-borderTopColor-1gxhl28",
"rn-borderRightColor-knoah9",
"rn-borderBottomColor-1ani3fp",
"rn-borderLeftColor-ribj9x",
"rn-borderTopWidth-13yce4e",
"rn-borderRightWidth-fnigne",
"rn-borderBottomWidth-ndvcnb",
"rn-borderLeftWidth-gxnn5r",
"rn-left-1tsx3h3",
"rn-opacity-icoktb",
"rn-position-u8s1d",
"rn-width-l0gwng",
],
"className": "rn-borderTopColor-1gxhl28 rn-borderRightColor-knoah9 rn-borderBottomColor-1ani3fp rn-borderLeftColor-ribj9x rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-left-1tsx3h3 rn-pointerEvents-ah5dr5 rn-position-u8s1d rn-width-l0gwng",
"className": "rn-borderTopColor-1gxhl28 rn-borderRightColor-knoah9 rn-borderBottomColor-1ani3fp rn-borderLeftColor-ribj9x rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-left-1tsx3h3 rn-opacity-icoktb rn-position-u8s1d rn-width-l0gwng",
}
`;
exports[`apis/StyleSheet/StyleRegistry resolve with register, resolves to className 3`] = `
exports[`StyleSheet/ReactNativeStyleResolver resolve with register, resolves to className 3`] = `
Object {
"classList": Array [
"rn-borderTopColor-1gxhl28",
@@ -63,22 +83,22 @@ Object {
"rn-borderBottomWidth-ndvcnb",
"rn-borderLeftWidth-gxnn5r",
"rn-left-1tsx3h3",
"rn-pointerEvents-ah5dr5",
"rn-opacity-icoktb",
"rn-position-u8s1d",
"rn-width-b8lwoo",
],
"className": "rn-borderTopColor-1gxhl28 rn-borderRightColor-knoah9 rn-borderBottomColor-1ani3fp rn-borderLeftColor-ribj9x rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-left-1tsx3h3 rn-pointerEvents-ah5dr5 rn-position-u8s1d rn-width-b8lwoo",
"className": "rn-borderTopColor-1gxhl28 rn-borderRightColor-knoah9 rn-borderBottomColor-1ani3fp rn-borderLeftColor-ribj9x rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-left-1tsx3h3 rn-opacity-icoktb rn-position-u8s1d rn-width-b8lwoo",
}
`;
exports[`apis/StyleSheet/StyleRegistry resolve with register, resolves to mixed 1`] = `
exports[`StyleSheet/ReactNativeStyleResolver resolve with register, resolves to mixed 1`] = `
Object {
"classList": Array [
"rn-left-1tsx3h3",
"rn-pointerEvents-ah5dr5",
"rn-opacity-icoktb",
"rn-position-u8s1d",
],
"className": "rn-left-1tsx3h3 rn-pointerEvents-ah5dr5 rn-position-u8s1d",
"className": "rn-left-1tsx3h3 rn-opacity-icoktb rn-position-u8s1d",
"style": Object {
"borderBottomColor": "red",
"borderBottomWidth": "0px",
@@ -93,15 +113,15 @@ Object {
}
`;
exports[`apis/StyleSheet/StyleRegistry resolve with register, resolves to mixed 2`] = `
exports[`StyleSheet/ReactNativeStyleResolver resolve with register, resolves to mixed 2`] = `
Object {
"classList": Array [
"rn-left-1tsx3h3",
"rn-pointerEvents-ah5dr5",
"rn-opacity-icoktb",
"rn-position-u8s1d",
"rn-width-l0gwng",
],
"className": "rn-left-1tsx3h3 rn-pointerEvents-ah5dr5 rn-position-u8s1d rn-width-l0gwng",
"className": "rn-left-1tsx3h3 rn-opacity-icoktb rn-position-u8s1d rn-width-l0gwng",
"style": Object {
"borderBottomColor": "red",
"borderBottomWidth": "0px",
@@ -115,14 +135,14 @@ Object {
}
`;
exports[`apis/StyleSheet/StyleRegistry resolve with register, resolves to mixed 3`] = `
exports[`StyleSheet/ReactNativeStyleResolver resolve with register, resolves to mixed 3`] = `
Object {
"classList": Array [
"rn-left-1tsx3h3",
"rn-pointerEvents-ah5dr5",
"rn-opacity-icoktb",
"rn-position-u8s1d",
],
"className": "rn-left-1tsx3h3 rn-pointerEvents-ah5dr5 rn-position-u8s1d",
"className": "rn-left-1tsx3h3 rn-opacity-icoktb rn-position-u8s1d",
"style": Object {
"borderBottomColor": "red",
"borderBottomWidth": "0px",
@@ -137,12 +157,10 @@ Object {
}
`;
exports[`apis/StyleSheet/StyleRegistry resolve without register, resolves to inline styles 1`] = `
exports[`StyleSheet/ReactNativeStyleResolver resolve without register, resolves to inline styles 1`] = `
Object {
"classList": Array [
"rn-pointerEvents-ah5dr5",
],
"className": "rn-pointerEvents-ah5dr5",
"classList": Array [],
"className": "",
"style": Object {
"borderBottomColor": "red",
"borderBottomWidth": "0px",
@@ -153,18 +171,17 @@ Object {
"borderTopColor": "red",
"borderTopWidth": "0px",
"left": "50px",
"opacity": 0.5,
"position": "absolute",
"width": "100px",
},
}
`;
exports[`apis/StyleSheet/StyleRegistry resolve without register, resolves to inline styles 2`] = `
exports[`StyleSheet/ReactNativeStyleResolver resolve without register, resolves to inline styles 2`] = `
Object {
"classList": Array [
"rn-pointerEvents-ah5dr5",
],
"className": "rn-pointerEvents-ah5dr5",
"classList": Array [],
"className": "",
"style": Object {
"borderBottomColor": "red",
"borderBottomWidth": "0px",
@@ -175,18 +192,17 @@ Object {
"borderTopColor": "red",
"borderTopWidth": "0px",
"left": "50px",
"opacity": 0.5,
"position": "absolute",
"width": "200px",
},
}
`;
exports[`apis/StyleSheet/StyleRegistry resolve without register, resolves to inline styles 3`] = `
exports[`StyleSheet/ReactNativeStyleResolver resolve without register, resolves to inline styles 3`] = `
Object {
"classList": Array [
"rn-pointerEvents-ah5dr5",
],
"className": "rn-pointerEvents-ah5dr5",
"classList": Array [],
"className": "",
"style": Object {
"borderBottomColor": "red",
"borderBottomWidth": "0px",
@@ -197,13 +213,14 @@ Object {
"borderTopColor": "red",
"borderTopWidth": "0px",
"left": "50px",
"opacity": 0.5,
"position": "absolute",
"width": "100px",
},
}
`;
exports[`apis/StyleSheet/StyleRegistry resolveStateful next class names have priority over current inline styles 1`] = `
exports[`StyleSheet/ReactNativeStyleResolver resolveWithNode next class names have priority over current inline styles 1`] = `
Object {
"className": "rn-opacity-6dt33c",
"style": Object {
@@ -212,7 +229,7 @@ Object {
}
`;
exports[`apis/StyleSheet/StyleRegistry resolveStateful next inline styles have priority over current inline styles 1`] = `
exports[`StyleSheet/ReactNativeStyleResolver resolveWithNode next inline styles have priority over current inline styles 1`] = `
Object {
"className": "",
"style": Object {
@@ -223,14 +240,14 @@ Object {
}
`;
exports[`apis/StyleSheet/StyleRegistry resolveStateful preserves unrelated class names 1`] = `
exports[`StyleSheet/ReactNativeStyleResolver resolveWithNode preserves unrelated class names 1`] = `
Object {
"className": "unknown-class-1 unknown-class-2",
"style": Object {},
}
`;
exports[`apis/StyleSheet/StyleRegistry resolveStateful preserves unrelated inline styles 1`] = `
exports[`StyleSheet/ReactNativeStyleResolver resolveWithNode preserves unrelated inline styles 1`] = `
Object {
"className": "",
"style": Object {
@@ -239,3 +256,35 @@ Object {
},
}
`;
exports[`StyleSheet/ReactNativeStyleResolver resolveWithNode when isRTL=true & doLeftAndRightSwapInRTL=false, resolves to non-flipped inline styles 1`] = `
Object {
"className": "",
"style": Object {
"marginRight": "10px",
"paddingLeft": "10px",
"right": "10px",
},
}
`;
exports[`StyleSheet/ReactNativeStyleResolver resolveWithNode when isRTL=true, resolves to flipped classNames 1`] = `
Object {
"className": "rn-left-1u10d71 rn-marginRight-zso239",
"style": Object {
"marginRight": "",
"right": "5px",
},
}
`;
exports[`StyleSheet/ReactNativeStyleResolver resolveWithNode when isRTL=true, resolves to flipped inline styles 1`] = `
Object {
"className": "",
"style": Object {
"left": "10px",
"marginRight": "10px",
"right": "5px",
},
}
`;

View File

@@ -1,43 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`apis/StyleSheet/StyleManager getClassName 1`] = `"rn-pointerEvents-ah5dr5"`;
exports[`apis/StyleSheet/StyleManager getStyleSheetHtml 1`] = `
"<style id=\\"react-native-stylesheet-static\\">
html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:rgba(0,0,0,0);}
body{margin:0;}
button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0;}
input::-webkit-inner-spin-button,input::-webkit-outer-spin-button,input::-webkit-search-cancel-button,input::-webkit-search-decoration,input::-webkit-search-results-button,input::-webkit-search-results-decoration{display:none;}
@keyframes rn-ActivityIndicator-animation{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg);}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg);}}
@keyframes rn-ProgressBar-animation{0%{-webkit-transform:translateX(-100%);transform:translateX(-100%);}100%{-webkit-transform:translateX(400%);transform:translateX(400%);}}
.rn-pointerEvents-105ug2t{pointer-events:auto !important;}
.rn-pointerEvents-ah5dr5{pointer-events:auto !important;}
.rn-pointerEvents-633pao{pointer-events:none !important;}
.rn-pointerEvents-12vffkv{pointer-events:none !important;}
.rn-pointerEvents-12vffkv > *{pointer-events:auto;}
.rn-pointerEvents-ah5dr5 > *{pointer-events:none;}
</style>
<style id=\\"react-native-stylesheet\\">
</style>"
`;
exports[`apis/StyleSheet/StyleManager getStyleSheetHtml 2`] = `
"<style id=\\"react-native-stylesheet-static\\">
html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:rgba(0,0,0,0);}
body{margin:0;}
button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0;}
input::-webkit-inner-spin-button,input::-webkit-outer-spin-button,input::-webkit-search-cancel-button,input::-webkit-search-decoration,input::-webkit-search-results-button,input::-webkit-search-results-decoration{display:none;}
@keyframes rn-ActivityIndicator-animation{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg);}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg);}}
@keyframes rn-ProgressBar-animation{0%{-webkit-transform:translateX(-100%);transform:translateX(-100%);}100%{-webkit-transform:translateX(400%);transform:translateX(400%);}}
.rn-pointerEvents-105ug2t{pointer-events:auto !important;}
.rn-pointerEvents-ah5dr5{pointer-events:auto !important;}
.rn-pointerEvents-633pao{pointer-events:none !important;}
.rn-pointerEvents-12vffkv{pointer-events:none !important;}
.rn-pointerEvents-12vffkv > *{pointer-events:auto;}
.rn-pointerEvents-ah5dr5 > *{pointer-events:none;}
</style>
<style id=\\"react-native-stylesheet\\">
.rn-width-b8lwoo{width:100px}
</style>"
`;

View File

@@ -0,0 +1,16 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`StyleSheet/StyleSheetManager getClassName 1`] = `undefined`;
exports[`StyleSheet/StyleSheetManager getStyleSheet 1`] = `
Object {
"id": "react-native-stylesheet",
"textContent": "@media all{
html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:rgba(0,0,0,0);}
body{margin:0;}
button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0;}
input::-webkit-inner-spin-button,input::-webkit-outer-spin-button,input::-webkit-search-cancel-button,input::-webkit-search-decoration,input::-webkit-search-results-button,input::-webkit-search-results-decoration{display:none;}
}
.rn---test-property-ax3bxi{--test-property:test-value}",
}
`;

View File

@@ -0,0 +1,30 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`StyleSheet/createAtomicRules transforms custom "animationName" declaration 1`] = `
Array [
"@media all {@-webkit-keyframes rn-anim-2k74q5{0%{top:0px}50%{top:5px}100%{top:10px}}}",
"@media all {@keyframes rn-anim-2k74q5{0%{top:0px}50%{top:5px}100%{top:10px}}}",
"@media all {@-webkit-keyframes rn-anim-zc91cv{from{left:0px}to{left:10px}}}",
"@media all {@keyframes rn-anim-zc91cv{from{left:0px}to{left:10px}}}",
".test{-webkit-animation-name:rn-anim-2k74q5,rn-anim-zc91cv;animation-name:rn-anim-2k74q5,rn-anim-zc91cv}",
]
`;
exports[`StyleSheet/createAtomicRules transforms custom "placeholderTextColor" declaration 1`] = `
Array [
"@media all {.test::-webkit-input-placeholder{color:gray;opacity:1}.test::-moz-placeholder{color:gray;opacity:1}.test:-ms-input-placeholder{color:gray;opacity:1}.test::placeholder{color:gray;opacity:1}}",
]
`;
exports[`StyleSheet/createAtomicRules transforms custom "pointerEvents" declaration 1`] = `
Array [
".test > *{pointer-events:none}",
".test{pointer-events:auto !important}",
]
`;
exports[`StyleSheet/createAtomicRules transforms standard declarations to a single rule 1`] = `
Array [
".test{margin:0px}",
]
`;

View File

@@ -1,24 +1,30 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`apis/StyleSheet/createReactDOMStyle fontFamily fontFamily: "System" 1`] = `
exports[`StyleSheet/createReactDOMStyle fontFamily "Noto, System" 1`] = `
Object {
"fontFamily": "-apple-system, BlinkMacSystemFont, \\"Segoe UI\\", Roboto, Ubuntu, \\"Helvetica Neue\\", sans-serif",
"fontFamily": "Noto, system-ui, -apple-system, BlinkMacSystemFont, \\"Segoe UI\\", Roboto, Ubuntu, \\"Helvetica Neue\\", sans-serif",
}
`;
exports[`apis/StyleSheet/createReactDOMStyle fontFamily fontFamily: "monospace" 1`] = `
exports[`StyleSheet/createReactDOMStyle fontFamily "System" 1`] = `
Object {
"fontFamily": "system-ui, -apple-system, BlinkMacSystemFont, \\"Segoe UI\\", Roboto, Ubuntu, \\"Helvetica Neue\\", sans-serif",
}
`;
exports[`StyleSheet/createReactDOMStyle fontFamily "monospace" 1`] = `
Object {
"fontFamily": "monospace, monospace",
}
`;
exports[`apis/StyleSheet/createReactDOMStyle fontFamily general case 1`] = `
exports[`StyleSheet/createReactDOMStyle fontFamily general case 1`] = `
Object {
"fontFamily": "Georgia, Times, serif",
}
`;
exports[`apis/StyleSheet/createReactDOMStyle shortform -> longform 1`] = `
exports[`StyleSheet/createReactDOMStyle shortform -> longform 1`] = `
Object {
"borderBottomColor": "white",
"borderBottomStyle": "solid",

View File

@@ -0,0 +1,3 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`StyleSheet/createRuleBlock generates correct css 1`] = `"-webkit-transition-duration:0.1s;border-width-left:2px;border-width-right:3px;box-shadow:1px 1px 1px 1px #000;position:absolute;transition-duration:0.1s"`;

View File

@@ -1,26 +1,26 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`apis/StyleSheet/flattenStyle should merge style objects 1`] = `
exports[`StyleSheet/flattenStyle should merge style objects 1`] = `
Object {
"opacity": 1,
"order": 2,
}
`;
exports[`apis/StyleSheet/flattenStyle should override style properties 1`] = `
exports[`StyleSheet/flattenStyle should override style properties 1`] = `
Object {
"backgroundColor": "#023c69",
"order": null,
}
`;
exports[`apis/StyleSheet/flattenStyle should overwrite properties with \`undefined\` 1`] = `
exports[`StyleSheet/flattenStyle should overwrite properties with \`undefined\` 1`] = `
Object {
"backgroundColor": undefined,
}
`;
exports[`apis/StyleSheet/flattenStyle should recursively flatten arrays 1`] = `
exports[`StyleSheet/flattenStyle should recursively flatten arrays 1`] = `
Object {
"opacity": 1,
"order": 3,

View File

@@ -1,3 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`apis/StyleSheet/generateCss generates correct css 1`] = `"-webkit-transition-duration:0.1s;border-width-left:2px;border-width-right:3px;box-shadow:1px 1px 1px 1px #000;position:absolute;transition-duration:0.1s"`;

View File

@@ -1,53 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`apis/StyleSheet/i18nStyle LTR mode does not auto-flip 1`] = `
Object {
"borderBottomLeftRadius": 20,
"borderBottomRightRadius": "2rem",
"borderLeftColor": "red",
"borderLeftStyle": "solid",
"borderLeftWidth": 5,
"borderRightColor": "blue",
"borderRightStyle": "dotted",
"borderRightWidth": 6,
"borderTopLeftRadius": 10,
"borderTopRightRadius": "1rem",
"left": 1,
"marginLeft": 7,
"marginRight": 8,
"paddingLeft": 9,
"paddingRight": 10,
"right": 2,
"textAlign": "left",
"textShadowOffset": Object {
"height": 10,
"width": "1rem",
},
}
`;
exports[`apis/StyleSheet/i18nStyle RTL mode does auto-flip 1`] = `
Object {
"borderBottomLeftRadius": "2rem",
"borderBottomRightRadius": 20,
"borderLeftColor": "blue",
"borderLeftStyle": "dotted",
"borderLeftWidth": 6,
"borderRightColor": "red",
"borderRightStyle": "solid",
"borderRightWidth": 5,
"borderTopLeftRadius": "1rem",
"borderTopRightRadius": 10,
"left": 2,
"marginLeft": 8,
"marginRight": 7,
"paddingLeft": 10,
"paddingRight": 9,
"right": 1,
"textAlign": "right",
"textShadowOffset": Object {
"height": 10,
"width": "-1rem",
},
}
`;

View File

@@ -1,185 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`apis/StyleSheet getStyleSheets 1`] = `
Array [
Object {
"id": "react-native-stylesheet-static",
"textContent": "html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:rgba(0,0,0,0);}
body{margin:0;}
button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0;}
input::-webkit-inner-spin-button,input::-webkit-outer-spin-button,input::-webkit-search-cancel-button,input::-webkit-search-decoration,input::-webkit-search-results-button,input::-webkit-search-results-decoration{display:none;}
@keyframes rn-ActivityIndicator-animation{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg);}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg);}}
@keyframes rn-ProgressBar-animation{0%{-webkit-transform:translateX(-100%);transform:translateX(-100%);}100%{-webkit-transform:translateX(400%);transform:translateX(400%);}}
.rn-pointerEvents-105ug2t{pointer-events:auto !important;}
.rn-pointerEvents-ah5dr5{pointer-events:auto !important;}
.rn-pointerEvents-633pao{pointer-events:none !important;}
.rn-pointerEvents-12vffkv{pointer-events:none !important;}
.rn-pointerEvents-12vffkv > *{pointer-events:auto;}
.rn-pointerEvents-ah5dr5 > *{pointer-events:none;}",
},
Object {
"id": "react-native-stylesheet",
"textContent": ".rn-bottom-1p0dtai{bottom:0px}
.rn-left-1d2f490{left:0px}
.rn-left-1fe0xdi{left:0%}
.rn-left-7b7h2f{left:100%}
.rn-position-u8s1d{position:absolute}
.rn-position-bnwqim{position:relative}
.rn-right-zchlnj{right:0px}
.rn-top-ipm5af{top:0px}
.rn-cursor-1loqt21{cursor:pointer}
.rn-cursor-7q8q6z{cursor:default}
.rn-cursor-1ei5mc7{cursor:inherit}
.rn-appearance-30o5oe{-moz-appearance:none;-webkit-appearance:none;appearance:none}
.rn-backgroundColor-wib322{background-color:transparent}
.rn-backgroundColor-8ndhhv{background-color:rgba(33,150,243,1)}
.rn-backgroundColor-15al3ab{background-color:rgba(223,223,223,1)}
.rn-backgroundColor-44z8sh{background-color:rgba(255,255,255,1)}
.rn-backgroundColor-5itogg{background-color:rgba(0,150,136,1)}
.rn-backgroundColor-1v82r4u{background-color:rgba(170,184,194,1)}
.rn-backgroundColor-1hj8efq{background-color:rgba(213,213,213,1)}
.rn-backgroundColor-1bgzomc{background-color:rgba(189,189,189,1)}
.rn-color-homxoj{color:inherit}
.rn-color-1qtguxu{color:rgba(255,255,255,1)}
.rn-color-istcb5{color:rgba(161,161,161,1)}
.rn-font-1lw9tu2{font:inherit}
.rn-textAlign-1ttztb7{text-align:inherit}
.rn-textAlign-q4m81j{text-align:center}
.rn-textDecoration-bauka4{text-decoration:none}
.rn-listStyle-1ebb2ja{list-style:none}
.rn-alignItems-1oszu61{-ms-flex-align:stretch;-webkit-align-items:stretch;-webkit-box-align:stretch;align-items:stretch}
.rn-alignItems-1awozwy{-ms-flex-align:center;-webkit-align-items:center;-webkit-box-align:center;align-items:center}
.rn-borderTopStyle-1efd50x{border-top-style:solid}
.rn-borderRightStyle-14skgim{border-right-style:solid}
.rn-borderBottomStyle-rull8r{border-bottom-style:solid}
.rn-borderLeftStyle-mm0ijv{border-left-style:solid}
.rn-borderTopWidth-13yce4e{border-top-width:0px}
.rn-borderTopWidth-1jxfwug{border-top-width:2px}
.rn-borderRightWidth-fnigne{border-right-width:0px}
.rn-borderRightWidth-18p6if4{border-right-width:2px}
.rn-borderBottomWidth-ndvcnb{border-bottom-width:0px}
.rn-borderBottomWidth-wgabs5{border-bottom-width:2px}
.rn-borderLeftWidth-gxnn5r{border-left-width:0px}
.rn-borderLeftWidth-dwliz8{border-left-width:2px}
.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-display-1471scf{display:inline}
.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-flexDirection-18u37iz{-ms-flex-direction:row;-webkit-box-direction:normal;-webkit-box-orient:horizontal;-webkit-flex-direction:row;flex-direction:row}
.rn-marginTop-1mnahxq{margin-top:0px}
.rn-marginTop-1t01tom{margin-top:auto}
.rn-marginRight-61z16t{margin-right:0px}
.rn-marginRight-lchren{margin-right:auto}
.rn-marginBottom-p1pxzi{margin-bottom:0px}
.rn-marginBottom-1qahzrx{margin-bottom:auto}
.rn-marginLeft-11wrixw{margin-left:0px}
.rn-marginLeft-1jj8364{margin-left:auto}
.rn-minHeight-ifefl9{min-height:0px}
.rn-minWidth-bcqeeo{min-width:0px}
.rn-paddingTop-wk8lta{padding-top:0px}
.rn-paddingTop-tskmnb{padding-top:8px}
.rn-paddingRight-9aemit{padding-right:0px}
.rn-paddingRight-1pyaxff{padding-right:8px}
.rn-paddingBottom-1mdbw0j{padding-bottom:0px}
.rn-paddingBottom-xd6kpl{padding-bottom:8px}
.rn-paddingLeft-gy4na3{padding-left:0px}
.rn-paddingLeft-1m04atk{padding-left:8px}
.rn-zIndex-1lgpqti{z-index:0}
.rn-zIndex-1wyyakw{z-index:-1}
.rn-backgroundPosition-vvn4in{background-position:center}
.rn-backgroundRepeat-u6sd8q{background-repeat:no-repeat}
.rn-backgroundRepeat-17leim2{background-repeat:repeat}
.rn-backgroundSize-4gszlv{background-size:cover}
.rn-backgroundSize-1sxrcry{background-size:auto}
.rn-backgroundSize-ehq7j7{background-size:contain}
.rn-backgroundSize-x3cy2q{background-size:100% 100%}
.rn-height-1pi2tsx{height:100%}
.rn-height-z80fyv{height:20px}
.rn-height-1r8g8re{height:36px}
.rn-height-10ptun7{height:16px}
.rn-height-4v7adb{height:5px}
.rn-height-1dernwh{height:70%}
.rn-opacity-1272l3b{opacity:0}
.rn-opacity-6dt33c{opacity:1}
.rn-width-13qz1uu{width:100%}
.rn-width-19wmn03{width:20px}
.rn-width-1acpoxo{width:36px}
.rn-width-1janqcz{width:16px}
.rn-touchAction-19z077z{-ms-touch-action:none;touch-action:none}
.rn-touchAction-1gvxusu{-ms-touch-action:manipulate;touch-action:manipulate}
.rn-WebkitOverflowScrolling-150rngu{-webkit-overflow-scrolling:touch}
.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}
.rn-overflowX-11yh6sk{overflow-x:hidden}
.rn-overflowX-lltvgl{overflow-x:auto}
.rn-overflowY-1rnoaur{overflow-y:auto}
.rn-overflowY-buy8e9{overflow-y:hidden}
.rn-transform-emqnss{-webkit-transform:translateZ(0px);transform:translateZ(0px)}
.rn-fontFamily-10u92zi{font-family:-apple-system, BlinkMacSystemFont, \\"Segoe UI\\", Roboto, Ubuntu, \\"Helvetica Neue\\", sans-serif}
.rn-fontFamily-poiln3{font-family:inherit}
.rn-fontSize-1b43r93{font-size:14px}
.rn-fontSize-7cikom{font-size:inherit}
.rn-whiteSpace-q42fyq{white-space:pre-wrap}
.rn-whiteSpace-irrty{white-space:inherit}
.rn-whiteSpace-3s2u2q{white-space:nowrap}
.rn-wordWrap-qvutc0{word-wrap:break-word}
.rn-userSelect-lrvibr{-moz-user-select:none;-ms-user-select:none;-webkit-user-select:none;user-select:none}
.rn-maxWidth-dnmrzs{max-width:100%}
.rn-textOverflow-1udbk01{text-overflow:ellipsis}
.rn-justifyContent-1777fci{-ms-flex-pack:center;-webkit-box-pack:center;-webkit-justify-content:center;justify-content:center}
.rn-visibility-11j9u27{visibility:hidden}
.rn-animationDuration-17bb2tj{-webkit-animation-duration:0.75s;animation-duration:0.75s}
.rn-animationDuration-1ay1djp{-webkit-animation-duration:1s;animation-duration:1s}
.rn-animationIterationCount-1muvv40{-webkit-animation-iteration-count:infinite;animation-iteration-count:infinite}
.rn-animationName-dozj4v{-webkit-animation-name:rn-ActivityIndicator-animation;animation-name:rn-ActivityIndicator-animation}
.rn-animationName-141g9a{-webkit-animation-name:rn-ProgressBar-animation;animation-name:rn-ProgressBar-animation}
.rn-animationTimingFunction-1ldzwu0{-webkit-animation-timing-function:linear;animation-timing-function:linear}
.rn-animationPlayState-1abnn5w{-webkit-animation-play-state:paused;animation-play-state:paused}
.rn-transitionDuration-eafdt9{-webkit-transition-duration:0.15s;transition-duration:0.15s}
.rn-transitionDuration-13tjlyg{-webkit-transition-duration:0.1s;transition-duration:0.1s}
.rn-transitionProperty-1i6wzkk{-moz-transition-property:opacity;-webkit-transition-property:opacity;transition-property:opacity}
.rn-borderTopLeftRadius-1iymjk7{border-top-left-radius:2px}
.rn-borderTopLeftRadius-jt3ufn{border-top-left-radius:100%}
.rn-borderTopLeftRadius-ou6ah9{border-top-left-radius:0px}
.rn-borderTopRightRadius-s2skl2{border-top-right-radius:2px}
.rn-borderTopRightRadius-1e868j9{border-top-right-radius:100%}
.rn-borderTopRightRadius-t12b5v{border-top-right-radius:0px}
.rn-borderBottomRightRadius-l5bh9y{border-bottom-right-radius:2px}
.rn-borderBottomRightRadius-ujv9e3{border-bottom-right-radius:100%}
.rn-borderBottomRightRadius-zmljjp{border-bottom-right-radius:0px}
.rn-borderBottomLeftRadius-101sy47{border-bottom-left-radius:2px}
.rn-borderBottomLeftRadius-1hakmuk{border-bottom-left-radius:100%}
.rn-borderBottomLeftRadius-pm2fo{border-bottom-left-radius:0px}
.rn-fontWeight-majxgm{font-weight:500}
.rn-textTransform-tsynxw{text-transform:uppercase}
.rn-borderTopColor-j4x2lb{border-top-color:rgba(101,119,134,1)}
.rn-borderTopColor-gj2eto{border-top-color:rgba(0,150,136,1)}
.rn-borderTopColor-1j7vz2b{border-top-color:rgba(204,214,221,1)}
.rn-borderTopColor-2dclza{border-top-color:rgba(170,184,194,1)}
.rn-borderTopColor-kqr9px{border-top-color:black}
.rn-borderRightColor-12i18q1{border-right-color:rgba(101,119,134,1)}
.rn-borderRightColor-31ud7z{border-right-color:rgba(0,150,136,1)}
.rn-borderRightColor-10fg1ub{border-right-color:rgba(204,214,221,1)}
.rn-borderRightColor-8jf312{border-right-color:rgba(170,184,194,1)}
.rn-borderRightColor-q0dj5p{border-right-color:black}
.rn-borderBottomColor-mg3rfb{border-bottom-color:rgba(101,119,134,1)}
.rn-borderBottomColor-1bgnb8i{border-bottom-color:rgba(0,150,136,1)}
.rn-borderBottomColor-zxuuv6{border-bottom-color:rgba(204,214,221,1)}
.rn-borderBottomColor-1yeakrt{border-bottom-color:rgba(170,184,194,1)}
.rn-borderBottomColor-1ah7hsa{border-bottom-color:black}
.rn-borderLeftColor-vnhemr{border-left-color:rgba(101,119,134,1)}
.rn-borderLeftColor-tbzcuz{border-left-color:rgba(0,150,136,1)}
.rn-borderLeftColor-1p34dw6{border-left-color:rgba(204,214,221,1)}
.rn-borderLeftColor-bluj2i{border-left-color:rgba(170,184,194,1)}
.rn-borderLeftColor-137uh4u{border-left-color:black}
.rn-backgroundImage-rs94m5{background-image:url(\\"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgdmVyc2lvbj0iMS4xIgogICB2aWV3Qm94PSIwIDAgMSAxIgogICBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWluWU1pbiBtZWV0Ij4KICA8cGF0aAogICAgIGQ9Ik0gMC4wNDAzODA1OSwwLjYyNjc3NjcgMC4xNDY0NDY2MSwwLjUyMDcxMDY4IDAuNDI5Mjg5MzIsMC44MDM1NTMzOSAwLjMyMzIyMzMsMC45MDk2MTk0MSB6IE0gMC4yMTcxNTcyOSwwLjgwMzU1MzM5IDAuODUzNTUzMzksMC4xNjcxNTcyOSAwLjk1OTYxOTQxLDAuMjczMjIzMyAwLjMyMzIyMzMsMC45MDk2MTk0MSB6IgogICAgIGlkPSJyZWN0Mzc4MCIKICAgICBzdHlsZT0iZmlsbDojZmZmZmZmO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lIiAvPgo8L3N2Zz4K\\")}
.rn-alignSelf-k200y{-ms-flex-item-align:start;-webkit-align-self:flex-start;align-self:flex-start}
.rn-boxShadow-1ewcgjf{box-shadow:0px 1px 3px rgba(0,0,0,0.5)}
.rn-resize-1dz5y72{resize:none}",
},
]
`;

View File

@@ -0,0 +1,25 @@
/* eslint-env jasmine, jest */
import createAtomicRules from '../createAtomicRules';
describe('StyleSheet/createAtomicRules', () => {
test('transforms standard declarations to a single rule', () => {
expect(createAtomicRules('.test', 'margin', 0)).toMatchSnapshot();
});
test('transforms custom "animationName" declaration', () => {
const value = [
{ '0%': { top: 0 }, '50%': { top: 5 }, '100%': { top: 10 } },
{ from: { left: 0 }, to: { left: 10 } }
];
expect(createAtomicRules('.test', 'animationName', value)).toMatchSnapshot();
});
test('transforms custom "pointerEvents" declaration', () => {
expect(createAtomicRules('.test', 'pointerEvents', 'box-only')).toMatchSnapshot();
});
test('transforms custom "placeholderTextColor" declaration', () => {
expect(createAtomicRules('.test', 'placeholderTextColor', 'gray')).toMatchSnapshot();
});
});

View File

@@ -15,13 +15,30 @@ const reactNativeStyle = {
resizeMode: 'contain'
};
describe('apis/StyleSheet/createReactDOMStyle', () => {
describe('StyleSheet/createReactDOMStyle', () => {
test('noop on DOM styles', () => {
const firstStyle = createReactDOMStyle(reactNativeStyle);
const secondStyle = createReactDOMStyle(firstStyle);
expect(firstStyle).toEqual(secondStyle);
});
test('shortform -> longform', () => {
const style = {
borderStyle: 'solid',
boxSizing: 'border-box',
borderBottomColor: 'white',
borderBottomWidth: 1,
borderWidth: 0,
marginTop: 50,
marginVertical: 25,
margin: 10,
overflow: 'hidden',
overscrollBehavior: 'contain'
};
expect(createReactDOMStyle(style)).toMatchSnapshot();
});
describe('borderWidth styles', () => {
test('defaults to 0 when "null"', () => {
expect(createReactDOMStyle({ borderWidth: null })).toEqual({
@@ -125,30 +142,23 @@ describe('apis/StyleSheet/createReactDOMStyle', () => {
expect(createReactDOMStyle({ fontFamily: 'Georgia, Times, serif' })).toMatchSnapshot();
});
test('fontFamily: "monospace"', () => {
test('"monospace"', () => {
expect(createReactDOMStyle({ fontFamily: 'monospace' })).toMatchSnapshot();
});
test('fontFamily: "System"', () => {
test('"System"', () => {
expect(createReactDOMStyle({ fontFamily: 'System' })).toMatchSnapshot();
});
test('"Noto, System"', () => {
expect(createReactDOMStyle({ fontFamily: 'Noto, System' })).toMatchSnapshot();
});
});
test('shortform -> longform', () => {
const style = {
borderStyle: 'solid',
boxSizing: 'border-box',
borderBottomColor: 'white',
borderBottomWidth: 1,
borderWidth: 0,
marginTop: 50,
marginVertical: 25,
margin: 10,
overflow: 'hidden',
overscrollBehavior: 'contain'
};
expect(createReactDOMStyle(style)).toMatchSnapshot();
test('fontVariant', () => {
expect(createReactDOMStyle({ fontVariant: ['common-ligatures', 'small-caps'] })).toEqual({
fontVariant: 'common-ligatures small-caps'
});
});
describe('shadow styles', () => {

View File

@@ -1,8 +1,8 @@
/* eslint-env jasmine, jest */
import generateCss from '../generateCss';
import createRuleBlock from '../createRuleBlock';
describe('apis/StyleSheet/generateCss', () => {
describe('StyleSheet/createRuleBlock', () => {
test('generates correct css', () => {
const style = {
boxShadow: '1px 1px 1px 1px #000',
@@ -11,6 +11,6 @@ describe('apis/StyleSheet/generateCss', () => {
position: 'absolute',
transitionDuration: '0.1s'
};
expect(generateCss(style)).toMatchSnapshot();
expect(createRuleBlock(style)).toMatchSnapshot();
});
});

View File

@@ -2,16 +2,14 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import flattenStyle from '../flattenStyle';
describe('apis/StyleSheet/flattenStyle', () => {
describe('StyleSheet/flattenStyle', () => {
test('should merge style objects', () => {
const style = flattenStyle([{ opacity: 1 }, { order: 2 }]);
expect(style).toMatchSnapshot();

View File

@@ -3,29 +3,8 @@
import I18nManager from '../../I18nManager';
import i18nStyle from '../i18nStyle';
const style = {
borderLeftColor: 'red',
borderRightColor: 'blue',
borderTopLeftRadius: 10,
borderTopRightRadius: '1rem',
borderBottomLeftRadius: 20,
borderBottomRightRadius: '2rem',
borderLeftStyle: 'solid',
borderRightStyle: 'dotted',
borderLeftWidth: 5,
borderRightWidth: 6,
left: 1,
marginLeft: 7,
marginRight: 8,
paddingLeft: 9,
paddingRight: 10,
right: 2,
textAlign: 'left',
textShadowOffset: { width: '1rem', height: 10 }
};
describe('apis/StyleSheet/i18nStyle', () => {
describe('LTR mode', () => {
describe('StyleSheet/i18nStyle', () => {
describe('isRTL is false', () => {
beforeEach(() => {
I18nManager.allowRTL(false);
});
@@ -34,12 +13,73 @@ describe('apis/StyleSheet/i18nStyle', () => {
I18nManager.allowRTL(true);
});
test('does not auto-flip', () => {
expect(i18nStyle(style)).toMatchSnapshot();
test('converts end/start properties', () => {
const initial = {
borderStartColor: 'red',
start: 1,
marginStart: 5,
paddingEnd: 10
};
const expected = {
borderLeftColor: 'red',
left: 1,
marginLeft: 5,
paddingRight: 10
};
expect(i18nStyle(initial)).toEqual(expected);
});
test('converts end/start values', () => {
const initial = {
float: 'start',
textAlign: 'end'
};
const expected = {
float: 'left',
textAlign: 'right'
};
expect(i18nStyle(initial)).toEqual(expected);
});
test('noop on left/right properties', () => {
const initial = {
paddingLeft: 0,
left: 0,
marginRight: 0,
paddingRight: 10
};
expect(i18nStyle(initial)).toEqual(initial);
});
test('noop on left/right values', () => {
const initial = {
clear: 'left',
float: 'left',
textAlign: 'right',
textShadowOffset: { width: '1rem', height: 10 }
};
expect(i18nStyle(initial)).toEqual(initial);
});
test('end/start properties take precedence', () => {
const initial = {
borderStartWidth: 10,
borderLeftWidth: 0,
end: 10,
right: 0,
marginStart: 10,
marginLeft: 0
};
const expected = {
borderLeftWidth: 10,
marginLeft: 10,
right: 10
};
expect(i18nStyle(initial)).toEqual(expected);
});
});
describe('RTL mode', () => {
describe('isRTL is true', () => {
beforeEach(() => {
I18nManager.forceRTL(true);
});
@@ -48,8 +88,156 @@ describe('apis/StyleSheet/i18nStyle', () => {
I18nManager.forceRTL(false);
});
test('does auto-flip', () => {
expect(i18nStyle(style)).toMatchSnapshot();
describe('doLeftAndRightSwapInRTL is false', () => {
beforeEach(() => {
I18nManager.swapLeftAndRightInRTL(false);
});
afterEach(() => {
I18nManager.swapLeftAndRightInRTL(true);
});
test('converts end/start properties', () => {
const initial = {
borderStartColor: 'red',
start: 1,
marginStart: 5,
paddingEnd: 10
};
const expected = {
borderRightColor: 'red',
right: 1,
marginRight: 5,
paddingLeft: 10
};
expect(i18nStyle(initial)).toEqual(expected);
});
test('converts end/start values', () => {
const initial = {
float: 'start',
textAlign: 'end'
};
const expected = {
float: 'right',
textAlign: 'left'
};
expect(i18nStyle(initial)).toEqual(expected);
});
test('noop on left/right properties', () => {
const initial = {
paddingLeft: 0,
left: 0,
marginRight: 0,
paddingRight: 10
};
expect(i18nStyle(initial)).toEqual(initial);
});
test('noop on left/right values', () => {
const initial = {
clear: 'left',
float: 'left',
textAlign: 'right',
textShadowOffset: { width: '1rem', height: 10 }
};
expect(i18nStyle(initial)).toEqual(initial);
});
test('end/start properties take precedence', () => {
const style = {
borderStartWidth: 10,
borderRightWidth: 0,
end: 10,
left: 0,
marginStart: 10,
marginRight: 0
};
const expected = {
borderRightWidth: 10,
marginRight: 10,
left: 10
};
expect(i18nStyle(style)).toEqual(expected);
});
});
describe('doLeftAndRightSwapInRTL is true', () => {
test('converts end/start properties', () => {
const initial = {
borderStartColor: 'red',
start: 1,
marginStart: 5,
paddingEnd: 10
};
const expected = {
borderRightColor: 'red',
right: 1,
marginRight: 5,
paddingLeft: 10
};
expect(i18nStyle(initial)).toEqual(expected);
});
test('converts end/start values', () => {
const initial = {
float: 'start',
textAlign: 'end'
};
const expected = {
float: 'right',
textAlign: 'left'
};
expect(i18nStyle(initial)).toEqual(expected);
});
test('converts left/right properties', () => {
const initial = {
borderLeftColor: 'red',
left: 1,
marginLeft: 5,
paddingRight: 10
};
const expected = {
borderRightColor: 'red',
right: 1,
marginRight: 5,
paddingLeft: 10
};
expect(i18nStyle(initial)).toEqual(expected);
});
test('converts left/right values', () => {
const initial = {
float: 'left',
textAlign: 'right',
textShadowOffset: { width: '1rem', height: 10 }
};
const expected = {
float: 'right',
textAlign: 'left',
textShadowOffset: { width: '-1rem', height: 10 }
};
expect(i18nStyle(initial)).toEqual(expected);
});
test('end/start properties take precedence', () => {
const style = {
borderStartWidth: 10,
borderLeftWidth: 0,
end: 10,
right: 0,
marginStart: 10,
marginLeft: 0
};
const expected = {
borderRightWidth: 10,
marginRight: 10,
left: 10
};
expect(i18nStyle(style)).toEqual(expected);
});
});
});
});

View File

@@ -13,7 +13,7 @@ const isPlainObject = x => {
/* eslint-enable */
};
describe('apis/StyleSheet', () => {
describe('StyleSheet', () => {
test('absoluteFill', () => {
expect(Number.isInteger(StyleSheet.absoluteFill) === true).toBeTruthy();
});
@@ -48,8 +48,4 @@ describe('apis/StyleSheet', () => {
test('hairlineWidth', () => {
expect(Number.isInteger(StyleSheet.hairlineWidth) === true).toBeTruthy();
});
test('getStyleSheets', () => {
expect(StyleSheet.getStyleSheets()).toMatchSnapshot();
});
});

View File

@@ -2,7 +2,7 @@
import normalizeValue from '../normalizeValue';
describe('apis/StyleSheet/normalizeValue', () => {
describe('StyleSheet/normalizeValue', () => {
test('normalizes property values requiring units', () => {
expect(normalizeValue('margin', 0)).toEqual('0px');
});

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