Experimental event API: adds context.isTargetDirectlyWithinEventComponent (#15481)
Dominic Gannaway committed
Apr 24, 2019 at 11:02 UTC
fa2fa3564d52f98c5796204a73d38228588a4a36
5 files changed
+75
-7
packages/react-dom/src/events/DOMEventResponderSystem.js
+16
@@ -170,6 +170,22 @@ const eventResponderContext: ReactResponderContext = {
170
}
171
return false;
172
},
173
+ isTargetDirectlyWithinEventComponent(target: Element | Document): boolean {
174
+ validateResponderContext();
175
+ if (target != null) {
176
+ let fiber = getClosestInstanceFromNode(target);
177
+ while (fiber !== null) {
178
+ if (fiber.stateNode === currentInstance) {
179
+ return true;
180
+ }
181
+ if (fiber.tag === EventComponent) {
182
+ return false;
183
+ }
184
+ fiber = fiber.return;
185
+ }
186
+ }
187
+ return false;
188
+ },
189
isTargetWithinElement(
190
childTarget: Element | Document,
191
parentTarget: Element | Document,
packages/react-dom/src/events/__tests__/DOMEventResponderSystem-test.internal.js
+48
@@ -948,4 +948,52 @@ describe('DOMEventResponderSystem', () => {
948
},
949
]);
950
});
951
+
952
+ it('isTargetDirectlyWithinEventComponent works', () => {
953
+ const buttonRef = React.createRef();
954
+ const divRef = React.createRef();
955
+ const log = [];
956
+
957
+ const EventComponent = createReactEventComponent(
958
+ ['pointerout'],
959
+ undefined,
960
+ undefined,
961
+ (event, context) => {
962
+ const isWithin = context.isTargetDirectlyWithinEventComponent(
963
+ event.nativeEvent.relatedTarget,
964
+ );
965
+ log.push(isWithin);
966
+ },
967
+ );
968
+
969
+ const Test = () => (
970
+ <EventComponent>
971
+ <div ref={divRef} />
972
+ <EventComponent>
973
+ <button ref={buttonRef}>Click me!</button>
974
+ </EventComponent>
975
+ </EventComponent>
976
+ );
977
+ ReactDOM.render(<Test />, container);
978
+
979
+ const createEvent = (type, data) => {
980
+ const event = document.createEvent('CustomEvent');
981
+ event.initCustomEvent(type, true, true);
982
+ if (data != null) {
983
+ Object.entries(data).forEach(([key, value]) => {
984
+ event[key] = value;
985
+ });
986
+ }
987
+ return event;
988
+ };
989
+
990
+ buttonRef.current.dispatchEvent(
991
+ createEvent('pointerout', {relatedTarget: divRef.current}),
992
+ );
993
+ divRef.current.dispatchEvent(
994
+ createEvent('pointerout', {relatedTarget: buttonRef.current}),
995
+ );
996
+
997
+ expect(log).toEqual([false, true, false]);
998
+ });
999
});
packages/react-events/src/Hover.js
+6
-2
@@ -99,7 +99,9 @@ function dispatchHoverStartEvents(
99
if (event !== null) {
100
const {nativeEvent} = event;
101
if (
102
- context.isTargetWithinEventComponent((nativeEvent: any).relatedTarget)
102
+ context.isTargetDirectlyWithinEventComponent(
103
+ (nativeEvent: any).relatedTarget,
104
+ )
105
) {
106
return;
107
}
@@ -156,7 +158,9 @@ function dispatchHoverEndEvents(
158
if (event !== null) {
159
const {nativeEvent} = event;
160
if (
159
- context.isTargetWithinEventComponent((nativeEvent: any).relatedTarget)
161
+ context.isTargetDirectlyWithinEventComponent(
162
+ (nativeEvent: any).relatedTarget,
163
+ )
164
) {
165
return;
166
}
packages/react-events/src/__tests__/Hover-test.internal.js
+4
-5
@@ -443,18 +443,17 @@ describe('Hover event responder', () => {
443
createPointerEvent('pointerover', {relatedTarget: innerRef.current}),
444
);
445
outerRef.current.dispatchEvent(createPointerEvent('pointerout'));
446
- // TODO: correct result should include commented events
446
expect(events).toEqual([
447
'outer: onHoverStart',
448
'outer: onHoverChange',
450
- // 'outer: onHoverEnd',
451
- // 'outer: onHoverChange',
449
+ 'outer: onHoverEnd',
450
+ 'outer: onHoverChange',
451
'inner: onHoverStart',
452
'inner: onHoverChange',
453
'inner: onHoverEnd',
454
'inner: onHoverChange',
456
- // 'outer: onHoverStart',
457
- // 'outer: onHoverChange',
455
+ 'outer: onHoverStart',
456
+ 'outer: onHoverChange',
457
'outer: onHoverEnd',
458
'outer: onHoverChange',
459
]);
packages/shared/ReactTypes.js
+1
@@ -171,6 +171,7 @@ export type ReactResponderContext = {
171
parentTarget: Element | Document,
172
) => boolean,
173
isTargetWithinEventComponent: (Element | Document) => boolean,
174
+ isTargetDirectlyWithinEventComponent: (Element | Document) => boolean,
175
isPositionWithinTouchHitTarget: (
176
doc: Document,
177
x: number,