From e1c2d02fddb40cb0b246d2c2da97b41b6ba378bc Mon Sep 17 00:00:00 2001 From: Pete Hunt Date: Sat, 14 Jun 2014 20:16:10 -0700 Subject: [PATCH] Create ReactEventListener Per our discussion friday, this creates ReactEventListener and renames variables to use the "handle" nomenclature. --- ...Emitter.js => ReactBrowserEventEmitter.js} | 191 ++++++++++-------- src/browser/ReactPutListenerQueue.js | 4 +- src/browser/ReactReconcileTransaction.js | 17 +- ...st.js => ReactBrowserEventEmitter-test.js} | 130 ++++++------ src/browser/eventPlugins/ChangeEventPlugin.js | 2 +- .../__tests__/AnalyticsEventPlugin-test.js | 4 +- src/browser/ui/ReactDOMComponent.js | 10 +- src/browser/ui/ReactDefaultInjection.js | 6 +- ...LevelCallback.js => ReactEventListener.js} | 170 +++++++++------- src/browser/ui/ReactInjection.js | 4 +- src/browser/ui/ReactMount.js | 4 +- .../ui/__tests__/ReactDOMComponent-test.js | 6 +- ...ack-test.js => ReactEventListener-test.js} | 63 +++--- .../ui/dom/components/LocalEventTrapMixin.js | 4 +- src/test/ReactTestUtils.js | 15 +- 15 files changed, 348 insertions(+), 282 deletions(-) rename src/browser/{ReactEventEmitter.js => ReactBrowserEventEmitter.js} (68%) rename src/browser/__tests__/{ReactEventEmitter-test.js => ReactBrowserEventEmitter-test.js} (76%) rename src/browser/ui/{ReactEventTopLevelCallback.js => ReactEventListener.js} (54%) rename src/browser/ui/__tests__/{ReactEventTopLevelCallback-test.js => ReactEventListener-test.js} (75%) diff --git a/src/browser/ReactEventEmitter.js b/src/browser/ReactBrowserEventEmitter.js similarity index 68% rename from src/browser/ReactEventEmitter.js rename to src/browser/ReactBrowserEventEmitter.js index 26cbfdfee3698..5d941d2a5cea0 100644 --- a/src/browser/ReactEventEmitter.js +++ b/src/browser/ReactBrowserEventEmitter.js @@ -13,29 +13,31 @@ * See the License for the specific language governing permissions and * limitations under the License. * - * @providesModule ReactEventEmitter + * @providesModule ReactBrowserEventEmitter * @typechecks static-only */ "use strict"; var EventConstants = require('EventConstants'); -var EventListener = require('EventListener'); var EventPluginHub = require('EventPluginHub'); var EventPluginRegistry = require('EventPluginRegistry'); -var ExecutionEnvironment = require('ExecutionEnvironment'); var ReactEventEmitterMixin = require('ReactEventEmitterMixin'); var ViewportMetrics = require('ViewportMetrics'); -var invariant = require('invariant'); var isEventSupported = require('isEventSupported'); var merge = require('merge'); /** - * Summary of `ReactEventEmitter` event handling: + * Summary of `ReactBrowserEventEmitter` event handling: * - * - Top-level delegation is used to trap native browser events. We normalize - * and de-duplicate events to account for browser quirks. + * - Top-level delegation is used to trap most native browser events. This + * may only occur in the main thread and is the responsibility of + * ReactEventListener, which is injected and can therefore support pluggable + * event sources. This is the only work that occurs in the main thread. + * + * - We normalize and de-duplicate events to account for browser quirks. This + * may be done in the worker thread. * * - Forward these native events (with the associated top-level type used to * trap it) to `EventPluginHub`, which in turn will ask plugins if they want @@ -48,11 +50,16 @@ var merge = require('merge'); * * Overview of React and the event system: * - * . * +------------+ . * | DOM | . + * +------------+ . + * | . + * v . + * +------------+ . + * | ReactEvent | . + * | Listener | . * +------------+ . +-----------+ - * + . +--------+|SimpleEvent| + * | . +--------+|SimpleEvent| * | . | |Plugin | * +-----|------+ . v +-----------+ * | | | . +--------------+ +------------+ @@ -139,68 +146,31 @@ function getListeningForDocument(mountAt) { } /** - * Traps top-level events by using event bubbling. + * `ReactBrowserEventEmitter` is used to attach top-level event listeners. For + * example: * - * @param {string} topLevelType Record from `EventConstants`. - * @param {string} handlerBaseName Event name (e.g. "click"). - * @param {DOMEventTarget} element Element on which to attach listener. - * @return {object} An object with a remove function which will forcefully - * remove the listener. - * @internal - */ -function trapBubbledEvent(topLevelType, handlerBaseName, element) { - return EventListener.listen( - element, - handlerBaseName, - ReactEventEmitter.TopLevelCallbackCreator.createTopLevelCallback( - topLevelType - ) - ); -} - -/** - * Traps a top-level event by using event capturing. - * - * @param {string} topLevelType Record from `EventConstants`. - * @param {string} handlerBaseName Event name (e.g. "click"). - * @param {DOMEventTarget} element Element on which to attach listener. - * @return {object} An object with a remove function which will forcefully - * remove the listener. - * @internal - */ -function trapCapturedEvent(topLevelType, handlerBaseName, element) { - return EventListener.capture( - element, - handlerBaseName, - ReactEventEmitter.TopLevelCallbackCreator.createTopLevelCallback( - topLevelType - ) - ); -} - -/** - * `ReactEventEmitter` is used to attach top-level event listeners. For example: - * - * ReactEventEmitter.putListener('myID', 'onClick', myFunction); + * ReactBrowserEventEmitter.putListener('myID', 'onClick', myFunction); * * This would allocate a "registration" of `('onClick', myFunction)` on 'myID'. * * @internal */ -var ReactEventEmitter = merge(ReactEventEmitterMixin, { +var ReactBrowserEventEmitter = merge(ReactEventEmitterMixin, { /** - * React references `ReactEventTopLevelCallback` using this property in order - * to allow dependency injection. + * Injectable event backend */ - TopLevelCallbackCreator: null, + ReactEventListener: null, injection: { /** - * @param {function} TopLevelCallbackCreator + * @param {object} ReactEventListener */ - injectTopLevelCallbackCreator: function(TopLevelCallbackCreator) { - ReactEventEmitter.TopLevelCallbackCreator = TopLevelCallbackCreator; + injectReactEventListener: function(ReactEventListener) { + ReactEventListener.setHandleTopLevel( + ReactBrowserEventEmitter.handleTopLevel + ); + ReactBrowserEventEmitter.ReactEventListener = ReactEventListener; } }, @@ -210,13 +180,8 @@ var ReactEventEmitter = merge(ReactEventEmitterMixin, { * @param {boolean} enabled True if callbacks should be enabled. */ setEnabled: function(enabled) { - invariant( - ExecutionEnvironment.canUseDOM, - 'setEnabled(...): Cannot toggle event listening in a Worker thread. ' + - 'This is likely a bug in the framework. Please report immediately.' - ); - if (ReactEventEmitter.TopLevelCallbackCreator) { - ReactEventEmitter.TopLevelCallbackCreator.setEnabled(enabled); + if (ReactBrowserEventEmitter.ReactEventListener) { + ReactBrowserEventEmitter.ReactEventListener.setEnabled(enabled); } }, @@ -225,8 +190,8 @@ var ReactEventEmitter = merge(ReactEventEmitterMixin, { */ isEnabled: function() { return !!( - ReactEventEmitter.TopLevelCallbackCreator && - ReactEventEmitter.TopLevelCallbackCreator.isEnabled() + ReactBrowserEventEmitter.ReactEventListener && + ReactBrowserEventEmitter.ReactEventListener.isEnabled() ); }, @@ -249,10 +214,10 @@ var ReactEventEmitter = merge(ReactEventEmitterMixin, { * they bubble to document. * * @param {string} registrationName Name of listener (e.g. `onClick`). - * @param {DOMDocument} contentDocument Document which owns the container + * @param {object} contentDocumentHandle Document which owns the container */ - listenTo: function(registrationName, contentDocument) { - var mountAt = contentDocument; + listenTo: function(registrationName, contentDocumentHandle) { + var mountAt = contentDocumentHandle; var isListening = getListeningForDocument(mountAt); var dependencies = EventPluginRegistry. registrationNameDependencies[registrationName]; @@ -268,42 +233,79 @@ var ReactEventEmitter = merge(ReactEventEmitterMixin, { if (topLevelType === topLevelTypes.topWheel) { if (isEventSupported('wheel')) { - trapBubbledEvent(topLevelTypes.topWheel, 'wheel', mountAt); + ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent( + topLevelTypes.topWheel, + 'wheel', + mountAt + ); } else if (isEventSupported('mousewheel')) { - trapBubbledEvent(topLevelTypes.topWheel, 'mousewheel', mountAt); + ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent( + topLevelTypes.topWheel, + 'mousewheel', + mountAt + ); } else { // Firefox needs to capture a different mouse scroll event. // @see http://www.quirksmode.org/dom/events/tests/scroll.html - trapBubbledEvent( + ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent( topLevelTypes.topWheel, 'DOMMouseScroll', - mountAt); + mountAt + ); } } else if (topLevelType === topLevelTypes.topScroll) { if (isEventSupported('scroll', true)) { - trapCapturedEvent(topLevelTypes.topScroll, 'scroll', mountAt); + ReactBrowserEventEmitter.ReactEventListener.trapCapturedEvent( + topLevelTypes.topScroll, + 'scroll', + mountAt + ); } else { - trapBubbledEvent(topLevelTypes.topScroll, 'scroll', window); + ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent( + topLevelTypes.topScroll, + 'scroll', + ReactBrowserEventEmitter.ReactEventListener.WINDOW_HANDLE + ); } } else if (topLevelType === topLevelTypes.topFocus || topLevelType === topLevelTypes.topBlur) { if (isEventSupported('focus', true)) { - trapCapturedEvent(topLevelTypes.topFocus, 'focus', mountAt); - trapCapturedEvent(topLevelTypes.topBlur, 'blur', mountAt); + ReactBrowserEventEmitter.ReactEventListener.trapCapturedEvent( + topLevelTypes.topFocus, + 'focus', + mountAt + ); + ReactBrowserEventEmitter.ReactEventListener.trapCapturedEvent( + topLevelTypes.topBlur, + 'blur', + mountAt + ); } else if (isEventSupported('focusin')) { // IE has `focusin` and `focusout` events which bubble. // @see http://www.quirksmode.org/blog/archives/2008/04/delegating_the.html - trapBubbledEvent(topLevelTypes.topFocus, 'focusin', mountAt); - trapBubbledEvent(topLevelTypes.topBlur, 'focusout', mountAt); + ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent( + topLevelTypes.topFocus, + 'focusin', + mountAt + ); + ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent( + topLevelTypes.topBlur, + 'focusout', + mountAt + ); } // to make sure blur and focus event listeners are only attached once isListening[topLevelTypes.topBlur] = true; isListening[topLevelTypes.topFocus] = true; } else if (topEventMapping.hasOwnProperty(dependency)) { - trapBubbledEvent(topLevelType, topEventMapping[dependency], mountAt); + ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent( + topLevelType, + topEventMapping[dependency], + mountAt + ); } isListening[dependency] = true; @@ -311,6 +313,22 @@ var ReactEventEmitter = merge(ReactEventEmitterMixin, { } }, + trapBubbledEvent: function(topLevelType, handlerBaseName, handle) { + return ReactBrowserEventEmitter.ReactEventListener.trapBubbledEvent( + topLevelType, + handlerBaseName, + handle + ); + }, + + trapCapturedEvent: function(topLevelType, handlerBaseName, handle) { + return ReactBrowserEventEmitter.ReactEventListener.trapCapturedEvent( + topLevelType, + handlerBaseName, + handle + ); + }, + /** * Listens to window scroll and resize events. We cache scroll values so that * application code can access them without triggering reflows. @@ -322,8 +340,7 @@ var ReactEventEmitter = merge(ReactEventEmitterMixin, { ensureScrollValueMonitoring: function(){ if (!isMonitoringScrollValue) { var refresh = ViewportMetrics.refreshScrollValues; - EventListener.listen(window, 'scroll', refresh); - EventListener.listen(window, 'resize', refresh); + ReactBrowserEventEmitter.ReactEventListener.monitorScrollValue(refresh); isMonitoringScrollValue = true; } }, @@ -338,12 +355,8 @@ var ReactEventEmitter = merge(ReactEventEmitterMixin, { deleteListener: EventPluginHub.deleteListener, - deleteAllListeners: EventPluginHub.deleteAllListeners, - - trapBubbledEvent: trapBubbledEvent, - - trapCapturedEvent: trapCapturedEvent + deleteAllListeners: EventPluginHub.deleteAllListeners }); -module.exports = ReactEventEmitter; +module.exports = ReactBrowserEventEmitter; diff --git a/src/browser/ReactPutListenerQueue.js b/src/browser/ReactPutListenerQueue.js index e81966e261817..0e2500e499632 100644 --- a/src/browser/ReactPutListenerQueue.js +++ b/src/browser/ReactPutListenerQueue.js @@ -19,7 +19,7 @@ "use strict"; var PooledClass = require('PooledClass'); -var ReactEventEmitter = require('ReactEventEmitter'); +var ReactBrowserEventEmitter = require('ReactBrowserEventEmitter'); var mixInto = require('mixInto'); @@ -39,7 +39,7 @@ mixInto(ReactPutListenerQueue, { putListeners: function() { for (var i = 0; i < this.listenersToPut.length; i++) { var listenerToPut = this.listenersToPut[i]; - ReactEventEmitter.putListener( + ReactBrowserEventEmitter.putListener( listenerToPut.rootNodeID, listenerToPut.propKey, listenerToPut.propValue diff --git a/src/browser/ReactReconcileTransaction.js b/src/browser/ReactReconcileTransaction.js index 87f726884a79f..e4ac1ffa020b4 100644 --- a/src/browser/ReactReconcileTransaction.js +++ b/src/browser/ReactReconcileTransaction.js @@ -21,7 +21,7 @@ var CallbackQueue = require('CallbackQueue'); var PooledClass = require('PooledClass'); -var ReactEventEmitter = require('ReactEventEmitter'); +var ReactBrowserEventEmitter = require('ReactBrowserEventEmitter'); var ReactInputSelection = require('ReactInputSelection'); var ReactPutListenerQueue = require('ReactPutListenerQueue'); var Transaction = require('Transaction'); @@ -50,21 +50,22 @@ var SELECTION_RESTORATION = { */ var EVENT_SUPPRESSION = { /** - * @return {boolean} The enabled status of `ReactEventEmitter` before the - * reconciliation. + * @return {boolean} The enabled status of `ReactBrowserEventEmitter` before + * the reconciliation. */ initialize: function() { - var currentlyEnabled = ReactEventEmitter.isEnabled(); - ReactEventEmitter.setEnabled(false); + var currentlyEnabled = ReactBrowserEventEmitter.isEnabled(); + ReactBrowserEventEmitter.setEnabled(false); return currentlyEnabled; }, /** - * @param {boolean} previouslyEnabled Enabled status of `ReactEventEmitter` - * before the reconciliation occured. `close` restores the previous value. + * @param {boolean} previouslyEnabled Enabled status of + * `ReactBrowserEventEmitter` before the reconciliation occured. `close` + * restores the previous value. */ close: function(previouslyEnabled) { - ReactEventEmitter.setEnabled(previouslyEnabled); + ReactBrowserEventEmitter.setEnabled(previouslyEnabled); } }; diff --git a/src/browser/__tests__/ReactEventEmitter-test.js b/src/browser/__tests__/ReactBrowserEventEmitter-test.js similarity index 76% rename from src/browser/__tests__/ReactEventEmitter-test.js rename to src/browser/__tests__/ReactBrowserEventEmitter-test.js index 4e2a03d3b7c09..68e2ba0c9c786 100644 --- a/src/browser/__tests__/ReactEventEmitter-test.js +++ b/src/browser/__tests__/ReactBrowserEventEmitter-test.js @@ -21,7 +21,7 @@ require('mock-modules') .dontMock('EventPluginHub') .dontMock('ReactMount') - .dontMock('ReactEventEmitter') + .dontMock('ReactBrowserEventEmitter') .dontMock('ReactInstanceHandles') .dontMock('EventPluginHub') .dontMock('TapEventPlugin') @@ -42,7 +42,7 @@ var setID = function(el, id) { var oldGetNode = ReactMount.getNode; var EventPluginHub; -var ReactEventEmitter; +var ReactBrowserEventEmitter; var ReactTestUtils; var TapEventPlugin; var EventListener; @@ -67,12 +67,12 @@ var ON_CHANGE_KEY = keyOf({onChange: null}); /** - * Since `ReactEventEmitter` is fairly well separated from the DOM, we can test - * almost all of `ReactEventEmitter` without ever rendering anything in the DOM. - * As long as we provide IDs that follow `React's` conventional id namespace - * hierarchy. The only reason why we create these DOM nodes, is so that when we - * feed them into `ReactEventEmitter` (through `ReactTestUtils`), the event - * handlers may receive a DOM node to inspect. + * Since `ReactBrowserEventEmitter` is fairly well separated from the DOM, we + * can test almost all of `ReactBrowserEventEmitter` without ever rendering + * anything in the DOM. As long as we provide IDs that follow `React's` + * conventional id namespace hierarchy. The only reason why we create these DOM + * nodes is so that when we feed them into `ReactBrowserEventEmitter` (through + * `ReactTestUtils`), the event handlers may receive a DOM node to inspect. */ var CHILD = document.createElement('div'); var PARENT = document.createElement('div'); @@ -82,14 +82,15 @@ setID(PARENT, '.0.0.0'); setID(GRANDPARENT, '.0.0'); function registerSimpleTestHandler() { - ReactEventEmitter.putListener(getID(CHILD), ON_CLICK_KEY, LISTENER); - var listener = ReactEventEmitter.getListener(getID(CHILD), ON_CLICK_KEY); + ReactBrowserEventEmitter.putListener(getID(CHILD), ON_CLICK_KEY, LISTENER); + var listener = + ReactBrowserEventEmitter.getListener(getID(CHILD), ON_CLICK_KEY); expect(listener).toEqual(LISTENER); - return ReactEventEmitter.getListener(getID(CHILD), ON_CLICK_KEY); + return ReactBrowserEventEmitter.getListener(getID(CHILD), ON_CLICK_KEY); } -describe('ReactEventEmitter', function() { +describe('ReactBrowserEventEmitter', function() { beforeEach(function() { require('mock-modules').dumpCache(); LISTENER.mockClear(); @@ -97,7 +98,7 @@ describe('ReactEventEmitter', function() { TapEventPlugin = require('TapEventPlugin'); ReactMount = require('ReactMount'); EventListener = require('EventListener'); - ReactEventEmitter = require('ReactEventEmitter'); + ReactBrowserEventEmitter = require('ReactBrowserEventEmitter'); ReactTestUtils = require('ReactTestUtils'); ReactMount.getNode = function(id) { return idToNode[id]; @@ -115,20 +116,23 @@ describe('ReactEventEmitter', function() { it('should store a listener correctly', function() { registerSimpleTestHandler(); - var listener = ReactEventEmitter.getListener(getID(CHILD), ON_CLICK_KEY); + var listener = + ReactBrowserEventEmitter.getListener(getID(CHILD), ON_CLICK_KEY); expect(listener).toBe(LISTENER); }); it('should retrieve a listener correctly', function() { registerSimpleTestHandler(); - var listener = ReactEventEmitter.getListener(getID(CHILD), ON_CLICK_KEY); + var listener = + ReactBrowserEventEmitter.getListener(getID(CHILD), ON_CLICK_KEY); expect(listener).toEqual(LISTENER); }); it('should clear all handlers when asked to', function() { registerSimpleTestHandler(); - ReactEventEmitter.deleteAllListeners(getID(CHILD)); - var listener = ReactEventEmitter.getListener(getID(CHILD), ON_CLICK_KEY); + ReactBrowserEventEmitter.deleteAllListeners(getID(CHILD)); + var listener = + ReactBrowserEventEmitter.getListener(getID(CHILD), ON_CLICK_KEY); expect(listener).toBe(undefined); }); @@ -138,28 +142,31 @@ describe('ReactEventEmitter', function() { expect(LISTENER.mock.calls.length).toBe(1); }); - it('should not invoke handlers if ReactEventEmitter is disabled', function() { - registerSimpleTestHandler(); - ReactEventEmitter.setEnabled(false); - ReactTestUtils.SimulateNative.click(CHILD); - expect(LISTENER.mock.calls.length).toBe(0); - ReactEventEmitter.setEnabled(true); - ReactTestUtils.SimulateNative.click(CHILD); - expect(LISTENER.mock.calls.length).toBe(1); - }); + it( + 'should not invoke handlers if ReactBrowserEventEmitter is disabled', + function() { + registerSimpleTestHandler(); + ReactBrowserEventEmitter.setEnabled(false); + ReactTestUtils.SimulateNative.click(CHILD); + expect(LISTENER.mock.calls.length).toBe(0); + ReactBrowserEventEmitter.setEnabled(true); + ReactTestUtils.SimulateNative.click(CHILD); + expect(LISTENER.mock.calls.length).toBe(1); + } + ); it('should bubble simply', function() { - ReactEventEmitter.putListener( + ReactBrowserEventEmitter.putListener( getID(CHILD), ON_CLICK_KEY, recordID.bind(null, getID(CHILD)) ); - ReactEventEmitter.putListener( + ReactBrowserEventEmitter.putListener( getID(PARENT), ON_CLICK_KEY, recordID.bind(null, getID(PARENT)) ); - ReactEventEmitter.putListener( + ReactBrowserEventEmitter.putListener( getID(GRANDPARENT), ON_CLICK_KEY, recordID.bind(null, getID(GRANDPARENT)) @@ -172,7 +179,7 @@ describe('ReactEventEmitter', function() { }); it('should set currentTarget', function() { - ReactEventEmitter.putListener( + ReactBrowserEventEmitter.putListener( getID(CHILD), ON_CLICK_KEY, function(event) { @@ -180,7 +187,7 @@ describe('ReactEventEmitter', function() { expect(event.currentTarget).toBe(CHILD); } ); - ReactEventEmitter.putListener( + ReactBrowserEventEmitter.putListener( getID(PARENT), ON_CLICK_KEY, function(event) { @@ -188,7 +195,7 @@ describe('ReactEventEmitter', function() { expect(event.currentTarget).toBe(PARENT); } ); - ReactEventEmitter.putListener( + ReactBrowserEventEmitter.putListener( getID(GRANDPARENT), ON_CLICK_KEY, function(event) { @@ -204,17 +211,17 @@ describe('ReactEventEmitter', function() { }); it('should support stopPropagation()', function() { - ReactEventEmitter.putListener( + ReactBrowserEventEmitter.putListener( getID(CHILD), ON_CLICK_KEY, recordID.bind(null, getID(CHILD)) ); - ReactEventEmitter.putListener( + ReactBrowserEventEmitter.putListener( getID(PARENT), ON_CLICK_KEY, recordIDAndStopPropagation.bind(null, getID(PARENT)) ); - ReactEventEmitter.putListener( + ReactBrowserEventEmitter.putListener( getID(GRANDPARENT), ON_CLICK_KEY, recordID.bind(null, getID(GRANDPARENT)) @@ -226,17 +233,17 @@ describe('ReactEventEmitter', function() { }); it('should stop after first dispatch if stopPropagation', function() { - ReactEventEmitter.putListener( + ReactBrowserEventEmitter.putListener( getID(CHILD), ON_CLICK_KEY, recordIDAndStopPropagation.bind(null, getID(CHILD)) ); - ReactEventEmitter.putListener( + ReactBrowserEventEmitter.putListener( getID(PARENT), ON_CLICK_KEY, recordID.bind(null, getID(PARENT)) ); - ReactEventEmitter.putListener( + ReactBrowserEventEmitter.putListener( getID(GRANDPARENT), ON_CLICK_KEY, recordID.bind(null, getID(GRANDPARENT)) @@ -247,17 +254,17 @@ describe('ReactEventEmitter', function() { }); it('should stopPropagation if false is returned', function() { - ReactEventEmitter.putListener( + ReactBrowserEventEmitter.putListener( getID(CHILD), ON_CLICK_KEY, recordIDAndReturnFalse.bind(null, getID(CHILD)) ); - ReactEventEmitter.putListener( + ReactBrowserEventEmitter.putListener( getID(PARENT), ON_CLICK_KEY, recordID.bind(null, getID(PARENT)) ); - ReactEventEmitter.putListener( + ReactBrowserEventEmitter.putListener( getID(GRANDPARENT), ON_CLICK_KEY, recordID.bind(null, getID(GRANDPARENT)) @@ -279,10 +286,14 @@ describe('ReactEventEmitter', function() { it('should invoke handlers that were removed while bubbling', function() { var handleParentClick = mocks.getMockFunction(); var handleChildClick = function(event) { - ReactEventEmitter.deleteAllListeners(getID(PARENT)); + ReactBrowserEventEmitter.deleteAllListeners(getID(PARENT)); }; - ReactEventEmitter.putListener(getID(CHILD), ON_CLICK_KEY, handleChildClick); - ReactEventEmitter.putListener( + ReactBrowserEventEmitter.putListener( + getID(CHILD), + ON_CLICK_KEY, + handleChildClick + ); + ReactBrowserEventEmitter.putListener( getID(PARENT), ON_CLICK_KEY, handleParentClick @@ -294,19 +305,23 @@ describe('ReactEventEmitter', function() { it('should not invoke newly inserted handlers while bubbling', function() { var handleParentClick = mocks.getMockFunction(); var handleChildClick = function(event) { - ReactEventEmitter.putListener( + ReactBrowserEventEmitter.putListener( getID(PARENT), ON_CLICK_KEY, handleParentClick ); }; - ReactEventEmitter.putListener(getID(CHILD), ON_CLICK_KEY, handleChildClick); + ReactBrowserEventEmitter.putListener( + getID(CHILD), + ON_CLICK_KEY, + handleChildClick + ); ReactTestUtils.Simulate.click(CHILD); expect(handleParentClick.mock.calls.length).toBe(0); }); it('should infer onTouchTap from a touchStart/End', function() { - ReactEventEmitter.putListener( + ReactBrowserEventEmitter.putListener( getID(CHILD), ON_TOUCH_TAP_KEY, recordID.bind(null, getID(CHILD)) @@ -324,7 +339,7 @@ describe('ReactEventEmitter', function() { }); it('should infer onTouchTap from when dragging below threshold', function() { - ReactEventEmitter.putListener( + ReactBrowserEventEmitter.putListener( getID(CHILD), ON_TOUCH_TAP_KEY, recordID.bind(null, getID(CHILD)) @@ -342,7 +357,7 @@ describe('ReactEventEmitter', function() { }); it('should not onTouchTap from when dragging beyond threshold', function() { - ReactEventEmitter.putListener( + ReactBrowserEventEmitter.putListener( getID(CHILD), ON_TOUCH_TAP_KEY, recordID.bind(null, getID(CHILD)) @@ -360,15 +375,15 @@ describe('ReactEventEmitter', function() { it('should listen to events only once', function() { spyOn(EventListener, 'listen'); - ReactEventEmitter.listenTo(ON_CLICK_KEY, document); - ReactEventEmitter.listenTo(ON_CLICK_KEY, document); + ReactBrowserEventEmitter.listenTo(ON_CLICK_KEY, document); + ReactBrowserEventEmitter.listenTo(ON_CLICK_KEY, document); expect(EventListener.listen.callCount).toBe(1); }); it('should work with event plugins without dependencies', function() { spyOn(EventListener, 'listen'); - ReactEventEmitter.listenTo(ON_CLICK_KEY, document); + ReactBrowserEventEmitter.listenTo(ON_CLICK_KEY, document); expect(EventListener.listen.argsForCall[0][1]).toBe('click'); }); @@ -377,7 +392,7 @@ describe('ReactEventEmitter', function() { spyOn(EventListener, 'listen'); spyOn(EventListener, 'capture'); - ReactEventEmitter.listenTo(ON_CHANGE_KEY, document); + ReactBrowserEventEmitter.listenTo(ON_CHANGE_KEY, document); var setEventListeners = []; var listenCalls = EventListener.listen.argsForCall; @@ -389,7 +404,8 @@ describe('ReactEventEmitter', function() { setEventListeners.push(captureCalls[i][1]); } - var module = ReactEventEmitter.registrationNameModules[ON_CHANGE_KEY]; + var module = + ReactBrowserEventEmitter.registrationNameModules[ON_CHANGE_KEY]; var dependencies = module.eventTypes.change.dependencies; expect(setEventListeners.length).toEqual(dependencies.length); @@ -399,17 +415,17 @@ describe('ReactEventEmitter', function() { }); it('should bubble onTouchTap', function() { - ReactEventEmitter.putListener( + ReactBrowserEventEmitter.putListener( getID(CHILD), ON_TOUCH_TAP_KEY, recordID.bind(null, getID(CHILD)) ); - ReactEventEmitter.putListener( + ReactBrowserEventEmitter.putListener( getID(PARENT), ON_TOUCH_TAP_KEY, recordID.bind(null, getID(PARENT)) ); - ReactEventEmitter.putListener( + ReactBrowserEventEmitter.putListener( getID(GRANDPARENT), ON_TOUCH_TAP_KEY, recordID.bind(null, getID(GRANDPARENT)) diff --git a/src/browser/eventPlugins/ChangeEventPlugin.js b/src/browser/eventPlugins/ChangeEventPlugin.js index 4a3d7aaa08262..499a882802838 100644 --- a/src/browser/eventPlugins/ChangeEventPlugin.js +++ b/src/browser/eventPlugins/ChangeEventPlugin.js @@ -85,7 +85,7 @@ function manualDispatchChangeEvent(nativeEvent) { EventPropagators.accumulateTwoPhaseDispatches(event); // If change and propertychange bubbled, we'd just bind to it like all the - // other events and have it go through ReactEventTopLevelCallback. Since it + // other events and have it go through ReactBrowserEventEmitter. Since it // doesn't, we manually listen for the events and so we have to enqueue and // process the abstract event manually. // diff --git a/src/browser/eventPlugins/__tests__/AnalyticsEventPlugin-test.js b/src/browser/eventPlugins/__tests__/AnalyticsEventPlugin-test.js index 770d73db6ec02..68fe7261f1f72 100644 --- a/src/browser/eventPlugins/__tests__/AnalyticsEventPlugin-test.js +++ b/src/browser/eventPlugins/__tests__/AnalyticsEventPlugin-test.js @@ -26,7 +26,7 @@ describe('AnalyticsEventPlugin', function() { var EventPluginHub; var EventPluginRegistry; var React; - var ReactEventEmitter; + var ReactBrowserEventEmitter; var ReactTestUtils; var DefaultEventPluginOrder; @@ -40,7 +40,7 @@ describe('AnalyticsEventPlugin', function() { EventPluginHub = require('EventPluginHub'); EventPluginRegistry = require('EventPluginRegistry'); React = require('React'); - ReactEventEmitter = require('ReactEventEmitter'); + ReactBrowserEventEmitter = require('ReactBrowserEventEmitter'); ReactTestUtils = require('ReactTestUtils'); EventPluginRegistry._resetEventPlugins(); diff --git a/src/browser/ui/ReactDOMComponent.js b/src/browser/ui/ReactDOMComponent.js index b9719b3686015..70c0ceb9e0a8a 100644 --- a/src/browser/ui/ReactDOMComponent.js +++ b/src/browser/ui/ReactDOMComponent.js @@ -24,7 +24,7 @@ var DOMProperty = require('DOMProperty'); var DOMPropertyOperations = require('DOMPropertyOperations'); var ReactBrowserComponentMixin = require('ReactBrowserComponentMixin'); var ReactComponent = require('ReactComponent'); -var ReactEventEmitter = require('ReactEventEmitter'); +var ReactBrowserEventEmitter = require('ReactBrowserEventEmitter'); var ReactMount = require('ReactMount'); var ReactMultiChild = require('ReactMultiChild'); var ReactPerf = require('ReactPerf'); @@ -35,9 +35,9 @@ var keyOf = require('keyOf'); var merge = require('merge'); var mixInto = require('mixInto'); -var deleteListener = ReactEventEmitter.deleteListener; -var listenTo = ReactEventEmitter.listenTo; -var registrationNameModules = ReactEventEmitter.registrationNameModules; +var deleteListener = ReactBrowserEventEmitter.deleteListener; +var listenTo = ReactBrowserEventEmitter.listenTo; +var registrationNameModules = ReactBrowserEventEmitter.registrationNameModules; // For quickly matching children type, to test if can be treated as content. var CONTENT_TYPES = {'string': true, 'number': true}; @@ -404,7 +404,7 @@ ReactDOMComponent.Mixin = { */ unmountComponent: function() { this.unmountChildren(); - ReactEventEmitter.deleteAllListeners(this._rootNodeID); + ReactBrowserEventEmitter.deleteAllListeners(this._rootNodeID); ReactComponent.Mixin.unmountComponent.call(this); } diff --git a/src/browser/ui/ReactDefaultInjection.js b/src/browser/ui/ReactDefaultInjection.js index c74769e881e1b..abdbad93fa518 100644 --- a/src/browser/ui/ReactDefaultInjection.js +++ b/src/browser/ui/ReactDefaultInjection.js @@ -39,7 +39,7 @@ var ReactDOMInput = require('ReactDOMInput'); var ReactDOMOption = require('ReactDOMOption'); var ReactDOMSelect = require('ReactDOMSelect'); var ReactDOMTextarea = require('ReactDOMTextarea'); -var ReactEventTopLevelCallback = require('ReactEventTopLevelCallback'); +var ReactEventListener = require('ReactEventListener'); var ReactInjection = require('ReactInjection'); var ReactInstanceHandles = require('ReactInstanceHandles'); var ReactMount = require('ReactMount'); @@ -51,8 +51,8 @@ var SVGDOMPropertyConfig = require('SVGDOMPropertyConfig'); var createFullPageComponent = require('createFullPageComponent'); function inject() { - ReactInjection.EventEmitter.injectTopLevelCallbackCreator( - ReactEventTopLevelCallback + ReactInjection.EventEmitter.injectReactEventListener( + ReactEventListener ); /** diff --git a/src/browser/ui/ReactEventTopLevelCallback.js b/src/browser/ui/ReactEventListener.js similarity index 54% rename from src/browser/ui/ReactEventTopLevelCallback.js rename to src/browser/ui/ReactEventListener.js index aef2eef09fa62..591e91ea60cd0 100644 --- a/src/browser/ui/ReactEventTopLevelCallback.js +++ b/src/browser/ui/ReactEventListener.js @@ -13,27 +13,22 @@ * See the License for the specific language governing permissions and * limitations under the License. * - * @providesModule ReactEventTopLevelCallback + * @providesModule ReactEventListener * @typechecks static-only */ "use strict"; +var EventListener = require('EventListener'); var PooledClass = require('PooledClass'); -var ReactEventEmitter = require('ReactEventEmitter'); var ReactInstanceHandles = require('ReactInstanceHandles'); var ReactMount = require('ReactMount'); var ReactUpdates = require('ReactUpdates'); var getEventTarget = require('getEventTarget'); +var getUnboundedScrollPosition = require('getUnboundedScrollPosition'); var mixInto = require('mixInto'); -/** - * @type {boolean} - * @private - */ -var _topLevelListenersEnabled = true; - /** * Finds the parent React component of `node`. * @@ -52,13 +47,24 @@ function findParent(node) { return parent; } -/** - * Calls ReactEventEmitter.handleTopLevel for each node stored in bookKeeping's - * ancestor list. Separated from createTopLevelCallback to avoid try/finally - * deoptimization. - * - * @param {TopLevelCallbackBookKeeping} bookKeeping - */ +// Used to store ancestor hierarchy in top level callback +function TopLevelCallbackBookKeeping(topLevelType, nativeEvent) { + this.topLevelType = topLevelType; + this.nativeEvent = nativeEvent; + this.ancestors = []; +} +mixInto(TopLevelCallbackBookKeeping, { + destructor: function() { + this.topLevelType = null; + this.nativeEvent = null; + this.ancestors.length = 0; + } +}); +PooledClass.addPoolingTo( + TopLevelCallbackBookKeeping, + PooledClass.twoArgumentPooler +); + function handleTopLevelImpl(bookKeeping) { var topLevelTarget = ReactMount.getFirstReactDOM( getEventTarget(bookKeeping.nativeEvent) @@ -77,7 +83,7 @@ function handleTopLevelImpl(bookKeeping) { for (var i = 0, l = bookKeeping.ancestors.length; i < l; i++) { topLevelTarget = bookKeeping.ancestors[i]; var topLevelTargetID = ReactMount.getID(topLevelTarget) || ''; - ReactEventEmitter.handleTopLevel( + ReactEventListener._handleTopLevel( bookKeeping.topLevelType, topLevelTarget, topLevelTargetID, @@ -86,75 +92,97 @@ function handleTopLevelImpl(bookKeeping) { } } -// Used to store ancestor hierarchy in top level callback -function TopLevelCallbackBookKeeping(topLevelType, nativeEvent) { - this.topLevelType = topLevelType; - this.nativeEvent = nativeEvent; - this.ancestors = []; +function scrollValueMonitor(cb) { + var scrollPosition = getUnboundedScrollPosition(window); + cb(scrollPosition); } -mixInto(TopLevelCallbackBookKeeping, { - destructor: function() { - this.topLevelType = null; - this.nativeEvent = null; - this.ancestors.length = 0; - } -}); -PooledClass.addPoolingTo( - TopLevelCallbackBookKeeping, - PooledClass.twoArgumentPooler -); -/** - * Top-level callback creator used to implement event handling using delegation. - * This is used via dependency injection. - */ -var ReactEventTopLevelCallback = { +var ReactEventListener = { + _enabled: true, + _handleTopLevel: null, + + WINDOW_HANDLE: window, + + setHandleTopLevel: function(handleTopLevel) { + ReactEventListener._handleTopLevel = handleTopLevel; + }, - /** - * Sets whether or not any created callbacks should be enabled. - * - * @param {boolean} enabled True if callbacks should be enabled. - */ setEnabled: function(enabled) { - _topLevelListenersEnabled = !!enabled; + ReactEventListener._enabled = !!enabled; }, + isEnabled: function() { + return ReactEventListener._enabled; + }, + + /** - * @return {boolean} True if callbacks are enabled. + * Traps top-level events by using event bubbling. + * + * @param {string} topLevelType Record from `EventConstants`. + * @param {string} handlerBaseName Event name (e.g. "click"). + * @param {object} handle Element on which to attach listener. + * @return {object} An object with a remove function which will forcefully + * remove the listener. + * @internal */ - isEnabled: function() { - return _topLevelListenersEnabled; + trapBubbledEvent: function(topLevelType, handlerBaseName, handle) { + var element = handle; + if (!element) { + return; + } + return EventListener.listen( + element, + handlerBaseName, + ReactEventListener.dispatchEvent.bind(null, topLevelType) + ); }, /** - * Creates a callback for the supplied `topLevelType` that could be added as - * a listener to the document. The callback computes a `topLevelTarget` which - * should be the root node of a mounted React component where the listener - * is attached. + * Traps a top-level event by using event capturing. * * @param {string} topLevelType Record from `EventConstants`. - * @return {function} Callback for handling top-level events. + * @param {string} handlerBaseName Event name (e.g. "click"). + * @param {object} handle Element on which to attach listener. + * @return {object} An object with a remove function which will forcefully + * remove the listener. + * @internal */ - createTopLevelCallback: function(topLevelType) { - return function(nativeEvent) { - if (!_topLevelListenersEnabled) { - return; - } - - var bookKeeping = TopLevelCallbackBookKeeping.getPooled( - topLevelType, - nativeEvent - ); - try { - // Event queue being processed in the same cycle allows - // `preventDefault`. - ReactUpdates.batchedUpdates(handleTopLevelImpl, bookKeeping); - } finally { - TopLevelCallbackBookKeeping.release(bookKeeping); - } - }; - } + trapCapturedEvent: function(topLevelType, handlerBaseName, handle) { + var element = handle; + if (!element) { + return; + } + return EventListener.capture( + element, + handlerBaseName, + ReactEventListener.dispatchEvent.bind(null, topLevelType) + ); + }, + + monitorScrollValue: function(refresh) { + var callback = scrollValueMonitor.bind(null, refresh); + EventListener.listen(window, 'scroll', callback); + EventListener.listen(window, 'resize', callback); + }, + + dispatchEvent: function(topLevelType, nativeEvent) { + if (!ReactEventListener._enabled) { + return; + } + var bookKeeping = TopLevelCallbackBookKeeping.getPooled( + topLevelType, + nativeEvent + ); + try { + // Event queue being processed in the same cycle allows + // `preventDefault`. + ReactUpdates.batchedUpdates(handleTopLevelImpl, bookKeeping); + } finally { + TopLevelCallbackBookKeeping.release(bookKeeping); + } + } }; -module.exports = ReactEventTopLevelCallback; +module.exports = ReactEventListener; diff --git a/src/browser/ui/ReactInjection.js b/src/browser/ui/ReactInjection.js index c51a2128003cc..b1132388ddaeb 100644 --- a/src/browser/ui/ReactInjection.js +++ b/src/browser/ui/ReactInjection.js @@ -24,7 +24,7 @@ var ReactComponent = require('ReactComponent'); var ReactCompositeComponent = require('ReactCompositeComponent'); var ReactDOM = require('ReactDOM'); var ReactEmptyComponent = require('ReactEmptyComponent'); -var ReactEventEmitter = require('ReactEventEmitter'); +var ReactBrowserEventEmitter = require('ReactBrowserEventEmitter'); var ReactPerf = require('ReactPerf'); var ReactRootIndex = require('ReactRootIndex'); var ReactUpdates = require('ReactUpdates'); @@ -36,7 +36,7 @@ var ReactInjection = { EmptyComponent: ReactEmptyComponent.injection, EventPluginHub: EventPluginHub.injection, DOM: ReactDOM.injection, - EventEmitter: ReactEventEmitter.injection, + EventEmitter: ReactBrowserEventEmitter.injection, Perf: ReactPerf.injection, RootIndex: ReactRootIndex.injection, Updates: ReactUpdates.injection diff --git a/src/browser/ui/ReactMount.js b/src/browser/ui/ReactMount.js index 8eeed35dc7357..a07a837a59b96 100644 --- a/src/browser/ui/ReactMount.js +++ b/src/browser/ui/ReactMount.js @@ -20,7 +20,7 @@ var DOMProperty = require('DOMProperty'); var ReactCurrentOwner = require('ReactCurrentOwner'); -var ReactEventEmitter = require('ReactEventEmitter'); +var ReactBrowserEventEmitter = require('ReactBrowserEventEmitter'); var ReactInstanceHandles = require('ReactInstanceHandles'); var ReactPerf = require('ReactPerf'); @@ -278,7 +278,7 @@ var ReactMount = { '_registerComponent(...): Target container is not a DOM element.' ); - ReactEventEmitter.ensureScrollValueMonitoring(); + ReactBrowserEventEmitter.ensureScrollValueMonitoring(); var reactRootID = ReactMount.registerContainer(container); instancesByReactRootID[reactRootID] = nextComponent; diff --git a/src/browser/ui/__tests__/ReactDOMComponent-test.js b/src/browser/ui/__tests__/ReactDOMComponent-test.js index 2ed703428d291..5bfdc194faf2f 100644 --- a/src/browser/ui/__tests__/ReactDOMComponent-test.js +++ b/src/browser/ui/__tests__/ReactDOMComponent-test.js @@ -388,7 +388,7 @@ describe('ReactDOMComponent', function() { describe('unmountComponent', function() { it("should clean up listeners", function() { var React = require('React'); - var ReactEventEmitter = require('ReactEventEmitter'); + var ReactBrowserEventEmitter = require('ReactBrowserEventEmitter'); var ReactMount = require('ReactMount'); var container = document.createElement('div'); @@ -401,13 +401,13 @@ describe('ReactDOMComponent', function() { var rootNode = instance.getDOMNode(); var rootNodeID = ReactMount.getID(rootNode); expect( - ReactEventEmitter.getListener(rootNodeID, 'onClick') + ReactBrowserEventEmitter.getListener(rootNodeID, 'onClick') ).toBe(callback); React.unmountComponentAtNode(container); expect( - ReactEventEmitter.getListener(rootNodeID, 'onClick') + ReactBrowserEventEmitter.getListener(rootNodeID, 'onClick') ).toBe(undefined); }); }); diff --git a/src/browser/ui/__tests__/ReactEventTopLevelCallback-test.js b/src/browser/ui/__tests__/ReactEventListener-test.js similarity index 75% rename from src/browser/ui/__tests__/ReactEventTopLevelCallback-test.js rename to src/browser/ui/__tests__/ReactEventListener-test.js index d077a670628d5..300f93ecf504d 100644 --- a/src/browser/ui/__tests__/ReactEventTopLevelCallback-test.js +++ b/src/browser/ui/__tests__/ReactEventListener-test.js @@ -19,23 +19,27 @@ 'use strict'; -require('mock-modules') - .mock('ReactEventEmitter'); +var mocks = require('mocks'); + var EVENT_TARGET_PARAM = 1; -describe('ReactEventTopLevelCallback', function() { +describe('ReactEventListener', function() { var React; - var ReactEventTopLevelCallback; + var ReactMount; - var ReactEventEmitter; // mocked + var ReactEventListener; + var handleTopLevel; beforeEach(function() { require('mock-modules').dumpCache(); React = require('React'); - ReactEventTopLevelCallback = require('ReactEventTopLevelCallback'); + ReactMount = require('ReactMount'); - ReactEventEmitter = require('ReactEventEmitter'); // mocked + ReactEventListener = require('ReactEventListener'); + + handleTopLevel = mocks.getMockFunction(); + ReactEventListener._handleTopLevel = handleTopLevel; }); describe('Propagation', function() { @@ -45,15 +49,16 @@ describe('ReactEventTopLevelCallback', function() { var parentContainer = document.createElement('div'); var parentControl =
Parent
; childControl = ReactMount.renderComponent(childControl, childContainer); - parentControl = ReactMount.renderComponent(parentControl, parentContainer); + parentControl = + ReactMount.renderComponent(parentControl, parentContainer); parentControl.getDOMNode().appendChild(childContainer); - var callback = ReactEventTopLevelCallback.createTopLevelCallback('test'); + var callback = ReactEventListener.dispatchEvent.bind(null, 'test'); callback({ target: childControl.getDOMNode() }); - var calls = ReactEventEmitter.handleTopLevel.mock.calls; + var calls = handleTopLevel.mock.calls; expect(calls.length).toBe(2); expect(calls[0][EVENT_TARGET_PARAM]).toBe(childControl.getDOMNode()); expect(calls[1][EVENT_TARGET_PARAM]).toBe(parentControl.getDOMNode()); @@ -67,17 +72,19 @@ describe('ReactEventTopLevelCallback', function() { var grandParentContainer = document.createElement('div'); var grandParentControl =
Parent
; childControl = ReactMount.renderComponent(childControl, childContainer); - parentControl = ReactMount.renderComponent(parentControl, parentContainer); - grandParentControl = ReactMount.renderComponent(grandParentControl, grandParentContainer); + parentControl = + ReactMount.renderComponent(parentControl, parentContainer); + grandParentControl = + ReactMount.renderComponent(grandParentControl, grandParentContainer); parentControl.getDOMNode().appendChild(childContainer); grandParentControl.getDOMNode().appendChild(parentContainer); - var callback = ReactEventTopLevelCallback.createTopLevelCallback('test'); + var callback = ReactEventListener.dispatchEvent.bind(null, 'test'); callback({ target: childControl.getDOMNode() }); - var calls = ReactEventEmitter.handleTopLevel.mock.calls; + var calls = handleTopLevel.mock.calls; expect(calls.length).toBe(3); expect(calls[0][EVENT_TARGET_PARAM]).toBe(childControl.getDOMNode()); expect(calls[1][EVENT_TARGET_PARAM]).toBe(parentControl.getDOMNode()); @@ -91,15 +98,16 @@ describe('ReactEventTopLevelCallback', function() { var parentContainer = document.createElement('div'); var parentControl =
Parent
; childControl = ReactMount.renderComponent(childControl, childContainer); - parentControl = ReactMount.renderComponent(parentControl, parentContainer); + parentControl = + ReactMount.renderComponent(parentControl, parentContainer); parentControl.getDOMNode().appendChild(childContainer); - // ReactEventEmitter.handleTopLevel might remove the target from the DOM. - // Here, we have handleTopLevel remove the node when the first event - // handlers are called; we'll still expect to receive a second call for - // the parent control. + // ReactBrowserEventEmitter.handleTopLevel might remove the + // target from the DOM. Here, we have handleTopLevel remove the + // node when the first event handlers are called; we'll still + // expect to receive a second call for the parent control. var childNode = childControl.getDOMNode(); - ReactEventEmitter.handleTopLevel.mockImplementation( + handleTopLevel.mockImplementation( function(topLevelType, topLevelTarget, topLevelTargetID, nativeEvent) { if (topLevelTarget === childNode) { ReactMount.unmountComponentAtNode(childContainer); @@ -107,12 +115,12 @@ describe('ReactEventTopLevelCallback', function() { } ); - var callback = ReactEventTopLevelCallback.createTopLevelCallback('test'); + var callback = ReactEventListener.dispatchEvent.bind(null, 'test'); callback({ target: childNode }); - var calls = ReactEventEmitter.handleTopLevel.mock.calls; + var calls = handleTopLevel.mock.calls; expect(calls.length).toBe(2); expect(calls[0][EVENT_TARGET_PARAM]).toBe(childNode); expect(calls[1][EVENT_TARGET_PARAM]).toBe(parentControl.getDOMNode()); @@ -134,7 +142,7 @@ describe('ReactEventTopLevelCallback', function() { // Suppose an event handler in each root enqueues an update to the // childControl element -- the two updates should get batched together. var childNode = childControl.getDOMNode(); - ReactEventEmitter.handleTopLevel.mockImplementation( + handleTopLevel.mockImplementation( function(topLevelType, topLevelTarget, topLevelTargetID, nativeEvent) { ReactMount.renderComponent(
{topLevelTarget === childNode ? '1' : '2'}
, @@ -145,12 +153,13 @@ describe('ReactEventTopLevelCallback', function() { } ); - var callback = ReactEventTopLevelCallback.createTopLevelCallback('test'); + var callback = + ReactEventListener.dispatchEvent.bind(ReactEventListener, 'test'); callback({ target: childNode }); - var calls = ReactEventEmitter.handleTopLevel.mock.calls; + var calls = handleTopLevel.mock.calls; expect(calls.length).toBe(2); expect(childNode.textContent).toBe('2'); }); @@ -173,12 +182,12 @@ describe('ReactEventTopLevelCallback', function() { var instance = ReactMount.renderComponent(, container); - var callback = ReactEventTopLevelCallback.createTopLevelCallback('test'); + var callback = ReactEventListener.dispatchEvent.bind(null, 'test'); callback({ target: instance.getInner().getDOMNode() }); - var calls = ReactEventEmitter.handleTopLevel.mock.calls; + var calls = handleTopLevel.mock.calls; expect(calls.length).toBe(1); expect(calls[0][EVENT_TARGET_PARAM]).toBe(instance.getInner().getDOMNode()); }); diff --git a/src/browser/ui/dom/components/LocalEventTrapMixin.js b/src/browser/ui/dom/components/LocalEventTrapMixin.js index 1e4ec4f101d2f..eee6f33a1da6a 100644 --- a/src/browser/ui/dom/components/LocalEventTrapMixin.js +++ b/src/browser/ui/dom/components/LocalEventTrapMixin.js @@ -18,7 +18,7 @@ "use strict"; -var ReactEventEmitter = require('ReactEventEmitter'); +var ReactBrowserEventEmitter = require('ReactBrowserEventEmitter'); var accumulate = require('accumulate'); var forEachAccumulated = require('forEachAccumulated'); @@ -31,7 +31,7 @@ function remove(event) { var LocalEventTrapMixin = { trapBubbledEvent(topLevelType, handlerBaseName) { invariant(this.isMounted(), 'Must be mounted to trap events'); - var listener = ReactEventEmitter.trapBubbledEvent( + var listener = ReactBrowserEventEmitter.trapBubbledEvent( topLevelType, handlerBaseName, this.getDOMNode() diff --git a/src/test/ReactTestUtils.js b/src/test/ReactTestUtils.js index cac11e9bbae7f..7b504c01c5e34 100644 --- a/src/test/ReactTestUtils.js +++ b/src/test/ReactTestUtils.js @@ -24,7 +24,7 @@ var EventPropagators = require('EventPropagators'); var React = require('React'); var ReactDescriptor = require('ReactDescriptor'); var ReactDOM = require('ReactDOM'); -var ReactEventEmitter = require('ReactEventEmitter'); +var ReactBrowserEventEmitter = require('ReactBrowserEventEmitter'); var ReactMount = require('ReactMount'); var ReactTextComponent = require('ReactTextComponent'); var ReactUpdates = require('ReactUpdates'); @@ -259,12 +259,11 @@ var ReactTestUtils = { * @param {?Event} fakeNativeEvent Fake native event to use in SyntheticEvent. */ simulateNativeEventOnNode: function(topLevelType, node, fakeNativeEvent) { - var virtualHandler = - ReactEventEmitter.TopLevelCallbackCreator.createTopLevelCallback( - topLevelType - ); fakeNativeEvent.target = node; - virtualHandler(fakeNativeEvent); + ReactBrowserEventEmitter.ReactEventListener.dispatchEvent( + topLevelType, + fakeNativeEvent + ); }, /** @@ -319,7 +318,7 @@ function makeSimulator(eventType) { // We don't use SyntheticEvent.getPooled in order to not have to worry about // properly destroying any properties assigned from `eventData` upon release var event = new SyntheticEvent( - ReactEventEmitter.eventNameDispatchConfigs[eventType], + ReactBrowserEventEmitter.eventNameDispatchConfigs[eventType], ReactMount.getID(node), fakeNativeEvent ); @@ -337,7 +336,7 @@ function buildSimulators() { ReactTestUtils.Simulate = {}; var eventType; - for (eventType in ReactEventEmitter.eventNameDispatchConfigs) { + for (eventType in ReactBrowserEventEmitter.eventNameDispatchConfigs) { /** * @param {!Element || ReactDOMComponent} domComponentOrNode * @param {?object} eventData Fake event data to use in SyntheticEvent.