Skip to content

Commit 185ddcd

Browse files
committed
Add ActionMenu to Hero and ButtonGroup changes
1 parent 9b5ffea commit 185ddcd

33 files changed

Lines changed: 864 additions & 212 deletions

.changeset/hero-button-group.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
---
2+
'@primer/react-brand': patch
3+
---
4+
5+
Added `Hero.ButtonGroup` for rendering `Button` and `ActionMenu` children. `Hero.ButtonGroup` is now the defacto way to display buttons in the `Hero`.
6+
7+
⚠️ `Hero.PrimaryAction` and `Hero.SecondaryAction` are now deprecated. Please migrate over to `Hero.ButtonGroup` as they will be removed in a future release.
8+
9+
Migrate using the following approach:
10+
11+
```diff
12+
-<Hero.PrimaryAction href="#">Primary action</Hero.PrimaryAction>
13+
-<Hero.SecondaryAction href="#">Secondary action</Hero.SecondaryAction>
14+
+<Hero.ButtonGroup>
15+
+ <Button as="a" href="#">
16+
+ Primary action
17+
+ </Button>
18+
+ <Button as="a" href="#" variant="secondary">
19+
+ Secondary action
20+
+ </Button>
21+
+</Hero.ButtonGroup>
22+
```

.changeset/soft-banners-sparkle.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@
44

55
- Restored rounded corners to `CTABanner` while preserving square edges when grid lines are enabled.
66
- Improved `ButtonGroup` to forward custom class names alongside its default styles.
7+
- Added native `ActionMenu` child support to `ButtonGroup`, including automatic positional variants and the `CTABanner.ButtonGroup` wrapper.

apps/next-docs/content/components/ButtonGroup/react.mdx

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
title: Button group
3-
description: Use the button group component to group related buttons together.
3+
description: Use the button group component to group related buttons and action menus together.
44
keywords: ['buttons', 'control', 'action', 'group', 'paired']
55
show-tabs: true
66
tab-label: React
@@ -12,14 +12,14 @@ storybook: '/brand/storybook/?path=/story/components-buttongroup--playground'
1212
import {ButtonGroupSizesProp, ButtonGroupAsProp} from './react'
1313

1414
```js
15-
import {ButtonGroup, Button} from '@primer/react-brand'
15+
import {ActionMenu, Button, ButtonGroup} from '@primer/react-brand'
1616
```
1717

1818
## Examples
1919

2020
### Default
2121

22-
This is the default variant for the ButtonGroup component. The first button in the group is the primary button and the second button is the secondary button.
22+
This is the default variant for the ButtonGroup component. The first item in the group uses the primary button variant and the second uses the subtle button variant.
2323

2424
```jsx live
2525
<ButtonGroup>
@@ -28,6 +28,25 @@ This is the default variant for the ButtonGroup component. The first button in t
2828
</ButtonGroup>
2929
```
3030

31+
### Action menus
32+
33+
`ButtonGroup` supports up to two `Button` or `ActionMenu` children and applies a default `variant` to each one.
34+
35+
Explicit `variant` overrides to `Button` or `ActionMenu.Button` will override the default values.
36+
37+
```jsx live
38+
<ButtonGroup>
39+
<Button>Primary action</Button>
40+
<ActionMenu>
41+
<ActionMenu.Button>More actions</ActionMenu.Button>
42+
<ActionMenu.Overlay aria-label="More actions">
43+
<ActionMenu.Item value="contact-sales">Contact sales</ActionMenu.Item>
44+
<ActionMenu.Item value="view-pricing">View pricing</ActionMenu.Item>
45+
</ActionMenu.Overlay>
46+
</ActionMenu>
47+
</ButtonGroup>
48+
```
49+
3150
### Sizes
3251

3352
The ButtonGroup component can be rendered in different sizes in `medium` and `large` sizes. The default size is `medium`.

apps/next-docs/content/components/Hero/react.mdx

Lines changed: 69 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import {
1818
} from './react'
1919

