@arbor-education/design-system.components 0.25.5 → 0.25.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/components/grid/Grid.d.ts +14 -0
  3. package/dist/components/grid/Grid.d.ts.map +1 -0
  4. package/dist/components/grid/Grid.js +11 -0
  5. package/dist/components/grid/Grid.js.map +1 -0
  6. package/dist/components/grid/Grid.stories.d.ts +52 -0
  7. package/dist/components/grid/Grid.stories.d.ts.map +1 -0
  8. package/dist/components/grid/Grid.stories.js +675 -0
  9. package/dist/components/grid/Grid.stories.js.map +1 -0
  10. package/dist/components/grid/Grid.test.d.ts +2 -0
  11. package/dist/components/grid/Grid.test.d.ts.map +1 -0
  12. package/dist/components/grid/Grid.test.js +59 -0
  13. package/dist/components/grid/Grid.test.js.map +1 -0
  14. package/dist/components/grid/GridColumn.d.ts +11 -0
  15. package/dist/components/grid/GridColumn.d.ts.map +1 -0
  16. package/dist/components/grid/GridColumn.js +7 -0
  17. package/dist/components/grid/GridColumn.js.map +1 -0
  18. package/dist/components/grid/GridRow.d.ts +10 -0
  19. package/dist/components/grid/GridRow.d.ts.map +1 -0
  20. package/dist/components/grid/GridRow.js +7 -0
  21. package/dist/components/grid/GridRow.js.map +1 -0
  22. package/dist/index.css +186 -0
  23. package/dist/index.css.map +1 -1
  24. package/dist/index.d.ts +1 -0
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js +1 -0
  27. package/dist/index.js.map +1 -1
  28. package/package.json +2 -1
  29. package/src/components/grid/Grid.stories.tsx +1130 -0
  30. package/src/components/grid/Grid.test.tsx +67 -0
  31. package/src/components/grid/Grid.tsx +34 -0
  32. package/src/components/grid/GridColumn.tsx +31 -0
  33. package/src/components/grid/GridRow.tsx +29 -0
  34. package/src/docs/Contributing.mdx +16 -0
  35. package/src/index.scss +1 -0
  36. package/src/index.ts +1 -0
