Skip to content

Commit 9bbc6de

Browse files
tenphicursoragent
andauthored
feat(Layout): add back button to header (#1054)
Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 2a109cf commit 9bbc6de

File tree

4 files changed

+76
-4
lines changed

4 files changed

+76
-4
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@cube-dev/ui-kit": minor
3+
---
4+
5+
Added `onBack` prop to `Layout.Header` component. When provided, a back button with arrow icon is rendered to the left of the title, allowing users to navigate back from the current page.

src/components/content/Layout/Layout.docs.mdx

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,8 +118,10 @@ A semantic page header with title, breadcrumbs, subtitle, and action areas.
118118
| subtitle | `ReactNode` | - | Text below the title |
119119
| suffix | `ReactNode` | - | Content next to the title |
120120
| extra | `ReactNode` | - | Content on the right side |
121+
| onBack | `() => void` | - | Callback for the back button. When provided, a back arrow button appears to the left of the title. |
121122

122123
**Sub-elements:**
124+
- `Back` - Back button container (visible when `onBack` is provided)
123125
- `Breadcrumbs` - Navigation path container
124126
- `Title` - Main heading element
125127
- `Suffix` - Content adjacent to title
@@ -431,6 +433,28 @@ A variant of Layout that enables CSS Grid for the content area.
431433
</Layout>
432434
```
433435

436+
### With Back Button
437+
438+
<Story of={LayoutStories.WithBackButton} />
439+
440+
```jsx
441+
<Layout height="100dvh">
442+
<Layout.Header
443+
title="Report Details"
444+
onBack={() => navigate(-1)}
445+
extra={
446+
<Space>
447+
<Button>Export</Button>
448+
<Button type="primary">Edit</Button>
449+
</Space>
450+
}
451+
/>
452+
<Layout.Content>
453+
<Text>Page content with a back button in the header</Text>
454+
</Layout.Content>
455+
</Layout>
456+
```
457+
434458
### Resizable Side Panel
435459

436460
<Story of={LayoutStories.ResizablePanel} />

src/components/content/Layout/Layout.stories.tsx

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,26 @@ export const WithBreadcrumbs: Story = {
180180
),
181181
};
182182

183+
export const WithBackButton: Story = {
184+
render: () => (
185+
<Layout height="100dvh">
186+
<Layout.Header
187+
title="Report Details"
188+
extra={
189+
<Space>
190+
<Button>Export</Button>
191+
<Button type="primary">Edit</Button>
192+
</Space>
193+
}
194+
onBack={() => console.log('Back pressed')}
195+
/>
196+
<Layout.Content>
197+
<Text>Page content with a back button in the header</Text>
198+
</Layout.Content>
199+
</Layout>
200+
),
201+
};
202+
183203
export const WithSidePanel: Story = {
184204
render: function WithSidePanelStory() {
185205
const [isPanelOpen, setIsPanelOpen] = useState(true);

src/components/content/Layout/LayoutHeader.tsx

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { IconArrowLeft } from '@tabler/icons-react';
12
import {
23
ForwardedRef,
34
forwardRef,
@@ -10,6 +11,7 @@ import {
1011

1112
import { SlashIcon } from '../../../icons/SlashIcon';
1213
import { tasty } from '../../../tasty';
14+
import { Button } from '../../actions/Button/Button';
1315
import { Link } from '../../actions/Link/Link';
1416
import { useAutoTooltip } from '../use-auto-tooltip';
1517

@@ -29,16 +31,24 @@ const HeaderElement = tasty(LayoutContent, {
2931
$: '>',
3032
display: 'grid',
3133
gridTemplate: `
32-
"breadcrumbs breadcrumbs breadcrumbs" auto
33-
"title suffix extra" 1fr
34-
"subtitle subtitle extra" auto
35-
/ max-content 1fr minmax(0, auto)
34+
"breadcrumbs breadcrumbs breadcrumbs breadcrumbs" auto
35+
"back title suffix extra" 1fr
36+
".. subtitle subtitle extra" auto
37+
/ auto max-content 1fr minmax(0, auto)
3638
`,
3739
gap: 0,
3840
placeContent: 'stretch',
3941
placeItems: 'center stretch',
4042
},
4143

44+
Back: {
45+
$: '> Inner >',
46+
gridArea: 'back',
47+
display: 'flex',
48+
placeItems: 'center',
49+
margin: '.5x right',
50+
},
51+
4252
Breadcrumbs: {
4353
$: '> Inner >',
4454
gridArea: 'breadcrumbs',
@@ -111,6 +121,8 @@ export interface CubeLayoutHeaderProps extends CubeLayoutContentProps {
111121
* Uses Link component which integrates with the navigation provider.
112122
*/
113123
breadcrumbs?: Array<[label: string, href: string]>;
124+
/** Callback for the back button. When provided, a back arrow button is rendered to the left of the title. */
125+
onBack?: () => void;
114126
}
115127

116128
function LayoutHeader(
@@ -124,6 +136,7 @@ function LayoutHeader(
124136
extra,
125137
subtitle,
126138
breadcrumbs,
139+
onBack,
127140
scrollbar = 'tiny',
128141
children,
129142
mods,
@@ -181,6 +194,16 @@ function LayoutHeader(
181194
scrollbar={scrollbar}
182195
>
183196
{renderBreadcrumbs()}
197+
{onBack && (
198+
<div data-element="Back">
199+
<Button
200+
type="neutral"
201+
icon={<IconArrowLeft />}
202+
aria-label="Go back"
203+
onPress={onBack}
204+
/>
205+
</div>
206+
)}
184207
{renderWithTooltip(renderTitle, 'bottom')}
185208
{suffix && <div data-element="Suffix">{suffix}</div>}
186209
{extra && <div data-element="Extra">{extra}</div>}

0 commit comments

Comments
 (0)