Skip to content
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
85ccd4d
Add component
TylerJDev Oct 10, 2025
2b1871d
Clean up function
TylerJDev Oct 10, 2025
f8e21be
Add comment
TylerJDev Oct 10, 2025
dbcc75b
Update story
TylerJDev Oct 10, 2025
b2bff28
Fix lint issue
TylerJDev Oct 10, 2025
20dd785
Update docs, tests
TylerJDev Oct 13, 2025
29f7f97
Add changeset
TylerJDev Oct 13, 2025
8eed237
Merge branch 'main' into tylerjdev/actionbar-groups
pksjce Oct 15, 2025
7538ee4
Remove menu functionality from groups
TylerJDev Oct 21, 2025
87cb0c3
Remove `label` usage
TylerJDev Oct 21, 2025
cbd06b7
Remove `label` from stories
TylerJDev Oct 21, 2025
4bbe643
Remove `label` from tests
TylerJDev Oct 21, 2025
8035d0a
Remove comment
TylerJDev Oct 21, 2025
f1bf903
Address feedback
TylerJDev Oct 23, 2025
b0a816e
Add vrt test
TylerJDev Oct 23, 2025
77076a3
Format
TylerJDev Oct 23, 2025
a8d5a47
Add `ActionBar.Menu`
TylerJDev Oct 23, 2025
f9f9440
Adjust based on context usage
TylerJDev Oct 27, 2025
792313f
Merge branch 'main' into tylerjdev/actionbar-menus
TylerJDev Oct 27, 2025
8dfa896
Fix merge
TylerJDev Oct 27, 2025
0cbdf84
Update API
TylerJDev Nov 3, 2025
72efd12
Some clean up
TylerJDev Nov 3, 2025
d1b8851
Add nested menus
TylerJDev Nov 4, 2025
9fd0f1b
Add changeset
TylerJDev Nov 4, 2025
365edfa
Merge branch 'main' into tylerjdev/actionbar-menus
TylerJDev Nov 4, 2025
9a4e164
Add leading, trailing visuals
TylerJDev Nov 4, 2025
d2917b9
Merge branch 'main' into tylerjdev/actionbar-menus
pksjce Nov 5, 2025
f6ccc36
Add docs
TylerJDev Nov 5, 2025
7f50934
Address feedback
TylerJDev Nov 5, 2025
3286ca4
Update test snapshots
TylerJDev Nov 5, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/busy-islands-fail.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@primer/react': minor
---

ActionBar: Adds `ActionBar.Group` sub component
16 changes: 16 additions & 0 deletions e2e/components/drafts/ActionBar.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,22 @@ test.describe('ActionBar', () => {
}
})

test.describe('Groups', () => {
for (const theme of themes) {
test.describe(theme, () => {
test('default @vrt', async ({page}) => {
await visit(page, {
id: 'experimental-components-actionbar-examples--with-groups',
globals: {
colorScheme: theme,
},
})
expect(await page.screenshot()).toMatchSnapshot(`drafts.ActionBar.WithGroups.${theme}.png`)
})
})
}
})

