Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
22 changes: 22 additions & 0 deletions .changeset/hero-button-group.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
'@primer/react-brand': patch
---

Added `Hero.ButtonGroup` for rendering `Button` and `ActionMenu` children. `Hero.ButtonGroup` is now the defacto way to display buttons in the `Hero`.
Comment thread
Copilot marked this conversation as resolved.
Outdated

⚠️ `Hero.PrimaryAction` and `Hero.SecondaryAction` are now deprecated. Please migrate over to `Hero.ButtonGroup` as they will be removed in a future release.

Migrate using the following approach:

```diff
-<Hero.PrimaryAction href="#">Primary action</Hero.PrimaryAction>
-<Hero.SecondaryAction href="#">Secondary action</Hero.SecondaryAction>
+<Hero.ButtonGroup>
+ <Button as="a" href="#">
+ Primary action
+ </Button>
+ <Button as="a" href="#" variant="secondary">
+ Secondary action
+ </Button>
+</Hero.ButtonGroup>
```
1 change: 1 addition & 0 deletions .changeset/soft-banners-sparkle.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@

- Restored rounded corners to `CTABanner` while preserving square edges when grid lines are enabled.
- Improved `ButtonGroup` to forward custom class names alongside its default styles.
- Added native `ActionMenu` child support to `ButtonGroup`, including automatic positional variants and the `CTABanner.ButtonGroup` wrapper.
25 changes: 22 additions & 3 deletions apps/next-docs/content/components/ButtonGroup/react.mdx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: Button group
description: Use the button group component to group related buttons together.
description: Use the button group component to group related buttons and action menus together.
keywords: ['buttons', 'control', 'action', 'group', 'paired']
show-tabs: true
tab-label: React
Expand All @@ -12,14 +12,14 @@ storybook: '/brand/storybook/?path=/story/components-buttongroup--playground'
import {ButtonGroupSizesProp, ButtonGroupAsProp} from './react'

```js
import {ButtonGroup, Button} from '@primer/react-brand'
import {ActionMenu, Button, ButtonGroup} from '@primer/react-brand'
```

## Examples

### Default

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.
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.

```jsx live
<ButtonGroup>
Expand All @@ -28,6 +28,25 @@ This is the default variant for the ButtonGroup component. The first button in t
</ButtonGroup>
```

### Action menus

`ButtonGroup` supports up to two `Button` or `ActionMenu` children and applies a default `variant` to each one.

Explicit `variant` overrides to `Button` or `ActionMenu.Button` will override the default values.

```jsx live
<ButtonGroup>
<Button>Primary action</Button>
<ActionMenu>
<ActionMenu.Button>More actions</ActionMenu.Button>
<ActionMenu.Overlay aria-label="More actions">
<ActionMenu.Item value="contact-sales">Contact sales</ActionMenu.Item>
<ActionMenu.Item value="view-pricing">View pricing</ActionMenu.Item>
</ActionMenu.Overlay>
</ActionMenu>
</ButtonGroup>
```

### Sizes

The ButtonGroup component can be rendered in different sizes in `medium` and `large` sizes. The default size is `medium`.
Expand Down
78 changes: 69 additions & 9 deletions apps/next-docs/content/components/Hero/react.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
} from './react'

```js
import {Hero} from '@primer/react-brand'
import {ActionMenu, Button, Hero} from '@primer/react-brand'
```

## Examples
Expand All @@ -33,8 +33,36 @@ import {Hero} from '@primer/react-brand'
Lorem ipsum dolor sit amet, consectetur adipiscing elit. In sapien sit ullamcorper id. Aliquam luctus sed turpis
felis nam pulvinar risus elementum.
</Hero.Description>
<Hero.PrimaryAction href="#">Primary action</Hero.PrimaryAction>
<Hero.SecondaryAction href="#">Secondary action</Hero.SecondaryAction>
<Hero.ButtonGroup>
<Button as="a" href="#">
Primary action
</Button>
<Button as="a" href="#" variant="secondary">
Secondary action
</Button>
</Hero.ButtonGroup>
</Hero>
```

### Action menus

`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.

```jsx live
<Hero>
<Hero.Heading>This is my super sweet hero heading</Hero.Heading>
<Hero.ButtonGroup>
<Button as="a" href="#">
Primary action
</Button>
<ActionMenu>
<ActionMenu.Button>More actions</ActionMenu.Button>
<ActionMenu.Overlay aria-label="More actions">
<ActionMenu.Item value="contact-sales">Contact sales</ActionMenu.Item>
<ActionMenu.Item value="view-pricing">View pricing</ActionMenu.Item>
</ActionMenu.Overlay>
</ActionMenu>
</Hero.ButtonGroup>
</Hero>
```

