@samitouri / QOS-React / commits / fa2fa3564d

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,