Fix change events for custom elements (#22938)

* Bypass react event system for custom elements

* Going to try fixing react event system instead

* finally got it to call onChange, but perhaps too many times

* update test

* Removed ReactDOMComponent changes, now works but still doubles for bubbles

* Maybe i should only support bubbling events

* removed some old stuff

* cleaned up changeeventplugin stuff

* prettier, lint

* removed changeeventplugin stuff

* remove unneeded gate for onInput test

* Go back to using ChangeEventPlugin

* Add input+change test

* lint

* Move logic to shouldUseChangeEvent

* use memoizedProps instead of pendingProps

* Run form control behavior before custom element behavior

* add bubbling test

* forgot to append container to body

* add child event target test

* expand <input is=...> test expectations

* Make tests more realistic

* Add extra test

* Add missing gating

* Actually fix gating

Co-authored-by: Dan Abramov <dan.abramov@me.com>
This commit is contained in:
Joey Arhar
2022-01-19 19:34:07 +00:00
committed by GitHub
co-authored by Dan Abramov
parent d8cfeaf221
commit 05a55a4b09
2 changed files with 591 additions and 1 deletions
@@ -22,6 +22,17 @@ describe('DOMPropertyOperations', () => {
ReactDOM = require('react-dom');
});
// Sets a value in a way that React doesn't see,
// so that a subsequent "change" event will trigger the event handler.
const setUntrackedValue = Object.getOwnPropertyDescriptor(
HTMLInputElement.prototype,
'value',
).set;
const setUntrackedChecked = Object.getOwnPropertyDescriptor(
HTMLInputElement.prototype,
'checked',
).set;
describe('setValueForProperty', () => {
it('should set values as properties by default', () => {
const container = document.createElement('div');
@@ -280,6 +291,575 @@ describe('DOMPropertyOperations', () => {
expect(syntheticClickEvent.nativeEvent).toBe(nativeClickEvent);
});
// @gate enableCustomElementPropertySupport
it('custom elements should have working onChange event listeners', () => {
let reactChangeEvent = null;
const eventHandler = jest.fn(event => (reactChangeEvent = event));
const container = document.createElement('div');
document.body.appendChild(container);
ReactDOM.render(<my-custom-element onChange={eventHandler} />, container);
const customElement = container.querySelector('my-custom-element');
let expectedHandlerCallCount = 0;
const changeEvent = new Event('change', {bubbles: true});
customElement.dispatchEvent(changeEvent);
expectedHandlerCallCount++;
expect(eventHandler).toHaveBeenCalledTimes(expectedHandlerCallCount);
expect(reactChangeEvent.nativeEvent).toBe(changeEvent);
// Also make sure that removing and re-adding the event listener works
ReactDOM.render(<my-custom-element />, container);
customElement.dispatchEvent(new Event('change', {bubbles: true}));
expect(eventHandler).toHaveBeenCalledTimes(expectedHandlerCallCount);
ReactDOM.render(<my-custom-element onChange={eventHandler} />, container);
customElement.dispatchEvent(new Event('change', {bubbles: true}));
expectedHandlerCallCount++;
expect(eventHandler).toHaveBeenCalledTimes(expectedHandlerCallCount);
});
it('custom elements should have working onInput event listeners', () => {
let reactInputEvent = null;
const eventHandler = jest.fn(event => (reactInputEvent = event));
const container = document.createElement('div');
document.body.appendChild(container);
ReactDOM.render(<my-custom-element onInput={eventHandler} />, container);
const customElement = container.querySelector('my-custom-element');
let expectedHandlerCallCount = 0;
const inputEvent = new Event('input', {bubbles: true});
customElement.dispatchEvent(inputEvent);
expectedHandlerCallCount++;
expect(eventHandler).toHaveBeenCalledTimes(expectedHandlerCallCount);
expect(reactInputEvent.nativeEvent).toBe(inputEvent);
// Also make sure that removing and re-adding the event listener works
ReactDOM.render(<my-custom-element />, container);
customElement.dispatchEvent(new Event('input', {bubbles: true}));
expect(eventHandler).toHaveBeenCalledTimes(expectedHandlerCallCount);
ReactDOM.render(<my-custom-element onInput={eventHandler} />, container);
customElement.dispatchEvent(new Event('input', {bubbles: true}));
expectedHandlerCallCount++;
expect(eventHandler).toHaveBeenCalledTimes(expectedHandlerCallCount);
});
// @gate enableCustomElementPropertySupport
it('custom elements should have separate onInput and onChange handling', () => {
const container = document.createElement('div');
document.body.appendChild(container);
const inputEventHandler = jest.fn();
const changeEventHandler = jest.fn();
ReactDOM.render(
<my-custom-element
onInput={inputEventHandler}
onChange={changeEventHandler}
/>,
container,
);
const customElement = container.querySelector('my-custom-element');
customElement.dispatchEvent(new Event('input', {bubbles: true}));
expect(inputEventHandler).toHaveBeenCalledTimes(1);
expect(changeEventHandler).toHaveBeenCalledTimes(0);
customElement.dispatchEvent(new Event('change', {bubbles: true}));
expect(inputEventHandler).toHaveBeenCalledTimes(1);
expect(changeEventHandler).toHaveBeenCalledTimes(1);
});
// @gate enableCustomElementPropertySupport
it('custom elements should be able to remove and re-add custom event listeners', () => {
const container = document.createElement('div');
document.body.appendChild(container);
const eventHandler = jest.fn();
ReactDOM.render(
<my-custom-element oncustomevent={eventHandler} />,
container,
);
const customElement = container.querySelector('my-custom-element');
customElement.dispatchEvent(new Event('customevent'));
expect(eventHandler).toHaveBeenCalledTimes(1);
ReactDOM.render(<my-custom-element />, container);
customElement.dispatchEvent(new Event('customevent'));
expect(eventHandler).toHaveBeenCalledTimes(1);
ReactDOM.render(
<my-custom-element oncustomevent={eventHandler} />,
container,
);
customElement.dispatchEvent(new Event('customevent'));
expect(eventHandler).toHaveBeenCalledTimes(2);
});
it('<input is=...> should have the same onChange/onInput/onClick behavior as <input>', () => {
const container = document.createElement('div');
document.body.appendChild(container);
const regularOnInputHandler = jest.fn();
const regularOnChangeHandler = jest.fn();
const regularOnClickHandler = jest.fn();
const customOnInputHandler = jest.fn();
const customOnChangeHandler = jest.fn();
const customOnClickHandler = jest.fn();
function clearMocks() {
regularOnInputHandler.mockClear();
regularOnChangeHandler.mockClear();
regularOnClickHandler.mockClear();
customOnInputHandler.mockClear();
customOnChangeHandler.mockClear();
customOnClickHandler.mockClear();
}
ReactDOM.render(
<div>
<input
onInput={regularOnInputHandler}
onChange={regularOnChangeHandler}
onClick={regularOnClickHandler}
/>
<input
is="my-custom-element"
onInput={customOnInputHandler}
onChange={customOnChangeHandler}
onClick={customOnClickHandler}
/>
</div>,
container,
);
const regularInput = container.querySelector(
'input:not([is=my-custom-element])',
);
const customInput = container.querySelector(
'input[is=my-custom-element]',
);
expect(regularInput).not.toBe(customInput);
// Typing should trigger onInput and onChange for both kinds of inputs.
clearMocks();
setUntrackedValue.call(regularInput, 'hello');
regularInput.dispatchEvent(new Event('input', {bubbles: true}));
expect(regularOnInputHandler).toHaveBeenCalledTimes(1);
expect(regularOnChangeHandler).toHaveBeenCalledTimes(1);
expect(regularOnClickHandler).toHaveBeenCalledTimes(0);
setUntrackedValue.call(customInput, 'hello');
customInput.dispatchEvent(new Event('input', {bubbles: true}));
expect(customOnInputHandler).toHaveBeenCalledTimes(1);
expect(customOnChangeHandler).toHaveBeenCalledTimes(1);
expect(customOnClickHandler).toHaveBeenCalledTimes(0);
// The native change event itself does not produce extra React events.
clearMocks();
regularInput.dispatchEvent(new Event('change', {bubbles: true}));
expect(regularOnInputHandler).toHaveBeenCalledTimes(0);
expect(regularOnChangeHandler).toHaveBeenCalledTimes(0);
expect(regularOnClickHandler).toHaveBeenCalledTimes(0);
customInput.dispatchEvent(new Event('change', {bubbles: true}));
expect(customOnInputHandler).toHaveBeenCalledTimes(0);
expect(customOnChangeHandler).toHaveBeenCalledTimes(0);
expect(customOnClickHandler).toHaveBeenCalledTimes(0);
// The click event is handled by both inputs.
clearMocks();
regularInput.dispatchEvent(new Event('click', {bubbles: true}));
expect(regularOnInputHandler).toHaveBeenCalledTimes(0);
expect(regularOnChangeHandler).toHaveBeenCalledTimes(0);
expect(regularOnClickHandler).toHaveBeenCalledTimes(1);
customInput.dispatchEvent(new Event('click', {bubbles: true}));
expect(customOnInputHandler).toHaveBeenCalledTimes(0);
expect(customOnChangeHandler).toHaveBeenCalledTimes(0);
expect(customOnClickHandler).toHaveBeenCalledTimes(1);
// Typing again should trigger onInput and onChange for both kinds of inputs.
clearMocks();
setUntrackedValue.call(regularInput, 'goodbye');
regularInput.dispatchEvent(new Event('input', {bubbles: true}));
expect(regularOnInputHandler).toHaveBeenCalledTimes(1);
expect(regularOnChangeHandler).toHaveBeenCalledTimes(1);
expect(regularOnClickHandler).toHaveBeenCalledTimes(0);
setUntrackedValue.call(customInput, 'goodbye');
customInput.dispatchEvent(new Event('input', {bubbles: true}));
expect(customOnInputHandler).toHaveBeenCalledTimes(1);
expect(customOnChangeHandler).toHaveBeenCalledTimes(1);
expect(customOnClickHandler).toHaveBeenCalledTimes(0);
});
it('<input type=radio is=...> should have the same onChange/onInput/onClick behavior as <input type=radio>', () => {
const container = document.createElement('div');
document.body.appendChild(container);
const regularOnInputHandler = jest.fn();
const regularOnChangeHandler = jest.fn();
const regularOnClickHandler = jest.fn();
const customOnInputHandler = jest.fn();
const customOnChangeHandler = jest.fn();
const customOnClickHandler = jest.fn();
function clearMocks() {
regularOnInputHandler.mockClear();
regularOnChangeHandler.mockClear();
regularOnClickHandler.mockClear();
customOnInputHandler.mockClear();
customOnChangeHandler.mockClear();
customOnClickHandler.mockClear();
}
ReactDOM.render(
<div>
<input
type="radio"
onInput={regularOnInputHandler}
onChange={regularOnChangeHandler}
onClick={regularOnClickHandler}
/>
<input
is="my-custom-element"
type="radio"
onInput={customOnInputHandler}
onChange={customOnChangeHandler}
onClick={customOnClickHandler}
/>
</div>,
container,
);
const regularInput = container.querySelector(
'input:not([is=my-custom-element])',
);
const customInput = container.querySelector(
'input[is=my-custom-element]',
);
expect(regularInput).not.toBe(customInput);
// Clicking should trigger onClick and onChange on both inputs.
clearMocks();
setUntrackedChecked.call(regularInput, true);
regularInput.dispatchEvent(new Event('click', {bubbles: true}));
expect(regularOnInputHandler).toHaveBeenCalledTimes(0);
expect(regularOnChangeHandler).toHaveBeenCalledTimes(1);
expect(regularOnClickHandler).toHaveBeenCalledTimes(1);
setUntrackedChecked.call(customInput, true);
customInput.dispatchEvent(new Event('click', {bubbles: true}));
expect(customOnInputHandler).toHaveBeenCalledTimes(0);
expect(customOnChangeHandler).toHaveBeenCalledTimes(1);
expect(customOnClickHandler).toHaveBeenCalledTimes(1);
// The native input event only produces a React onInput event.
clearMocks();
regularInput.dispatchEvent(new Event('input', {bubbles: true}));
expect(regularOnInputHandler).toHaveBeenCalledTimes(1);
expect(regularOnChangeHandler).toHaveBeenCalledTimes(0);
expect(regularOnClickHandler).toHaveBeenCalledTimes(0);
customInput.dispatchEvent(new Event('input', {bubbles: true}));
expect(customOnInputHandler).toHaveBeenCalledTimes(1);
expect(customOnChangeHandler).toHaveBeenCalledTimes(0);
expect(customOnClickHandler).toHaveBeenCalledTimes(0);
// Clicking again should trigger onClick and onChange on both inputs.
clearMocks();
setUntrackedChecked.call(regularInput, false);
regularInput.dispatchEvent(new Event('click', {bubbles: true}));
expect(regularOnInputHandler).toHaveBeenCalledTimes(0);
expect(regularOnChangeHandler).toHaveBeenCalledTimes(1);
expect(regularOnClickHandler).toHaveBeenCalledTimes(1);
setUntrackedChecked.call(customInput, false);
customInput.dispatchEvent(new Event('click', {bubbles: true}));
expect(customOnInputHandler).toHaveBeenCalledTimes(0);
expect(customOnChangeHandler).toHaveBeenCalledTimes(1);
expect(customOnClickHandler).toHaveBeenCalledTimes(1);
});
it('<select is=...> should have the same onChange/onInput/onClick behavior as <select>', () => {
const container = document.createElement('div');
document.body.appendChild(container);
const regularOnInputHandler = jest.fn();
const regularOnChangeHandler = jest.fn();
const regularOnClickHandler = jest.fn();
const customOnInputHandler = jest.fn();
const customOnChangeHandler = jest.fn();
const customOnClickHandler = jest.fn();
function clearMocks() {
regularOnInputHandler.mockClear();
regularOnChangeHandler.mockClear();
regularOnClickHandler.mockClear();
customOnInputHandler.mockClear();
customOnChangeHandler.mockClear();
customOnClickHandler.mockClear();
}
ReactDOM.render(
<div>
<select
onInput={regularOnInputHandler}
onChange={regularOnChangeHandler}
onClick={regularOnClickHandler}
/>
<select
is="my-custom-element"
onInput={customOnInputHandler}
onChange={customOnChangeHandler}
onClick={customOnClickHandler}
/>
</div>,
container,
);
const regularSelect = container.querySelector(
'select:not([is=my-custom-element])',
);
const customSelect = container.querySelector(
'select[is=my-custom-element]',
);
expect(regularSelect).not.toBe(customSelect);
// Clicking should only trigger onClick on both inputs.
clearMocks();
regularSelect.dispatchEvent(new Event('click', {bubbles: true}));
expect(regularOnInputHandler).toHaveBeenCalledTimes(0);
expect(regularOnChangeHandler).toHaveBeenCalledTimes(0);
expect(regularOnClickHandler).toHaveBeenCalledTimes(1);
customSelect.dispatchEvent(new Event('click', {bubbles: true}));
expect(customOnInputHandler).toHaveBeenCalledTimes(0);
expect(customOnChangeHandler).toHaveBeenCalledTimes(0);
expect(customOnClickHandler).toHaveBeenCalledTimes(1);
// Native input event should only trigger onInput on both inputs.
clearMocks();
regularSelect.dispatchEvent(new Event('input', {bubbles: true}));
expect(regularOnInputHandler).toHaveBeenCalledTimes(1);
expect(regularOnChangeHandler).toHaveBeenCalledTimes(0);
expect(regularOnClickHandler).toHaveBeenCalledTimes(0);
customSelect.dispatchEvent(new Event('input', {bubbles: true}));
expect(customOnInputHandler).toHaveBeenCalledTimes(1);
expect(customOnChangeHandler).toHaveBeenCalledTimes(0);
expect(customOnClickHandler).toHaveBeenCalledTimes(0);
// Native change event should trigger onChange.
clearMocks();
regularSelect.dispatchEvent(new Event('change', {bubbles: true}));
expect(regularOnInputHandler).toHaveBeenCalledTimes(0);
expect(regularOnChangeHandler).toHaveBeenCalledTimes(1);
expect(regularOnClickHandler).toHaveBeenCalledTimes(0);
customSelect.dispatchEvent(new Event('change', {bubbles: true}));
expect(customOnInputHandler).toHaveBeenCalledTimes(0);
expect(customOnChangeHandler).toHaveBeenCalledTimes(1);
expect(customOnClickHandler).toHaveBeenCalledTimes(0);
});
// @gate enableCustomElementPropertySupport
it('onChange/onInput/onClick on div with various types of children', () => {
const container = document.createElement('div');
document.body.appendChild(container);
const onChangeHandler = jest.fn();
const onInputHandler = jest.fn();
const onClickHandler = jest.fn();
function clearMocks() {
onChangeHandler.mockClear();
onInputHandler.mockClear();
onClickHandler.mockClear();
}
ReactDOM.render(
<div
onChange={onChangeHandler}
onInput={onInputHandler}
onClick={onClickHandler}>
<my-custom-element />
<input />
<input is="my-custom-element" />
</div>,
container,
);
const customElement = container.querySelector('my-custom-element');
const regularInput = container.querySelector(
'input:not([is="my-custom-element"])',
);
const customInput = container.querySelector(
'input[is="my-custom-element"]',
);
expect(regularInput).not.toBe(customInput);
// Custom element has no special logic for input/change.
clearMocks();
customElement.dispatchEvent(new Event('input', {bubbles: true}));
expect(onChangeHandler).toBeCalledTimes(0);
expect(onInputHandler).toBeCalledTimes(1);
expect(onClickHandler).toBeCalledTimes(0);
customElement.dispatchEvent(new Event('change', {bubbles: true}));
expect(onChangeHandler).toBeCalledTimes(1);
expect(onInputHandler).toBeCalledTimes(1);
expect(onClickHandler).toBeCalledTimes(0);
customElement.dispatchEvent(new Event('click', {bubbles: true}));
expect(onChangeHandler).toBeCalledTimes(1);
expect(onInputHandler).toBeCalledTimes(1);
expect(onClickHandler).toBeCalledTimes(1);
// Regular input treats browser input as onChange.
clearMocks();
setUntrackedValue.call(regularInput, 'hello');
regularInput.dispatchEvent(new Event('input', {bubbles: true}));
expect(onChangeHandler).toBeCalledTimes(1);
expect(onInputHandler).toBeCalledTimes(1);
expect(onClickHandler).toBeCalledTimes(0);
regularInput.dispatchEvent(new Event('change', {bubbles: true}));
expect(onChangeHandler).toBeCalledTimes(1);
expect(onInputHandler).toBeCalledTimes(1);
expect(onClickHandler).toBeCalledTimes(0);
regularInput.dispatchEvent(new Event('click', {bubbles: true}));
expect(onChangeHandler).toBeCalledTimes(1);
expect(onInputHandler).toBeCalledTimes(1);
expect(onClickHandler).toBeCalledTimes(1);
// Custom input treats browser input as onChange.
clearMocks();
setUntrackedValue.call(customInput, 'hello');
customInput.dispatchEvent(new Event('input', {bubbles: true}));
expect(onChangeHandler).toBeCalledTimes(1);
expect(onInputHandler).toBeCalledTimes(1);
expect(onClickHandler).toBeCalledTimes(0);
customInput.dispatchEvent(new Event('change', {bubbles: true}));
expect(onChangeHandler).toBeCalledTimes(1);
expect(onInputHandler).toBeCalledTimes(1);
expect(onClickHandler).toBeCalledTimes(0);
customInput.dispatchEvent(new Event('click', {bubbles: true}));
expect(onChangeHandler).toBeCalledTimes(1);
expect(onInputHandler).toBeCalledTimes(1);
expect(onClickHandler).toBeCalledTimes(1);
});
it('custom element onChange/onInput/onClick with event target input child', () => {
const container = document.createElement('div');
document.body.appendChild(container);
const onChangeHandler = jest.fn();
const onInputHandler = jest.fn();
const onClickHandler = jest.fn();
ReactDOM.render(
<my-custom-element
onChange={onChangeHandler}
onInput={onInputHandler}
onClick={onClickHandler}>
<input />
</my-custom-element>,
container,
);
const input = container.querySelector('input');
setUntrackedValue.call(input, 'hello');
input.dispatchEvent(new Event('input', {bubbles: true}));
// Simulated onChange from the child's input event
// bubbles to the parent custom element.
expect(onChangeHandler).toBeCalledTimes(1);
expect(onInputHandler).toBeCalledTimes(1);
expect(onClickHandler).toBeCalledTimes(0);
// Consequently, the native change event is ignored.
input.dispatchEvent(new Event('change', {bubbles: true}));
expect(onChangeHandler).toBeCalledTimes(1);
expect(onInputHandler).toBeCalledTimes(1);
expect(onClickHandler).toBeCalledTimes(0);
input.dispatchEvent(new Event('click', {bubbles: true}));
expect(onChangeHandler).toBeCalledTimes(1);
expect(onInputHandler).toBeCalledTimes(1);
expect(onClickHandler).toBeCalledTimes(1);
});
it('custom element onChange/onInput/onClick with event target div child', () => {
const container = document.createElement('div');
document.body.appendChild(container);
const onChangeHandler = jest.fn();
const onInputHandler = jest.fn();
const onClickHandler = jest.fn();
ReactDOM.render(
<my-custom-element
onChange={onChangeHandler}
onInput={onInputHandler}
onClick={onClickHandler}>
<div />
</my-custom-element>,
container,
);
const div = container.querySelector('div');
div.dispatchEvent(new Event('input', {bubbles: true}));
expect(onChangeHandler).toBeCalledTimes(0);
expect(onInputHandler).toBeCalledTimes(1);
expect(onClickHandler).toBeCalledTimes(0);
div.dispatchEvent(new Event('change', {bubbles: true}));
// React always ignores change event invoked on non-custom and non-input targets.
// So change event emitted on a div does not propagate upwards.
expect(onChangeHandler).toBeCalledTimes(0);
expect(onInputHandler).toBeCalledTimes(1);
expect(onClickHandler).toBeCalledTimes(0);
div.dispatchEvent(new Event('click', {bubbles: true}));
expect(onChangeHandler).toBeCalledTimes(0);
expect(onInputHandler).toBeCalledTimes(1);
expect(onClickHandler).toBeCalledTimes(1);
});
it('div onChange/onInput/onClick with event target div child', () => {
const container = document.createElement('div');
document.body.appendChild(container);
const onChangeHandler = jest.fn();
const onInputHandler = jest.fn();
const onClickHandler = jest.fn();
ReactDOM.render(
<div
onChange={onChangeHandler}
onInput={onInputHandler}
onClick={onClickHandler}>
<div />
</div>,
container,
);
const div = container.querySelector('div > div');
div.dispatchEvent(new Event('input', {bubbles: true}));
expect(onChangeHandler).toBeCalledTimes(0);
expect(onInputHandler).toBeCalledTimes(1);
expect(onClickHandler).toBeCalledTimes(0);
div.dispatchEvent(new Event('change', {bubbles: true}));
// React always ignores change event invoked on non-custom and non-input targets.
// So change event emitted on a div does not propagate upwards.
expect(onChangeHandler).toBeCalledTimes(0);
expect(onInputHandler).toBeCalledTimes(1);
expect(onClickHandler).toBeCalledTimes(0);
div.dispatchEvent(new Event('click', {bubbles: true}));
expect(onChangeHandler).toBeCalledTimes(0);
expect(onInputHandler).toBeCalledTimes(1);
expect(onClickHandler).toBeCalledTimes(1);
});
// @gate enableCustomElementPropertySupport
it('custom element onChange/onInput/onClick with event target custom element child', () => {
const container = document.createElement('div');
document.body.appendChild(container);
const onChangeHandler = jest.fn();
const onInputHandler = jest.fn();
const onClickHandler = jest.fn();
ReactDOM.render(
<my-custom-element
onChange={onChangeHandler}
onInput={onInputHandler}
onClick={onClickHandler}>
<other-custom-element />
</my-custom-element>,
container,
);
const customChild = container.querySelector('other-custom-element');
customChild.dispatchEvent(new Event('input', {bubbles: true}));
// There is no simulated onChange, only raw onInput is dispatched.
expect(onChangeHandler).toBeCalledTimes(0);
expect(onInputHandler).toBeCalledTimes(1);
expect(onClickHandler).toBeCalledTimes(0);
// The native change event propagates to the parent as onChange.
customChild.dispatchEvent(new Event('change', {bubbles: true}));
expect(onChangeHandler).toBeCalledTimes(1);
expect(onInputHandler).toBeCalledTimes(1);
expect(onClickHandler).toBeCalledTimes(0);
customChild.dispatchEvent(new Event('click', {bubbles: true}));
expect(onChangeHandler).toBeCalledTimes(1);
expect(onInputHandler).toBeCalledTimes(1);
expect(onClickHandler).toBeCalledTimes(1);
});
// @gate enableCustomElementPropertySupport
it('custom elements should allow custom events with capture event listeners', () => {
const oncustomeventCapture = jest.fn();
+11 -1
View File
@@ -23,12 +23,16 @@ import {updateValueIfChanged} from '../../client/inputValueTracking';
import {setDefaultValue} from '../../client/ReactDOMInput';
import {enqueueStateRestore} from '../ReactDOMControlledComponent';
import {disableInputAttributeSyncing} from 'shared/ReactFeatureFlags';
import {
disableInputAttributeSyncing,
enableCustomElementPropertySupport,
} from 'shared/ReactFeatureFlags';
import {batchedUpdates} from '../ReactDOMUpdateBatching';
import {
processDispatchQueue,
accumulateTwoPhaseListeners,
} from '../DOMPluginEventSystem';
import isCustomComponent from '../../shared/isCustomComponent';
function registerEvents() {
registerTwoPhaseEvent('onChange', [
@@ -292,6 +296,12 @@ function extractEvents(
}
} else if (shouldUseClickEvent(targetNode)) {
getTargetInstFunc = getTargetInstForClickEvent;
} else if (
enableCustomElementPropertySupport &&
targetInst &&
isCustomComponent(targetInst.elementType, targetInst.memoizedProps)
) {
getTargetInstFunc = getTargetInstForChangeEvent;
}
if (getTargetInstFunc) {