From 6ef7eaf6630ad6fdd97c5d377771e2df72fcff7e Mon Sep 17 00:00:00 2001 From: Georgiy Kassabli Date: Mon, 18 May 2015 07:32:21 -0700 Subject: [PATCH 01/11] Added accessibility traits support to View class --- Libraries/Components/View/View.js | 28 +++++++++++++++++++ .../ReactNative/ReactNativeViewAttributes.js | 1 + React/Base/RCTConvert.h | 16 +++++++++++ React/Base/RCTConvert.m | 16 +++++++++++ React/Views/RCTViewManager.m | 25 +++++++++++++++++ 5 files changed, 86 insertions(+) diff --git a/Libraries/Components/View/View.js b/Libraries/Components/View/View.js index c3659e961..7fb9d34a6 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,14 @@ 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)), + ]), /** * Used to locate this view in end-to-end tests. */ diff --git a/Libraries/ReactNative/ReactNativeViewAttributes.js b/Libraries/ReactNative/ReactNativeViewAttributes.js index d154d045f..e9e9bbd56 100644 --- a/Libraries/ReactNative/ReactNativeViewAttributes.js +++ b/Libraries/ReactNative/ReactNativeViewAttributes.js @@ -19,6 +19,7 @@ ReactNativeViewAttributes.UIView = { pointerEvents: true, accessible: true, accessibilityLabel: true, + accessibilityTraits: true, testID: true, onLayout: true, }; 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/Views/RCTViewManager.m b/React/Views/RCTViewManager.m index cb42c7ec5..3bedccdc8 100644 --- a/React/Views/RCTViewManager.m +++ b/React/Views/RCTViewManager.m @@ -18,6 +18,30 @@ #import "RCTUtils.h" #import "RCTView.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 @synthesize bridge = _bridge; @@ -67,6 +91,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) From 03905e69f4027eb9e8da7aba50988d412a3608b2 Mon Sep 17 00:00:00 2001 From: Andrei Coman Date: Mon, 18 May 2015 07:33:45 -0700 Subject: [PATCH 02/11] [react_native] JS files from D2079609: [react_native] Use email type for login field --- Libraries/Components/TextInput/TextInput.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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. From de27f1db54c4a8630daac927f7b9ab0790c66a8b Mon Sep 17 00:00:00 2001 From: Luke Date: Mon, 18 May 2015 11:53:23 -0700 Subject: [PATCH 03/11] fix exception when ES6 class with no propTypes defined before calling requireNativeComponent Summary: Closes https://github.com/facebook/react-native/pull/1260 Github Author: Luke Test Plan: Created `class Foo extends React.Component` and made sure error messages were good. --- Libraries/ReactIOS/verifyPropTypes.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) 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] + '`' ); } } From 33ac55b2bdc9374ff75bd295c436a20b99b46b99 Mon Sep 17 00:00:00 2001 From: Premasagar Rose Date: Mon, 18 May 2015 13:03:57 -0700 Subject: [PATCH 04/11] Fix typo in image documentation Summary: Closes https://github.com/facebook/react-native/pull/970 Github Author: Premasagar Rose Test Plan: Imported from GitHub, without a `Test Plan:` line. --- Libraries/Image/Image.ios.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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, From 1c70f33511448c6d8b3d81aa0898a840ae0dd334 Mon Sep 17 00:00:00 2001 From: Spencer Ahrens Date: Mon, 18 May 2015 15:30:17 -0700 Subject: [PATCH 05/11] [ReactNative] clean lint for Libraries/Components --- .eslintrc | 2 +- .../ActivityIndicatorIOS/ActivityIndicatorIOS.ios.js | 1 - Libraries/Components/ScrollResponder.js | 4 ++-- Libraries/Components/Subscribable.js | 2 +- Libraries/Components/WebView/WebView.android.js | 4 ++-- Libraries/Components/WebView/WebView.ios.js | 3 +-- package.json | 2 +- 7 files changed, 8 insertions(+), 10 deletions(-) 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/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/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/package.json b/package.json index 3fffc5491..d524c1785 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/Components", "start": "./packager/packager.sh" }, "bin": { From a4f92ba3db1b5c7a7c04a5e41493ef129c46cf0c Mon Sep 17 00:00:00 2001 From: Alex Akers Date: Tue, 19 May 2015 06:21:52 -0700 Subject: [PATCH 06/11] [React Native] Add magic tap accessibility gesture --- Libraries/Components/View/View.js | 7 +++++++ Libraries/ReactNative/ReactNativeViewAttributes.js | 1 + React/Modules/RCTUIManager.m | 3 +++ React/Views/RCTView.h | 5 +++++ React/Views/RCTView.m | 10 ++++++++++ React/Views/RCTViewManager.m | 14 ++++++++++++++ 6 files changed, 40 insertions(+) diff --git a/Libraries/Components/View/View.js b/Libraries/Components/View/View.js index 7fb9d34a6..568ae43fd 100644 --- a/Libraries/Components/View/View.js +++ b/Libraries/Components/View/View.js @@ -98,6 +98,13 @@ var View = React.createClass({ PropTypes.oneOf(AccessibilityTraits), PropTypes.arrayOf(PropTypes.oneOf(AccessibilityTraits)), ]), + + /** + * 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/ReactNative/ReactNativeViewAttributes.js b/Libraries/ReactNative/ReactNativeViewAttributes.js index e9e9bbd56..7fca115df 100644 --- a/Libraries/ReactNative/ReactNativeViewAttributes.js +++ b/Libraries/ReactNative/ReactNativeViewAttributes.js @@ -22,6 +22,7 @@ ReactNativeViewAttributes.UIView = { accessibilityTraits: true, testID: true, onLayout: true, + onMagicTap: true, }; ReactNativeViewAttributes.RCTView = merge( diff --git a/React/Modules/RCTUIManager.m b/React/Modules/RCTUIManager.m index 7d6925b12..51b37635a 100644 --- a/React/Modules/RCTUIManager.m +++ b/React/Modules/RCTUIManager.m @@ -1309,6 +1309,9 @@ RCT_EXPORT_METHOD(clearJSResponder) @"topLoadingError": @{ @"registrationName": @"onLoadingError" }, + @"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..7335605eb 100644 --- a/React/Views/RCTView.h +++ b/React/Views/RCTView.h @@ -15,8 +15,13 @@ @protocol RCTAutoInsetsProtocol; +@class RCTView; +typedef void (^RCTViewMagicTapHandler)(RCTView *view); + @interface RCTView : UIView +@property (nonatomic, copy) RCTViewMagicTapHandler magicTapHandler; + /** * Used to control how touch events are processed. */ diff --git a/React/Views/RCTView.m b/React/Views/RCTView.m index c2cd04703..675b3b8be 100644 --- a/React/Views/RCTView.m +++ b/React/Views/RCTView.m @@ -167,6 +167,16 @@ static NSString *RCTRecursiveAccessibilityLabel(UIView *view) } } +- (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 3bedccdc8..eff68f05f 100644 --- a/React/Views/RCTViewManager.m +++ b/React/Views/RCTViewManager.m @@ -17,6 +17,7 @@ #import "RCTUIManager.h" #import "RCTUtils.h" #import "RCTView.h" +#import "UIView+React.h" @implementation RCTConvert(UIAccessibilityTraits) @@ -171,6 +172,19 @@ RCT_CUSTOM_VIEW_PROPERTY(borderWidth, CGFloat, RCTView) view.layer.borderWidth = json ? [RCTConvert CGFloat:json] : defaultView.layer.borderWidth; } } +RCT_CUSTOM_VIEW_PROPERTY(onMagicTap, BOOL, RCTView) +{ + RCTViewMagicTapHandler handler = nil; + if ([RCTConvert BOOL:json]) { + __weak RCTViewManager *weakSelf = self; + handler = ^(RCTView *tappedView) { + NSDictionary *body = @{ @"target": tappedView.reactTag }; + [weakSelf.bridge.eventDispatcher sendInputEventWithName:@"topMagicTap" body:body]; + }; + } + + view.magicTapHandler = handler; +} #define RCT_VIEW_BORDER_PROPERTY(SIDE) \ RCT_CUSTOM_VIEW_PROPERTY(border##SIDE##Width, CGFloat, RCTView) \ From 11b515b1b0c25eec3e02f840373fc94c2f6b34b2 Mon Sep 17 00:00:00 2001 From: Spencer Ahrens Date: Tue, 19 May 2015 13:43:46 -0700 Subject: [PATCH 07/11] [ReactNative] clean lint in all of Libraries/ --- .eslintignore | 4 +++- Libraries/ART/ARTSerializablePath.js | 3 +-- Libraries/ART/ReactNativeART.js | 3 +-- Libraries/CustomComponents/Navigator/Navigator.js | 4 +--- .../Navigator/NavigatorSceneConfigs.js | 2 +- Libraries/Fetch/fetch.js | 1 + Libraries/Geolocation/Geolocation.js | 2 +- .../JavaScriptAppEngine/Initialization/SourceMap.js | 1 + .../Initialization/source-map-url.js | 1 + Libraries/PushNotificationIOS/PushNotificationIOS.js | 1 - Libraries/RKBackendNode/queryLayoutByID.js | 1 - Libraries/ReactIOS/IOSNativeBridgeEventPlugin.js | 6 ++---- Libraries/ReactNative/React.js | 3 +-- Libraries/ReactNative/ReactNative.js | 2 +- Libraries/ReactNative/ReactNativeBaseComponent.js | 5 +++-- Libraries/ReactNative/ReactNativeDOMIDOperations.js | 3 +-- Libraries/ReactNative/ReactNativeDefaultInjection.js | 4 +--- Libraries/ReactNative/ReactNativeEventEmitter.js | 3 +-- Libraries/ReactNative/ReactNativeMount.js | 1 - .../ReactNative/ReactNativeReconcileTransaction.js | 3 +-- .../ReactNative/createReactNativeComponentClass.js | 3 +-- Libraries/Settings/Settings.ios.js | 11 +++++------ Libraries/StyleSheet/EdgeInsetsPropType.js | 3 +-- Libraries/StyleSheet/PointPropType.js | 3 +-- Libraries/Utilities/ErrorUtils.js | 1 + Libraries/Utilities/MatrixMath.js | 1 + Libraries/Utilities/RCTLog.js | 1 - Libraries/Utilities/RCTRenderingPerf.js | 1 - Libraries/Utilities/__mocks__/ErrorUtils.js | 2 +- Libraries/Utilities/buildStyleInterpolator.js | 3 ++- Libraries/Utilities/truncate.js | 1 - package.json | 2 +- 32 files changed, 36 insertions(+), 49 deletions(-) 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/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/CustomComponents/Navigator/Navigator.js b/Libraries/CustomComponents/Navigator/Navigator.js index b8c019e32..063012c14 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; @@ -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; 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/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/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/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/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/package.json b/package.json index d524c1785..6c4fa493d 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ ], "scripts": { "test": "jest", - "lint": "node linter.js Examples/ Libraries/Components", + "lint": "node linter.js Examples/ Libraries/", "start": "./packager/packager.sh" }, "bin": { From 97137e8bccdf04bde18fd150e20f9bf835cfaa82 Mon Sep 17 00:00:00 2001 From: Eric Vicenti Date: Tue, 19 May 2015 18:45:08 -0700 Subject: [PATCH 08/11] [ReactNative] Navigator clean scenes popped with gesture Summary: Fixes https://github.com/facebook/react-native/issues/1252 Scenes dismissed/popped via a gesture were not being removed. This is probably a regression from an earlier refactor. Test plan: log statements after scene focusing now reports that `navigator.getCurrentRoutes().length` lowers after gesture. Tested on UIExplorer Navigator example Closes https://github.com/facebook/react-native/pull/1346 Github Author: Eric Vicenti @public Test Plan: Imported from GitHub, without a `Test Plan:` line. --- Libraries/CustomComponents/Navigator/Navigator.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Libraries/CustomComponents/Navigator/Navigator.js b/Libraries/CustomComponents/Navigator/Navigator.js index 063012c14..2327316fc 100644 --- a/Libraries/CustomComponents/Navigator/Navigator.js +++ b/Libraries/CustomComponents/Navigator/Navigator.js @@ -827,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(); }, From c1609bcab8f97e52ff7e9b40be3b000a05d13bfc Mon Sep 17 00:00:00 2001 From: Eric Vicenti Date: Tue, 19 May 2015 18:44:45 -0700 Subject: [PATCH 09/11] [ReactNative] Navigator fix for sceneStyle Summary: Fixes https://github.com/facebook/react-native/issues/1332 When the absolute left position is not set to zero on a provided sceneStyle, scene enabling is broken and no scene will be visible when it is pushed. This was broken recently when the scene disabling was modified to push the scenes offscreen. Closes https://github.com/facebook/react-native/pull/1347 Github Author: Eric Vicenti @public Test Plan: Tested when pushing a scene Navigator in the UIExplorer example while sceneStyle is set on the Navigator --- Libraries/CustomComponents/Navigator/Navigator.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Libraries/CustomComponents/Navigator/Navigator.js b/Libraries/CustomComponents/Navigator/Navigator.js index 2327316fc..569351745 100644 --- a/Libraries/CustomComponents/Navigator/Navigator.js +++ b/Libraries/CustomComponents/Navigator/Navigator.js @@ -688,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, From 32666f0aa2a4b0b38d52e06fa54cc52ce209c681 Mon Sep 17 00:00:00 2001 From: Spencer Ahrens Date: Tue, 19 May 2015 23:47:38 -0700 Subject: [PATCH 10/11] [ReactNative] Add AppEventsTest --- IntegrationTests/AppEventsTest.js | 62 +++++++++++++++++++ IntegrationTests/IntegrationTestsApp.js | 1 + .../IntegrationTestsTests.m | 5 ++ Libraries/RCTTest/RCTTestModule.m | 6 ++ 4 files changed, 74 insertions(+) create mode 100644 IntegrationTests/AppEventsTest.js 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/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 From d211359aeba005f30643d66d0dd1d212db1307e4 Mon Sep 17 00:00:00 2001 From: Georgiy Kassabli Date: Wed, 20 May 2015 08:33:16 -0700 Subject: [PATCH 11/11] Added ability to set custom accessibility tap handler to React Native --- Libraries/Components/View/View.js | 6 ++++++ .../ReactNative/ReactNativeViewAttributes.js | 1 + React/Modules/RCTUIManager.m | 3 +++ React/Views/RCTView.h | 5 +++-- React/Views/RCTView.m | 10 ++++++++++ React/Views/RCTViewManager.m | 16 ++++++++++++---- 6 files changed, 35 insertions(+), 6 deletions(-) diff --git a/Libraries/Components/View/View.js b/Libraries/Components/View/View.js index 568ae43fd..c6a279a22 100644 --- a/Libraries/Components/View/View.js +++ b/Libraries/Components/View/View.js @@ -99,6 +99,12 @@ var View = React.createClass({ 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. diff --git a/Libraries/ReactNative/ReactNativeViewAttributes.js b/Libraries/ReactNative/ReactNativeViewAttributes.js index 7fca115df..50b839e1d 100644 --- a/Libraries/ReactNative/ReactNativeViewAttributes.js +++ b/Libraries/ReactNative/ReactNativeViewAttributes.js @@ -22,6 +22,7 @@ ReactNativeViewAttributes.UIView = { accessibilityTraits: true, testID: true, onLayout: true, + onAccessibilityTap: true, onMagicTap: true, }; diff --git a/React/Modules/RCTUIManager.m b/React/Modules/RCTUIManager.m index 51b37635a..e9734c204 100644 --- a/React/Modules/RCTUIManager.m +++ b/React/Modules/RCTUIManager.m @@ -1309,6 +1309,9 @@ RCT_EXPORT_METHOD(clearJSResponder) @"topLoadingError": @{ @"registrationName": @"onLoadingError" }, + @"topAccessibilityTap": @{ + @"registrationName": @"onAccessibilityTap" + }, @"topMagicTap": @{ @"registrationName": @"onMagicTap" }, diff --git a/React/Views/RCTView.h b/React/Views/RCTView.h index 7335605eb..51d060ca3 100644 --- a/React/Views/RCTView.h +++ b/React/Views/RCTView.h @@ -16,11 +16,12 @@ @protocol RCTAutoInsetsProtocol; @class RCTView; -typedef void (^RCTViewMagicTapHandler)(RCTView *view); +typedef void (^RCTViewEventHandler)(RCTView *view); @interface RCTView : UIView -@property (nonatomic, copy) RCTViewMagicTapHandler magicTapHandler; +@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 675b3b8be..d6394f2c6 100644 --- a/React/Views/RCTView.m +++ b/React/Views/RCTView.m @@ -167,6 +167,16 @@ static NSString *RCTRecursiveAccessibilityLabel(UIView *view) } } +- (BOOL)accessibilityActivate +{ + if (self.accessibilityTapHandler) { + self.accessibilityTapHandler(self); + return YES; + } else { + return NO; + } +} + - (BOOL)accessibilityPerformMagicTap { if (self.magicTapHandler) { diff --git a/React/Views/RCTViewManager.m b/React/Views/RCTViewManager.m index eff68f05f..8230e398d 100644 --- a/React/Views/RCTViewManager.m +++ b/React/Views/RCTViewManager.m @@ -172,18 +172,26 @@ 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) { - RCTViewMagicTapHandler handler = nil; + 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:@"topMagicTap" body:body]; + [weakSelf.bridge.eventDispatcher sendInputEventWithName:eventName body:body]; }; } - - view.magicTapHandler = handler; + return handler; } #define RCT_VIEW_BORDER_PROPERTY(SIDE) \