Skip to content

Commit

Permalink
Render string passed to ActionMenu.Button ID prop (#3897)
Browse files Browse the repository at this point in the history
* fixes ActionMenu.Button 'id' prop to render in the DOM

* adds changeset

* also considers Anchor as a menu button child
  • Loading branch information
mperrotti authored Nov 14, 2023
1 parent 942fb57 commit 6b0c118
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 1 deletion.
7 changes: 7 additions & 0 deletions .changeset/fifty-coins-enjoy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@primer/react': patch
---

When passing an `id` prop to ActionMenu.Button, it will be passed as the rendered button element's `id` attribute instead of being set as an automatically generated ID.

<!-- Changed components: ActionMenu -->
7 changes: 6 additions & 1 deletion src/ActionMenu/ActionMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,13 @@ const Menu: React.FC<React.PropsWithChildren<ActionMenuProps>> = ({
const onOpen = React.useCallback(() => setCombinedOpenState(true), [setCombinedOpenState])
const onClose = React.useCallback(() => setCombinedOpenState(false), [setCombinedOpenState])

const menuButtonChild = React.Children.toArray(children).find(
child => React.isValidElement<ActionMenuButtonProps>(child) && (child.type === MenuButton || child.type === Anchor),
)
const menuButtonChildId = React.isValidElement(menuButtonChild) ? menuButtonChild.props.id : undefined

const anchorRef = useProvidedRefOrCreate(externalAnchorRef)
const anchorId = useId()
const anchorId = useId(menuButtonChildId)
let renderAnchor: AnchoredOverlayProps['renderAnchor'] = null
// 🚨 Hack for good API!
// we strip out Anchor from children and pass it to AnchoredOverlay to render
Expand Down
32 changes: 32 additions & 0 deletions src/__tests__/ActionMenu.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -364,4 +364,36 @@ describe('ActionMenu', () => {
button.focus()
expect(component.getByRole('tooltip')).toBeInTheDocument()
})

it('should pass the "id" prop from ActionMenu.Button to the HTML button', async () => {
const buttonId = 'toggle-menu-custom-id'
const component = HTMLRender(
<ThemeProvider theme={theme}>
<SSRProvider>
<BaseStyles>
<ActionMenu>
<ActionMenu.Button id={buttonId}>Toggle Menu</ActionMenu.Button>
<ActionMenu.Overlay>
<ActionList>
<ActionList.Item>New file</ActionList.Item>
<ActionList.Divider />
<ActionList.Item>Copy link</ActionList.Item>
<ActionList.Item>Edit file</ActionList.Item>
<ActionList.Item variant="danger" onSelect={event => event.preventDefault()}>
Delete file
</ActionList.Item>
<ActionList.LinkItem href="//github.com" title="anchor" aria-keyshortcuts="s">
Github
</ActionList.LinkItem>
</ActionList>
</ActionMenu.Overlay>
</ActionMenu>
</BaseStyles>
</SSRProvider>
</ThemeProvider>,
)
const button = component.getByRole('button')

expect(button.id).toBe(buttonId)
})
})

0 comments on commit 6b0c118

Please sign in to comment.