@samitouri / QOS-React-2 / commits / fec74f99da

Event API: ensure preventDefault works for nested targets (#15633)

Dominic Gannaway committed May 13, 2019 at 13:45 UTC fec74f99daebd07c38a647941afa6bf0e0b2306e
4 files changed +46 -5
packages/react-dom/src/events/DOMEventResponderSystem.js
+22
@@ -208,6 +208,7 @@ const eventResponderContext: ReactResponderContext = {
208 childTarget: Element | Document,
209 parentTarget: Element | Document,
210 ): boolean {
211 + validateResponderContext();
212 const childFiber = getClosestInstanceFromNode(childTarget);
213 const parentFiber = getClosestInstanceFromNode(parentTarget);
214
@@ -345,6 +346,7 @@ const eventResponderContext: ReactResponderContext = {
346 }
347 },
348 getFocusableElementsInScope(): Array<HTMLElement> {
349 + validateResponderContext();
350 const focusableElements = [];
351 const eventComponentInstance = ((currentInstance: any): ReactEventComponentInstance);
352 let node = ((eventComponentInstance.currentFiber: any): Fiber).child;
@@ -383,6 +385,7 @@ const eventResponderContext: ReactResponderContext = {
385 getEventPointerType(
386 event: ReactResponderEvent,
387 ): '' | 'mouse' | 'keyboard' | 'pen' | 'touch' {
388 + validateResponderContext();
389 const nativeEvent: any = event.nativeEvent;
390 const {type, pointerType} = nativeEvent;
391 if (pointerType != null) {
@@ -400,6 +403,7 @@ const eventResponderContext: ReactResponderContext = {
403 return '';
404 },
405 getEventCurrentTarget(event: ReactResponderEvent): Element {
406 + validateResponderContext();
407 const target: any = event.target;
408 let currentTarget = target;
409 while (
@@ -412,8 +416,26 @@ const eventResponderContext: ReactResponderContext = {
416 return currentTarget;
417 },
418 getTimeStamp(): number {
419 + validateResponderContext();
420 return currentTimeStamp;
421 },
422 + isTargetWithinHostComponent(
423 + target: Element | Document,
424 + elementType: string,
425 + ): boolean {
426 + validateResponderContext();
427 + let fiber = getClosestInstanceFromNode(target);
428 + while (fiber !== null) {
429 + if (fiber.stateNode === currentInstance) {
430 + return false;
431 + }
432 + if (fiber.tag === HostComponent && fiber.type === elementType) {
433 + return true;
434 + }
435 + fiber = fiber.return;
436 + }
437 + return false;
438 + },
439 };
440
441 function isTargetWithinEventComponent(target: Element | Document): boolean {
packages/react-events/src/Press.js
+1 -5
@@ -418,10 +418,6 @@ function dispatchCancel(
418 }
419 }
420
421 -function isAnchorTagElement(eventTarget: EventTarget): boolean {
422 - return (eventTarget: any).nodeName === 'A';
423 -}
424 -
421 function isValidKeyPress(key: string): boolean {
422 // Accessibility for keyboards. Space and Enter only.
423 return key === ' ' || key === 'Enter';
@@ -673,7 +669,7 @@ const PressResponder = {
669 }
670
671 case 'click': {
676 - if (isAnchorTagElement(target)) {
672 + if (context.isTargetWithinHostComponent(target, 'a')) {
673 const {
674 altKey,
675 ctrlKey,
packages/react-events/src/__tests__/Press-test.internal.js
+19
@@ -1965,6 +1965,25 @@ describe('Event responder: Press', () => {
1965 expect(preventDefault).toBeCalled();
1966 });
1967
1968 + it('prevents native behaviour by default with nested elements', () => {
1969 + const onPress = jest.fn();
1970 + const preventDefault = jest.fn();
1971 + const ref = React.createRef();
1972 + const element = (
1973 + <Press onPress={onPress}>
1974 + <a href="#">
1975 + <div ref={ref} />
1976 + </a>
1977 + </Press>
1978 + );
1979 + ReactDOM.render(element, container);
1980 +
1981 + ref.current.dispatchEvent(createEvent('pointerdown'));
1982 + ref.current.dispatchEvent(createEvent('pointerup'));
1983 + ref.current.dispatchEvent(createEvent('click', {preventDefault}));
1984 + expect(preventDefault).toBeCalled();
1985 + });
1986 +
1987 it('uses native behaviour for interactions with modifier keys', () => {
1988 const onPress = jest.fn();
1989 const preventDefault = jest.fn();
packages/shared/ReactTypes.js
+4
@@ -194,4 +194,8 @@ export type ReactResponderContext = {
194 ): '' | 'mouse' | 'keyboard' | 'pen' | 'touch',
195 getEventCurrentTarget(event: ReactResponderEvent): Element,
196 getTimeStamp: () => number,
197 + isTargetWithinHostComponent: (
198 + target: Element | Document,
199 + elementType: string,
200 + ) => boolean,
201 };