From e538d26e1c6c2b1fde4e0cf84d9477c39ed39c29 Mon Sep 17 00:00:00 2001 From: Kim Niedermaier Date: Thu, 3 Apr 2025 14:31:46 -0700 Subject: [PATCH 1/2] Apply `aria-invalid` property to element with `listbox` role --- packages/design-system/src/components/Dropdown/Dropdown.tsx | 2 +- .../design-system/src/components/Dropdown/DropdownMenu.tsx | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/design-system/src/components/Dropdown/Dropdown.tsx b/packages/design-system/src/components/Dropdown/Dropdown.tsx index f6161cfb56..cb529d4b29 100644 --- a/packages/design-system/src/components/Dropdown/Dropdown.tsx +++ b/packages/design-system/src/components/Dropdown/Dropdown.tsx @@ -276,7 +276,6 @@ export const Dropdown: React.FC = (props: DropdownProps) => { ref: mergeRefs([triggerRef, inputRef, useAutofocus(props.autoFocus)]), 'aria-controls': menuId, 'aria-labelledby': `${buttonContentId} ${labelProps.id}`, - 'aria-invalid': invalid, 'aria-describedby': describeField({ ...props, hintId, errorId }), // TODO: Someday we may want to add this `combobox` role back to the button, but right // now desktop VoiceOver has an issue. It seems to interpret the selected value in the @@ -319,6 +318,7 @@ export const Dropdown: React.FC = (props: DropdownProps) => { {state.isOpen && ( extends AriaListBoxOptions { + ariaInvalid?: React.AriaAttributes['aria-invalid']; children?: React.ReactNode; componentClass: string; heading?: React.ReactNode; @@ -22,6 +23,7 @@ interface DropdownMenuProps extends AriaListBoxOptions { } export function DropdownMenu({ + ariaInvalid, children, componentClass, heading, @@ -93,6 +95,7 @@ export function DropdownMenu({
    Date: Thu, 3 Apr 2025 16:52:26 -0700 Subject: [PATCH 2/2] updates unit and browser tests --- .../src/components/Dropdown/Dropdown.test.tsx | 14 +++++++++++--- .../Dropdown/__snapshots__/Dropdown.test.tsx.snap | 2 +- .../__snapshots__/ds-dropdown.test.tsx.snap | 2 -- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/packages/design-system/src/components/Dropdown/Dropdown.test.tsx b/packages/design-system/src/components/Dropdown/Dropdown.test.tsx index 9cfc56fa4f..e58a35d21d 100644 --- a/packages/design-system/src/components/Dropdown/Dropdown.test.tsx +++ b/packages/design-system/src/components/Dropdown/Dropdown.test.tsx @@ -134,15 +134,19 @@ describe('Dropdown', () => { ); const button = getButton(); - expect(button).toHaveAttribute('aria-invalid', 'true'); expect(button).toHaveAttribute('aria-describedby', errorId); expect(button).toHaveClass('ds-c-field--error'); + userEvent.click(button); + + const listbox = screen.getByRole('listbox'); + expect(listbox).toHaveAttribute('aria-invalid', 'true'); + const error = container.querySelector(`#${errorId}`); expect(error).toMatchSnapshot(); }); - it('supports bottom placed error', () => { + it('supports bottom placed error', async () => { const errorId = 'my-error'; const { container } = makeDropdown( { @@ -154,10 +158,14 @@ describe('Dropdown', () => { ); const button = getButton(); - expect(button).toHaveAttribute('aria-invalid', 'true'); expect(button).toHaveAttribute('aria-describedby', errorId); expect(button).toHaveClass('ds-c-field--error'); + userEvent.click(button); + + const listbox = screen.getByRole('listbox'); + expect(listbox).toHaveAttribute('aria-invalid', 'true'); + const dropdown = container.querySelector('.ds-c-dropdown'); const error = container.querySelector(`#${errorId}`); expect(dropdown.lastChild).toEqual(error); diff --git a/packages/design-system/src/components/Dropdown/__snapshots__/Dropdown.test.tsx.snap b/packages/design-system/src/components/Dropdown/__snapshots__/Dropdown.test.tsx.snap index abb22ea3e1..c294c8ee6f 100644 --- a/packages/design-system/src/components/Dropdown/__snapshots__/Dropdown.test.tsx.snap +++ b/packages/design-system/src/components/Dropdown/__snapshots__/Dropdown.test.tsx.snap @@ -2,6 +2,7 @@ exports[`Dropdown accepts optgroup children 1`] = `