Compare commits

..

24 Commits

Author SHA1 Message Date
Nicolas Gallagher
25a11e673d 0.0.81 2017-04-05 14:03:07 -07:00
Nicolas Gallagher
e846054f4e Add 'Tweet' to performance benchmarks 2017-04-05 14:02:17 -07:00
Nicolas Gallagher
d6854abd7d [fix] accessibilityLiveRegion values 2017-04-02 16:16:41 -07:00
Nicolas Gallagher
1b172319b9 [change] use 'aria-level' to determine DOM heading tag
Fix #401
Close #402
2017-03-30 09:25:13 -07:00
Nicolas Gallagher
e81394c26e Add 'platform' benchmark
The "platform" benchmark relies on no intermediate layer. All the static
CSS it requires is inlined in the HTML page.
2017-03-25 09:11:23 -07:00
Nicolas Gallagher
d33aa3eee2 [change] Touchable no default 'accessibilityRole' 2017-03-23 15:42:20 -07:00
Nicolas Gallagher
5d78c73e8c [add] export 'ViewPropTypes'
See https://github.com/reactjs/react-codemod/pull/99
2017-03-23 12:06:24 -07:00
Nicolas Gallagher
7735d304ef [fix] export 'processColor' 2017-03-23 11:57:14 -07:00
Nicolas Gallagher
b7c72308ea 0.0.80 2017-03-22 23:43:37 -07:00
Nicolas Gallagher
5fee075774 Add 'processColor' tests 2017-03-22 23:40:40 -07:00
Nicolas Gallagher
25204eeff0 [fix] convert color values to CSS color
Convert all hex and numeric colors to rgba. Assume non-hex strings are
valid CSS colors.
2017-03-22 23:15:42 -07:00
Nicolas Gallagher
9c61fe58d3 [add] View 'hitSlop' shim
Shim the 'hitSlop' prop using a positioned element to extend the size of
a View's touch target without changing layout. Unlike the native
implementation, the touch target may extend past the parent view bounds.
2017-03-22 23:01:53 -07:00
Nicolas Gallagher
782125d169 Remove pointerEvents code from View 2017-03-20 22:53:57 -07:00
Nicolas Gallagher
af805d67e6 0.0.79 2017-03-20 22:42:00 -07:00
Nicolas Gallagher
68068f8cb6 [fix] support React Native props in 'setNativeProps'
React Native allows props like 'pointerEvents' to be set using
'setNativeProps'.

Fix #392
2017-03-20 22:33:59 -07:00
Nicolas Gallagher
e05e2122d7 [fix] avoid setting empty style objects 2017-03-20 22:33:59 -07:00
Nicolas Gallagher
47dac44120 [fix] filter 'lineBreakMode' from Text props 2017-03-20 22:33:53 -07:00
Nicolas Gallagher
22af6894c2 Update jest 2017-03-20 22:18:50 -07:00
Nicolas Gallagher
458c534200 [change] improve button accessibility and styling
1. If no 'accessibilityRole' is set, fallback to looking for 'button'
role in the equivalent native props. This helps improve accessibility of
button-like components authored without the web platform in mind.

2. Ensure button context is properly inherited.

3. Add 'appearance:none' to DOM button elements to enable better styling
support in Safari

Fix #378
2017-03-20 14:50:01 -07:00
Nicolas Gallagher
ec2db3e2a3 0.0.78 2017-03-19 18:45:21 -07:00
Bruno Lemos
e6f00f7592 [add] default option for Platform.select
See: f30ab35e92
2017-03-19 15:27:07 -07:00
Nicolas Gallagher
976320916e [change] move bridge code into createDOMElement
Moves event normalization and the ResponderEventPlugin injection from
'View' to 'createDOMElement'.

The 'react-native-web/lite' variant is removed from the performance
directory as the implementation is not substantially different.
Micro-optimizations to marginally narrow the performance gap to
css-modules.
2017-03-19 15:19:26 -07:00
Nicolas Gallagher
808790505e [change] onLayout improvements
1. Fires when window resizes
2. Guards against nodes being unmounted

Fix #397
Ref #60
2017-03-19 13:29:47 -07:00
Nicolas Gallagher
89ad493ce5 Update benchmark results 2017-03-14 17:23:16 -07:00
99 changed files with 2327 additions and 954 deletions

View File

@@ -45,9 +45,7 @@ If true, disable all interactions for this component.
**hitSlop**: `{top: number, left: number, bottom: number, right: number}`
This defines how far your touch can start away from the button. This is added
to `pressRetentionOffset` when moving off of the button. **NOTE**: The touch
area never extends past the parent view bounds and the z-index of sibling views
always takes precedence if a touch hits two overlapping views.
to `pressRetentionOffset` when moving off of the button.
**onLayout**: function

View File

