Skip to content

Commit 3577768

Browse files
Merge 42f6924 into d971e89
2 parents d971e89 + 42f6924 commit 3577768

37 files changed

+1890
-43
lines changed

.changeset/add-menu-component.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@guardian/stand': patch
3+
---
4+
5+
Add Menu component
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@guardian/stand': minor
3+
---
4+
5+
Add TopBarItem component
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@guardian/stand': minor
3+
---
4+
5+
Add TopBarNavigation component

.changeset/solid-dogs-fail.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@guardian/stand': patch
3+
---
4+
5+
Add TopBar and TopBarItem components

src/TopBar.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,17 @@
1212
* If you only need the built CSS (./component/TopBar.css),
1313
* you don't need to install these.
1414
*/
15-
export { TopBarToolName } from './components/topbar/toolName/TopBarToolName';
16-
export type { TopBarToolNameProps } from './components/topbar/toolName/types';
17-
export type { PartialTopBarToolNameTheme as TopBarToolNameTheme } from './components/topbar/toolName/styles';
1815
export { componentTopBar } from './styleD/build/typescript/component/TopBar';
1916
export type { ComponentTopBar } from './styleD/build/typescript/component/TopBar';
17+
18+
export { TopBarToolName } from './components/topbar/topBarToolName/TopBarToolName';
19+
export type { TopBarToolNameProps } from './components/topbar/topBarToolName/types';
20+
export type { PartialTopBarToolNameTheme as TopBarToolNameTheme } from './components/topbar/topBarToolName/styles';
21+
22+
export { TopBarNavigation } from './components/topbar/topBarNavigation/TopBarNavigation';
23+
export type { TopBarNavigationProps } from './components/topbar/topBarNavigation/types';
24+
export type { PartialTopBarNavigationTheme as TopBarNavigationTheme } from './components/topbar/topBarNavigation/styles';
25+
26+
export { TopBarItem } from './components/topbar/topBarItem/TopBarItem';
27+
export type { TopBarItemProps } from './components/topbar/topBarItem/types';
28+
export type { PartialTopBarItemTheme as TopBarItemTheme } from './components/topbar/topBarItem/styles';

