Create ReactEventListener
Per our discussion friday, this creates ReactEventListener and renames variables to use the "handle" nomenclature.
Pete Hunt committed
Jun 14, 2014 at 20:16 UTC
e1c2d02fddb40cb0b246d2c2da97b41b6ba378bc
15 files changed
+348
-282
src/browser/ReactBrowserEventEmitter.js
renamed
+102
-89
@@ -13,29 +13,31 @@
13
* See the License for the specific language governing permissions and
14
* limitations under the License.
15
*
16
- * @providesModule ReactEventEmitter
16
+ * @providesModule ReactBrowserEventEmitter
17
* @typechecks static-only
18
*/
19
20
"use strict";
21
22
var EventConstants = require('EventConstants');
23
-var EventListener = require('EventListener');
23
var EventPluginHub = require('EventPluginHub');
24
var EventPluginRegistry = require('EventPluginRegistry');
26
-var ExecutionEnvironment = require('ExecutionEnvironment');
25
var ReactEventEmitterMixin = require('ReactEventEmitterMixin');
26
var ViewportMetrics = require('ViewportMetrics');
27
30
-var invariant = require('invariant');
28
var isEventSupported = require('isEventSupported');
29
var merge = require('merge');
30
31
/**
35
- * Summary of `ReactEventEmitter` event handling:
32
+ * Summary of `ReactBrowserEventEmitter` event handling:
33
*
37
- * - Top-level delegation is used to trap native browser events. We normalize
38
- * and de-duplicate events to account for browser quirks.
34
+ * - Top-level delegation is used to trap most native browser events. This
35
+ * may only occur in the main thread and is the responsibility of
36
+ * ReactEventListener, which is injected and can therefore support pluggable
37
+ * event sources. This is the only work that occurs in the main thread.
38
+ *
39
+ * - We normalize and de-duplicate events to account for browser quirks. This
40
+ * may be done in the worker thread.
41
*
42
* - Forward these native events (with the associated top-level type used to
43
* trap it) to `EventPluginHub`, which in turn will ask plugins if they want
@@ -48,11 +50,16 @@ var merge = require('merge');
50
*
51
* Overview of React and the event system:
52
*
51
- * .
53
* +------------+ .
54
* | DOM | .
55
+ * +------------+ .
56
+ * | .
57
+ * v .
58
+ * +------------+ .
59
+ * | ReactEvent | .
60
+ * | Listener | .
61
* +------------+ . +-----------+
55
- * + . +--------+|SimpleEvent|
62
+ * | . +--------+|SimpleEvent|
63
* | . | |Plugin |
64
* +-----|------+ . v +-----------+
65
* | | | . +--------------+ +------------+
@@ -139,68 +146,31 @@ function getListeningForDocument(mountAt) {
146
}
147
148
/**
142
- * Traps top-level events by using event bubbling.
149
+ * `ReactBrowserEventEmitter` is used to attach top-level event listeners. For
150
+ * example:
151
*
144
- * @param {string} topLevelType Record from `EventConstants`.
145
- * @param {string} handlerBaseName Event name (e.g. "click").
146
- * @param {DOMEventTarget} element Element on which to attach listener.
147
- * @return {object} An object with a remove function which will forcefully
148
- * remove the listener.
149
- * @internal
150
- */
151
-function trapBubbledEvent(topLevelType, handlerBaseName, element) {
152
- return EventListener.listen(
153
- element,
154
- handlerBaseName,
155
- ReactEventEmitter.TopLevelCallbackCreator.createTopLevelCallback(
156
- topLevelType
157
- )
158
- );
159
-}
160
-
161
-/**
162
- * Traps a top-level event by using event capturing.
163
- *
164
- * @param {string} topLevelType Record from `EventConstants`.
165
- * @param {string} handlerBaseName Event name (e.g. "click").
166
- * @param {DOMEventTarget} element Element on which to attach listener.
167
- * @return {object} An object with a remove function which will forcefully
168
- * remove the listener.
169
- * @internal
170
- */
171
-function trapCapturedEvent(topLevelType, handlerBaseName, element) {
172
- return EventListener.capture(
173
- element,
174
- handlerBaseName,
175
- ReactEventEmitter.TopLevelCallbackCreator.createTopLevelCallback(
176
- topLevelType
177
- )
178
- );
179
-}
180
-
181
-/**
182
- * `ReactEventEmitter` is used to attach top-level event listeners. For example:
183
- *
184
- * ReactEventEmitter.putListener('myID', 'onClick', myFunction);
152
+ * ReactBrowserEventEmitter.putListener('myID', 'onClick', myFunction);
153
*
154
* This would allocate a "registration" of `('onClick', myFunction)` on 'myID'.
155
*
156
* @internal
157
*/
190
-var ReactEventEmitter = merge(ReactEventEmitterMixin, {
158
+var ReactBrowserEventEmitter = merge(ReactEventEmitterMixin, {
159
160
/**
193
- * React references `ReactEventTopLevelCallback` using this property in order
194
- * to allow dependency injection.
161
+ * Injectable event backend
162
*/
196
- TopLevelCallbackCreator: null,
163
+ ReactEventListener: null,
164
165
injection: {
166
/**
200
- * @param {function} TopLevelCallbackCreator
167
+ * @param {object} ReactEventListener
168
*/
202
- injectTopLevelCallbackCreator: function(TopLevelCallbackCreator) {
203
- ReactEventEmitter.TopLevelCallbackCreator = TopLevelCallbackCreator;
169
+ injectReactEventListener: function(ReactEventListener) {
170
+ ReactEventListener.setHandleTopLevel(
171
+ ReactBrowserEventEmitter.handleTopLevel
172
+ );
173
+ ReactBrowserEventEmitter.ReactEventListener = ReactEventListener;
174
}
175
},
176
@@ -210,13 +180,8 @@ var ReactEventEmitter = merge(ReactEventEmitterMixin, {
180
* @param {boolean} enabled True if callbacks should be enabled.
181
*/
182
setEnabled: function(enabled) {
213
- invariant(
214
- ExecutionEnvironment.canUseDOM,
215
- 'setEnabled(...): Cannot toggle event listening in a Worker thread. ' +
216
- 'This is likely a bug in the framework. Please report immediately.'
217
- );
218
- if (ReactEventEmitter.TopLevelCallbackCreator) {
219
- ReactEventEmitter.TopLevelCallbackCreator.setEnabled(enabled);
183
+ if (ReactBrowserEventEmitter.ReactEventListener) {
184
+ ReactBrowserEventEmitter.ReactEventListener.setEnabled(enabled);
185
}
186
},
187
@@ -225,8 +190,8 @@ var ReactEventEmitter = merge(ReactEventEmitterMixin, {
190
*/
191
isEnabled: function() {
192
return !!(
228
- ReactEventEmitter.TopLevelCallbackCreator &&
229
- ReactEventEmitter.TopLevelCallbackCreator.isEnabled()
193
+ ReactBrowserEventEmitter.ReactEventListener &&
194
+ ReactBrowserEventEmitter.ReactEventListener.isEnabled()
195
);
196
},
197
@@ -249,10 +214,10 @@ var ReactEventEmitter = merge(ReactEventEmitterMixin, {
214
* they bubble to document.
215
*
216
* @param {string} registrationName Name of listener (e.g. `onClick`).
252
- * @param {DOMDocument} contentDocument Document which owns the container
217
+ * @param {object} contentDocumentHandle Document which owns the container
218
*/
254
- listenTo: function(registrationName, contentDocument) {
255
- var mountAt = contentDocument;
219
+ listenTo: function(registrationName, contentDocumentHandle) {
220
+ var mountAt = contentDocumentHandle;
221
var isListening = getListeningForDocument(mountAt);
222
var dependencies = EventPluginRegistry.
223
registrationNameDependencies[registrationName];
@@ -268,42 +233,79 @@ var ReactEventEmitter = merge(ReactEventEmitterMixin, {
233
234
if (topLevelType === topLevelTypes.topWheel) {
235
if (isEventSupported('wheel')) {
271
- trapBubbledEvent(topLevelTypes.topWheel, 'wheel', mountAt);
236
+ ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent(
237
+ topLevelTypes.topWheel,
238
+ 'wheel',
239
+ mountAt
240
+ );
241
} else if (isEventSupported('mousewheel')) {
273
- trapBubbledEvent(topLevelTypes.topWheel, 'mousewheel', mountAt);
242
+ ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent(
243
+ topLevelTypes.topWheel,
244
+ 'mousewheel',
245
+ mountAt
246
+ );
247
} else {
248
// Firefox needs to capture a different mouse scroll event.
249
// @see http://www.quirksmode.org/dom/events/tests/scroll.html
277
- trapBubbledEvent(
250
+ ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent(
251
topLevelTypes.topWheel,
252
'DOMMouseScroll',
280
- mountAt);
253
+ mountAt
254
+ );
255
}
256
} else if (topLevelType === topLevelTypes.topScroll) {
257
258
if (isEventSupported('scroll', true)) {
285
- trapCapturedEvent(topLevelTypes.topScroll, 'scroll', mountAt);
259
+ ReactBrowserEventEmitter.ReactEventListener.trapCapturedEvent(
260
+ topLevelTypes.topScroll,
261
+ 'scroll',
262
+ mountAt
263
+ );
264
} else {
287
- trapBubbledEvent(topLevelTypes.topScroll, 'scroll', window);
265
+ ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent(
266
+ topLevelTypes.topScroll,
267
+ 'scroll',
268
+ ReactBrowserEventEmitter.ReactEventListener.WINDOW_HANDLE
269
+ );
270
}
271
} else if (topLevelType === topLevelTypes.topFocus ||
272
topLevelType === topLevelTypes.topBlur) {
273
274
if (isEventSupported('focus', true)) {
293
- trapCapturedEvent(topLevelTypes.topFocus, 'focus', mountAt);
294
- trapCapturedEvent(topLevelTypes.topBlur, 'blur', mountAt);
275
+ ReactBrowserEventEmitter.ReactEventListener.trapCapturedEvent(
276
+ topLevelTypes.topFocus,
277
+ 'focus',
278
+ mountAt
279
+ );
280
+ ReactBrowserEventEmitter.ReactEventListener.trapCapturedEvent(
281
+ topLevelTypes.topBlur,
282
+ 'blur',
283
+ mountAt
284
+ );
285
} else if (isEventSupported('focusin')) {
286
// IE has `focusin` and `focusout` events which bubble.
287
// @see http://www.quirksmode.org/blog/archives/2008/04/delegating_the.html
298
- trapBubbledEvent(topLevelTypes.topFocus, 'focusin', mountAt);
299
- trapBubbledEvent(topLevelTypes.topBlur, 'focusout', mountAt);
288
+ ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent(
289
+ topLevelTypes.topFocus,
290
+ 'focusin',
291
+ mountAt
292
+ );
293
+ ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent(
294
+ topLevelTypes.topBlur,
295
+ 'focusout',
296
+ mountAt
297
+ );
298
}
299
300
// to make sure blur and focus event listeners are only attached once
301
isListening[topLevelTypes.topBlur] = true;
302
isListening[topLevelTypes.topFocus] = true;
303
} else if (topEventMapping.hasOwnProperty(dependency)) {
306
- trapBubbledEvent(topLevelType, topEventMapping[dependency], mountAt);
304
+ ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent(
305
+ topLevelType,
306
+ topEventMapping[dependency],
307
+ mountAt
308
+ );
309
}
310
311
isListening[dependency] = true;
@@ -311,6 +313,22 @@ var ReactEventEmitter = merge(ReactEventEmitterMixin, {
313
}
314
},
315
316
+ trapBubbledEvent: function(topLevelType, handlerBaseName, handle) {
317
+ return ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent(
318
+ topLevelType,
319
+ handlerBaseName,
320
+ handle
321
+ );
322
+ },
323
+
324
+ trapCapturedEvent: function(topLevelType, handlerBaseName, handle) {
325
+ return ReactBrowserEventEmitter.ReactEventListener.trapCapturedEvent(
326
+ topLevelType,
327
+ handlerBaseName,
328
+ handle
329
+ );
330
+ },
331
+
332
/**
333
* Listens to window scroll and resize events. We cache scroll values so that
334
* application code can access them without triggering reflows.
@@ -322,8 +340,7 @@ var ReactEventEmitter = merge(ReactEventEmitterMixin, {
340
ensureScrollValueMonitoring: function(){
341
if (!isMonitoringScrollValue) {
342
var refresh = ViewportMetrics.refreshScrollValues;
325
- EventListener.listen(window, 'scroll', refresh);
326
- EventListener.listen(window, 'resize', refresh);
343
+ ReactBrowserEventEmitter.ReactEventListener.monitorScrollValue(refresh);
344
isMonitoringScrollValue = true;
345
}
346
},
@@ -338,12 +355,8 @@ var ReactEventEmitter = merge(ReactEventEmitterMixin, {
355
356
deleteListener: EventPluginHub.deleteListener,
357
341
- deleteAllListeners: EventPluginHub.deleteAllListeners,
342
-
343
- trapBubbledEvent: trapBubbledEvent,
344
-
345
- trapCapturedEvent: trapCapturedEvent
358
+ deleteAllListeners: EventPluginHub.deleteAllListeners
359
360
});
361
349
-module.exports = ReactEventEmitter;
362
+module.exports = ReactBrowserEventEmitter;
src/browser/ReactPutListenerQueue.js
+2
-2
@@ -19,7 +19,7 @@
19
"use strict";
20
21
var PooledClass = require('PooledClass');
22
-var ReactEventEmitter = require('ReactEventEmitter');
22
+var ReactBrowserEventEmitter = require('ReactBrowserEventEmitter');
23
24
var mixInto = require('mixInto');
25
@@ -39,7 +39,7 @@ mixInto(ReactPutListenerQueue, {
39
putListeners: function() {
40
for (var i = 0; i < this.listenersToPut.length; i++) {
41
var listenerToPut = this.listenersToPut[i];
42
- ReactEventEmitter.putListener(
42
+ ReactBrowserEventEmitter.putListener(
43
listenerToPut.rootNodeID,
44
listenerToPut.propKey,
45
listenerToPut.propValue
src/browser/ReactReconcileTransaction.js
+9
-8
@@ -21,7 +21,7 @@
21
22
var CallbackQueue = require('CallbackQueue');
23
var PooledClass = require('PooledClass');
24
-var ReactEventEmitter = require('ReactEventEmitter');
24
+var ReactBrowserEventEmitter = require('ReactBrowserEventEmitter');
25
var ReactInputSelection = require('ReactInputSelection');
26
var ReactPutListenerQueue = require('ReactPutListenerQueue');
27
var Transaction = require('Transaction');
@@ -50,21 +50,22 @@ var SELECTION_RESTORATION = {
50
*/
51
var EVENT_SUPPRESSION = {
52
/**
53
- * @return {boolean} The enabled status of `ReactEventEmitter` before the
54
- * reconciliation.
53
+ * @return {boolean} The enabled status of `ReactBrowserEventEmitter` before
54
+ * the reconciliation.
55
*/
56
initialize: function() {
57
- var currentlyEnabled = ReactEventEmitter.isEnabled();
58
- ReactEventEmitter.setEnabled(false);
57
+ var currentlyEnabled = ReactBrowserEventEmitter.isEnabled();
58
+ ReactBrowserEventEmitter.setEnabled(false);
59
return currentlyEnabled;
60
},
61
62
/**
63
- * @param {boolean} previouslyEnabled Enabled status of `ReactEventEmitter`
64
- * before the reconciliation occured. `close` restores the previous value.
63
+ * @param {boolean} previouslyEnabled Enabled status of
64
+ * `ReactBrowserEventEmitter` before the reconciliation occured. `close`
65
+ * restores the previous value.
66
*/
67
close: function(previouslyEnabled) {
67
- ReactEventEmitter.setEnabled(previouslyEnabled);
68
+ ReactBrowserEventEmitter.setEnabled(previouslyEnabled);
69
}
70
};
71
src/browser/__tests__/ReactBrowserEventEmitter-test.js
renamed
+73
-57
@@ -21,7 +21,7 @@
21
require('mock-modules')
22
.dontMock('EventPluginHub')
23
.dontMock('ReactMount')
24
- .dontMock('ReactEventEmitter')
24
+ .dontMock('ReactBrowserEventEmitter')
25
.dontMock('ReactInstanceHandles')
26
.dontMock('EventPluginHub')
27
.dontMock('TapEventPlugin')
@@ -42,7 +42,7 @@ var setID = function(el, id) {
42
var oldGetNode = ReactMount.getNode;
43
44
var EventPluginHub;
45
-var ReactEventEmitter;
45
+var ReactBrowserEventEmitter;
46
var ReactTestUtils;
47
var TapEventPlugin;
48
var EventListener;
@@ -67,12 +67,12 @@ var ON_CHANGE_KEY = keyOf({onChange: null});
67
68
69
/**
70
- * Since `ReactEventEmitter` is fairly well separated from the DOM, we can test
71
- * almost all of `ReactEventEmitter` without ever rendering anything in the DOM.
72
- * As long as we provide IDs that follow `React's` conventional id namespace
73
- * hierarchy. The only reason why we create these DOM nodes, is so that when we
74
- * feed them into `ReactEventEmitter` (through `ReactTestUtils`), the event
75
- * handlers may receive a DOM node to inspect.
70
+ * Since `ReactBrowserEventEmitter` is fairly well separated from the DOM, we
71
+ * can test almost all of `ReactBrowserEventEmitter` without ever rendering
72
+ * anything in the DOM. As long as we provide IDs that follow `React's`
73
+ * conventional id namespace hierarchy. The only reason why we create these DOM
74
+ * nodes is so that when we feed them into `ReactBrowserEventEmitter` (through
75
+ * `ReactTestUtils`), the event handlers may receive a DOM node to inspect.
76
*/
77
var CHILD = document.createElement('div');
78
var PARENT = document.createElement('div');
@@ -82,14 +82,15 @@ setID(PARENT, '.0.0.0');
82
setID(GRANDPARENT, '.0.0');
83
84
function registerSimpleTestHandler() {
85
- ReactEventEmitter.putListener(getID(CHILD), ON_CLICK_KEY, LISTENER);
86
- var listener = ReactEventEmitter.getListener(getID(CHILD), ON_CLICK_KEY);
85
+ ReactBrowserEventEmitter.putListener(getID(CHILD), ON_CLICK_KEY, LISTENER);
86
+ var listener =
87
+ ReactBrowserEventEmitter.getListener(getID(CHILD), ON_CLICK_KEY);
88
expect(listener).toEqual(LISTENER);
88
- return ReactEventEmitter.getListener(getID(CHILD), ON_CLICK_KEY);
89
+ return ReactBrowserEventEmitter.getListener(getID(CHILD), ON_CLICK_KEY);
90
}
91
92
92
-describe('ReactEventEmitter', function() {
93
+describe('ReactBrowserEventEmitter', function() {
94
beforeEach(function() {
95
require('mock-modules').dumpCache();
96
LISTENER.mockClear();
@@ -97,7 +98,7 @@ describe('ReactEventEmitter', function() {
98
TapEventPlugin = require('TapEventPlugin');
99
ReactMount = require('ReactMount');
100
EventListener = require('EventListener');
100
- ReactEventEmitter = require('ReactEventEmitter');
101
+ ReactBrowserEventEmitter = require('ReactBrowserEventEmitter');
102
ReactTestUtils = require('ReactTestUtils');
103
ReactMount.getNode = function(id) {
104
return idToNode[id];
@@ -115,20 +116,23 @@ describe('ReactEventEmitter', function() {
116
117
it('should store a listener correctly', function() {
118
registerSimpleTestHandler();
118
- var listener = ReactEventEmitter.getListener(getID(CHILD), ON_CLICK_KEY);
119
+ var listener =
120
+ ReactBrowserEventEmitter.getListener(getID(CHILD), ON_CLICK_KEY);
121
expect(listener).toBe(LISTENER);
122
});
123
124
it('should retrieve a listener correctly', function() {
125
registerSimpleTestHandler();
124
- var listener = ReactEventEmitter.getListener(getID(CHILD), ON_CLICK_KEY);
126
+ var listener =
127
+ ReactBrowserEventEmitter.getListener(getID(CHILD), ON_CLICK_KEY);
128
expect(listener).toEqual(LISTENER);
129
});
130
131
it('should clear all handlers when asked to', function() {
132
registerSimpleTestHandler();
130
- ReactEventEmitter.deleteAllListeners(getID(CHILD));
131
- var listener = ReactEventEmitter.getListener(getID(CHILD), ON_CLICK_KEY);
133
+ ReactBrowserEventEmitter.deleteAllListeners(getID(CHILD));
134
+ var listener =
135
+ ReactBrowserEventEmitter.getListener(getID(CHILD), ON_CLICK_KEY);
136
expect(listener).toBe(undefined);
137
});
138
@@ -138,28 +142,31 @@ describe('ReactEventEmitter', function() {
142
expect(LISTENER.mock.calls.length).toBe(1);
143
});
144
141
- it('should not invoke handlers if ReactEventEmitter is disabled', function() {
142
- registerSimpleTestHandler();
143
- ReactEventEmitter.setEnabled(false);
144
- ReactTestUtils.SimulateNative.click(CHILD);
145
- expect(LISTENER.mock.calls.length).toBe(0);
146
- ReactEventEmitter.setEnabled(true);
147
- ReactTestUtils.SimulateNative.click(CHILD);
148
- expect(LISTENER.mock.calls.length).toBe(1);
149
- });
145
+ it(
146
+ 'should not invoke handlers if ReactBrowserEventEmitter is disabled',
147
+ function() {
148
+ registerSimpleTestHandler();
149
+ ReactBrowserEventEmitter.setEnabled(false);
150
+ ReactTestUtils.SimulateNative.click(CHILD);
151
+ expect(LISTENER.mock.calls.length).toBe(0);
152
+ ReactBrowserEventEmitter.setEnabled(true);
153
+ ReactTestUtils.SimulateNative.click(CHILD);
154
+ expect(LISTENER.mock.calls.length).toBe(1);
155
+ }
156
+ );
157
158
it('should bubble simply', function() {
152
- ReactEventEmitter.putListener(
159
+ ReactBrowserEventEmitter.putListener(
160
getID(CHILD),
161
ON_CLICK_KEY,
162
recordID.bind(null, getID(CHILD))
163
);
157
- ReactEventEmitter.putListener(
164
+ ReactBrowserEventEmitter.putListener(
165
getID(PARENT),
166
ON_CLICK_KEY,
167
recordID.bind(null, getID(PARENT))
168
);
162
- ReactEventEmitter.putListener(
169
+ ReactBrowserEventEmitter.putListener(
170
getID(GRANDPARENT),
171
ON_CLICK_KEY,
172
recordID.bind(null, getID(GRANDPARENT))
@@ -172,7 +179,7 @@ describe('ReactEventEmitter', function() {
179
});
180
181
it('should set currentTarget', function() {
175
- ReactEventEmitter.putListener(
182
+ ReactBrowserEventEmitter.putListener(
183
getID(CHILD),
184
ON_CLICK_KEY,
185
function(event) {
@@ -180,7 +187,7 @@ describe('ReactEventEmitter', function() {
187
expect(event.currentTarget).toBe(CHILD);
188
}
189
);
183
- ReactEventEmitter.putListener(
190
+ ReactBrowserEventEmitter.putListener(
191
getID(PARENT),
192
ON_CLICK_KEY,
193
function(event) {
@@ -188,7 +195,7 @@ describe('ReactEventEmitter', function() {
195
expect(event.currentTarget).toBe(PARENT);
196
}
197
);
191
- ReactEventEmitter.putListener(
198
+ ReactBrowserEventEmitter.putListener(
199
getID(GRANDPARENT),
200
ON_CLICK_KEY,
201
function(event) {
@@ -204,17 +211,17 @@ describe('ReactEventEmitter', function() {
211
});
212
213
it('should support stopPropagation()', function() {
207
- ReactEventEmitter.putListener(
214
+ ReactBrowserEventEmitter.putListener(
215
getID(CHILD),
216
ON_CLICK_KEY,
217
recordID.bind(null, getID(CHILD))
218
);
212
- ReactEventEmitter.putListener(
219
+ ReactBrowserEventEmitter.putListener(
220
getID(PARENT),
221
ON_CLICK_KEY,
222
recordIDAndStopPropagation.bind(null, getID(PARENT))
223
);
217
- ReactEventEmitter.putListener(
224
+ ReactBrowserEventEmitter.putListener(
225
getID(GRANDPARENT),
226
ON_CLICK_KEY,
227
recordID.bind(null, getID(GRANDPARENT))
@@ -226,17 +233,17 @@ describe('ReactEventEmitter', function() {
233
});
234
235
it('should stop after first dispatch if stopPropagation', function() {
229
- ReactEventEmitter.putListener(
236
+ ReactBrowserEventEmitter.putListener(
237
getID(CHILD),
238
ON_CLICK_KEY,
239
recordIDAndStopPropagation.bind(null, getID(CHILD))
240
);
234
- ReactEventEmitter.putListener(
241
+ ReactBrowserEventEmitter.putListener(
242
getID(PARENT),
243
ON_CLICK_KEY,
244
recordID.bind(null, getID(PARENT))
245
);
239
- ReactEventEmitter.putListener(
246
+ ReactBrowserEventEmitter.putListener(
247
getID(GRANDPARENT),
248
ON_CLICK_KEY,
249
recordID.bind(null, getID(GRANDPARENT))
@@ -247,17 +254,17 @@ describe('ReactEventEmitter', function() {
254
});
255
256
it('should stopPropagation if false is returned', function() {
250
- ReactEventEmitter.putListener(
257
+ ReactBrowserEventEmitter.putListener(
258
getID(CHILD),
259
ON_CLICK_KEY,
260
recordIDAndReturnFalse.bind(null, getID(CHILD))
261
);
255
- ReactEventEmitter.putListener(
262
+ ReactBrowserEventEmitter.putListener(
263
getID(PARENT),
264
ON_CLICK_KEY,
265
recordID.bind(null, getID(PARENT))
266
);
260
- ReactEventEmitter.putListener(
267
+ ReactBrowserEventEmitter.putListener(
268
getID(GRANDPARENT),
269
ON_CLICK_KEY,
270
recordID.bind(null, getID(GRANDPARENT))
@@ -279,10 +286,14 @@ describe('ReactEventEmitter', function() {
286
it('should invoke handlers that were removed while bubbling', function() {
287
var handleParentClick = mocks.getMockFunction();
288
var handleChildClick = function(event) {
282
- ReactEventEmitter.deleteAllListeners(getID(PARENT));
289
+ ReactBrowserEventEmitter.deleteAllListeners(getID(PARENT));
290
};
284
- ReactEventEmitter.putListener(getID(CHILD), ON_CLICK_KEY, handleChildClick);
285
- ReactEventEmitter.putListener(
291
+ ReactBrowserEventEmitter.putListener(
292
+ getID(CHILD),
293
+ ON_CLICK_KEY,
294
+ handleChildClick
295
+ );
296
+ ReactBrowserEventEmitter.putListener(
297
getID(PARENT),
298
ON_CLICK_KEY,
299
handleParentClick
@@ -294,19 +305,23 @@ describe('ReactEventEmitter', function() {
305
it('should not invoke newly inserted handlers while bubbling', function() {
306
var handleParentClick = mocks.getMockFunction();
307
var handleChildClick = function(event) {
297
- ReactEventEmitter.putListener(
308
+ ReactBrowserEventEmitter.putListener(
309
getID(PARENT),
310
ON_CLICK_KEY,
311
handleParentClick
312
);
313
};
303
- ReactEventEmitter.putListener(getID(CHILD), ON_CLICK_KEY, handleChildClick);
314
+ ReactBrowserEventEmitter.putListener(
315
+ getID(CHILD),
316
+ ON_CLICK_KEY,
317
+ handleChildClick
318
+ );
319
ReactTestUtils.Simulate.click(CHILD);
320
expect(handleParentClick.mock.calls.length).toBe(0);
321
});
322
323
it('should infer onTouchTap from a touchStart/End', function() {
309
- ReactEventEmitter.putListener(
324
+ ReactBrowserEventEmitter.putListener(
325
getID(CHILD),
326
ON_TOUCH_TAP_KEY,
327
recordID.bind(null, getID(CHILD))
@@ -324,7 +339,7 @@ describe('ReactEventEmitter', function() {
339
});
340
341
it('should infer onTouchTap from when dragging below threshold', function() {
327
- ReactEventEmitter.putListener(
342
+ ReactBrowserEventEmitter.putListener(
343
getID(CHILD),
344
ON_TOUCH_TAP_KEY,
345
recordID.bind(null, getID(CHILD))
@@ -342,7 +357,7 @@ describe('ReactEventEmitter', function() {
357
});
358
359
it('should not onTouchTap from when dragging beyond threshold', function() {
345
- ReactEventEmitter.putListener(
360
+ ReactBrowserEventEmitter.putListener(
361
getID(CHILD),
362
ON_TOUCH_TAP_KEY,
363
recordID.bind(null, getID(CHILD))
@@ -360,15 +375,15 @@ describe('ReactEventEmitter', function() {
375
376
it('should listen to events only once', function() {
377
spyOn(EventListener, 'listen');
363
- ReactEventEmitter.listenTo(ON_CLICK_KEY, document);
364
- ReactEventEmitter.listenTo(ON_CLICK_KEY, document);
378
+ ReactBrowserEventEmitter.listenTo(ON_CLICK_KEY, document);
379
+ ReactBrowserEventEmitter.listenTo(ON_CLICK_KEY, document);
380
expect(EventListener.listen.callCount).toBe(1);
381
});
382
383
it('should work with event plugins without dependencies', function() {
384
spyOn(EventListener, 'listen');
385
371
- ReactEventEmitter.listenTo(ON_CLICK_KEY, document);
386
+ ReactBrowserEventEmitter.listenTo(ON_CLICK_KEY, document);
387
388
expect(EventListener.listen.argsForCall[0][1]).toBe('click');
389
});
@@ -377,7 +392,7 @@ describe('ReactEventEmitter', function() {
392
spyOn(EventListener, 'listen');
393
spyOn(EventListener, 'capture');
394
380
- ReactEventEmitter.listenTo(ON_CHANGE_KEY, document);
395
+ ReactBrowserEventEmitter.listenTo(ON_CHANGE_KEY, document);
396
397
var setEventListeners = [];
398
var listenCalls = EventListener.listen.argsForCall;
@@ -389,7 +404,8 @@ describe('ReactEventEmitter', function() {
404
setEventListeners.push(captureCalls[i][1]);
405
}
406
392
- var module = ReactEventEmitter.registrationNameModules[ON_CHANGE_KEY];
407
+ var module =
408
+ ReactBrowserEventEmitter.registrationNameModules[ON_CHANGE_KEY];
409
var dependencies = module.eventTypes.change.dependencies;
410
expect(setEventListeners.length).toEqual(dependencies.length);
411
@@ -399,17 +415,17 @@ describe('ReactEventEmitter', function() {
415
});
416
417
it('should bubble onTouchTap', function() {
402
- ReactEventEmitter.putListener(
418
+ ReactBrowserEventEmitter.putListener(
419
getID(CHILD),
420
ON_TOUCH_TAP_KEY,
421
recordID.bind(null, getID(CHILD))
422
);
407
- ReactEventEmitter.putListener(
423
+ ReactBrowserEventEmitter.putListener(
424
getID(PARENT),
425
ON_TOUCH_TAP_KEY,
426
recordID.bind(null, getID(PARENT))
427
);
412
- ReactEventEmitter.putListener(
428
+ ReactBrowserEventEmitter.putListener(
429
getID(GRANDPARENT),
430
ON_TOUCH_TAP_KEY,
431
recordID.bind(null, getID(GRANDPARENT))
src/browser/eventPlugins/ChangeEventPlugin.js
+1
-1
@@ -85,7 +85,7 @@ function manualDispatchChangeEvent(nativeEvent) {
85
EventPropagators.accumulateTwoPhaseDispatches(event);
86
87
// If change and propertychange bubbled, we'd just bind to it like all the
88
- // other events and have it go through ReactEventTopLevelCallback. Since it
88
+ // other events and have it go through ReactBrowserEventEmitter. Since it
89
// doesn't, we manually listen for the events and so we have to enqueue and
90
// process the abstract event manually.
91
//
src/browser/eventPlugins/__tests__/AnalyticsEventPlugin-test.js
+2
-2
@@ -26,7 +26,7 @@ describe('AnalyticsEventPlugin', function() {
26
var EventPluginHub;
27
var EventPluginRegistry;
28
var React;
29
- var ReactEventEmitter;
29
+ var ReactBrowserEventEmitter;
30
var ReactTestUtils;
31
32
var DefaultEventPluginOrder;
@@ -40,7 +40,7 @@ describe('AnalyticsEventPlugin', function() {
40
EventPluginHub = require('EventPluginHub');
41
EventPluginRegistry = require('EventPluginRegistry');
42
React = require('React');
43
- ReactEventEmitter = require('ReactEventEmitter');
43
+ ReactBrowserEventEmitter = require('ReactBrowserEventEmitter');
44
ReactTestUtils = require('ReactTestUtils');
45
46
EventPluginRegistry._resetEventPlugins();
src/browser/ui/ReactDOMComponent.js
+5
-5
@@ -24,7 +24,7 @@ var DOMProperty = require('DOMProperty');
24
var DOMPropertyOperations = require('DOMPropertyOperations');
25
var ReactBrowserComponentMixin = require('ReactBrowserComponentMixin');
26
var ReactComponent = require('ReactComponent');
27
-var ReactEventEmitter = require('ReactEventEmitter');
27
+var ReactBrowserEventEmitter = require('ReactBrowserEventEmitter');
28
var ReactMount = require('ReactMount');
29
var ReactMultiChild = require('ReactMultiChild');
30
var ReactPerf = require('ReactPerf');
@@ -35,9 +35,9 @@ var keyOf = require('keyOf');
35
var merge = require('merge');
36
var mixInto = require('mixInto');
37
38
-var deleteListener = ReactEventEmitter.deleteListener;
39
-var listenTo = ReactEventEmitter.listenTo;
40
-var registrationNameModules = ReactEventEmitter.registrationNameModules;
38
+var deleteListener = ReactBrowserEventEmitter.deleteListener;
39
+var listenTo = ReactBrowserEventEmitter.listenTo;
40
+var registrationNameModules = ReactBrowserEventEmitter.registrationNameModules;
41
42
// For quickly matching children type, to test if can be treated as content.
43
var CONTENT_TYPES = {'string': true, 'number': true};
@@ -404,7 +404,7 @@ ReactDOMComponent.Mixin = {
404
*/
405
unmountComponent: function() {
406
this.unmountChildren();
407
- ReactEventEmitter.deleteAllListeners(this._rootNodeID);
407
+ ReactBrowserEventEmitter.deleteAllListeners(this._rootNodeID);
408
ReactComponent.Mixin.unmountComponent.call(this);
409
}
410
src/browser/ui/ReactDefaultInjection.js
+3
-3
@@ -39,7 +39,7 @@ var ReactDOMInput = require('ReactDOMInput');
39
var ReactDOMOption = require('ReactDOMOption');
40
var ReactDOMSelect = require('ReactDOMSelect');
41
var ReactDOMTextarea = require('ReactDOMTextarea');
42
-var ReactEventTopLevelCallback = require('ReactEventTopLevelCallback');
42
+var ReactEventListener = require('ReactEventListener');
43
var ReactInjection = require('ReactInjection');
44
var ReactInstanceHandles = require('ReactInstanceHandles');
45
var ReactMount = require('ReactMount');
@@ -51,8 +51,8 @@ var SVGDOMPropertyConfig = require('SVGDOMPropertyConfig');
51
var createFullPageComponent = require('createFullPageComponent');
52
53
function inject() {
54
- ReactInjection.EventEmitter.injectTopLevelCallbackCreator(
55
- ReactEventTopLevelCallback
54
+ ReactInjection.EventEmitter.injectReactEventListener(
55
+ ReactEventListener
56
);
57
58
/**
src/browser/ui/ReactEventListener.js
renamed
+99
-71
@@ -13,27 +13,22 @@
13
* See the License for the specific language governing permissions and
14
* limitations under the License.
15
*
16
- * @providesModule ReactEventTopLevelCallback
16
+ * @providesModule ReactEventListener
17
* @typechecks static-only
18
*/
19
20
"use strict";
21
22
+var EventListener = require('EventListener');
23
var PooledClass = require('PooledClass');
23
-var ReactEventEmitter = require('ReactEventEmitter');
24
var ReactInstanceHandles = require('ReactInstanceHandles');
25
var ReactMount = require('ReactMount');
26
var ReactUpdates = require('ReactUpdates');
27
28
var getEventTarget = require('getEventTarget');
29
+var getUnboundedScrollPosition = require('getUnboundedScrollPosition');
30
var mixInto = require('mixInto');
31
31
-/**
32
- * @type {boolean}
33
- * @private
34
- */
35
-var _topLevelListenersEnabled = true;
36
-
32
/**
33
* Finds the parent React component of `node`.
34
*
@@ -52,13 +47,24 @@ function findParent(node) {
47
return parent;
48
}
49
55
-/**
56
- * Calls ReactEventEmitter.handleTopLevel for each node stored in bookKeeping's
57
- * ancestor list. Separated from createTopLevelCallback to avoid try/finally
58
- * deoptimization.
59
- *
60
- * @param {TopLevelCallbackBookKeeping} bookKeeping
61
- */
50
+// Used to store ancestor hierarchy in top level callback
51
+function TopLevelCallbackBookKeeping(topLevelType, nativeEvent) {
52
+ this.topLevelType = topLevelType;
53
+ this.nativeEvent = nativeEvent;
54
+ this.ancestors = [];
55
+}
56
+mixInto(TopLevelCallbackBookKeeping, {
57
+ destructor: function() {
58
+ this.topLevelType = null;
59
+ this.nativeEvent = null;
60
+ this.ancestors.length = 0;
61
+ }
62
+});
63
+PooledClass.addPoolingTo(
64
+ TopLevelCallbackBookKeeping,
65
+ PooledClass.twoArgumentPooler
66
+);
67
+
68
function handleTopLevelImpl(bookKeeping) {
69
var topLevelTarget = ReactMount.getFirstReactDOM(
70
getEventTarget(bookKeeping.nativeEvent)
@@ -77,7 +83,7 @@ function handleTopLevelImpl(bookKeeping) {
83
for (var i = 0, l = bookKeeping.ancestors.length; i < l; i++) {
84
topLevelTarget = bookKeeping.ancestors[i];
85
var topLevelTargetID = ReactMount.getID(topLevelTarget) || '';
80
- ReactEventEmitter.handleTopLevel(
86
+ ReactEventListener._handleTopLevel(
87
bookKeeping.topLevelType,
88
topLevelTarget,
89
topLevelTargetID,
@@ -86,75 +92,97 @@ function handleTopLevelImpl(bookKeeping) {
92
}
93
}
94
89
-// Used to store ancestor hierarchy in top level callback
90
-function TopLevelCallbackBookKeeping(topLevelType, nativeEvent) {
91
- this.topLevelType = topLevelType;
92
- this.nativeEvent = nativeEvent;
93
- this.ancestors = [];
95
+function scrollValueMonitor(cb) {
96
+ var scrollPosition = getUnboundedScrollPosition(window);
97
+ cb(scrollPosition);
98
}
95
-mixInto(TopLevelCallbackBookKeeping, {
96
- destructor: function() {
97
- this.topLevelType = null;
98
- this.nativeEvent = null;
99
- this.ancestors.length = 0;
100
- }
101
-});
102
-PooledClass.addPoolingTo(
103
- TopLevelCallbackBookKeeping,
104
- PooledClass.twoArgumentPooler
105
-);
99
107
-/**
108
- * Top-level callback creator used to implement event handling using delegation.
109
- * This is used via dependency injection.
110
- */
111
-var ReactEventTopLevelCallback = {
100
+var ReactEventListener = {
101
+ _enabled: true,
102
+ _handleTopLevel: null,
103
+
104
+ WINDOW_HANDLE: window,
105
+
106
+ setHandleTopLevel: function(handleTopLevel) {
107
+ ReactEventListener._handleTopLevel = handleTopLevel;
108
+ },
109
113
- /**
114
- * Sets whether or not any created callbacks should be enabled.
115
- *
116
- * @param {boolean} enabled True if callbacks should be enabled.
117
- */
110
setEnabled: function(enabled) {
119
- _topLevelListenersEnabled = !!enabled;
111
+ ReactEventListener._enabled = !!enabled;
112
},
113
114
+ isEnabled: function() {
115
+ return ReactEventListener._enabled;
116
+ },
117
+
118
+
119
/**
123
- * @return {boolean} True if callbacks are enabled.
120
+ * Traps top-level events by using event bubbling.
121
+ *
122
+ * @param {string} topLevelType Record from `EventConstants`.
123
+ * @param {string} handlerBaseName Event name (e.g. "click").
124
+ * @param {object} handle Element on which to attach listener.
125
+ * @return {object} An object with a remove function which will forcefully
126
+ * remove the listener.
127
+ * @internal
128
*/
125
- isEnabled: function() {
126
- return _topLevelListenersEnabled;
129
+ trapBubbledEvent: function(topLevelType, handlerBaseName, handle) {
130
+ var element = handle;
131
+ if (!element) {
132
+ return;
133
+ }
134
+ return EventListener.listen(
135
+ element,
136
+ handlerBaseName,
137
+ ReactEventListener.dispatchEvent.bind(null, topLevelType)
138
+ );
139
},
140
141
/**
130
- * Creates a callback for the supplied `topLevelType` that could be added as
131
- * a listener to the document. The callback computes a `topLevelTarget` which
132
- * should be the root node of a mounted React component where the listener
133
- * is attached.
142
+ * Traps a top-level event by using event capturing.
143
*
144
* @param {string} topLevelType Record from `EventConstants`.
136
- * @return {function} Callback for handling top-level events.
145
+ * @param {string} handlerBaseName Event name (e.g. "click").
146
+ * @param {object} handle Element on which to attach listener.
147
+ * @return {object} An object with a remove function which will forcefully
148
+ * remove the listener.
149
+ * @internal
150
*/
138
- createTopLevelCallback: function(topLevelType) {
139
- return function(nativeEvent) {
140
- if (!_topLevelListenersEnabled) {
141
- return;
142
- }
143
-
144
- var bookKeeping = TopLevelCallbackBookKeeping.getPooled(
145
- topLevelType,
146
- nativeEvent
147
- );
148
- try {
149
- // Event queue being processed in the same cycle allows
150
- // `preventDefault`.
151
- ReactUpdates.batchedUpdates(handleTopLevelImpl, bookKeeping);
152
- } finally {
153
- TopLevelCallbackBookKeeping.release(bookKeeping);
154
- }
155
- };
156
- }
151
+ trapCapturedEvent: function(topLevelType, handlerBaseName, handle) {
152
+ var element = handle;
153
+ if (!element) {
154
+ return;
155
+ }
156
+ return EventListener.capture(
157
+ element,
158
+ handlerBaseName,
159
+ ReactEventListener.dispatchEvent.bind(null, topLevelType)
160
+ );
161
+ },
162
+
163
+ monitorScrollValue: function(refresh) {
164
+ var callback = scrollValueMonitor.bind(null, refresh);
165
+ EventListener.listen(window, 'scroll', callback);
166
+ EventListener.listen(window, 'resize', callback);
167
+ },
168
+
169
+ dispatchEvent: function(topLevelType, nativeEvent) {
170
+ if (!ReactEventListener._enabled) {
171
+ return;
172
+ }
173
174
+ var bookKeeping = TopLevelCallbackBookKeeping.getPooled(
175
+ topLevelType,
176
+ nativeEvent
177
+ );
178
+ try {
179
+ // Event queue being processed in the same cycle allows
180
+ // `preventDefault`.
181
+ ReactUpdates.batchedUpdates(handleTopLevelImpl, bookKeeping);
182
+ } finally {
183
+ TopLevelCallbackBookKeeping.release(bookKeeping);
184
+ }
185
+ }
186
};
187
160
-module.exports = ReactEventTopLevelCallback;
188
+module.exports = ReactEventListener;
src/browser/ui/ReactInjection.js
+2
-2
@@ -24,7 +24,7 @@ var ReactComponent = require('ReactComponent');
24
var ReactCompositeComponent = require('ReactCompositeComponent');
25
var ReactDOM = require('ReactDOM');
26
var ReactEmptyComponent = require('ReactEmptyComponent');
27
-var ReactEventEmitter = require('ReactEventEmitter');
27
+var ReactBrowserEventEmitter = require('ReactBrowserEventEmitter');
28
var ReactPerf = require('ReactPerf');
29
var ReactRootIndex = require('ReactRootIndex');
30
var ReactUpdates = require('ReactUpdates');
@@ -36,7 +36,7 @@ var ReactInjection = {
36
EmptyComponent: ReactEmptyComponent.injection,
37
EventPluginHub: EventPluginHub.injection,
38
DOM: ReactDOM.injection,
39
- EventEmitter: ReactEventEmitter.injection,
39
+ EventEmitter: ReactBrowserEventEmitter.injection,
40
Perf: ReactPerf.injection,
41
RootIndex: ReactRootIndex.injection,
42
Updates: ReactUpdates.injection
src/browser/ui/ReactMount.js
+2
-2
@@ -20,7 +20,7 @@
20
21
var DOMProperty = require('DOMProperty');
22
var ReactCurrentOwner = require('ReactCurrentOwner');
23
-var ReactEventEmitter = require('ReactEventEmitter');
23
+var ReactBrowserEventEmitter = require('ReactBrowserEventEmitter');
24
var ReactInstanceHandles = require('ReactInstanceHandles');
25
var ReactPerf = require('ReactPerf');
26
@@ -278,7 +278,7 @@ var ReactMount = {
278
'_registerComponent(...): Target container is not a DOM element.'
279
);
280
281
- ReactEventEmitter.ensureScrollValueMonitoring();
281
+ ReactBrowserEventEmitter.ensureScrollValueMonitoring();
282
283
var reactRootID = ReactMount.registerContainer(container);
284
instancesByReactRootID[reactRootID] = nextComponent;
src/browser/ui/__tests__/ReactDOMComponent-test.js
+3
-3
@@ -388,7 +388,7 @@ describe('ReactDOMComponent', function() {
388
describe('unmountComponent', function() {
389
it("should clean up listeners", function() {
390
var React = require('React');
391
- var ReactEventEmitter = require('ReactEventEmitter');
391
+ var ReactBrowserEventEmitter = require('ReactBrowserEventEmitter');
392
var ReactMount = require('ReactMount');
393
394
var container = document.createElement('div');
@@ -401,13 +401,13 @@ describe('ReactDOMComponent', function() {
401
var rootNode = instance.getDOMNode();
402
var rootNodeID = ReactMount.getID(rootNode);
403
expect(
404
- ReactEventEmitter.getListener(rootNodeID, 'onClick')
404
+ ReactBrowserEventEmitter.getListener(rootNodeID, 'onClick')
405
).toBe(callback);
406
407
React.unmountComponentAtNode(container);
408
409
expect(
410
- ReactEventEmitter.getListener(rootNodeID, 'onClick')
410
+ ReactBrowserEventEmitter.getListener(rootNodeID, 'onClick')
411
).toBe(undefined);
412
});
413
});
src/browser/ui/__tests__/ReactEventListener-test.js
renamed
+36
-27
@@ -19,23 +19,27 @@
19
20
'use strict';
21
22
-require('mock-modules')
23
- .mock('ReactEventEmitter');
22
+var mocks = require('mocks');
23
+
24
25
var EVENT_TARGET_PARAM = 1;
26
27
-describe('ReactEventTopLevelCallback', function() {
27
+describe('ReactEventListener', function() {
28
var React;
29
- var ReactEventTopLevelCallback;
29
+
30
var ReactMount;
31
- var ReactEventEmitter; // mocked
31
+ var ReactEventListener;
32
+ var handleTopLevel;
33
34
beforeEach(function() {
35
require('mock-modules').dumpCache();
36
React = require('React');
36
- ReactEventTopLevelCallback = require('ReactEventTopLevelCallback');
37
+
38
ReactMount = require('ReactMount');
38
- ReactEventEmitter = require('ReactEventEmitter'); // mocked
39
+ ReactEventListener = require('ReactEventListener');
40
+
41
+ handleTopLevel = mocks.getMockFunction();
42
+ ReactEventListener._handleTopLevel = handleTopLevel;
43
});
44
45
describe('Propagation', function() {
@@ -45,15 +49,16 @@ describe('ReactEventTopLevelCallback', function() {
49
var parentContainer = document.createElement('div');
50
var parentControl = <div>Parent</div>;
51
childControl = ReactMount.renderComponent(childControl, childContainer);
48
- parentControl = ReactMount.renderComponent(parentControl, parentContainer);
52
+ parentControl =
53
+ ReactMount.renderComponent(parentControl, parentContainer);
54
parentControl.getDOMNode().appendChild(childContainer);
55
51
- var callback = ReactEventTopLevelCallback.createTopLevelCallback('test');
56
+ var callback = ReactEventListener.dispatchEvent.bind(null, 'test');
57
callback({
58
target: childControl.getDOMNode()
59
});
60
56
- var calls = ReactEventEmitter.handleTopLevel.mock.calls;
61
+ var calls = handleTopLevel.mock.calls;
62
expect(calls.length).toBe(2);
63
expect(calls[0][EVENT_TARGET_PARAM]).toBe(childControl.getDOMNode());
64
expect(calls[1][EVENT_TARGET_PARAM]).toBe(parentControl.getDOMNode());
@@ -67,17 +72,19 @@ describe('ReactEventTopLevelCallback', function() {
72
var grandParentContainer = document.createElement('div');
73
var grandParentControl = <div>Parent</div>;
74
childControl = ReactMount.renderComponent(childControl, childContainer);
70
- parentControl = ReactMount.renderComponent(parentControl, parentContainer);
71
- grandParentControl = ReactMount.renderComponent(grandParentControl, grandParentContainer);
75
+ parentControl =
76
+ ReactMount.renderComponent(parentControl, parentContainer);
77
+ grandParentControl =
78
+ ReactMount.renderComponent(grandParentControl, grandParentContainer);
79
parentControl.getDOMNode().appendChild(childContainer);
80
grandParentControl.getDOMNode().appendChild(parentContainer);
81
75
- var callback = ReactEventTopLevelCallback.createTopLevelCallback('test');
82
+ var callback = ReactEventListener.dispatchEvent.bind(null, 'test');
83
callback({
84
target: childControl.getDOMNode()
85
});
86
80
- var calls = ReactEventEmitter.handleTopLevel.mock.calls;
87
+ var calls = handleTopLevel.mock.calls;
88
expect(calls.length).toBe(3);
89
expect(calls[0][EVENT_TARGET_PARAM]).toBe(childControl.getDOMNode());
90
expect(calls[1][EVENT_TARGET_PARAM]).toBe(parentControl.getDOMNode());
@@ -91,15 +98,16 @@ describe('ReactEventTopLevelCallback', function() {
98
var parentContainer = document.createElement('div');
99
var parentControl = <div>Parent</div>;
100
childControl = ReactMount.renderComponent(childControl, childContainer);
94
- parentControl = ReactMount.renderComponent(parentControl, parentContainer);
101
+ parentControl =
102
+ ReactMount.renderComponent(parentControl, parentContainer);
103
parentControl.getDOMNode().appendChild(childContainer);
104
97
- // ReactEventEmitter.handleTopLevel might remove the target from the DOM.
98
- // Here, we have handleTopLevel remove the node when the first event
99
- // handlers are called; we'll still expect to receive a second call for
100
- // the parent control.
105
+ // ReactBrowserEventEmitter.handleTopLevel might remove the
106
+ // target from the DOM. Here, we have handleTopLevel remove the
107
+ // node when the first event handlers are called; we'll still
108
+ // expect to receive a second call for the parent control.
109
var childNode = childControl.getDOMNode();
102
- ReactEventEmitter.handleTopLevel.mockImplementation(
110
+ handleTopLevel.mockImplementation(
111
function(topLevelType, topLevelTarget, topLevelTargetID, nativeEvent) {
112
if (topLevelTarget === childNode) {
113
ReactMount.unmountComponentAtNode(childContainer);
@@ -107,12 +115,12 @@ describe('ReactEventTopLevelCallback', function() {
115
}
116
);
117
110
- var callback = ReactEventTopLevelCallback.createTopLevelCallback('test');
118
+ var callback = ReactEventListener.dispatchEvent.bind(null, 'test');
119
callback({
120
target: childNode
121
});
122
115
- var calls = ReactEventEmitter.handleTopLevel.mock.calls;
123
+ var calls = handleTopLevel.mock.calls;
124
expect(calls.length).toBe(2);
125
expect(calls[0][EVENT_TARGET_PARAM]).toBe(childNode);
126
expect(calls[1][EVENT_TARGET_PARAM]).toBe(parentControl.getDOMNode());
@@ -134,7 +142,7 @@ describe('ReactEventTopLevelCallback', function() {
142
// Suppose an event handler in each root enqueues an update to the
143
// childControl element -- the two updates should get batched together.
144
var childNode = childControl.getDOMNode();
137
- ReactEventEmitter.handleTopLevel.mockImplementation(
145
+ handleTopLevel.mockImplementation(
146
function(topLevelType, topLevelTarget, topLevelTargetID, nativeEvent) {
147
ReactMount.renderComponent(
148
<div>{topLevelTarget === childNode ? '1' : '2'}</div>,
@@ -145,12 +153,13 @@ describe('ReactEventTopLevelCallback', function() {
153
}
154
);
155
148
- var callback = ReactEventTopLevelCallback.createTopLevelCallback('test');
156
+ var callback =
157
+ ReactEventListener.dispatchEvent.bind(ReactEventListener, 'test');
158
callback({
159
target: childNode
160
});
161
153
- var calls = ReactEventEmitter.handleTopLevel.mock.calls;
162
+ var calls = handleTopLevel.mock.calls;
163
expect(calls.length).toBe(2);
164
expect(childNode.textContent).toBe('2');
165
});
@@ -173,12 +182,12 @@ describe('ReactEventTopLevelCallback', function() {
182
183
var instance = ReactMount.renderComponent(<Wrapper />, container);
184
176
- var callback = ReactEventTopLevelCallback.createTopLevelCallback('test');
185
+ var callback = ReactEventListener.dispatchEvent.bind(null, 'test');
186
callback({
187
target: instance.getInner().getDOMNode()
188
});
189
181
- var calls = ReactEventEmitter.handleTopLevel.mock.calls;
190
+ var calls = handleTopLevel.mock.calls;
191
expect(calls.length).toBe(1);
192
expect(calls[0][EVENT_TARGET_PARAM]).toBe(instance.getInner().getDOMNode());
193
});
src/browser/ui/dom/components/LocalEventTrapMixin.js
+2
-2
@@ -18,7 +18,7 @@
18
19
"use strict";
20
21
-var ReactEventEmitter = require('ReactEventEmitter');
21
+var ReactBrowserEventEmitter = require('ReactBrowserEventEmitter');
22
23
var accumulate = require('accumulate');
24
var forEachAccumulated = require('forEachAccumulated');
@@ -31,7 +31,7 @@ function remove(event) {
31
var LocalEventTrapMixin = {
32
trapBubbledEvent(topLevelType, handlerBaseName) {
33
invariant(this.isMounted(), 'Must be mounted to trap events');
34
- var listener = ReactEventEmitter.trapBubbledEvent(
34
+ var listener = ReactBrowserEventEmitter.trapBubbledEvent(
35
topLevelType,
36
handlerBaseName,
37
this.getDOMNode()
src/test/ReactTestUtils.js
+7
-8
@@ -24,7 +24,7 @@ var EventPropagators = require('EventPropagators');
24
var React = require('React');
25
var ReactDescriptor = require('ReactDescriptor');
26
var ReactDOM = require('ReactDOM');
27
-var ReactEventEmitter = require('ReactEventEmitter');
27
+var ReactBrowserEventEmitter = require('ReactBrowserEventEmitter');
28
var ReactMount = require('ReactMount');
29
var ReactTextComponent = require('ReactTextComponent');
30
var ReactUpdates = require('ReactUpdates');
@@ -259,12 +259,11 @@ var ReactTestUtils = {
259
* @param {?Event} fakeNativeEvent Fake native event to use in SyntheticEvent.
260
*/
261
simulateNativeEventOnNode: function(topLevelType, node, fakeNativeEvent) {
262
- var virtualHandler =
263
- ReactEventEmitter.TopLevelCallbackCreator.createTopLevelCallback(
264
- topLevelType
265
- );
262
fakeNativeEvent.target = node;
267
- virtualHandler(fakeNativeEvent);
263
+ ReactBrowserEventEmitter.ReactEventListener.dispatchEvent(
264
+ topLevelType,
265
+ fakeNativeEvent
266
+ );
267
},
268
269
/**
@@ -319,7 +318,7 @@ function makeSimulator(eventType) {
318
// We don't use SyntheticEvent.getPooled in order to not have to worry about
319
// properly destroying any properties assigned from `eventData` upon release
320
var event = new SyntheticEvent(
322
- ReactEventEmitter.eventNameDispatchConfigs[eventType],
321
+ ReactBrowserEventEmitter.eventNameDispatchConfigs[eventType],
322
ReactMount.getID(node),
323
fakeNativeEvent
324
);
@@ -337,7 +336,7 @@ function buildSimulators() {
336
ReactTestUtils.Simulate = {};
337
338
var eventType;
340
- for (eventType in ReactEventEmitter.eventNameDispatchConfigs) {
339
+ for (eventType in ReactBrowserEventEmitter.eventNameDispatchConfigs) {
340
/**
341
* @param {!Element || ReactDOMComponent} domComponentOrNode
342
* @param {?object} eventData Fake event data to use in SyntheticEvent.