From 42485af5646e0e82dfce758fa1c1d3b6d795edaa Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Mon, 13 May 2019 13:27:06 +0100 Subject: [PATCH 1/2] Event API: ensure preventDefault works for nested targets --- .../src/events/DOMEventResponderSystem.js | 22 +++++++++++++++++++ packages/react-events/src/Press.js | 6 +---- .../src/__tests__/Press-test.internal.js | 19 ++++++++++++++++ packages/shared/ReactTypes.js | 4 ++++ 4 files changed, 46 insertions(+), 5 deletions(-) diff --git a/packages/react-dom/src/events/DOMEventResponderSystem.js b/packages/react-dom/src/events/DOMEventResponderSystem.js index 0f47f72a4f9ae..251e5c6695f53 100644 --- a/packages/react-dom/src/events/DOMEventResponderSystem.js +++ b/packages/react-dom/src/events/DOMEventResponderSystem.js @@ -208,6 +208,7 @@ const eventResponderContext: ReactResponderContext = { childTarget: Element | Document, parentTarget: Element | Document, ): boolean { + validateResponderContext(); const childFiber = getClosestInstanceFromNode(childTarget); const parentFiber = getClosestInstanceFromNode(parentTarget); @@ -345,6 +346,7 @@ const eventResponderContext: ReactResponderContext = { } }, getFocusableElementsInScope(): Array { + validateResponderContext(); const focusableElements = []; const eventComponentInstance = ((currentInstance: any): ReactEventComponentInstance); let node = ((eventComponentInstance.currentFiber: any): Fiber).child; @@ -383,6 +385,7 @@ const eventResponderContext: ReactResponderContext = { getEventPointerType( event: ReactResponderEvent, ): '' | 'mouse' | 'keyboard' | 'pen' | 'touch' { + validateResponderContext(); const nativeEvent: any = event.nativeEvent; const {type, pointerType} = nativeEvent; if (pointerType != null) { @@ -400,6 +403,7 @@ const eventResponderContext: ReactResponderContext = { return ''; }, getEventCurrentTarget(event: ReactResponderEvent): Element { + validateResponderContext(); const target: any = event.target; let currentTarget = target; while ( @@ -412,8 +416,26 @@ const eventResponderContext: ReactResponderContext = { return currentTarget; }, getTimeStamp(): number { + validateResponderContext(); return currentTimeStamp; }, + isTargetWithinHostComponent( + target: Element | Document, + elementType: string, + ): boolean { + validateResponderContext(); + let fiber = getClosestInstanceFromNode(target); + while (fiber !== null) { + if (fiber.stateNode === currentInstance) { + return false; + } + if (fiber.tag === HostComponent && fiber.type === elementType) { + return true; + } + fiber = fiber.return; + } + return false; + }, }; function isTargetWithinEventComponent(target: Element | Document): boolean { diff --git a/packages/react-events/src/Press.js b/packages/react-events/src/Press.js index d67296bc6bf05..994a10c8f087a 100644 --- a/packages/react-events/src/Press.js +++ b/packages/react-events/src/Press.js @@ -418,10 +418,6 @@ function dispatchCancel( } } -function isAnchorTagElement(eventTarget: EventTarget): boolean { - return (eventTarget: any).nodeName === 'A'; -} - function isValidKeyPress(key: string): boolean { // Accessibility for keyboards. Space and Enter only. return key === ' ' || key === 'Enter'; @@ -673,7 +669,7 @@ const PressResponder = { } case 'click': { - if (isAnchorTagElement(target)) { + if (context.isTargetWithinHostComponent(target, 'a')) { const { altKey, ctrlKey, diff --git a/packages/react-events/src/__tests__/Press-test.internal.js b/packages/react-events/src/__tests__/Press-test.internal.js index 7ebbfccb65b2b..39a38e4dc83ef 100644 --- a/packages/react-events/src/__tests__/Press-test.internal.js +++ b/packages/react-events/src/__tests__/Press-test.internal.js @@ -1965,6 +1965,25 @@ describe('Event responder: Press', () => { expect(preventDefault).toBeCalled(); }); + it.only('prevents native behaviour by default #2', () => { + const onPress = jest.fn(); + const preventDefault = jest.fn(); + const ref = React.createRef(); + const element = ( + + +
+ + + ); + ReactDOM.render(element, container); + + ref.current.dispatchEvent(createEvent('pointerdown')); + ref.current.dispatchEvent(createEvent('pointerup')); + ref.current.dispatchEvent(createEvent('click', {preventDefault})); + expect(preventDefault).toBeCalled(); + }); + it('uses native behaviour for interactions with modifier keys', () => { const onPress = jest.fn(); const preventDefault = jest.fn(); diff --git a/packages/shared/ReactTypes.js b/packages/shared/ReactTypes.js index 7a90c0f80252c..b64f38c27bc99 100644 --- a/packages/shared/ReactTypes.js +++ b/packages/shared/ReactTypes.js @@ -194,4 +194,8 @@ export type ReactResponderContext = { ): '' | 'mouse' | 'keyboard' | 'pen' | 'touch', getEventCurrentTarget(event: ReactResponderEvent): Element, getTimeStamp: () => number, + isTargetWithinHostComponent: ( + target: Element | Document, + elementType: string, + ) => boolean, }; From 62b718f8f46c69913ac0ef8a1cbb1d1ba2cb1885 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Mon, 13 May 2019 13:31:53 +0100 Subject: [PATCH 2/2] Remove .only --- packages/react-events/src/__tests__/Press-test.internal.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-events/src/__tests__/Press-test.internal.js b/packages/react-events/src/__tests__/Press-test.internal.js index 39a38e4dc83ef..edcd56899cdf3 100644 --- a/packages/react-events/src/__tests__/Press-test.internal.js +++ b/packages/react-events/src/__tests__/Press-test.internal.js @@ -1965,7 +1965,7 @@ describe('Event responder: Press', () => { expect(preventDefault).toBeCalled(); }); - it.only('prevents native behaviour by default #2', () => { + it('prevents native behaviour by default with nested elements', () => { const onPress = jest.fn(); const preventDefault = jest.fn(); const ref = React.createRef();