diff --git a/packages/react-native-web/src/exports/Pressable/__tests__/__snapshots__/index-test.js.snap b/packages/react-native-web/src/exports/Pressable/__tests__/__snapshots__/index-test.js.snap index 1ba73854..52e058a9 100644 --- a/packages/react-native-web/src/exports/Pressable/__tests__/__snapshots__/index-test.js.snap +++ b/packages/react-native-web/src/exports/Pressable/__tests__/__snapshots__/index-test.js.snap @@ -61,14 +61,14 @@ exports[`components/Pressable hover interaction 3`] = ` /> `; -exports[`components/Pressable press interaction (keyboard) 1`] = ` +exports[`components/Pressable press interaction (keyboard) trigger press when keyup is on the same element 1`] = `
`; -exports[`components/Pressable press interaction (keyboard) 2`] = ` +exports[`components/Pressable press interaction (keyboard) trigger press when keyup is on the same element 2`] = `
`; -exports[`components/Pressable press interaction (keyboard) 3`] = `null`; +exports[`components/Pressable press interaction (keyboard) trigger press when keyup is on the same element 3`] = `null`; exports[`components/Pressable press interaction (pointer) 1`] = `
{ expect(onContextMenu).toBeCalled(); }); - test('press interaction (keyboard)', () => { - let container; - const onPress = jest.fn(); - const onPressIn = jest.fn(); - const onPressOut = jest.fn(); - const ref = React.createRef(); + describe('press interaction (keyboard)', () => { + test('trigger press when keyup is on the same element', () => { + let container; + const onPress = jest.fn(); + const onPressIn = jest.fn(); + const onPressOut = jest.fn(); + const ref = React.createRef(); - function TestCase() { - const [shown, setShown] = React.useState(true); - return shown ? ( - - pressed ?
: null - } - onPress={(e) => { - onPress(e); - setShown(false); - }} - onPressIn={onPressIn} - onPressOut={onPressOut} - ref={ref} - style={({ pressed }) => [pressed && { outline: 'press-ring' }]} - /> - ) : null; - } + function TestCase() { + const [shown, setShown] = React.useState(true); + return shown ? ( + + pressed ?
: null + } + onPress={(e) => { + onPress(e); + setShown(false); + }} + onPressIn={onPressIn} + onPressOut={onPressOut} + ref={ref} + style={({ pressed }) => [pressed && { outline: 'press-ring' }]} + /> + ) : null; + } - act(() => { - ({ container } = render()); + act(() => { + ({ container } = render()); + }); + const target = createEventTarget(ref.current); + expect(container.firstChild).toMatchSnapshot(); + act(() => { + target.keydown({ key: 'Enter' }); + jest.runAllTimers(); + }); + expect(onPressIn).toBeCalled(); + expect(container.firstChild).toMatchSnapshot(); + act(() => { + target.keyup({ key: 'Enter' }); + jest.runAllTimers(); + }); + expect(onPressOut).toBeCalled(); + expect(onPress).toBeCalled(); + expect(container.firstChild).toMatchSnapshot(); }); - const target = createEventTarget(ref.current); - expect(container.firstChild).toMatchSnapshot(); - act(() => { - target.keydown({ key: 'Enter' }); - jest.runAllTimers(); + + test('ignore press when keyup is on a different element', () => { + const onPress = jest.fn(); + const firstRef = React.createRef(); + + function TestCase() { + return ( + { + onPress(e); + }} + ref={firstRef} + /> + ); + } + + act(() => { + render(); + }); + const target = createEventTarget(firstRef.current); + const body = createEventTarget(document.body); + act(() => { + target.keydown({ key: 'Enter' }); + body.keyup({ key: 'Enter' }); + jest.runAllTimers(); + }); + expect(onPress).not.toBeCalled(); }); - expect(onPressIn).toBeCalled(); - expect(container.firstChild).toMatchSnapshot(); - act(() => { - target.keyup({ key: 'Enter' }); - jest.runAllTimers(); - }); - expect(onPressOut).toBeCalled(); - expect(onPress).toBeCalled(); - expect(container.firstChild).toMatchSnapshot(); }); test('press interaction as button (keyboard)', () => { diff --git a/packages/react-native-web/src/modules/usePressEvents/PressResponder.js b/packages/react-native-web/src/modules/usePressEvents/PressResponder.js index 857db94b..fa9d26d7 100644 --- a/packages/react-native-web/src/modules/usePressEvents/PressResponder.js +++ b/packages/react-native-web/src/modules/usePressEvents/PressResponder.js @@ -233,6 +233,7 @@ export default class PressResponder { pageY: number |}>; _touchState: TouchState = NOT_RESPONDER; + _responderElement: ?HTMLElement = null; constructor(config: PressResponderConfig) { this.configure(config); @@ -320,10 +321,13 @@ export default class PressResponder { elementType === 'input' || elementType === 'select' || elementType === 'textarea'; + const isActiveElement = this._responderElement === target; - if (onPress != null && !isNativeInteractiveElement) { + if (onPress != null && !isNativeInteractiveElement && isActiveElement) { onPress(event); } + + this._responderElement = null; } }; @@ -345,6 +349,7 @@ export default class PressResponder { if (!disabled && isValidKeyPress(event)) { if (this._touchState === NOT_RESPONDER) { start(event, false); + this._responderElement = target; // Listen to 'keyup' on document to account for situations where // focus is moved to another element during 'keydown'. document.addEventListener('keyup', keyupHandler);