diff --git a/.changeset/dull-moons-repair.md b/.changeset/dull-moons-repair.md
new file mode 100644
index 00000000000..25257d13a72
--- /dev/null
+++ b/.changeset/dull-moons-repair.md
@@ -0,0 +1,5 @@
+---
+'@primer/react': patch
+---
+
+ActionList: Add more guards for `ActionList.Item` before utilizing button semantics (behind feature flag `primer_react_action_list_item_as_button`)
diff --git a/packages/react/src/ActionList/ActionList.features.stories.tsx b/packages/react/src/ActionList/ActionList.features.stories.tsx
index 23e126409dd..59155c9c0f4 100644
--- a/packages/react/src/ActionList/ActionList.features.stories.tsx
+++ b/packages/react/src/ActionList/ActionList.features.stories.tsx
@@ -358,6 +358,24 @@ export const ListBoxMultiSelect = () => {
)
}
+export const WithDynamicContent = () => {
+ const [isTrue, setIsTrue] = React.useState(false)
+
+ return (
+
+
+ {
+ setIsTrue(!isTrue)
+ }}
+ >
+ Activated? {isTrue ? 'Yes' : 'No'}
+
+
+
+ )
+}
+
export const DisabledSelectedMultiselect = () => (
diff --git a/packages/react/src/ActionList/ActionList.test.tsx b/packages/react/src/ActionList/ActionList.test.tsx
index fb847677cdc..79b50978b4c 100644
--- a/packages/react/src/ActionList/ActionList.test.tsx
+++ b/packages/react/src/ActionList/ActionList.test.tsx
@@ -586,6 +586,37 @@ describe('ActionList', () => {
expect(mockOnSelect).toHaveBeenCalledTimes(1)
})
+ it('should not render buttons when feature flag is enabled and is specified role', async () => {
+ const {getByRole} = HTMLRender(
+
+
+ Item 1
+ Item 2
+ Item 3
+ Item 4
+ Item 5
+
+ ,
+ )
+
+ const option = getByRole('option')
+ expect(option.tagName).toBe('LI')
+ expect(option.textContent).toBe('Item 1')
+
+ const menuItem = getByRole('menuitem')
+ expect(menuItem.tagName).toBe('LI')
+
+ const menuItemCheckbox = getByRole('menuitemcheckbox')
+ expect(menuItemCheckbox.tagName).toBe('LI')
+
+ const menuItemRadio = getByRole('menuitemradio')
+ expect(menuItemRadio.tagName).toBe('LI')
+
+ const button = getByRole('button')
+ expect(button.parentElement?.tagName).toBe('LI')
+ expect(button.textContent).toBe('Item 5')
+ })
+
it('should be navigatable with arrow keys for certain roles', async () => {
HTMLRender(
diff --git a/packages/react/src/ActionList/Item.tsx b/packages/react/src/ActionList/Item.tsx
index 53dae15b6a3..5db1765bfb5 100644
--- a/packages/react/src/ActionList/Item.tsx
+++ b/packages/react/src/ActionList/Item.tsx
@@ -24,6 +24,14 @@ import VisuallyHidden from '../_VisuallyHidden'
const LiBox = styled.li(sx)
+const ButtonItemContainer = React.forwardRef(({as: Component = 'button', children, styles, ...props}, forwardedRef) => {
+ return (
+
+ {children}
+
+ )
+}) as PolymorphicForwardRefComponent
+
export const Item = React.forwardRef(
(
{
@@ -112,7 +120,12 @@ export const Item = React.forwardRef(
const itemSelectionAttribute = selectionAttribute || inferredSelectionAttribute
// Ensures ActionList.Item retains list item semantics if a valid ARIA role is applied, or if item is inactive
- const listSemantics = listRole === 'listbox' || listRole === 'menu' || inactive || container === 'NavList'
+ const listItemSemantics =
+ role === 'option' || role === 'menuitem' || role === 'menuitemradio' || role === 'menuitemcheckbox'
+
+ const listRoleTypes = ['listbox', 'menu', 'list']
+ const listSemantics =
+ (listRole && listRoleTypes.includes(listRole)) || inactive || container === 'NavList' || listItemSemantics
const buttonSemantics = !listSemantics && !_PrivateItemWrapper && buttonSemanticsFeatureFlag
const {theme} = useTheme()
@@ -268,22 +281,9 @@ export const Item = React.forwardRef(
const trailingVisualId = `${itemId}--trailing-visual`
const inactiveWarningId = inactive && !showInactiveIndicator ? `${itemId}--warning-message` : undefined
- const ButtonItemWrapper = React.forwardRef(({as: Component = 'button', children, ...props}, forwardedRef) => {
- return (
- (styles, sxProp)}
- ref={forwardedRef}
- {...props}
- >
- {children}
-
- )
- }) as PolymorphicForwardRefComponent
-
let DefaultItemWrapper = React.Fragment
if (buttonSemanticsFeatureFlag) {
- DefaultItemWrapper = listSemantics ? React.Fragment : ButtonItemWrapper
+ DefaultItemWrapper = listSemantics ? React.Fragment : ButtonItemContainer
}
const ItemWrapper = _PrivateItemWrapper || DefaultItemWrapper