diff --git a/.changeset/rare-jeans-rush.md b/.changeset/rare-jeans-rush.md new file mode 100644 index 00000000000..2368e160309 --- /dev/null +++ b/.changeset/rare-jeans-rush.md @@ -0,0 +1,5 @@ +--- +'@primer/react': patch +--- + +Fix missing `aria-selected` & `aria-checked` attributes in ActionList items diff --git a/src/ActionList/ActionList.test.tsx b/src/ActionList/ActionList.test.tsx index b3d8c814c41..a6a8a431c7a 100644 --- a/src/ActionList/ActionList.test.tsx +++ b/src/ActionList/ActionList.test.tsx @@ -44,7 +44,6 @@ function SingleSelectListStory(): JSX.Element { key={index} role="option" selected={index === selectedIndex} - aria-selected={index === selectedIndex} onSelect={() => setSelectedIndex(index)} disabled={project.disabled} inactiveText={project.inactiveText} diff --git a/src/ActionList/Item.tsx b/src/ActionList/Item.tsx index c2148c9bec7..a5736755e67 100644 --- a/src/ActionList/Item.tsx +++ b/src/ActionList/Item.tsx @@ -98,15 +98,24 @@ export const Item = React.forwardRef( : listSelectionVariant /** Infer item role based on the container */ - let itemRole: ActionListItemProps['role'] + let inferredItemRole: ActionListItemProps['role'] if (container === 'ActionMenu') { - if (selectionVariant === 'single') itemRole = 'menuitemradio' - else if (selectionVariant === 'multiple') itemRole = 'menuitemcheckbox' - else itemRole = 'menuitem' + if (selectionVariant === 'single') inferredItemRole = 'menuitemradio' + else if (selectionVariant === 'multiple') inferredItemRole = 'menuitemcheckbox' + else inferredItemRole = 'menuitem' } else if (container === 'SelectPanel' && listRole === 'listbox') { - if (selectionVariant !== undefined) itemRole = 'option' + if (selectionVariant !== undefined) inferredItemRole = 'option' } + const itemRole = role || inferredItemRole + + /** Infer the proper selection attribute based on the item's role */ + let inferredSelectionAttribute: 'aria-selected' | 'aria-checked' | undefined + if (itemRole === 'menuitemradio' || itemRole === 'menuitemcheckbox') inferredSelectionAttribute = 'aria-checked' + else if (itemRole === 'option') inferredSelectionAttribute = 'aria-selected' + + const itemSelectionAttribute = selectionAttribute || inferredSelectionAttribute + const {theme} = useTheme() const activeStyles = { @@ -234,6 +243,10 @@ export const Item = React.forwardRef( const ItemWrapper = _PrivateItemWrapper || React.Fragment + // only apply aria-selected and aria-checked to selectable items + const selectableRoles = ['menuitemradio', 'menuitemcheckbox', 'option'] + const includeSelectionAttribute = itemSelectionAttribute && itemRole && selectableRoles.includes(itemRole) + const menuItemProps = { onClick: clickHandler, onKeyPress: keyPressHandler, @@ -244,12 +257,12 @@ export const Item = React.forwardRef( 'aria-describedby': slots.blockDescription ? [blockDescriptionId, inactiveWarningId].join(' ') : inactiveWarningId, - ...(selectionAttribute && {[selectionAttribute]: selected}), - role: role || itemRole, + ...(includeSelectionAttribute && {[itemSelectionAttribute]: selected}), + role: itemRole, id: itemId, } - const containerProps = _PrivateItemWrapper ? {role: role || itemRole ? 'none' : undefined} : menuItemProps + const containerProps = _PrivateItemWrapper ? {role: itemRole ? 'none' : undefined} : menuItemProps const wrapperProps = _PrivateItemWrapper ? menuItemProps : {} diff --git a/src/__tests__/__snapshots__/Autocomplete.test.tsx.snap b/src/__tests__/__snapshots__/Autocomplete.test.tsx.snap index 74982962b7d..c569d6033de 100644 --- a/src/__tests__/__snapshots__/Autocomplete.test.tsx.snap +++ b/src/__tests__/__snapshots__/Autocomplete.test.tsx.snap @@ -789,6 +789,7 @@ exports[`snapshots renders a menu that contains an item to add to the menu 1`] = >