@@ -18,8 +18,10 @@ NOTE: `View` will transfer all other props to the rendered HTML element.
**accessibilityLabel**: string
Defines the text available to assistive technologies upon interaction with the
element. (This is implemented using `aria-label`.)
Overrides the text that's read by the screen reader when the user interacts
with the element. By default, the label is constructed by traversing all the
children and accumulating all the `Text` nodes separated by space. (This is
implemented using `aria-label`.)
**accessibilityLiveRegion**: oneOf('assertive', 'off', 'polite') = 'off'
@@ -46,6 +48,15 @@ assistive technologies of a `role` value change.
When `false`, the view is hidden from assistive technologies. (This is
implemented using `aria-hidden`.)
**hitSlop**: {top: number, left: number, bottom: number, right: number}
This defines how far a touch event can start away from the view. Typical
interface guidelines recommend touch targets that are at least 30 - 40
points/density-independent pixels.
For example, if a touchable view has a height of 20 the touchable height can be
extended to 40 with `hitSlop={{top: 10, bottom: 10, left: 0, right: 0}}`.
**onLayout**: function
Invoked on mount and layout changes with `{ nativeEvent: { layout: { x, y, width,

View File

@@ -1,6 +1,6 @@
{
"name": "react-native-web",
"version": "0.0.77",
"version": "0.0.81",
"description": "React Native for Web",
"main": "dist/index.js",
"files": [
@@ -25,7 +25,6 @@
"dependencies": {
"animated": "^0.2.0",
"array-find-index": "^1.0.2",
"asap": "^2.0.5",
"babel-runtime": "^6.23.0",
"debounce": "^1.0.0",
"deep-assign": "^2.0.0",
@@ -53,7 +52,7 @@
"eslint-plugin-promise": "^2.0.1",
"eslint-plugin-react": "^6.1.2",
"file-loader": "^0.9.0",
"jest": "^16.0.2",
"jest": "^19.0.2",
"node-libs-browser": "^0.5.3",
"prettier": "^0.19.0",
"react": "~15.4.1",

View File

@@ -10,27 +10,18 @@ open ./performance/index.html
## Notes
The components used in the render benchmarks are simple enough to be
implemented by multiple styling libraries. The implementations are not
equivalent but are useful for framing the relative performance of
`react-native-web` against these tests.
The implementations are not equivalent. For example, the `react-native-web`
implementation of `View` does more than just styling. The
`react-native-web/lite` variant implements a minimal `View` that allows for a
more direct comparison with the `css-modules` baseline.
implemented by multiple UI or style libraries. The implementations are not
equivalent in functionality.
## Benchmark results
Typical render timings*: mean / two standard deviations
Typical render timings*: mean ± two standard deviations
Version: 0.0.73
| Implementation | Deep tree (ms) | Wide tree (ms) |
| Implementation | Deep tree (ms) | Wide tree (ms) |
| :--- | ---: | ---: |
| css-modules | `80.47` `±18.04` | `166.91` `±19.90` |
| react-native-web/lite | `87.91` 13.37` | `181.45` `±20.06` |
| react-native-web | `113.45` 09.27` | `237.33` 38.77` |
| styled-components | `170.86` 15.67` | `378.83` `±36.11` |
| glamor | `275.41` `±19.56` | `474.76` `±29.02` |
| `css-modules` | `76.66` `±18.46` | `157.03` `±19.79` |
| `react-native-web@0.0.78` | `90.13` 20.91` | `198.72` `±24.44` |
| `styled-components@2.0.0-7` | `263.06` 31.87` | `564.53` 27.62` |
| `glamor@3.0.0-1` | `267.49` 35.12` | `451.99` `±37.32` |
*MacBook Pro (13-inch, Early 2011); 2.7 GHz Intel Core i7; 16 GB 1600 MHz DDR3. Google Chrome 56.

View File

@@ -0,0 +1,115 @@
import theme from '../theme';
import React, { PropTypes, PureComponent } from 'react';
import { StyleSheet, Text } from 'react-native';
class AppText extends PureComponent {
static displayName = 'AppText';
static propTypes = {
align: PropTypes.oneOf(['center', 'left', 'right']),
color: PropTypes.oneOf(['blue', 'deepGray', 'normal', 'red', 'white']),
fontStyle: PropTypes.oneOf(['normal', 'italic']),
size: PropTypes.oneOf(['small', 'normal', 'large']),
uppercase: PropTypes.bool,
weight: PropTypes.oneOf(['normal', 'bold'])
};
render() {
const {
align,
color,
fontStyle,
size,
uppercase,
weight,
...other
} = this.props;
const style = [
styles.root,
align && alignStyles[align],
color && colorStyles[color],
fontStyle && fontStyles[fontStyle],
size && sizeStyles[size],
weight && weightStyles[weight],
uppercase === true && styles.uppercase
];
return <Text {...other} style={style} />;
}
}
const styles = StyleSheet.create({
root: {
fontFamily: theme.fontFamily,
fontSize: theme.fontSize.normal,
fontWeight: 'normal',
lineHeight: theme.createLength(theme.lineHeight),
wordWrap: 'break-word'
},
uppercase: {
textTransform: 'uppercase'
}
});
const alignStyles = StyleSheet.create({
center: {
textAlign: 'center'
},
left: {
textAlign: 'left'
},
right: {
textAlign: 'right'
}
});
const colorStyles = StyleSheet.create({
blue: {
color: theme.colors.blue
},
deepGray: {
color: theme.colors.deepGray
},
normal: {
color: theme.colors.textBlack
},
red: {
color: theme.colors.red
},
white: {
color: theme.colors.white
}
});
const fontStyles = StyleSheet.create({
normal: {
fontStyle: 'normal'
},
italic: {
fontStyle: 'italic'
}
});
const sizeStyles = StyleSheet.create({
small: {
fontSize: theme.fontSize.small
},
normal: {
fontSize: theme.fontSize.normal
},
large: {
fontSize: theme.fontSize.large
}
});
const weightStyles = StyleSheet.create({
normal: {
fontWeight: '400'
},
bold: {
fontWeight: 'bold'
}
});
export default AppText;

View File

@@ -0,0 +1,40 @@
import React, { PureComponent, PropTypes } from 'react';
import { StyleSheet, View } from 'react-native';
class AspectRatio extends PureComponent {
static displayName = 'AspectRatio';
static propTypes = {
children: PropTypes.any,
ratio: PropTypes.number,
style: PropTypes.object
};
static defaultProps = {
ratio: 1
};
render() {
const { children, ratio, style } = this.props;
const percentage = 100 / ratio;
return (
<View style={[styles.root, style]}>
<View style={[styles.shim, { paddingBottom: `${percentage}%` }]} />
<View style={StyleSheet.absoluteFill}>{children}</View>
</View>
);
}
}
const styles = StyleSheet.create({
root: {
overflow: 'hidden'
},
shim: {
display: 'block',
width: '100%'
}
});
export default AspectRatio;

View File

@@ -1,7 +1,7 @@
/* eslint-disable react/prop-types */
import classnames from 'classnames';
import React from 'react';
import View from '../View';
import View from '../View/index.css-modules';
import styles from './styles.css';
const Box = ({ color, fixed = false, layout = 'column', outer = false, ...other }) => (

View File

@@ -1,7 +1,7 @@
/* eslint-disable react/prop-types */
import { css } from 'glamor';
import React from 'react';
import View from '../View';
import View from '../View/index.glamor';
const Box = ({ color, fixed = false, layout = 'column', outer = false, ...other }) => (
<View

View File

@@ -1,7 +1,6 @@
/* eslint-disable react/prop-types */
import React from 'react';
import StyleSheet from 'react-native/apis/StyleSheet';
import View from '../View';
import { StyleSheet, View } from 'react-native';
const Box = ({ color, fixed = false, layout = 'column', outer = false, ...other }) => (
<View

View File

@@ -0,0 +1,17 @@
/* eslint-disable react/prop-types */
import classnames from 'classnames';
import React from 'react';
import View from '../View/index.platform';
const Box = ({ color, fixed = false, layout = 'column', outer = false, ...other }) => (
<View
{...other}
className={classnames(`color${color}`, {
fixed: fixed,
outer: outer,
row: layout === 'row'
})}
/>
);
module.exports = Box;

View File

@@ -1,5 +1,5 @@
import styled from 'styled-components';
import View from '../View';
import View from '../View/index.styled';
const getColor = color => {
switch (color) {

View File

@@ -0,0 +1,50 @@
import { StyleSheet, View } from 'react-native';
import React, { Component, PropTypes } from 'react';
import theme from '../theme';
class GridView extends Component {
static displayName = 'GridView';
static propTypes = {
children: PropTypes.node,
hasGap: PropTypes.bool,
style: PropTypes.object
};
render() {
const { children, hasGap, style, ...other } = this.props;
return (
<View {...other} style={[style, styles.root, hasGap && styles.hasGap]}>
{React.Children.map(children, child => {
return child &&
React.cloneElement(child, {
style: [child.props.style, styles.column, hasGap && styles.hasGapColumn]
});
})}
</View>
);
}
}
const styles = StyleSheet.create({
root: {
flexDirection: 'row'
},
/**
* 1. Distribute all space (rather than extra space)
* 2. Prevent wide content from forcing wider flex columns
*/
column: {
flexBasis: 0, // 1
minWidth: 0 // 2
},
hasGap: {
marginHorizontal: theme.createLength(theme.spaceX * (-0.5), 'rem')
},
hasGapColumn: {
marginHorizontal: theme.createLength(theme.spaceX * 0.5, 'rem')
}
});
export default GridView;

View File

@@ -0,0 +1,20 @@
import { createDOMElement } from 'react-native';
import React from 'react';
import styles from './styles';
const IconDirectMessage = props => createDOMElement('svg', {
children: (
<g>
<path d="M43.34 14H12.66L28 27.946z" />
<path
d="M51.392 14.789L30.018 34.22c-.009.008-.028.006-.039.012-.563.5-1.266.768-1.98.768-.72 0-1.442-.258-2.017-.78L4.609 14.79A3.957 3.957 0 0 0 3 18v37a1.998 1.998 0 0 0 2 2c.464 0 .924-.162 1.292-.473L19 46h30c2.243 0 4-1.757 4-4V18a3.96 3.96 0 0 0-1.608-3.211z"
/>
</g>
),
style: [styles.icon, props.style],
viewBox: '0 0 56 72'
});
IconDirectMessage.metadata = { height: 72, width: 56 };
export default IconDirectMessage;

View File

@@ -0,0 +1,19 @@
import { createDOMElement } from 'react-native';
import React from 'react';
import styles from './styles';
const IconHeart = props => createDOMElement('svg', {
children: (
<g>
<path
d="M38.723 12c-7.187 0-11.16 7.306-11.723 8.131C26.437 19.306 22.504 12 15.277 12 8.791 12 3.533 18.163 3.533 24.647 3.533 39.964 21.891 55.907 27 56c5.109-.093 23.467-16.036 23.467-31.353C50.467 18.163 45.209 12 38.723 12z"
/>
</g>
),
style: [styles.icon, props.style],
viewBox: '0 0 54 72'
});
IconHeart.metadata = { height: 72, width: 54 };
export default IconHeart;

View File

@@ -0,0 +1,19 @@
import { createDOMElement } from 'react-native';
import React from 'react';
import styles from './styles';
const IconReply = props => createDOMElement('svg', {
children: (
<g>
<path
d="M41 31h-9V19a2.999 2.999 0 0 0-4.817-2.386l-21 16a3 3 0 0 0-.001 4.773l21 16a3.006 3.006 0 0 0 3.15.301A2.997 2.997 0 0 0 32 51V39h9c5.514 0 10 4.486 10 10a4 4 0 0 0 8 0c0-9.925-8.075-18-18-18z"
/>
</g>
),
style: [styles.icon, props.style],
viewBox: '0 0 62 72'
});
IconReply.metadata = { height: 72, width: 62 };
export default IconReply;

View File

@@ -0,0 +1,19 @@
import { createDOMElement } from 'react-native';
import React from 'react';
import styles from './styles';
const IconRetweet = props => createDOMElement('svg', {
children: (
<g>
<path
d="M70.676 36.644A3 3 0 0 0 68 35h-7V19a4 4 0 0 0-4-4H34a4 4 0 0 0 0 8h18a1 1 0 0 1 1 .998V35h-7a3.001 3.001 0 0 0-2.419 4.775l11 15a3.003 3.003 0 0 0 4.839-.001l11-15a3.001 3.001 0 0 0 .256-3.13zM40.001 48H22a.995.995 0 0 1-.992-.96L21.001 36h7a3.001 3.001 0 0 0 2.419-4.775l-11-15a3.003 3.003 0 0 0-4.839.001l-11 15A3 3 0 0 0 6.001 36h7l.011 16.003a4 4 0 0 0 4 3.997h22.989a4 4 0 0 0 0-8z"
/>
</g>
),
style: [styles.icon, props.style],
viewBox: '0 0 74 72'
});
IconRetweet.metadata = { height: 72, width: 74 };
export default IconRetweet;

View File

@@ -0,0 +1,15 @@
import { StyleSheet } from 'react-native';
const styles = StyleSheet.create({
icon: {
display: 'inline-block',
fill: 'currentcolor',
height: '1.25em',
maxWidth: '100%',
position: 'relative',
userSelect: 'none',
verticalAlign: 'text-bottom'
}
});
export default styles;

View File

@@ -0,0 +1,144 @@
import AspectRatio from '../AspectRatio';
import GridView from '../GridView';
import TweetActionsBar from '../TweetActionsBar';
import TweetText from '../TweetText';
import UserAvatar from '../UserAvatar';
import UserNames from '../UserNames';
import { Image, StyleSheet, Text, View } from 'react-native';
import React, { Component, PropTypes } from 'react';
import theme from '../theme';
export class Tweet extends Component {
static displayName = 'Tweet';
static propTypes = {
tweet: PropTypes.object.isRequired
};
render() {
const { tweet } = this.props;
const { id, lang, media, textParts, timestamp, user } = tweet;
const { fullName, profileImageUrl, screenName } = user;
return (
<View accessibilityRole="article" accessible style={styles.root}>
<GridView hasGap>
<View style={styles.avatarColumn}>
<View
accessibilityRole="link"
accessible
href={`/${screenName}`}
style={styles.avatarLink}
>
<UserAvatar style={styles.avatar} uri={profileImageUrl} />
</View>
</View>
<View style={styles.bodyColumn}>
<View style={styles.body}>
<View style={styles.row}>
<Text
accessibilityRole="link"
children={timestamp}
href={`/${screenName}/status/${id}`}
style={styles.timestamp}
/>
<UserNames fullName={fullName} screenName={screenName} />
</View>
<View accessibilityRole="heading" aria-level="4">
<TweetText displayMode={'links'} lang={lang} textParts={textParts} />
</View>
{media
? <View style={styles.richContent}>
<AspectRatio ratio={16 / 9}>
<Image
resizeMode={Image.resizeMode.cover}
source={media.source}
style={styles.media}
/>
</AspectRatio>
</View>
: null}
</View>
<TweetActionsBar
actions={[
{ name: 'reply', label: 'Reply' },
{
name: 'retweet',
label: 'Retweet',
count: tweet.retweet_count,
highlighted: tweet.retweeted
},
{
name: 'like',
label: 'Like',
count: tweet.favorite_count,
highlighted: tweet.favorited
},
{ name: 'directMessage', label: 'Direct Message' }
]}
style={styles.actionBar}
/>
</View>
</GridView>
</View>
);
}
}
const styles = StyleSheet.create({
root: {
paddingVertical: theme.createLength(theme.spaceY * 0.75, 'rem'),
paddingHorizontal: theme.createLength(theme.spaceX, 'rem')
},
avatarColumn: {
flexGrow: 1,
minWidth: 32
},
bodyColumn: {
flexGrow: 7
},
row: {
flexDirection: 'row',
justifyContent: 'space-between'
},
avatarLink: {
display: 'block',
flexShrink: 1,
flexGrow: 0,
width: '100%'
},
avatar: {
width: '100%'
},
body: {
marginTop: '-0.15rem'
},
timestamp: {
color: theme.colors.deepGray,
marginLeft: theme.createLength(theme.spaceX, 'rem'),
order: 1,
textDecorationLine: 'none',
whiteSpace: 'nowrap'
},
actionBar: {
marginTop: theme.createLength(theme.spaceY * 0.5, 'rem')
},
richContent: {
borderRadius: '0.35rem',
marginTop: theme.createLength(theme.spaceY * 0.5, 'rem'),
overflow: 'hidden'
},
media: {
...StyleSheet.absoluteFillObject,
margin: 'auto',
width: 'auto',
height: 'auto'
}
});
export default Tweet;

View File

@@ -0,0 +1,77 @@
import IconReply from '../Icons/Reply';
import IconHeart from '../Icons/Heart';
import IconRetweet from '../Icons/Retweet';
import IconDirectMessage from '../Icons/DirectMessage';
import { Text, View, StyleSheet } from 'react-native';
import React, { PropTypes } from 'react';
import theme from '../theme';
const getIcon = (icon, highlighted) => {
switch (icon) {
case 'like':
return <IconHeart />;
case 'reply':
return <IconReply />;
case 'retweet':
return <IconRetweet />;
case 'directMessage':
return <IconDirectMessage />;
default:
return null;
}
};
export default class TweetAction extends React.Component {
static displayName = 'TweetAction';
static propTypes = {
count: PropTypes.number,
displayMode: PropTypes.oneOf(['like', 'reply', 'retweet', 'directMessage']),
highlighted: PropTypes.bool,
onPress: PropTypes.func,
style: PropTypes.object
};
render() {
const { count, displayMode, highlighted, onPress, style } = this.props;
return (
<View accessibilityRole="button" onPress={onPress} style={[styles.root, style]}>
<Text
style={[
styles.inner,
displayMode === 'like' && highlighted && styles.likedColor,
displayMode === 'retweet' && highlighted && styles.retweetedColor
]}
>
{getIcon(displayMode, highlighted)}
{count > 0 ? <Text style={styles.count}>{count}</Text> : null}
</Text>
</View>
);
}
}
const styles = StyleSheet.create({
root: {
minHeight: theme.createLength(theme.lineHeight, 'rem'),
overflow: 'visible',
userSelect: 'none',
whiteSpace: 'nowrap'
},
inner: {
alignItems: 'center',
color: theme.colors.deepGray,
display: 'flex',
flexDirection: 'row'
},
count: {
marginLeft: '0.25em'
},
retweetedColor: {
color: theme.colors.green
},
likedColor: {
color: theme.colors.red
}
});

View File

@@ -0,0 +1,51 @@
import TweetAction from '../TweetAction';
import { View, StyleSheet } from 'react-native';
import React, { PropTypes, PureComponent } from 'react';
const actionNames = ['reply', 'retweet', 'like', 'directMessage'];
export default class TweetActionsBar extends PureComponent {
static propTypes = {
actions: PropTypes.arrayOf(
PropTypes.shape({
count: PropTypes.number,
label: PropTypes.string,
highlighted: PropTypes.bool,
name: PropTypes.oneOf(actionNames).isRequired,
onPress: PropTypes.func
})
),
style: PropTypes.object
};
render() {
const { actions, style } = this.props;
/* eslint-disable react/jsx-handler-names */
return (
<View style={[styles.root, style]}>
{actions.map((action, i) => (
<TweetAction
accessibilityLabel={actions.label}
count={action.count}
displayMode={action.name}
highlighted={action.highlighted}
key={i}
onPress={action.onPress}
style={styles.action}
/>
))}
</View>
);
}
}
const styles = StyleSheet.create({
root: {
flexDirection: 'row'
},
action: {
display: 'block',
marginRight: '10%'
}
});

View File

@@ -0,0 +1,28 @@
import AppText from '../AppText';
import TweetTextPart from '../TweetTextPart';
import React, { PropTypes } from 'react';
class TweetText extends React.Component {
static displayName = 'TweetText';
static propTypes = {
displayMode: TweetTextPart.propTypes.displayMode,
lang: PropTypes.string,
numberOfLines: PropTypes.number,
textParts: PropTypes.array.isRequired
};
render() {
const { displayMode, lang, numberOfLines, textParts, ...other } = this.props;
return (
<AppText {...other} lang={lang} numberOfLines={numberOfLines}>
{textParts.map((part, i) => (
<TweetTextPart displayMode={displayMode} key={i} part={part} />
))}
</AppText>
);
}
}
export default TweetText;

View File

@@ -0,0 +1,112 @@
/* eslint-disable react/prop-types */
import { Image, StyleSheet, Text } from 'react-native';
import React, { PropTypes } from 'react';
import theme from '../theme';
const createTextEntity = ({ part }) => <Text>{`${part.prefix}${part.text}`}</Text>;
const createTwemojiEntity = ({ part }) => (
<Image
accessibilityLabel={part.text}
draggable={false}
source={{ uri: part.emoji }}
style={styles.twemoji}
/>
);
// @mention, #hashtag, $cashtag
const createSymbolEntity = ({ displayMode, part }) => {
const links = displayMode === 'links';
return (
<Text accessibilityRole={links ? 'link' : null} href={part.url} style={[links && styles.link]}>
{`${part.prefix}${part.text}`}
</Text>
);
};
// internal links
const createLinkEntity = ({ displayMode, part }) => {
const { displayUrl, linkRelation, url } = part;
const links = displayMode === 'links';
return (
<Text
accessibilityRole={links ? 'link' : null}
href={url}
rel={links ? linkRelation : null}
style={[links && styles.link]}
>
{displayUrl}
</Text>
);
};
// external links
const createExternalLinkEntity = ({ displayMode, part }) => {
const { displayUrl, linkRelation, url } = part;
const links = displayMode === 'links';
return (
<Text
accessibilityRole={links ? 'link' : null}
href={url}
rel={links ? linkRelation : null}
style={[links && styles.link]}
target="_blank"
>
{displayUrl}
</Text>
);
};
class TweetTextPart extends React.Component {
static displayName = 'TweetTextPart';
static propTypes = {
displayMode: PropTypes.oneOf(['links', 'no-links']),
part: PropTypes.object
};
static defaultProps = {
displayMode: 'links'
};
render() {
let renderer;
const { isEmoji, isEntity, isHashtag, isMention, isMedia, isUrl } = this.props.part;
if (isEmoji || isEntity || isUrl || isMedia) {
if (isUrl) {
renderer = createExternalLinkEntity;
} else if (isHashtag || isMention) {
renderer = createSymbolEntity;
} else if (isEmoji) {
renderer = createTwemojiEntity;
} else {
renderer = createLinkEntity;
}
} else {
renderer = createTextEntity;
}
return renderer(this.props);
}
}
const styles = StyleSheet.create({
link: {
color: theme.colors.blue,
textDecorationLine: 'none',
unicodeBidi: 'embed'
},
twemoji: {
display: 'inline-block',
height: '1.25em',
width: '1.25em',
paddingRight: '0.05em',
paddingLeft: '0.1em',
verticalAlign: '-0.2em'
}
});
export default TweetTextPart;

View File

@@ -0,0 +1,64 @@
import AspectRatio from '../AspectRatio';
import { Image, StyleSheet } from 'react-native';
import React, { PropTypes, PureComponent } from 'react';
import theme from '../theme';
class UserAvatar extends PureComponent {
static displayName = 'UserAvatar';
static propTypes = {
accessibilityLabel: PropTypes.string,
circle: PropTypes.bool,
style: PropTypes.object,
uri: PropTypes.string
};
static defaultProps = {
circle: false
};
render() {
const { accessibilityLabel, circle, style, uri } = this.props;
return (
<AspectRatio ratio={1} style={[styles.root, style]}>
{uri
? <Image
accessibilityLabel={accessibilityLabel}
onLoad={this._handleLoad}
ref={this._setImageRef}
source={{ uri }}
style={[styles.image, circle && styles.circle]}
/>
: null}
</AspectRatio>
);
}
_handleLoad = () => {
this._imageRef && this._imageRef.setNativeProps(nativeProps);
};
_setImageRef = component => {
this._imageRef = component;
};
}
const nativeProps = { style: { backgroundColor: '#fff' } };
const styles = StyleSheet.create({
root: {
borderRadius: '0.35rem'
},
circle: {
borderRadius: '9999px'
},
image: {
backgroundColor: theme.colors.fadedGray,
display: 'block',
height: '100%',
width: '100%'
}
});
export default UserAvatar;

View File

@@ -0,0 +1,56 @@
import AppText from '../AppText';
import { StyleSheet } from 'react-native';
import React, { PropTypes, PureComponent } from 'react';
class UserNames extends PureComponent {
static displayName = 'UserNames';
static propTypes = {
fullName: PropTypes.string,
layout: PropTypes.oneOf(['nowrap', 'stack']),
onPress: PropTypes.func,
screenName: PropTypes.string,
style: PropTypes.object
};
static defaultProps = {
layout: 'nowrap'
};
render() {
const {
fullName,
layout,
onPress,
screenName,
style,
...other
} = this.props;
return (
<AppText
{...other}
color="deepGray"
numberOfLines={layout === 'nowrap' ? 1 : null}
onPress={onPress}
style={[styles.root, style]}
>
<AppText color="normal" weight="bold">{fullName}</AppText>
{layout === 'stack' ? ' \u000A' : ' '}
<AppText color="deepGray" style={styles.screenName}>{`@${screenName}`}</AppText>
</AppText>
);
}
}
const styles = StyleSheet.create({
root: {
display: 'inline-block'
},
screenName: {
unicodeBidi: 'embed',
writingDirection: 'ltr'
}
});
export default UserNames;

View File

@@ -0,0 +1,7 @@
/* eslint-disable react/prop-types */
import classnames from 'classnames';
import React from 'react';
const View = props => <div {...props} className={classnames('view', props.className)} />;
module.exports = View;

View File

@@ -18,4 +18,4 @@
list-style: none;
min-height: 0;
min-width: 0;
};
}

View File

@@ -0,0 +1,7 @@
import Box from './Box/index.css-modules';
import View from './View/index.css-modules';
export default {
Box,
View
};

View File

@@ -0,0 +1,7 @@
import Box from './Box/index.glamor';
import View from './View/index.glamor';
export default {
Box,
View
};

View File

@@ -0,0 +1,7 @@
import Box from './Box/index.platform';
import View from './View/index.platform';
export default {
Box,
View
};

View File

@@ -0,0 +1,9 @@
import Box from './Box';
import Tweet from './Tweet';
import { View } from 'react-native';
export default {
Box,
Tweet,
View
};

View File

@@ -0,0 +1,7 @@
import Box from './Box/index.styled';
import View from './View/index.styled';
export default {
Box,
View
};

View File

@@ -0,0 +1,37 @@
const colors = {
blue: '#1B95E0',
lightBlue: '#71C9F8',
green: '#17BF63',
orange: '#F45D22',
purple: '#794BC4',
red: '#E0245E',
white: '#FFFFFF',
yellow: '#FFAD1F',
deepGray: '#657786',
fadedGray: '#E6ECF0',
faintGray: '#F5F8FA',
gray: '#AAB8C2',
lightGray: '#CCD6DD',
textBlack: '#14171A'
};
const fontSize = {
root: '14px',
// font scale
small: '0.85rem',
normal: '1rem',
large: '1.25rem'
};
module.exports = {
colors,
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, "Helvetica Neue", sans-serif, ' +
'"Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"', // emoji fonts
fontSize,
lineHeight: 1.3125,
spaceX: 0.6,
spaceY: 1.3125,
createLength(num, unit) {
return `${num}${unit}`;
}
};

View File

@@ -1,7 +0,0 @@
import Box from './Box';
import View from './View';
export default {
Box,
View
};

View File

@@ -1,7 +0,0 @@
import Box from './Box';
import View from './View';
export default {
Box,
View
};

View File

@@ -1,49 +0,0 @@
/* eslint-disable react/prop-types */
import React from 'react';
import StyleSheet from 'react-native/apis/StyleSheet';
import View from '../View/lite';
const Box = ({ color, fixed = false, layout = 'column', outer = false, ...other }) => (
<View
{...other}
style={[
styles[`color${color}`],
fixed && styles.fixed,
layout === 'row' && styles.row,
outer && styles.outer
]}
/>
);
const styles = StyleSheet.create({
outer: {
padding: 4
},
row: {
flexDirection: 'row'
},
color0: {
backgroundColor: '#222'
},
color1: {
backgroundColor: '#666'
},
color2: {
backgroundColor: '#999'
},
color3: {
backgroundColor: 'blue'
},
color4: {
backgroundColor: 'orange'
},
color5: {
backgroundColor: 'red'
},
fixed: {
width: 20,
height: 20
}
});
module.exports = Box;

View File

@@ -1,2 +0,0 @@
import View from 'react-native/components/View';
export default View;

View File

@@ -1,32 +0,0 @@
import createDOMElement from 'react-native/modules/createDOMElement';
import StyleSheet from 'react-native/apis/StyleSheet';
const View = props => createDOMElement('div', { ...props, style: [styles.initial, props.style] });
const styles = StyleSheet.create({
initial: {
alignItems: 'stretch',
borderWidth: 0,
borderStyle: 'solid',
boxSizing: 'border-box',
display: 'flex',
flexBasis: 'auto',
flexDirection: 'column',
margin: 0,
padding: 0,
position: 'relative',
// button and anchor reset
backgroundColor: 'transparent',
color: 'inherit',
font: 'inherit',
textAlign: 'inherit',
textDecorationLine: 'none',
// list reset
listStyle: 'none',
// fix flexbox bugs
minHeight: 0,
minWidth: 0
}
});
module.exports = View;

View File

@@ -1,7 +0,0 @@
import Box from './Box';
import View from './View';
export default {
Box,
View
};

View File

@@ -1,7 +0,0 @@
import Box from './Box/lite';
import View from './View/lite';
export default {
Box,
View
};

View File

@@ -1,7 +0,0 @@
import Box from './Box';
import View from './View';
export default {
Box,
View
};

View File

@@ -3,6 +3,39 @@
<head>
<meta charset="UTF-8">
<title>Title</title>
<!-- "platform" implementation -->
<style>
.view {
align-items: stretch;
border-width: 0;
border-style: solid;
box-sizing: border-box;
display: flex;
flex-basis: auto;
flex-direction: column;
flex-shrink: 0;
margin: 0;
padding: 0;
position: relative;
background-color: transparent;
color: inherit;
font: inherit;
text-align: inherit;
text-decoration: none;
list-style: none;
min-height: 0;
min-width: 0;
}
.outer { padding: 4px; }
.row { flex-direction: row; }
.color0 { background-color: #222; }
.color1 { background-color: #666; }
.color2 { background-color: #999; }
.color3 { background-color: blue; }
.color4 { background-color: orange; }
.color5 { background-color: red; }
.fixed { width: 20px; height: 20px; }
</style>
</head>
<body>
<div class="root"></div>

View File

@@ -1,22 +1,25 @@
import cssModules from './implementations/css-modules';
import glamor from './implementations/glamor';
import reactNative from './implementations/react-native-web';
import reactNativeLite from './implementations/react-native-web/lite';
import styledComponents from './implementations/styled-components';
import cssModules from './components/css-modules';
import glamor from './components/glamor';
import platform from './components/platform';
import reactNative from './components/react-native-web';
import styledComponents from './components/styled-components';
import renderDeepTree from './tests/renderDeepTree';
import renderTweet from './tests/renderTweet';
import renderWideTree from './tests/renderWideTree';
const tests = [
// tweet
() => renderTweet('react-native-web', reactNative),
// deep tree
() => renderDeepTree('platform', platform),
() => renderDeepTree('css-modules', cssModules),
() => renderDeepTree('react-native-web/lite', reactNativeLite),
() => renderDeepTree('react-native-web', reactNative),
() => renderDeepTree('styled-components', styledComponents),
() => renderDeepTree('glamor', glamor),
// wide tree
() => renderWideTree('platform', platform),
() => renderWideTree('css-modules', cssModules),
() => renderWideTree('react-native-web/lite', reactNativeLite),
() => renderWideTree('react-native-web', reactNative),
() => renderWideTree('styled-components', styledComponents),
() => renderWideTree('glamor', glamor)

View File

@@ -3,9 +3,9 @@
"private": true,
"dependencies": {
"classnames": "^2.2.5",
"glamor": "^2.20.24",
"glamor": "3.0.0-1",
"marky": "^1.1.3",
"styled-components": "2.0.0-5"
"styled-components": "2.0.0-7"
},
"devDependencies": {
"css-loader": "^0.26.2",

View File

@@ -1,5 +1,5 @@
import createRenderBenchmark from '../modules/createRenderBenchmark';
import NestedTree from '../modules/NestedTree';
import createRenderBenchmark from '../createRenderBenchmark';
import NestedTree from '../components/NestedTree';
import React from 'react';
const renderDeepTree = (label, components) => createRenderBenchmark({

View File

@@ -0,0 +1,112 @@
import createRenderBenchmark from '../createRenderBenchmark';
import Tweet from '../components/Tweet';
import React from 'react';
const tweet1 = {
favorite_count: 30,
favorited: true,
id: '834889712556875776',
lang: 'en',
retweet_count: 6,
retweeted: false,
textParts: [
{
prefix: '',
text: 'Living burrito to burrito '
},
{
emoji: 'https://abs-0.twimg.com/emoji/v2/svg/1f32f.svg',
isEmoji: true,
prefix: '',
text: '🌯'
},
{
emoji: 'https://abs-0.twimg.com/emoji/v2/svg/1f32f.svg',
isEmoji: true,
prefix: '',
text: '🌯'
},
{
emoji: 'https://abs-0.twimg.com/emoji/v2/svg/1f32f.svg',
isEmoji: true,
prefix: '',
text: '🌯'
}
],
timestamp: 'Feb 23',
user: {
fullName: 'Nicolas',
screenName: 'necolas',
profileImageUrl: 'https://pbs.twimg.com/profile_images/804365942360719360/dQnPejph_normal.jpg'
}
};
const tweet2 = {
favorite_count: 84,
favorited: false,
id: '730896800060579840',
lang: 'en',
media: {
source: {
uri: 'https://pbs.twimg.com/media/CiSqvsJVEAAtLZ1.jpg',
width: 600,
height: 338
}
},
retweet_count: 4,
retweeted: true,
textParts: [
{
prefix: '',
text: 'Presenting '
},
{
displayUrl: 'mobile.twitter.com',
expandedUrl: 'https://mobile.twitter.com',
isEntity: true,
isUrl: true,
linkRelation: 'nofollow',
prefix: '',
text: '',
textDirection: 'ltr',
url: 'https://t.co/4hRCAxiUUG'
},
{
prefix: '',
text: ' with '
},
{
isEntity: true,
isMention: true,
prefix: '@',
text: 'davidbellona',
textDirection: 'ltr',
url: '/davidbellona'
},
{
prefix: '',
text: " at Twitter's all hands meeting "
}
],
timestamp: 'May 12',
user: {
fullName: 'Nicolas',
screenName: 'necolas',
profileImageUrl: 'https://pbs.twimg.com/profile_images/804365942360719360/dQnPejph_normal.jpg'
}
};
const renderTweet = (label, components) => createRenderBenchmark({
name: `Tweet [${label}]`,
runs: 10,
getElement() {
return (
<div style={{ width: 500 }}>
<Tweet tweet={tweet1} />
<Tweet tweet={tweet2} />
</div>
);
}
});
export default renderTweet;

View File

@@ -1,5 +1,5 @@
import createRenderBenchmark from '../modules/createRenderBenchmark';
import NestedTree from '../modules/NestedTree';
import createRenderBenchmark from '../createRenderBenchmark';
import NestedTree from '../components/NestedTree';
import React from 'react';
const renderWideTree = (label, components) => createRenderBenchmark({

View File

@@ -19,7 +19,9 @@ module.exports = {
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
query: { cacheDirectory: true }
query: {
cacheDirectory: true
}
}
]
},

View File

@@ -155,10 +155,6 @@ colormin@^1.0.5:
css-color-names "0.0.4"
has "^1.0.1"
colors@0.5.x:
version "0.5.1"
resolved "https://registry.yarnpkg.com/colors/-/colors-0.5.1.tgz#7d0023eaeb154e8ee9fce75dcb923d0ed1667774"
colors@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63"
@@ -171,15 +167,9 @@ core-js@^2.4.0:
version "2.4.1"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.4.1.tgz#4de911e667b0eae9124e34254b53aea6fc618d3e"
css-color-list@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/css-color-list/-/css-color-list-0.0.1.tgz#8718e8695ae7a2cc8787be8715f1c008a7f28b15"
dependencies:
css-color-names "0.0.1"
css-color-names@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.1.tgz#5d0548fa256456ede4a9a0c2ac7ab19d3eb1ad81"
css-color-keywords@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/css-color-keywords/-/css-color-keywords-1.0.0.tgz#fea2616dc676b2962686b3af8dbdbe180b244e05"
css-color-names@0.0.4:
version "0.0.4"
@@ -218,13 +208,13 @@ css-selector-tokenizer@^0.7.0:
fastparse "^1.1.1"
regexpu-core "^1.0.0"
css-to-react-native@^1.0.6:
version "1.0.6"
resolved "https://registry.yarnpkg.com/css-to-react-native/-/css-to-react-native-1.0.6.tgz#728c7e774e56536558a0ecaa990d9507c43a4ac4"
css-to-react-native@2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/css-to-react-native/-/css-to-react-native-2.0.1.tgz#179b25cba8cab7798118b1679b81f4d6b3fa0de3"
dependencies:
css-color-list "0.0.1"
css-color-keywords "^1.0.0"
fbjs "^0.8.5"
nearley "^2.7.7"
postcss-value-parser "^3.3.0"
cssesc@^0.1.0:
version "0.1.0"
@@ -282,10 +272,6 @@ defined@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693"
discontinuous-range@1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/discontinuous-range/-/discontinuous-range-1.0.0.tgz#e38331f0844bba49b9a9cb71c771585aab1bc65a"
electron-to-chromium@^1.2.3:
version "1.2.4"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.2.4.tgz#9751cbea89fa120bf88c226ba41eb8d0b6f1b597"
@@ -336,7 +322,15 @@ function-bind@^1.0.2:
version "1.1.0"
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.0.tgz#16176714c801798e4e8f2cf7f7529467bb4a5771"
glamor@^2.20.22, glamor@^2.20.24:
glamor@3.0.0-1:
version "3.0.0-1"
resolved "https://registry.yarnpkg.com/glamor/-/glamor-3.0.0-1.tgz#60f489e96d96c12620803d3677ac26413cb76a95"
dependencies:
babel-runtime "^6.18.0"
fbjs "^0.8.8"
object-assign "^4.1.0"
glamor@^2.20.22:
version "2.20.24"
resolved "https://registry.yarnpkg.com/glamor/-/glamor-2.20.24.tgz#a299af2eec687322634ba38e4a0854d8743d2041"
dependencies:
@@ -501,14 +495,6 @@ mkdirp@~0.5.1:
dependencies:
minimist "0.0.8"
nearley@^2.7.7:
version "2.7.13"
resolved "https://registry.yarnpkg.com/nearley/-/nearley-2.7.13.tgz#ae19927cc821a4b517de91962db9ed0e90d991fa"
dependencies:
nomnom "~1.6.2"
railroad-diagrams "^1.0.0"
randexp "^0.4.2"
node-fetch@^1.0.1:
version "1.6.3"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.6.3.tgz#dc234edd6489982d58e8f0db4f695029abcd8c04"
@@ -516,13 +502,6 @@ node-fetch@^1.0.1:
encoding "^0.1.11"
is-stream "^1.0.1"
nomnom@~1.6.2:
version "1.6.2"
resolved "https://registry.yarnpkg.com/nomnom/-/nomnom-1.6.2.tgz#84a66a260174408fc5b77a18f888eccc44fb6971"
dependencies:
colors "0.5.x"
underscore "~1.4.4"
normalize-range@^0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942"
@@ -803,17 +782,6 @@ query-string@^4.1.0:
object-assign "^4.1.0"
strict-uri-encode "^1.0.0"
railroad-diagrams@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz#eb7e6267548ddedfb899c1b90e57374559cddb7e"
randexp@^0.4.2:
version "0.4.4"
resolved "https://registry.yarnpkg.com/randexp/-/randexp-0.4.4.tgz#ba68265f4a9f9e85f5814d34e160291f939f168e"
dependencies:
discontinuous-range "1.0.0"
ret "~0.1.10"
reduce-css-calc@^1.2.6:
version "1.3.0"
resolved "https://registry.yarnpkg.com/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz#747c914e049614a4c9cfbba629871ad1d2927716"
@@ -854,10 +822,6 @@ regjsparser@^0.1.4:
dependencies:
jsesc "~0.5.0"
ret@~0.1.10:
version "0.1.13"
resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.13.tgz#38c2702ece654978941edd8b7dfac6aeeef4067d"
sax@~1.2.1:
version "1.2.2"
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.2.tgz#fd8631a23bc7826bef5d871bdb87378c95647828"
@@ -900,23 +864,23 @@ style-loader@^0.13.2:
dependencies:
loader-utils "^1.0.2"
styled-components@2.0.0-5:
version "2.0.0-5"
resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-2.0.0-5.tgz#df802d110cfb1a08a37e7746ecff11c765075e34"
styled-components@2.0.0-7:
version "2.0.0-7"
resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-2.0.0-7.tgz#a420b99622eacec26ae3d62c1c75985791d9691b"
dependencies:
buffer "^5.0.3"
css-to-react-native "^1.0.6"
css-to-react-native "2.0.1"
fbjs "^0.8.9"
glamor "^2.20.22"
inline-style-prefixer "^2.0.5"
is-function "^1.0.1"
is-plain-object "^2.0.1"
stylis "^1.1.13"
stylis "^1.2.0"
supports-color "^3.2.3"
stylis@^1.1.13:
version "1.1.13"
resolved "https://registry.yarnpkg.com/stylis/-/stylis-1.1.13.tgz#e15095d0027f49683f7556e31df184c1eafe1d6d"
stylis@^1.2.0:
version "1.2.6"
resolved "https://registry.yarnpkg.com/stylis/-/stylis-1.2.6.tgz#d7e72d3c8de52684a4f6cc82b3086e3634dc3c13"
supports-color@^2.0.0:
version "2.0.0"
@@ -944,10 +908,6 @@ ua-parser-js@^0.7.9:
version "0.7.12"
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.12.tgz#04c81a99bdd5dc52263ea29d24c6bf8d4818a4bb"
underscore@~1.4.4:
version "1.4.4"
resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.4.4.tgz#61a6a32010622afa07963bf325203cf12239d604"
uniq@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff"

View File

@@ -1,5 +1,7 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`apis/AppRegistry/renderApplication getApplication 1`] = `
"<style id=\"react-native-stylesheet-static\">
"<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;}
@@ -13,7 +15,7 @@ input::-webkit-inner-spin-button,input::-webkit-outer-spin-button,input::-webkit
.rn-pointerEvents-ah5dr5 *{pointer-events:none;}
.rn-pointerEvents-633pao{pointer-events:none;}
</style>
<style id=\"react-native-stylesheet\">
<style id=\\"react-native-stylesheet\\">
.rn-bottom-1p0dtai{bottom:0px}
.rn-left-1d2f490{left:0px}
.rn-position-u8s1d{position:absolute}
@@ -50,5 +52,8 @@ input::-webkit-inner-spin-button,input::-webkit-outer-spin-button,input::-webkit
.rn-paddingLeft-gy4na3{padding-left:0px}
.rn-textAlign-1ttztb7{text-align:inherit}
.rn-textDecoration-bauka4{text-decoration:none}
.rn-appearance-30o5oe{-moz-appearance:none;-webkit-appearance:none;appearance:none}
.rn-zIndex-1lgpqti{z-index:0}
.rn-zIndex-1wyyakw{z-index:-1}
</style>"
`;

View File

@@ -1,3 +1,5 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`apis/AsyncStorage mergeLocalStorageItem should have same behavior as react-native 1`] = `
Object {
"age": 31,

View File

@@ -6,11 +6,11 @@
* @flow
*/
import { canUseDOM } from 'fbjs/lib/ExecutionEnvironment';
import debounce from 'debounce';
import ExecutionEnvironment from 'fbjs/lib/ExecutionEnvironment';
import invariant from 'fbjs/lib/invariant';
const win = ExecutionEnvironment.canUseDOM ? window : { screen: {} };
const win = canUseDOM ? window : { screen: {} };
const dimensions = {};
@@ -38,6 +38,9 @@ class Dimensions {
}
Dimensions.set();
ExecutionEnvironment.canUseDOM && window.addEventListener('resize', debounce(Dimensions.set, 50));
if (canUseDOM) {
window.addEventListener('resize', debounce(Dimensions.set, 16), false);
}
module.exports = Dimensions;

View File

@@ -1,6 +1,6 @@
const Platform = {
OS: 'web',
select: (obj: Object) => obj.web
select: (obj: Object) => 'web' in obj ? obj.web : obj.default
};
module.exports = Platform;

View File

@@ -1,7 +1,7 @@
import asap from 'asap';
import { canUseDOM } from 'fbjs/lib/ExecutionEnvironment';
import generateCss from './generateCss';
import hash from './hash';
import requestAnimationFrame from 'fbjs/lib/requestAnimationFrame';
import staticCss from './staticCss';
const emptyObject = {};
@@ -111,7 +111,7 @@ class StyleManager {
className = createClassName(prop, value);
this._addToCache(className, prop, value);
if (canUseDOM) {
asap(() => {
requestAnimationFrame(() => {
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) {

View File

@@ -127,16 +127,20 @@ class StyleRegistry {
if (className) {
props.classList.push(className);
} else {
if (!props.style) {
props.style = {};
}
// 4x slower render
props.style[styleProp] = value;
}
}
return props;
}, { classList: [], style: {} });
}, { classList: [] });
const style = prefixInlineStyles(props.style);
props.className = classListToString(props.classList);
props.style = style;
if (props.style) {
props.style = prefixInlineStyles(props.style);
}
return props;
}

View File

@@ -1,7 +1,9 @@
// 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\">
"<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;}
@@ -15,13 +17,13 @@ input::-webkit-inner-spin-button,input::-webkit-outer-spin-button,input::-webkit
.rn-pointerEvents-ah5dr5 *{pointer-events:none;}
.rn-pointerEvents-633pao{pointer-events:none;}
</style>
<style id=\"react-native-stylesheet\">
<style id=\\"react-native-stylesheet\\">
</style>"
`;
exports[`apis/StyleSheet/StyleManager getStyleSheetHtml 2`] = `
"<style id=\"react-native-stylesheet-static\">
"<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;}
@@ -35,7 +37,7 @@ input::-webkit-inner-spin-button,input::-webkit-outer-spin-button,input::-webkit
.rn-pointerEvents-ah5dr5 *{pointer-events:none;}
.rn-pointerEvents-633pao{pointer-events:none;}
</style>
<style id=\"react-native-stylesheet\">
<style id=\\"react-native-stylesheet\\">
.rn-width-b8lwoo{width:100px}
</style>"
`;

View File

@@ -1,3 +1,5 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`apis/StyleSheet/StyleRegistry resolve with register, resolves to className 1`] = `
Object {
"classList": Array [
@@ -15,7 +17,6 @@ Object {
"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",
"style": Object {},
}
`;
@@ -36,7 +37,6 @@ Object {
"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",
"style": Object {},
}
`;
@@ -57,7 +57,6 @@ Object {
"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",
"style": Object {},
}
`;

View File

@@ -1,3 +1,5 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`apis/StyleSheet/createReactDOMStyle converts ReactNative style to ReactDOM style 1`] = `
Object {
"borderBottomWidth": "1px",
@@ -6,7 +8,7 @@ Object {
"borderTopWidth": "1px",
"borderWidthLeft": "2px",
"borderWidthRight": "3px",
"boxShadow": "1px 1px 1px 1px #000, 1px 2px 0px rgba(255,0,0,1)",
"boxShadow": "1px 1px 1px 1px #000, 1px 2px 0px red",
"display": "flex",
"flexShrink": 0,
"marginBottom": "0px",

View File

@@ -1,3 +1,5 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`apis/StyleSheet/expandStyle shortform -> longform 1`] = `
Object {
"borderBottomColor": "white",

View File

@@ -1,3 +1,5 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`apis/StyleSheet/flattenStyle should merge style objects 1`] = `
Object {
"opacity": 1,

View File

@@ -1 +1,3 @@
// 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,3 +1,5 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`apis/StyleSheet/i18nStyle LTR mode does not auto-flip 1`] = `
Object {
"borderBottomLeftRadius": 20,

View File

@@ -1,5 +1,7 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`apis/StyleSheet renderToString 1`] = `
"<style id=\"react-native-stylesheet-static\">
"<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;}
@@ -13,7 +15,7 @@ input::-webkit-inner-spin-button,input::-webkit-outer-spin-button,input::-webkit
.rn-pointerEvents-ah5dr5 *{pointer-events:none;}
.rn-pointerEvents-633pao{pointer-events:none;}
</style>
<style id=\"react-native-stylesheet\">
<style id=\\"react-native-stylesheet\\">
.rn-bottom-1p0dtai{bottom:0px}
.rn-left-1d2f490{left:0px}
.rn-position-u8s1d{position:absolute}

View File

@@ -9,7 +9,7 @@ describe('apis/StyleSheet/resolveBoxShadow', () => {
resolveBoxShadow(resolvedStyle, style);
expect(resolvedStyle).toEqual({
boxShadow: '0px 0px 0px rgba(255,0,0,1)'
boxShadow: '0px 0px 0px red'
});
});
@@ -28,9 +28,7 @@ describe('apis/StyleSheet/resolveBoxShadow', () => {
const style = { shadowOffset: { width: 1, height: 2 } };
resolveBoxShadow(resolvedStyle, style);
expect(resolvedStyle).toEqual({
boxShadow: '1px 2px 0px rgba(0,0,0,0)'
});
expect(resolvedStyle).toEqual({});
});
test('shadowRadius only', () => {
@@ -38,9 +36,7 @@ describe('apis/StyleSheet/resolveBoxShadow', () => {
const style = { shadowRadius: 5 };
resolveBoxShadow(resolvedStyle, style);
expect(resolvedStyle).toEqual({
boxShadow: '0px 0px 5px rgba(0,0,0,0)'
});
expect(resolvedStyle).toEqual({});
});
test('shadowOffset, shadowRadius, shadowColor', () => {

View File

@@ -10,6 +10,7 @@
*/
import normalizeValue from './normalizeValue';
import processColor from '../../modules/processColor';
import resolveBoxShadow from './resolveBoxShadow';
import resolveTextShadow from './resolveTextShadow';
import resolveTransform from './resolveTransform';
@@ -36,6 +37,16 @@ const styleShortFormProperties = {
writingDirection: ['direction']
};
const colorProps = {
backgroundColor: true,
borderColor: true,
borderTopColor: true,
borderRightColor: true,
borderBottomColor: true,
borderLeftColor: true,
color: true
};
const alphaSortProps = propsArray => propsArray.sort((a, b) => {
if (a < b) {
return -1;
@@ -106,17 +117,23 @@ const createReducer = (style, styleProps) => {
break;
}
default: {
// normalize color values
let finalValue = value;
if (colorProps[prop]) {
finalValue = processColor(value);
}
const longFormProperties = styleShortFormProperties[prop];
if (longFormProperties) {
longFormProperties.forEach((longForm, i) => {
// the value of any longform property in the original styles takes
// precedence over the shortform's value
if (styleProps.indexOf(longForm) === -1) {
resolvedStyle[longForm] = value;
resolvedStyle[longForm] = finalValue;
}
});
} else {
resolvedStyle[prop] = value;
resolvedStyle[prop] = finalValue;
}
}
}

View File

@@ -1,26 +1,22 @@
import normalizeColor from 'normalize-css-color';
import normalizeValue from './normalizeValue';
import processColor from '../../modules/processColor';
const defaultOffset = { height: 0, width: 0 };
const applyOpacity = (color, opacity = 1) => {
const nullableColor = normalizeColor(color);
const colorInt = nullableColor === null ? 0x00000000 : nullableColor;
const { r, g, b, a } = normalizeColor.rgba(colorInt);
const alpha = a.toFixed(2);
return `rgba(${r},${g},${b},${alpha * opacity})`;
};
// TODO: add inset and spread support
const resolveBoxShadow = (resolvedStyle, style) => {
const { height, width } = style.shadowOffset || defaultOffset;
const offsetX = normalizeValue(null, width);
const offsetY = normalizeValue(null, height);
const blurRadius = normalizeValue(null, style.shadowRadius || 0);
const color = applyOpacity(style.shadowColor, style.shadowOpacity);
const color = processColor(style.shadowColor, style.shadowOpacity);
const boxShadow = `${offsetX} ${offsetY} ${blurRadius} ${color}`;
resolvedStyle.boxShadow = style.boxShadow ? `${style.boxShadow}, ${boxShadow}` : boxShadow;
if (color) {
const boxShadow = `${offsetX} ${offsetY} ${blurRadius} ${color}`;
resolvedStyle.boxShadow = style.boxShadow ? `${style.boxShadow}, ${boxShadow}` : boxShadow;
} else if (style.boxShadow) {
resolvedStyle.boxShadow = style.boxShadow;
}
};
module.exports = resolveBoxShadow;

View File

@@ -1,4 +1,5 @@
import normalizeValue from './normalizeValue';
import processColor from '../../modules/processColor';
const defaultOffset = { height: 0, width: 0 };
@@ -7,9 +8,11 @@ const resolveTextShadow = (resolvedStyle, style) => {
const offsetX = normalizeValue(null, width);
const offsetY = normalizeValue(null, height);
const blurRadius = normalizeValue(null, style.textShadowRadius || 0);
const color = style.textShadowColor || 'currentcolor';
const color = processColor(style.textShadowColor);
resolvedStyle.textShadow = `${offsetX} ${offsetY} ${blurRadius} ${color}`;
if (color) {
resolvedStyle.textShadow = `${offsetX} ${offsetY} ${blurRadius} ${color}`;
}
};
module.exports = resolveTextShadow;

View File

@@ -1,5 +1,5 @@
import asap from 'asap';
import CSSPropertyOperations from 'react-dom/lib/CSSPropertyOperations';
import requestAnimationFrame from 'fbjs/lib/requestAnimationFrame';
const getRect = node => {
const height = node.offsetHeight;
@@ -15,13 +15,15 @@ const getRect = node => {
};
const measureLayout = (node, relativeToNativeNode, callback) => {
asap(() => {
const relativeNode = relativeToNativeNode || node.parentNode;
const relativeRect = getRect(relativeNode);
const { height, left, top, width } = getRect(node);
const x = left - relativeRect.left;
const y = top - relativeRect.top;
callback(x, y, width, height, left, top);
requestAnimationFrame(() => {
const relativeNode = relativeToNativeNode || (node && node.parentNode);
if (node && relativeNode) {
const relativeRect = getRect(relativeNode);
const { height, left, top, width } = getRect(node);
const x = left - relativeRect.left;
const y = top - relativeRect.top;
callback(x, y, width, height, left, top);
}
});
};
@@ -43,13 +45,16 @@ const UIManager = {
},
measureInWindow(node, callback) {
const { height, left, top, width } = getRect(node);
callback(left, top, width, height);
requestAnimationFrame(() => {
if (node) {
const { height, left, top, width } = getRect(node);
callback(left, top, width, height);
}
});
},
measureLayout(node, relativeToNativeNode, onFail, onSuccess) {
const relativeTo = relativeToNativeNode || node.parentNode;
measureLayout(node, relativeTo, onSuccess);
measureLayout(node, relativeToNativeNode, onSuccess);
},
updateView(node, props, component /* only needed to surpress React errors in development */) {

View File

@@ -1,17 +1,20 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`components/ActivityIndicator default render 1`] = `
<div
aria-valuemax="1"
aria-valuemin="0"
className="rn-alignItems-1awozwy rn-backgroundColor-wib322 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-color-homxoj rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-font-1lw9tu2 rn-justifyContent-1777fci rn-listStyle-1ebb2ja rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim rn-textAlign-1ttztb7 rn-textDecoration-bauka4"
role="progressbar"
style={Object {}}>
>
<div
className="rn-alignItems-1oszu61 rn-animationDuration-17bb2tj rn-animationIterationCount-1muvv40 rn-animationName-dozj4v rn-animationTimingFunction-1ldzwu0 rn-backgroundColor-wib322 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-color-homxoj rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-font-1lw9tu2 rn-height-z80fyv rn-listStyle-1ebb2ja rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim rn-textAlign-1ttztb7 rn-textDecoration-bauka4 rn-width-19wmn03"
style={Object {}}>
>
<svg
height="100%"
viewBox="0 0 32 32"
width="100%">
width="100%"
>
<circle
cx="16"
cy="16"
@@ -23,7 +26,8 @@ exports[`components/ActivityIndicator default render 1`] = `
"opacity": 0.2,
"stroke": "#1976D2",
}
} />
}
/>
<circle
cx="16"
cy="16"
@@ -36,7 +40,8 @@ exports[`components/ActivityIndicator default render 1`] = `
"strokeDasharray": 80,
"strokeDashoffset": 60,
}
} />
}
/>
</svg>
</div>
</div>
@@ -48,14 +53,15 @@ exports[`components/ActivityIndicator other render 1`] = `
aria-valuemin="0"
className="rn-alignItems-1awozwy rn-backgroundColor-wib322 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-color-homxoj rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-font-1lw9tu2 rn-justifyContent-1777fci rn-listStyle-1ebb2ja rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim rn-textAlign-1ttztb7 rn-textDecoration-bauka4"
role="progressbar"
style={Object {}}>
>
<div
className="rn-alignItems-1oszu61 rn-animationDuration-17bb2tj rn-animationIterationCount-1muvv40 rn-animationName-dozj4v rn-animationPlayState-1abnn5w rn-animationTimingFunction-1ldzwu0 rn-backgroundColor-wib322 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-color-homxoj rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-font-1lw9tu2 rn-height-1r8g8re rn-listStyle-1ebb2ja rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim rn-textAlign-1ttztb7 rn-textDecoration-bauka4 rn-width-1acpoxo"
style={Object {}}>
>
<svg
height="100%"
viewBox="0 0 32 32"
width="100%">
width="100%"
>
<circle
cx="16"
cy="16"
@@ -67,7 +73,8 @@ exports[`components/ActivityIndicator other render 1`] = `
"opacity": 0.2,
"stroke": "#1976D2",
}
} />
}
/>
<circle
cx="16"
cy="16"
@@ -80,7 +87,8 @@ exports[`components/ActivityIndicator other render 1`] = `
"strokeDasharray": 80,
"strokeDashoffset": 60,
}
} />
}
/>
</svg>
</div>
</div>

View File

@@ -1,30 +1,33 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`components/Image passes other props through to underlying View 1`] = `
<div
className="rn-alignItems-1oszu61 rn-backgroundColor-wib322 rn-backgroundPosition-vvn4in rn-backgroundRepeat-u6sd8q rn-backgroundSize-4gszlv rn-borderTopStyle-1efd50x rn-borderRightStyle-14skgim rn-borderBottomStyle-rull8r rn-borderLeftStyle-mm0ijv rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxSizing-deolkf rn-color-homxoj rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-font-1lw9tu2 rn-listStyle-1ebb2ja rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim rn-textAlign-1ttztb7 rn-textDecoration-bauka4 rn-zIndex-1lgpqti"
onResponderGrant={[Function]}
style={Object {}} />
/>
`;
exports[`components/Image prop "accessibilityLabel" 1`] = `
<div
aria-label="accessibilityLabel"
className="rn-alignItems-1oszu61 rn-backgroundColor-wib322 rn-backgroundPosition-vvn4in rn-backgroundRepeat-u6sd8q rn-backgroundSize-4gszlv rn-borderTopStyle-1efd50x rn-borderRightStyle-14skgim rn-borderBottomStyle-rull8r rn-borderLeftStyle-mm0ijv rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxSizing-deolkf rn-color-homxoj rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-font-1lw9tu2 rn-listStyle-1ebb2ja rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim rn-textAlign-1ttztb7 rn-textDecoration-bauka4 rn-zIndex-1lgpqti"
style={Object {}} />
/>
`;
exports[`components/Image prop "accessible" 1`] = `
<div
aria-hidden={true}
className="rn-alignItems-1oszu61 rn-backgroundColor-wib322 rn-backgroundPosition-vvn4in rn-backgroundRepeat-u6sd8q rn-backgroundSize-4gszlv rn-borderTopStyle-1efd50x rn-borderRightStyle-14skgim rn-borderBottomStyle-rull8r rn-borderLeftStyle-mm0ijv rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxSizing-deolkf rn-color-homxoj rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-font-1lw9tu2 rn-listStyle-1ebb2ja rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim rn-textAlign-1ttztb7 rn-textDecoration-bauka4 rn-zIndex-1lgpqti"
style={Object {}} />
/>
`;
exports[`components/Image prop "children" 1`] = `
<div
className="rn-alignItems-1oszu61 rn-backgroundColor-wib322 rn-backgroundPosition-vvn4in rn-backgroundRepeat-u6sd8q rn-backgroundSize-4gszlv rn-borderTopStyle-1efd50x rn-borderRightStyle-14skgim rn-borderBottomStyle-rull8r rn-borderLeftStyle-mm0ijv rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxSizing-deolkf rn-color-homxoj rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-font-1lw9tu2 rn-listStyle-1ebb2ja rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim rn-textAlign-1ttztb7 rn-textDecoration-bauka4 rn-zIndex-1lgpqti"
style={Object {}}>
>
<div
className="unique" />
className="unique"
/>
</div>
`;
@@ -33,15 +36,16 @@ exports[`components/Image prop "defaultSource" does not override "height" and "w
className="rn-alignItems-1oszu61 rn-backgroundColor-wib322 rn-backgroundPosition-vvn4in rn-backgroundRepeat-u6sd8q rn-backgroundSize-4gszlv rn-borderTopStyle-1efd50x rn-borderRightStyle-14skgim rn-borderBottomStyle-rull8r rn-borderLeftStyle-mm0ijv rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxSizing-deolkf rn-color-homxoj rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-font-1lw9tu2 rn-listStyle-1ebb2ja rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim rn-textAlign-1ttztb7 rn-textDecoration-bauka4 rn-zIndex-1lgpqti"
style={
Object {
"backgroundImage": "url(\"https://google.com/favicon.ico\")",
"backgroundImage": "url(\\"https://google.com/favicon.ico\\")",
"height": "20px",
"width": "40px",
}
}>
}
>
<img
className="rn-bottom-1p0dtai rn-height-1pi2tsx rn-left-1d2f490 rn-opacity-1272l3b rn-position-u8s1d rn-right-zchlnj rn-top-ipm5af rn-width-13qz1uu rn-zIndex-1wyyakw"
src="https://google.com/favicon.ico"
style={Object {}} />
/>
</div>
`;
@@ -50,15 +54,16 @@ exports[`components/Image prop "defaultSource" sets "height" and "width" styles
className="rn-alignItems-1oszu61 rn-backgroundColor-wib322 rn-backgroundPosition-vvn4in rn-backgroundRepeat-u6sd8q rn-backgroundSize-4gszlv rn-borderTopStyle-1efd50x rn-borderRightStyle-14skgim rn-borderBottomStyle-rull8r rn-borderLeftStyle-mm0ijv rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxSizing-deolkf rn-color-homxoj rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-font-1lw9tu2 rn-listStyle-1ebb2ja rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim rn-textAlign-1ttztb7 rn-textDecoration-bauka4 rn-zIndex-1lgpqti"
style={
Object {
"backgroundImage": "url(\"https://google.com/favicon.ico\")",
"backgroundImage": "url(\\"https://google.com/favicon.ico\\")",
"height": "10px",
"width": "20px",
}
}>
}
>
<img
className="rn-bottom-1p0dtai rn-height-1pi2tsx rn-left-1d2f490 rn-opacity-1272l3b rn-position-u8s1d rn-right-zchlnj rn-top-ipm5af rn-width-13qz1uu rn-zIndex-1wyyakw"
src="https://google.com/favicon.ico"
style={Object {}} />
/>
</div>
`;
@@ -67,13 +72,14 @@ exports[`components/Image prop "defaultSource" sets background image when value
className="rn-alignItems-1oszu61 rn-backgroundColor-wib322 rn-backgroundPosition-vvn4in rn-backgroundRepeat-u6sd8q rn-backgroundSize-4gszlv rn-borderTopStyle-1efd50x rn-borderRightStyle-14skgim rn-borderBottomStyle-rull8r rn-borderLeftStyle-mm0ijv rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxSizing-deolkf rn-color-homxoj rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-font-1lw9tu2 rn-listStyle-1ebb2ja rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim rn-textAlign-1ttztb7 rn-textDecoration-bauka4 rn-zIndex-1lgpqti"
style={
Object {
"backgroundImage": "url(\"https://google.com/favicon.ico\")",
"backgroundImage": "url(\\"https://google.com/favicon.ico\\")",
}
}>
}
>
<img
className="rn-bottom-1p0dtai rn-height-1pi2tsx rn-left-1d2f490 rn-opacity-1272l3b rn-position-u8s1d rn-right-zchlnj rn-top-ipm5af rn-width-13qz1uu rn-zIndex-1wyyakw"
src="https://google.com/favicon.ico"
style={Object {}} />
/>
</div>
`;
@@ -82,61 +88,62 @@ exports[`components/Image prop "defaultSource" sets background image when value
className="rn-alignItems-1oszu61 rn-backgroundColor-wib322 rn-backgroundPosition-vvn4in rn-backgroundRepeat-u6sd8q rn-backgroundSize-4gszlv rn-borderTopStyle-1efd50x rn-borderRightStyle-14skgim rn-borderBottomStyle-rull8r rn-borderLeftStyle-mm0ijv rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxSizing-deolkf rn-color-homxoj rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-font-1lw9tu2 rn-listStyle-1ebb2ja rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim rn-textAlign-1ttztb7 rn-textDecoration-bauka4 rn-zIndex-1lgpqti"
style={
Object {
"backgroundImage": "url(\"https://google.com/favicon.ico\")",
"backgroundImage": "url(\\"https://google.com/favicon.ico\\")",
}
}>
}
>
<img
className="rn-bottom-1p0dtai rn-height-1pi2tsx rn-left-1d2f490 rn-opacity-1272l3b rn-position-u8s1d rn-right-zchlnj rn-top-ipm5af rn-width-13qz1uu rn-zIndex-1wyyakw"
src="https://google.com/favicon.ico"
style={Object {}} />
/>
</div>
`;
exports[`components/Image prop "resizeMode" value "contain" 1`] = `
<div
className="rn-alignItems-1oszu61 rn-backgroundColor-wib322 rn-backgroundPosition-vvn4in rn-backgroundRepeat-u6sd8q rn-backgroundSize-ehq7j7 rn-borderTopStyle-1efd50x rn-borderRightStyle-14skgim rn-borderBottomStyle-rull8r rn-borderLeftStyle-mm0ijv rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxSizing-deolkf rn-color-homxoj rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-font-1lw9tu2 rn-listStyle-1ebb2ja rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim rn-textAlign-1ttztb7 rn-textDecoration-bauka4 rn-zIndex-1lgpqti"
style={Object {}} />
/>
`;
exports[`components/Image prop "resizeMode" value "cover" 1`] = `
<div
className="rn-alignItems-1oszu61 rn-backgroundColor-wib322 rn-backgroundPosition-vvn4in rn-backgroundRepeat-u6sd8q rn-backgroundSize-4gszlv rn-borderTopStyle-1efd50x rn-borderRightStyle-14skgim rn-borderBottomStyle-rull8r rn-borderLeftStyle-mm0ijv rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxSizing-deolkf rn-color-homxoj rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-font-1lw9tu2 rn-listStyle-1ebb2ja rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim rn-textAlign-1ttztb7 rn-textDecoration-bauka4 rn-zIndex-1lgpqti"
style={Object {}} />
/>
`;
exports[`components/Image prop "resizeMode" value "none" 1`] = `
<div
className="rn-alignItems-1oszu61 rn-backgroundColor-wib322 rn-backgroundPosition-vvn4in rn-backgroundRepeat-u6sd8q rn-backgroundSize-1sxrcry rn-borderTopStyle-1efd50x rn-borderRightStyle-14skgim rn-borderBottomStyle-rull8r rn-borderLeftStyle-mm0ijv rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxSizing-deolkf rn-color-homxoj rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-font-1lw9tu2 rn-listStyle-1ebb2ja rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim rn-textAlign-1ttztb7 rn-textDecoration-bauka4 rn-zIndex-1lgpqti"
style={Object {}} />
/>
`;
exports[`components/Image prop "resizeMode" value "stretch" 1`] = `
<div
className="rn-alignItems-1oszu61 rn-backgroundColor-wib322 rn-backgroundPosition-vvn4in rn-backgroundRepeat-u6sd8q rn-backgroundSize-x3cy2q rn-borderTopStyle-1efd50x rn-borderRightStyle-14skgim rn-borderBottomStyle-rull8r rn-borderLeftStyle-mm0ijv rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxSizing-deolkf rn-color-homxoj rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-font-1lw9tu2 rn-listStyle-1ebb2ja rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim rn-textAlign-1ttztb7 rn-textDecoration-bauka4 rn-zIndex-1lgpqti"
style={Object {}} />
/>
`;
exports[`components/Image prop "resizeMode" value "undefined" 1`] = `
<div
className="rn-alignItems-1oszu61 rn-backgroundColor-wib322 rn-backgroundPosition-vvn4in rn-backgroundRepeat-u6sd8q rn-backgroundSize-4gszlv rn-borderTopStyle-1efd50x rn-borderRightStyle-14skgim rn-borderBottomStyle-rull8r rn-borderLeftStyle-mm0ijv rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxSizing-deolkf rn-color-homxoj rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-font-1lw9tu2 rn-listStyle-1ebb2ja rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim rn-textAlign-1ttztb7 rn-textDecoration-bauka4 rn-zIndex-1lgpqti"
style={Object {}} />
/>
`;
exports[`components/Image prop "style" correctly supports "resizeMode" property 1`] = `
<div
className="rn-alignItems-1oszu61 rn-backgroundColor-wib322 rn-backgroundPosition-vvn4in rn-backgroundRepeat-u6sd8q rn-backgroundSize-ehq7j7 rn-borderTopStyle-1efd50x rn-borderRightStyle-14skgim rn-borderBottomStyle-rull8r rn-borderLeftStyle-mm0ijv rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxSizing-deolkf rn-color-homxoj rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-font-1lw9tu2 rn-listStyle-1ebb2ja rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim rn-textAlign-1ttztb7 rn-textDecoration-bauka4 rn-zIndex-1lgpqti"
style={Object {}} />
/>
`;
exports[`components/Image prop "testID" 1`] = `
<div
className="rn-alignItems-1oszu61 rn-backgroundColor-wib322 rn-backgroundPosition-vvn4in rn-backgroundRepeat-u6sd8q rn-backgroundSize-4gszlv rn-borderTopStyle-1efd50x rn-borderRightStyle-14skgim rn-borderBottomStyle-rull8r rn-borderLeftStyle-mm0ijv rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxSizing-deolkf rn-color-homxoj rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-font-1lw9tu2 rn-listStyle-1ebb2ja rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim rn-textAlign-1ttztb7 rn-textDecoration-bauka4 rn-zIndex-1lgpqti"
data-testid="testID"
style={Object {}} />
/>
`;
exports[`components/Image sets correct accessibility role" 1`] = `
<div
className="rn-alignItems-1oszu61 rn-backgroundColor-wib322 rn-backgroundPosition-vvn4in rn-backgroundRepeat-u6sd8q rn-backgroundSize-4gszlv rn-borderTopStyle-1efd50x rn-borderRightStyle-14skgim rn-borderBottomStyle-rull8r rn-borderLeftStyle-mm0ijv rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxSizing-deolkf rn-color-homxoj rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-font-1lw9tu2 rn-listStyle-1ebb2ja rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim rn-textAlign-1ttztb7 rn-textDecoration-bauka4 rn-zIndex-1lgpqti"
style={Object {}} />
/>
`;

View File

@@ -1,3 +1,5 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`components/Switch disabled when "false" a default checkbox is rendered 1`] = `
<div
className="rn-alignItems-1oszu61 rn-backgroundColor-wib322 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-color-homxoj rn-cursor-1loqt21 rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-font-1lw9tu2 rn-listStyle-1ebb2ja rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim rn-textAlign-1ttztb7 rn-textDecoration-bauka4 rn-userSelect-lrvibr"
@@ -6,27 +8,30 @@ exports[`components/Switch disabled when "false" a default checkbox is rendered
"height": "20px",
"width": "40px",
}
}>
}
>
<div
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-bottom-1p0dtai rn-boxSizing-deolkf rn-color-homxoj rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-font-1lw9tu2 rn-height-1dernwh rn-left-1d2f490 rn-listStyle-1ebb2ja rn-marginTop-1t01tom rn-marginRight-lchren rn-marginBottom-1qahzrx rn-marginLeft-1jj8364 rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-u8s1d rn-right-zchlnj rn-textAlign-1ttztb7 rn-textDecoration-bauka4 rn-top-ipm5af rn-transitionDuration-13tjlyg rn-width-e7q0ms"
style={
Object {
"backgroundColor": "#939393",
"backgroundColor": "rgba(147,147,147,1)",
"borderBottomLeftRadius": "10px",
"borderBottomRightRadius": "10px",
"borderTopLeftRadius": "10px",
"borderTopRightRadius": "10px",
}
} />
}
/>
<div
className="rn-alignItems-1oszu61 rn-alignSelf-k200y rn-borderTopLeftRadius-jt3ufn rn-borderTopRightRadius-1e868j9 rn-borderBottomRightRadius-ujv9e3 rn-borderBottomLeftRadius-1hakmuk rn-borderTopStyle-1efd50x rn-borderRightStyle-14skgim rn-borderBottomStyle-rull8r rn-borderLeftStyle-mm0ijv rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxShadow-1ewcgjf rn-boxSizing-deolkf rn-color-homxoj rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-font-1lw9tu2 rn-left-1fe0xdi rn-listStyle-1ebb2ja rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim rn-textAlign-1ttztb7 rn-textDecoration-bauka4 rn-transform-emqnss rn-transitionDuration-13tjlyg"
style={
Object {
"backgroundColor": "#FAFAFA",
"backgroundColor": "rgba(250,250,250,1)",
"height": "20px",
"width": "20px",
}
} />
}
/>
<input
checked={false}
className="rn-bottom-1p0dtai rn-cursor-1ei5mc7 rn-height-1pi2tsx rn-left-1d2f490 rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-opacity-1272l3b rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-u8s1d rn-right-zchlnj rn-top-ipm5af rn-width-13qz1uu"
@@ -34,8 +39,8 @@ exports[`components/Switch disabled when "false" a default checkbox is rendered
onBlur={[Function]}
onChange={[Function]}
onFocus={[Function]}
style={Object {}}
type="checkbox" />
type="checkbox"
/>
</div>
`;
@@ -47,9 +52,10 @@ exports[`components/Switch disabled when "true" a disabled checkbox is rendered
"height": "20px",
"width": "40px",
}
}>
}
>
<div
className="rn-alignItems-1oszu61 rn-backgroundColor-1i8mn1y rn-borderTopStyle-1efd50x rn-borderRightStyle-14skgim rn-borderBottomStyle-rull8r rn-borderLeftStyle-mm0ijv rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-bottom-1p0dtai rn-boxSizing-deolkf rn-color-homxoj rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-font-1lw9tu2 rn-height-1dernwh rn-left-1d2f490 rn-listStyle-1ebb2ja rn-marginTop-1t01tom rn-marginRight-lchren rn-marginBottom-1qahzrx rn-marginLeft-1jj8364 rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-u8s1d rn-right-zchlnj rn-textAlign-1ttztb7 rn-textDecoration-bauka4 rn-top-ipm5af rn-transitionDuration-13tjlyg rn-width-e7q0ms"
className="rn-alignItems-1oszu61 rn-backgroundColor-1hj8efq rn-borderTopStyle-1efd50x rn-borderRightStyle-14skgim rn-borderBottomStyle-rull8r rn-borderLeftStyle-mm0ijv rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-bottom-1p0dtai rn-boxSizing-deolkf rn-color-homxoj rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-font-1lw9tu2 rn-height-1dernwh rn-left-1d2f490 rn-listStyle-1ebb2ja rn-marginTop-1t01tom rn-marginRight-lchren rn-marginBottom-1qahzrx rn-marginLeft-1jj8364 rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-u8s1d rn-right-zchlnj rn-textAlign-1ttztb7 rn-textDecoration-bauka4 rn-top-ipm5af rn-transitionDuration-13tjlyg rn-width-e7q0ms"
style={
Object {
"borderBottomLeftRadius": "10px",
@@ -57,15 +63,17 @@ exports[`components/Switch disabled when "true" a disabled checkbox is rendered
"borderTopLeftRadius": "10px",
"borderTopRightRadius": "10px",
}
} />
}
/>
<div
className="rn-alignItems-1oszu61 rn-alignSelf-k200y rn-backgroundColor-1jvdyr1 rn-borderTopLeftRadius-jt3ufn rn-borderTopRightRadius-1e868j9 rn-borderBottomRightRadius-ujv9e3 rn-borderBottomLeftRadius-1hakmuk rn-borderTopStyle-1efd50x rn-borderRightStyle-14skgim rn-borderBottomStyle-rull8r rn-borderLeftStyle-mm0ijv rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxShadow-1ewcgjf rn-boxSizing-deolkf rn-color-homxoj rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-font-1lw9tu2 rn-left-1fe0xdi rn-listStyle-1ebb2ja rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim rn-textAlign-1ttztb7 rn-textDecoration-bauka4 rn-transform-emqnss rn-transitionDuration-13tjlyg"
className="rn-alignItems-1oszu61 rn-alignSelf-k200y rn-backgroundColor-1bgzomc rn-borderTopLeftRadius-jt3ufn rn-borderTopRightRadius-1e868j9 rn-borderBottomRightRadius-ujv9e3 rn-borderBottomLeftRadius-1hakmuk rn-borderTopStyle-1efd50x rn-borderRightStyle-14skgim rn-borderBottomStyle-rull8r rn-borderLeftStyle-mm0ijv rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxShadow-1ewcgjf rn-boxSizing-deolkf rn-color-homxoj rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-font-1lw9tu2 rn-left-1fe0xdi rn-listStyle-1ebb2ja rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim rn-textAlign-1ttztb7 rn-textDecoration-bauka4 rn-transform-emqnss rn-transitionDuration-13tjlyg"
style={
Object {
"height": "20px",
"width": "20px",
}
} />
}
/>
<input
checked={false}
className="rn-bottom-1p0dtai rn-cursor-1ei5mc7 rn-height-1pi2tsx rn-left-1d2f490 rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-opacity-1272l3b rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-u8s1d rn-right-zchlnj rn-top-ipm5af rn-width-13qz1uu"
@@ -73,8 +81,8 @@ exports[`components/Switch disabled when "true" a disabled checkbox is rendered
onBlur={[Function]}
onChange={[Function]}
onFocus={[Function]}
style={Object {}}
type="checkbox" />
type="checkbox"
/>
</div>
`;
@@ -86,27 +94,30 @@ exports[`components/Switch value when "false" an unchecked checkbox is rendered
"height": "20px",
"width": "40px",
}
}>
}
>
<div
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-bottom-1p0dtai rn-boxSizing-deolkf rn-color-homxoj rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-font-1lw9tu2 rn-height-1dernwh rn-left-1d2f490 rn-listStyle-1ebb2ja rn-marginTop-1t01tom rn-marginRight-lchren rn-marginBottom-1qahzrx rn-marginLeft-1jj8364 rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-u8s1d rn-right-zchlnj rn-textAlign-1ttztb7 rn-textDecoration-bauka4 rn-top-ipm5af rn-transitionDuration-13tjlyg rn-width-e7q0ms"
style={
Object {
"backgroundColor": "#939393",
"backgroundColor": "rgba(147,147,147,1)",
"borderBottomLeftRadius": "10px",
"borderBottomRightRadius": "10px",
"borderTopLeftRadius": "10px",
"borderTopRightRadius": "10px",
}
} />
}
/>
<div
className="rn-alignItems-1oszu61 rn-alignSelf-k200y rn-borderTopLeftRadius-jt3ufn rn-borderTopRightRadius-1e868j9 rn-borderBottomRightRadius-ujv9e3 rn-borderBottomLeftRadius-1hakmuk rn-borderTopStyle-1efd50x rn-borderRightStyle-14skgim rn-borderBottomStyle-rull8r rn-borderLeftStyle-mm0ijv rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxShadow-1ewcgjf rn-boxSizing-deolkf rn-color-homxoj rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-font-1lw9tu2 rn-left-1fe0xdi rn-listStyle-1ebb2ja rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim rn-textAlign-1ttztb7 rn-textDecoration-bauka4 rn-transform-emqnss rn-transitionDuration-13tjlyg"
style={
Object {
"backgroundColor": "#FAFAFA",
"backgroundColor": "rgba(250,250,250,1)",
"height": "20px",
"width": "20px",
}
} />
}
/>
<input
checked={false}
className="rn-bottom-1p0dtai rn-cursor-1ei5mc7 rn-height-1pi2tsx rn-left-1d2f490 rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-opacity-1272l3b rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-u8s1d rn-right-zchlnj rn-top-ipm5af rn-width-13qz1uu"
@@ -114,8 +125,8 @@ exports[`components/Switch value when "false" an unchecked checkbox is rendered
onBlur={[Function]}
onChange={[Function]}
onFocus={[Function]}
style={Object {}}
type="checkbox" />
type="checkbox"
/>
</div>
`;
@@ -127,28 +138,31 @@ exports[`components/Switch value when "true" a checked checkbox is rendered 1`]
"height": "20px",
"width": "40px",
}
}>
}
>
<div
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-bottom-1p0dtai rn-boxSizing-deolkf rn-color-homxoj rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-font-1lw9tu2 rn-height-1dernwh rn-left-1d2f490 rn-listStyle-1ebb2ja rn-marginTop-1t01tom rn-marginRight-lchren rn-marginBottom-1qahzrx rn-marginLeft-1jj8364 rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-u8s1d rn-right-zchlnj rn-textAlign-1ttztb7 rn-textDecoration-bauka4 rn-top-ipm5af rn-transitionDuration-13tjlyg rn-width-e7q0ms"
style={
Object {
"backgroundColor": "#A3D3CF",
"backgroundColor": "rgba(163,211,207,1)",
"borderBottomLeftRadius": "10px",
"borderBottomRightRadius": "10px",
"borderTopLeftRadius": "10px",
"borderTopRightRadius": "10px",
}
} />
}
/>
<div
className="rn-alignItems-1oszu61 rn-alignSelf-k200y rn-borderTopLeftRadius-jt3ufn rn-borderTopRightRadius-1e868j9 rn-borderBottomRightRadius-ujv9e3 rn-borderBottomLeftRadius-1hakmuk rn-borderTopStyle-1efd50x rn-borderRightStyle-14skgim rn-borderBottomStyle-rull8r rn-borderLeftStyle-mm0ijv rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxShadow-1ewcgjf rn-boxSizing-deolkf rn-color-homxoj rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-font-1lw9tu2 rn-left-7b7h2f rn-listStyle-1ebb2ja rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim rn-textAlign-1ttztb7 rn-textDecoration-bauka4 rn-transform-emqnss rn-transitionDuration-13tjlyg"
style={
Object {
"backgroundColor": "#009688",
"backgroundColor": "rgba(0,150,136,1)",
"height": "20px",
"marginLeft": "-20px",
"width": "20px",
}
} />
}
/>
<input
checked={true}
className="rn-bottom-1p0dtai rn-cursor-1ei5mc7 rn-height-1pi2tsx rn-left-1d2f490 rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-opacity-1272l3b rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-u8s1d rn-right-zchlnj rn-top-ipm5af rn-width-13qz1uu"
@@ -156,7 +170,7 @@ exports[`components/Switch value when "true" a checked checkbox is rendered 1`]
onBlur={[Function]}
onChange={[Function]}
onFocus={[Function]}
style={Object {}}
type="checkbox" />
type="checkbox"
/>
</div>
`;

View File

@@ -1,7 +1,10 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`components/Text prop "children" 1`] = `
<span
className="rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-color-homxoj rn-display-1471scf rn-font-1lw9tu2 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">
dir="auto"
>
children
</span>
`;
@@ -12,19 +15,20 @@ exports[`components/Text prop "onPress" 1`] = `
dir="auto"
onClick={[Function]}
onKeyDown={[Function]}
style={Object {}}
tabIndex={0} />
tabIndex={0}
/>
`;
exports[`components/Text prop "selectable" 1`] = `
<span
className="rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-color-homxoj rn-display-1471scf rn-font-1lw9tu2 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" />
dir="auto"
/>
`;
exports[`components/Text prop "selectable" 2`] = `
<span
className="rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-color-homxoj rn-display-1471scf rn-font-1lw9tu2 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-userSelect-lrvibr rn-whiteSpace-q42fyq rn-wordWrap-qvutc0"
dir="auto"
style={Object {}} />
/>
`;

View File

@@ -36,6 +36,7 @@ class Text extends Component {
adjustsFontSizeToFit,
allowFontScaling,
ellipsizeMode,
lineBreakMode,
minimumFontScale,
onLayout,
suppressHighlighting,

View File

@@ -14,6 +14,7 @@
/* @edit start */
const BoundingDimensions = require('./BoundingDimensions');
const normalizeColor = require('normalize-css-color');
const Position = require('./Position');
const React = require('react');
const TouchEventUtils = require('fbjs/lib/TouchEventUtils');
@@ -755,7 +756,40 @@ var TouchableMixin = {
};
var Touchable = {
Mixin: TouchableMixin
Mixin: TouchableMixin,
TOUCH_TARGET_DEBUG: false, // Highlights all touchable targets. Toggle with Inspector.
/**
* Renders a debugging overlay to visualize touch target with hitSlop (might not work on Android).
*/
renderDebugView: ({ color, hitSlop }) => {
if (process.env.NODE_ENV !== 'production') {
if (!Touchable.TOUCH_TARGET_DEBUG) {
return null;
}
const debugHitSlopStyle = {};
hitSlop = hitSlop || { top: 0, bottom: 0, left: 0, right: 0 };
for (const key in hitSlop) {
debugHitSlopStyle[key] = -hitSlop[key];
}
const hexColor = '#' + ('00000000' + normalizeColor(color).toString(16)).substr(-8);
return (
<View
pointerEvents="none"
style={{
position: 'absolute',
borderColor: hexColor.slice(0, -2) + '55', // More opaque
borderWidth: 1,
borderStyle: 'dashed',
backgroundColor: hexColor.slice(0, -2) + '0F', // Less opaque
...debugHitSlopStyle
}}
/>
);
}
}
};
module.exports = Touchable;

View File

@@ -31,7 +31,6 @@ var ensurePositiveDelayProps = require('./ensurePositiveDelayProps');
type Event = Object;
var DEFAULT_PROPS = {
accessibilityRole: 'button',
activeOpacity: 0.85,
underlayColor: 'black'
};
@@ -231,6 +230,7 @@ var TouchableHighlight = React.createClass({
render: function() {
const {
children,
/* eslint-disable */
activeOpacity,
onHideUnderlay,
@@ -270,9 +270,10 @@ var TouchableHighlight = React.createClass({
style={[styles.root, this.props.disabled && styles.disabled, this.state.underlayStyle]}
tabIndex={this.props.disabled ? null : '0'}
>
{React.cloneElement(React.Children.only(this.props.children), {
{React.cloneElement(React.Children.only(children), {
ref: CHILD_REF
})}
{Touchable.renderDebugView({ color: 'green', hitSlop: this.props.hitSlop })}
</View>
);
}

View File

@@ -65,7 +65,6 @@ var TouchableOpacity = React.createClass({
getDefaultProps: function() {
return {
accessibilityRole: 'button',
activeOpacity: 0.2,
focusedOpacity: 0.7
};
@@ -160,6 +159,7 @@ var TouchableOpacity = React.createClass({
render: function() {
const {
children,
/* eslint-disable */
activeOpacity,
focusedOpacity,
@@ -195,7 +195,10 @@ var TouchableOpacity = React.createClass({
onResponderRelease={this.touchableHandleResponderRelease}
onResponderTerminate={this.touchableHandleResponderTerminate}
tabIndex={this.props.disabled ? null : '0'}
/>
>
{children}
{Touchable.renderDebugView({ color: 'blue', hitSlop: this.props.hitSlop })}
</View>
);
}
});

View File

@@ -166,6 +166,15 @@ const TouchableWithoutFeedback = React.createClass({
'TouchableWithoutFeedback does not work well with Text children. Wrap children in a View instead. See ' +
((child._owner && child._owner.getName && child._owner.getName()) || '<unknown>')
);
if (
process.env.NODE_ENV !== 'production' &&
Touchable.TOUCH_TARGET_DEBUG &&
child.type &&
child.type.displayName === 'View'
) {
children = React.Children.toArray(children);
children.push(Touchable.renderDebugView({ color: 'red', hitSlop: this.props.hitSlop }));
}
const style = Touchable.TOUCH_TARGET_DEBUG && child.type && child.type.displayName === 'Text'
? [styles.root, this.props.disabled && styles.disabled, child.props.style, { color: 'red' }]
: [styles.root, this.props.disabled && styles.disabled, child.props.style];

View File

@@ -1,29 +1,69 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`components/View prop "children" 1`] = `
<div
className="rn-alignItems-1oszu61 rn-backgroundColor-wib322 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-color-homxoj rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-font-1lw9tu2 rn-listStyle-1ebb2ja rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim rn-textAlign-1ttztb7 rn-textDecoration-bauka4">
className="rn-alignItems-1oszu61 rn-backgroundColor-wib322 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-color-homxoj rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-font-1lw9tu2 rn-listStyle-1ebb2ja rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim rn-textAlign-1ttztb7 rn-textDecoration-bauka4"
>
<div
className="rn-alignItems-1oszu61 rn-backgroundColor-wib322 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-color-homxoj rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-font-1lw9tu2 rn-listStyle-1ebb2ja rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim rn-textAlign-1ttztb7 rn-textDecoration-bauka4"
data-testid="1" />
data-testid="1"
/>
</div>
`;
exports[`components/View prop "hitSlop" handles partial offsets 1`] = `
<div
className="rn-alignItems-1oszu61 rn-backgroundColor-wib322 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-color-homxoj rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-font-1lw9tu2 rn-listStyle-1ebb2ja rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim rn-textAlign-1ttztb7 rn-textDecoration-bauka4 rn-zIndex-1lgpqti"
>
<span
className="rn-bottom-1p0dtai rn-left-1d2f490 rn-position-u8s1d rn-right-zchlnj rn-zIndex-1wyyakw"
style={
Object {
"top": "-10px",
}
}
/>
</div>
`;
exports[`components/View prop "hitSlop" renders a span with negative position offsets 1`] = `
<div
className="rn-alignItems-1oszu61 rn-backgroundColor-wib322 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-color-homxoj rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-font-1lw9tu2 rn-listStyle-1ebb2ja rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim rn-textAlign-1ttztb7 rn-textDecoration-bauka4 rn-zIndex-1lgpqti"
>
<span
className="rn-position-u8s1d rn-zIndex-1wyyakw"
style={
Object {
"bottom": "-10px",
"left": "-5px",
"right": "-5px",
"top": "-10px",
}
}
/>
</div>
`;
exports[`components/View prop "pointerEvents" 1`] = `
<div
className="rn-alignItems-1oszu61 rn-backgroundColor-wib322 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-color-homxoj rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-font-1lw9tu2 rn-listStyle-1ebb2ja rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-pointerEvents-ah5dr5 rn-position-bnwqim rn-textAlign-1ttztb7 rn-textDecoration-bauka4"
style={Object {}} />
/>
`;
exports[`components/View rendered element is a "div" by default 1`] = `
<div
className="rn-alignItems-1oszu61 rn-backgroundColor-wib322 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-color-homxoj rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-font-1lw9tu2 rn-listStyle-1ebb2ja rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim rn-textAlign-1ttztb7 rn-textDecoration-bauka4" />
className="rn-alignItems-1oszu61 rn-backgroundColor-wib322 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-color-homxoj rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-font-1lw9tu2 rn-listStyle-1ebb2ja rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim rn-textAlign-1ttztb7 rn-textDecoration-bauka4"
/>
`;
exports[`components/View rendered element is a "span" when inside <View accessibilityRole="button" /> 1`] = `
<button
className="rn-alignItems-1oszu61 rn-backgroundColor-wib322 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-color-homxoj rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-font-1lw9tu2 rn-listStyle-1ebb2ja rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim rn-textAlign-1ttztb7 rn-textDecoration-bauka4"
className="rn-alignItems-1oszu61 rn-appearance-30o5oe rn-backgroundColor-wib322 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-color-homxoj rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-font-1lw9tu2 rn-listStyle-1ebb2ja rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim rn-textAlign-1ttztb7 rn-textDecoration-bauka4"
role="button"
type="button">
type="button"
>
<span
className="rn-alignItems-1oszu61 rn-backgroundColor-wib322 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-color-homxoj rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-font-1lw9tu2 rn-listStyle-1ebb2ja rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim rn-textAlign-1ttztb7 rn-textDecoration-bauka4" />
className="rn-alignItems-1oszu61 rn-backgroundColor-wib322 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-color-homxoj rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-font-1lw9tu2 rn-listStyle-1ebb2ja rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim rn-textAlign-1ttztb7 rn-textDecoration-bauka4"
/>
</button>
`;

View File

@@ -25,6 +25,20 @@ describe('components/View', () => {
expect(component.toJSON()).toMatchSnapshot();
});
describe('prop "hitSlop"', () => {
it('renders a span with negative position offsets', () => {
const component = renderer.create(
<View hitSlop={{ top: 10, bottom: 10, right: 5, left: 5 }} />
);
expect(component.toJSON()).toMatchSnapshot();
});
it('handles partial offsets', () => {
const component = renderer.create(<View hitSlop={{ top: 10 }} />);
expect(component.toJSON()).toMatchSnapshot();
});
});
test('prop "pointerEvents"', () => {
const component = renderer.create(<View pointerEvents="box-only" />);
expect(component.toJSON()).toMatchSnapshot();

View File

@@ -1,48 +1,22 @@
import '../../modules/injectResponderEventPlugin';
import applyLayout from '../../modules/applyLayout';
import applyNativeMethods from '../../modules/applyNativeMethods';
import createDOMElement from '../../modules/createDOMElement';
import normalizeNativeEvent from '../../modules/normalizeNativeEvent';
import getAccessibilityRole from '../../modules/getAccessibilityRole';
import StyleSheet from '../../apis/StyleSheet';
import ViewPropTypes from './ViewPropTypes';
import { Component, PropTypes } from 'react';
import React, { Component, PropTypes } from 'react';
const eventHandlerNames = [
'onClick',
'onClickCapture',
'onMoveShouldSetResponder',
'onMoveShouldSetResponderCapture',
'onResponderGrant',
'onResponderMove',
'onResponderReject',
'onResponderRelease',
'onResponderTerminate',
'onResponderTerminationRequest',
'onStartShouldSetResponder',
'onStartShouldSetResponderCapture',
'onTouchCancel',
'onTouchCancelCapture',
'onTouchEnd',
'onTouchEndCapture',
'onTouchMove',
'onTouchMoveCapture',
'onTouchStart',
'onTouchStartCapture'
];
const emptyObject = {};
const _normalizeEventForHandler = handler => e => {
e.nativeEvent = normalizeNativeEvent(e.nativeEvent);
return handler(e);
};
const normalizeEventHandlers = props => {
eventHandlerNames.forEach(handlerName => {
const handler = props[handlerName];
if (typeof handler === 'function') {
props[handlerName] = _normalizeEventForHandler(handler);
const calculateHitSlopStyle = hitSlop => {
const hitStyle = {};
for (const prop in hitSlop) {
if (hitSlop.hasOwnProperty(prop)) {
const value = hitSlop[prop];
hitStyle[prop] = value > 0 ? (-1) * value : 0;
}
});
}
return hitStyle;
};
class View extends Component {
@@ -63,20 +37,17 @@ class View extends Component {
};
getChildContext() {
return {
isInAButtonView: this.props.accessibilityRole === 'button'
};
const isInAButtonView = getAccessibilityRole(this.props) === 'button' ||
this.context.isInAButtonView;
return isInAButtonView ? { isInAButtonView } : emptyObject;
}
render() {
const {
pointerEvents,
hitSlop,
style,
/* eslint-disable */
accessibilityComponentType,
accessibilityTraits,
collapsable,
hitSlop,
onAccessibilityTap,
onLayout,
onMagicTap,
@@ -85,13 +56,20 @@ class View extends Component {
...otherProps
} = this.props;
const component = this.context.isInAButtonView ? 'span' : 'div';
const { isInAButtonView } = this.context;
const isButton = getAccessibilityRole(this.props) === 'button';
// DOM events need to be normalized to expect RN format
normalizeEventHandlers(otherProps);
otherProps.style = [styles.initial, isButton && styles.buttonOnly, style];
otherProps.style = [styles.initial, style, pointerEvents && pointerEventStyles[pointerEvents]];
if (hitSlop) {
const hitSlopStyle = calculateHitSlopStyle(hitSlop);
const hitSlopChild = createDOMElement('span', { style: [styles.hitSlop, hitSlopStyle] });
otherProps.children = React.Children.toArray(otherProps.children);
otherProps.children.unshift(hitSlopChild);
otherProps.style.unshift(styles.hasHitSlop);
}
const component = isInAButtonView ? 'span' : 'div';
return createDOMElement(component, otherProps);
}
}
@@ -109,7 +87,7 @@ const styles = StyleSheet.create({
margin: 0,
padding: 0,
position: 'relative',
// button and anchor reset
// button and anchor resets
backgroundColor: 'transparent',
color: 'inherit',
font: 'inherit',
@@ -120,21 +98,18 @@ const styles = StyleSheet.create({
// fix flexbox bugs
minHeight: 0,
minWidth: 0
}
});
const pointerEventStyles = StyleSheet.create({
auto: {
pointerEvents: 'auto'
},
'box-none': {
pointerEvents: 'box-none'
buttonOnly: {
appearance: 'none'
},
'box-only': {
pointerEvents: 'box-only'
// this zIndex ordering positions the hitSlop above the View but behind
// its children
hasHitSlop: {
zIndex: 0
},
none: {
pointerEvents: 'none'
hitSlop: {
...StyleSheet.absoluteFillObject,
zIndex: -1
}
});

View File

@@ -1,4 +1,8 @@
import createDOMElement from './modules/createDOMElement';
import findNodeHandle from './modules/findNodeHandle';
import modality from './modules/modality';
import NativeModules from './modules/NativeModules';
import processColor from './modules/processColor';
import { render, unmountComponentAtNode } from 'react-dom';
// APIs
@@ -37,15 +41,11 @@ import TouchableOpacity from './components/Touchable/TouchableOpacity';
import TouchableWithoutFeedback from './components/Touchable/TouchableWithoutFeedback';
import View from './components/View';
// modules
import createDOMElement from './modules/createDOMElement';
import modality from './modules/modality';
import NativeModules from './modules/NativeModules';
// propTypes
import ColorPropType from './propTypes/ColorPropType';
import EdgeInsetsPropType from './propTypes/EdgeInsetsPropType';
import PointPropType from './propTypes/PointPropType';
import ViewPropTypes from './components/View/ViewPropTypes';
modality();
@@ -55,6 +55,11 @@ const ReactNative = {
render,
unmountComponentAtNode,
// modules
createDOMElement,
NativeModules,
processColor,
// APIs
Animated,
AppRegistry,
@@ -91,14 +96,11 @@ const ReactNative = {
TouchableWithoutFeedback,
View,
// modules
createDOMElement,
NativeModules,
// propTypes
ColorPropType,
EdgeInsetsPropType,
PointPropType
PointPropType,
ViewPropTypes
};
module.exports = ReactNative;

View File

@@ -6,6 +6,7 @@
* @flow
*/
import createDOMProps from '../createDOMProps';
import findNodeHandle from '../findNodeHandle';
import StyleRegistry from '../../apis/StyleSheet/registry';
import UIManager from '../../apis/UIManager';
@@ -70,10 +71,9 @@ const NativeMethodsMixin = {
const node = findNodeHandle(this);
const classList = [...node.classList];
const { className, style } = StyleRegistry.resolveStateful(nativeProps.style, classList);
const props = { ...nativeProps, className, style };
UIManager.updateView(node, props, this);
const domProps = createDOMProps(nativeProps, style =>
StyleRegistry.resolveStateful(style, classList));
UIManager.updateView(node, domProps, this);
}
};

View File

@@ -5,24 +5,66 @@
* @flow
*/
import emptyFunction from 'fbjs/lib/emptyFunction';
import { canUseDOM } from 'fbjs/lib/ExecutionEnvironment';
import debounce from 'debounce';
const emptyObject = {};
const registry = {};
let id = 1;
const guid = () => `r-${id++}`;
if (canUseDOM) {
const triggerAll = () => {
Object.keys(registry).forEach(key => {
const instance = registry[key];
instance._handleLayout();
});
};
window.addEventListener('resize', debounce(triggerAll, 16), false);
}
const safeOverride = (original, next) => {
if (original) {
return function prototypeOverride() {
original.call(this);
next.call(this);
};
}
return next;
};
const applyLayout = Component => {
const componentDidMount = Component.prototype.componentDidMount || emptyFunction;
const componentDidUpdate = Component.prototype.componentDidUpdate || emptyFunction;
const componentDidMount = Component.prototype.componentDidMount;
const componentDidUpdate = Component.prototype.componentDidUpdate;
const componentWillUnmount = Component.prototype.componentWillUnmount;
Component.prototype.componentDidMount = function() {
componentDidMount.call(this);
this._layoutState = emptyObject;
this._handleLayout();
};
Component.prototype.componentDidMount = safeOverride(
componentDidMount,
function componentDidMount() {
this._layoutState = emptyObject;
this._isMounted = true;
this._onLayoutId = guid();
registry[this._onLayoutId] = this;
this._handleLayout();
}
);
Component.prototype.componentDidUpdate = function() {
componentDidUpdate.call(this);
this._handleLayout();
};
Component.prototype.componentDidUpdate = safeOverride(
componentDidUpdate,
function componentDidUpdate() {
this._handleLayout();
}
);
Component.prototype.componentWillUnmount = safeOverride(
componentWillUnmount,
function componentWillUnmount() {
this._isMounted = false;
delete registry[this._onLayoutId];
}
);
Component.prototype._handleLayout = function() {
const layout = this._layoutState;
@@ -30,13 +72,14 @@ const applyLayout = Component => {
if (onLayout) {
this.measure((x, y, width, height) => {
if (!this._isMounted) return;
if (
layout.x !== x || layout.y !== y || layout.width !== width || layout.height !== height
) {
const nextLayout = { x, y, width, height };
const nativeEvent = { layout: nextLayout };
this._layoutState = { x, y, width, height };
const nativeEvent = { layout: this._layoutState };
onLayout({ nativeEvent, timeStamp: Date.now() });
this._layoutState = nextLayout;
}
});
}

View File

@@ -1,29 +1,75 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`modules/createDOMElement prop "accessibilityLabel" 1`] = `
<span
aria-label="accessibilityLabel" />
aria-label="accessibilityLabel"
/>
`;
exports[`modules/createDOMElement prop "accessibilityLiveRegion" 1`] = `
<span
aria-live="polite" />
aria-live="off"
/>
`;
exports[`modules/createDOMElement prop "accessibilityRole" button 1`] = `
<button
role="button"
type="button" />
type="button"
/>
`;
exports[`modules/createDOMElement prop "accessibilityRole" compatibility with accessibilityComponentType 1`] = `
<button
role="button"
type="button"
/>
`;
exports[`modules/createDOMElement prop "accessibilityRole" compatibility with accessibilityComponentType 2`] = `
<a
role="link"
/>
`;
exports[`modules/createDOMElement prop "accessibilityRole" compatibility with accessibilityTraits 1`] = `
<button
role="button"
type="button"
/>
`;
exports[`modules/createDOMElement prop "accessibilityRole" compatibility with accessibilityTraits 2`] = `
<a
role="link"
/>
`;
exports[`modules/createDOMElement prop "accessibilityRole" headings 1`] = `
<h1
role="heading"
/>
`;
exports[`modules/createDOMElement prop "accessibilityRole" headings 2`] = `
<h3
aria-level="3"
role="heading"
/>
`;
exports[`modules/createDOMElement prop "accessibilityRole" link and target="_blank" 1`] = `
<a
rel=" noopener noreferrer"
role="link"
target="_blank" />
target="_blank"
/>
`;
exports[`modules/createDOMElement prop "accessibilityRole" roles 1`] = `
<header
role="banner" />
role="banner"
/>
`;
exports[`modules/createDOMElement prop "accessible" 1`] = `<span />`;
@@ -32,12 +78,14 @@ exports[`modules/createDOMElement prop "accessible" 2`] = `<span />`;
exports[`modules/createDOMElement prop "accessible" 3`] = `
<span
aria-hidden={true} />
aria-hidden={true}
/>
`;
exports[`modules/createDOMElement prop "testID" 1`] = `
<span
data-testid="Example.testID" />
data-testid="Example.testID"
/>
`;
exports[`modules/createDOMElement renders correct DOM element 1`] = `<span />`;

View File

@@ -18,7 +18,7 @@ describe('modules/createDOMElement', () => {
});
test('prop "accessibilityLiveRegion"', () => {
const accessibilityLiveRegion = 'polite';
const accessibilityLiveRegion = 'none';
const component = renderer.create(createDOMElement('span', { accessibilityLiveRegion }));
expect(component.toJSON()).toMatchSnapshot();
});
@@ -34,6 +34,16 @@ describe('modules/createDOMElement', () => {
expect(component.toJSON()).toMatchSnapshot();
});
test('headings', () => {
let component = renderer.create(createDOMElement('div', { accessibilityRole: 'heading' }));
expect(component.toJSON()).toMatchSnapshot();
component = renderer.create(
createDOMElement('div', { accessibilityRole: 'heading', 'aria-level': '3' })
);
expect(component.toJSON()).toMatchSnapshot();
});
test('link and target="_blank"', () => {
const component = renderer.create(
createDOMElement('span', {
@@ -43,6 +53,35 @@ describe('modules/createDOMElement', () => {
);
expect(component.toJSON()).toMatchSnapshot();
});
describe('compatibility with', () => {
test('accessibilityComponentType', () => {
let component = renderer.create(
createDOMElement('span', { accessibilityComponentType: 'button' })
);
expect(component.toJSON()).toMatchSnapshot();
component = renderer.create(
createDOMElement('span', {
accessibilityComponentType: 'button',
accessibilityRole: 'link'
})
);
expect(component.toJSON()).toMatchSnapshot();
});
test('accessibilityTraits', () => {
let component = renderer.create(
createDOMElement('span', { accessibilityTraits: 'button' })
);
expect(component.toJSON()).toMatchSnapshot();
component = renderer.create(
createDOMElement('span', { accessibilityTraits: 'button', accessibilityRole: 'link' })
);
expect(component.toJSON()).toMatchSnapshot();
});
});
});
test('prop "accessible"', () => {

View File

@@ -1,3 +1,8 @@
import '../injectResponderEventPlugin';
import createDOMProps from '../createDOMProps';
import getAccessibilityRole from '../getAccessibilityRole';
import normalizeNativeEvent from '../normalizeNativeEvent';
import React from 'react';
import StyleRegistry from '../../apis/StyleSheet/registry';
@@ -10,7 +15,6 @@ const roleComponents = {
complementary: 'aside',
contentinfo: 'footer',
form: 'form',
heading: 'h1',
link: 'a',
list: 'ul',
listitem: 'li',
@@ -19,52 +23,60 @@ const roleComponents = {
region: 'section'
};
const createDOMElement = (component, rnProps) => {
const {
accessibilityLabel,
accessibilityLiveRegion,
accessibilityRole,
accessible = true,
style: rnStyle, // we need to remove the RN styles from 'domProps'
testID,
type,
...domProps
} = rnProps || emptyObject;
const eventHandlerNames = {
onClick: true,
onClickCapture: true,
onMoveShouldSetResponder: true,
onMoveShouldSetResponderCapture: true,
onResponderGrant: true,
onResponderMove: true,
onResponderReject: true,
onResponderRelease: true,
onResponderTerminate: true,
onResponderTerminationRequest: true,
onStartShouldSetResponder: true,
onStartShouldSetResponderCapture: true,
onTouchCancel: true,
onTouchCancelCapture: true,
onTouchEnd: true,
onTouchEndCapture: true,
onTouchMove: true,
onTouchMoveCapture: true,
onTouchStart: true,
onTouchStartCapture: true
};
const accessibilityComponent = accessibilityRole && roleComponents[accessibilityRole];
const wrapEventHandler = handler => e => {
e.nativeEvent = normalizeNativeEvent(e.nativeEvent);
return handler(e);
};
const getAccessibilityComponent = (props = emptyObject) => {
const role = getAccessibilityRole(props);
if (role === 'heading') {
const level = props['aria-level'] || 1;
return `h${level}`;
}
return roleComponents[role];
};
const createDOMElement = (component, rnProps) => {
// use equivalent platform elements where possible
const accessibilityComponent = getAccessibilityComponent(rnProps);
const Component = accessibilityComponent || component;
const { className, style } = StyleRegistry.resolve(rnStyle) || emptyObject;
const domProps = createDOMProps(rnProps, style => StyleRegistry.resolve(style));
if (!accessible) {
domProps['aria-hidden'] = true;
}
if (accessibilityLabel) {
domProps['aria-label'] = accessibilityLabel;
}
if (accessibilityLiveRegion) {
domProps['aria-live'] = accessibilityLiveRegion;
}
if (testID) {
domProps['data-testid'] = testID;
}
if (accessibilityRole) {
domProps.role = accessibilityRole;
if (accessibilityRole === 'button') {
domProps.type = 'button';
} else if (accessibilityRole === 'link' && domProps.target === '_blank') {
domProps.rel = `${domProps.rel || ''} noopener noreferrer`;
// normalize DOM events to match React Native events
// TODO: move this out of the render path
for (const prop in domProps) {
if (Object.prototype.hasOwnProperty.call(domProps, prop)) {
const isEventHandler = typeof prop === 'function' && eventHandlerNames[prop];
if (isEventHandler) {
domProps[prop] = wrapEventHandler(prop);
}
}
}
if (className && className !== '') {
domProps.className = domProps.className ? `${domProps.className} ${className}` : className;
}
if (style) {
domProps.style = style;
}
if (type) {
domProps.type = type;
}
return <Component {...domProps} />;
};

View File

@@ -0,0 +1,75 @@
import getAccessibilityRole from '../getAccessibilityRole';
import StyleSheet from '../../apis/StyleSheet';
const emptyObject = {};
const pointerEventStyles = StyleSheet.create({
auto: {
pointerEvents: 'auto'
},
'box-none': {
pointerEvents: 'box-none'
},
'box-only': {
pointerEvents: 'box-only'
},
none: {
pointerEvents: 'none'
}
});
const createDOMProps = (rnProps, resolveStyle) => {
const {
accessibilityLabel,
accessibilityLiveRegion,
accessible = true,
pointerEvents,
style: rnStyle,
testID,
type,
/* eslint-disable */
accessibilityComponentType,
accessibilityRole,
accessibilityTraits,
/* eslint-enable */
...domProps
} = rnProps || emptyObject;
const pointerEventStyle = pointerEvents && pointerEventStyles[pointerEvents];
const { className, style } = resolveStyle([rnStyle, pointerEventStyle]) || emptyObject;
const role = getAccessibilityRole(rnProps || emptyObject);
if (!accessible) {
domProps['aria-hidden'] = true;
}
if (accessibilityLabel) {
domProps['aria-label'] = accessibilityLabel;
}
if (typeof accessibilityLiveRegion === 'string') {
domProps['aria-live'] = accessibilityLiveRegion === 'none' ? 'off' : accessibilityLiveRegion;
}
if (className && className !== '') {
domProps.className = domProps.className ? `${domProps.className} ${className}` : className;
}
if (role) {
domProps.role = role;
if (role === 'button') {
domProps.type = 'button';
} else if (role === 'link' && domProps.target === '_blank') {
domProps.rel = `${domProps.rel || ''} noopener noreferrer`;
}
}
if (style) {
domProps.style = style;
}
if (testID) {
domProps['data-testid'] = testID;
}
if (type) {
domProps.type = type;
}
return domProps;
};
module.exports = createDOMProps;

View File

@@ -0,0 +1,28 @@
/* eslint-env jasmine, jest */
import getAccessibilityRole from '..';
describe('modules/getAccessibilityRole', () => {
test('returns undefined when missing accessibility props', () => {
expect(getAccessibilityRole({})).toBeUndefined();
});
test('returns value of "accessibilityRole" when defined', () => {
expect(getAccessibilityRole({ accessibilityRole: 'banner' })).toEqual('banner');
});
test('returns "button" when iOS/Android accessibility prop equals "button"', () => {
expect(getAccessibilityRole({ accessibilityComponentType: 'button' })).toEqual('button');
expect(getAccessibilityRole({ accessibilityTraits: 'button' })).toEqual('button');
});
test('prioritizes "accessibilityRole" when defined', () => {
expect(
getAccessibilityRole({
accessibilityComponentType: 'button',
accessibilityRole: 'link',
accessibilityTraits: 'button'
})
).toEqual('link');
});
});

View File

@@ -0,0 +1,15 @@
const getAccessibilityRole = (
{
accessibilityComponentType,
accessibilityRole,
accessibilityTraits
}
) => {
if (accessibilityRole) {
return accessibilityRole;
} else if (accessibilityComponentType === 'button' || accessibilityTraits === 'button') {
return 'button';
}
};
module.exports = getAccessibilityRole;

View File

@@ -0,0 +1,89 @@
/* eslint-env jasmine, jest */
import processColor from '..';
describe('apis/StyleSheet/processColor', () => {
describe('predefined color names', () => {
it('should not convert "red"', () => {
const color = processColor('red');
expect(color).toEqual('red');
});
it('should not convert "white"', () => {
const color = processColor('white');
expect(color).toEqual('white');
});
it('should not convert "black"', () => {
const color = processColor('black');
expect(color).toEqual('black');
});
it('should not convert "currentcolor"', () => {
const color = processColor('currentcolor');
expect(color).toEqual('currentcolor');
});
it('should not convert "inherit"', () => {
const color = processColor('inherit');
expect(color).toEqual('inherit');
});
it('should not convert "transparent"', () => {
const color = processColor('transparent');
expect(color).toEqual('transparent');
});
});
describe('RGB strings', () => {
it('should not convert "rgb(x,y,z)"', () => {
const color = processColor('rgb(10,20,30)');
expect(color).toEqual('rgb(10,20,30)');
});
});
describe('RGBA strings', () => {
it('should not convert "rgba(x,y,z,a)"', () => {
const color = processColor('rgba(10,20,30,0.4)');
expect(color).toEqual('rgba(10,20,30,0.4)');
});
});
describe('HSL strings', () => {
it('should not convert "hsl(x,y%,z%)"', () => {
const color = processColor('hsl(318,69%,55%)');
expect(color).toEqual('hsl(318,69%,55%)');
});
});
describe('HSLA strings', () => {
it('should not convert "hsla(x,y%,z%,a)"', () => {
const color = processColor('hsla(318,69%,55%,0.25)');
expect(color).toEqual('hsla(318,69%,55%,0.25)');
});
});
describe('hex strings', () => {
it('should convert "#rrggbb"', () => {
const color = processColor('#1e83c9');
expect(color).toEqual('rgba(30,131,201,1)');
});
it('should convert "#rgba"', () => {
const color = processColor('#123A');
expect(color).toEqual('rgba(17,34,51,0.7)');
});
it('should convert "#rrggbbaa"', () => {
const color = processColor('#1e83c9AA');
expect(color).toEqual('rgba(30,131,201,0.7)');
});
});
describe('color int', () => {
it('should convert 0xff0000ff', () => {
const color = processColor(0xff0000ff);
expect(color).toEqual('rgba(255,0,0,1)');
});
});
});

View File

@@ -0,0 +1,25 @@
import normalizeColor from 'normalize-css-color';
const processColor = (color, opacity = 1) => {
if (
color === undefined ||
color === null ||
(opacity === 1 && typeof color === 'string' && color.charAt(0) !== '#')
) {
return color;
}
// convert number and hex
const int32Color = normalizeColor(color);
if (int32Color === null) {
return undefined;
}
// convert 0xrrggbbaa into rgba
const rgba = normalizeColor.rgba(int32Color);
rgba.a = rgba.a.toFixed(1);
const { r, g, b, a } = rgba;
return `rgba(${r},${g},${b},${a * opacity})`;
};
module.exports = processColor;

View File

@@ -3,7 +3,7 @@ const { array, bool, number, object, oneOf, oneOfType, string } = PropTypes;
const BaseComponentPropTypes = {
accessibilityLabel: string,
accessibilityLiveRegion: oneOf(['assertive', 'off', 'polite']),
accessibilityLiveRegion: oneOf(['assertive', 'none', 'polite']),
accessibilityRole: string,
accessible: bool,
style: oneOfType([array, number, object]),

826
yarn.lock

File diff suppressed because it is too large Load Diff