Create ReactEventListener

Per our discussion friday, this creates ReactEventListener and renames variables to use the "handle"
nomenclature.
This commit is contained in:
Pete Hunt
2014-06-14 20:16:10 -07:00
committed by Paul O’Shannessy
parent a6cd945d9f
commit e1c2d02fdd
15 changed files with 368 additions and 302 deletions
@@ -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;
+2 -2
View File
@@ -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
+9 -8
View File
@@ -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);
}
};
@@ -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))
@@ -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.
//
@@ -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();
+5 -5
View File
@@ -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);
}
+3 -3
View File
@@ -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
);
/**
@@ -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,40 +47,6 @@ 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
*/
function handleTopLevelImpl(bookKeeping) {
var topLevelTarget = ReactMount.getFirstReactDOM(
getEventTarget(bookKeeping.nativeEvent)
) || window;
// Loop through the hierarchy, in case there's any nested components.
// It's important that we build the array of ancestors before calling any
// event handlers, because event handlers can modify the DOM, leading to
// inconsistencies with ReactMount's node cache. See #1105.
var ancestor = topLevelTarget;
while (ancestor) {
bookKeeping.ancestors.push(ancestor);
ancestor = findParent(ancestor);
}
for (var i = 0, l = bookKeeping.ancestors.length; i < l; i++) {
topLevelTarget = bookKeeping.ancestors[i];
var topLevelTargetID = ReactMount.getID(topLevelTarget) || '';
ReactEventEmitter.handleTopLevel(
bookKeeping.topLevelType,
topLevelTarget,
topLevelTargetID,
bookKeeping.nativeEvent
);
}
}
// Used to store ancestor hierarchy in top level callback
function TopLevelCallbackBookKeeping(topLevelType, nativeEvent) {
this.topLevelType = topLevelType;
@@ -104,57 +65,124 @@ PooledClass.addPoolingTo(
PooledClass.twoArgumentPooler
);
/**
* Top-level callback creator used to implement event handling using delegation.
* This is used via dependency injection.
*/
var ReactEventTopLevelCallback = {
function handleTopLevelImpl(bookKeeping) {
var topLevelTarget = ReactMount.getFirstReactDOM(
getEventTarget(bookKeeping.nativeEvent)
) || window;
/**
* Sets whether or not any created callbacks should be enabled.
*
* @param {boolean} enabled True if callbacks should be enabled.
*/
setEnabled: function(enabled) {
_topLevelListenersEnabled = !!enabled;
},
/**
* @return {boolean} True if callbacks are enabled.
*/
isEnabled: function() {
return _topLevelListenersEnabled;
},
/**
* 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.
*
* @param {string} topLevelType Record from `EventConstants`.
* @return {function} Callback for handling top-level events.
*/
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);
}
};
// Loop through the hierarchy, in case there's any nested components.
// It's important that we build the array of ancestors before calling any
// event handlers, because event handlers can modify the DOM, leading to
// inconsistencies with ReactMount's node cache. See #1105.
var ancestor = topLevelTarget;
while (ancestor) {
bookKeeping.ancestors.push(ancestor);
ancestor = findParent(ancestor);
}
for (var i = 0, l = bookKeeping.ancestors.length; i < l; i++) {
topLevelTarget = bookKeeping.ancestors[i];
var topLevelTargetID = ReactMount.getID(topLevelTarget) || '';
ReactEventListener._handleTopLevel(
bookKeeping.topLevelType,
topLevelTarget,
topLevelTargetID,
bookKeeping.nativeEvent
);
}
}
function scrollValueMonitor(cb) {
var scrollPosition = getUnboundedScrollPosition(window);
cb(scrollPosition);
}
var ReactEventListener = {
_enabled: true,
_handleTopLevel: null,
WINDOW_HANDLE: window,
setHandleTopLevel: function(handleTopLevel) {
ReactEventListener._handleTopLevel = handleTopLevel;
},
setEnabled: function(enabled) {
ReactEventListener._enabled = !!enabled;
},
isEnabled: function() {
return ReactEventListener._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
*/
trapBubbledEvent: function(topLevelType, handlerBaseName, handle) {
var element = handle;
if (!element) {
return;
}
return EventListener.listen(
element,
handlerBaseName,
ReactEventListener.dispatchEvent.bind(null, 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 {object} handle Element on which to attach listener.
* @return {object} An object with a remove function which will forcefully
* remove the listener.
* @internal
*/
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;
+2 -2
View File
@@ -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
+2 -2
View File
@@ -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;
@@ -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);
});
});
@@ -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 = <div>Parent</div>;
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 = <div>Parent</div>;
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 = <div>Parent</div>;
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(
<div>{topLevelTarget === childNode ? '1' : '2'}</div>,
@@ -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(<Wrapper />, 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());
});
@@ -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()
+7 -8
View File
@@ -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.