src/components/topbar/TopBar.mdx

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
import { Meta, Story } from '@storybook/addon-docs/blocks';
2+
import TopBarStories, {
3+
CssOverrides,
4+
CustomTheme,
5+
Default,
6+
} from './TopBar.stories';
7+
import {
8+
SandboxReact,
9+
SandboxCss,
10+
SandboxJs,
11+
} from '../../util/storybook/sandbox/Sandbox';
12+
import {
13+
componentName,
14+
componentCss,
15+
componentHtml,
16+
componentJs,
17+
componentTsx,
18+
} from './sandbox';
19+
20+
<Meta of={TopBarStories} />
21+
22+
# TopBar
23+
24+
A top bar that provides information about the tool, the current state and navigation links among other things.
25+
26+
## When to use
27+
28+
- At the top of a tool
29+
- To identify the tool, its current state and the user
30+
- To navigate around the tool
31+
32+
## Peer dependencies
33+
34+
- `@emotion/react`
35+
- `react`
36+
- `react-dom`
37+
- `typescript`
38+
39+
See the `peerDependencies` section of `package.json` for compatible versions.
40+
41+
See [custom component build](#custom-component-build) for usage without React/Emotion.
42+
43+
## Example usage
44+
45+
<SandboxReact componentName={componentName} componentTsx={componentTsx} />
46+
47+
```tsx
48+
import { TopBar, TopBarLHS, TopBarItem } from '@guardian/stand/TopBar';
49+
50+
/* types, if required */
51+
import type { TopBarProps, TopBarTheme } from '@guardian/stand/TopBar';
52+
53+
<TopBar theme={customTheme}>
54+
<TopBarToolName name="Author" favicon={{ letter: 'A' }} />
55+
<TopBarLHS>
56+
<TopBarItem>Hello</TopBarItem>
57+
</TopBarLHS>
58+
<TopBarRHS>
59+
<TopBarNavigation text={'Link to somewhere'} href="#" />
60+
</TopBarRHS>
61+
<Avatar
62+
src="https://uploads.guimcode.co.uk/2026/01/27/f85e2e477ce54f4c3b671faa5cd21673aa9f8072fddb5d70a73e6038dc812eec.jpg"
63+
alt="Mahesh Makani"
64+
size="sm"
65+
/>
66+
</TopBar>;
67+
```
68+
69+
## Composition
70+
71+
The `TopBar` component is composed of several sub-components. Only children of type `TopBarToolName`, `Avatar`, `TopBarLHS` or `TopBarRHS` are valid.
72+
73+
The children will be rendered in the following order from left to right:
74+
75+
| `TopBarToolName` | `TopBarLHS` | `TopBarRHS` | `Avatar` |
76+
77+
`TopBarToolName` will always be on the left and `Avatar` on the right.
78+
79+
### Props
80+
81+
| Name | Type | Required | Default | Description |
82+
| -------------- | ------------------ | -------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------- |
83+
| `children` | `React.ReactNode` | No | N/A | Content to render inside the component. Only children of type `TopBarToolName`, `Avatar`, `TopBarLHS` or `TopBarRHS` will be rendered. |
84+
| `theme` | `TopBarTheme` | No | N/A | Custom theme overrides. |
85+
| `cssOverrides` | `SerializedStyles` | No | N/A | Custom CSS styles. |
86+
| `className` | `string` | No | N/A | Additional class name(s). |
87+
88+
### `TopBarRHS` / `TopBarRHS` props
89+
90+
All other content must be composed within the left hand side or right hand side. The only valid children are [TopBarNavigation](/docs/stand-tools-design-system-components-topbar-topbarnavigation--docs) and [TopBarItem](/docs/stand-tools-design-system-components-topbar-topbaritem--docs).
91+
92+
`TopBarNavigation` children should be used for navigation links around the tool. `TopBarItem` provides the necessary styling for any other child components in the top bar.
93+
94+
| Name | Type | Required | Default | Description |
95+
| ---------- | ----------------- | -------- | ------- | ------------------------------------------------------------------------------------------------------------------------ |
96+
| `children` | `React.ReactNode` | No | N/A | Content to render on one side of the top bar. Only children of type `TopBarItem` or `TopBarNavigation` will be rendered. |
97+
98+
## Stories
99+
100+
### Default
101+
102+
<Story of={Default} />
103+
104+
## Customisation
105+
106+
We recommend using the TopBar component as provided, but it can be customised using the `theme` or `cssOverrides` props as required.
107+
108+
### Custom theme
109+
110+
The `theme` prop allows you to override specific design tokens for the TopBar component:
111+
112+
```tsx
113+
import type { TopBarTheme } from '@guardian/stand/TopBar';
114+
import { TopBar, TopBarLHS, TopBarItem } from '@guardian/stand/TopBar';
115+
import { baseColors } from '@guardian/stand';
116+
117+
const customTheme: Partial<TopBarTheme> = {
118+
border: `5px solid ${baseColors['cool-purple']['700']}`,
119+
};
120+
121+
const Component = () => (
122+
<TopBar theme={customTheme}>
123+
<TopBarLHS>
124+
<TopBarToolName name="Custom" favicon={{ letter: 'C' }} />
125+
<TopBarItem>Hello</TopBarItem>
126+
</TopBarLHS>
127+
</TopBar>
128+
);
129+
```
130+
131+
<Story of={CustomTheme} />
132+
133+
### CSS overrides
134+
135+
The `cssOverrides` prop allows you to pass custom CSS to the TopBar component:
136+
137+
```tsx
138+
import { TopBar } from '@guardian/stand/TopBar';
139+
import { baseColors } from '@guardian/stand';
140+
import { css } from '@emotion/react';
141+
142+
const customStyles = css\`
143+
border: 4px solid ${baseColors.cyan['300']};
144+
\`;
145+
146+
const Component = () =>
147+
<TopBar cssOverrides={customStyles}>
148+
<TopBarToolName name="CssOverides" favicon={{ letter: 'C' }} />
149+
<TopBarLHS>
150+
<TopBarItem>Hello</TopBarItem>
151+
</TopBarLHS>
152+
</TopBar>
153+
;
154+
```
155+
156+
<Story of={CssOverrides} />
157+
158+
## Custom Component Build
159+
160+
If you're not using React/Emotion, you can create a custom TopBar component using the styles defined in the `TopBarTheme` type.
161+
162+
**`css`**
163+
164+
You can import the TopBar styles as CSS from the package:
165+
166+
<SandboxCss
167+
componentName={componentName}
168+
componentHtml={componentHtml}
169+
componentCss={componentCss}
170+
/>
171+
172+
**TypeScript/JavaScript**
173+
174+
Use the `componentTopBar` variable and the `ComponentTopBar` type:
175+
176+
<SandboxJs componentName={componentName} componentJs={componentJs} />
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import { css } from '@emotion/react';
2+
import type { Meta, StoryObj } from '@storybook/react-vite';
3+
import { baseColors } from '../../styleD/build/typescript/base/colors';
4+
import { Avatar } from '../avatar/Avatar';
5+
import { Button } from '../button/Button';
6+
import { MenuItem, MenuSection } from '../menu/Menu';
7+
import { TopBar, TopBarLHS, TopBarRHS } from './TopBar';
8+
import { TopBarItem } from './topBarItem/TopBarItem';
9+
import { TopBarNavigation } from './topBarNavigation/TopBarNavigation';
10+
import { TopBarToolName } from './topBarToolName/TopBarToolName';
11+
12+
const meta = {
13+
title: 'Stand/Tools Design System/Components/TopBar/Top Bar',
14+
component: TopBar,
15+
parameters: {},
16+
args: {},
17+
} satisfies Meta<typeof TopBar>;
18+
19+
type Story = StoryObj<typeof TopBar>;
20+
21+
export default meta;
22+
23+
export const Default = {
24+
name: 'Default',
25+
render: () => (
26+
<TopBar>
27+
<TopBarToolName name="Default" favicon={{ letter: 'D' }} />
28+
<TopBarLHS>
29+
<TopBarItem>Some text</TopBarItem>
30+
<TopBarItem>
31+
<Button>Click me</Button>
32+
</TopBarItem>
33+
<TopBarNavigation icon="zoom_out_map" text={'Or click me'} href="#" />
34+
</TopBarLHS>
35+
<TopBarRHS>
36+
<TopBarNavigation
37+
text="Menu"
38+
menuChildren={
39+
<MenuSection name="Stuff">
40+
<MenuItem label="Text" />
41+
</MenuSection>
42+
}
43+
/>
44+
<TopBarItem>On the right</TopBarItem>
45+
</TopBarRHS>
46+
<Avatar
47+
src="https://uploads.guimcode.co.uk/2026/01/27/f85e2e477ce54f4c3b671faa5cd21673aa9f8072fddb5d70a73e6038dc812eec.jpg"
48+
alt="Mahesh Makani"
49+
size="sm"
50+
/>
51+
</TopBar>
52+
),
53+
} satisfies Story;
54+
55+
export const CustomTheme = {
56+
render: () => (
57+
<TopBar
58+
theme={{
59+
border: `5px solid ${baseColors['cool-purple']['700']}`,
60+
}}
61+
>
62+
<TopBarToolName name="Custom" favicon={{ letter: 'C' }} />
63+
<TopBarLHS>
64+
<TopBarItem>Top Bar</TopBarItem>
65+
</TopBarLHS>
66+
</TopBar>
67+
),
68+
} satisfies Story;
69+
70+
export const CssOverrides = {
71+
render: () => (
72+
<TopBar
73+
cssOverrides={css`
74+
border: 4px solid ${baseColors.cyan['300']};
75+
`}
76+
>
77+
<TopBarToolName name="CssOverides" favicon={{ letter: 'C' }} />
78+
<TopBarLHS>
79+
<TopBarItem>Top Bar</TopBarItem>
80+
</TopBarLHS>
81+
</TopBar>
82+
),
83+
} satisfies Story;

0 commit comments

Comments
 (0)