diff --git a/.changeset/four-shoes-yell.md b/.changeset/four-shoes-yell.md deleted file mode 100644 index 6920922ec27..00000000000 --- a/.changeset/four-shoes-yell.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@primer/react': minor ---- - -Set `openOnFocus` default to `false`, making the menu closed initially rather than opening on focus of input diff --git a/packages/react/src/Autocomplete/Autocomplete.docs.json b/packages/react/src/Autocomplete/Autocomplete.docs.json index ff776c4ab01..80da9258ba4 100644 --- a/packages/react/src/Autocomplete/Autocomplete.docs.json +++ b/packages/react/src/Autocomplete/Autocomplete.docs.json @@ -23,9 +23,8 @@ { "name": "openOnFocus", "type": "boolean", - "defaultValue": "false", - "description": "Whether the associated autocomplete menu should open on an input focus event", - "deprecated": true + "defaultValue": "true", + "description": "Whether the associated autocomplete menu should open on an input focus event" } ], "passthrough": { diff --git a/packages/react/src/Autocomplete/AutocompleteInput.tsx b/packages/react/src/Autocomplete/AutocompleteInput.tsx index 873781965b5..f114d881aee 100644 --- a/packages/react/src/Autocomplete/AutocompleteInput.tsx +++ b/packages/react/src/Autocomplete/AutocompleteInput.tsx @@ -10,11 +10,8 @@ import useSafeTimeout from '../hooks/useSafeTimeout' type InternalAutocompleteInputProps = { // eslint-disable-next-line @typescript-eslint/no-explicit-any as?: React.ComponentType> - - /** - * @deprecated `openOnFocus` is deprecated and will be removed in v38. - * When `true`, autocomplete menu will show on focus or click. - */ + // When false, the autocomplete menu will not render either on mouse click or + // keyboard focus. openOnFocus?: boolean } @@ -31,7 +28,7 @@ const AutocompleteInput = React.forwardRef( onKeyUp, onKeyPress, value, - openOnFocus = false, + openOnFocus = true, ...props }, forwardedRef, @@ -55,12 +52,15 @@ const AutocompleteInput = React.forwardRef( const [highlightRemainingText, setHighlightRemainingText] = useState(true) const {safeSetTimeout} = useSafeTimeout() - const handleInputFocus: FocusEventHandler = event => { - onFocus?.(event) - if (openOnFocus) { - setShowMenu(true) - } - } + const handleInputFocus: FocusEventHandler = useCallback( + event => { + if (openOnFocus) { + onFocus?.(event) + setShowMenu(true) + } + }, + [onFocus, setShowMenu, openOnFocus], + ) const handleInputBlur: FocusEventHandler = useCallback( event => { @@ -78,13 +78,16 @@ const AutocompleteInput = React.forwardRef( [onBlur, setShowMenu, inputRef, safeSetTimeout], ) - const handleInputChange: ChangeEventHandler = event => { - onChange && onChange(event) - setInputValue(event.currentTarget.value) - if (!showMenu) { - setShowMenu(true) - } - } + const handleInputChange: ChangeEventHandler = useCallback( + event => { + onChange && onChange(event) + setInputValue(event.currentTarget.value) + if (!showMenu) { + setShowMenu(true) + } + }, + [onChange, setInputValue, setShowMenu, showMenu], + ) const handleInputKeyDown: KeyboardEventHandler = useCallback( event => { @@ -119,6 +122,7 @@ const AutocompleteInput = React.forwardRef( const onInputKeyPress: KeyboardEventHandler = useCallback( event => { onKeyPress && onKeyPress(event) + if (showMenu && event.key === 'Enter' && activeDescendantRef.current) { event.preventDefault() event.nativeEvent.stopImmediatePropagation() diff --git a/packages/react/src/__tests__/Autocomplete.test.tsx b/packages/react/src/__tests__/Autocomplete.test.tsx index 34542a4ba2e..3ce3bbc3598 100644 --- a/packages/react/src/__tests__/Autocomplete.test.tsx +++ b/packages/react/src/__tests__/Autocomplete.test.tsx @@ -1,4 +1,4 @@ -import {render as HTMLRender, fireEvent, screen, waitFor} from '@testing-library/react' +import {render as HTMLRender, fireEvent, waitFor, screen} from '@testing-library/react' import userEvent from '@testing-library/user-event' import React from 'react' import type {AutocompleteInputProps} from '../Autocomplete' @@ -130,15 +130,14 @@ describe('Autocomplete', () => { expect(onKeyPressMock).toHaveBeenCalled() }) - it('opens the menu when the input is focused and arrow key is pressed', () => { + it('opens the menu when the input is focused', () => { const {getByLabelText} = HTMLRender( , ) const inputNode = getByLabelText(AUTOCOMPLETE_LABEL) expect(inputNode.getAttribute('aria-expanded')).not.toBe('true') - fireEvent.click(inputNode) - fireEvent.keyDown(inputNode, {key: 'ArrowDown'}) + fireEvent.focus(inputNode) expect(inputNode.getAttribute('aria-expanded')).toBe('true') }) @@ -149,14 +148,13 @@ describe('Autocomplete', () => { const inputNode = getByLabelText(AUTOCOMPLETE_LABEL) expect(inputNode.getAttribute('aria-expanded')).not.toBe('true') - fireEvent.click(inputNode) - fireEvent.keyDown(inputNode, {key: 'ArrowDown'}) - + fireEvent.focus(inputNode) expect(inputNode.getAttribute('aria-expanded')).toBe('true') + // eslint-disable-next-line github/no-blur + fireEvent.blur(inputNode) - await userEvent.tab() - - expect(inputNode.getAttribute('aria-expanded')).not.toBe('true') + // wait a tick for blur to finish + await waitFor(() => expect(inputNode.getAttribute('aria-expanded')).not.toBe('true')) }) it('sets the input value to the suggested item text and highlights the untyped part of the word', async () => { @@ -308,7 +306,7 @@ describe('Autocomplete', () => { expect(onSelectedChangeMock).not.toHaveBeenCalled() if (inputNode) { fireEvent.focus(inputNode) - await user.type(inputNode, '{arrowdown}{enter}') + await user.type(inputNode, '{enter}') } expect(onSelectedChangeMock).toHaveBeenCalledWith([mockItems[0]]) @@ -331,8 +329,6 @@ describe('Autocomplete', () => { if (inputNode) { expect(inputNode.getAttribute('aria-expanded')).not.toBe('true') await user.click(inputNode) - - fireEvent.keyDown(inputNode, {key: 'ArrowDown'}) expect(inputNode.getAttribute('aria-expanded')).toBe('true') await user.click(getByText(mockItems[1].text)) expect(inputNode.getAttribute('aria-expanded')).toBe('true') @@ -356,7 +352,6 @@ describe('Autocomplete', () => { if (inputNode) { expect(inputNode.getAttribute('aria-expanded')).not.toBe('true') await user.click(inputNode) - fireEvent.keyDown(inputNode, {key: 'ArrowDown'}) expect(inputNode.getAttribute('aria-expanded')).toBe('true') await user.click(getByText(mockItems[1].text)) expect(inputNode.getAttribute('aria-expanded')).not.toBe('true')