2020
```js
21-
import {Hero} from '@primer/react-brand'
21+
import {ActionMenu, Button, Hero} from '@primer/react-brand'
2222
```
2323

2424
## Examples
@@ -33,8 +33,36 @@ import {Hero} from '@primer/react-brand'
3333
Lorem ipsum dolor sit amet, consectetur adipiscing elit. In sapien sit ullamcorper id. Aliquam luctus sed turpis
3434
felis nam pulvinar risus elementum.
3535
</Hero.Description>
36-
<Hero.PrimaryAction href="#">Primary action</Hero.PrimaryAction>
37-
<Hero.SecondaryAction href="#">Secondary action</Hero.SecondaryAction>
36+
<Hero.ButtonGroup>
37+
<Button as="a" href="#">
38+
Primary action
39+
</Button>
40+
<Button as="a" href="#" variant="secondary">
41+
Secondary action
42+
</Button>
43+
</Hero.ButtonGroup>
44+
</Hero>
45+
```
46+
47+
### Action menus
48+
49+
`Hero.ButtonGroup` accepts up to two `Button` or `ActionMenu` children and applies the primary variant to the first item and the subtle variant to the second. Set `variant` on `Button` or `ActionMenu.Button` to override it.
50+
51+
```jsx live
52+
<Hero>
53+
<Hero.Heading>This is my super sweet hero heading</Hero.Heading>
54+
<Hero.ButtonGroup>
55+
<Button as="a" href="#">
56+
Primary action
57+
</Button>
58+
<ActionMenu>
59+
<ActionMenu.Button>More actions</ActionMenu.Button>
60+
<ActionMenu.Overlay aria-label="More actions">
61+
<ActionMenu.Item value="contact-sales">Contact sales</ActionMenu.Item>
62+
<ActionMenu.Item value="view-pricing">View pricing</ActionMenu.Item>
63+
</ActionMenu.Overlay>
64+
</ActionMenu>
65+
</Hero.ButtonGroup>
3866
</Hero>
3967
```
4068

@@ -47,14 +75,22 @@ import {Hero} from '@primer/react-brand'
4775
<Hero>
4876
<Hero.Label>Actions</Hero.Label>
4977
<Hero.Heading>Automate your workflow from idea to production</Hero.Heading>
50-
<Hero.PrimaryAction href="#">Get started with Actions</Hero.PrimaryAction>
78+
<Hero.ButtonGroup>
79+
<Button as="a" href="#">
80+
Get started with Actions
81+
</Button>
82+
</Hero.ButtonGroup>
5183
<Hero.Image src="/images/placeholder.png" alt="placeholder, blank area with a gray background color" />
5284
</Hero>
5385
<br />
5486
<Hero align="center">
5587
<Hero.Label>Actions</Hero.Label>
5688
<Hero.Heading size="2">Automate your workflow from idea to production</Hero.Heading>
57-
<Hero.PrimaryAction href="#">Get started with Actions</Hero.PrimaryAction>
89+
<Hero.ButtonGroup>
90+
<Button as="a" href="#">
91+
Get started with Actions
92+
</Button>
93+
</Hero.ButtonGroup>
5894
<Hero.Image
5995
position="inline-end"
6096
src="/images/placeholder.png"
@@ -78,7 +114,11 @@ The `GridLine` variants all feature a trailing border by default. Use `Box` if y
78114
Lorem ipsum dolor sit amet, consectetur adipiscing elit. In sapien sit ullamcorper id. Aliquam luctus sed turpis
79115
felis nam pulvinar risus elementum.
80116
</Hero.Description>
81-
<Hero.PrimaryAction href="#">Primary action</Hero.PrimaryAction>
117+
<Hero.ButtonGroup>
118+
<Button as="a" href="#">
119+
Primary action
120+
</Button>
121+
</Hero.ButtonGroup>
82122
<Hero.Image src="/images/placeholder.png" alt="placeholder, blank area with a gray background color" />
83123
</Hero>
84124
```
@@ -96,7 +136,11 @@ The `GridLine` variants all feature a trailing border by default. Use `Box` if y
96136
Lorem ipsum dolor sit amet, consectetur adipiscing elit. In sapien sit ullamcorper id. Aliquam luctus sed turpis
97137
felis nam pulvinar risus elementum.
98138
</Hero.Description>
99-
<Hero.PrimaryAction href="#">Primary action</Hero.PrimaryAction>
139+
<Hero.ButtonGroup>
140+
<Button as="a" href="#">
141+
Primary action
142+
</Button>
143+
</Hero.ButtonGroup>
100144
<Hero.Image src="/images/placeholder.png" alt="placeholder, blank area with a gray background color" />
101145
</Hero>
102146
```
@@ -115,7 +159,11 @@ Use [VideoPlayer](/components/VideoPlayer), a native `<video>` element or a YouT
115159
Lorem ipsum dolor sit amet, consectetur adipiscing elit. In sapien sit ullamcorper id. Aliquam luctus sed turpis
116160
felis nam pulvinar risus elementum.
117161
</Hero.Description>
118-
<Hero.PrimaryAction href="#">Primary action</Hero.PrimaryAction>
162+
<Hero.ButtonGroup>
163+
<Button as="a" href="#">
164+
Primary action
165+
</Button>
166+
</Hero.ButtonGroup>
119167
<Hero.Video>
120168
<iframe
121169
src="https://www.youtube.com/embed/fHwtrOcLAnI"
@@ -142,7 +190,11 @@ Use [VideoPlayer](/components/VideoPlayer), a native `<video>` element or a YouT
142190
<br />
143191
developer environments
144192
</Hero.Heading>
145-
<Hero.PrimaryAction href="#">Primary action</Hero.PrimaryAction>
193+
<Hero.ButtonGroup>
194+
<Button as="a" href="#">
195+
Primary action
196+
</Button>
197+
</Hero.ButtonGroup>
146198
</Hero>
147199
```
148200

