diff --git a/packages/react-dom/src/events/DOMEventResponderSystem.js b/packages/react-dom/src/events/DOMEventResponderSystem.js
index 2e3da99145f..c7d8d84a79f 100644
--- a/packages/react-dom/src/events/DOMEventResponderSystem.js
+++ b/packages/react-dom/src/events/DOMEventResponderSystem.js
@@ -170,6 +170,22 @@ const eventResponderContext: ReactResponderContext = {
}
return false;
},
+ isTargetDirectlyWithinEventComponent(target: Element | Document): boolean {
+ validateResponderContext();
+ if (target != null) {
+ let fiber = getClosestInstanceFromNode(target);
+ while (fiber !== null) {
+ if (fiber.stateNode === currentInstance) {
+ return true;
+ }
+ if (fiber.tag === EventComponent) {
+ return false;
+ }
+ fiber = fiber.return;
+ }
+ }
+ return false;
+ },
isTargetWithinElement(
childTarget: Element | Document,
parentTarget: Element | Document,
diff --git a/packages/react-dom/src/events/__tests__/DOMEventResponderSystem-test.internal.js b/packages/react-dom/src/events/__tests__/DOMEventResponderSystem-test.internal.js
index 8d476b4568c..f1555339573 100644
--- a/packages/react-dom/src/events/__tests__/DOMEventResponderSystem-test.internal.js
+++ b/packages/react-dom/src/events/__tests__/DOMEventResponderSystem-test.internal.js
@@ -917,4 +917,52 @@ describe('DOMEventResponderSystem', () => {
},
]);
});
+
+ it('isTargetDirectlyWithinEventComponent works', () => {
+ const buttonRef = React.createRef();
+ const divRef = React.createRef();
+ const log = [];
+
+ const EventComponent = createReactEventComponent(
+ ['pointerout'],
+ undefined,
+ undefined,
+ (event, context) => {
+ const isWithin = context.isTargetDirectlyWithinEventComponent(
+ event.nativeEvent.relatedTarget,
+ );
+ log.push(isWithin);
+ },
+ );
+
+ const Test = () => (
+
+
+
+
+
+
+ );
+ ReactDOM.render(, container);
+
+ const createEvent = (type, data) => {
+ const event = document.createEvent('CustomEvent');
+ event.initCustomEvent(type, true, true);
+ if (data != null) {
+ Object.entries(data).forEach(([key, value]) => {
+ event[key] = value;
+ });
+ }
+ return event;
+ };
+
+ buttonRef.current.dispatchEvent(
+ createEvent('pointerout', {relatedTarget: divRef.current}),
+ );
+ divRef.current.dispatchEvent(
+ createEvent('pointerout', {relatedTarget: buttonRef.current}),
+ );
+
+ expect(log).toEqual([false, true, false]);
+ });
});
diff --git a/packages/react-events/src/Hover.js b/packages/react-events/src/Hover.js
index 90b5a00883c..b18dc3610b3 100644
--- a/packages/react-events/src/Hover.js
+++ b/packages/react-events/src/Hover.js
@@ -99,7 +99,9 @@ function dispatchHoverStartEvents(
if (event !== null) {
const {nativeEvent} = event;
if (
- context.isTargetWithinEventComponent((nativeEvent: any).relatedTarget)
+ context.isTargetDirectlyWithinEventComponent(
+ (nativeEvent: any).relatedTarget,
+ )
) {
return;
}
@@ -156,7 +158,9 @@ function dispatchHoverEndEvents(
if (event !== null) {
const {nativeEvent} = event;
if (
- context.isTargetWithinEventComponent((nativeEvent: any).relatedTarget)
+ context.isTargetDirectlyWithinEventComponent(
+ (nativeEvent: any).relatedTarget,
+ )
) {
return;
}
diff --git a/packages/react-events/src/__tests__/Hover-test.internal.js b/packages/react-events/src/__tests__/Hover-test.internal.js
index dc29723b659..b4f2303008c 100644
--- a/packages/react-events/src/__tests__/Hover-test.internal.js
+++ b/packages/react-events/src/__tests__/Hover-test.internal.js
@@ -443,18 +443,17 @@ describe('Hover event responder', () => {
createPointerEvent('pointerover', {relatedTarget: innerRef.current}),
);
outerRef.current.dispatchEvent(createPointerEvent('pointerout'));
- // TODO: correct result should include commented events
expect(events).toEqual([
'outer: onHoverStart',
'outer: onHoverChange',
- // 'outer: onHoverEnd',
- // 'outer: onHoverChange',
+ 'outer: onHoverEnd',
+ 'outer: onHoverChange',
'inner: onHoverStart',
'inner: onHoverChange',
'inner: onHoverEnd',
'inner: onHoverChange',
- // 'outer: onHoverStart',
- // 'outer: onHoverChange',
+ 'outer: onHoverStart',
+ 'outer: onHoverChange',
'outer: onHoverEnd',
'outer: onHoverChange',
]);
diff --git a/packages/shared/ReactTypes.js b/packages/shared/ReactTypes.js
index d625733dfd3..8f1cd7c76e0 100644
--- a/packages/shared/ReactTypes.js
+++ b/packages/shared/ReactTypes.js
@@ -167,6 +167,7 @@ export type ReactResponderContext = {
parentTarget: Element | Document,
) => boolean,
isTargetWithinEventComponent: (Element | Document) => boolean,
+ isTargetDirectlyWithinEventComponent: (Element | Document) => boolean,
isPositionWithinTouchHitTarget: (
doc: Document,
x: number,