Expand All @@ -47,14 +75,22 @@ import {Hero} from '@primer/react-brand'
<Hero>
<Hero.Label>Actions</Hero.Label>
<Hero.Heading>Automate your workflow from idea to production</Hero.Heading>
<Hero.PrimaryAction href="#">Get started with Actions</Hero.PrimaryAction>
<Hero.ButtonGroup>
<Button as="a" href="#">
Get started with Actions
</Button>
</Hero.ButtonGroup>
<Hero.Image src="/images/placeholder.png" alt="placeholder, blank area with a gray background color" />
</Hero>
<br />
<Hero align="center">
<Hero.Label>Actions</Hero.Label>
<Hero.Heading size="2">Automate your workflow from idea to production</Hero.Heading>
<Hero.PrimaryAction href="#">Get started with Actions</Hero.PrimaryAction>
<Hero.ButtonGroup>
<Button as="a" href="#">
Get started with Actions
</Button>
</Hero.ButtonGroup>
<Hero.Image
position="inline-end"
src="/images/placeholder.png"
Expand All @@ -78,7 +114,11 @@ The `GridLine` variants all feature a trailing border by default. Use `Box` if y
Lorem ipsum dolor sit amet, consectetur adipiscing elit. In sapien sit ullamcorper id. Aliquam luctus sed turpis
felis nam pulvinar risus elementum.
</Hero.Description>
<Hero.PrimaryAction href="#">Primary action</Hero.PrimaryAction>
<Hero.ButtonGroup>
<Button as="a" href="#">
Primary action
</Button>
</Hero.ButtonGroup>
<Hero.Image src="/images/placeholder.png" alt="placeholder, blank area with a gray background color" />
</Hero>
```
Expand All @@ -96,7 +136,11 @@ The `GridLine` variants all feature a trailing border by default. Use `Box` if y
Lorem ipsum dolor sit amet, consectetur adipiscing elit. In sapien sit ullamcorper id. Aliquam luctus sed turpis
felis nam pulvinar risus elementum.
</Hero.Description>
<Hero.PrimaryAction href="#">Primary action</Hero.PrimaryAction>
<Hero.ButtonGroup>
<Button as="a" href="#">
Primary action
</Button>
</Hero.ButtonGroup>
<Hero.Image src="/images/placeholder.png" alt="placeholder, blank area with a gray background color" />
</Hero>
```
Expand All @@ -115,7 +159,11 @@ Use [VideoPlayer](/components/VideoPlayer), a native `<video>` element or a YouT
Lorem ipsum dolor sit amet, consectetur adipiscing elit. In sapien sit ullamcorper id. Aliquam luctus sed turpis
felis nam pulvinar risus elementum.
</Hero.Description>
<Hero.PrimaryAction href="#">Primary action</Hero.PrimaryAction>
<Hero.ButtonGroup>
<Button as="a" href="#">
Primary action
</Button>
</Hero.ButtonGroup>
<Hero.Video>
<iframe
src="https://www.youtube.com/embed/fHwtrOcLAnI"
Expand All @@ -142,7 +190,11 @@ Use [VideoPlayer](/components/VideoPlayer), a native `<video>` element or a YouT
<br />
developer environments
</Hero.Heading>
<Hero.PrimaryAction href="#">Primary action</Hero.PrimaryAction>
<Hero.ButtonGroup>
<Button as="a" href="#">
Primary action
</Button>
</Hero.ButtonGroup>
</Hero>
```

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

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

### Hero.ButtonGroup

Groups up to two [Button](/components/Button) or [ActionMenu](/components/ActionMenu) children. Forwards all props from the [ButtonGroup component](/components/ButtonGroup).

### Hero.PrimaryAction

Deprecated. Use `Hero.ButtonGroup` with a `Button` child instead.

| name | type | default | description |
| ----------- | ------------- | ------- | ----------------------------------- |
| `className` | `string` | | Primary link custom class |
Expand All @@ -198,6 +256,8 @@ Forwards all props from the [Button component](/components/Button).

### Hero.SecondaryAction

Deprecated. Use `Hero.ButtonGroup` with a `Button` child instead.

| name | type | default | description |
| ----------- | ------------- | ------- | ----------------------------------- |
| `className` | `string` | | Secondary link custom class |
Expand Down
12 changes: 9 additions & 3 deletions apps/next-docs/content/getting-started/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ Components can be imported into any React-based application.
E.g.

```js
import {Hero} from '@primer/react-brand'
import {Button, Hero} from '@primer/react-brand'
```

```jsx
Expand All @@ -94,8 +94,14 @@ import {Hero} from '@primer/react-brand'
Lorem ipsum dolor sit amet, consectetur adipiscing elit. In sapien sit ullamcorper id. Aliquam luctus sed turpis
felis nam pulvinar risus elementum.
</Hero.Description>
<Hero.PrimaryAction href="#">Primary action</Hero.PrimaryAction>
<Hero.SecondaryAction href="#">Secondary action</Hero.SecondaryAction>
<Hero.ButtonGroup>
<Button as="a" href="#">
Primary action
</Button>
<Button as="a" href="#" variant="secondary">
Secondary action
</Button>
</Hero.ButtonGroup>
</Hero>
```

Expand Down
9 changes: 7 additions & 2 deletions packages/mcp/src/test-utils/catalog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export function makeCatalog(overrides: Partial<Catalog> = {}): Catalog {
{
name: 'Hero',
module: '@primer/react-brand',
subcomponents: ['Hero.Heading', 'Hero.Description', 'Hero.PrimaryAction'],
subcomponents: ['Hero.Heading', 'Hero.Description', 'Hero.ButtonGroup', 'Hero.PrimaryAction'],
props: [
{
name: 'align',
Expand All @@ -29,7 +29,12 @@ export function makeCatalog(overrides: Partial<Catalog> = {}): Catalog {
{
title: 'Hero example',
source: 'story',
code: '<Hero align="center"><Hero.Heading>Build like the best</Hero.Heading></Hero>',
code: `<Hero align="center">
<Hero.Heading>Build like the best</Hero.Heading>
<Hero.ButtonGroup>
<Button>Get started</Button>
</Hero.ButtonGroup>
</Hero>`,
},
],
description: 'Prominent banner for the top of a landing page',
Expand Down
4 changes: 3 additions & 1 deletion packages/react/src/ActionMenu/ActionMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ import React, {
ReactElement,
useMemo,
} from 'react'
import {Button, ButtonProps, Text, ThemeProvider, useTheme} from '../'
import {Button, ButtonProps} from '../Button'
import {Text} from '../Text'
import {ThemeProvider, useTheme} from '../ThemeProvider'
import {useAnchoredPosition} from '../hooks/useAnchoredPosition'
import {useOnClickOutside} from '../hooks/useOnClickOutside'
import {useKeyboardEscape} from '../hooks/useKeyboardEscape'
Expand Down
33 changes: 32 additions & 1 deletion packages/react/src/ButtonGroup/ButtonGroup.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ import React from 'react'
import type {Meta, StoryObj} from '@storybook/react'
import {ButtonGroup} from '.'
import {Button} from '../Button'
import {ActionMenu} from '../ActionMenu'

const meta = {
title: 'Components/ButtonGroup',
component: ButtonGroup,
subcomponents: {Button},
subcomponents: {Button, ActionMenu},
args: {
buttonSize: 'medium',
buttonsAs: 'button',
Expand Down Expand Up @@ -58,6 +59,36 @@ export const SingleButtonGroup: Story = {
),
}

export const WithActionMenu: Story = {
render: args => (
<ButtonGroup {...args}>
<Button>Primary action</Button>
<ActionMenu>
<ActionMenu.Button>More actions</ActionMenu.Button>
<ActionMenu.Overlay aria-label="More actions">
<ActionMenu.Item value="Contact sales">Contact sales</ActionMenu.Item>
<ActionMenu.Item value="View pricing">View pricing</ActionMenu.Item>
</ActionMenu.Overlay>
</ActionMenu>
</ButtonGroup>
),
}

export const WithVariantOverrides: Story = {
render: args => (
<ButtonGroup {...args}>
<Button variant="secondary">Secondary override</Button>
<ActionMenu>
<ActionMenu.Button variant="primary">Primary override</ActionMenu.Button>
<ActionMenu.Overlay aria-label="More actions">
<ActionMenu.Item value="Contact sales">Contact sales</ActionMenu.Item>
<ActionMenu.Item value="View pricing">View pricing</ActionMenu.Item>
</ActionMenu.Overlay>
</ActionMenu>
</ButtonGroup>
),
}

export const LargeButtonGroup: Story = {
...Template,
args: {
Expand Down
Loading
Loading