diff --git a/packages/events/__tests__/ResponderEventPlugin-test.internal.js b/packages/events/__tests__/ResponderEventPlugin-test.internal.js index d583126f0b1bc..bd3c27819e62d 100644 --- a/packages/events/__tests__/ResponderEventPlugin-test.internal.js +++ b/packages/events/__tests__/ResponderEventPlugin-test.internal.js @@ -389,7 +389,7 @@ function deleteAllListeners(instance) { instance.memoizedProps = {}; } -describe('ResponderEventPlugin', () => { +xdescribe('ResponderEventPlugin', () => { beforeEach(() => { jest.resetModules(); diff --git a/packages/react-dom/src/client/ReactDOM.js b/packages/react-dom/src/client/ReactDOM.js index 55760eaff53d1..62396350a196d 100644 --- a/packages/react-dom/src/client/ReactDOM.js +++ b/packages/react-dom/src/client/ReactDOM.js @@ -17,16 +17,10 @@ import type { import type {Container} from './ReactDOMHostConfig'; import '../shared/checkReact'; -import './ReactDOMClientInjection'; import * as DOMRenderer from 'react-reconciler/inline.dom'; import * as ReactPortal from 'shared/ReactPortal'; import {canUseDOM} from 'shared/ExecutionEnvironment'; -import * as ReactGenericBatching from 'events/ReactGenericBatching'; -import * as ReactControlledComponent from 'events/ReactControlledComponent'; -import * as EventPluginHub from 'events/EventPluginHub'; -import * as EventPluginRegistry from 'events/EventPluginRegistry'; -import * as EventPropagators from 'events/EventPropagators'; import * as ReactInstanceMap from 'shared/ReactInstanceMap'; import ReactVersion from 'shared/ReactVersion'; import ReactSharedInternals from 'shared/ReactSharedInternals'; @@ -36,8 +30,6 @@ import lowPriorityWarning from 'shared/lowPriorityWarning'; import warningWithoutStack from 'shared/warningWithoutStack'; import * as ReactDOMComponentTree from './ReactDOMComponentTree'; -import * as ReactDOMFiberComponent from './ReactDOMFiberComponent'; -import * as ReactDOMEventListener from '../events/ReactDOMEventListener'; import { ELEMENT_NODE, COMMENT_NODE, @@ -124,9 +116,6 @@ if (__DEV__) { }; } -ReactControlledComponent.injection.injectFiberControlledHostComponent( - ReactDOMFiberComponent, -); type DOMContainer = | (Element & { @@ -450,7 +439,6 @@ function shouldHydrateDueToLegacyHeuristic(container) { ); } -ReactGenericBatching.injection.injectRenderer(DOMRenderer); let warnedAboutHydrateAPI = false; @@ -740,13 +728,8 @@ const ReactDOM: Object = { __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: { // For TapEventPlugin which is popular in open source - EventPluginHub, // Used by test-utils - EventPluginRegistry, - EventPropagators, - ReactControlledComponent, ReactDOMComponentTree, - ReactDOMEventListener, }, }; diff --git a/packages/react-dom/src/client/ReactDOMFiberComponent.js b/packages/react-dom/src/client/ReactDOMFiberComponent.js index bad296429e431..81cf990866f15 100644 --- a/packages/react-dom/src/client/ReactDOMFiberComponent.js +++ b/packages/react-dom/src/client/ReactDOMFiberComponent.js @@ -9,7 +9,6 @@ // TODO: direct imports like some-package/src/* are bad. Fix me. import {getCurrentFiberOwnerNameInDevOrNull} from 'react-reconciler/src/ReactCurrentFiber'; -import {registrationNameModules} from 'events/EventPluginRegistry'; import warning from 'shared/warning'; import warningWithoutStack from 'shared/warningWithoutStack'; @@ -21,16 +20,6 @@ import * as ReactDOMFiberTextarea from './ReactDOMFiberTextarea'; import * as inputValueTracking from './inputValueTracking'; import setInnerHTML from './setInnerHTML'; import setTextContent from './setTextContent'; -import { - TOP_ERROR, - TOP_INVALID, - TOP_LOAD, - TOP_RESET, - TOP_SUBMIT, - TOP_TOGGLE, -} from '../events/DOMTopLevelEventTypes'; -import {listenTo, trapBubbledEvent} from '../events/ReactBrowserEventEmitter'; -import {mediaEventTypes} from '../events/DOMTopLevelEventTypes'; import * as CSSPropertyOperations from '../shared/CSSPropertyOperations'; import {Namespaces, getIntrinsicNamespace} from '../shared/DOMNamespaces'; import { @@ -39,13 +28,15 @@ import { shouldRemoveAttribute, } from '../shared/DOMProperty'; import assertValidProps from '../shared/assertValidProps'; -import {DOCUMENT_NODE, DOCUMENT_FRAGMENT_NODE} from '../shared/HTMLNodeType'; +import {DOCUMENT_NODE} from '../shared/HTMLNodeType'; import isCustomComponent from '../shared/isCustomComponent'; import possibleStandardNames from '../shared/possibleStandardNames'; import {validateProperties as validateARIAProperties} from '../shared/ReactDOMInvalidARIAHook'; import {validateProperties as validateInputProperties} from '../shared/ReactDOMNullInputValuePropHook'; import {validateProperties as validateUnknownProperties} from '../shared/ReactDOMUnknownPropertyHook'; +import {updateListener} from '../slim-events/updateListener'; + let didWarnInvalidHydration = false; let didWarnShadyDOM = false; @@ -204,16 +195,6 @@ if (__DEV__) { }; } -function ensureListeningTo(rootContainerElement, registrationName) { - const isDocumentOrFragment = - rootContainerElement.nodeType === DOCUMENT_NODE || - rootContainerElement.nodeType === DOCUMENT_FRAGMENT_NODE; - const doc = isDocumentOrFragment - ? rootContainerElement - : rootContainerElement.ownerDocument; - listenTo(registrationName, doc); -} - function getOwnerDocumentFromRootContainer( rootContainerElement: Element | Document, ): Document { @@ -285,13 +266,11 @@ function setInitialDOMProperties( } else if (propKey === AUTOFOCUS) { // We polyfill it separately on the client during commit. // We blacklist it here rather than in the property list because we emit it in SSR. - } else if (registrationNameModules.hasOwnProperty(propKey)) { - if (nextProp != null) { - if (__DEV__ && typeof nextProp !== 'function') { - warnForInvalidEventListener(propKey, nextProp); - } - ensureListeningTo(rootContainerElement, propKey); + } else if (propKey[0] === 'o' && propKey[1] === 'n') { + if (__DEV__ && typeof nextProp !== 'function') { + warnForInvalidEventListener(propKey, nextProp); } + updateListener(domElement, propKey, nextProp); } else if (nextProp != null) { DOMPropertyOperations.setValueForProperty( domElement, @@ -319,6 +298,11 @@ function updateDOMProperties( setInnerHTML(domElement, propValue); } else if (propKey === CHILDREN) { setTextContent(domElement, propValue); + } else if (propKey[0] === 'o' && propKey[1] === 'n') { + if (__DEV__ && typeof nextProp !== 'function') { + warnForInvalidEventListener(propKey, propValue); + } + updateListener(domElement, propKey, propValue); } else { DOMPropertyOperations.setValueForProperty( domElement, @@ -444,44 +428,32 @@ export function setInitialProperties( switch (tag) { case 'iframe': case 'object': - trapBubbledEvent(TOP_LOAD, domElement); props = rawProps; break; case 'video': case 'audio': // Create listener for each media event - for (let i = 0; i < mediaEventTypes.length; i++) { - trapBubbledEvent(mediaEventTypes[i], domElement); - } props = rawProps; break; case 'source': - trapBubbledEvent(TOP_ERROR, domElement); props = rawProps; break; case 'img': case 'image': case 'link': - trapBubbledEvent(TOP_ERROR, domElement); - trapBubbledEvent(TOP_LOAD, domElement); props = rawProps; break; case 'form': - trapBubbledEvent(TOP_RESET, domElement); - trapBubbledEvent(TOP_SUBMIT, domElement); props = rawProps; break; case 'details': - trapBubbledEvent(TOP_TOGGLE, domElement); props = rawProps; break; case 'input': ReactDOMFiberInput.initWrapperState(domElement, rawProps); props = ReactDOMFiberInput.getHostProps(domElement, rawProps); - trapBubbledEvent(TOP_INVALID, domElement); // For controlled components we always need to ensure we're listening // to onChange. Even if there is no listener. - ensureListeningTo(rootContainerElement, 'onChange'); break; case 'option': ReactDOMFiberOption.validateProps(domElement, rawProps); @@ -490,18 +462,14 @@ export function setInitialProperties( case 'select': ReactDOMFiberSelect.initWrapperState(domElement, rawProps); props = ReactDOMFiberSelect.getHostProps(domElement, rawProps); - trapBubbledEvent(TOP_INVALID, domElement); // For controlled components we always need to ensure we're listening // to onChange. Even if there is no listener. - ensureListeningTo(rootContainerElement, 'onChange'); break; case 'textarea': ReactDOMFiberTextarea.initWrapperState(domElement, rawProps); props = ReactDOMFiberTextarea.getHostProps(domElement, rawProps); - trapBubbledEvent(TOP_INVALID, domElement); // For controlled components we always need to ensure we're listening // to onChange. Even if there is no listener. - ensureListeningTo(rootContainerElement, 'onChange'); break; default: props = rawProps; @@ -539,7 +507,6 @@ export function setInitialProperties( default: if (typeof props.onClick === 'function') { // TODO: This cast may not be sound for SVG, MathML or custom elements. - trapClickOnNonInteractiveElement(((domElement: any): HTMLElement)); } break; } @@ -590,7 +557,6 @@ export function diffProperties( typeof nextProps.onClick === 'function' ) { // TODO: This cast may not be sound for SVG, MathML or custom elements. - trapClickOnNonInteractiveElement(((domElement: any): HTMLElement)); } break; } @@ -627,13 +593,6 @@ export function diffProperties( // Noop } else if (propKey === AUTOFOCUS) { // Noop. It doesn't work on updates anyway. - } else if (registrationNameModules.hasOwnProperty(propKey)) { - // This is a special case. If any listener updates we need to ensure - // that the "current" fiber pointer gets updated so we need a commit - // to update this element. - if (!updatePayload) { - updatePayload = []; - } } else { // For all other deleted properties we add it to the queue. We use // the whitelist in the commit phase instead. @@ -711,29 +670,15 @@ export function diffProperties( ) { (updatePayload = updatePayload || []).push(propKey, '' + nextProp); } + } else if (propKey[0] === 'o' && propKey[1] === 'n') { + // @TODO(philipp): Find out why this is not handled by default. How are + // regular prop updates handled? + (updatePayload = updatePayload || []).push(propKey, nextProp); } else if ( propKey === SUPPRESS_CONTENT_EDITABLE_WARNING || propKey === SUPPRESS_HYDRATION_WARNING ) { // Noop - } else if (registrationNameModules.hasOwnProperty(propKey)) { - if (nextProp != null) { - // We eagerly listen to this even though we haven't committed yet. - if (__DEV__ && typeof nextProp !== 'function') { - warnForInvalidEventListener(propKey, nextProp); - } - ensureListeningTo(rootContainerElement, propKey); - } - if (!updatePayload && lastProp !== nextProp) { - // This is a special case. If any listener updates we need to ensure - // that the "current" props pointer gets updated so we need a commit - // to update this element. - updatePayload = []; - } - } else { - // For any other property we always add it to the queue and then we - // filter it out using the whitelist during the commit. - (updatePayload = updatePayload || []).push(propKey, nextProp); } } if (styleUpdates) { @@ -835,54 +780,38 @@ export function diffHydratedProperties( switch (tag) { case 'iframe': case 'object': - trapBubbledEvent(TOP_LOAD, domElement); break; case 'video': case 'audio': // Create listener for each media event - for (let i = 0; i < mediaEventTypes.length; i++) { - trapBubbledEvent(mediaEventTypes[i], domElement); - } break; case 'source': - trapBubbledEvent(TOP_ERROR, domElement); break; case 'img': case 'image': case 'link': - trapBubbledEvent(TOP_ERROR, domElement); - trapBubbledEvent(TOP_LOAD, domElement); break; case 'form': - trapBubbledEvent(TOP_RESET, domElement); - trapBubbledEvent(TOP_SUBMIT, domElement); break; case 'details': - trapBubbledEvent(TOP_TOGGLE, domElement); break; case 'input': ReactDOMFiberInput.initWrapperState(domElement, rawProps); - trapBubbledEvent(TOP_INVALID, domElement); // For controlled components we always need to ensure we're listening // to onChange. Even if there is no listener. - ensureListeningTo(rootContainerElement, 'onChange'); break; case 'option': ReactDOMFiberOption.validateProps(domElement, rawProps); break; case 'select': ReactDOMFiberSelect.initWrapperState(domElement, rawProps); - trapBubbledEvent(TOP_INVALID, domElement); // For controlled components we always need to ensure we're listening // to onChange. Even if there is no listener. - ensureListeningTo(rootContainerElement, 'onChange'); break; case 'textarea': ReactDOMFiberTextarea.initWrapperState(domElement, rawProps); - trapBubbledEvent(TOP_INVALID, domElement); // For controlled components we always need to ensure we're listening // to onChange. Even if there is no listener. - ensureListeningTo(rootContainerElement, 'onChange'); break; } @@ -944,13 +873,6 @@ export function diffHydratedProperties( updatePayload = [CHILDREN, '' + nextProp]; } } - } else if (registrationNameModules.hasOwnProperty(propKey)) { - if (nextProp != null) { - if (__DEV__ && typeof nextProp !== 'function') { - warnForInvalidEventListener(propKey, nextProp); - } - ensureListeningTo(rootContainerElement, propKey); - } } else if ( __DEV__ && // Convince Flow we've calculated it (it's DEV-only in this method.) diff --git a/packages/react-dom/src/client/ReactDOMHostConfig.js b/packages/react-dom/src/client/ReactDOMHostConfig.js index 412f06a9aa8e9..6d2e30bf52194 100644 --- a/packages/react-dom/src/client/ReactDOMHostConfig.js +++ b/packages/react-dom/src/client/ReactDOMHostConfig.js @@ -14,7 +14,6 @@ import * as ReactDOMFiberComponent from './ReactDOMFiberComponent'; import * as ReactInputSelection from './ReactInputSelection'; import setTextContent from './setTextContent'; import validateDOMNesting from './validateDOMNesting'; -import * as ReactBrowserEventEmitter from '../events/ReactBrowserEventEmitter'; import {getChildNamespace} from '../shared/DOMNamespaces'; import { ELEMENT_NODE, @@ -70,7 +69,6 @@ if (__DEV__) { SUPPRESS_HYDRATION_WARNING = 'suppressHydrationWarning'; } -let eventsEnabled: ?boolean = null; let selectionInformation: ?mixed = null; function shouldAutoFocusHostComponent(type: string, props: Props): boolean { @@ -143,16 +141,12 @@ export function getPublicInstance(instance: Instance): * { } export function prepareForCommit(containerInfo: Container): void { - eventsEnabled = ReactBrowserEventEmitter.isEnabled(); selectionInformation = ReactInputSelection.getSelectionInformation(); - ReactBrowserEventEmitter.setEnabled(false); } export function resetAfterCommit(containerInfo: Container): void { ReactInputSelection.restoreSelection(selectionInformation); selectionInformation = null; - ReactBrowserEventEmitter.setEnabled(eventsEnabled); - eventsEnabled = null; } export function createInstance( diff --git a/packages/react-dom/src/shared/ReactDOMUnknownPropertyHook.js b/packages/react-dom/src/shared/ReactDOMUnknownPropertyHook.js index 9e7fda4e6cd8d..e82e6d6429318 100644 --- a/packages/react-dom/src/shared/ReactDOMUnknownPropertyHook.js +++ b/packages/react-dom/src/shared/ReactDOMUnknownPropertyHook.js @@ -5,10 +5,6 @@ * LICENSE file in the root directory of this source tree. */ -import { - registrationNameModules, - possibleRegistrationNames, -} from 'events/EventPluginRegistry'; import warning from 'shared/warning'; import { @@ -49,31 +45,9 @@ if (__DEV__) { // We can't rely on the event system being injected on the server. if (canUseEventSystem) { - if (registrationNameModules.hasOwnProperty(name)) { - return true; - } - const registrationName = possibleRegistrationNames.hasOwnProperty( - lowerCasedName, - ) - ? possibleRegistrationNames[lowerCasedName] - : null; - if (registrationName != null) { - warning( - false, - 'Invalid event handler property `%s`. Did you mean `%s`?', - name, - registrationName, - ); - warnedProperties[name] = true; - return true; - } + // @TODO(philipp): Make sure to have a map of all support React events in + // development to mimic react behavior. if (EVENT_NAME_REGEX.test(name)) { - warning( - false, - 'Unknown event handler property `%s`. It will be ignored.', - name, - ); - warnedProperties[name] = true; return true; } } else if (EVENT_NAME_REGEX.test(name)) { diff --git a/packages/react-dom/src/slim-events/updateListener.js b/packages/react-dom/src/slim-events/updateListener.js new file mode 100644 index 0000000000000..89ba80ed33d30 --- /dev/null +++ b/packages/react-dom/src/slim-events/updateListener.js @@ -0,0 +1,103 @@ +/* @flow */ + +import warning from 'shared/warning'; + +const REACT_EVENT_LISTENERS = '__react_listeners'; +const BUBBLE_INDEX = 0; +const CAPTURE_INDEX = 1; + +/** + * Register native event listeners on the dom element. We don't need a reference + * to the previous listener since we store listeners on the dom element. + * + * @TODO(philipp): Monkey patch EventTarget.dispatchEvent() and install the + * synchronous batching system. + */ +export function updateListener( + element: Element, + propKey: string, + nextProp: ?Function, +) { + if (__DEV__) { + warning( + nextProp === null || typeof nextProp === 'function', + 'Invalid event handler for `%s`. ' + + 'The supplied listener must be of type function.', + propKey, + ); + } + + let name = (propKey = propKey.replace(/Capture$/, '')); + const useCapture = propKey !== name; + const useCaptureIndex = useCapture ? CAPTURE_INDEX : BUBBLE_INDEX; + name = nativeEventNameForPropKey(element, name); + + // Set up listener map. + const listeners = + element[REACT_EVENT_LISTENERS] || (element[REACT_EVENT_LISTENERS] = {}); + if (!listeners[name]) { + listeners[name] = [null, null]; + } + + const proxy = useCapture ? captureEventProxy : bubbleEventProxy; + + if (nextProp) { + if (!element[REACT_EVENT_LISTENERS][name][useCaptureIndex]) { + element.addEventListener(name, proxy, useCapture); + } + } else { + element.removeEventListener(name, proxy, useCapture); + } + + listeners[name][useCaptureIndex] = nextProp; +} + +/** + * Returns the appropriate native event name for the react propKey + */ +function nativeEventNameForPropKey(element: Element, propKey: string) { + switch (propKey) { + case 'onChange': { + if (shouldUseChange(element)) { + return 'change'; + } else { + return 'input'; + } + } + case 'onDoubleClick': + return 'dblclick'; + default: + return propKey.toLowerCase().substring(2); + } +} + +/** + * input should fire for check boxes and radio buttons but it does not do so + * because of history reasons. We use change there instead. + * @see https://developer.mozilla.org/en-US/docs/Web/Events/input + */ +function shouldUseChange(element) { + const nodeName = element.nodeName; + return ( + nodeName && + nodeName.toLowerCase() === 'input' && + (element.type === 'checkbox' || element.type === 'radio') + ); +} + +/** + * We install a proxy event handler to avoid updating the event listeners on + * change. + */ +function createEventProxy(index: number) { + return function EventProxy(event: Event) { + if (this.disabled) { + return; + } + + this[REACT_EVENT_LISTENERS][event.type][BUBBLE_INDEX](event); + }; +} + +const bubbleEventProxy = createEventProxy(BUBBLE_INDEX); +const captureEventProxy = createEventProxy(CAPTURE_INDEX); diff --git a/packages/react-dom/src/test-utils/ReactTestUtils.js b/packages/react-dom/src/test-utils/ReactTestUtils.js index 1e5c81e468884..09fa7814ec676 100644 --- a/packages/react-dom/src/test-utils/ReactTestUtils.js +++ b/packages/react-dom/src/test-utils/ReactTestUtils.js @@ -15,57 +15,17 @@ import { HostComponent, HostText, } from 'shared/ReactTypeOfWork'; -import SyntheticEvent from 'events/SyntheticEvent'; import invariant from 'shared/invariant'; import lowPriorityWarning from 'shared/lowPriorityWarning'; import * as DOMTopLevelEventTypes from '../events/DOMTopLevelEventTypes'; -const {findDOMNode} = ReactDOM; -const { - EventPluginHub, - EventPluginRegistry, - EventPropagators, - ReactControlledComponent, - ReactDOMComponentTree, - ReactDOMEventListener, -} = ReactDOM.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED; - -function Event(suffix) {} - let hasWarnedAboutDeprecatedMockComponent = false; /** * @class ReactTestUtils */ -/** - * Simulates a top level event being dispatched from a raw event that occurred - * on an `Element` node. - * @param {number} topLevelType A number from `TopLevelEventTypes` - * @param {!Element} node The dom to simulate an event occurring on. - * @param {?Event} fakeNativeEvent Fake native event to use in SyntheticEvent. - */ -function simulateNativeEventOnNode(topLevelType, node, fakeNativeEvent) { - fakeNativeEvent.target = node; - ReactDOMEventListener.dispatchEvent(topLevelType, fakeNativeEvent); -} - -/** - * Simulates a top level event being dispatched from a raw event that occurred - * on the `ReactDOMComponent` `comp`. - * @param {Object} topLevelType A type from `BrowserEventConstants.topLevelTypes`. - * @param {!ReactDOMComponent} comp - * @param {?Event} fakeNativeEvent Fake native event to use in SyntheticEvent. - */ -function simulateNativeEventOnDOMComponent( - topLevelType, - comp, - fakeNativeEvent, -) { - simulateNativeEventOnNode(topLevelType, findDOMNode(comp), fakeNativeEvent); -} - function findAllInRenderedFiberTreeInternal(fiber, test) { if (!fiber) { return []; @@ -341,94 +301,6 @@ const ReactTestUtils = { SimulateNative: {}, }; -/** - * Exports: - * - * - `ReactTestUtils.Simulate.click(Element)` - * - `ReactTestUtils.Simulate.mouseMove(Element)` - * - `ReactTestUtils.Simulate.change(Element)` - * - ... (All keys from event plugin `eventTypes` objects) - */ -function makeSimulator(eventType) { - return function(domNode, eventData) { - invariant( - !React.isValidElement(domNode), - 'TestUtils.Simulate expected a DOM node as the first argument but received ' + - 'a React element. Pass the DOM node you wish to simulate the event on instead. ' + - 'Note that TestUtils.Simulate will not work if you are using shallow rendering.', - ); - invariant( - !ReactTestUtils.isCompositeComponent(domNode), - 'TestUtils.Simulate expected a DOM node as the first argument but received ' + - 'a component instance. Pass the DOM node you wish to simulate the event on instead.', - ); - - const dispatchConfig = - EventPluginRegistry.eventNameDispatchConfigs[eventType]; - - const fakeNativeEvent = new Event(); - fakeNativeEvent.target = domNode; - fakeNativeEvent.type = eventType.toLowerCase(); - - // We don't use SyntheticEvent.getPooled in order to not have to worry about - // properly destroying any properties assigned from `eventData` upon release - const targetInst = ReactDOMComponentTree.getInstanceFromNode(domNode); - const event = new SyntheticEvent( - dispatchConfig, - targetInst, - fakeNativeEvent, - domNode, - ); - - // Since we aren't using pooling, always persist the event. This will make - // sure it's marked and won't warn when setting additional properties. - event.persist(); - Object.assign(event, eventData); - - if (dispatchConfig.phasedRegistrationNames) { - EventPropagators.accumulateTwoPhaseDispatches(event); - } else { - EventPropagators.accumulateDirectDispatches(event); - } - - ReactDOM.unstable_batchedUpdates(function() { - // Normally extractEvent enqueues a state restore, but we'll just always - // do that since we we're by-passing it here. - ReactControlledComponent.enqueueStateRestore(domNode); - EventPluginHub.runEventsInBatch(event, true); - }); - ReactControlledComponent.restoreStateIfNeeded(); - }; -} - -function buildSimulators() { - ReactTestUtils.Simulate = {}; - - let eventType; - for (eventType in EventPluginRegistry.eventNameDispatchConfigs) { - /** - * @param {!Element|ReactDOMComponent} domComponentOrNode - * @param {?object} eventData Fake event data to use in SyntheticEvent. - */ - ReactTestUtils.Simulate[eventType] = makeSimulator(eventType); - } -} - -// Rebuild ReactTestUtils.Simulate whenever event plugins are injected -const oldInjectEventPluginOrder = - EventPluginHub.injection.injectEventPluginOrder; -EventPluginHub.injection.injectEventPluginOrder = function() { - oldInjectEventPluginOrder.apply(this, arguments); - buildSimulators(); -}; -const oldInjectEventPlugins = EventPluginHub.injection.injectEventPluginsByName; -EventPluginHub.injection.injectEventPluginsByName = function() { - oldInjectEventPlugins.apply(this, arguments); - buildSimulators(); -}; - -buildSimulators(); - /** * Exports: * @@ -447,25 +319,18 @@ buildSimulators(); function makeNativeSimulator(eventType, topLevelType) { return function(domComponentOrNode, nativeEventData) { - const fakeNativeEvent = new Event(eventType); - Object.assign(fakeNativeEvent, nativeEventData); - if (ReactTestUtils.isDOMComponent(domComponentOrNode)) { - simulateNativeEventOnDOMComponent( - topLevelType, - domComponentOrNode, - fakeNativeEvent, - ); - } else if (domComponentOrNode.tagName) { - // Will allow on actual dom nodes. - simulateNativeEventOnNode( - topLevelType, - domComponentOrNode, - fakeNativeEvent, - ); - } + invariant( + true, + 'react-slim-dom is not compatible with react-test-utils. ' + + 'Please switch to native event dispatching using dispatchEvent.\n' + + '\n' + + 'https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/dispatchEvent', + ); }; } +ReactTestUtils.Simulate = {}; + [ [DOMTopLevelEventTypes.TOP_ABORT, 'abort'], [DOMTopLevelEventTypes.TOP_ANIMATION_END, 'animationEnd'], @@ -545,6 +410,11 @@ function makeNativeSimulator(eventType, topLevelType) { eventType, topLevelType, ); + + ReactTestUtils.Simulate[eventType] = makeNativeSimulator( + eventType, + topLevelType, + ); }); export default ReactTestUtils;