diff --git a/.eslintignore b/.eslintignore
index 15261d31c..d0528c4a5 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -1,3 +1,5 @@
-**/node_modules/**/.*js
+# node_modules ignored by default
+
**/staticBundle.js
**/main.js
+Libraries/vendor/**/*
diff --git a/.eslintrc b/.eslintrc
index da893862d..84045365c 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -168,7 +168,7 @@
"no-underscore-dangle": 0, // disallow dangling underscores in identifiers
"no-wrap-func": 1, // disallow wrapping of non-IIFE statements in parens
"no-mixed-spaces-and-tabs": 1, // disallow mixed spaces and tabs for indentation
- "quotes": [1, "single"], // specify whether double or single quotes should be used
+ "quotes": [1, "single", "avoid-escape"], // specify whether double or single quotes should be used
"quote-props": 0, // require quotes around object literal property names (off by default)
"semi": 1, // require or disallow use of semicolons instead of ASI
"sort-vars": 0, // sort variables within the same declaration block (off by default)
diff --git a/IntegrationTests/AppEventsTest.js b/IntegrationTests/AppEventsTest.js
new file mode 100644
index 000000000..e46b956b4
--- /dev/null
+++ b/IntegrationTests/AppEventsTest.js
@@ -0,0 +1,62 @@
+/**
+ * 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 AppEventsTest
+ * @flow
+ */
+'use strict';
+
+var React = require('react-native');
+var {
+ NativeAppEventEmitter,
+ NativeModules,
+ StyleSheet,
+ Text,
+ View,
+} = React;
+var TestModule = NativeModules.TestModule || NativeModules.SnapshotTestManager;
+
+var deepDiffer = require('deepDiffer');
+
+var TEST_PAYLOAD = {foo: 'bar'};
+
+var AppEventsTest = React.createClass({
+ getInitialState: function() {
+ return {sent: 'none', received: 'none'};
+ },
+ componentDidMount: function() {
+ NativeAppEventEmitter.addListener('testEvent', this.receiveEvent);
+ var event = {data: TEST_PAYLOAD, ts: Date.now()};
+ TestModule.sendAppEvent('testEvent', event);
+ this.setState({sent: event});
+ },
+ receiveEvent: function(event: any) {
+ if (deepDiffer(event.data, TEST_PAYLOAD)) {
+ throw new Error('Received wrong event: ' + JSON.stringify(event));
+ }
+ var elapsed = (Date.now() - event.ts) + 'ms';
+ this.setState({received: event, elapsed}, TestModule.markTestCompleted);
+ },
+ render: function() {
+ return (
+
+
+ {JSON.stringify(this.state, null, ' ')}
+
+
+ );
+ }
+});
+
+var styles = StyleSheet.create({
+ container: {
+ margin: 40,
+ },
+});
+
+module.exports = AppEventsTest;
diff --git a/IntegrationTests/IntegrationTestsApp.js b/IntegrationTests/IntegrationTestsApp.js
index 1e61a0dbc..d769c0831 100644
--- a/IntegrationTests/IntegrationTestsApp.js
+++ b/IntegrationTests/IntegrationTestsApp.js
@@ -26,6 +26,7 @@ var TESTS = [
require('./TimersTest'),
require('./AsyncStorageTest'),
require('./LayoutEventsTest'),
+ require('./AppEventsTest'),
require('./SimpleSnapshotTest'),
];
diff --git a/IntegrationTests/IntegrationTestsTests/IntegrationTestsTests.m b/IntegrationTests/IntegrationTestsTests/IntegrationTestsTests.m
index 9bf1a4fc1..0aca49d3c 100644
--- a/IntegrationTests/IntegrationTestsTests/IntegrationTestsTests.m
+++ b/IntegrationTests/IntegrationTestsTests/IntegrationTestsTests.m
@@ -76,6 +76,11 @@
[_runner runTest:_cmd module:@"LayoutEventsTest"];
}
+- (void)testAppEvents
+{
+ [_runner runTest:_cmd module:@"AppEventsTest"];
+}
+
#pragma mark Snapshot Tests
- (void)testSimpleSnapshot
diff --git a/Libraries/ART/ARTSerializablePath.js b/Libraries/ART/ARTSerializablePath.js
index 2df8ff6bb..4e8b3c227 100644
--- a/Libraries/ART/ARTSerializablePath.js
+++ b/Libraries/ART/ARTSerializablePath.js
@@ -8,8 +8,7 @@
*
* @providesModule ARTSerializablePath
*/
-
-"use strict";
+'use strict';
// TODO: Move this into an ART mode called "serialized" or something
diff --git a/Libraries/ART/ReactNativeART.js b/Libraries/ART/ReactNativeART.js
index b2533f2a8..3b5801d00 100644
--- a/Libraries/ART/ReactNativeART.js
+++ b/Libraries/ART/ReactNativeART.js
@@ -8,8 +8,7 @@
*
* @providesModule ReactNativeART
*/
-
-"use strict";
+'use strict';
var Color = require('art/core/color');
var Path = require('ARTSerializablePath');
diff --git a/Libraries/Components/ActivityIndicatorIOS/ActivityIndicatorIOS.ios.js b/Libraries/Components/ActivityIndicatorIOS/ActivityIndicatorIOS.ios.js
index d6afa3e06..c229b60c3 100644
--- a/Libraries/Components/ActivityIndicatorIOS/ActivityIndicatorIOS.ios.js
+++ b/Libraries/Components/ActivityIndicatorIOS/ActivityIndicatorIOS.ios.js
@@ -12,7 +12,6 @@
'use strict';
var NativeMethodsMixin = require('NativeMethodsMixin');
-var NativeModules = require('NativeModules');
var PropTypes = require('ReactPropTypes');
var React = require('React');
var StyleSheet = require('StyleSheet');
diff --git a/Libraries/Components/ScrollResponder.js b/Libraries/Components/ScrollResponder.js
index 0818a0bf9..94e0850f2 100644
--- a/Libraries/Components/ScrollResponder.js
+++ b/Libraries/Components/ScrollResponder.js
@@ -183,7 +183,7 @@ var ScrollResponderMixin = {
var currentlyFocusedTextInput = TextInputState.currentlyFocusedField();
if (!this.props.keyboardShouldPersistTaps &&
currentlyFocusedTextInput != null &&
- e.target != currentlyFocusedTextInput) {
+ e.target !== currentlyFocusedTextInput) {
return true;
}
return this.scrollResponderIsAnimating();
@@ -244,7 +244,7 @@ var ScrollResponderMixin = {
var currentlyFocusedTextInput = TextInputState.currentlyFocusedField();
if (!this.props.keyboardShouldPersistTaps &&
currentlyFocusedTextInput != null &&
- e.target != currentlyFocusedTextInput &&
+ e.target !== currentlyFocusedTextInput &&
!this.state.observedScrollSinceBecomingResponder &&
!this.state.becameResponderWhileAnimating) {
this.props.onScrollResponderKeyboardDismissed &&
diff --git a/Libraries/Components/Subscribable.js b/Libraries/Components/Subscribable.js
index cf9bc773a..8474b2149 100644
--- a/Libraries/Components/Subscribable.js
+++ b/Libraries/Components/Subscribable.js
@@ -11,7 +11,7 @@
*/
'use strict';
-var EventEmitter = require('EventEmitter');
+import type EventEmitter from 'EventEmitter';
/**
* Subscribable provides a mixin for safely subscribing a component to an
diff --git a/Libraries/Components/TextInput/TextInput.js b/Libraries/Components/TextInput/TextInput.js
index 5b3ffe1c1..dc6d05eb9 100644
--- a/Libraries/Components/TextInput/TextInput.js
+++ b/Libraries/Components/TextInput/TextInput.js
@@ -179,12 +179,12 @@ var TextInput = React.createClass({
'number-pad',
'phone-pad',
'name-phone-pad',
- 'email-address',
'decimal-pad',
'twitter',
'web-search',
// Cross-platform
'numeric',
+ 'email-address',
]),
/**
* Determines how the return key should look.
diff --git a/Libraries/Components/View/View.js b/Libraries/Components/View/View.js
index c3659e961..c6a279a22 100644
--- a/Libraries/Components/View/View.js
+++ b/Libraries/Components/View/View.js
@@ -24,6 +24,26 @@ var createReactNativeComponentClass = require('createReactNativeComponentClass')
var stylePropType = StyleSheetPropType(ViewStylePropTypes);
+var AccessibilityTraits = [
+ 'none',
+ 'button',
+ 'link',
+ 'header',
+ 'search',
+ 'image',
+ 'selected',
+ 'plays',
+ 'key',
+ 'text',
+ 'summary',
+ 'disabled',
+ 'frequentUpdates',
+ 'startsMedia',
+ 'adjustable',
+ 'allowsDirectInteraction',
+ 'pageTurn',
+];
+
/**
* The most fundamental component for building UI, `View` is a
* container that supports layout with flexbox, style, some touch handling, and
@@ -70,6 +90,27 @@ var View = React.createClass({
*/
accessibilityLabel: PropTypes.string,
+ /**
+ * Provides additional traits to screen reader. By default no traits are
+ * provided unless specified otherwise in element
+ */
+ accessibilityTraits: PropTypes.oneOfType([
+ PropTypes.oneOf(AccessibilityTraits),
+ PropTypes.arrayOf(PropTypes.oneOf(AccessibilityTraits)),
+ ]),
+
+ /**
+ * When `accessible` is true, the system will try to invoke this function
+ * when the user performs accessibility tap gesture.
+ */
+ onAcccessibilityTap: PropTypes.func,
+
+ /**
+ * When `accessible` is true, the system will invoke this function when the
+ * user performs the magic tap gesture.
+ */
+ onMagicTap: PropTypes.func,
+
/**
* Used to locate this view in end-to-end tests.
*/
diff --git a/Libraries/Components/WebView/WebView.android.js b/Libraries/Components/WebView/WebView.android.js
index a9ee724fa..18491d52c 100644
--- a/Libraries/Components/WebView/WebView.android.js
+++ b/Libraries/Components/WebView/WebView.android.js
@@ -75,7 +75,7 @@ var WebView = React.createClass({
errorEvent.code,
errorEvent.description);
} else if (this.state.viewState !== WebViewState.IDLE) {
- console.error("RCTWebView invalid state encountered: " + this.state.loading);
+ console.error('RCTWebView invalid state encountered: ' + this.state.loading);
}
var webViewStyles = [styles.container, this.props.style];
@@ -152,7 +152,7 @@ var WebView = React.createClass({
onLoadingError: function(event) {
event.persist(); // persist this event because we need to store it
- console.error("encountered an error loading page", event.nativeEvent);
+ console.error('Encountered an error loading page', event.nativeEvent);
this.setState({
lastErrorEvent: event.nativeEvent,
diff --git a/Libraries/Components/WebView/WebView.ios.js b/Libraries/Components/WebView/WebView.ios.js
index de9ff2ef1..83af4a8ad 100644
--- a/Libraries/Components/WebView/WebView.ios.js
+++ b/Libraries/Components/WebView/WebView.ios.js
@@ -14,7 +14,6 @@
var ActivityIndicatorIOS = require('ActivityIndicatorIOS');
var EdgeInsetsPropType = require('EdgeInsetsPropType');
var React = require('React');
-var ReactNativeViewAttributes = require('ReactNativeViewAttributes');
var StyleSheet = require('StyleSheet');
var Text = require('Text');
var View = require('View');
@@ -198,7 +197,7 @@ var WebView = React.createClass({
onLoadingError: function(event: Event) {
event.persist(); // persist this event because we need to store it
- console.error("encountered an error loading page", event.nativeEvent);
+ console.error('Encountered an error loading page', event.nativeEvent);
this.setState({
lastErrorEvent: event.nativeEvent,
diff --git a/Libraries/CustomComponents/Navigator/Navigator.js b/Libraries/CustomComponents/Navigator/Navigator.js
index b8c019e32..569351745 100644
--- a/Libraries/CustomComponents/Navigator/Navigator.js
+++ b/Libraries/CustomComponents/Navigator/Navigator.js
@@ -24,6 +24,7 @@
*
* @providesModule Navigator
*/
+ /* eslint-disable no-extra-boolean-cast*/
'use strict';
var AnimationsDebugModule = require('NativeModules').AnimationsDebugModule;
@@ -48,8 +49,6 @@ var clamp = require('clamp');
var flattenStyle = require('flattenStyle');
var getNavigatorContext = require('getNavigatorContext');
var invariant = require('invariant');
-var keyMirror = require('keyMirror');
-var merge = require('merge');
var rebound = require('rebound');
var PropTypes = React.PropTypes;
@@ -689,7 +688,7 @@ var Navigator = React.createClass({
*/
_enableScene: function(sceneIndex) {
// First, determine what the defined styles are for scenes in this navigator
- var sceneStyle = flattenStyle(this.props.sceneStyle);
+ var sceneStyle = flattenStyle([styles.baseScene, this.props.sceneStyle]);
// Then restore the left value for this scene
var enabledSceneNativeProps = {
left: sceneStyle.left,
@@ -745,7 +744,6 @@ var Navigator = React.createClass({
},
_handleMoveShouldSetPanResponder: function(e, gestureState) {
- var currentRoute = this.state.routeStack[this.state.presentedIndex];
var sceneConfig = this.state.sceneConfigStack[this.state.presentedIndex];
this._expectingGestureGrant = this._matchGestureAction(this._eligibleGestures, sceneConfig.gestures, gestureState);
return !! this._expectingGestureGrant;
@@ -829,7 +827,16 @@ var Navigator = React.createClass({
}
} else {
// The gesture has enough velocity to complete, so we transition to the gesture's destination
- this._transitionTo(destIndex, transitionVelocity);
+ this._transitionTo(
+ destIndex,
+ transitionVelocity,
+ null,
+ () => {
+ if (releaseGestureAction === 'pop') {
+ this._cleanScenesPastIndex(destIndex);
+ }
+ }
+ );
}
this._detachGesture();
},
diff --git a/Libraries/CustomComponents/Navigator/NavigatorSceneConfigs.js b/Libraries/CustomComponents/Navigator/NavigatorSceneConfigs.js
index ed295346d..48be13ea1 100644
--- a/Libraries/CustomComponents/Navigator/NavigatorSceneConfigs.js
+++ b/Libraries/CustomComponents/Navigator/NavigatorSceneConfigs.js
@@ -213,7 +213,7 @@ var FromTheLeft = {
translateX: {
from: -SCREEN_WIDTH,
to: 0,
- min: 0,
+ min: 0,
max: 1,
type: 'linear',
extrapolate: true,
diff --git a/Libraries/Fetch/fetch.js b/Libraries/Fetch/fetch.js
index 829f7c425..b5e8b151c 100644
--- a/Libraries/Fetch/fetch.js
+++ b/Libraries/Fetch/fetch.js
@@ -12,6 +12,7 @@
* @providesModule fetch
* @nolint
*/
+/* eslint-disable */
'use strict';
var self = {};
diff --git a/Libraries/Geolocation/Geolocation.js b/Libraries/Geolocation/Geolocation.js
index fae309aef..80dbfa19b 100644
--- a/Libraries/Geolocation/Geolocation.js
+++ b/Libraries/Geolocation/Geolocation.js
@@ -120,6 +120,6 @@ var Geolocation = {
subscriptions = [];
}
}
-}
+};
module.exports = Geolocation;
diff --git a/Libraries/Image/Image.ios.js b/Libraries/Image/Image.ios.js
index 45c1ad828..9b84937af 100644
--- a/Libraries/Image/Image.ios.js
+++ b/Libraries/Image/Image.ios.js
@@ -60,7 +60,7 @@ var Image = React.createClass({
/**
* `uri` is a string representing the resource identifier for the image, which
* could be an http address, a local file path, or the name of a static image
- * resource (which should be wrapped in the `required('image!name')` function).
+ * resource (which should be wrapped in the `require('image!name')` function).
*/
source: PropTypes.shape({
uri: PropTypes.string,
diff --git a/Libraries/JavaScriptAppEngine/Initialization/SourceMap.js b/Libraries/JavaScriptAppEngine/Initialization/SourceMap.js
index 79a5e818c..311e573fc 100644
--- a/Libraries/JavaScriptAppEngine/Initialization/SourceMap.js
+++ b/Libraries/JavaScriptAppEngine/Initialization/SourceMap.js
@@ -18,6 +18,7 @@
* and wrapping resulting file into `wrapper` function.
*
*/
+/*eslint-disable */
var scope = {};
wrapper.call(scope);
diff --git a/Libraries/JavaScriptAppEngine/Initialization/source-map-url.js b/Libraries/JavaScriptAppEngine/Initialization/source-map-url.js
index 8602288ee..98610724a 100644
--- a/Libraries/JavaScriptAppEngine/Initialization/source-map-url.js
+++ b/Libraries/JavaScriptAppEngine/Initialization/source-map-url.js
@@ -11,6 +11,7 @@
*
* @nolint
*/
+/* eslint-disable */
(function() {
var define = null; // Hack to make it work with our packager
diff --git a/Libraries/PushNotificationIOS/PushNotificationIOS.js b/Libraries/PushNotificationIOS/PushNotificationIOS.js
index 9432fbef3..4d03c6641 100644
--- a/Libraries/PushNotificationIOS/PushNotificationIOS.js
+++ b/Libraries/PushNotificationIOS/PushNotificationIOS.js
@@ -11,7 +11,6 @@
*/
'use strict';
-var NativeModules = require('NativeModules');
var RCTDeviceEventEmitter = require('RCTDeviceEventEmitter');
var RCTPushNotificationManager = require('NativeModules').PushNotificationManager;
var invariant = require('invariant');
diff --git a/Libraries/RCTTest/RCTTestModule.m b/Libraries/RCTTest/RCTTestModule.m
index 33f562515..2508b88fe 100644
--- a/Libraries/RCTTest/RCTTestModule.m
+++ b/Libraries/RCTTest/RCTTestModule.m
@@ -11,6 +11,7 @@
#import "FBSnapshotTestController.h"
#import "RCTAssert.h"
+#import "RCTEventDispatcher.h"
#import "RCTLog.h"
#import "RCTUIManager.h"
@@ -63,4 +64,9 @@ RCT_EXPORT_METHOD(markTestCompleted)
}];
}
+RCT_EXPORT_METHOD(sendAppEvent:(NSString *)name body:(id)body)
+{
+ [_bridge.eventDispatcher sendAppEventWithName:name body:body];
+}
+
@end
diff --git a/Libraries/RKBackendNode/queryLayoutByID.js b/Libraries/RKBackendNode/queryLayoutByID.js
index d9b1ed8cc..d6492e6da 100644
--- a/Libraries/RKBackendNode/queryLayoutByID.js
+++ b/Libraries/RKBackendNode/queryLayoutByID.js
@@ -58,4 +58,3 @@ var queryLayoutByID = function(
};
module.exports = queryLayoutByID;
-
diff --git a/Libraries/ReactIOS/IOSNativeBridgeEventPlugin.js b/Libraries/ReactIOS/IOSNativeBridgeEventPlugin.js
index b585db5a7..55b22a959 100644
--- a/Libraries/ReactIOS/IOSNativeBridgeEventPlugin.js
+++ b/Libraries/ReactIOS/IOSNativeBridgeEventPlugin.js
@@ -9,8 +9,7 @@
* @providesModule IOSNativeBridgeEventPlugin
* @flow
*/
-
-"use strict";
+'use strict';
var EventPropagators = require('EventPropagators');
var NativeModules = require('NativeModules');
@@ -33,7 +32,7 @@ for (var bubblingTypeName in customBubblingEventTypes) {
for (var directTypeName in customDirectEventTypes) {
warning(
!customBubblingEventTypes[directTypeName],
- "Event cannot be both direct and bubbling: %s",
+ 'Event cannot be both direct and bubbling: %s',
directTypeName
);
allTypesByEventName[directTypeName] = customDirectEventTypes[directTypeName];
@@ -76,4 +75,3 @@ var IOSNativeBridgeEventPlugin = {
};
module.exports = IOSNativeBridgeEventPlugin;
-
diff --git a/Libraries/ReactIOS/verifyPropTypes.js b/Libraries/ReactIOS/verifyPropTypes.js
index 6ee23cda8..5fd3ea1b8 100644
--- a/Libraries/ReactIOS/verifyPropTypes.js
+++ b/Libraries/ReactIOS/verifyPropTypes.js
@@ -22,6 +22,13 @@ function verifyPropTypes(
if (!viewConfig) {
return; // This happens for UnimplementedView.
}
+ var componentName = component.name || component.displayName;
+ if (!component.propTypes) {
+ throw new Error(
+ '`' + componentName + '` has no propTypes defined`'
+ );
+ }
+
var nativeProps = viewConfig.nativeProps;
for (var prop in nativeProps) {
if (!component.propTypes[prop] &&
@@ -29,9 +36,9 @@ function verifyPropTypes(
!ReactNativeStyleAttributes[prop] &&
(!nativePropsToIgnore || !nativePropsToIgnore[prop])) {
throw new Error(
- '`' + component.displayName + '` has no propType for native prop `' +
+ '`' + componentName + '` has no propType for native prop `' +
viewConfig.uiViewClassName + '.' + prop + '` of native type `' +
- nativeProps[prop].type + '`'
+ nativeProps[prop] + '`'
);
}
}
diff --git a/Libraries/ReactNative/React.js b/Libraries/ReactNative/React.js
index 1eeeb32c3..9cd4464f2 100644
--- a/Libraries/ReactNative/React.js
+++ b/Libraries/ReactNative/React.js
@@ -9,7 +9,6 @@
* @providesModule React
* @flow
*/
-
-"use strict";
+'use strict';
module.exports = require('ReactNative');
diff --git a/Libraries/ReactNative/ReactNative.js b/Libraries/ReactNative/ReactNative.js
index abe31a361..970944baa 100644
--- a/Libraries/ReactNative/ReactNative.js
+++ b/Libraries/ReactNative/ReactNative.js
@@ -9,7 +9,7 @@
* @providesModule ReactNative
* @flow
*/
-"use strict";
+'use strict';
var ReactChildren = require('ReactChildren');
var ReactClass = require('ReactClass');
diff --git a/Libraries/ReactNative/ReactNativeBaseComponent.js b/Libraries/ReactNative/ReactNativeBaseComponent.js
index dcc31a2b3..95af29023 100644
--- a/Libraries/ReactNative/ReactNativeBaseComponent.js
+++ b/Libraries/ReactNative/ReactNativeBaseComponent.js
@@ -63,7 +63,8 @@ var cachedIndexArray = function(size) {
for (var i = 0; i < size; i++) {
arr[i] = i;
}
- return cachedIndexArray._cache[size] = arr;
+ cachedIndexArray._cache[size] = arr;
+ return arr;
} else {
return cachedResult;
}
@@ -228,7 +229,7 @@ ReactNativeBaseComponent.Mixin = {
*/
_reconcileListenersUponUpdate: function(prevProps, nextProps) {
for (var key in nextProps) {
- if (registrationNames[key] && (nextProps[key] != prevProps[key])) {
+ if (registrationNames[key] && (nextProps[key] !== prevProps[key])) {
putListener(this._rootNodeID, key, nextProps[key]);
}
}
diff --git a/Libraries/ReactNative/ReactNativeDOMIDOperations.js b/Libraries/ReactNative/ReactNativeDOMIDOperations.js
index 3b47d4d8f..af17f9e07 100644
--- a/Libraries/ReactNative/ReactNativeDOMIDOperations.js
+++ b/Libraries/ReactNative/ReactNativeDOMIDOperations.js
@@ -9,8 +9,7 @@
* @providesModule ReactNativeDOMIDOperations
* @flow
*/
-
-"use strict";
+'use strict';
var ReactNativeTagHandles = require('ReactNativeTagHandles');
var ReactMultiChildUpdateTypes = require('ReactMultiChildUpdateTypes');
diff --git a/Libraries/ReactNative/ReactNativeDefaultInjection.js b/Libraries/ReactNative/ReactNativeDefaultInjection.js
index 3b15a8cc9..ce040c779 100644
--- a/Libraries/ReactNative/ReactNativeDefaultInjection.js
+++ b/Libraries/ReactNative/ReactNativeDefaultInjection.js
@@ -9,8 +9,7 @@
* @providesModule ReactNativeDefaultInjection
* @flow
*/
-
-"use strict";
+'use strict';
/**
* Make sure `setTimeout`/`setInterval` are patched correctly.
@@ -21,7 +20,6 @@ var EventPluginUtils = require('EventPluginUtils');
var IOSDefaultEventPluginOrder = require('IOSDefaultEventPluginOrder');
var IOSNativeBridgeEventPlugin = require('IOSNativeBridgeEventPlugin');
var NodeHandle = require('NodeHandle');
-var ReactClass = require('ReactClass');
var ReactComponentEnvironment = require('ReactComponentEnvironment');
var ReactDefaultBatchingStrategy = require('ReactDefaultBatchingStrategy');
var ReactEmptyComponent = require('ReactEmptyComponent');
diff --git a/Libraries/ReactNative/ReactNativeEventEmitter.js b/Libraries/ReactNative/ReactNativeEventEmitter.js
index 9bd344e79..b8f773100 100644
--- a/Libraries/ReactNative/ReactNativeEventEmitter.js
+++ b/Libraries/ReactNative/ReactNativeEventEmitter.js
@@ -9,8 +9,7 @@
* @providesModule ReactNativeEventEmitter
* @flow
*/
-
-"use strict";
+'use strict';
var EventPluginHub = require('EventPluginHub');
var ReactEventEmitterMixin = require('ReactEventEmitterMixin');
diff --git a/Libraries/ReactNative/ReactNativeMount.js b/Libraries/ReactNative/ReactNativeMount.js
index 004a3fbda..82efa57a6 100644
--- a/Libraries/ReactNative/ReactNativeMount.js
+++ b/Libraries/ReactNative/ReactNativeMount.js
@@ -21,7 +21,6 @@ var ReactUpdates = require('ReactUpdates');
var emptyObject = require('emptyObject');
var instantiateReactComponent = require('instantiateReactComponent');
-var invariant = require('invariant');
var shouldUpdateReactComponent = require('shouldUpdateReactComponent');
function instanceNumberToChildRootID(rootNodeID, instanceNumber) {
diff --git a/Libraries/ReactNative/ReactNativeReconcileTransaction.js b/Libraries/ReactNative/ReactNativeReconcileTransaction.js
index ac9ed657b..309630e3c 100644
--- a/Libraries/ReactNative/ReactNativeReconcileTransaction.js
+++ b/Libraries/ReactNative/ReactNativeReconcileTransaction.js
@@ -9,8 +9,7 @@
* @providesModule ReactNativeReconcileTransaction
* @flow
*/
-
-"use strict";
+'use strict';
var CallbackQueue = require('CallbackQueue');
var PooledClass = require('PooledClass');
diff --git a/Libraries/ReactNative/ReactNativeViewAttributes.js b/Libraries/ReactNative/ReactNativeViewAttributes.js
index d154d045f..50b839e1d 100644
--- a/Libraries/ReactNative/ReactNativeViewAttributes.js
+++ b/Libraries/ReactNative/ReactNativeViewAttributes.js
@@ -19,8 +19,11 @@ ReactNativeViewAttributes.UIView = {
pointerEvents: true,
accessible: true,
accessibilityLabel: true,
+ accessibilityTraits: true,
testID: true,
onLayout: true,
+ onAccessibilityTap: true,
+ onMagicTap: true,
};
ReactNativeViewAttributes.RCTView = merge(
diff --git a/Libraries/ReactNative/createReactNativeComponentClass.js b/Libraries/ReactNative/createReactNativeComponentClass.js
index 3a63089b6..c821cfa75 100644
--- a/Libraries/ReactNative/createReactNativeComponentClass.js
+++ b/Libraries/ReactNative/createReactNativeComponentClass.js
@@ -10,9 +10,8 @@
* @flow
*/
-"use strict";
+'use strict';
-var ReactElement = require('ReactElement');
var ReactNativeBaseComponent = require('ReactNativeBaseComponent');
// See also ReactNativeBaseComponent
diff --git a/Libraries/Settings/Settings.ios.js b/Libraries/Settings/Settings.ios.js
index c1099df93..89388be57 100644
--- a/Libraries/Settings/Settings.ios.js
+++ b/Libraries/Settings/Settings.ios.js
@@ -31,7 +31,7 @@ var Settings = {
},
watchKeys(keys: string | Array, callback: Function): number {
- if (typeof keys == 'string') {
+ if (typeof keys === 'string') {
keys = [keys];
}
@@ -41,7 +41,7 @@ var Settings = {
);
var sid = subscriptions.length;
- subscriptions.push({keys: keys, callback: callback})
+ subscriptions.push({keys: keys, callback: callback});
return sid;
},
@@ -52,15 +52,14 @@ var Settings = {
},
_sendObservations(body: Object) {
- var _this = this;
Object.keys(body).forEach((key) => {
var newValue = body[key];
- var didChange = _this._settings[key] !== newValue;
- _this._settings[key] = newValue;
+ var didChange = this._settings[key] !== newValue;
+ this._settings[key] = newValue;
if (didChange) {
subscriptions.forEach((sub) => {
- if (~sub.keys.indexOf(key) && sub.callback) {
+ if (sub.keys.indexOf(key) !== -1 && sub.callback) {
sub.callback();
}
});
diff --git a/Libraries/StyleSheet/EdgeInsetsPropType.js b/Libraries/StyleSheet/EdgeInsetsPropType.js
index 3089fe7bd..1850ae611 100644
--- a/Libraries/StyleSheet/EdgeInsetsPropType.js
+++ b/Libraries/StyleSheet/EdgeInsetsPropType.js
@@ -9,12 +9,11 @@
* @providesModule EdgeInsetsPropType
* @flow
*/
-'use strict'
+'use strict';
var PropTypes = require('ReactPropTypes');
var createStrictShapeTypeChecker = require('createStrictShapeTypeChecker');
-var insetsDiffer = require('insetsDiffer');
var EdgeInsetsPropType = createStrictShapeTypeChecker({
top: PropTypes.number,
diff --git a/Libraries/StyleSheet/PointPropType.js b/Libraries/StyleSheet/PointPropType.js
index 1e8fe95e9..606482dd5 100644
--- a/Libraries/StyleSheet/PointPropType.js
+++ b/Libraries/StyleSheet/PointPropType.js
@@ -9,12 +9,11 @@
* @providesModule PointPropType
* @flow
*/
-'use strict'
+'use strict';
var PropTypes = require('ReactPropTypes');
var createStrictShapeTypeChecker = require('createStrictShapeTypeChecker');
-var pointsDiffer = require('pointsDiffer');
var PointPropType = createStrictShapeTypeChecker({
x: PropTypes.number,
diff --git a/Libraries/Utilities/ErrorUtils.js b/Libraries/Utilities/ErrorUtils.js
index 666d536be..b66b08546 100644
--- a/Libraries/Utilities/ErrorUtils.js
+++ b/Libraries/Utilities/ErrorUtils.js
@@ -8,6 +8,7 @@
*
* @providesModule ErrorUtils
*/
+/* eslint-disable consistent-this, global-strict */
var GLOBAL = this;
diff --git a/Libraries/Utilities/MatrixMath.js b/Libraries/Utilities/MatrixMath.js
index a7f082604..0550eb187 100755
--- a/Libraries/Utilities/MatrixMath.js
+++ b/Libraries/Utilities/MatrixMath.js
@@ -3,6 +3,7 @@
*
* @providesModule MatrixMath
*/
+/* eslint-disable space-infix-ops */
'use strict';
var invariant = require('invariant');
diff --git a/Libraries/Utilities/RCTLog.js b/Libraries/Utilities/RCTLog.js
index e5a8db49c..b76a91a58 100644
--- a/Libraries/Utilities/RCTLog.js
+++ b/Libraries/Utilities/RCTLog.js
@@ -9,7 +9,6 @@
* @providesModule RCTLog
* @flow
*/
- /* globals nativeLoggingHook */
'use strict';
var invariant = require('invariant');
diff --git a/Libraries/Utilities/RCTRenderingPerf.js b/Libraries/Utilities/RCTRenderingPerf.js
index c6466aa64..126c8a032 100644
--- a/Libraries/Utilities/RCTRenderingPerf.js
+++ b/Libraries/Utilities/RCTRenderingPerf.js
@@ -12,7 +12,6 @@
'use strict';
var ReactDefaultPerf = require('ReactDefaultPerf');
-var ReactPerf = require('ReactPerf');
var invariant = require('invariant');
diff --git a/Libraries/Utilities/__mocks__/ErrorUtils.js b/Libraries/Utilities/__mocks__/ErrorUtils.js
index 99db79177..59fbbafbc 100644
--- a/Libraries/Utilities/__mocks__/ErrorUtils.js
+++ b/Libraries/Utilities/__mocks__/ErrorUtils.js
@@ -6,7 +6,7 @@
function execute(fun, context, args) {
return fun.apply(context, args);
-};
+}
function reportError(error) {
throw error;
diff --git a/Libraries/Utilities/buildStyleInterpolator.js b/Libraries/Utilities/buildStyleInterpolator.js
index 67f07cb41..5e6515cc0 100644
--- a/Libraries/Utilities/buildStyleInterpolator.js
+++ b/Libraries/Utilities/buildStyleInterpolator.js
@@ -7,6 +7,7 @@
/**
* Cannot "use strict" because we must use eval in this file.
*/
+/* eslint-disable global-strict */
var keyOf = require('keyOf');
@@ -372,7 +373,7 @@ var MatrixOpsInitial = {
var setNextValAndDetectChange = function(name, tmpVarName) {
return (
' if (!didChange) {\n' +
- ' var prevVal = result.' + name +';\n' +
+ ' var prevVal = result.' + name + ';\n' +
' result.' + name + ' = ' + tmpVarName + ';\n' +
' didChange = didChange || (' + tmpVarName + ' !== prevVal);\n' +
' } else {\n' +
diff --git a/Libraries/Utilities/truncate.js b/Libraries/Utilities/truncate.js
index 1d318e835..a15da571b 100644
--- a/Libraries/Utilities/truncate.js
+++ b/Libraries/Utilities/truncate.js
@@ -45,4 +45,3 @@ var truncate = function(
};
module.exports = truncate;
-
diff --git a/React/Base/RCTConvert.h b/React/Base/RCTConvert.h
index dd99ac9fb..0f73c2829 100644
--- a/React/Base/RCTConvert.h
+++ b/React/Base/RCTConvert.h
@@ -141,6 +141,7 @@ RCT_EXTERN BOOL RCTCopyProperty(id target, id source, NSString *keyPath);
* Underlying implementations of RCT_XXX_CONVERTER macros. Ignore these.
*/
RCT_EXTERN NSNumber *RCTConvertEnumValue(const char *, NSDictionary *, NSNumber *, id);
+RCT_EXTERN NSNumber *RCTConvertMultiEnumValue(const char *, NSDictionary *, NSNumber *, id);
RCT_EXTERN NSArray *RCTConvertArrayValue(SEL, id);
RCT_EXTERN void RCTLogConvertError(id, const char *);
@@ -194,6 +195,21 @@ RCT_CUSTOM_CONVERTER(type, type, [[self NSNumber:json] getter])
return [RCTConvertEnumValue(#type, mapping, @(default), json) getter]; \
}
+/**
+ * This macro is used for creating converters for enum types for
+ * multiple enum values combined with | operator
+ */
+#define RCT_MULTI_ENUM_CONVERTER(type, values, default, getter) \
++ (type)type:(id)json \
+{ \
+ static NSDictionary *mapping; \
+ static dispatch_once_t onceToken; \
+ dispatch_once(&onceToken, ^{ \
+ mapping = values; \
+ }); \
+ return [RCTConvertMultiEnumValue(#type, mapping, @(default), json) getter]; \
+}
+
/**
* This macro is used for creating converter functions for typed arrays.
*/
diff --git a/React/Base/RCTConvert.m b/React/Base/RCTConvert.m
index 06b5aa023..0047f8461 100644
--- a/React/Base/RCTConvert.m
+++ b/React/Base/RCTConvert.m
@@ -175,6 +175,22 @@ NSNumber *RCTConvertEnumValue(const char *typeName, NSDictionary *mapping, NSNum
return value ?: defaultValue;
}
+NSNumber *RCTConvertMultiEnumValue(const char *typeName, NSDictionary *mapping, NSNumber *defaultValue, id json)
+{
+ if ([json isKindOfClass:[NSArray class]]) {
+ if ([json count] == 0) {
+ return defaultValue;
+ }
+ long long result = 0;
+ for (id arrayElement in json) {
+ NSNumber *value = RCTConvertEnumValue(typeName, mapping, defaultValue, arrayElement);
+ result |= [value longLongValue];
+ }
+ return @(result);
+ }
+ return RCTConvertEnumValue(typeName, mapping, defaultValue, json);
+}
+
RCT_ENUM_CONVERTER(NSTextAlignment, (@{
@"auto": @(NSTextAlignmentNatural),
@"left": @(NSTextAlignmentLeft),
diff --git a/React/Modules/RCTUIManager.m b/React/Modules/RCTUIManager.m
index 7d6925b12..e9734c204 100644
--- a/React/Modules/RCTUIManager.m
+++ b/React/Modules/RCTUIManager.m
@@ -1309,6 +1309,12 @@ RCT_EXPORT_METHOD(clearJSResponder)
@"topLoadingError": @{
@"registrationName": @"onLoadingError"
},
+ @"topAccessibilityTap": @{
+ @"registrationName": @"onAccessibilityTap"
+ },
+ @"topMagicTap": @{
+ @"registrationName": @"onMagicTap"
+ },
} mutableCopy];
[_viewManagers enumerateKeysAndObjectsUsingBlock:^(NSString *name, RCTViewManager *manager, BOOL *stop) {
diff --git a/React/Views/RCTView.h b/React/Views/RCTView.h
index b8da37b15..51d060ca3 100644
--- a/React/Views/RCTView.h
+++ b/React/Views/RCTView.h
@@ -15,8 +15,14 @@
@protocol RCTAutoInsetsProtocol;
+@class RCTView;
+typedef void (^RCTViewEventHandler)(RCTView *view);
+
@interface RCTView : UIView
+@property (nonatomic, copy) RCTViewEventHandler accessibilityTapHandler;
+@property (nonatomic, copy) RCTViewEventHandler magicTapHandler;
+
/**
* Used to control how touch events are processed.
*/
diff --git a/React/Views/RCTView.m b/React/Views/RCTView.m
index c2cd04703..d6394f2c6 100644
--- a/React/Views/RCTView.m
+++ b/React/Views/RCTView.m
@@ -167,6 +167,26 @@ static NSString *RCTRecursiveAccessibilityLabel(UIView *view)
}
}
+- (BOOL)accessibilityActivate
+{
+ if (self.accessibilityTapHandler) {
+ self.accessibilityTapHandler(self);
+ return YES;
+ } else {
+ return NO;
+ }
+}
+
+- (BOOL)accessibilityPerformMagicTap
+{
+ if (self.magicTapHandler) {
+ self.magicTapHandler(self);
+ return YES;
+ } else {
+ return NO;
+ }
+}
+
#pragma mark - Statics for dealing with layoutGuides
+ (void)autoAdjustInsetsForView:(UIView *)parentView
diff --git a/React/Views/RCTViewManager.m b/React/Views/RCTViewManager.m
index cb42c7ec5..8230e398d 100644
--- a/React/Views/RCTViewManager.m
+++ b/React/Views/RCTViewManager.m
@@ -17,6 +17,31 @@
#import "RCTUIManager.h"
#import "RCTUtils.h"
#import "RCTView.h"
+#import "UIView+React.h"
+
+@implementation RCTConvert(UIAccessibilityTraits)
+
+RCT_MULTI_ENUM_CONVERTER(UIAccessibilityTraits, (@{
+ @"none": @(UIAccessibilityTraitNone),
+ @"button": @(UIAccessibilityTraitButton),
+ @"link": @(UIAccessibilityTraitLink),
+ @"header": @(UIAccessibilityTraitHeader),
+ @"search": @(UIAccessibilityTraitSearchField),
+ @"image": @(UIAccessibilityTraitImage),
+ @"selected": @(UIAccessibilityTraitSelected),
+ @"plays": @(UIAccessibilityTraitPlaysSound),
+ @"key": @(UIAccessibilityTraitKeyboardKey),
+ @"text": @(UIAccessibilityTraitStaticText),
+ @"summary": @(UIAccessibilityTraitSummaryElement),
+ @"disabled": @(UIAccessibilityTraitNotEnabled),
+ @"frequentUpdates": @(UIAccessibilityTraitUpdatesFrequently),
+ @"startsMedia": @(UIAccessibilityTraitStartsMediaSession),
+ @"adjustable": @(UIAccessibilityTraitAdjustable),
+ @"allowsDirectInteraction": @(UIAccessibilityTraitAllowsDirectInteraction),
+ @"pageTurn": @(UIAccessibilityTraitCausesPageTurn),
+ }), UIAccessibilityTraitNone, unsignedLongLongValue)
+
+@end
@implementation RCTViewManager
@@ -67,6 +92,7 @@ RCT_EXPORT_MODULE()
#pragma mark - View properties
RCT_EXPORT_VIEW_PROPERTY(accessibilityLabel, NSString)
+RCT_EXPORT_VIEW_PROPERTY(accessibilityTraits, UIAccessibilityTraits)
RCT_EXPORT_VIEW_PROPERTY(backgroundColor, UIColor)
RCT_REMAP_VIEW_PROPERTY(accessible, isAccessibilityElement, BOOL)
RCT_REMAP_VIEW_PROPERTY(testID, accessibilityIdentifier, NSString)
@@ -146,6 +172,27 @@ RCT_CUSTOM_VIEW_PROPERTY(borderWidth, CGFloat, RCTView)
view.layer.borderWidth = json ? [RCTConvert CGFloat:json] : defaultView.layer.borderWidth;
}
}
+RCT_CUSTOM_VIEW_PROPERTY(onAccessibilityTap, BOOL, RCTView)
+{
+ view.accessibilityTapHandler = [self eventHandlerWithName:@"topAccessibilityTap" json:json];
+}
+RCT_CUSTOM_VIEW_PROPERTY(onMagicTap, BOOL, RCTView)
+{
+ view.magicTapHandler = [self eventHandlerWithName:@"topMagicTap" json:json];
+}
+
+- (RCTViewEventHandler)eventHandlerWithName:(NSString *)eventName json:(id)json
+{
+ RCTViewEventHandler handler = nil;
+ if ([RCTConvert BOOL:json]) {
+ __weak RCTViewManager *weakSelf = self;
+ handler = ^(RCTView *tappedView) {
+ NSDictionary *body = @{ @"target": tappedView.reactTag };
+ [weakSelf.bridge.eventDispatcher sendInputEventWithName:eventName body:body];
+ };
+ }
+ return handler;
+}
#define RCT_VIEW_BORDER_PROPERTY(SIDE) \
RCT_CUSTOM_VIEW_PROPERTY(border##SIDE##Width, CGFloat, RCTView) \
diff --git a/package.json b/package.json
index 3fffc5491..6c4fa493d 100644
--- a/package.json
+++ b/package.json
@@ -36,7 +36,7 @@
],
"scripts": {
"test": "jest",
- "lint": "node linter.js Examples/",
+ "lint": "node linter.js Examples/ Libraries/",
"start": "./packager/packager.sh"
},
"bin": {