@arbor-education/design-system.components 0.16.1 → 0.17.0
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.
- package/.gather/instructions/project-overview.md +0 -4
- package/.gather/skills/aroo-hunni/SKILL.md +58 -0
- package/CHANGELOG.md +25 -0
- package/CONTRIBUTING.md +1 -0
- package/dist/components/arborLogo/ArborLogo.d.ts +9 -0
- package/dist/components/arborLogo/ArborLogo.d.ts.map +1 -0
- package/dist/components/arborLogo/ArborLogo.js +17 -0
- package/dist/components/arborLogo/ArborLogo.js.map +1 -0
- package/dist/components/arborLogo/ArborLogo.stories.d.ts +94 -0
- package/dist/components/arborLogo/ArborLogo.stories.d.ts.map +1 -0
- package/dist/components/arborLogo/ArborLogo.stories.js +418 -0
- package/dist/components/arborLogo/ArborLogo.stories.js.map +1 -0
- package/dist/components/arborLogo/ArborLogo.test.d.ts +2 -0
- package/dist/components/arborLogo/ArborLogo.test.d.ts.map +1 -0
- package/dist/components/arborLogo/ArborLogo.test.js +32 -0
- package/dist/components/arborLogo/ArborLogo.test.js.map +1 -0
- package/dist/components/dataViewCard/DataViewCard.d.ts +19 -0
- package/dist/components/dataViewCard/DataViewCard.d.ts.map +1 -0
- package/dist/components/dataViewCard/DataViewCard.js +13 -0
- package/dist/components/dataViewCard/DataViewCard.js.map +1 -0
- package/dist/components/dataViewCard/DataViewCard.stories.d.ts +100 -0
- package/dist/components/dataViewCard/DataViewCard.stories.d.ts.map +1 -0
- package/dist/components/dataViewCard/DataViewCard.stories.js +317 -0
- package/dist/components/dataViewCard/DataViewCard.stories.js.map +1 -0
- package/dist/components/dataViewCard/DataViewCard.test.d.ts +2 -0
- package/dist/components/dataViewCard/DataViewCard.test.d.ts.map +1 -0
- package/dist/components/dataViewCard/DataViewCard.test.js +67 -0
- package/dist/components/dataViewCard/DataViewCard.test.js.map +1 -0
- package/dist/components/row/Row.d.ts +2 -1
- package/dist/components/row/Row.d.ts.map +1 -1
- package/dist/components/row/Row.js +2 -2
- package/dist/components/row/Row.js.map +1 -1
- package/dist/components/treeRow/TreeRow.d.ts +32 -0
- package/dist/components/treeRow/TreeRow.d.ts.map +1 -0
- package/dist/components/treeRow/TreeRow.js +19 -0
- package/dist/components/treeRow/TreeRow.js.map +1 -0
- package/dist/components/treeRow/TreeRow.stories.d.ts +13 -0
- package/dist/components/treeRow/TreeRow.stories.d.ts.map +1 -0
- package/dist/components/treeRow/TreeRow.stories.js +774 -0
- package/dist/components/treeRow/TreeRow.stories.js.map +1 -0
- package/dist/components/treeRow/TreeRow.test.d.ts +2 -0
- package/dist/components/treeRow/TreeRow.test.d.ts.map +1 -0
- package/dist/components/treeRow/TreeRow.test.js +262 -0
- package/dist/components/treeRow/TreeRow.test.js.map +1 -0
- package/dist/components/treeRow/TreeRowSection.d.ts +12 -0
- package/dist/components/treeRow/TreeRowSection.d.ts.map +1 -0
- package/dist/components/treeRow/TreeRowSection.js +20 -0
- package/dist/components/treeRow/TreeRowSection.js.map +1 -0
- package/dist/index.css +146 -1
- package/dist/index.css.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/components/arborLogo/ArborLogo.stories.tsx +663 -0
- package/src/components/arborLogo/ArborLogo.test.tsx +36 -0
- package/src/components/arborLogo/ArborLogo.tsx +92 -0
- package/src/components/arborLogo/__snapshots__/ArborLogo.test.tsx.snap +424 -0
- package/src/components/dataViewCard/DataViewCard.stories.tsx +464 -0
- package/src/components/dataViewCard/DataViewCard.test.tsx +127 -0
- package/src/components/dataViewCard/DataViewCard.tsx +62 -0
- package/src/components/dataViewCard/dataViewCard.scss +25 -0
- package/src/components/row/Row.tsx +4 -1
- package/src/components/row/row.scss +9 -1
- package/src/components/treeRow/TreeRow.stories.tsx +870 -0
- package/src/components/treeRow/TreeRow.test.tsx +371 -0
- package/src/components/treeRow/TreeRow.tsx +85 -0
- package/src/components/treeRow/TreeRowSection.tsx +56 -0
- package/src/components/treeRow/treeRow.scss +134 -0
- package/src/docs/Contributing.mdx +1 -0
- package/src/index.scss +2 -0
- package/src/index.ts +4 -1
|
@@ -0,0 +1,774 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import { Controls, Heading, Markdown, Primary, Stories, Subheading, Subtitle, Title, } from '@storybook/addon-docs/blocks';
|
|
4
|
+
import { TreeRow } from '../treeRow/TreeRow';
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
// Description blocks
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
const DESCRIPTION_INTRO = `
|
|
9
|
+
**TreeRow** is a collapsible, hierarchical list component for displaying nested data — think
|
|
10
|
+
school timetables, curriculum structures, student contact breakdowns, or any tree-shaped
|
|
11
|
+
information that benefits from progressive disclosure.
|
|
12
|
+
`.trim();
|
|
13
|
+
const SECTION_PROPS_TABLE = `
|
|
14
|
+
| Prop | Type | Required | Default | Description |
|
|
15
|
+
|------|------|----------|---------|-------------|
|
|
16
|
+
| \`label\` | \`string\` | Yes | — | The heading text shown on the branch row (always visible). |
|
|
17
|
+
| \`children\` | \`ReactNode\` | Yes | — | The content revealed when the section is expanded. Usually \`Row\` components or nested \`TreeRow.Section\` elements. |
|
|
18
|
+
| \`id\` | \`string\` | No | auto-generated | Stable identifier used by Radix Accordion. **Must be provided** if you want \`defaultValue\` to reliably pre-open this section — auto-generated IDs are non-deterministic across renders. |
|
|
19
|
+
| \`onClick\` | \`MouseEventHandler<HTMLDivElement>\` | No | — | When provided, the branch row becomes clickable independently of the expand/collapse trigger. A navigation caret appears on the right. The trigger's click is deliberately isolated via \`stopPropagation\` so both interactions work without conflict. |
|
|
20
|
+
| \`className\` | \`string\` | No | — | Extra CSS class(es) applied to the \`Accordion.Item\` wrapper. |
|
|
21
|
+
`.trim();
|
|
22
|
+
const USAGE_GUIDANCE = `
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## When to use
|
|
26
|
+
|
|
27
|
+
- Displaying hierarchical data that would be overwhelming if shown flat — timetable subjects
|
|
28
|
+
and their lessons, a pupil's assessment breakdown by strand and topic, staff rotas with
|
|
29
|
+
team groupings.
|
|
30
|
+
- When the user needs to explore structure progressively rather than scan everything at once.
|
|
31
|
+
- Sidebar panels, detail drawers, or data-dense cards where vertical space is precious.
|
|
32
|
+
|
|
33
|
+
## When NOT to use
|
|
34
|
+
|
|
35
|
+
- Simple flat lists — use [\`Row\`](?path=/docs/components-row--docs) directly.
|
|
36
|
+
- Navigation menus — use a dedicated nav component with proper ARIA roles.
|
|
37
|
+
- Data grids or sortable tables — use the \`Table\` component instead.
|
|
38
|
+
- Deeply nested structures beyond 3–4 levels: cognitive load becomes too high; consider
|
|
39
|
+
restructuring the information architecture instead.
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Two APIs
|
|
44
|
+
|
|
45
|
+
**TreeRow** supports two usage patterns — pick one per instance, never mix.
|
|
46
|
+
|
|
47
|
+
### Data-driven (recommended for simple trees)
|
|
48
|
+
|
|
49
|
+
Pass an \`items\` array of \`TreeRowItem\` objects. Nesting is expressed recursively via
|
|
50
|
+
\`children\` arrays on each item. The component works out whether each item is a branch
|
|
51
|
+
(Section) or a leaf (Row) automatically.
|
|
52
|
+
|
|
53
|
+
\`\`\`tsx
|
|
54
|
+
import { TreeRow } from '@arbor-education/design-system.components';
|
|
55
|
+
|
|
56
|
+
const items = [
|
|
57
|
+
{
|
|
58
|
+
id: 'twilight',
|
|
59
|
+
label: 'Twilight',
|
|
60
|
+
children: [
|
|
61
|
+
{ label: 'Author', value: 'Stephenie Meyer' },
|
|
62
|
+
{ label: 'Published', value: '2005' },
|
|
63
|
+
],
|
|
64
|
+
},
|
|
65
|
+
];
|
|
66
|
+
|
|
67
|
+
<TreeRow items={items} defaultValue={['twilight']} />
|
|
68
|
+
\`\`\`
|
|
69
|
+
|
|
70
|
+
### Compound component (for custom layouts or mixed content)
|
|
71
|
+
|
|
72
|
+
Use \`TreeRow.Section\` and \`Row\` directly as JSX children. This lets you
|
|
73
|
+
nest non-Row content inside sections, apply custom class names per node, or mix
|
|
74
|
+
interactive and static rows freely.
|
|
75
|
+
|
|
76
|
+
\`\`\`tsx
|
|
77
|
+
import { TreeRow, Row } from '@arbor-education/design-system.components';
|
|
78
|
+
|
|
79
|
+
<TreeRow>
|
|
80
|
+
<TreeRow.Section id="biology" label="Biology">
|
|
81
|
+
<Row label="Teacher" value="Dr Cullen" />
|
|
82
|
+
<Row label="Room" value="Lab 3" />
|
|
83
|
+
</TreeRow.Section>
|
|
84
|
+
</TreeRow>
|
|
85
|
+
\`\`\`
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## Clickable sections
|
|
90
|
+
|
|
91
|
+
Sections can have an \`onClick\` handler in addition to (or instead of) expand/collapse
|
|
92
|
+
behaviour. When \`onClick\` is provided:
|
|
93
|
+
|
|
94
|
+
- A navigation caret appears on the trailing edge of the branch row.
|
|
95
|
+
- Clicking the **row area** fires \`onClick\`.
|
|
96
|
+
- Clicking the **label / chevron trigger** still only toggles expand/collapse — the
|
|
97
|
+
trigger's click is deliberately isolated via \`stopPropagation\`.
|
|
98
|
+
|
|
99
|
+
This means a section can simultaneously navigate somewhere **and** expand to reveal
|
|
100
|
+
child rows — a common pattern in Arbor's sidebar detail panels.
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## Pre-opening sections
|
|
105
|
+
|
|
106
|
+
Use \`defaultValue\` with an array of section \`id\` strings to open sections on initial
|
|
107
|
+
render. If a section does not have an explicit \`id\` prop, its accordion value is an
|
|
108
|
+
auto-generated React ID that changes between renders — so **always set \`id\` on any
|
|
109
|
+
section you want in \`defaultValue\`**.
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## Accessibility
|
|
114
|
+
|
|
115
|
+
- Built on [Radix UI Accordion](https://www.radix-ui.com/primitives/docs/components/accordion)
|
|
116
|
+
which ships with full keyboard support and correct ARIA attributes (\`aria-expanded\`,
|
|
117
|
+
\`aria-controls\`, \`role="region"\`) out of the box.
|
|
118
|
+
- Each trigger is a focusable button — keyboard users can navigate with **Tab** and
|
|
119
|
+
toggle sections with **Enter** or **Space**.
|
|
120
|
+
- Only the props listed above are exposed — for advanced accordion behaviour see the
|
|
121
|
+
[Radix Accordion docs](https://www.radix-ui.com/primitives/docs/components/accordion).
|
|
122
|
+
`.trim();
|
|
123
|
+
const RELATED_COMPONENTS = `
|
|
124
|
+
## Related components
|
|
125
|
+
|
|
126
|
+
- [Row](?path=/docs/components-row--docs) — use directly as children inside \`TreeRow.Section\`.
|
|
127
|
+
`.trim();
|
|
128
|
+
// ---------------------------------------------------------------------------
|
|
129
|
+
// Docs page (compound component pattern)
|
|
130
|
+
// ---------------------------------------------------------------------------
|
|
131
|
+
function TreeRowDocsPage() {
|
|
132
|
+
return (_jsxs(_Fragment, { children: [_jsx(Title, {}), _jsx(Subtitle, {}), _jsx(Markdown, { children: DESCRIPTION_INTRO }), _jsx(Primary, {}), _jsx(Heading, { children: "Props reference" }), _jsx(Subheading, { children: "TreeRow root props" }), _jsx(Controls, {}), _jsx(Subheading, { children: "TreeRow.Section props" }), _jsx(Markdown, { children: SECTION_PROPS_TABLE }), _jsx(Markdown, { children: USAGE_GUIDANCE }), _jsx(Heading, { children: "Stories" }), _jsx(Stories, { title: "" }), _jsx(Markdown, { children: RELATED_COMPONENTS })] }));
|
|
133
|
+
}
|
|
134
|
+
// ---------------------------------------------------------------------------
|
|
135
|
+
// Meta
|
|
136
|
+
// ---------------------------------------------------------------------------
|
|
137
|
+
const meta = {
|
|
138
|
+
title: 'Components/TreeRow',
|
|
139
|
+
component: TreeRow,
|
|
140
|
+
tags: ['autodocs'],
|
|
141
|
+
parameters: {
|
|
142
|
+
docs: {
|
|
143
|
+
page: TreeRowDocsPage,
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
argTypes: {
|
|
147
|
+
items: {
|
|
148
|
+
description: 'Data-driven mode: an array of `TreeRowItem` objects to render. Items with a `children` array become collapsible sections; items without become leaf rows. **Mutually exclusive with `children`.**',
|
|
149
|
+
control: false,
|
|
150
|
+
table: {
|
|
151
|
+
type: { summary: 'TreeRowItem[]' },
|
|
152
|
+
defaultValue: { summary: 'undefined' },
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
children: {
|
|
156
|
+
description: 'Compound mode: pass `TreeRow.Section` and `Row` elements directly as JSX children. **Mutually exclusive with `items`.**',
|
|
157
|
+
control: false,
|
|
158
|
+
table: {
|
|
159
|
+
type: { summary: 'ReactNode' },
|
|
160
|
+
defaultValue: { summary: 'undefined' },
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
defaultValue: {
|
|
164
|
+
description: 'Array of section `id` strings that should be expanded on initial render. Sections without an explicit `id` prop receive a non-deterministic auto-generated ID — always set `id` on any section you want to pre-open.',
|
|
165
|
+
control: false,
|
|
166
|
+
table: {
|
|
167
|
+
type: { summary: 'string[]' },
|
|
168
|
+
defaultValue: { summary: 'undefined' },
|
|
169
|
+
},
|
|
170
|
+
},
|
|
171
|
+
className: {
|
|
172
|
+
description: 'Additional CSS class(es) applied to the root `Accordion.Root` wrapper element.',
|
|
173
|
+
control: 'text',
|
|
174
|
+
table: {
|
|
175
|
+
type: { summary: 'string' },
|
|
176
|
+
defaultValue: { summary: 'undefined' },
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
},
|
|
180
|
+
};
|
|
181
|
+
export default meta;
|
|
182
|
+
// ---------------------------------------------------------------------------
|
|
183
|
+
// Default — data-driven, two top-level sections, all collapsed
|
|
184
|
+
// ---------------------------------------------------------------------------
|
|
185
|
+
export const Default = {
|
|
186
|
+
args: {
|
|
187
|
+
items: [
|
|
188
|
+
{
|
|
189
|
+
id: 'english',
|
|
190
|
+
label: 'English Literature',
|
|
191
|
+
children: [
|
|
192
|
+
{ label: 'Teacher', value: 'Ms Swan' },
|
|
193
|
+
{ label: 'Room', value: 'A12' },
|
|
194
|
+
{ label: 'Periods per week', value: '5' },
|
|
195
|
+
],
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
id: 'biology',
|
|
199
|
+
label: 'Biology',
|
|
200
|
+
children: [
|
|
201
|
+
{ label: 'Teacher', value: 'Dr Cullen' },
|
|
202
|
+
{ label: 'Room', value: 'Lab 3' },
|
|
203
|
+
{ label: 'Periods per week', value: '4' },
|
|
204
|
+
],
|
|
205
|
+
},
|
|
206
|
+
],
|
|
207
|
+
},
|
|
208
|
+
render: args => _jsx(TreeRow, { ...args }),
|
|
209
|
+
};
|
|
210
|
+
// ---------------------------------------------------------------------------
|
|
211
|
+
// PreOpenedSections — defaultValue with explicit IDs
|
|
212
|
+
// ---------------------------------------------------------------------------
|
|
213
|
+
export const PreOpenedSections = {
|
|
214
|
+
parameters: {
|
|
215
|
+
docs: {
|
|
216
|
+
description: {
|
|
217
|
+
story: 'Sections can be expanded on initial render by passing their `id` values in `defaultValue`. Both a top-level section ("Forks High School") and a nested section ("Year 11") are pre-opened here. Note that every section involved — at any depth — must have an explicit `id` prop for this to work reliably.',
|
|
218
|
+
},
|
|
219
|
+
source: {
|
|
220
|
+
code: `
|
|
221
|
+
import { TreeRow } from '@arbor-education/design-system.components';
|
|
222
|
+
|
|
223
|
+
function TreeRowPreOpenedSectionsExample() {
|
|
224
|
+
return (
|
|
225
|
+
<TreeRow
|
|
226
|
+
defaultValue={['forks-hs', 'year-11']}
|
|
227
|
+
items={[
|
|
228
|
+
{
|
|
229
|
+
id: 'forks-hs',
|
|
230
|
+
label: 'Forks High School',
|
|
231
|
+
children: [
|
|
232
|
+
{
|
|
233
|
+
id: 'year-10',
|
|
234
|
+
label: 'Year 10',
|
|
235
|
+
children: [
|
|
236
|
+
{ label: 'Form tutor', value: 'Mr Black' },
|
|
237
|
+
{ label: 'Pupils on roll', value: '28' },
|
|
238
|
+
],
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
id: 'year-11',
|
|
242
|
+
label: 'Year 11',
|
|
243
|
+
children: [
|
|
244
|
+
{ label: 'Form tutor', value: 'Ms Cullen' },
|
|
245
|
+
{ label: 'Pupils on roll', value: '31' },
|
|
246
|
+
],
|
|
247
|
+
},
|
|
248
|
+
],
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
id: 'la-push-academy',
|
|
252
|
+
label: 'La Push Academy',
|
|
253
|
+
children: [
|
|
254
|
+
{ label: 'Headteacher', value: 'Mr Ateara' },
|
|
255
|
+
{ label: 'Pupils on roll', value: '142' },
|
|
256
|
+
],
|
|
257
|
+
},
|
|
258
|
+
]}
|
|
259
|
+
/>
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
export default TreeRowPreOpenedSectionsExample;
|
|
264
|
+
`.trim(),
|
|
265
|
+
},
|
|
266
|
+
},
|
|
267
|
+
},
|
|
268
|
+
render: () => (_jsx(TreeRow, { defaultValue: ['forks-hs', 'year-11'], items: [
|
|
269
|
+
{
|
|
270
|
+
id: 'forks-hs',
|
|
271
|
+
label: 'Forks High School',
|
|
272
|
+
children: [
|
|
273
|
+
{
|
|
274
|
+
id: 'year-10',
|
|
275
|
+
label: 'Year 10',
|
|
276
|
+
children: [
|
|
277
|
+
{ label: 'Form tutor', value: 'Mr Black' },
|
|
278
|
+
{ label: 'Pupils on roll', value: '28' },
|
|
279
|
+
],
|
|
280
|
+
},
|
|
281
|
+
{
|
|
282
|
+
id: 'year-11',
|
|
283
|
+
label: 'Year 11',
|
|
284
|
+
children: [
|
|
285
|
+
{ label: 'Form tutor', value: 'Ms Cullen' },
|
|
286
|
+
{ label: 'Pupils on roll', value: '31' },
|
|
287
|
+
],
|
|
288
|
+
},
|
|
289
|
+
],
|
|
290
|
+
},
|
|
291
|
+
{
|
|
292
|
+
id: 'la-push-academy',
|
|
293
|
+
label: 'La Push Academy',
|
|
294
|
+
children: [
|
|
295
|
+
{ label: 'Headteacher', value: 'Mr Ateara' },
|
|
296
|
+
{ label: 'Pupils on roll', value: '142' },
|
|
297
|
+
],
|
|
298
|
+
},
|
|
299
|
+
] })),
|
|
300
|
+
};
|
|
301
|
+
// ---------------------------------------------------------------------------
|
|
302
|
+
// DeepNesting — 4 levels, shows indentation progression
|
|
303
|
+
// ---------------------------------------------------------------------------
|
|
304
|
+
export const DeepNesting = {
|
|
305
|
+
parameters: {
|
|
306
|
+
docs: {
|
|
307
|
+
description: {
|
|
308
|
+
story: 'Nesting can go as deep as the data requires. Each additional level receives `calc(var(--spacing-xxlarge) * depth)` of left padding automatically via the internal `DepthContext`. Three to four levels is the practical maximum before readability suffers — restructure the hierarchy if you need more.',
|
|
309
|
+
},
|
|
310
|
+
source: {
|
|
311
|
+
code: `
|
|
312
|
+
import { TreeRow } from '@arbor-education/design-system.components';
|
|
313
|
+
|
|
314
|
+
function TreeRowDeepNestingExample() {
|
|
315
|
+
return (
|
|
316
|
+
<TreeRow
|
|
317
|
+
defaultValue={['assessment', 'science', 'biology', 'cells']}
|
|
318
|
+
items={[
|
|
319
|
+
{
|
|
320
|
+
id: 'assessment',
|
|
321
|
+
label: 'Assessment — Bella Swan',
|
|
322
|
+
children: [
|
|
323
|
+
{
|
|
324
|
+
id: 'science',
|
|
325
|
+
label: 'Science',
|
|
326
|
+
children: [
|
|
327
|
+
{
|
|
328
|
+
id: 'biology',
|
|
329
|
+
label: 'Biology',
|
|
330
|
+
children: [
|
|
331
|
+
{
|
|
332
|
+
id: 'cells',
|
|
333
|
+
label: 'Cell Biology',
|
|
334
|
+
children: [
|
|
335
|
+
{ label: 'Grade', value: 'A*' },
|
|
336
|
+
{ label: 'Score', value: '98%' },
|
|
337
|
+
{ label: 'Assessed by', value: 'Dr Cullen' },
|
|
338
|
+
],
|
|
339
|
+
},
|
|
340
|
+
{
|
|
341
|
+
id: 'genetics',
|
|
342
|
+
label: 'Genetics',
|
|
343
|
+
children: [
|
|
344
|
+
{ label: 'Grade', value: 'A' },
|
|
345
|
+
{ label: 'Score', value: '91%' },
|
|
346
|
+
],
|
|
347
|
+
},
|
|
348
|
+
],
|
|
349
|
+
},
|
|
350
|
+
{
|
|
351
|
+
id: 'chemistry',
|
|
352
|
+
label: 'Chemistry',
|
|
353
|
+
children: [
|
|
354
|
+
{ label: 'Grade', value: 'B+' },
|
|
355
|
+
{ label: 'Score', value: '79%' },
|
|
356
|
+
],
|
|
357
|
+
},
|
|
358
|
+
],
|
|
359
|
+
},
|
|
360
|
+
],
|
|
361
|
+
},
|
|
362
|
+
]}
|
|
363
|
+
/>
|
|
364
|
+
);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
export default TreeRowDeepNestingExample;
|
|
368
|
+
`.trim(),
|
|
369
|
+
},
|
|
370
|
+
},
|
|
371
|
+
},
|
|
372
|
+
render: () => (_jsx(TreeRow, { defaultValue: ['assessment', 'science', 'biology', 'cells'], items: [
|
|
373
|
+
{
|
|
374
|
+
id: 'assessment',
|
|
375
|
+
label: 'Assessment — Bella Swan',
|
|
376
|
+
children: [
|
|
377
|
+
{
|
|
378
|
+
id: 'science',
|
|
379
|
+
label: 'Science',
|
|
380
|
+
children: [
|
|
381
|
+
{
|
|
382
|
+
id: 'biology',
|
|
383
|
+
label: 'Biology',
|
|
384
|
+
children: [
|
|
385
|
+
{
|
|
386
|
+
id: 'cells',
|
|
387
|
+
label: 'Cell Biology',
|
|
388
|
+
children: [
|
|
389
|
+
{ label: 'Grade', value: 'A*' },
|
|
390
|
+
{ label: 'Score', value: '98%' },
|
|
391
|
+
{ label: 'Assessed by', value: 'Dr Cullen' },
|
|
392
|
+
],
|
|
393
|
+
},
|
|
394
|
+
{
|
|
395
|
+
id: 'genetics',
|
|
396
|
+
label: 'Genetics',
|
|
397
|
+
children: [
|
|
398
|
+
{ label: 'Grade', value: 'A' },
|
|
399
|
+
{ label: 'Score', value: '91%' },
|
|
400
|
+
],
|
|
401
|
+
},
|
|
402
|
+
],
|
|
403
|
+
},
|
|
404
|
+
{
|
|
405
|
+
id: 'chemistry',
|
|
406
|
+
label: 'Chemistry',
|
|
407
|
+
children: [
|
|
408
|
+
{ label: 'Grade', value: 'B+' },
|
|
409
|
+
{ label: 'Score', value: '79%' },
|
|
410
|
+
],
|
|
411
|
+
},
|
|
412
|
+
],
|
|
413
|
+
},
|
|
414
|
+
],
|
|
415
|
+
},
|
|
416
|
+
] })),
|
|
417
|
+
};
|
|
418
|
+
// ---------------------------------------------------------------------------
|
|
419
|
+
// ClickableSections — compound API, all sections have onClick
|
|
420
|
+
// ---------------------------------------------------------------------------
|
|
421
|
+
export const ClickableSections = {
|
|
422
|
+
parameters: {
|
|
423
|
+
docs: {
|
|
424
|
+
description: {
|
|
425
|
+
story: 'When a section has an `onClick` handler, a navigation caret appears on the trailing edge. Clicking the row area fires `onClick`; clicking the label or chevron trigger only toggles expand/collapse. Both interactions are fully independent.',
|
|
426
|
+
},
|
|
427
|
+
source: {
|
|
428
|
+
code: `
|
|
429
|
+
import { TreeRow } from '@arbor-education/design-system.components';
|
|
430
|
+
|
|
431
|
+
function TreeRowClickableSectionsExample() {
|
|
432
|
+
return (
|
|
433
|
+
<TreeRow
|
|
434
|
+
items={[
|
|
435
|
+
{
|
|
436
|
+
id: 'edward',
|
|
437
|
+
label: 'Edward Cullen',
|
|
438
|
+
onClick: () => console.log('Navigate to Edward Cullen'),
|
|
439
|
+
children: [
|
|
440
|
+
{ label: 'Year group', value: 'Year 12' },
|
|
441
|
+
{ label: 'Form', value: '12C' },
|
|
442
|
+
{ label: 'Attendance', value: '100%' },
|
|
443
|
+
],
|
|
444
|
+
},
|
|
445
|
+
{
|
|
446
|
+
id: 'bella',
|
|
447
|
+
label: 'Bella Swan',
|
|
448
|
+
onClick: () => console.log('Navigate to Bella Swan'),
|
|
449
|
+
children: [
|
|
450
|
+
{ label: 'Year group', value: 'Year 12' },
|
|
451
|
+
{ label: 'Form', value: '12B' },
|
|
452
|
+
{ label: 'Attendance', value: '97.4%' },
|
|
453
|
+
],
|
|
454
|
+
},
|
|
455
|
+
{
|
|
456
|
+
id: 'jacob',
|
|
457
|
+
label: 'Jacob Black',
|
|
458
|
+
onClick: () => console.log('Navigate to Jacob Black'),
|
|
459
|
+
children: [
|
|
460
|
+
{ label: 'Year group', value: 'Year 11' },
|
|
461
|
+
{ label: 'Form', value: '11J' },
|
|
462
|
+
{ label: 'Attendance', value: '88.2%' },
|
|
463
|
+
],
|
|
464
|
+
},
|
|
465
|
+
]}
|
|
466
|
+
/>
|
|
467
|
+
);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
export default TreeRowClickableSectionsExample;
|
|
471
|
+
`.trim(),
|
|
472
|
+
},
|
|
473
|
+
},
|
|
474
|
+
},
|
|
475
|
+
render: () => (_jsx(TreeRow, { items: [
|
|
476
|
+
{
|
|
477
|
+
id: 'edward',
|
|
478
|
+
label: 'Edward Cullen',
|
|
479
|
+
onClick: () => console.log('Navigate to Edward Cullen'),
|
|
480
|
+
children: [
|
|
481
|
+
{ label: 'Year group', value: 'Year 12' },
|
|
482
|
+
{ label: 'Form', value: '12C' },
|
|
483
|
+
{ label: 'Attendance', value: '100%' },
|
|
484
|
+
],
|
|
485
|
+
},
|
|
486
|
+
{
|
|
487
|
+
id: 'bella',
|
|
488
|
+
label: 'Bella Swan',
|
|
489
|
+
onClick: () => console.log('Navigate to Bella Swan'),
|
|
490
|
+
children: [
|
|
491
|
+
{ label: 'Year group', value: 'Year 12' },
|
|
492
|
+
{ label: 'Form', value: '12B' },
|
|
493
|
+
{ label: 'Attendance', value: '97.4%' },
|
|
494
|
+
],
|
|
495
|
+
},
|
|
496
|
+
{
|
|
497
|
+
id: 'jacob',
|
|
498
|
+
label: 'Jacob Black',
|
|
499
|
+
onClick: () => console.log('Navigate to Jacob Black'),
|
|
500
|
+
children: [
|
|
501
|
+
{ label: 'Year group', value: 'Year 11' },
|
|
502
|
+
{ label: 'Form', value: '11J' },
|
|
503
|
+
{ label: 'Attendance', value: '88.2%' },
|
|
504
|
+
],
|
|
505
|
+
},
|
|
506
|
+
] })),
|
|
507
|
+
};
|
|
508
|
+
// ---------------------------------------------------------------------------
|
|
509
|
+
// ClickableRows — data-driven leaf rows with onClick
|
|
510
|
+
// ---------------------------------------------------------------------------
|
|
511
|
+
export const ClickableRows = {
|
|
512
|
+
parameters: {
|
|
513
|
+
docs: {
|
|
514
|
+
description: {
|
|
515
|
+
story: 'Leaf rows (items without `children`) can also be clickable. Provide an `onClick` on the `TreeRowItem` and it is forwarded to the underlying `Row` component. Useful for navigating to a pupil record, opening a document, or triggering an action inline.',
|
|
516
|
+
},
|
|
517
|
+
source: {
|
|
518
|
+
code: `
|
|
519
|
+
import { TreeRow } from '@arbor-education/design-system.components';
|
|
520
|
+
|
|
521
|
+
function TreeRowClickableRowsExample() {
|
|
522
|
+
return (
|
|
523
|
+
<TreeRow
|
|
524
|
+
defaultValue={['contacts']}
|
|
525
|
+
items={[
|
|
526
|
+
{
|
|
527
|
+
id: 'contacts',
|
|
528
|
+
label: 'Emergency Contacts — Bella Swan',
|
|
529
|
+
children: [
|
|
530
|
+
{
|
|
531
|
+
label: 'Charlie Swan',
|
|
532
|
+
value: 'Father',
|
|
533
|
+
note: '07700 900123',
|
|
534
|
+
onClick: () => console.log('Open Charlie Swan'),
|
|
535
|
+
},
|
|
536
|
+
{
|
|
537
|
+
label: 'Renée Dwyer',
|
|
538
|
+
value: 'Mother',
|
|
539
|
+
note: '07700 900456',
|
|
540
|
+
onClick: () => console.log('Open Renée Dwyer'),
|
|
541
|
+
},
|
|
542
|
+
{
|
|
543
|
+
label: 'Jacob Black',
|
|
544
|
+
value: 'Family friend',
|
|
545
|
+
note: '07700 900789',
|
|
546
|
+
onClick: () => console.log('Open Jacob Black'),
|
|
547
|
+
},
|
|
548
|
+
],
|
|
549
|
+
},
|
|
550
|
+
]}
|
|
551
|
+
/>
|
|
552
|
+
);
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
export default TreeRowClickableRowsExample;
|
|
556
|
+
`.trim(),
|
|
557
|
+
},
|
|
558
|
+
},
|
|
559
|
+
},
|
|
560
|
+
render: () => (_jsx(TreeRow, { defaultValue: ['contacts'], items: [
|
|
561
|
+
{
|
|
562
|
+
id: 'contacts',
|
|
563
|
+
label: 'Emergency Contacts — Bella Swan',
|
|
564
|
+
children: [
|
|
565
|
+
{
|
|
566
|
+
label: 'Charlie Swan',
|
|
567
|
+
value: 'Father',
|
|
568
|
+
note: '07700 900123',
|
|
569
|
+
onClick: () => console.log('Open Charlie Swan'),
|
|
570
|
+
},
|
|
571
|
+
{
|
|
572
|
+
label: 'Renée Dwyer',
|
|
573
|
+
value: 'Mother',
|
|
574
|
+
note: '07700 900456',
|
|
575
|
+
onClick: () => console.log('Open Renée Dwyer'),
|
|
576
|
+
},
|
|
577
|
+
{
|
|
578
|
+
label: 'Jacob Black',
|
|
579
|
+
value: 'Family friend',
|
|
580
|
+
note: '07700 900789',
|
|
581
|
+
onClick: () => console.log('Open Jacob Black'),
|
|
582
|
+
},
|
|
583
|
+
],
|
|
584
|
+
},
|
|
585
|
+
] })),
|
|
586
|
+
};
|
|
587
|
+
// ---------------------------------------------------------------------------
|
|
588
|
+
// MixedClickability — some sections clickable, some not
|
|
589
|
+
// ---------------------------------------------------------------------------
|
|
590
|
+
export const MixedClickability = {
|
|
591
|
+
parameters: {
|
|
592
|
+
docs: {
|
|
593
|
+
description: {
|
|
594
|
+
story: 'The navigation caret only appears on sections that have an `onClick` prop. Sections without one look and behave like plain collapsible groups — a common pattern where some summary rows navigate to a detail view while others are purely informational.',
|
|
595
|
+
},
|
|
596
|
+
source: {
|
|
597
|
+
code: `
|
|
598
|
+
import { TreeRow } from '@arbor-education/design-system.components';
|
|
599
|
+
|
|
600
|
+
function TreeRowMixedClickabilityExample() {
|
|
601
|
+
return (
|
|
602
|
+
<TreeRow
|
|
603
|
+
defaultValue={['cullen-family', 'quileute-tribe']}
|
|
604
|
+
items={[
|
|
605
|
+
{
|
|
606
|
+
id: 'cullen-family',
|
|
607
|
+
label: 'Cullen Family',
|
|
608
|
+
onClick: () => console.log('Navigate to Cullen Family group'),
|
|
609
|
+
children: [
|
|
610
|
+
{ label: 'Edward Cullen', value: 'Year 12' },
|
|
611
|
+
{ label: 'Alice Cullen', value: 'Year 12' },
|
|
612
|
+
{ label: 'Rosalie Hale', value: 'Year 12' },
|
|
613
|
+
{ label: 'Emmett Cullen', value: 'Year 12' },
|
|
614
|
+
],
|
|
615
|
+
},
|
|
616
|
+
{
|
|
617
|
+
id: 'school-info',
|
|
618
|
+
label: 'School Information',
|
|
619
|
+
children: [
|
|
620
|
+
{ label: 'School', value: 'Forks High School' },
|
|
621
|
+
{ label: 'LA', value: 'Clallam County' },
|
|
622
|
+
{ label: 'Ofsted rating', value: 'Outstanding' },
|
|
623
|
+
],
|
|
624
|
+
},
|
|
625
|
+
{
|
|
626
|
+
id: 'quileute-tribe',
|
|
627
|
+
label: 'Quileute Tribe (La Push)',
|
|
628
|
+
onClick: () => console.log('Navigate to Quileute Tribe group'),
|
|
629
|
+
children: [
|
|
630
|
+
{ label: 'Jacob Black', value: 'Year 11' },
|
|
631
|
+
{ label: 'Embry Call', value: 'Year 11' },
|
|
632
|
+
{ label: 'Quil Ateara', value: 'Year 11' },
|
|
633
|
+
],
|
|
634
|
+
},
|
|
635
|
+
]}
|
|
636
|
+
/>
|
|
637
|
+
);
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
export default TreeRowMixedClickabilityExample;
|
|
641
|
+
`.trim(),
|
|
642
|
+
},
|
|
643
|
+
},
|
|
644
|
+
},
|
|
645
|
+
render: () => (_jsx(TreeRow, { defaultValue: ['cullen-family', 'quileute-tribe'], items: [
|
|
646
|
+
{
|
|
647
|
+
id: 'cullen-family',
|
|
648
|
+
label: 'Cullen Family',
|
|
649
|
+
onClick: () => console.log('Navigate to Cullen Family group'),
|
|
650
|
+
children: [
|
|
651
|
+
{ label: 'Edward Cullen', value: 'Year 12' },
|
|
652
|
+
{ label: 'Alice Cullen', value: 'Year 12' },
|
|
653
|
+
{ label: 'Rosalie Hale', value: 'Year 12' },
|
|
654
|
+
{ label: 'Emmett Cullen', value: 'Year 12' },
|
|
655
|
+
],
|
|
656
|
+
},
|
|
657
|
+
{
|
|
658
|
+
id: 'school-info',
|
|
659
|
+
label: 'School Information',
|
|
660
|
+
children: [
|
|
661
|
+
{ label: 'School', value: 'Forks High School' },
|
|
662
|
+
{ label: 'LA', value: 'Clallam County' },
|
|
663
|
+
{ label: 'Ofsted rating', value: 'Outstanding' },
|
|
664
|
+
],
|
|
665
|
+
},
|
|
666
|
+
{
|
|
667
|
+
id: 'quileute-tribe',
|
|
668
|
+
label: 'Quileute Tribe (La Push)',
|
|
669
|
+
onClick: () => console.log('Navigate to Quileute Tribe group'),
|
|
670
|
+
children: [
|
|
671
|
+
{ label: 'Jacob Black', value: 'Year 11' },
|
|
672
|
+
{ label: 'Embry Call', value: 'Year 11' },
|
|
673
|
+
{ label: 'Quil Ateara', value: 'Year 11' },
|
|
674
|
+
],
|
|
675
|
+
},
|
|
676
|
+
] })),
|
|
677
|
+
};
|
|
678
|
+
// ---------------------------------------------------------------------------
|
|
679
|
+
// WithClickFeedback — interactive useState story
|
|
680
|
+
// ---------------------------------------------------------------------------
|
|
681
|
+
function WithClickFeedbackExample() {
|
|
682
|
+
const [lastClicked, setLastClicked] = useState(null);
|
|
683
|
+
return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 'var(--spacing-small)' }, children: [_jsx(TreeRow, { defaultValue: ['timetable'], items: [
|
|
684
|
+
{
|
|
685
|
+
id: 'timetable',
|
|
686
|
+
label: 'Timetable — Jacob Black',
|
|
687
|
+
onClick: () => setLastClicked('Timetable — Jacob Black'),
|
|
688
|
+
children: [
|
|
689
|
+
{
|
|
690
|
+
id: 'monday',
|
|
691
|
+
label: 'Monday',
|
|
692
|
+
onClick: () => setLastClicked('Monday'),
|
|
693
|
+
children: [
|
|
694
|
+
{ label: 'Period 1', value: 'English', note: 'Room A12 · Ms Swan', onClick: () => setLastClicked('Period 1 — English') },
|
|
695
|
+
{ label: 'Period 2', value: 'Biology', note: 'Lab 3 · Dr Cullen', onClick: () => setLastClicked('Period 2 — Biology') },
|
|
696
|
+
{ label: 'Period 3', value: 'PE', note: 'Sports Hall · Mr Black', onClick: () => setLastClicked('Period 3 — PE') },
|
|
697
|
+
],
|
|
698
|
+
},
|
|
699
|
+
{
|
|
700
|
+
id: 'tuesday',
|
|
701
|
+
label: 'Tuesday',
|
|
702
|
+
onClick: () => setLastClicked('Tuesday'),
|
|
703
|
+
children: [
|
|
704
|
+
{ label: 'Period 1', value: 'Maths', note: 'Room B4 · Mrs Clearwater', onClick: () => setLastClicked('Period 1 — Maths') },
|
|
705
|
+
{ label: 'Period 2', value: 'Art', note: 'Studio · Ms Hale', onClick: () => setLastClicked('Period 2 — Art') },
|
|
706
|
+
],
|
|
707
|
+
},
|
|
708
|
+
],
|
|
709
|
+
},
|
|
710
|
+
] }), _jsx("p", { className: "ds-text", style: { color: 'var(--color-grey-600)' }, children: lastClicked ? `Last clicked: "${lastClicked}"` : 'Click any section or row above.' })] }));
|
|
711
|
+
}
|
|
712
|
+
export const WithClickFeedback = {
|
|
713
|
+
parameters: {
|
|
714
|
+
docs: {
|
|
715
|
+
description: {
|
|
716
|
+
story: 'A fully interactive example demonstrating how both section `onClick` and row `onClick` work together in the same tree. Each click is independent — expanding a section does not fire `onClick`, and clicking a row does not toggle any section. Open the "Monday" or "Tuesday" sections, then click any row or section header to see the feedback update.',
|
|
717
|
+
},
|
|
718
|
+
source: {
|
|
719
|
+
code: `
|
|
720
|
+
import { useState } from 'react';
|
|
721
|
+
import { TreeRow } from '@arbor-education/design-system.components';
|
|
722
|
+
|
|
723
|
+
function TreeRowWithClickFeedbackExample() {
|
|
724
|
+
const [lastClicked, setLastClicked] = useState<string | null>(null);
|
|
725
|
+
|
|
726
|
+
return (
|
|
727
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: 'var(--spacing-small)' }}>
|
|
728
|
+
<TreeRow
|
|
729
|
+
defaultValue={['timetable']}
|
|
730
|
+
items={[
|
|
731
|
+
{
|
|
732
|
+
id: 'timetable',
|
|
733
|
+
label: 'Timetable — Jacob Black',
|
|
734
|
+
onClick: () => setLastClicked('Timetable — Jacob Black'),
|
|
735
|
+
children: [
|
|
736
|
+
{
|
|
737
|
+
id: 'monday',
|
|
738
|
+
label: 'Monday',
|
|
739
|
+
onClick: () => setLastClicked('Monday'),
|
|
740
|
+
children: [
|
|
741
|
+
{ label: 'Period 1', value: 'English', note: 'Room A12 · Ms Swan', onClick: () => setLastClicked('Period 1 — English') },
|
|
742
|
+
{ label: 'Period 2', value: 'Biology', note: 'Lab 3 · Dr Cullen', onClick: () => setLastClicked('Period 2 — Biology') },
|
|
743
|
+
{ label: 'Period 3', value: 'PE', note: 'Sports Hall · Mr Black', onClick: () => setLastClicked('Period 3 — PE') },
|
|
744
|
+
],
|
|
745
|
+
},
|
|
746
|
+
{
|
|
747
|
+
id: 'tuesday',
|
|
748
|
+
label: 'Tuesday',
|
|
749
|
+
onClick: () => setLastClicked('Tuesday'),
|
|
750
|
+
children: [
|
|
751
|
+
{ label: 'Period 1', value: 'Maths', note: 'Room B4 · Mrs Clearwater', onClick: () => setLastClicked('Period 1 — Maths') },
|
|
752
|
+
{ label: 'Period 2', value: 'Art', note: 'Studio · Ms Hale', onClick: () => setLastClicked('Period 2 — Art') },
|
|
753
|
+
],
|
|
754
|
+
},
|
|
755
|
+
],
|
|
756
|
+
},
|
|
757
|
+
]}
|
|
758
|
+
/>
|
|
759
|
+
|
|
760
|
+
<p className="ds-text" style={{ color: 'var(--color-grey-600)' }}>
|
|
761
|
+
{lastClicked ? \`Last clicked: "\${lastClicked}"\` : 'Click any section or row above.'}
|
|
762
|
+
</p>
|
|
763
|
+
</div>
|
|
764
|
+
);
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
export default TreeRowWithClickFeedbackExample;
|
|
768
|
+
`.trim(),
|
|
769
|
+
},
|
|
770
|
+
},
|
|
771
|
+
},
|
|
772
|
+
render: () => _jsx(WithClickFeedbackExample, {}),
|
|
773
|
+
};
|
|
774
|
+
//# sourceMappingURL=TreeRow.stories.js.map
|