Skip to content

Commit d023ad6

Browse files
Merge d2f009b into 01a74a0
2 parents 01a74a0 + d2f009b commit d023ad6

File tree

14 files changed

+851
-4
lines changed

14 files changed

+851
-4
lines changed
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

src/TopBar.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,13 @@
1212
* If you only need the built CSS (./component/TopBar.css),
1313
* you don't need to install these.
1414
*/
15+
export { componentTopBar } from './styleD/build/typescript/component/TopBar';
16+
export type { ComponentTopBar } from './styleD/build/typescript/component/TopBar';
17+
1518
export { TopBarToolName } from './components/topbar/toolName/TopBarToolName';
1619
export type { TopBarToolNameProps } from './components/topbar/toolName/types';
1720
export type { TopBarToolNameTheme } from './components/topbar/toolName/styles';
18-
export { componentTopBar } from './styleD/build/typescript/component/TopBar';
19-
export type { ComponentTopBar } from './styleD/build/typescript/component/TopBar';
21+
22+
export { TopBarNavigation } from './components/topbar/navigation/TopBarNavigation';
23+
export type { TopBarNavigationProps } from './components/topbar/navigation/types';
24+
export type { TopBarNavigationTheme } from './components/topbar/navigation/styles';
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
import { Meta, Story } from '@storybook/addon-docs/blocks';
2+
import TopBarNavigationStories, {
3+
CssOverrides,
4+
CustomTheme,
5+
Default,
6+
WithIcon,
7+
WithMenu,
8+
Selected,
9+
Disabled,
10+
} from './TopBarNavigation.stories';
11+
import {
12+
SandboxReact,
13+
SandboxCss,
14+
SandboxJs,
15+
} from '../../../util/storybook/sandbox/Sandbox';
16+
import {
17+
componentName,
18+
componentCss,
19+
componentHtml,
20+
componentJs,
21+
componentTsx,
22+
} from './sandbox';
23+
24+
<Meta of={TopBarNavigationStories} />
25+
26+
# TopBarNavigation
27+
28+
The Top Bar Navigation component is a link to another part of a tool, and also shows the currently navigated page or section.
29+
30+
## When to use
31+
32+
- In a Top Bar component
33+
- When you have multiple pages or tabs in an app and need a way to navigate between them
34+
35+
## Peer dependencies
36+
37+
- `@emotion/react`
38+
- `react`
39+
- `react-dom`
40+
- `typescript`
41+
- ToDo: react aria if menu?
42+
43+
See the `peerDependencies` section of `package.json` for compatible versions.
44+
45+
See [custom component build](#custom-component-build) for usage without React/Emotion.
46+
47+
## Example usage
48+
49+
<SandboxReact componentName={componentName} componentTsx={componentTsx} />
50+
51+
```tsx
52+
import { TopBarNavigation } from '@guardian/stand/TopBar';
53+
54+
/* types, if required */
55+
import type {
56+
TopBarNavigationProps,
57+
TopBarNavigationTheme,
58+
} from '@guardian/stand/TopBar';
59+
60+
export const Component = () => (
61+
<TopBarNavigation text="Recipe" icon="yakitori" />
62+
);
63+
```
64+
65+
## Props
66+
67+
| Name | Type | Required | Default | Description |
68+
| -------------- | ----------------------------------------------------------------- | -------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
69+
| `text` | `string` | Yes | N/A | Text content to render inside the component. |
70+
| `size` | `'md'` \| `'sm'` | No | `'md'` | Size of the component - affects text and icon. |
71+
| `isSelected` | `boolean` | No | `false` | Whether this navigation component represents the currently selected navigation option. |
72+
| `icon` | `IconProps['symbol']` \| `Exclude<IconProps['children'], string>` | No | N/A | Icon rendered inside the navigation. Accepts a `MaterialSymbol` name or a non-string `ReactNode` (e.g. an SVG element). Size is default. |
73+
| `menuChildren` | `ReactNode` | No | `false` | Menu sections and items to render in the dropdown menu - only children of type MenuSection and MenuItem will be rendered. `href` and `onPress` will be ignored if this prop is supplied. |
74+
| `href` | `string` | No | N/A | A URL to link to handle navigation. |
75+
| `onPress` | `(e: PressEvent) => void` | No | N/A | On Press handler that handles navigation. |
76+
| `theme` | `TopBarNavigationTheme` | No | N/A | Custom theme overrides. |
77+
| `cssOverrides` | `SerializedStyles` | No | N/A | Custom CSS styles. |
78+
| `className` | `string` | No | N/A | Additional class name(s). |
79+
80+
## Stories
81+
82+
### Default
83+
84+
<Story of={Default} />
85+
86+
### With Icon
87+
88+
<Story of={WithIcon} />
89+
90+
### With Menu
91+
92+
<Story of={WithMenu} />
93+
94+
### Selected
95+
96+
<Story of={Selected} />
97+
98+
### Disabled
99+
100+
<Story of={Disabled} />
101+
102+
## Customisation
103+
104+
We recommend using the TopBarNavigation component as provided, but it can be customised using the `theme` or `cssOverrides` props as required.
105+
106+
### Custom theme
107+
108+
The `theme` prop allows you to override specific design tokens for the TopBarNavigation component:
109+
110+
<Story of={CustomTheme} />
111+
112+
```tsx
113+
import type { TopBarNavigationTheme } from '@guardian/stand/TopBar';
114+
import { TopBarNavigation } from '@guardian/stand/TopBar';
115+
import { baseColors } from '@guardian/stand';
116+
117+
const customTheme: Partial<TopBarNavigationTheme> = {
118+
selected: {
119+
color: baseColors.blue[200],
120+
'border-bottom': `5px solid ${baseColors['cool-purple'][700]}`,
121+
},
122+
};
123+
124+
const Component = () => (
125+
<TopBarNavigation
126+
theme={customTheme}
127+
icon="file_upload"
128+
text="Navigation"
129+
isSelected={true}
130+
/>
131+
);
132+
```
133+
134+
### CSS overrides
135+
136+
The `cssOverrides` prop allows you to pass custom CSS to the TopBarNavigation component:
137+
138+
<Story of={CssOverrides} />
139+
140+
```tsx
141+
import { TopBarNavigation } from '@guardian/stand/TopBar';
142+
import { semanticColors } from '@guardian/stand';
143+
import { css } from '@emotion/react';
144+
145+
const customStyles = css\`
146+
background-color: ${semanticColors.surface['dark-1']};
147+
color: ${semanticColors.text['inverse-default']};
148+
\`;
149+
150+
const Component = () => (
151+
<TopBarNavigation
152+
cssOverrides={customStyles}
153+
icon="file_upload"
154+
text="Navigation"
155+
isSelected={true}
156+
/>
157+
);
158+
```
159+
160+
## Custom Component Build
161+
162+
If you're not using React/Emotion, you can create a custom TopBarNavigation component using the styles defined in the `TopBarNavigationTheme` type.
163+
164+
**`css`**
165+
166+
You can import the TopBarNavigation styles as CSS from the package:
167+
168+
<SandboxCss
169+
componentName={componentName}
170+
componentHtml={componentHtml}
171+
componentCss={componentCss}
172+
/>
173+
174+
**TypeScript/JavaScript**
175+
176+
Use the `componentTopBarNavigation` variable and the `ComponentTopBarNavigation` type:
177+
178+
<SandboxJs componentName={componentName} componentJs={componentJs} />
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
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 { semanticColors } from '../../../styleD/build/typescript/semantic/colors';
5+
import { tableStyles } from '../../../util/storybook/styles';
6+
import { MenuItem, MenuSection } from '../../menu/Menu';
7+
import { TopBarNavigation } from './TopBarNavigation';
8+
import type { TopBarNavigationProps } from './types';
9+
10+
const meta = {
11+
title: 'Stand/Tools Design System/Components/TopBar/Navigation',
12+
component: TopBarNavigation,
13+
parameters: {},
14+
args: {
15+
onPress: () => {},
16+
},
17+
render: (props) => renderBothSizes(props),
18+
} satisfies Meta<typeof TopBarNavigation>;
19+
20+
type Story = StoryObj<typeof TopBarNavigation>;
21+
22+
export default meta;
23+
24+
// render a sm and md size side by side
25+
const renderBothSizes = (props: TopBarNavigationProps) => (
26+
<table css={tableStyles}>
27+
<thead>
28+
<tr>
29+
<th>Small</th>
30+
<th>Medium</th>
31+
</tr>
32+
</thead>
33+
<tbody>
34+
<tr>
35+
<td>
36+
<TopBarNavigation {...props} size="sm" />
37+
</td>
38+
<td>
39+
<TopBarNavigation {...props} size="md" />
40+
</td>
41+
</tr>
42+
</tbody>
43+
</table>
44+
);
45+
46+
export const Default = {
47+
name: 'Default',
48+
args: {
49+
text: 'Navigation',
50+
},
51+
} satisfies Story;
52+
53+
export const WithIcon = {
54+
args: {
55+
text: 'Navigation',
56+
icon: 'file_upload',
57+
},
58+
} satisfies Story;
59+
60+
export const WithMenu = {
61+
args: {
62+
text: 'Navigation',
63+
icon: 'file_upload',
64+
menuChildren: (
65+
<MenuSection name="Menu section">
66+
<MenuItem label="Menu item 1" />
67+
<MenuItem label="Menu item 2" />
68+
</MenuSection>
69+
),
70+
},
71+
} satisfies Story;
72+
73+
export const WithMenuDisabled = {
74+
args: {
75+
text: 'Navigation',
76+
icon: 'file_upload',
77+
isDisabled: true,
78+
menuChildren: (
79+
<MenuSection name="Menu section">
80+
<MenuItem label="Menu item 1" />
81+
<MenuItem label="Menu item 2" />
82+
</MenuSection>
83+
),
84+
},
85+
} satisfies Story;
86+
87+
export const Selected = {
88+
args: {
89+
text: 'Navigation',
90+
icon: 'file_upload',
91+
isSelected: true,
92+
},
93+
} satisfies Story;
94+
95+
export const Disabled = {
96+
args: {
97+
text: 'Navigation',
98+
icon: 'file_upload',
99+
isDisabled: true,
100+
},
101+
} satisfies Story;
102+
103+
export const CustomTheme = {
104+
args: {
105+
text: 'Navigation',
106+
icon: 'file_upload',
107+
isSelected: true,
108+
theme: {
109+
selected: {
110+
color: baseColors.blue[200],
111+
'border-bottom': `5px solid ${baseColors['cool-purple'][700]}`,
112+
},
113+
},
114+
},
115+
} satisfies Story;
116+
117+
export const CssOverrides = {
118+
args: {
119+
text: 'Navigation',
120+
icon: 'file_upload',
121+
isSelected: true,
122+
cssOverrides: css`
123+
background-color: ${semanticColors.surface['dark-1']};
124+
color: ${semanticColors.text['inverse-default']};
125+
`,
126+
},
127+
} satisfies Story;

0 commit comments

Comments
 (0)