diff --git a/.changeset/big-corners-type.md b/.changeset/big-corners-type.md new file mode 100644 index 00000000000..67dcfae0f89 --- /dev/null +++ b/.changeset/big-corners-type.md @@ -0,0 +1,5 @@ +--- +"@primer/react": patch +--- + +Don't focus Autocomplete menu items while menu is closed: fix SR announcement bug and menu item skipping bug diff --git a/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Dev-Within-Dialog-dark-colorblind-css-anchor-positioning-linux.png b/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Dev-Within-Dialog-dark-colorblind-css-anchor-positioning-linux.png index b124e96e548..586c01d3f2b 100644 Binary files a/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Dev-Within-Dialog-dark-colorblind-css-anchor-positioning-linux.png and b/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Dev-Within-Dialog-dark-colorblind-css-anchor-positioning-linux.png differ diff --git a/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Dev-Within-Dialog-light-colorblind-linux.png b/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Dev-Within-Dialog-light-colorblind-linux.png index aa8cecf7a10..3fba5bb2c12 100644 Binary files a/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Dev-Within-Dialog-light-colorblind-linux.png and b/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Dev-Within-Dialog-light-colorblind-linux.png differ diff --git a/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Dev-Within-Dialog-light-high-contrast-linux.png b/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Dev-Within-Dialog-light-high-contrast-linux.png index 5deadf4ac4a..6cd1de84ea1 100644 Binary files a/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Dev-Within-Dialog-light-high-contrast-linux.png and b/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Dev-Within-Dialog-light-high-contrast-linux.png differ diff --git a/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Dev-Within-Dialog-light-tritanopia-linux.png b/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Dev-Within-Dialog-light-tritanopia-linux.png index aa8cecf7a10..3fba5bb2c12 100644 Binary files a/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Dev-Within-Dialog-light-tritanopia-linux.png and b/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Dev-Within-Dialog-light-tritanopia-linux.png differ diff --git a/.playwright/snapshots/components/Autocomplete.test.ts-snapshots/Autocomplete-In-Overlay-With-Custom-Scroll-Container-Ref-dark-colorblind-linux.png b/.playwright/snapshots/components/Autocomplete.test.ts-snapshots/Autocomplete-In-Overlay-With-Custom-Scroll-Container-Ref-dark-colorblind-linux.png index a2d92809dcb..2fbda6f3a3d 100644 Binary files a/.playwright/snapshots/components/Autocomplete.test.ts-snapshots/Autocomplete-In-Overlay-With-Custom-Scroll-Container-Ref-dark-colorblind-linux.png and b/.playwright/snapshots/components/Autocomplete.test.ts-snapshots/Autocomplete-In-Overlay-With-Custom-Scroll-Container-Ref-dark-colorblind-linux.png differ diff --git a/.playwright/snapshots/components/Autocomplete.test.ts-snapshots/Autocomplete-In-Overlay-With-Custom-Scroll-Container-Ref-dark-dimmed-linux.png b/.playwright/snapshots/components/Autocomplete.test.ts-snapshots/Autocomplete-In-Overlay-With-Custom-Scroll-Container-Ref-dark-dimmed-linux.png index 09b18cdaa2c..df144646792 100644 Binary files a/.playwright/snapshots/components/Autocomplete.test.ts-snapshots/Autocomplete-In-Overlay-With-Custom-Scroll-Container-Ref-dark-dimmed-linux.png and b/.playwright/snapshots/components/Autocomplete.test.ts-snapshots/Autocomplete-In-Overlay-With-Custom-Scroll-Container-Ref-dark-dimmed-linux.png differ diff --git a/.playwright/snapshots/components/Autocomplete.test.ts-snapshots/Autocomplete-In-Overlay-With-Custom-Scroll-Container-Ref-dark-high-contrast-linux.png b/.playwright/snapshots/components/Autocomplete.test.ts-snapshots/Autocomplete-In-Overlay-With-Custom-Scroll-Container-Ref-dark-high-contrast-linux.png index a5d214d4cb6..80bbe785f3a 100644 Binary files a/.playwright/snapshots/components/Autocomplete.test.ts-snapshots/Autocomplete-In-Overlay-With-Custom-Scroll-Container-Ref-dark-high-contrast-linux.png and b/.playwright/snapshots/components/Autocomplete.test.ts-snapshots/Autocomplete-In-Overlay-With-Custom-Scroll-Container-Ref-dark-high-contrast-linux.png differ diff --git a/.playwright/snapshots/components/Autocomplete.test.ts-snapshots/Autocomplete-In-Overlay-With-Custom-Scroll-Container-Ref-dark-linux.png b/.playwright/snapshots/components/Autocomplete.test.ts-snapshots/Autocomplete-In-Overlay-With-Custom-Scroll-Container-Ref-dark-linux.png index ece5062ee2f..2fbda6f3a3d 100644 Binary files a/.playwright/snapshots/components/Autocomplete.test.ts-snapshots/Autocomplete-In-Overlay-With-Custom-Scroll-Container-Ref-dark-linux.png and b/.playwright/snapshots/components/Autocomplete.test.ts-snapshots/Autocomplete-In-Overlay-With-Custom-Scroll-Container-Ref-dark-linux.png differ diff --git a/.playwright/snapshots/components/Autocomplete.test.ts-snapshots/Autocomplete-In-Overlay-With-Custom-Scroll-Container-Ref-dark-tritanopia-linux.png b/.playwright/snapshots/components/Autocomplete.test.ts-snapshots/Autocomplete-In-Overlay-With-Custom-Scroll-Container-Ref-dark-tritanopia-linux.png index ece5062ee2f..2fbda6f3a3d 100644 Binary files a/.playwright/snapshots/components/Autocomplete.test.ts-snapshots/Autocomplete-In-Overlay-With-Custom-Scroll-Container-Ref-dark-tritanopia-linux.png and b/.playwright/snapshots/components/Autocomplete.test.ts-snapshots/Autocomplete-In-Overlay-With-Custom-Scroll-Container-Ref-dark-tritanopia-linux.png differ diff --git a/.playwright/snapshots/components/Autocomplete.test.ts-snapshots/Autocomplete-In-Overlay-With-Custom-Scroll-Container-Ref-light-colorblind-linux.png b/.playwright/snapshots/components/Autocomplete.test.ts-snapshots/Autocomplete-In-Overlay-With-Custom-Scroll-Container-Ref-light-colorblind-linux.png index 3a966daebb3..17c9c6ff91f 100644 Binary files a/.playwright/snapshots/components/Autocomplete.test.ts-snapshots/Autocomplete-In-Overlay-With-Custom-Scroll-Container-Ref-light-colorblind-linux.png and b/.playwright/snapshots/components/Autocomplete.test.ts-snapshots/Autocomplete-In-Overlay-With-Custom-Scroll-Container-Ref-light-colorblind-linux.png differ diff --git a/.playwright/snapshots/components/Autocomplete.test.ts-snapshots/Autocomplete-In-Overlay-With-Custom-Scroll-Container-Ref-light-high-contrast-linux.png b/.playwright/snapshots/components/Autocomplete.test.ts-snapshots/Autocomplete-In-Overlay-With-Custom-Scroll-Container-Ref-light-high-contrast-linux.png index ff8eeeff8c2..116a39581ee 100644 Binary files a/.playwright/snapshots/components/Autocomplete.test.ts-snapshots/Autocomplete-In-Overlay-With-Custom-Scroll-Container-Ref-light-high-contrast-linux.png and b/.playwright/snapshots/components/Autocomplete.test.ts-snapshots/Autocomplete-In-Overlay-With-Custom-Scroll-Container-Ref-light-high-contrast-linux.png differ diff --git a/.playwright/snapshots/components/Autocomplete.test.ts-snapshots/Autocomplete-In-Overlay-With-Custom-Scroll-Container-Ref-light-linux.png b/.playwright/snapshots/components/Autocomplete.test.ts-snapshots/Autocomplete-In-Overlay-With-Custom-Scroll-Container-Ref-light-linux.png index 3a966daebb3..17c9c6ff91f 100644 Binary files a/.playwright/snapshots/components/Autocomplete.test.ts-snapshots/Autocomplete-In-Overlay-With-Custom-Scroll-Container-Ref-light-linux.png and b/.playwright/snapshots/components/Autocomplete.test.ts-snapshots/Autocomplete-In-Overlay-With-Custom-Scroll-Container-Ref-light-linux.png differ diff --git a/.playwright/snapshots/components/Autocomplete.test.ts-snapshots/Autocomplete-In-Overlay-With-Custom-Scroll-Container-Ref-light-tritanopia-linux.png b/.playwright/snapshots/components/Autocomplete.test.ts-snapshots/Autocomplete-In-Overlay-With-Custom-Scroll-Container-Ref-light-tritanopia-linux.png index 3a966daebb3..17c9c6ff91f 100644 Binary files a/.playwright/snapshots/components/Autocomplete.test.ts-snapshots/Autocomplete-In-Overlay-With-Custom-Scroll-Container-Ref-light-tritanopia-linux.png and b/.playwright/snapshots/components/Autocomplete.test.ts-snapshots/Autocomplete-In-Overlay-With-Custom-Scroll-Container-Ref-light-tritanopia-linux.png differ diff --git a/packages/react/src/Autocomplete/Autocomplete.features.stories.module.css b/packages/react/src/Autocomplete/Autocomplete.features.stories.module.css index 913e5e5cdfa..7bc09e99537 100644 --- a/packages/react/src/Autocomplete/Autocomplete.features.stories.module.css +++ b/packages/react/src/Autocomplete/Autocomplete.features.stories.module.css @@ -36,8 +36,9 @@ } .OverlayInputBar { + /* stylelint-disable-next-line primer/spacing */ + padding: 1px; border-width: 0; - border-bottom-width: var(--borderWidth-default); border-color: var(--borderColor-default); border-style: solid; } @@ -45,8 +46,8 @@ .OverlayInput { display: flex; border: 0; - padding-left: var(--base-size-16); - padding-right: var(--base-size-16); + /* stylelint-disable-next-line primer/borders */ + border-radius: calc(var(--borderRadius-large) - 1px); padding-top: var(--base-size-4); padding-bottom: var(--base-size-4); box-shadow: none; diff --git a/packages/react/src/Autocomplete/Autocomplete.test.tsx b/packages/react/src/Autocomplete/Autocomplete.test.tsx index 054a9b52e0a..da069dbb231 100644 --- a/packages/react/src/Autocomplete/Autocomplete.test.tsx +++ b/packages/react/src/Autocomplete/Autocomplete.test.tsx @@ -1,6 +1,6 @@ import {render, fireEvent, screen, waitFor} from '@testing-library/react' import userEvent from '@testing-library/user-event' -import React from 'react' +import {createRef} from 'react' import {describe, expect, it, vi} from 'vitest' import type {AutocompleteInputProps} from '../Autocomplete' import Autocomplete from '../Autocomplete' @@ -269,6 +269,49 @@ describe('Autocomplete', () => { }) describe('Autocomplete.Menu', () => { + it('only sets an active descendant while a menu rendered without an overlay is open', async () => { + const user = userEvent.setup() + const ancestorRef = createRef() + const handleAncestorKeyDown = (event: KeyboardEvent) => { + if (!event.defaultPrevented && event.key === 'ArrowDown') { + ancestorRef.current?.querySelector('[role="option"]')?.focus() + } + } + + render( + +
+ + + + + +
+
, + ) + const inputNode = screen.getByRole('combobox', {name: AUTOCOMPLETE_LABEL}) + ancestorRef.current?.addEventListener('keydown', handleAncestorKeyDown) + + expect(inputNode).toHaveAttribute('aria-expanded', 'false') + expect(screen.getByRole('listbox', {hidden: true}).closest('[hidden]')).toBeInTheDocument() + + await user.click(inputNode) + expect(inputNode).not.toHaveAttribute('aria-activedescendant') + + await user.keyboard('{ArrowDown}') + await waitFor(() => { + expect(inputNode).toHaveFocus() + expect(inputNode).toHaveAttribute('aria-activedescendant', mockItems[0].id) + expect(screen.getByRole('option', {name: mockItems[0].text})).not.toHaveFocus() + }) + + // eslint-disable-next-line github/no-blur + fireEvent.blur(inputNode) + await waitFor(() => expect(inputNode).not.toHaveAttribute('aria-activedescendant')) + }) + it('calls a custom filter function', async () => { const user = userEvent.setup() const filterFnMock = vi.fn() diff --git a/packages/react/src/Autocomplete/AutocompleteInput.tsx b/packages/react/src/Autocomplete/AutocompleteInput.tsx index d512a53e3cf..18a2ab14e3b 100644 --- a/packages/react/src/Autocomplete/AutocompleteInput.tsx +++ b/packages/react/src/Autocomplete/AutocompleteInput.tsx @@ -28,6 +28,7 @@ const AutocompleteInput = React.forwardRef( onBlur, onChange, onKeyDown, + onKeyDownCapture, onKeyUp, onKeyPress, value, @@ -111,6 +112,14 @@ const AutocompleteInput = React.forwardRef( [inputRef, setInputValue, setHighlightRemainingText, onKeyDown, showMenu, setShowMenu], ) + const handleInputKeyDownCapture: KeyboardEventHandler = event => { + onKeyDownCapture?.(event) + + if (!showMenu && ARROW_KEYS_NAV.has(event.key) && !event.altKey) { + event.preventDefault() + } + } + const handleInputKeyUp: KeyboardEventHandler = useCallback( event => { onKeyUp?.(event) @@ -174,6 +183,7 @@ const AutocompleteInput = React.forwardRef( onBlur={handleInputBlur} onChange={handleInputChange} onKeyDown={handleInputKeyDown} + onKeyDownCapture={handleInputKeyDownCapture} onKeyPress={onInputKeyPress} onKeyUp={handleInputKeyUp} ref={mergedRef} diff --git a/packages/react/src/Autocomplete/AutocompleteMenu.tsx b/packages/react/src/Autocomplete/AutocompleteMenu.tsx index 57ecf08420c..1e9ea6addbb 100644 --- a/packages/react/src/Autocomplete/AutocompleteMenu.tsx +++ b/packages/react/src/Autocomplete/AutocompleteMenu.tsx @@ -287,6 +287,7 @@ function AutocompleteMenu(props: AutocompleteMe useFocusZone( { containerRef: listContainerRef, + disabled: !showMenu, focusOutBehavior: 'wrap', focusableElementFilter: element => { return !(element instanceof HTMLInputElement) @@ -354,7 +355,7 @@ function AutocompleteMenu(props: AutocompleteMe } return ( - +