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
+});