@@ -186,8 +238,14 @@ Forwards all props from the [Heading component](/typography/Heading), including
186238

187239
Forwards `size` and `weight` props from the [Text component](/typography/Text).
188240

241+
### Hero.ButtonGroup
242+
243+
Groups up to two [Button](/components/Button) or [ActionMenu](/components/ActionMenu) children. Forwards all props from the [ButtonGroup component](/components/ButtonGroup).
244+
189245
### Hero.PrimaryAction
190246

247+
Deprecated. Use `Hero.ButtonGroup` with a `Button` child instead.
248+
191249
| name | type | default | description |
192250
| ----------- | ------------- | ------- | ----------------------------------- |
193251
| `className` | `string` | | Primary link custom class |
@@ -198,6 +256,8 @@ Forwards all props from the [Button component](/components/Button).
198256

199257
### Hero.SecondaryAction
200258

259+
Deprecated. Use `Hero.ButtonGroup` with a `Button` child instead.
260+
201261
| name | type | default | description |
202262
| ----------- | ------------- | ------- | ----------------------------------- |
203263
| `className` | `string` | | Secondary link custom class |

apps/next-docs/content/getting-started/index.mdx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ Components can be imported into any React-based application.
8383
E.g.
8484

8585
```js
86-
import {Hero} from '@primer/react-brand'
86+
import {Button, Hero} from '@primer/react-brand'
8787
```
8888

8989
```jsx
@@ -94,8 +94,14 @@ import {Hero} from '@primer/react-brand'
9494
Lorem ipsum dolor sit amet, consectetur adipiscing elit. In sapien sit ullamcorper id. Aliquam luctus sed turpis
9595
felis nam pulvinar risus elementum.
9696
</Hero.Description>
97-
<Hero.PrimaryAction href="#">Primary action</Hero.PrimaryAction>
98-
<Hero.SecondaryAction href="#">Secondary action</Hero.SecondaryAction>
97+
<Hero.ButtonGroup>
98+
<Button as="a" href="#">
99+
Primary action
100+
</Button>
101+
<Button as="a" href="#" variant="secondary">
102+
Secondary action
103+
</Button>
104+
</Hero.ButtonGroup>
99105
</Hero>
100106
```
101107

