diff --git a/packages/react-aria-components/test/ListBox.test.js b/packages/react-aria-components/test/ListBox.test.js index 026eea5d36a..ef72826d104 100644 --- a/packages/react-aria-components/test/ListBox.test.js +++ b/packages/react-aria-components/test/ListBox.test.js @@ -2523,6 +2523,39 @@ describe('keyboard modifier keys', () => { expect(document.activeElement).toBe(options[1]); }); + it('does not activate an element focused during selection with Enter', async () => { + let onClick = jest.fn(); + let triggerRef = React.createRef(); + let {getByRole} = render( + <> + + { + // oxlint-disable-next-line rsp-rules/act-events-test + triggerRef.current.focus(); + }}> + Cat + Dog + + + ); + + let options = within(getByRole('listbox')).getAllByRole('option'); + act(() => options[0].focus()); + + // Selection moves focus to the trigger during keydown. Unless Enter's default is + // prevented, the browser dispatches the activation click to the newly focused + // element. + await user.keyboard('{Enter}'); + + expect(triggerRef.current).toHaveFocus(); + expect(onClick).not.toHaveBeenCalled(); + }); + describe('multiple selection, replace behavior', () => { it('replaces selection as focus moves with arrow keys', async () => { let onSelectionChange = jest.fn(); diff --git a/packages/react-aria/src/interactions/usePress.ts b/packages/react-aria/src/interactions/usePress.ts index 517c8019b90..8b847ac1848 100644 --- a/packages/react-aria/src/interactions/usePress.ts +++ b/packages/react-aria/src/interactions/usePress.ts @@ -391,7 +391,7 @@ export function usePress(props: PressHookProps): PressResult { isValidKeyboardEvent(e.nativeEvent, e.currentTarget as Element) && nodeContains(e.currentTarget as Element, getEventTarget(e) as Element) ) { - if (shouldPreventDefaultKeyboard(getEventTarget(e) as Element, e.key)) { + if (shouldPreventDefaultKeyboard(getEventTarget(e) as Element, e)) { e.preventDefault(); } @@ -497,7 +497,7 @@ export function usePress(props: PressHookProps): PressResult { let onKeyUp = (e: KeyboardEvent) => { if (state.isPressed && state.target && isValidKeyboardEvent(e, state.target)) { - if (shouldPreventDefaultKeyboard(getEventTarget(e) as Element, e.key)) { + if (shouldPreventDefaultKeyboard(getEventTarget(e) as Element, e)) { e.preventDefault(); } @@ -1161,9 +1161,13 @@ function shouldPreventDefaultUp(target: Element) { return true; } -function shouldPreventDefaultKeyboard(target: Element, key: string) { - // Don't prevent the contextmenu shortcut on mac. - if (isMac() && key === 'Enter') { +function shouldPreventDefaultKeyboard( + target: Element, + event: Pick +) { + let {key} = event; + // Don't prevent the contextmenu shortcut (Control + Enter) on mac. + if (isMac() && key === 'Enter' && event.ctrlKey) { return false; } diff --git a/packages/react-aria/test/interactions/usePress.test.js b/packages/react-aria/test/interactions/usePress.test.js index 1f944d2d9ca..e54662e0f27 100644 --- a/packages/react-aria/test/interactions/usePress.test.js +++ b/packages/react-aria/test/interactions/usePress.test.js @@ -3173,6 +3173,29 @@ describe('usePress', function () { ]); }); + it('should prevent default on Enter on macOS', function () { + let {getByText} = render(); + using spy = jest.spyOn(window.navigator, 'platform', 'get').mockImplementation(() => 'Mac'); + + let el = getByText('test'); + let allowDefault = fireEvent.keyDown(el, {key: 'Enter'}); + expect(allowDefault).toBe(false); + allowDefault = fireEvent.keyUp(el, {key: 'Enter'}); + expect(allowDefault).toBe(false); + spy.mockRestore(); + }); + + it('should not prevent default on Control + Enter on macOS so the context menu can open', function () { + let {getByText} = render(); + using spy = jest.spyOn(window.navigator, 'platform', 'get').mockImplementation(() => 'Mac'); + + let el = getByText('test'); + let allowDefault = fireEvent.keyDown(el, {key: 'Enter', ctrlKey: true}); + expect(allowDefault).toBe(true); + fireEvent.keyUp(el, {key: 'Enter', ctrlKey: true}); + spy.mockRestore(); + }); + it('should handle when focus moves between keydown and keyup', function () { let events = []; let addEvent = e => events.push(e);