mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
[react-interactions] Add handleSimulateChildBlur upon DOM node removal (#17225)
* [react-interactions] Add handleSimulateChildBlur upon DOM node removal
This commit is contained in:
@@ -55,6 +55,7 @@ import {
|
||||
addRootEventTypesForResponderInstance,
|
||||
mountEventResponder,
|
||||
unmountEventResponder,
|
||||
dispatchEventForResponderEventSystem,
|
||||
} from '../events/DOMEventResponderSystem';
|
||||
import {retryIfBlockedOn} from '../events/ReactDOMEventReplaying';
|
||||
|
||||
@@ -108,6 +109,10 @@ import {
|
||||
enableFlareAPI,
|
||||
enableFundamentalAPI,
|
||||
} from 'shared/ReactFeatureFlags';
|
||||
import {
|
||||
RESPONDER_EVENT_SYSTEM,
|
||||
IS_PASSIVE,
|
||||
} from 'legacy-events/EventSystemFlags';
|
||||
|
||||
let SUPPRESS_HYDRATION_WARNING;
|
||||
if (__DEV__) {
|
||||
@@ -447,10 +452,36 @@ export function insertInContainerBefore(
|
||||
}
|
||||
}
|
||||
|
||||
function handleSimulateChildBlur(
|
||||
child: Instance | TextInstance | SuspenseInstance,
|
||||
): void {
|
||||
if (
|
||||
enableFlareAPI &&
|
||||
selectionInformation &&
|
||||
child === selectionInformation.focusedElem
|
||||
) {
|
||||
const targetFiber = getClosestInstanceFromNode(child);
|
||||
// Simlulate a blur event to the React Flare responder system.
|
||||
dispatchEventForResponderEventSystem(
|
||||
'blur',
|
||||
targetFiber,
|
||||
({
|
||||
relatedTarget: null,
|
||||
target: child,
|
||||
timeStamp: Date.now(),
|
||||
type: 'blur',
|
||||
}: any),
|
||||
((child: any): Document | Element),
|
||||
RESPONDER_EVENT_SYSTEM | IS_PASSIVE,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function removeChild(
|
||||
parentInstance: Instance,
|
||||
child: Instance | TextInstance | SuspenseInstance,
|
||||
): void {
|
||||
handleSimulateChildBlur(child);
|
||||
parentInstance.removeChild(child);
|
||||
}
|
||||
|
||||
@@ -461,6 +492,7 @@ export function removeChildFromContainer(
|
||||
if (container.nodeType === COMMENT_NODE) {
|
||||
(container.parentNode: any).removeChild(child);
|
||||
} else {
|
||||
handleSimulateChildBlur(child);
|
||||
container.removeChild(child);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,23 +77,24 @@ describe.each(table)('FocusWithin responder', hasPointerEvents => {
|
||||
describe('onFocusWithinChange', () => {
|
||||
let onFocusWithinChange, ref, innerRef, innerRef2;
|
||||
|
||||
const Component = ({show}) => {
|
||||
const listener = useFocusWithin({
|
||||
onFocusWithinChange,
|
||||
});
|
||||
return (
|
||||
<div ref={ref} listeners={listener}>
|
||||
{show && <input ref={innerRef} />}
|
||||
<div ref={innerRef2} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
onFocusWithinChange = jest.fn();
|
||||
ref = React.createRef();
|
||||
innerRef = React.createRef();
|
||||
innerRef2 = React.createRef();
|
||||
const Component = () => {
|
||||
const listener = useFocusWithin({
|
||||
onFocusWithinChange,
|
||||
});
|
||||
return (
|
||||
<div ref={ref} listeners={listener}>
|
||||
<div ref={innerRef} />
|
||||
<div ref={innerRef2} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
ReactDOM.render(<Component />, container);
|
||||
ReactDOM.render(<Component show={true} />, container);
|
||||
});
|
||||
|
||||
it('is called after "blur" and "focus" events on focus target', () => {
|
||||
@@ -140,28 +141,39 @@ describe.each(table)('FocusWithin responder', hasPointerEvents => {
|
||||
expect(onFocusWithinChange).toHaveBeenCalledTimes(2);
|
||||
expect(onFocusWithinChange).toHaveBeenCalledWith(false);
|
||||
});
|
||||
|
||||
it('is called after a focused element is unmounted', () => {
|
||||
const target = createEventTarget(innerRef.current);
|
||||
target.focus();
|
||||
expect(onFocusWithinChange).toHaveBeenCalledTimes(1);
|
||||
expect(onFocusWithinChange).toHaveBeenCalledWith(true);
|
||||
ReactDOM.render(<Component show={false} />, container);
|
||||
expect(onFocusWithinChange).toHaveBeenCalledTimes(2);
|
||||
expect(onFocusWithinChange).toHaveBeenCalledWith(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('onFocusWithinVisibleChange', () => {
|
||||
let onFocusWithinVisibleChange, ref, innerRef, innerRef2;
|
||||
|
||||
const Component = ({show}) => {
|
||||
const listener = useFocusWithin({
|
||||
onFocusWithinVisibleChange,
|
||||
});
|
||||
return (
|
||||
<div ref={ref} listeners={listener}>
|
||||
{show && <input ref={innerRef} />}
|
||||
<div ref={innerRef2} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
onFocusWithinVisibleChange = jest.fn();
|
||||
ref = React.createRef();
|
||||
innerRef = React.createRef();
|
||||
innerRef2 = React.createRef();
|
||||
const Component = () => {
|
||||
const listener = useFocusWithin({
|
||||
onFocusWithinVisibleChange,
|
||||
});
|
||||
return (
|
||||
<div ref={ref} listeners={listener}>
|
||||
<div ref={innerRef} />
|
||||
<div ref={innerRef2} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
ReactDOM.render(<Component />, container);
|
||||
ReactDOM.render(<Component show={true} />, container);
|
||||
});
|
||||
|
||||
it('is called after "focus" and "blur" on focus target if keyboard was used', () => {
|
||||
@@ -258,6 +270,18 @@ describe.each(table)('FocusWithin responder', hasPointerEvents => {
|
||||
expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(2);
|
||||
expect(onFocusWithinVisibleChange).toHaveBeenCalledWith(false);
|
||||
});
|
||||
|
||||
it('is called after a focused element is unmounted', () => {
|
||||
const inner = innerRef.current;
|
||||
const target = createEventTarget(inner);
|
||||
target.keydown({key: 'Tab'});
|
||||
target.focus();
|
||||
expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(1);
|
||||
expect(onFocusWithinVisibleChange).toHaveBeenCalledWith(true);
|
||||
ReactDOM.render(<Component show={false} />, container);
|
||||
expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(2);
|
||||
expect(onFocusWithinVisibleChange).toHaveBeenCalledWith(false);
|
||||
});
|
||||
});
|
||||
|
||||
it('expect displayName to show up for event component', () => {
|
||||
|
||||
@@ -34,6 +34,7 @@ const createEventTarget = node => ({
|
||||
},
|
||||
focus(payload) {
|
||||
node.dispatchEvent(domEvents.focus(payload));
|
||||
node.focus();
|
||||
},
|
||||
scroll(payload) {
|
||||
node.dispatchEvent(domEvents.scroll(payload));
|
||||
|
||||
Reference in New Issue
Block a user