mirror of
https://github.com/zhigang1992/react-native-web.git
synced 2026-03-30 23:23:35 +08:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
65055028c6 | ||
|
|
93f425e414 | ||
|
|
ce4cc8a946 | ||
|
|
77fd867421 | ||
|
|
22999d7e5b |
28
.github/CONTRIBUTING.md
vendored
28
.github/CONTRIBUTING.md
vendored
@@ -23,35 +23,47 @@ Install dependencies (requires [yarn](https://yarnpkg.com/en/docs/install):
|
||||
yarn
|
||||
```
|
||||
|
||||
## Unit tests
|
||||
## Automated tests
|
||||
|
||||
To run flow:
|
||||
|
||||
```
|
||||
npm run flow
|
||||
```
|
||||
|
||||
To run the unit tests:
|
||||
|
||||
```
|
||||
npm test
|
||||
npm run jest
|
||||
```
|
||||
|
||||
…in watch mode:
|
||||
|
||||
```
|
||||
npm run test:watch
|
||||
npm run jest:watch
|
||||
```
|
||||
|
||||
To run all automated tests:
|
||||
|
||||
```
|
||||
npm test
|
||||
```
|
||||
|
||||
## Visual tests
|
||||
|
||||
Run the interactive storybook:
|
||||
To run the interactive storybook:
|
||||
|
||||
```
|
||||
npm run docs:start
|
||||
```
|
||||
|
||||
Run generate a static build of the storybook:
|
||||
To generate a static build of the storybook:
|
||||
|
||||
```
|
||||
npm run docs:build
|
||||
```
|
||||
|
||||
Run the performance benchmarks in a browser (opening `./performance/index.html`):
|
||||
To run the performance benchmarks in a browser (opening `./benchmarks/index.html`):
|
||||
|
||||
```
|
||||
npm run benchmarks
|
||||
@@ -59,7 +71,7 @@ npm run benchmarks
|
||||
|
||||
## Compile and build
|
||||
|
||||
Compile the source code to `dist`:
|
||||
To compile the source code to `dist`:
|
||||
|
||||
```
|
||||
npm run compile
|
||||
@@ -73,7 +85,7 @@ npm run build
|
||||
|
||||
### Pre-commit
|
||||
|
||||
Before creating a commit run:
|
||||
To format and lint code before commit:
|
||||
|
||||
```
|
||||
npm run precommit
|
||||
|
||||
18
package.json
18
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "react-native-web",
|
||||
"version": "0.0.97",
|
||||
"version": "0.0.98",
|
||||
"description": "React Native for Web",
|
||||
"main": "dist/index.js",
|
||||
"files": [
|
||||
@@ -18,19 +18,25 @@
|
||||
"flow": "flow",
|
||||
"fmt": "find benchmarks docs src -name '*.js' | grep -v -E '(node_modules|dist)' | xargs npm run fmt:cmd",
|
||||
"fmt:cmd": "prettier --print-width=100 --single-quote --write",
|
||||
"jest": "jest",
|
||||
"jest:watch": "npm run test -- --watch",
|
||||
"lint": "npm run lint:cmd -- benchmarks docs src",
|
||||
"lint:cmd": "eslint --fix --ignore-path .gitignore",
|
||||
"precommit": "lint-staged",
|
||||
"release": "npm run compile && npm run build && npm publish",
|
||||
"test": "jest",
|
||||
"test:watch": "npm run test -- --watch"
|
||||
"release": "npm run lint && npm run test && npm run compile && npm run build && npm publish",
|
||||
"test": "flow && jest"
|
||||
},
|
||||
"babel": {
|
||||
"presets": [
|
||||
"react-native"
|
||||
],
|
||||
"plugins": [
|
||||
[ "transform-react-remove-prop-types", { "mode": "wrap" } ]
|
||||
[
|
||||
"transform-react-remove-prop-types",
|
||||
{
|
||||
"mode": "wrap"
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"jest": {
|
||||
@@ -77,7 +83,7 @@
|
||||
"eslint-plugin-promise": "^3.5.0",
|
||||
"eslint-plugin-react": "^6.10.3",
|
||||
"file-loader": "^0.11.1",
|
||||
"flow-bin": "^0.46.0",
|
||||
"flow-bin": "^0.47.0",
|
||||
"jest": "^19.0.2",
|
||||
"lint-staged": "^3.4.2",
|
||||
"node-libs-browser": "^0.5.3",
|
||||
|
||||
65
src/apis/AppRegistry/AppContainer.js
Normal file
65
src/apis/AppRegistry/AppContainer.js
Normal file
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import StyleSheet from '../StyleSheet';
|
||||
import View from '../../components/View';
|
||||
import { any, node } from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
|
||||
type Context = {
|
||||
rootTag: any
|
||||
};
|
||||
|
||||
type Props = {
|
||||
children?: React.Children,
|
||||
rootTag: any
|
||||
};
|
||||
|
||||
type State = {
|
||||
mainKey: number
|
||||
};
|
||||
|
||||
class AppContainer extends Component {
|
||||
props: Props;
|
||||
state: State = { mainKey: 1 };
|
||||
|
||||
static childContextTypes = {
|
||||
rootTag: any
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
children: node,
|
||||
rootTag: any.isRequired
|
||||
};
|
||||
|
||||
getChildContext(): Context {
|
||||
return {
|
||||
rootTag: this.props.rootTag
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View pointerEvents="box-none" style={styles.appContainer}>
|
||||
<View
|
||||
children={this.props.children}
|
||||
key={this.state.mainKey}
|
||||
pointerEvents="box-none"
|
||||
style={styles.appContainer}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
/**
|
||||
* Ensure that the application covers the whole screen.
|
||||
*/
|
||||
appContainer: {
|
||||
flex: 1
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = AppContainer;
|
||||
@@ -1,41 +0,0 @@
|
||||
/**
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import StyleSheet from '../StyleSheet';
|
||||
import View from '../../components/View';
|
||||
import { any, object } from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
|
||||
class ReactNativeApp extends Component {
|
||||
static propTypes = {
|
||||
initialProps: object,
|
||||
rootComponent: any.isRequired,
|
||||
rootTag: any
|
||||
};
|
||||
|
||||
render() {
|
||||
const { initialProps, rootComponent: RootComponent, rootTag } = this.props;
|
||||
|
||||
return (
|
||||
<View style={styles.appContainer}>
|
||||
<RootComponent {...initialProps} rootTag={rootTag} />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
/**
|
||||
* Ensure that the application covers the whole screen.
|
||||
*/
|
||||
appContainer: {
|
||||
position: 'absolute',
|
||||
left: 0,
|
||||
top: 0,
|
||||
right: 0,
|
||||
bottom: 0
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = ReactNativeApp;
|
||||
@@ -1,6 +1,14 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`apis/AppRegistry/renderApplication getApplication 1`] = `
|
||||
<AppContainer
|
||||
rootTag={Object {}}
|
||||
>
|
||||
<RootComponent />
|
||||
</AppContainer>
|
||||
`;
|
||||
|
||||
exports[`apis/AppRegistry/renderApplication getApplication 2`] = `
|
||||
"<style id=\\"react-native-stylesheet-static\\">
|
||||
html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:rgba(0,0,0,0);}
|
||||
body{margin:0;}
|
||||
@@ -41,6 +49,7 @@ input::-webkit-inner-spin-button,input::-webkit-outer-spin-button,input::-webkit
|
||||
.rn-boxSizing-deolkf{box-sizing:border-box}
|
||||
.rn-display-6koalj{display:-webkit-box;display:-moz-box;display:-ms-flexbox;display:-webkit-flex;display:flex}
|
||||
.rn-flexShrink-1qe8dj5{-webkit-flex-shrink:0;flex-shrink:0}
|
||||
.rn-flexShrink-1wbh5a2{-webkit-flex-shrink:1;flex-shrink:1}
|
||||
.rn-flexBasis-1mlwlqe{-webkit-flex-basis:auto;flex-basis:auto}
|
||||
.rn-flexDirection-eqz5dr{-webkit-box-direction:normal;-webkit-box-orient:vertical;-webkit-flex-direction:column;flex-direction:column}
|
||||
.rn-marginTop-1mnahxq{margin-top:0px}
|
||||
@@ -55,5 +64,6 @@ input::-webkit-inner-spin-button,input::-webkit-outer-spin-button,input::-webkit
|
||||
.rn-paddingLeft-gy4na3{padding-left:0px}
|
||||
.rn-zIndex-1lgpqti{z-index:0}
|
||||
.rn-zIndex-1wyyakw{z-index:-1}
|
||||
.rn-flexGrow-16y2uox{-webkit-flex-grow:1;flex-grow:1}
|
||||
</style>"
|
||||
`;
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
import { getApplication } from '../renderApplication';
|
||||
import React from 'react';
|
||||
|
||||
const component = () => <div />;
|
||||
const RootComponent = () => <div />;
|
||||
|
||||
describe('apis/AppRegistry/renderApplication', () => {
|
||||
test('getApplication', () => {
|
||||
const { element, stylesheet } = getApplication(component, {});
|
||||
const { element, stylesheet } = getApplication(RootComponent, {});
|
||||
|
||||
expect(element).toBeTruthy();
|
||||
expect(element).toMatchSnapshot();
|
||||
expect(stylesheet).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import { Component } from 'react';
|
||||
import invariant from 'fbjs/lib/invariant';
|
||||
import { unmountComponentAtNode } from 'react-dom';
|
||||
import renderApplication, { getApplication } from './renderApplication';
|
||||
@@ -14,9 +13,9 @@ import renderApplication, { getApplication } from './renderApplication';
|
||||
const emptyObject = {};
|
||||
const runnables = {};
|
||||
|
||||
type ComponentProvider = () => Component<any, any, any>;
|
||||
export type ComponentProvider = () => ReactClass<any>;
|
||||
|
||||
type AppConfig = {
|
||||
export type AppConfig = {
|
||||
appKey: string,
|
||||
component?: ComponentProvider,
|
||||
run?: Function
|
||||
|
||||
@@ -8,25 +8,31 @@
|
||||
|
||||
import invariant from 'fbjs/lib/invariant';
|
||||
import { render } from 'react-dom';
|
||||
import ReactNativeApp from './ReactNativeApp';
|
||||
import AppContainer from './AppContainer';
|
||||
import StyleSheet from '../../apis/StyleSheet';
|
||||
import React, { Component } from 'react';
|
||||
import React from 'react';
|
||||
|
||||
export default function renderApplication(
|
||||
RootComponent: Component,
|
||||
RootComponent: ReactClass<Object>,
|
||||
initialProps: Object,
|
||||
rootTag: any
|
||||
) {
|
||||
invariant(rootTag, 'Expect to have a valid rootTag, instead got ', rootTag);
|
||||
|
||||
const component = (
|
||||
<ReactNativeApp initialProps={initialProps} rootComponent={RootComponent} rootTag={rootTag} />
|
||||
render(
|
||||
<AppContainer rootTag={rootTag}>
|
||||
<RootComponent {...initialProps} />
|
||||
</AppContainer>,
|
||||
rootTag
|
||||
);
|
||||
render(component, rootTag);
|
||||
}
|
||||
|
||||
export function getApplication(RootComponent: Component, initialProps: Object): Object {
|
||||
const element = <ReactNativeApp initialProps={initialProps} rootComponent={RootComponent} />;
|
||||
export function getApplication(RootComponent: ReactClass<Object>, initialProps: Object): Object {
|
||||
const element = (
|
||||
<AppContainer rootTag={{}}>
|
||||
<RootComponent {...initialProps} />
|
||||
</AppContainer>
|
||||
);
|
||||
const stylesheet = StyleSheet.renderToString();
|
||||
return { element, stylesheet };
|
||||
}
|
||||
|
||||
@@ -16,30 +16,33 @@ class Clipboard {
|
||||
|
||||
static setString(text) {
|
||||
let success = false;
|
||||
const body = document.body;
|
||||
|
||||
// add the text to a hidden node
|
||||
const node = document.createElement('span');
|
||||
node.textContent = text;
|
||||
node.style.position = 'absolute';
|
||||
node.style.opacity = '0';
|
||||
document.body.appendChild(node);
|
||||
if (body) {
|
||||
// add the text to a hidden node
|
||||
const node = document.createElement('span');
|
||||
node.textContent = text;
|
||||
node.style.position = 'absolute';
|
||||
node.style.opacity = '0';
|
||||
body.appendChild(node);
|
||||
|
||||
// select the text
|
||||
const selection = window.getSelection();
|
||||
selection.removeAllRanges();
|
||||
const range = document.createRange();
|
||||
range.selectNodeContents(node);
|
||||
selection.addRange(range);
|
||||
// select the text
|
||||
const selection = window.getSelection();
|
||||
selection.removeAllRanges();
|
||||
const range = document.createRange();
|
||||
range.selectNodeContents(node);
|
||||
selection.addRange(range);
|
||||
|
||||
// attempt to copy
|
||||
try {
|
||||
document.execCommand('copy');
|
||||
success = true;
|
||||
} catch (e) {}
|
||||
// attempt to copy
|
||||
try {
|
||||
document.execCommand('copy');
|
||||
success = true;
|
||||
} catch (e) {}
|
||||
|
||||
// remove selection and node
|
||||
selection.removeAllRanges();
|
||||
document.body.removeChild(node);
|
||||
// remove selection and node
|
||||
selection.removeAllRanges();
|
||||
body.removeChild(node);
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
@@ -24,7 +24,9 @@ const isRTL = () => {
|
||||
|
||||
const onChange = () => {
|
||||
if (ExecutionEnvironment.canUseDOM) {
|
||||
document.documentElement.setAttribute('dir', isRTL() ? 'rtl' : 'ltr');
|
||||
if (document.documentElement && document.documentElement.setAttribute) {
|
||||
document.documentElement.setAttribute('dir', isRTL() ? 'rtl' : 'ltr');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -33,19 +33,25 @@ const Linking = {
|
||||
*/
|
||||
const iframeOpen = url => {
|
||||
const noOpener = url.indexOf('mailto:') !== 0;
|
||||
const iframe = document.createElement('iframe');
|
||||
iframe.style.display = 'none';
|
||||
document.body.appendChild(iframe);
|
||||
const body = document.body;
|
||||
if (body) {
|
||||
const iframe = document.createElement('iframe');
|
||||
iframe.style.display = 'none';
|
||||
body.appendChild(iframe);
|
||||
|
||||
const iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
|
||||
const script = iframeDoc.createElement('script');
|
||||
const openerExpression = noOpener ? 'child.opener = null' : '';
|
||||
script.text = `
|
||||
window.parent = null; window.top = null; window.frameElement = null;
|
||||
var child = window.open("${url}"); ${openerExpression};
|
||||
`;
|
||||
iframeDoc.body.appendChild(script);
|
||||
document.body.removeChild(iframe);
|
||||
const iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
|
||||
const iframeBody = iframeDoc.body;
|
||||
if (iframeBody) {
|
||||
const script = iframeDoc.createElement('script');
|
||||
const openerExpression = noOpener ? 'child.opener = null' : '';
|
||||
script.text = `
|
||||
window.parent = null; window.top = null; window.frameElement = null;
|
||||
var child = window.open("${url}"); ${openerExpression};
|
||||
`;
|
||||
iframeBody.appendChild(script);
|
||||
}
|
||||
body.removeChild(iframe);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = Linking;
|
||||
|
||||
@@ -1,62 +1,68 @@
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Copyright (c) 2016-present, Nicolas Gallagher.
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule StyleSheetValidation
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import ImageStylePropTypes from '../../components/Image/ImageStylePropTypes';
|
||||
import ReactPropTypeLocationNames from '../../vendor/ReactPropTypeLocationNames';
|
||||
import ReactPropTypesSecret from '../../vendor/ReactPropTypesSecret';
|
||||
import TextInputStylePropTypes from '../../components/TextInput/TextInputStylePropTypes';
|
||||
import TextStylePropTypes from '../../components/Text/TextStylePropTypes';
|
||||
import ViewStylePropTypes from '../../components/View/ViewStylePropTypes';
|
||||
import warning from 'fbjs/lib/warning';
|
||||
import { oneOf, string } from 'prop-types';
|
||||
|
||||
// Hardcoded because this is a legit case but we don't want to load it from
|
||||
// a private API. We might likely want to unify style sheet creation with how it
|
||||
// is done in the DOM so this might move into React. I know what I'm doing so
|
||||
// plz don't fire me.
|
||||
const ReactPropTypesSecret = 'SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED';
|
||||
|
||||
class StyleSheetValidation {
|
||||
static validateStyleProp(prop, style, caller) {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
if (allStylePropTypes[prop] === undefined) {
|
||||
var message1 = '"' + prop + '" is not a valid style property.';
|
||||
var message2 =
|
||||
const message1 = '"' + prop + '" is not a valid style property.';
|
||||
const message2 =
|
||||
'\nValid style props: ' +
|
||||
JSON.stringify(Object.keys(allStylePropTypes).sort(), null, ' ');
|
||||
styleError(message1, style, caller, message2);
|
||||
} else {
|
||||
var error = allStylePropTypes[prop](
|
||||
style,
|
||||
prop,
|
||||
caller,
|
||||
ReactPropTypeLocationNames.prop,
|
||||
null,
|
||||
ReactPropTypesSecret
|
||||
);
|
||||
if (error) {
|
||||
styleError(error.message, style, caller);
|
||||
}
|
||||
}
|
||||
const error = allStylePropTypes[prop](
|
||||
style,
|
||||
prop,
|
||||
caller,
|
||||
'prop',
|
||||
null,
|
||||
ReactPropTypesSecret
|
||||
);
|
||||
if (error) {
|
||||
styleError(error.message, style, caller);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static validateStyle(name, styles) {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
for (var prop in styles[name]) {
|
||||
for (const prop in styles[name]) {
|
||||
StyleSheetValidation.validateStyleProp(prop, styles[name], 'StyleSheet ' + name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static addValidStylePropTypes(stylePropTypes) {
|
||||
for (var key in stylePropTypes) {
|
||||
for (const key in stylePropTypes) {
|
||||
allStylePropTypes[key] = stylePropTypes[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var styleError = function(message1, style, caller?, message2?) {
|
||||
const styleError = function(message1, style, caller?, message2?) {
|
||||
warning(
|
||||
false,
|
||||
message1 +
|
||||
@@ -68,7 +74,7 @@ var styleError = function(message1, style, caller?, message2?) {
|
||||
);
|
||||
};
|
||||
|
||||
var allStylePropTypes = {};
|
||||
const allStylePropTypes = {};
|
||||
|
||||
StyleSheetValidation.addValidStylePropTypes(ImageStylePropTypes);
|
||||
StyleSheetValidation.addValidStylePropTypes(TextStylePropTypes);
|
||||
|
||||
@@ -23,7 +23,7 @@ function getStyle(style) {
|
||||
}
|
||||
|
||||
function flattenStyle(style: ?StyleObj): ?Object {
|
||||
if (!style) {
|
||||
if (style == null || typeof style === 'boolean') {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,8 @@ import React, { Component } from 'react';
|
||||
import { bool, number } from 'prop-types';
|
||||
|
||||
class ProgressBar extends Component {
|
||||
_progressElement = null;
|
||||
|
||||
static displayName = 'ProgressBar';
|
||||
|
||||
static propTypes = {
|
||||
@@ -58,17 +60,19 @@ class ProgressBar extends Component {
|
||||
);
|
||||
}
|
||||
|
||||
_setProgressRef = component => {
|
||||
this._progressRef = component;
|
||||
_setProgressRef = element => {
|
||||
this._progressElement = element;
|
||||
};
|
||||
|
||||
_updateProgressWidth = () => {
|
||||
const { indeterminate, progress } = this.props;
|
||||
const percentageProgress = indeterminate ? 50 : progress * 100;
|
||||
const width = indeterminate ? '25%' : `${percentageProgress}%`;
|
||||
this._progressRef.setNativeProps({
|
||||
style: { width }
|
||||
});
|
||||
if (this._progressElement) {
|
||||
this._progressElement.setNativeProps({
|
||||
style: { width }
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,8 @@ const thumbDefaultBoxShadow = '0px 1px 3px rgba(0,0,0,0.5)';
|
||||
const thumbFocusedBoxShadow = `${thumbDefaultBoxShadow}, 0 0 0 10px rgba(0,0,0,0.1)`;
|
||||
|
||||
class Switch extends PureComponent {
|
||||
_checkbox: HTMLInputElement;
|
||||
_checkboxElement: HTMLInputElement;
|
||||
_thumbElement = null;
|
||||
|
||||
static displayName = 'Switch';
|
||||
|
||||
@@ -44,11 +45,11 @@ class Switch extends PureComponent {
|
||||
};
|
||||
|
||||
blur() {
|
||||
UIManager.blur(this._checkbox);
|
||||
UIManager.blur(this._checkboxElement);
|
||||
}
|
||||
|
||||
focus() {
|
||||
UIManager.focus(this._checkbox);
|
||||
UIManager.focus(this._checkboxElement);
|
||||
}
|
||||
|
||||
render() {
|
||||
@@ -136,15 +137,17 @@ class Switch extends PureComponent {
|
||||
_handleFocusState = (event: Object) => {
|
||||
const isFocused = event.nativeEvent.type === 'focus';
|
||||
const boxShadow = isFocused ? thumbFocusedBoxShadow : thumbDefaultBoxShadow;
|
||||
this._thumb.setNativeProps({ style: { boxShadow } });
|
||||
if (this._thumbElement) {
|
||||
this._thumbElement.setNativeProps({ style: { boxShadow } });
|
||||
}
|
||||
};
|
||||
|
||||
_setCheckboxRef = component => {
|
||||
this._checkbox = component;
|
||||
_setCheckboxRef = element => {
|
||||
this._checkboxElement = element;
|
||||
};
|
||||
|
||||
_setThumbRef = component => {
|
||||
this._thumb = component;
|
||||
_setThumbRef = element => {
|
||||
this._thumbElement = element;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -82,6 +82,7 @@ const TouchableWithoutFeedback = createReactClass({
|
||||
* reactivated! Move it back and forth several times while the scroll view
|
||||
* is disabled. Ensure you pass in a constant to reduce memory allocations.
|
||||
*/
|
||||
// $FlowFixMe
|
||||
pressRetentionOffset: EdgeInsetsPropType,
|
||||
/**
|
||||
* This defines how far your touch can start away from the button. This is
|
||||
@@ -91,6 +92,7 @@ const TouchableWithoutFeedback = createReactClass({
|
||||
* of sibling views always takes precedence if a touch hits two overlapping
|
||||
* views.
|
||||
*/
|
||||
// $FlowFixMe
|
||||
hitSlop: EdgeInsetsPropType
|
||||
},
|
||||
|
||||
|
||||
@@ -1,5 +1,20 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`modules/createDOMElement onClick 1`] = `
|
||||
Object {
|
||||
"_normalized": true,
|
||||
"changedTouches": Array [],
|
||||
"pageX": undefined,
|
||||
"pageY": undefined,
|
||||
"preventDefault": [Function],
|
||||
"stopImmediatePropagation": [Function],
|
||||
"stopPropagation": [Function],
|
||||
"target": undefined,
|
||||
"timestamp": 1496876171255,
|
||||
"touches": Array [],
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`modules/createDOMElement prop "accessibilityLabel" 1`] = `
|
||||
<span
|
||||
aria-label="accessibilityLabel"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* eslint-env jasmine, jest */
|
||||
|
||||
import createDOMElement from '..';
|
||||
import { render } from 'enzyme';
|
||||
import { shallow, render } from 'enzyme';
|
||||
|
||||
describe('modules/createDOMElement', () => {
|
||||
test('renders correct DOM element', () => {
|
||||
@@ -103,6 +103,22 @@ describe('modules/createDOMElement', () => {
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('onClick', done => {
|
||||
const onClick = e => {
|
||||
e.nativeEvent.timestamp = 1496876171255;
|
||||
expect(e.nativeEvent).toMatchSnapshot();
|
||||
done();
|
||||
};
|
||||
const component = shallow(createDOMElement('span', { onClick }));
|
||||
component.find('span').simulate('click', {
|
||||
nativeEvent: {
|
||||
preventDefault() {},
|
||||
stopImmediatePropagation() {},
|
||||
stopPropagation() {}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('prop "testID"', () => {
|
||||
const component = render(createDOMElement('span', { testID: 'Example.testID' }));
|
||||
expect(component).toMatchSnapshot();
|
||||
|
||||
@@ -44,10 +44,11 @@ const createDOMElement = (component, props) => {
|
||||
|
||||
// normalize DOM events to match React Native events
|
||||
// TODO: move this out of the render path
|
||||
Object.keys(domProps).forEach(prop => {
|
||||
const isEventHandler = typeof prop === 'function' && eventHandlerNames[prop];
|
||||
Object.keys(domProps).forEach(propName => {
|
||||
const prop = domProps[propName];
|
||||
const isEventHandler = typeof prop === 'function' && eventHandlerNames[propName];
|
||||
if (isEventHandler) {
|
||||
domProps[prop] = wrapEventHandler(prop);
|
||||
domProps[propName] = wrapEventHandler(prop);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,25 +1,23 @@
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule ColorPropType
|
||||
*/
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule ColorPropType
|
||||
* @flow
|
||||
*/
|
||||
|
||||
var colorPropType = function(isRequired, props, propName, componentName, location, propFullName) {
|
||||
var normalizeColor = require('normalize-css-color');
|
||||
var ReactPropTypeLocationNames = require('../vendor/ReactPropTypeLocationNames');
|
||||
var color = props[propName];
|
||||
const colorPropType = function(isRequired, props, propName, componentName, location, propFullName) {
|
||||
const normalizeColor = require('normalize-css-color');
|
||||
const color = props[propName];
|
||||
if (color === undefined || color === null) {
|
||||
if (isRequired) {
|
||||
var locationName = ReactPropTypeLocationNames[location];
|
||||
return new Error(
|
||||
'Required ' +
|
||||
locationName +
|
||||
location +
|
||||
' `' +
|
||||
(propFullName || propName) +
|
||||
'` was not specified in `' +
|
||||
@@ -42,10 +40,9 @@ var colorPropType = function(isRequired, props, propName, componentName, locatio
|
||||
}
|
||||
|
||||
if (normalizeColor(color) === null) {
|
||||
var locationName = ReactPropTypeLocationNames[location];
|
||||
return new Error(
|
||||
'Invalid ' +
|
||||
locationName +
|
||||
location +
|
||||
' `' +
|
||||
(propFullName || propName) +
|
||||
'` supplied to `' +
|
||||
@@ -70,11 +67,13 @@ var colorPropType = function(isRequired, props, propName, componentName, locatio
|
||||
}
|
||||
};
|
||||
|
||||
let ColorPropType;
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
var ColorPropType = colorPropType.bind(null, false /* isRequired */);
|
||||
ColorPropType = colorPropType.bind(null, false /* isRequired */);
|
||||
ColorPropType.isRequired = colorPropType.bind(null, true /* isRequired */);
|
||||
} else {
|
||||
var ColorPropType = function() {};
|
||||
ColorPropType = function() {};
|
||||
}
|
||||
|
||||
module.exports = ColorPropType;
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
/* eslint-disable */
|
||||
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
@@ -8,29 +6,28 @@
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule createStrictShapeTypeChecker
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import invariant from 'fbjs/lib/invariant';
|
||||
import ReactPropTypeLocationNames from '../vendor/ReactPropTypeLocationNames';
|
||||
import ReactPropTypesSecret from '../vendor/ReactPropTypesSecret';
|
||||
|
||||
function createStrictShapeTypeChecker(shapeTypes: {
|
||||
[key: string]: ReactPropsCheckType
|
||||
}): ReactPropsChainableTypeChecker {
|
||||
function checkType(isRequired, props, propName, componentName, location?) {
|
||||
function checkType(isRequired, props, propName, componentName, location?, ...rest) {
|
||||
if (!props[propName]) {
|
||||
if (isRequired) {
|
||||
invariant(
|
||||
false,
|
||||
`Required object \`${propName}\` was not specified in ` + `\`${componentName}\`.`
|
||||
`Required object \`${propName}\` was not specified in \`${componentName}\`.`
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
var propValue = props[propName];
|
||||
var propType = typeof propValue;
|
||||
var locationName = (location && ReactPropTypeLocationNames[location]) || '(unknown)';
|
||||
const propValue = props[propName];
|
||||
const propType = typeof propValue;
|
||||
const locationName = location || '(unknown)';
|
||||
if (propType !== 'object') {
|
||||
invariant(
|
||||
false,
|
||||
@@ -40,24 +37,24 @@ function createStrictShapeTypeChecker(shapeTypes: {
|
||||
}
|
||||
// We need to check all keys in case some are required but missing from
|
||||
// props.
|
||||
var allKeys = { ...props[propName], ...shapeTypes };
|
||||
for (var key in allKeys) {
|
||||
var checker = shapeTypes[key];
|
||||
const allKeys = { ...props[propName], ...shapeTypes };
|
||||
for (const key in allKeys) {
|
||||
const checker = shapeTypes[key];
|
||||
if (!checker) {
|
||||
invariant(
|
||||
false,
|
||||
`Invalid props.${propName} key \`${key}\` supplied to \`${componentName}\`.` +
|
||||
`\nBad object: ` +
|
||||
'\nBad object: ' +
|
||||
JSON.stringify(props[propName], null, ' ') +
|
||||
`\nValid keys: ` +
|
||||
'\nValid keys: ' +
|
||||
JSON.stringify(Object.keys(shapeTypes), null, ' ')
|
||||
);
|
||||
}
|
||||
var error = checker(propValue, key, componentName, location, null, ReactPropTypesSecret);
|
||||
const error = checker(propValue, key, componentName, location, ...rest);
|
||||
if (error) {
|
||||
invariant(
|
||||
false,
|
||||
error.message + `\nBad object: ` + JSON.stringify(props[propName], null, ' ')
|
||||
error.message + '\nBad object: ' + JSON.stringify(props[propName], null, ' ')
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -66,9 +63,10 @@ function createStrictShapeTypeChecker(shapeTypes: {
|
||||
props: { [key: string]: any },
|
||||
propName: string,
|
||||
componentName: string,
|
||||
location?: string
|
||||
location?: string,
|
||||
...rest
|
||||
): ?Error {
|
||||
return checkType(false, props, propName, componentName, location);
|
||||
return checkType(false, props, propName, componentName, location, ...rest);
|
||||
}
|
||||
chainedCheckType.isRequired = checkType.bind(null, true);
|
||||
return chainedCheckType;
|
||||
|
||||
20
src/vendor/ReactPropTypeLocationNames/index.js
vendored
20
src/vendor/ReactPropTypeLocationNames/index.js
vendored
@@ -1,20 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
let ReactPropTypeLocationNames = {};
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
ReactPropTypeLocationNames = {
|
||||
prop: 'prop',
|
||||
context: 'context',
|
||||
childContext: 'child context'
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = ReactPropTypeLocationNames;
|
||||
10
src/vendor/ReactPropTypesSecret/index.js
vendored
10
src/vendor/ReactPropTypesSecret/index.js
vendored
@@ -1,10 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
const ReactPropTypesSecret = 'SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED';
|
||||
module.exports = ReactPropTypesSecret;
|
||||
@@ -2756,9 +2756,9 @@ flatten@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.2.tgz#dae46a9d78fbe25292258cc1e780a41d95c03782"
|
||||
|
||||
flow-bin@^0.46.0:
|
||||
version "0.46.0"
|
||||
resolved "https://registry.yarnpkg.com/flow-bin/-/flow-bin-0.46.0.tgz#06ad7fe19dddb1042264438064a2a32fee12b872"
|
||||
flow-bin@^0.47.0:
|
||||
version "0.47.0"
|
||||
resolved "https://registry.yarnpkg.com/flow-bin/-/flow-bin-0.47.0.tgz#a2a08ab3e0d1f1cb57d17e27b30b118b62fda367"
|
||||
|
||||
flow-parser@0.45.0:
|
||||
version "0.45.0"
|
||||
|
||||
Reference in New Issue
Block a user