packages/mcp/src/test-utils/catalog.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export function makeCatalog(overrides: Partial<Catalog> = {}): Catalog {
1414
{
1515
name: 'Hero',
1616
module: '@primer/react-brand',
17-
subcomponents: ['Hero.Heading', 'Hero.Description', 'Hero.PrimaryAction'],
17+
subcomponents: ['Hero.Heading', 'Hero.Description', 'Hero.ButtonGroup', 'Hero.PrimaryAction'],
1818
props: [
1919
{
2020
name: 'align',
@@ -29,7 +29,12 @@ export function makeCatalog(overrides: Partial<Catalog> = {}): Catalog {
2929
{
3030
title: 'Hero example',
3131
source: 'story',
32-
code: '<Hero align="center"><Hero.Heading>Build like the best</Hero.Heading></Hero>',
32+
code: `<Hero align="center">
33+
<Hero.Heading>Build like the best</Hero.Heading>
34+
<Hero.ButtonGroup>
35+
<Button>Get started</Button>
36+
</Hero.ButtonGroup>
37+
</Hero>`,
3338
},
3439
],
3540
description: 'Prominent banner for the top of a landing page',

packages/react/src/ActionMenu/ActionMenu.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ import React, {
1212
ReactElement,
1313
useMemo,
1414
} from 'react'
15-
import {Button, ButtonProps, Text, ThemeProvider, useTheme} from '../'
15+
import {Button, ButtonProps} from '../Button'
16+
import {Text} from '../Text'
17+
import {ThemeProvider, useTheme} from '../ThemeProvider'
1618
import {useAnchoredPosition} from '../hooks/useAnchoredPosition'
1719
import {useOnClickOutside} from '../hooks/useOnClickOutside'
1820
import {useKeyboardEscape} from '../hooks/useKeyboardEscape'

packages/react/src/ButtonGroup/ButtonGroup.stories.tsx

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@ import React from 'react'
22
import type {Meta, StoryObj} from '@storybook/react'
33
import {ButtonGroup} from '.'
44
import {Button} from '../Button'
5+
import {ActionMenu} from '../ActionMenu'
56

67
const meta = {
78
title: 'Components/ButtonGroup',
89
component: ButtonGroup,
9-
subcomponents: {Button},
10+
subcomponents: {Button, ActionMenu},
1011
args: {
1112
buttonSize: 'medium',
1213
buttonsAs: 'button',
@@ -58,6 +59,36 @@ export const SingleButtonGroup: Story = {
5859
),
5960
}
6061

62+
export const WithActionMenu: Story = {
63+
render: args => (
64+
<ButtonGroup {...args}>
65+
<Button>Primary action</Button>
66+
<ActionMenu>
67+
<ActionMenu.Button>More actions</ActionMenu.Button>
68+
<ActionMenu.Overlay aria-label="More actions">
69+
<ActionMenu.Item value="Contact sales">Contact sales</ActionMenu.Item>
70+
<ActionMenu.Item value="View pricing">View pricing</ActionMenu.Item>
71+
</ActionMenu.Overlay>
72+
</ActionMenu>
73+
</ButtonGroup>
74+
),
75+
}
76+
77+
export const WithVariantOverrides: Story = {
78+
render: args => (
79+
<ButtonGroup {...args}>
80+
<Button variant="secondary">Secondary override</Button>
81+
<ActionMenu>
82+
<ActionMenu.Button variant="primary">Primary override</ActionMenu.Button>
83+
<ActionMenu.Overlay aria-label="More actions">
84+
<ActionMenu.Item value="Contact sales">Contact sales</ActionMenu.Item>
85+
<ActionMenu.Item value="View pricing">View pricing</ActionMenu.Item>
86+
</ActionMenu.Overlay>
87+
</ActionMenu>
88+
</ButtonGroup>
89+
),
90+
}
91+
6192
export const LargeButtonGroup: Story = {
6293
...Template,
6394
args: {

0 commit comments

Comments
 (0)