test.describe('ActionBar Interactions', () => {
for (const theme of themes) {
test.describe(theme, () => {
Expand Down
10 changes: 10 additions & 0 deletions packages/react/src/ActionBar/ActionBar.docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,16 @@
{
"name": "ActionBar.Divider",
"props": []
},
{
"name": "ActionBar.Group",
"props": [
{
"name": "children",
"type": "React.ReactNode",
"defaultValue": ""
}
]
}
]
}
50 changes: 50 additions & 0 deletions packages/react/src/ActionBar/ActionBar.examples.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
TasklistIcon,
ReplyIcon,
ThreeBarsIcon,
TrashIcon,
} from '@primer/octicons-react'
import {Button, Avatar, ActionMenu, IconButton, ActionList, Textarea} from '..'
import {Dialog} from '../DialogV1'
Expand All @@ -28,6 +29,55 @@ export default {
title: 'Experimental/Components/ActionBar/Examples',
} as Meta<typeof ActionBar>

export const WithGroups = () => (
<ActionBar aria-label="Toolbar">
<ActionBar.Group>
<>
<ActionBar.IconButton icon={BoldIcon} aria-label="Bold"></ActionBar.IconButton>
<ActionBar.IconButton icon={ItalicIcon} aria-label="Italic"></ActionBar.IconButton>
<ActionBar.IconButton icon={CodeIcon} aria-label="Code"></ActionBar.IconButton>
<ActionBar.IconButton icon={LinkIcon} aria-label="Link"></ActionBar.IconButton>
</>
</ActionBar.Group>
<ActionBar.Divider />
<ActionBar.Group>
<ActionBar.IconButton icon={FileAddedIcon} aria-label="File Added"></ActionBar.IconButton>
<ActionBar.IconButton icon={SearchIcon} aria-label="Search"></ActionBar.IconButton>
</ActionBar.Group>
<ActionBar.Group>
<ActionBar.IconButton icon={ListUnorderedIcon} aria-label="Unordered List"></ActionBar.IconButton>
<ActionBar.IconButton icon={ListOrderedIcon} aria-label="Ordered List"></ActionBar.IconButton>
</ActionBar.Group>
<ActionBar.IconButton icon={TasklistIcon} aria-label="Task List"></ActionBar.IconButton>
<ActionBar.IconButton icon={ReplyIcon} aria-label="Saved Replies"></ActionBar.IconButton>
</ActionBar>
)

export const WithMenus = () => (
<ActionBar aria-label="Toolbar">
<ActionBar.IconButton icon={ItalicIcon} aria-label="Italic"></ActionBar.IconButton>
<ActionBar.IconButton icon={CodeIcon} aria-label="Code"></ActionBar.IconButton>
<ActionBar.IconButton icon={LinkIcon} aria-label="Link"></ActionBar.IconButton>
<ActionBar.Divider />
<ActionBar.IconButton icon={FileAddedIcon} aria-label="File Added"></ActionBar.IconButton>
<ActionBar.IconButton icon={SearchIcon} aria-label="Search"></ActionBar.IconButton>
<ActionBar.Menu aria-label="More Actions" icon={ThreeBarsIcon}>
<ActionList.Item>
<ActionList.LeadingVisual>
<PencilIcon />
</ActionList.LeadingVisual>
Edit
</ActionList.Item>
<ActionList.Item>
<ActionList.LeadingVisual>
<TrashIcon />
</ActionList.LeadingVisual>
Delete
</ActionList.Item>
</ActionBar.Menu>
</ActionBar>
)

export const TextLabels = () => (
<ActionBar aria-label="Toolbar">
<Button>Edit</Button>
Expand Down
5 changes: 5 additions & 0 deletions packages/react/src/ActionBar/ActionBar.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,8 @@
background: var(--borderColor-muted);
}
}

.Group {
display: flex;
gap: inherit;
}
30 changes: 29 additions & 1 deletion packages/react/src/ActionBar/ActionBar.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,32 @@ describe('ActionBar Registry System', () => {
expect(buttons[2]).toHaveAccessibleName('Third')
})

it('should preserve group order with deep nesting', () => {
render(
<ActionBar aria-label="Deep test">
<div>
<ActionBar.Group>
<ActionBar.IconButton icon={BoldIcon} aria-label="First" />
</ActionBar.Group>
</div>
<ActionBar.Group>
<ActionBar.IconButton icon={ItalicIcon} aria-label="Second" />
</ActionBar.Group>
<div>
<ActionBar.Group>
<ActionBar.IconButton icon={CodeIcon} aria-label="Third" />
</ActionBar.Group>
</div>
</ActionBar>,
)

const buttons = screen.getAllByRole('button')
expect(buttons).toHaveLength(3)
expect(buttons[0]).toHaveAccessibleName('First')
expect(buttons[1]).toHaveAccessibleName('Second')
expect(buttons[2]).toHaveAccessibleName('Third')
})

it('should handle conditional rendering without breaking order', async () => {
const ConditionalTest = () => {
const [show, setShow] = useState([true, true, true])
Expand All @@ -108,7 +134,9 @@ describe('ActionBar Registry System', () => {
<div>
<ActionBar aria-label="Conditional">
{show[0] && <ActionBar.IconButton icon={BoldIcon} aria-label="First" />}
{show[1] && <ActionBar.IconButton icon={ItalicIcon} aria-label="Second" />}
<ActionBar.Group>
{show[1] && <ActionBar.IconButton icon={ItalicIcon} aria-label="Second" />}
</ActionBar.Group>
{show[2] && <ActionBar.IconButton icon={CodeIcon} aria-label="Third" />}
</ActionBar>
<button type="button" onClick={() => setShow([false, true, true])}>
Expand Down
Loading
Loading