@@ -0,0 +1,675 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Controls, Heading as DocHeading, Markdown, Primary as DocPrimary, Stories, Subtitle, Title, } from '@storybook/addon-docs/blocks';
3
+ import { Grid } from './Grid.js';
4
+ import { Avatar } from '../avatar/Avatar.js';
5
+ import { Badge } from '../badge/Badge.js';
6
+ import { Button } from '../button/Button.js';
7
+ import { DataViewCard } from '../dataViewCard/DataViewCard.js';
8
+ import { Heading } from '../heading/Heading.js';
9
+ import { KPICard } from '../kpiCard/KPICard.js';
10
+ import { KVPList } from '../kvpList/KVPList.js';
11
+ import { Section } from '../section/Section.js';
12
+ import { Tag } from '../tag/Tag.js';
13
+ // ---------------------------------------------------------------------------
14
+ // Docs page content
15
+ // ---------------------------------------------------------------------------
16
+ const DESCRIPTION_INTRO = [
17
+ 'Grid is a compound layout component that implements the',
18
+ '[GOV.UK Frontend grid system](https://frontend.design-system.service.gov.uk/styles/layout/).',
19
+ 'It provides a structured, column-based page layout that matches the GOV.UK design',
20
+ 'patterns used across Arbor\'s school management interface.',
21
+ '',
22
+ 'The component exposes three building blocks through a single compound API:',
23
+ '',
24
+ '| Part | Class rendered | Description |',
25
+ '|---|---|---|',
26
+ '| `<Grid>` | `govuk-main-wrapper` | Page-level wrapper for all layout content |',
27
+ '| `<Grid.Row>` | `govuk-grid-row` | Horizontal row — contains one or more columns |',
28
+ '| `<Grid.Column>` | `govuk-grid-column-{width}` | A single column within a row |',
29
+ '',
30
+ '### Important: `Grid.Row` and `Grid.Column` are only available via the compound API',
31
+ '',
32
+ '`GridRow` and `GridColumn` are **not** individually exported from the package.',
33
+ 'You must always use the compound form:',
34
+ '',
35
+ '```tsx',
36
+ "import { Grid } from '@arbor-education/design-system.components';",
37
+ '',
38
+ '// Correct',
39
+ '<Grid.Row>',
40
+ ' <Grid.Column columnWidth="two-thirds">...</Grid.Column>',
41
+ '</Grid.Row>',
42
+ '',
43
+ '// Wrong — these imports do not exist in the package',
44
+ "import { GridRow, GridColumn } from '@arbor-education/design-system.components';",
45
+ '```',
46
+ '',
47
+ '### GOV.UK grid styles are bundled',
48
+ '',
49
+ 'The `govuk-grid-row` and `govuk-grid-column-*` styles are compiled into the library\'s',
50
+ 'CSS bundle — import `@arbor-education/design-system.components/dist/index.css` and',
51
+ 'columns will lay out correctly with no additional stylesheet required.',
52
+ '',
53
+ 'The `govuk-main-wrapper` class applied to the `Grid` root is **not** part of the bundled',
54
+ 'grid object — it is a semantic class name only. If your page relies on GOV.UK\'s own',
55
+ '`govuk-main-wrapper` padding, load the GOV.UK Frontend stylesheet separately.',
56
+ ].join('\n');
57
+ const PROPS_INTRO = 'The interactive canvas below is wired to the **Controls** panel — tweak any prop to see the story update in real time.';
58
+ const USAGE_GUIDANCE = [
59
+ '### When to use Grid',
60
+ '',
61
+ '- **Building GOV.UK-pattern page layouts** — sidebar + main content, full-width sections,',
62
+ ' or equal-width columns that follow the service manual\'s grid',
63
+ '- **Creating standardised column-based structure** across Arbor\'s school management pages',
64
+ ' so layouts stay consistent and accessible',
65
+ '- **School MIS page layouts** such as a student profile page (narrow summary sidebar + wide',
66
+ ' main content) or a dashboard with four equal-width metric cards',
67
+ '',
68
+ '---',
69
+ '',
70
+ '### When NOT to use Grid',
71
+ '',
72
+ '| Situation | Use instead |',
73
+ '|---|---|',
74
+ '| Generic flexbox or grid inside a card or small component | Plain `div` with inline styles or a utility class |',
75
+ '| Custom breakpoints beyond GOV.UK Frontend\'s defaults | Custom CSS — GOV.UK columns use fixed breakpoints |',
76
+ '| Columns deeply nested inside an already-padded container | Avoid nesting — `govuk-main-wrapper` carries page-level padding |',
77
+ ].join('\n');
78
+ const DEVELOPER_NOTES = [
79
+ '### GOV.UK grid styles are bundled — no extra stylesheet needed for columns',
80
+ '',
81
+ '`govuk-grid-row` and `govuk-grid-column-*` styles are compiled into',
82
+ '`@arbor-education/design-system.components/dist/index.css` via the library\'s SCSS bundle.',
83
+ 'Importing the library\'s CSS is sufficient for `Grid.Row` and `Grid.Column` to lay out correctly.',
84
+ '',
85
+ '`govuk-main-wrapper` (applied to the `Grid` root) is **not** part of the bundled grid object.',
86
+ 'It is a semantic class name. If your application depends on GOV.UK Frontend\'s own',
87
+ '`govuk-main-wrapper` padding styles, load the GOV.UK Frontend stylesheet separately.',
88
+ '',
89
+ '---',
90
+ '',
91
+ '### Compound component API',
92
+ '',
93
+ 'Only `Grid` and `GridProps` are exported from the package. Access rows and columns',
94
+ 'through the compound API:',
95
+ '',
96
+ '```tsx',
97
+ "import { Grid, type GridProps } from '@arbor-education/design-system.components';",
98
+ '',
99
+ '<Grid>',
100
+ ' <Grid.Row>',
101
+ ' <Grid.Column columnWidth="one-third">Sidebar</Grid.Column>',
102
+ ' <Grid.Column columnWidth="two-thirds">Main content</Grid.Column>',
103
+ ' </Grid.Row>',
104
+ '</Grid>',
105
+ '```',
106
+ '',
107
+ '---',
108
+ '',
109
+ '### `Grid.Column` props',
110
+ '',
111
+ '| Prop | Type | Default | Description |',
112
+ '|---|---|---|---|',
113
+ '| `columnWidth` | `\'one-quarter\' \\| \'three-quarters\' \\| \'one-third\' \\| \'two-thirds\' \\| \'one-half\' \\| \'full\'` | `\'full\'` | GOV.UK grid column width class |',
114
+ '| `className` | `string` | — | Additional CSS classes |',
115
+ '| `children` | `ReactNode` | — | Column content |',
116
+ '',
117
+ '### `Grid.Row` props',
118
+ '',
119
+ '| Prop | Type | Default | Description |',
120
+ '|---|---|---|---|',
121
+ '| `className` | `string` | — | Additional CSS classes |',
122
+ '| `children` | `ReactNode` | — | One or more `Grid.Column` elements |',
123
+ '',
124
+ '---',
125
+ '',
126
+ '### Column widths must sum to a full row',
127
+ '',
128
+ 'The component does **not** enforce that column widths add up. Columns that exceed 100%',
129
+ 'will wrap to a new line; columns that total less than 100% will leave empty space.',
130
+ 'Always verify widths sum correctly:',
131
+ '',
132
+ '| Combination | Total |',
133
+ '|---|---|',
134
+ '| `one-half` + `one-half` | 100% |',
135
+ '| `one-third` + `two-thirds` | 100% |',
136
+ '| `one-quarter` + `three-quarters` | 100% |',
137
+ '| `one-quarter` × 4 | 100% |',
138
+ '| `full` | 100% |',
139
+ '',
140
+ '---',
141
+ '',
142
+ '### Do NOT add padding or margin directly to `Grid.Column`',
143
+ '',
144
+ 'GOV.UK Frontend uses gutters (padding) on grid column elements. Adding extra `padding`',
145
+ 'or `margin` to the column itself will break the gutter calculations and misalign content.',
146
+ 'Always put inner spacing on a wrapper `div` **inside** the column:',
147
+ '',
148
+ '```tsx',
149
+ '// Correct',
150
+ '<Grid.Column columnWidth="one-third">',
151
+ ' <div style={{ padding: \'var(--spacing-medium)\' }}>Sidebar content</div>',
152
+ '</Grid.Column>',
153
+ '',
154
+ '// Wrong — overrides GOV.UK gutter padding',
155
+ '<Grid.Column columnWidth="one-third" className="my-padded-column">',
156
+ ' Sidebar content',
157
+ '</Grid.Column>',
158
+ '```',
159
+ '',
160
+ '---',
161
+ '',
162
+ '### TypeScript types',
163
+ '',
164
+ '```ts',
165
+ "import { Grid, type GridProps } from '@arbor-education/design-system.components';",
166
+ '',
167
+ 'function MyLayout(props: GridProps) { ... }',
168
+ '```',
169
+ '',
170
+ '| Type | Description |',
171
+ '|---|---|',
172
+ '| `GridProps` | Props for the `<Grid>` root element |',
173
+ ].join('\n');
174
+ const RELATED_COMPONENTS = [
175
+ '## Related components',
176
+ '',
177
+ '[Row](?path=/docs/components-row--docs) · [Section](?path=/docs/components-section--docs) · [Modal](?path=/docs/components-modals-modal--docs)',
178
+ ].join('\n');
179
+ // ---------------------------------------------------------------------------
180
+ // Placeholder box — used only in schematic stories to make column boundaries
181
+ // visible. Each column in a row is given a distinct hue (cycled by position)
182
+ // so it is obvious where one column ends and the next begins. The real-page
183
+ // composition stories below do not use this — they render true page content.
184
+ // ---------------------------------------------------------------------------
185
+ const PLACEHOLDER_HUES = ['blue', 'green', 'purple', 'orange', 'teal', 'salmon', 'yellow'];
186
+ const placeholderStyle = (index) => {
187
+ const hue = PLACEHOLDER_HUES[index % PLACEHOLDER_HUES.length];
188
+ return {
189
+ background: `var(--color-extended-colours-${hue}-100)`,
190
+ border: `var(--border-width-thin) solid var(--color-extended-colours-${hue}-500)`,
191
+ borderRadius: 'var(--border-radius-small)',
192
+ padding: 'var(--spacing-medium)',
193
+ color: `var(--color-extended-colours-${hue}-800)`,
194
+ fontSize: 'var(--font-size-200)',
195
+ fontWeight: 500,
196
+ };
197
+ };
198
+ // ---------------------------------------------------------------------------
199
+ // Docs page
200
+ // ---------------------------------------------------------------------------
201
+ function GridDocsPage() {
202
+ return (_jsxs(_Fragment, { children: [_jsx(Title, {}), _jsx(Subtitle, {}), _jsx(Markdown, { children: DESCRIPTION_INTRO }), _jsx(DocHeading, { children: "Interactive example" }), _jsx(Markdown, { children: PROPS_INTRO }), _jsx(DocPrimary, {}), _jsx(Controls, {}), _jsx(DocHeading, { children: "Usage guidance" }), _jsx(Markdown, { children: USAGE_GUIDANCE }), _jsx(DocHeading, { children: "Developer notes" }), _jsx(Markdown, { children: DEVELOPER_NOTES }), _jsx(DocHeading, { children: "Examples" }), _jsx(Stories, { title: "" }), _jsx(Markdown, { children: RELATED_COMPONENTS })] }));
203
+ }
204
+ // ---------------------------------------------------------------------------
205
+ // Meta
206
+ // ---------------------------------------------------------------------------
207
+ const meta = {
208
+ title: 'Components/Grid',
209
+ component: Grid,
210
+ tags: ['autodocs'],
211
+ parameters: {
212
+ layout: 'padded',
213
+ docs: { page: GridDocsPage },
214
+ },
215
+ argTypes: {
216
+ className: {
217
+ control: false,
218
+ description: 'Additional CSS class names applied to the `govuk-main-wrapper` root element.',
219
+ table: {
220
+ type: { summary: 'string' },
221
+ },
222
+ },
223
+ children: {
224
+ control: false,
225
+ description: 'One or more `Grid.Row` elements that make up the page layout.',
226
+ table: {
227
+ type: { summary: 'ReactNode' },
228
+ },
229
+ },
230
+ },
231
+ };
232
+ export default meta;
233
+ // ---------------------------------------------------------------------------
234
+ // Helper: attach a per-story description to docs
235
+ // ---------------------------------------------------------------------------
236
+ const withDescription = (story, description) => ({
237
+ ...story,
238
+ parameters: {
239
+ ...story.parameters,
240
+ docs: {
241
+ ...story.parameters?.docs,
242
+ description: {
243
+ story: description,
244
+ },
245
+ },
246
+ },
247
+ });
248
+ // ---------------------------------------------------------------------------
249
+ // Template components
250
+ // ---------------------------------------------------------------------------
251
+ const AllWidthsTemplate = () => (_jsx(Grid, { children: ['full', 'three-quarters', 'two-thirds', 'one-half', 'one-third', 'one-quarter'].map((width, index) => (_jsx(Grid.Row, { children: _jsx(Grid.Column, { columnWidth: width, children: _jsx("div", { style: placeholderStyle(index), children: width }) }) }, width))) }));
252
+ const MultipleRowsTemplate = () => (_jsxs(Grid, { children: [_jsx(Grid.Row, { children: _jsx(Grid.Column, { columnWidth: "full", children: _jsx("div", { style: placeholderStyle(0), children: "Page header \u2014 full width" }) }) }), _jsxs(Grid.Row, { children: [_jsx(Grid.Column, { columnWidth: "one-third", children: _jsx("div", { style: placeholderStyle(0), children: "Navigation" }) }), _jsx(Grid.Column, { columnWidth: "two-thirds", children: _jsx("div", { style: placeholderStyle(1), children: "Main content" }) })] }), _jsx(Grid.Row, { children: _jsx(Grid.Column, { columnWidth: "full", children: _jsx("div", { style: placeholderStyle(0), children: "Footer \u2014 full width" }) }) })] }));
253
+ const FourQuartersTemplate = () => (_jsx(Grid, { children: _jsxs(Grid.Row, { children: [_jsx(Grid.Column, { columnWidth: "one-quarter", children: _jsx("div", { style: placeholderStyle(0), children: "Student summary" }) }), _jsx(Grid.Column, { columnWidth: "one-quarter", children: _jsx("div", { style: placeholderStyle(1), children: "Attendance" }) }), _jsx(Grid.Column, { columnWidth: "one-quarter", children: _jsx("div", { style: placeholderStyle(2), children: "Assessment" }) }), _jsx(Grid.Column, { columnWidth: "one-quarter", children: _jsx("div", { style: placeholderStyle(3), children: "Finance" }) })] }) }));
254
+ // ---------------------------------------------------------------------------
255
+ // Stories
256
+ // ---------------------------------------------------------------------------
257
+ export const Default = withDescription({
258
+ args: {},
259
+ render: args => (_jsx(Grid, { ...args, children: _jsx(Grid.Row, { children: _jsx(Grid.Column, { columnWidth: "full", children: _jsx("div", { style: placeholderStyle(0), children: "Main content \u2014 full width" }) }) }) })),
260
+ }, [
261
+ 'The simplest Grid usage: a single `Grid.Row` containing one `Grid.Column` set to `full` width.',
262
+ 'Use this as your starting point and add more columns or rows as the layout requires.',
263
+ 'The `className` prop on the Controls panel applies to the `govuk-main-wrapper` root.',
264
+ ].join(' '));
265
+ export const OneHalf = withDescription({
266
+ render: () => (_jsx(Grid, { children: _jsxs(Grid.Row, { children: [_jsx(Grid.Column, { columnWidth: "one-half", children: _jsx("div", { style: placeholderStyle(0), children: "Student information" }) }), _jsx(Grid.Column, { columnWidth: "one-half", children: _jsx("div", { style: placeholderStyle(1), children: "Attendance overview" }) })] }) })),
267
+ parameters: {
268
+ controls: { disable: true },
269
+ docs: {
270
+ source: {
271
+ language: 'tsx',
272
+ code: `
273
+ import { Grid } from '@arbor-education/design-system.components';
274
+
275
+ function OneHalfExample() {
276
+ return (
277
+ <Grid>
278
+ <Grid.Row>
279
+ <Grid.Column columnWidth="one-half">
280
+ Student information
281
+ </Grid.Column>
282
+ <Grid.Column columnWidth="one-half">
283
+ Attendance overview
284
+ </Grid.Column>
285
+ </Grid.Row>
286
+ </Grid>
287
+ );
288
+ }
289
+ export default OneHalfExample;
290
+ `.trim(),
291
+ },
292
+ },
293
+ },
294
+ }, [
295
+ 'Two equal `one-half` columns placed side by side.',
296
+ 'Use this when two content areas carry equal visual weight, such as a student information',
297
+ 'panel alongside an attendance summary.',
298
+ ].join(' '));
299
+ export const OneThirdTwoThirds = withDescription({
300
+ render: () => (_jsx(Grid, { children: _jsxs(Grid.Row, { children: [_jsx(Grid.Column, { columnWidth: "one-third", children: _jsx("div", { style: placeholderStyle(0), children: "Navigation" }) }), _jsx(Grid.Column, { columnWidth: "two-thirds", children: _jsx("div", { style: placeholderStyle(1), children: "Main content" }) })] }) })),
301
+ parameters: {
302
+ controls: { disable: true },
303
+ docs: {
304
+ source: {
305
+ language: 'tsx',
306
+ code: `
307
+ import { Grid } from '@arbor-education/design-system.components';
308
+
309
+ function SidebarLayoutExample() {
310
+ return (
311
+ <Grid>
312
+ <Grid.Row>
313
+ <Grid.Column columnWidth="one-third">
314
+ Navigation
315
+ </Grid.Column>
316
+ <Grid.Column columnWidth="two-thirds">
317
+ Main content
318
+ </Grid.Column>
319
+ </Grid.Row>
320
+ </Grid>
321
+ );
322
+ }
323
+ export default SidebarLayoutExample;
324
+ `.trim(),
325
+ },
326
+ },
327
+ },
328
+ }, [
329
+ 'The canonical GOV.UK sidebar + main content pattern.',
330
+ 'A narrow left-hand `one-third` column holds navigation or filters,',
331
+ 'while the `two-thirds` column carries the primary page content.',
332
+ ].join(' '));
333
+ export const TwoThirdsOneThird = withDescription({
334
+ render: () => (_jsx(Grid, { children: _jsxs(Grid.Row, { children: [_jsx(Grid.Column, { columnWidth: "two-thirds", children: _jsx("div", { style: placeholderStyle(0), children: "Assessment results" }) }), _jsx(Grid.Column, { columnWidth: "one-third", children: _jsx("div", { style: placeholderStyle(1), children: "Filters" }) })] }) })),
335
+ parameters: {
336
+ controls: { disable: true },
337
+ docs: {
338
+ source: {
339
+ language: 'tsx',
340
+ code: `
341
+ import { Grid } from '@arbor-education/design-system.components';
342
+
343
+ function ReversedSidebarExample() {
344
+ return (
345
+ <Grid>
346
+ <Grid.Row>
347
+ <Grid.Column columnWidth="two-thirds">
348
+ Assessment results
349
+ </Grid.Column>
350
+ <Grid.Column columnWidth="one-third">
351
+ Filters
352
+ </Grid.Column>
353
+ </Grid.Row>
354
+ </Grid>
355
+ );
356
+ }
357
+ export default ReversedSidebarExample;
358
+ `.trim(),
359
+ },
360
+ },
361
+ },
362
+ }, [
363
+ 'The reversed layout — main content on the left with a narrower right-hand sidebar.',
364
+ 'Useful when filter controls or contextual information should sit to the right of primary content.',
365
+ ].join(' '));
366
+ export const OneQuarterThreeQuarters = withDescription({
367
+ render: () => (_jsx(Grid, { children: _jsxs(Grid.Row, { children: [_jsx(Grid.Column, { columnWidth: "one-quarter", children: _jsx("div", { style: placeholderStyle(0), children: "Navigation" }) }), _jsx(Grid.Column, { columnWidth: "three-quarters", children: _jsx("div", { style: placeholderStyle(1), children: "Main content" }) })] }) })),
368
+ parameters: {
369
+ controls: { disable: true },
370
+ docs: {
371
+ source: {
372
+ language: 'tsx',
373
+ code: `
374
+ import { Grid } from '@arbor-education/design-system.components';
375
+
376
+ function NarrowSidebarExample() {
377
+ return (
378
+ <Grid>
379
+ <Grid.Row>
380
+ <Grid.Column columnWidth="one-quarter">
381
+ Navigation
382
+ </Grid.Column>
383
+ <Grid.Column columnWidth="three-quarters">
384
+ Main content
385
+ </Grid.Column>
386
+ </Grid.Row>
387
+ </Grid>
388
+ );
389
+ }
390
+ export default NarrowSidebarExample;
391
+ `.trim(),
392
+ },
393
+ },
394
+ },
395
+ }, [
396
+ 'A narrow `one-quarter` sidebar paired with a wide `three-quarters` content area.',
397
+ 'Use this when the sidebar needs to be more compact than the standard `one-third` variant,',
398
+ 'such as a compact navigation list alongside a data-dense report.',
399
+ ].join(' '));
400
+ export const ThreeQuartersOneQuarter = withDescription({
401
+ render: () => (_jsx(Grid, { children: _jsxs(Grid.Row, { children: [_jsx(Grid.Column, { columnWidth: "three-quarters", children: _jsx("div", { style: placeholderStyle(0), children: "Main content" }) }), _jsx(Grid.Column, { columnWidth: "one-quarter", children: _jsx("div", { style: placeholderStyle(1), children: "Summary panel" }) })] }) })),
402
+ parameters: {
403
+ controls: { disable: true },
404
+ docs: {
405
+ source: {
406
+ language: 'tsx',
407
+ code: `
408
+ import { Grid } from '@arbor-education/design-system.components';
409
+
410
+ function WideContentNarrowPanelExample() {
411
+ return (
412
+ <Grid>
413
+ <Grid.Row>
414
+ <Grid.Column columnWidth="three-quarters">
415
+ Main content
416
+ </Grid.Column>
417
+ <Grid.Column columnWidth="one-quarter">
418
+ Summary panel
419
+ </Grid.Column>
420
+ </Grid.Row>
421
+ </Grid>
422
+ );
423
+ }
424
+ export default WideContentNarrowPanelExample;
425
+ `.trim(),
426
+ },
427
+ },
428
+ },
429
+ }, [
430
+ 'Wide primary content on the left with a compact right-hand summary panel.',
431
+ 'The `three-quarters` + `one-quarter` split gives you the most content space',
432
+ 'while still showing a supplementary panel — ideal for report views with a key-metrics sidebar.',
433
+ ].join(' '));
434
+ export const FourQuarters = withDescription({
435
+ render: FourQuartersTemplate,
436
+ parameters: {
437
+ controls: { disable: true },
438
+ docs: {
439
+ source: {
440
+ language: 'tsx',
441
+ code: `
442
+ import { Grid } from '@arbor-education/design-system.components';
443
+
444
+ function FourQuartersExample() {
445
+ return (
446
+ <Grid>
447
+ <Grid.Row>
448
+ <Grid.Column columnWidth="one-quarter">
449
+ Student summary
450
+ </Grid.Column>
451
+ <Grid.Column columnWidth="one-quarter">
452
+ Attendance
453
+ </Grid.Column>
454
+ <Grid.Column columnWidth="one-quarter">
455
+ Assessment
456
+ </Grid.Column>
457
+ <Grid.Column columnWidth="one-quarter">
458
+ Finance
459
+ </Grid.Column>
460
+ </Grid.Row>
461
+ </Grid>
462
+ );
463
+ }
464
+ export default FourQuartersExample;
465
+ `.trim(),
466
+ },
467
+ },
468
+ },
469
+ }, [
470
+ 'Four equal `one-quarter` columns across a single row — the card-grid pattern.',
471
+ 'Use this for dashboard metric cards, category overviews, or any set of four equal-weight items',
472
+ 'that should be presented side by side at the page level.',
473
+ ].join(' '));
474
+ export const MultipleRows = withDescription({
475
+ render: MultipleRowsTemplate,
476
+ parameters: {
477
+ controls: { disable: true },
478
+ docs: {
479
+ source: {
480
+ language: 'tsx',
481
+ code: `
482
+ import { Grid } from '@arbor-education/design-system.components';
483
+
484
+ function MultipleRowsExample() {
485
+ return (
486
+ <Grid>
487
+ <Grid.Row>
488
+ <Grid.Column columnWidth="full">
489
+ Page header — full width
490
+ </Grid.Column>
491
+ </Grid.Row>
492
+ <Grid.Row>
493
+ <Grid.Column columnWidth="one-third">
494
+ Navigation
495
+ </Grid.Column>
496
+ <Grid.Column columnWidth="two-thirds">
497
+ Main content
498
+ </Grid.Column>
499
+ </Grid.Row>
500
+ <Grid.Row>
501
+ <Grid.Column columnWidth="full">
502
+ Footer — full width
503
+ </Grid.Column>
504
+ </Grid.Row>
505
+ </Grid>
506
+ );
507
+ }
508
+ export default MultipleRowsExample;
509
+ `.trim(),
510
+ },
511
+ },
512
+ },
513
+ }, [
514
+ 'Multiple `Grid.Row` instances stacked inside a single `Grid` wrapper.',
515
+ 'Each row is independent — rows can use different column configurations.',
516
+ 'The `Grid` root (`govuk-main-wrapper`) wraps all rows and provides the outer page padding.',
517
+ ].join(' '));
518
+ export const AllWidths = withDescription({
519
+ render: AllWidthsTemplate,
520
+ parameters: {
521
+ controls: { disable: true },
522
+ docs: {
523
+ source: {
524
+ language: 'tsx',
525
+ code: `
526
+ import { Grid } from '@arbor-education/design-system.components';
527
+
528
+ // All six columnWidth values, each in its own row for comparison
529
+ function AllWidthsExample() {
530
+ return (
531
+ <Grid>
532
+ <Grid.Row>
533
+ <Grid.Column columnWidth="full">full</Grid.Column>
534
+ </Grid.Row>
535
+ <Grid.Row>
536
+ <Grid.Column columnWidth="three-quarters">three-quarters</Grid.Column>
537
+ </Grid.Row>
538
+ <Grid.Row>
539
+ <Grid.Column columnWidth="two-thirds">two-thirds</Grid.Column>
540
+ </Grid.Row>
541
+ <Grid.Row>
542
+ <Grid.Column columnWidth="one-half">one-half</Grid.Column>
543
+ </Grid.Row>
544
+ <Grid.Row>
545
+ <Grid.Column columnWidth="one-third">one-third</Grid.Column>
546
+ </Grid.Row>
547
+ <Grid.Row>
548
+ <Grid.Column columnWidth="one-quarter">one-quarter</Grid.Column>
549
+ </Grid.Row>
550
+ </Grid>
551
+ );
552
+ }
553
+ export default AllWidthsExample;
554
+ `.trim(),
555
+ },
556
+ },
557
+ },
558
+ }, [
559
+ 'All six available `columnWidth` values shown in isolation, one per row, from widest to narrowest.',
560
+ 'Use this as a visual reference when choosing the right column proportion for your layout.',
561
+ ].join(' '));
562
+ // ---------------------------------------------------------------------------
563
+ // Real-page composition examples
564
+ // ---------------------------------------------------------------------------
565
+ const kpiRowStyle = {
566
+ display: 'flex',
567
+ gap: 'var(--spacing-medium)',
568
+ marginBottom: 'var(--spacing-medium)',
569
+ };
570
+ const pageHeaderStyle = {
571
+ display: 'flex',
572
+ alignItems: 'center',
573
+ justifyContent: 'space-between',
574
+ paddingBottom: 'var(--spacing-medium)',
575
+ borderBottom: 'var(--border-width-thin) solid var(--color-neutral-200)',
576
+ marginBottom: 'var(--spacing-medium)',
577
+ };
578
+ const actionRowStyle = {
579
+ display: 'flex',
580
+ gap: 'var(--spacing-small)',
581
+ };
582
+ const sidebarInnerStyle = {
583
+ display: 'flex',
584
+ flexDirection: 'column',
585
+ alignItems: 'center',
586
+ gap: 'var(--spacing-medium)',
587
+ paddingRight: 'var(--spacing-medium)',
588
+ };
589
+ export const StudentProfilePage = withDescription({
590
+ render: () => (_jsxs(Grid, { children: [_jsx(Grid.Row, { children: _jsx(Grid.Column, { columnWidth: "full", children: _jsxs("div", { style: pageHeaderStyle, children: [_jsxs("div", { children: [_jsx(Heading, { level: 1, children: "Emma Johnson" }), _jsxs("div", { style: { display: 'flex', gap: 'var(--spacing-small)', marginTop: 'var(--spacing-xsmall)' }, children: [_jsx(Tag, { color: "blue", children: "Year 9" }), _jsx(Tag, { color: "teal", children: "Set 2" }), _jsx(Tag, { color: "green", children: "On Roll" })] })] }), _jsxs("div", { style: actionRowStyle, children: [_jsx(Button, { variant: "secondary", size: "S", iconLeftName: "pencil", children: "Edit" }), _jsx(Button, { variant: "secondary", size: "S", iconLeftName: "file", children: "Print" }), _jsx(Button, { variant: "primary", size: "S", children: "Contact Home" })] })] }) }) }), _jsxs(Grid.Row, { children: [_jsx(Grid.Column, { columnWidth: "one-quarter", children: _jsxs("div", { style: sidebarInnerStyle, children: [_jsx(Avatar, { size: "extra-large", initials: "EJ" }), _jsxs(KVPList, { children: [_jsxs(KVPList.Row, { orientation: "vertical", children: [_jsx(KVPList.Term, { children: "Date of Birth" }), _jsx(KVPList.Definition, { children: "12 Mar 2010" })] }), _jsxs(KVPList.Row, { orientation: "vertical", children: [_jsx(KVPList.Term, { children: "Tutor Group" }), _jsx(KVPList.Definition, { children: "9B" })] }), _jsxs(KVPList.Row, { orientation: "vertical", children: [_jsx(KVPList.Term, { children: "Form Tutor" }), _jsx(KVPList.Definition, { children: "Mr T. Harris" })] }), _jsxs(KVPList.Row, { orientation: "vertical", children: [_jsx(KVPList.Term, { children: "SEN Status" }), _jsx(KVPList.Definition, { children: _jsx(Tag, { color: "orange", children: "Monitor" }) })] }), _jsxs(KVPList.Row, { orientation: "vertical", children: [_jsx(KVPList.Term, { children: "FSM" }), _jsx(KVPList.Definition, { children: "Yes" })] })] })] }) }), _jsxs(Grid.Column, { columnWidth: "three-quarters", children: [_jsx(Section, { title: "Academic Overview", headingLevel: 2, children: _jsxs("div", { style: kpiRowStyle, children: [_jsx(KPICard, { label: "Attendance", value: 94.2, isPercentage: true }), _jsx(KPICard, { label: "Sessions This Term", value: 128 }), _jsx(KPICard, { label: "Unauthorised Absences", value: 3 }), _jsx(KPICard, { label: "Behaviour Points", value: 12 })] }) }), _jsx(Section, { title: "Recent Classes", headingLevel: 2, children: _jsxs("div", { style: { display: 'flex', gap: 'var(--spacing-medium)', flexWrap: 'wrap' }, children: [_jsx(DataViewCard, { header: "Mathematics", rows: [
591
+ { label: 'Teacher', value: 'Ms R. Patel' },
592
+ { label: 'Room', value: 'B14' },
593
+ { label: 'Last Grade', value: 'A' },
594
+ ] }), _jsx(DataViewCard, { header: "English Literature", rows: [
595
+ { label: 'Teacher', value: 'Mr D. Clarke' },
596
+ { label: 'Room', value: 'A03' },
597
+ { label: 'Last Grade', value: 'B+' },
598
+ ] }), _jsx(DataViewCard, { header: "Science", rows: [
599
+ { label: 'Teacher', value: 'Dr S. Okafor' },
600
+ { label: 'Room', value: 'Lab 2' },
601
+ { label: 'Last Grade', value: 'A*' },
602
+ ] })] }) })] })] })] })),
603
+ parameters: {
604
+ controls: { disable: true },
605
+ layout: 'fullscreen',
606
+ },
607
+ }, [
608
+ 'A realistic student profile page composed entirely from design system components.',
609
+ 'The `one-quarter` sidebar holds identity details while `three-quarters` carries KPI metrics and class cards.',
610
+ 'Use this pattern when a student record needs a persistent summary alongside rich contextual content.',
611
+ ].join(' '));
612
+ export const AttendanceDashboard = withDescription({
613
+ render: () => (_jsxs(Grid, { children: [_jsx(Grid.Row, { children: _jsx(Grid.Column, { columnWidth: "full", children: _jsxs("div", { style: pageHeaderStyle, children: [_jsxs("div", { children: [_jsx(Heading, { level: 1, children: "Attendance Overview" }), _jsxs("div", { style: { display: 'flex', gap: 'var(--spacing-small)', marginTop: 'var(--spacing-xsmall)' }, children: [_jsx(Badge, { colour: "teal", children: "Autumn Term 2024" }), _jsx(Badge, { colour: "blue", children: "All Year Groups" })] })] }), _jsxs("div", { style: actionRowStyle, children: [_jsx(Button, { variant: "secondary", size: "S", iconLeftName: "download", children: "Export" }), _jsx(Button, { variant: "primary", size: "S", children: "Generate Report" })] })] }) }) }), _jsxs(Grid.Row, { children: [_jsx(Grid.Column, { columnWidth: "one-quarter", children: _jsx(KPICard, { label: "Overall Attendance", value: 96.1, isPercentage: true }) }), _jsx(Grid.Column, { columnWidth: "one-quarter", children: _jsx(KPICard, { label: "Persistent Absentees", value: 23 }) }), _jsx(Grid.Column, { columnWidth: "one-quarter", children: _jsx(KPICard, { label: "Sessions Recorded", value: 2840 }) }), _jsx(Grid.Column, { columnWidth: "one-quarter", children: _jsx(KPICard, { label: "Punctuality Issues", value: 11 }) })] }), _jsxs(Grid.Row, { children: [_jsx(Grid.Column, { columnWidth: "two-thirds", children: _jsx(Section, { title: "Lowest Attendance", headingLevel: 2, children: _jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 'var(--spacing-small)' }, children: [_jsx(DataViewCard, { header: "Callum Wright", avatar: { initials: 'CW' }, rows: [
614
+ { label: 'Year Group', value: 'Year 10' },
615
+ { label: 'Attendance', value: '71.4%' },
616
+ { label: 'Unauthorised', value: '14 sessions' },
617
+ ] }), _jsx(DataViewCard, { header: "Priya Sharma", avatar: { initials: 'PS' }, rows: [
618
+ { label: 'Year Group', value: 'Year 8' },
619
+ { label: 'Attendance', value: '74.8%' },
620
+ { label: 'Unauthorised', value: '11 sessions' },
621
+ ] }), _jsx(DataViewCard, { header: "Jordan Blake", avatar: { initials: 'JB' }, rows: [
622
+ { label: 'Year Group', value: 'Year 11' },
623
+ { label: 'Attendance', value: '78.2%' },
624
+ { label: 'Unauthorised', value: '9 sessions' },
625
+ ] })] }) }) }), _jsxs(Grid.Column, { columnWidth: "one-third", children: [_jsx(Section, { title: "Filter by Year Group", headingLevel: 2, children: _jsx("div", { style: { display: 'flex', flexWrap: 'wrap', gap: 'var(--spacing-xsmall)' }, children: ['Year 7', 'Year 8', 'Year 9', 'Year 10', 'Year 11', 'Year 12', 'Year 13'].map(year => (_jsx(Tag, { color: "neutral", children: year }, year))) }) }), _jsx(Section, { title: "Absence Codes", headingLevel: 2, children: _jsxs(KVPList, { children: [_jsxs(KVPList.Row, { orientation: "horizontal", children: [_jsx(KVPList.Term, { children: "Authorised" }), _jsx(KVPList.Definition, { prominence: "strong", children: "847" })] }), _jsxs(KVPList.Row, { orientation: "horizontal", children: [_jsx(KVPList.Term, { children: "Unauthorised" }), _jsx(KVPList.Definition, { prominence: "strong", children: "312" })] }), _jsxs(KVPList.Row, { orientation: "horizontal", children: [_jsx(KVPList.Term, { children: "Medical" }), _jsx(KVPList.Definition, { prominence: "strong", children: "204" })] }), _jsxs(KVPList.Row, { orientation: "horizontal", children: [_jsx(KVPList.Term, { children: "Holiday" }), _jsx(KVPList.Definition, { prominence: "strong", children: "58" })] })] }) })] })] })] })),
626
+ parameters: {
627
+ controls: { disable: true },
628
+ layout: 'fullscreen',
629
+ },
630
+ }, [
631
+ 'A school attendance dashboard showing the full-width four-column KPI pattern followed by a',
632
+ '`two-thirds` + `one-third` detail layout.',
633
+ 'The left column surfaces the students most at risk; the right column holds quick filters and summary stats.',
634
+ ].join(' '));
635
+ export const StaffDirectory = withDescription({
636
+ render: () => (_jsxs(Grid, { children: [_jsx(Grid.Row, { children: _jsx(Grid.Column, { columnWidth: "full", children: _jsxs("div", { style: pageHeaderStyle, children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 'var(--spacing-small)' }, children: [_jsx(Heading, { level: 1, children: "Staff Directory" }), _jsx(Badge, { colour: "blue", children: "24 staff" })] }), _jsx(Button, { variant: "primary", size: "S", iconLeftName: "plus", children: "Add Staff Member" })] }) }) }), _jsx(Grid.Row, { children: _jsx(Grid.Column, { columnWidth: "full", children: _jsx("div", { style: { display: 'flex', gap: 'var(--spacing-xsmall)', marginBottom: 'var(--spacing-medium)', flexWrap: 'wrap' }, children: [
637
+ { label: 'Teaching', color: 'blue' },
638
+ { label: 'Support', color: 'teal' },
639
+ { label: 'Leadership', color: 'purple' },
640
+ { label: 'Admin', color: 'orange' },
641
+ ].map(({ label, color }) => (_jsx(Tag, { color: color, children: label }, label))) }) }) }), _jsxs(Grid.Row, { children: [_jsx(Grid.Column, { columnWidth: "one-third", children: _jsx(DataViewCard, { header: "Sarah Thompson", avatar: { initials: 'ST' }, rows: [
642
+ { label: 'Role', value: 'Head of Maths' },
643
+ { label: 'Department', value: 'Mathematics' },
644
+ { label: 'Email', value: 's.thompson@school.edu' },
645
+ ] }) }), _jsx(Grid.Column, { columnWidth: "one-third", children: _jsx(DataViewCard, { header: "James Okafor", avatar: { initials: 'JO' }, rows: [
646
+ { label: 'Role', value: 'SENCO' },
647
+ { label: 'Department', value: 'Inclusion' },
648
+ { label: 'Email', value: 'j.okafor@school.edu' },
649
+ ] }) }), _jsx(Grid.Column, { columnWidth: "one-third", children: _jsx(DataViewCard, { header: "Rachel Patel", avatar: { initials: 'RP' }, rows: [
650
+ { label: 'Role', value: 'Science Teacher' },
651
+ { label: 'Department', value: 'Science' },
652
+ { label: 'Email', value: 'r.patel@school.edu' },
653
+ ] }) })] }), _jsxs(Grid.Row, { children: [_jsx(Grid.Column, { columnWidth: "one-third", children: _jsx(DataViewCard, { header: "David Clarke", avatar: { initials: 'DC' }, rows: [
654
+ { label: 'Role', value: 'English Teacher' },
655
+ { label: 'Department', value: 'English' },
656
+ { label: 'Email', value: 'd.clarke@school.edu' },
657
+ ] }) }), _jsx(Grid.Column, { columnWidth: "one-third", children: _jsx(DataViewCard, { header: "Amara Diallo", avatar: { initials: 'AD' }, rows: [
658
+ { label: 'Role', value: 'Deputy Head' },
659
+ { label: 'Department', value: 'Leadership' },
660
+ { label: 'Email', value: 'a.diallo@school.edu' },
661
+ ] }) }), _jsx(Grid.Column, { columnWidth: "one-third", children: _jsx(DataViewCard, { header: "Tom Harris", avatar: { initials: 'TH' }, rows: [
662
+ { label: 'Role', value: 'PE Teacher' },
663
+ { label: 'Department', value: 'PE & Sport' },
664
+ { label: 'Email', value: 't.harris@school.edu' },
665
+ ] }) })] })] })),
666
+ parameters: {
667
+ controls: { disable: true },
668
+ layout: 'fullscreen',
669
+ },
670
+ }, [
671
+ 'A staff directory page demonstrating the three-column `one-third` grid pattern across multiple rows.',
672
+ 'Each `DataViewCard` represents a staff member with avatar, role, and contact details.',
673
+ 'The filter tags at the top use a full-width row; the card grid rows beneath are fully independent.',
674
+ ].join(' '));
675
+ //# sourceMappingURL=Grid.stories.js.map