@samitouri / QOS-React / commits / a8beab3341

Fix captured/bubbled in ReactNativeTreeTraversal (#8019)

Follow-up to #7741. Added a test for RN event bubbling that fails before the fix.

Ben Alpert committed Oct 20, 2016 at 11:17 UTC a8beab3341f699efcbc886b0b63710891a688db4
3 files changed +137 -2
src/renderers/native/ReactNativeTreeTraversal.js
+2 -2
@@ -82,10 +82,10 @@ function traverseTwoPhase(inst, fn, arg) {
82 }
83 var i;
84 for (i = path.length; i-- > 0;) {
85 - fn(path[i], false, arg);
85 + fn(path[i], 'captured', arg);
86 }
87 for (i = 0; i < path.length; i++) {
88 - fn(path[i], true, arg);
88 + fn(path[i], 'bubbled', arg);
89 }
90 }
91
src/renderers/native/__mocks__/UIManager.js
+46
@@ -18,6 +18,52 @@ var RCTUIManager = {
18 updateView: jest.fn(),
19 removeSubviewsFromContainerWithID: jest.fn(),
20 replaceExistingNonRootView: jest.fn(),
21 + customBubblingEventTypes: {
22 + topBlur: {
23 + phasedRegistrationNames: {
24 + bubbled: 'onBlur',
25 + captured: 'onBlurCapture',
26 + },
27 + },
28 + topFocus: {
29 + phasedRegistrationNames: {
30 + bubbled: 'onFocus',
31 + captured: 'onFocusCapture',
32 + },
33 + },
34 + topTouchCancel: {
35 + phasedRegistrationNames: {
36 + bubbled: 'onTouchCancel',
37 + captured: 'onTouchCancelCapture',
38 + },
39 + },
40 + topTouchEnd: {
41 + phasedRegistrationNames: {
42 + bubbled: 'onTouchEnd',
43 + captured: 'onTouchEndCapture',
44 + },
45 + },
46 + topTouchMove: {
47 + phasedRegistrationNames: {
48 + bubbled: 'onTouchMove',
49 + captured: 'onTouchMoveCapture',
50 + },
51 + },
52 + topTouchStart: {
53 + phasedRegistrationNames: {
54 + bubbled: 'onTouchStart',
55 + captured: 'onTouchStartCapture',
56 + },
57 + },
58 + },
59 + customDirectEventTypes: {
60 + topAccessibilityTap: {
61 + registrationName: 'onAccessibilityTap',
62 + },
63 + topTextLayout: {
64 + registrationName: 'onTextLayout',
65 + },
66 + },
67 };
68
69 module.exports = RCTUIManager;
src/renderers/native/__tests__/ReactNativeEvents-test.js new
+89
@@ -0,0 +1,89 @@
1 +/**
2 + * Copyright 2013-2015, Facebook, Inc.
3 + * All rights reserved.
4 + *
5 + * This source code is licensed under the BSD-style license found in the
6 + * LICENSE file in the root directory of this source tree. An additional grant
7 + * of patent rights can be found in the PATENTS file in the same directory.
8 + *
9 + * @emails react-core
10 + */
11 +
12 +'use strict';
13 +
14 +var RCTEventEmitter;
15 +var React;
16 +var ReactErrorUtils;
17 +var ReactNative;
18 +var UIManager;
19 +var createReactNativeComponentClass;
20 +
21 +beforeEach(() => {
22 + jest.resetModuleRegistry();
23 +
24 + RCTEventEmitter = require('RCTEventEmitter');
25 + React = require('React');
26 + ReactErrorUtils = require('ReactErrorUtils');
27 + ReactNative = require('ReactNative');
28 + UIManager = require('UIManager');
29 + createReactNativeComponentClass = require('createReactNativeComponentClass');
30 +
31 + // Ensure errors from event callbacks are properly surfaced (otherwise,
32 + // jest/jsdom swallows them when we do the .dispatchEvent call)
33 + ReactErrorUtils.invokeGuardedCallback =
34 + ReactErrorUtils.invokeGuardedCallbackWithCatch;
35 +});
36 +
37 +it('handles events', () => {
38 + expect(RCTEventEmitter.register.mock.calls.length).toBe(1);
39 + var EventEmitter = RCTEventEmitter.register.mock.calls[0][0];
40 + var View = createReactNativeComponentClass({
41 + validAttributes: { foo: true },
42 + uiViewClassName: 'View',
43 + });
44 +
45 +
46 + var log = [];
47 + ReactNative.render(
48 + <View
49 + foo="outer"
50 + onTouchEnd={() => log.push('outer touchend')}
51 + onTouchEndCapture={() => log.push('outer touchend capture')}
52 + onTouchStart={() => log.push('outer touchstart')}
53 + onTouchStartCapture={() => log.push('outer touchstart capture')}>
54 + <View
55 + foo="inner"
56 + onTouchEndCapture={() => log.push('inner touchend capture')}
57 + onTouchEnd={() => log.push('inner touchend')}
58 + onTouchStartCapture={() => log.push('inner touchstart capture')}
59 + onTouchStart={() => log.push('inner touchstart')}
60 + />
61 + </View>,
62 + 1
63 + );
64 +
65 + expect(UIManager.createView.mock.calls.length).toBe(2);
66 + var innerTag = UIManager.createView.mock.calls[1][0];
67 +
68 + EventEmitter.receiveTouches(
69 + 'topTouchStart',
70 + [{target: innerTag, identifier: 17}],
71 + [0]
72 + );
73 + EventEmitter.receiveTouches(
74 + 'topTouchEnd',
75 + [{target: innerTag, identifier: 17}],
76 + [0]
77 + );
78 +
79 + expect(log).toEqual([
80 + 'outer touchstart capture',
81 + 'inner touchstart capture',
82 + 'inner touchstart',
83 + 'outer touchstart',
84 + 'outer touchend capture',
85 + 'inner touchend capture',
86 + 'inner touchend',
87 + 'outer touchend',
88 + ]);
89 +});