@arbor-education/design-system.components 0.10.0 → 0.11.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.
Files changed (69) hide show
  1. package/.claude/agent-memory/blanche-designspert/MEMORY.md +64 -0
  2. package/.claude/agent-memory/blanche-designspert/token-review-patterns.md +29 -0
  3. package/.claude/agent-memory/dorothy-fact-checker/MEMORY.md +129 -0
  4. package/.claude/agent-memory/rose-storybookspert/MEMORY.md +29 -0
  5. package/.claude/agent-memory/rose-storybookspert/patterns.md +132 -0
  6. package/.claude/agent-memory/sophia-componentspert/MEMORY.md +14 -0
  7. package/.claude/agent-memory/sophia-componentspert/components.md +367 -0
  8. package/.claude/agents/blanche-designspert.md +150 -0
  9. package/.claude/agents/dorothy-fact-checker.md +145 -0
  10. package/.claude/agents/rose-storybookspert.md +148 -0
  11. package/.claude/agents/sophia-componentspert.md +133 -0
  12. package/.claude/component-library.md +1107 -0
  13. package/.claude/design-assessment-daily-attendance-2026-04-10.md +566 -0
  14. package/.claude/figma-assessment-7154-58899.md +404 -0
  15. package/.claude/figma-assessment-UKQfcxnT4rlHHNuiumt4o1-11086-97537.md +392 -0
  16. package/.claude/figma-assessment-UKQfcxnT4rlHHNuiumt4o1-551-41974.md +474 -0
  17. package/.claude/figma-assessment-UKQfcxnT4rlHHNuiumt4o1-551-43094.md +462 -0
  18. package/.claude/figma-assessment-fcFK4CGzkz2fVyY3koX8ZE-7154-59061.md +440 -0
  19. package/.claude/migration-report-custom-report-writer-2026-02-19.md +591 -0
  20. package/.claude/skills/analyze-design/README.md +295 -0
  21. package/.claude/skills/analyze-design/SKILL.md +741 -0
  22. package/.claude/skills/create-page/README.md +246 -0
  23. package/.claude/skills/create-page/SKILL.md +634 -0
  24. package/.claude/skills/create-page/design-analysis-template.md +333 -0
  25. package/.claude/skills/create-page/page-template.scss +118 -0
  26. package/.claude/skills/create-page/page-template.tsx +230 -0
  27. package/.claude/skills/map-legacy/README.md +87 -0
  28. package/.claude/skills/map-legacy/SKILL.md +465 -0
  29. package/.claude/skills/migrate-page/README.md +125 -0
  30. package/.claude/skills/migrate-page/SKILL.md +374 -0
  31. package/.github/CODEOWNERS +1 -0
  32. package/.github/pull_request_template.md +39 -0
  33. package/CHANGELOG.md +6 -0
  34. package/CLAUDE.md +31 -0
  35. package/CONTRIBUTING.md +191 -0
  36. package/README.md +110 -20
  37. package/dist/components/table/DSDefaultColDef.js +2 -2
  38. package/dist/components/table/DSDefaultColDef.js.map +1 -1
  39. package/dist/components/table/Table.d.ts.map +1 -1
  40. package/dist/components/table/Table.js +2 -0
  41. package/dist/components/table/Table.js.map +1 -1
  42. package/dist/components/table/Table.stories.d.ts +1 -0
  43. package/dist/components/table/Table.stories.d.ts.map +1 -1
  44. package/dist/components/table/Table.stories.js +95 -3
  45. package/dist/components/table/Table.stories.js.map +1 -1
  46. package/dist/components/table/Table.test.js +106 -5
  47. package/dist/components/table/Table.test.js.map +1 -1
  48. package/dist/components/table/cellRenderers/CheckboxCellRenderer.d.ts +3 -0
  49. package/dist/components/table/cellRenderers/CheckboxCellRenderer.d.ts.map +1 -0
  50. package/dist/components/table/cellRenderers/CheckboxCellRenderer.js +12 -0
  51. package/dist/components/table/cellRenderers/CheckboxCellRenderer.js.map +1 -0
  52. package/dist/components/table/cellRenderers/CheckboxCellRenderer.test.d.ts +2 -0
  53. package/dist/components/table/cellRenderers/CheckboxCellRenderer.test.d.ts.map +1 -0
  54. package/dist/components/table/cellRenderers/CheckboxCellRenderer.test.js +65 -0
  55. package/dist/components/table/cellRenderers/CheckboxCellRenderer.test.js.map +1 -0
  56. package/dist/index.css +1 -1
  57. package/dist/index.d.ts +1 -0
  58. package/dist/index.d.ts.map +1 -1
  59. package/dist/index.js +1 -0
  60. package/dist/index.js.map +1 -1
  61. package/package.json +1 -1
  62. package/src/components/table/DSDefaultColDef.ts +2 -2
  63. package/src/components/table/Table.stories.tsx +99 -3
  64. package/src/components/table/Table.test.tsx +131 -5
  65. package/src/components/table/Table.tsx +2 -0
  66. package/src/components/table/cellRenderers/CheckboxCellRenderer.test.tsx +74 -0
  67. package/src/components/table/cellRenderers/CheckboxCellRenderer.tsx +28 -0
  68. package/src/components/table/table.scss +1 -1
  69. package/src/index.ts +1 -0
@@ -0,0 +1,1107 @@
1
+ # Arbor Design System — Component Library Manifest
2
+
3
+ > Auto-generated reference for AI agents (Sophia, analyze-design, etc.)
4
+ > Source: `src/components/` · Public API: `src/index.ts`
5
+ > Last updated: 2026-04-17
6
+
7
+ ---
8
+
9
+ ## Design Tokens Overview
10
+
11
+ File: `src/tokens.scss` — Auto-generated CSS custom properties on `:root`.
12
+
13
+ | Category | Key tokens |
14
+ |---|---|
15
+ | **Spacing** | `--spacing-xsmall` (4px), `--spacing-small` (8px), `--spacing-medium` (12px), `--spacing-large` (16px), `--spacing-xlarge` (24px), `--spacing-xxlarge` (32px), `--spacing-xxxlarge` (64px) |
16
+ | **Border radius** | `--border-radius-xsmall` (4px), `--border-radius-small` (8px), `--border-radius-large` (16px), `--border-radius-round` (99px) |
17
+ | **Font sizes** | `--font-size-1-11` → `--font-size-8-40` (11px–40px) |
18
+ | **Font families** | `--font-family-display: Grenette`, `--font-family-standard: Inter` |
19
+ | **Font weights** | `--font-weight-regular` (400), `--font-weight-medium` (500), `--font-weight-semi-bold` (600), `--font-weight-bold` (700) |
20
+ | **Brand colors** | `--color-brand-050` → `--color-brand-900` (greens; brand green = `#3cad51`) |
21
+ | **Semantic colors** | info, caution, success, warning, destructive scales (050–900) |
22
+ | **Grey scale** | `--color-grey-050` → `--color-grey-900` |
23
+ | **Chart colors** | red, blue, teal, green, orange, purple, yellow (1–4 variants each) |
24
+ | **Extended colors** | blue, teal, green, orange, purple, salmon, yellow (050/100/500/800) |
25
+ | **Gradient ramps** | diverging and sequential ramps (050–900) |
26
+ | **Shadow** | `--shadow-small` |
27
+ | **Icon sizes** | `--icon-size-xsmall` (12px), `--icon-size-small` (16px), `--icon-size-medium` (24px) |
28
+ | **Focus** | `--focus-color-focus: var(--color-brand-500)`, `--focus-border: 3px` |
29
+ | **Avatar sizes** | `--avatar-small-size` (1.25rem), `--avatar-medium-size` (2rem), `--avatar-large-size` (3rem), `--avatar-extra-large-size` (6rem) |
30
+ | **Control sizes** | `--size-control-xsmall` → `--size-control-xlarge` (2rem–5rem) |
31
+
32
+ Component-scoped tokens follow: `--{component}-{state}-{property}` (e.g. `--button-primary-default-color-background`, `--tag-neutral-color-text`).
33
+
34
+ ---
35
+
36
+ ## Component Manifest
37
+
38
+ ### 0a. Badge
39
+
40
+ **File:** `src/components/badge/Badge.tsx` · **Public export:** Yes
41
+ **Also exports:** `BadgeColour`, `BadgeProps`, `BadgeSize` types
42
+
43
+ **Use case:** Small coloured label/count badge for categorisation, notification counts, or status indicators.
44
+
45
+ | Prop | Type | Required | Default | Description |
46
+ |---|---|---|---|---|
47
+ | `children` | `ReactNode` | Yes | — | Badge content |
48
+ | `size` | `'sm' \| 'md' \| 'lg'` | No | `'md'` | Visual size |
49
+ | `colour` | `BadgeColour` | No | — | Color variant |
50
+ | `a11yLabel` | `string` | No | — | Accessible label (hides children from SR, shows aria-label) |
51
+ | `className` | `string` | No | — | Additional CSS class |
52
+
53
+ **`BadgeColour` / `DotColour` values:** `'purple'` · `'salmon'` · `'teal'` · `'yellow'` · `'green'` · `'orange'` · `'blue'`
54
+
55
+ ---
56
+
57
+ ### 0b. Dot
58
+
59
+ **File:** `src/components/dot/Dot.tsx` · **Public export:** Yes
60
+ **Also exports:** `DotColour` type
61
+
62
+ **Use case:** Small coloured circle indicator. Used inside `Tag` (optional dot prefix) and standalone for status indicators.
63
+
64
+ | Prop | Type | Required | Default | Description |
65
+ |---|---|---|---|---|
66
+ | `colour` | `DotColour` | Yes | — | Dot colour |
67
+ | `label` | `string` | No | — | Accessible label; if omitted, dot is `aria-hidden` |
68
+
69
+ ---
70
+
71
+ ### 1. Avatar
72
+
73
+ **File:** `src/components/avatar/Avatar.tsx` · **Public export:** Yes
74
+
75
+ **Use case:** Display a user's profile picture, initials, or generic silhouette. Building block for `AvatarGroup` and `UserDropdown`.
76
+
77
+ | Prop | Type | Required | Default | Description |
78
+ |---|---|---|---|---|
79
+ | `size` | `'small' \| 'medium' \| 'large' \| 'extra-large'` | No | `'medium'` | Visual size |
80
+ | `src` | `string` | No | — | Image URL |
81
+ | `alt` | `string` | No | `''` | Alt text / aria-label |
82
+ | `initials` | `string` | No | — | Text initials if no image |
83
+ | `className` | `string` | No | — | Additional CSS class |
84
+
85
+ **Variants:** size × content mode (image → initials → placeholder SVG silhouette, in priority order)
86
+
87
+ **Key features:**
88
+ - Three rendering modes, prioritised: image → initials → placeholder SVG
89
+ - `aria-hidden` on image; `aria-label` on initials/placeholder
90
+ - CSS: `ds-avatar`, `ds-avatar--{size}`
91
+
92
+ ---
93
+
94
+ ### 2. AvatarGroup
95
+
96
+ **File:** `src/components/avatarGroup/AvatarGroup.tsx` · **Public export:** Yes
97
+ **Also exports:** `AvatarGroupItem`, `AvatarGroupListOrder`, `AvatarGroupOverflowCountProps`, `AvatarGroupProps` types
98
+
99
+ **Use case:** Horizontal stack of user avatars with optional overflow count. Used for groups, assignments, and teams.
100
+
101
+ | Prop | Type | Required | Default | Description |
102
+ |---|---|---|---|---|
103
+ | `items` | `readonly AvatarGroupItem[]` | Mutually exclusive with `children` | — | Flat array of avatar data |
104
+ | `children` | `React.ReactNode` | Mutually exclusive with `items` | — | Composable `<Avatar />` children |
105
+ | `size` | `AvatarSize` | No | — | Overrides size of all avatars |
106
+ | `showMaxItems` | `number` | No | — | Max visible avatars before overflow |
107
+ | `listOrder` | `'ascending' \| 'descending'` | No | `'ascending'` | Which avatars shown when truncating |
108
+ | `label` | `string` | No | — | `aria-label` on the `<ul>` |
109
+ | `overflowCountLabel` | `string \| ((count: number) => string)` | No | `'plus N more'` | SR label for overflow badge |
110
+ | `presentAllUpdatesToScreenReader` | `boolean` | No | `false` | `aria-live="polite"` on overflow count |
111
+
112
+ **Sub-components:** `AvatarGroup.OverflowCount`
113
+
114
+ **Key features:**
115
+ - Dual API: `items` prop array OR composable `children`
116
+ - Group-level `size` overrides individual sizes
117
+ - `AvatarGroupItem` type: `{ src?, alt?, initials?, size?, className? }`
118
+
119
+ ---
120
+
121
+ ### 3. Banner
122
+
123
+ **File:** `src/components/banner/Banner.tsx` · **Public export:** Yes
124
+ **Also exports:** `BANNER_LEVEL` (enum), `BannerProps` (type)
125
+
126
+ **Use case:** Full-width contextual messaging strip for alerts, warnings, session notices, and errors.
127
+
128
+ | Prop | Type | Required | Default | Description |
129
+ |---|---|---|---|---|
130
+ | `level` | `BANNER_LEVEL` | No | `BANNER_LEVEL.NEUTRAL` | Semantic level |
131
+ | `title` | `string` | No | — | Bold heading text |
132
+ | `text` | `string` | No | — | Body text |
133
+ | `buttonText` | `string` | No | — | CTA button label |
134
+ | `buttonOnClick` | `(e: React.MouseEvent<HTMLButtonElement>) => void` | No | — | CTA button handler |
135
+ | `icon` | `IconName` | No | Auto from level | Override leading icon |
136
+ | `hideIcon` | `boolean` | No | `false` | Suppress icon |
137
+
138
+ **`BANNER_LEVEL` values:** `INFO` (blue), `NEUTRAL` (grey), `WARNING` (yellow), `DESTRUCTIVE` (red)
139
+
140
+ **Default icon map:** `INFO`/`NEUTRAL` → `info`, `WARNING` → `triangle-alert`, `DESTRUCTIVE` → `circle-alert`
141
+
142
+ **Key features:**
143
+ - Built with CVA (class-variance-authority)
144
+ - CTA rendered as `Button variant="text-link"` aligned right
145
+ - Not dismissable by default
146
+
147
+ ---
148
+
149
+ ### 4. Button
150
+
151
+ **File:** `src/components/button/Button.tsx` · **Public export:** Yes
152
+
153
+ **Use case:** Primary interactive element for form submissions, action triggers, navigation, and as trigger targets for Dropdown/Tooltip.
154
+
155
+ | Prop | Type | Required | Default | Description |
156
+ |---|---|---|---|---|
157
+ | `variant` | `ButtonVariant` | No | `'primary'` | Visual variant |
158
+ | `size` | `'M' \| 'S'` | No | `'M'` | Size |
159
+ | `children` | `React.ReactNode` | No | — | Button label |
160
+ | `disabled` | `boolean` | No | `false` | Disabled state |
161
+ | `error` | `boolean` | No | — | Error state styling |
162
+ | `hasHorizontalPadding` | `boolean` | No | `true` | Adds/removes horizontal padding |
163
+ | `iconRightName` | `IconName` | No | — | Icon after text |
164
+ | `iconRightScreenReaderText` | `string` | No | icon name | SR text for right icon |
165
+ | `iconLeftName` | `IconName` | No | — | Icon before text |
166
+ | `iconLeftScreenReaderText` | `string` | No | icon name | SR text for left icon |
167
+ | `borderless` | `boolean` | No | `false` | Removes border |
168
+ | `onClick` | `(event: React.MouseEvent<HTMLButtonElement>, ...otherArgs: unknown[]) => void` | No | — | Click handler |
169
+
170
+ **`ButtonVariant` values:** `'primary'` · `'secondary'` · `'tertiary'` · `'primary-destructive'` · `'secondary-destructive'` · `'text-link'` · `'dropdown'`
171
+
172
+ **Key features:**
173
+ - `forwardRef` — ref forwarded to `<button>`
174
+ - Icon-only mode auto-applied when no `children` but icons are set
175
+ - Does NOT set HTML `type` attribute (caller responsibility)
176
+ - `'dropdown'` variant used internally by SelectDropdown, ColourPickerDropdown, etc.
177
+
178
+ ---
179
+
180
+ ### 5. Card
181
+
182
+ **File:** `src/components/card/Card.tsx` · **Public export:** Yes
183
+
184
+ **Use case:** Navigational or informational content block for module selection, settings pages, or pick-from-a-list UIs.
185
+
186
+ | Prop | Type | Required | Default | Description |
187
+ |---|---|---|---|---|
188
+ | `title` | `string` | No | — | Card heading |
189
+ | `paragraph` | `string` | No | — | Descriptive text |
190
+ | `icon` | `IconName` | No | — | Leading icon |
191
+ | `iconColor` | `string` | No | — | Icon color override |
192
+ | `iconScreenReaderText` | `string` | No | — | SR text for icon |
193
+ | `tagText` | `string` | No | — | Tag label inside card |
194
+ | `tagColor` | `TagColor` | No | — | Tag color |
195
+ | `onClick` | `(e: React.MouseEvent<HTMLElement>) => void` | No | — | Makes card clickable |
196
+ | `disabled` | `boolean` | No | — | Disables interaction |
197
+
198
+ **Variants:** clickable (chevron + arrow icons, cursor pointer) / static / disabled
199
+
200
+ **Key features:**
201
+ - Renders as `<article>`
202
+ - Clickable cards get `tabIndex={0}` for keyboard access
203
+ - `aria-label="Card"` hardcoded — consider overriding via `...rest`
204
+
205
+ ---
206
+
207
+ ### 6. Combobox
208
+
209
+ **File:** `src/components/combobox/Combobox.tsx` · **Public export:** Yes
210
+ **Also exports:** `ComboboxProps`, `ComboboxOption`, `ComboboxAriaInvalid`, `ComboboxCreateResult`, `ComboboxSearchFn`, `ComboboxSearchType` types
211
+
212
+ **Use case:** Searchable, filterable, optionally multi-select dropdown. Supports async search, option grouping, chip tags, create-new, and custom renderers.
213
+
214
+ **`ComboboxOption` shape:** `{ value: string, label: string, tagLabel?: string, iconName?: IconName, disabled?: boolean, group?: string }`
215
+
216
+ | Prop | Type | Required | Default | Description |
217
+ |---|---|---|---|---|
218
+ | `options` | `ComboboxOption[]` | Yes | — | List of options |
219
+ | `multiple` | `boolean` | No | `false` | Multi-select |
220
+ | `value` | `string[]` | No | — | Controlled selected values (array even in single mode) |
221
+ | `defaultValue` | `string[]` | No | — | Uncontrolled initial values |
222
+ | `onValueChange` | `(values: string[]) => void` | No | — | Selection change callback |
223
+ | `onSearch` | `(query: string) => void` | No | — | Async search callback; enables async mode |
224
+ | `searchType` | `'prefix' \| 'substring' \| fn` | No | `'prefix'` | Built-in filter strategy |
225
+ | `highlightStringMatches` | `boolean` | No | `false` | Highlights matched text in options |
226
+ | `allowCreate` | `boolean` | No | `false` | Shows "Create X" row, calls `onCreateNew` |
227
+ | `onCreateNew` | `(input: string) => ComboboxCreateResult` | No | — | Create callback |
228
+ | `onDeleteCreated` | `(value: string) => void` | No | — | Delete created option callback |
229
+ | `placeholder` | `string` | No | `'Select...'` | Placeholder text |
230
+ | `disabled` | `boolean` | No | `false` | Disabled state |
231
+ | `dropdownOnFocus` | `boolean` | No | `true` | Open dropdown on focus |
232
+ | `triggerVariant` | `'input' \| 'button'` | No | `'input'` | `'input'` = inline text trigger; `'button'` = button opens dropdown with search inside |
233
+ | `selectedValueDisplay` | `'tags' \| 'text'` | No | `'tags'` | `'tags'` = removable chip tags; `'text'` = plain text |
234
+ | `showDropdownTrigger` | `boolean` | No | `true` | Show/hide the chevron button |
235
+ | `triggerEndContent` | `ReactNode` | No | — | Custom content at end of trigger |
236
+ | `showSelectionCountBadge` | `boolean` | No | auto | Shows count badge (auto-enabled for button+multiple) |
237
+ | `selectionCountA11yLabel` | `string \| ((count: number) => string)` | No | — | SR label for badge count |
238
+ | `loading` | `boolean` | No | `false` | Loading state in listbox |
239
+ | `hasError` | `boolean` | No | `false` | Error state |
240
+ | `showClearAll` | `boolean` | No | `false` | Show "Clear all" below trigger when items selected |
241
+ | `clearAllLabel` | `string` | No | `'Clear all'` | Label for clear button |
242
+ | `renderOption` | `(option, selected) => ReactNode` | No | — | Custom option renderer |
243
+ | `getTagLabel` | `(option) => string` | No | — | Custom tag label getter |
244
+ | `id`, `aria-describedby`, `aria-invalid`, `aria-label` | — | No | — | Standard ARIA props |
245
+
246
+ **Sub-components:** `Combobox.Trigger`, `Combobox.ButtonTrigger`, `Combobox.Listbox` (advanced composition)
247
+
248
+ **Key features:**
249
+ - Options with the same `group` string are visually grouped under a heading
250
+ - Radix Popover rendered into `PopupParentContext`
251
+ - Keyboard-navigable: arrow keys, Enter to select, Escape to close, Backspace to remove tags
252
+
253
+ ---
254
+
255
+ ### 6b. DatePicker
256
+
257
+ **File:** `src/components/datePicker/DatePicker.tsx` · **Public export:** Yes
258
+
259
+ **Use case:** Date selection via combined text input (free-type) and calendar popover for forms, filters, and settings.
260
+
261
+ | Prop | Type | Required | Default | Description |
262
+ |---|---|---|---|---|
263
+ | `value` | `Date` | No | — | Controlled selected date |
264
+ | `defaultValue` | `Date` | No | — | Uncontrolled initial date |
265
+ | `onChange` | `(newDate?: Date) => void` | No | — | Called when date changes or is cleared |
266
+ | `displayFormat` | `DateDisplayFormat` | No | `'default'` | Display format (e.g. 'dmy', 'mdy') |
267
+ | `placeholder` | `string` | No | Format-derived | Placeholder text |
268
+ | `id` | `string` | No | — | Applied to text input |
269
+ | `hasError` | `boolean` | No | — | Error state |
270
+ | `aria-describedby` | `string` | No | — | ARIA description |
271
+ | `aria-invalid` | `boolean` | No | — | ARIA invalid |
272
+
273
+ **Key features:**
274
+ - Dual input: type directly OR open calendar via icon button
275
+ - Powered by `react-day-picker`
276
+ - Calendar popup uses Radix `Popover` rendered into `PopupParentContext`
277
+ - Month/year navigation in calendar header
278
+ - `onChange` called with `undefined` when date is cleared
279
+
280
+ ---
281
+
282
+ ### 7. Dropdown
283
+
284
+ **File:** `src/components/dropdown/Dropdown.tsx` · **Public export:** Yes
285
+
286
+ **Use case:** General-purpose contextual menu or action panel. Foundation for `SelectDropdown`, `ColourPickerDropdown`, `UserDropdown`, `BulkActionsDropdown`, `TableSettingsDropdown`.
287
+
288
+ **Compound components:**
289
+
290
+ | Sub-component | Description |
291
+ |---|---|
292
+ | `Dropdown.Trigger` | Wraps trigger element with `asChild` |
293
+ | `Dropdown.Content` | Portal + content panel |
294
+ | `Dropdown.Item` | Standard menu item |
295
+ | `Dropdown.SelectItem` | Checkbox-style selectable item |
296
+ | `Dropdown.Separator` | Visual divider |
297
+ | `Dropdown.Group` | Groups items together |
298
+
299
+ **`Dropdown.Content` key props:** `portalProps`, `contentProps` (align defaults `'start'`), `className`
300
+
301
+ **`Dropdown.SelectItem` key props:** `selected` (required), `onSelectChange` (required), `closeAfterSelection` (default `true`)
302
+
303
+ **Key features:**
304
+ - Built on Radix UI `DropdownMenu` primitives
305
+ - Portal rendered into `PopupParentContext` ref
306
+ - `modal={false}` by default — no body scroll lock
307
+ - `closeAfterSelection={false}` for multi-select UIs
308
+
309
+ ---
310
+
311
+ ### 8. EditableText
312
+
313
+ **File:** `src/components/editableText/EditableText.tsx` · **Public export:** Yes
314
+
315
+ **Use case:** Inline editable text — click to edit, Enter/blur to save, Escape to cancel. Suitable for table cell names, titles, any occasionally-edited label.
316
+
317
+ | Prop | Type | Required | Default | Description |
318
+ |---|---|---|---|---|
319
+ | `text` | `string` | Yes | — | Current text value |
320
+ | `onEditSave` | `(newText: string) => void` | Yes | — | Called when editing is committed |
321
+ | `isEditing` | `boolean` | No | `false` | Controlled editing state |
322
+ | `multiline` | `boolean` | No | `false` | Use `TextArea` instead of `TextInput` |
323
+
324
+ **Key features:**
325
+ - Toggle between display (button + pencil icon) and edit (TextInput/TextArea)
326
+ - `autoFocus` on input when entering edit mode
327
+ - Enter saves (single-line); Escape cancels and restores value
328
+ - `isEditing` prop allows programmatic control
329
+
330
+ ---
331
+
332
+ ### 9. FormField
333
+
334
+ **File:** `src/components/formField/FormField.tsx` · **Public export:** No (internal)
335
+
336
+ **Use case:** Complete form field wrapper combining label, description, input, error text, and helper link. The preferred way to render any form input with consistent layout and accessibility wiring.
337
+
338
+ | Prop | Type | Required | Default | Description |
339
+ |---|---|---|---|---|
340
+ | `label` | `string` | No | — | Field label |
341
+ | `id` | `string` | No | — | Associates label and input |
342
+ | `inputType` | `'text' \| 'textarea' \| 'number' \| 'colourPicker' \| 'selectDropdown' \| 'datePicker'` | No | `'text'` | Which input to render |
343
+ | `inputProps` | Varies by `inputType` | No | — | Props forwarded to the input |
344
+ | `fieldDescription` | `ReactNode` | No | — | Descriptive text below label |
345
+ | `helperLinkText` | `string` | No | — | Helper link label |
346
+ | `helperLinkUrl` | `string` | No | — | Helper link URL |
347
+ | `errorText` | `string` | No | — | Error message |
348
+
349
+ **Key features:**
350
+ - Auto-wires `aria-describedby` to description and error IDs
351
+ - Sets `hasError` and `aria-invalid` on inner input when `errorText` present
352
+ - Error icon: `triangle-alert`; helper link icon: `arrow-up-right`
353
+
354
+ ---
355
+
356
+ ### 10. Fieldset
357
+
358
+ **File:** `src/components/formField/fieldset/Fieldset.tsx` · **Public export:** Yes
359
+
360
+ **Use case:** Groups related form controls (checkboxes, radios) with semantic `<fieldset>/<legend>`.
361
+
362
+ | Prop | Type | Required | Default | Description |
363
+ |---|---|---|---|---|
364
+ | `legend` | `string` | No | — | Renders `<legend>` |
365
+ | `children` | `ReactNode` | No | — | Form controls |
366
+ | `...rest` | `HTMLProps<HTMLFieldSetElement>` | No | — | Native fieldset attributes (incl. `disabled`) |
367
+
368
+ **Key features:**
369
+ - Native `disabled` disables all child inputs in one prop
370
+ - Used internally by `CheckboxGroup` and `RadioButtonGroup`
371
+
372
+ ---
373
+
374
+ ### 11. TextInput
375
+
376
+ **File:** `src/components/formField/inputs/text/TextInput.tsx` · **Public export:** Yes
377
+
378
+ **Use case:** Standard single-line text input for forms. Used by `FormField`, `DatePicker`, `EditableText`, `InlineTextCellRenderer`.
379
+
380
+ | Prop | Type | Required | Default | Description |
381
+ |---|---|---|---|---|
382
+ | `size` | `'M' \| 'S'` | No | `'M'` | Input height |
383
+ | `hasError` | `boolean` | No | — | Error state styling |
384
+ | `disabled` | `boolean` | No | `false` | Disabled state |
385
+
386
+ ---
387
+
388
+ ### 12. TextArea
389
+
390
+ **File:** `src/components/formField/inputs/textArea/TextArea.tsx` · **Public export:** Yes
391
+
392
+ **Use case:** Multi-line text input that auto-grows to fit content.
393
+
394
+ | Prop | Type | Required | Default | Description |
395
+ |---|---|---|---|---|
396
+ | `hasError` | `boolean` | No | — | Error state |
397
+ | `autoSize` | `boolean` | No | `true` | Auto-grows height |
398
+ | `disabled` | `boolean` | No | `false` | Disabled state |
399
+
400
+ **Key features:**
401
+ - `autoSize` sets explicit `height` from `scrollHeight` on each change — grows down, never scrolls internally
402
+
403
+ ---
404
+
405
+ ### 13. NumberInput
406
+
407
+ **File:** `src/components/formField/inputs/number/NumberInput.tsx` · **Public export:** Yes
408
+
409
+ **Use case:** Numeric input with +/− spinner buttons. Uses `Decimal.js` for precise arithmetic.
410
+
411
+ | Prop | Type | Required | Default | Description |
412
+ |---|---|---|---|---|
413
+ | `hasError` | `boolean` | No | — | Error state |
414
+ | `disableSpinners` | `boolean` | No | `false` | Hide increment/decrement buttons |
415
+ | `step` | `number` | No | `1` | Spinner step size |
416
+ | `min` | `number` | No | `-Infinity` | Minimum value |
417
+ | `max` | `number` | No | `Infinity` | Maximum value |
418
+
419
+ **Key features:**
420
+ - Renders as `type="text"` with `inputMode="numeric"` (avoids browser native spinner styling)
421
+ - Clamps value to `min`/`max` on blur and spinner click
422
+
423
+ ---
424
+
425
+ ### 14. CheckboxInput
426
+
427
+ **File:** `src/components/formField/inputs/checkbox/CheckboxInput.tsx` · **Public export:** Yes (exported from index.ts line 24)
428
+
429
+ **Use case:** Single styled checkbox with optional inline label. Used standalone or via `CheckboxGroup`.
430
+
431
+ | Prop | Type | Required | Default | Description |
432
+ |---|---|---|---|---|
433
+ | `checked` | `boolean` | No | `false` | Checked state |
434
+ | `indeterminate` | `boolean` | No | `false` | Indeterminate state |
435
+ | `label` | `string` | No | — | Inline label text |
436
+ | `disabled` | `boolean` | No | — | Disabled state |
437
+
438
+ **Key features:**
439
+ - Real `<input type="checkbox">` visually hidden; custom `<span>` visible
440
+ - `indeterminate` set via DOM API; `aria-checked="mixed"` when indeterminate
441
+ - Icons: `check` (checked), `minus` (indeterminate)
442
+
443
+ ---
444
+
445
+ ### 15. CheckboxGroup
446
+
447
+ **File:** `src/components/formField/inputs/checkbox/CheckboxGroup.tsx` · **Public export:** No (internal)
448
+
449
+ **Use case:** Group of related checkboxes within a `Fieldset`. Used in `TableSettingsDropdown`.
450
+
451
+ **Props:** `options: CheckboxInputProps[]` + all `FieldsetProps`
452
+
453
+ ---
454
+
455
+ ### 16. RadioButtonInput
456
+
457
+ **File:** `src/components/formField/inputs/radio/RadioButtonInput.tsx` · **Public export:** Yes
458
+
459
+ **Use case:** Single radio button with inline label. Combine multiple with `RadioButtonGroup`.
460
+
461
+ | Prop | Type | Required | Default | Description |
462
+ |---|---|---|---|---|
463
+ | `label` | `string` | No | — | Inline label |
464
+ | `hasError` | `boolean` | No | — | Error state |
465
+ | `name` | `string` | No | — | Radio group name |
466
+ | `value` | `string` | No | — | Radio value |
467
+ | `checked` | `boolean` | No | `false` | Selected state |
468
+ | `disabled` | `boolean` | No | `false` | Disabled state |
469
+
470
+ **Key features:** `type` hardcoded to `"radio"`, cannot be overridden
471
+
472
+ ---
473
+
474
+ ### 17. RadioButtonGroup
475
+
476
+ **File:** `src/components/formField/inputs/radio/RadioButtonGroup.tsx` · **Public export:** No (internal)
477
+
478
+ **Use case:** Controlled group of radio buttons within a `Fieldset`. Used in `TableSettingsDropdown` for spacing selection.
479
+
480
+ | Prop | Type | Required | Description |
481
+ |---|---|---|---|
482
+ | `name` | `string` | Yes | Shared group name |
483
+ | `options` | `RadioButtonInputProps[]` | Yes | Radio option configs |
484
+ | `checkedValue` | `string` | Yes | Currently selected value |
485
+ | `onChange` | `(event: ChangeEvent<HTMLInputElement>) => void` | Yes | Change handler |
486
+ | `legend` | `string` | No | Fieldset legend |
487
+
488
+ ---
489
+
490
+ ### 18. SelectDropdown
491
+
492
+ **File:** `src/components/formField/inputs/selectDropdown/SelectDropdown.tsx` · **Public export:** Yes
493
+
494
+ **Use case:** Styled single or multi-select dropdown. Supports option grouping, icons, and two-line option layouts.
495
+
496
+ | Prop | Type | Required | Default | Description |
497
+ |---|---|---|---|---|
498
+ | `options` | `SelectDropdownItemProps[]` | Yes | — | List of options |
499
+ | `multiple` | `boolean` | No | — | Multi-select mode |
500
+ | `placeholder` | `string` | No | `'Select'` | Placeholder text |
501
+ | `disabled` | `boolean` | No | — | Disabled state |
502
+ | `hasError` | `boolean` | No | — | Error state |
503
+ | `onSelectionChange` | `(value: string[]) => void` | No | — | Selection callback |
504
+ | `alwaysShowPlaceholder` | `boolean` | No | `false` | Always show placeholder (not selected label) |
505
+ | `initialSelectedValues` | `string[]` | No | `[]` | Pre-selected values |
506
+ | `open` | `boolean` | No | — | Controlled open state |
507
+ | `onOpenChange` | `(open: boolean) => void` | No | — | Open state change handler |
508
+
509
+ **`SelectDropdownItemProps`:** `{ value, label?, icon?, header?, group? }`
510
+
511
+ **Key features:**
512
+ - Groups options by `group` field with `<h3>` group headers
513
+ - Hidden `<input type="hidden">` for form serialization
514
+ - Trigger: `Button variant="dropdown"` with `chevron-down`
515
+ - Multi-select: deselect by clicking again; dropdown stays open
516
+
517
+ ---
518
+
519
+ ### 19. ColourPickerDropdown
520
+
521
+ **File:** `src/components/formField/inputs/colourPickerDropdown/ColourPickerDropdown.tsx` · **Public export:** Yes
522
+
523
+ **Use case:** Color selection input showing a swatch + hex value. Opens a Sketch-style picker dropdown.
524
+
525
+ | Prop | Type | Required | Default | Description |
526
+ |---|---|---|---|---|
527
+ | `value` | `string` | No | `'#3cad51'` | Current hex color |
528
+ | `onChange` | `(value: ColorResult) => void` | No | — | Color change callback |
529
+ | `hasError` | `boolean` | No | — | Error state |
530
+ | `disabled` | `boolean` | No | — | Disabled state |
531
+
532
+ **Key features:**
533
+ - Built on `@uiw/react-color` `<Sketch>` — alpha channel disabled
534
+ - Hidden `<input type="hidden">` stores hex value for form serialization
535
+
536
+ ---
537
+
538
+ ### 20. Heading
539
+
540
+ **File:** `src/components/heading/Heading.tsx` · **Public export:** Yes
541
+
542
+ **Use case:** Semantic heading element mapping a numeric `level` to the correct `h1`–`h4` HTML tag.
543
+
544
+ | Prop | Type | Required | Default | Description |
545
+ |---|---|---|---|---|
546
+ | `level` | `1 \| 2 \| 3 \| 4` | No | `1` | Heading element level |
547
+ | `children` | `ReactNode` | No | — | Content |
548
+
549
+ **Sub-components:** `Heading.InnerContainer` — `<span>` for flex layout inside headings (title left, buttons right)
550
+
551
+ ---
552
+
553
+ ### 21. Icon
554
+
555
+ **File:** `src/components/icon/Icon.tsx` · **Public export:** Yes
556
+
557
+ **Use case:** Renders a single icon from the curated allowed-icons set. Standard icon primitive used throughout.
558
+
559
+ | Prop | Type | Required | Default | Description |
560
+ |---|---|---|---|---|
561
+ | `name` | `IconName` | Yes | — | Icon identifier |
562
+ | `size` | `12 \| 16 \| 24` | No | `16` | Pixel size |
563
+ | `color` | `string` | No | `'currentColor'` | SVG color |
564
+ | `screenReaderText` | `string` | No | — | Visually hidden SR label |
565
+
566
+ **Icon library:** ~100+ icons from `lucide-react` + 5 custom: `ask-arbor`, `google`, `check-solid`, `x-solid`, `favourite-filled`
567
+
568
+ **Key icons:** `search`, `settings`, `chevron-down/up/left/right`, `x`, `check`, `info`, `triangle-alert`, `circle-alert`, `circle-check`, `pencil`, `trash`, `plus`, `minus`, `date`, `user`, `users`, `arrow-right`, `arrow-left`, `log-out`, `sparkles`, `funnel`, `sorting`, `grab`, `pin`, `lock`, `lock-open`, `eye`, `eye-off`
569
+
570
+ **Key features:**
571
+ - `aria-hidden` on SVG; accessible text via `<span className="sr-only">` when `screenReaderText` provided
572
+ - `IconName` type constrains to approved set — TypeScript errors on unknown names
573
+ - `IconSize` is `12 | 16 | 24` only — not arbitrary values
574
+
575
+ ---
576
+
577
+ ### 22. Modal
578
+
579
+ **Files:** `src/components/modal/Modal.tsx` + `src/components/modal/modalManager/ModalManager.tsx` · **Public export:** Yes (both)
580
+
581
+ **Use case:**
582
+ - `Modal`: Self-managed dialog, controlled via `open`/`closeHandler`
583
+ - `ModalManager`: Mount once at app root; open modals imperatively via `ModalUtils` from anywhere
584
+
585
+ **`Modal` props:**
586
+
587
+ | Prop | Type | Required | Default | Description |
588
+ |---|---|---|---|---|
589
+ | `open` | `boolean` | No | — | Controlled open state |
590
+ | `title` | `string` | No | — | Shortcut title (renders `ModalHeader` + `ModalTitle`) |
591
+ | `closeHandler` | `() => void` | No | — | Close callback; enables X button + Escape |
592
+ | `hideCloseButton` | `boolean` | No | `false` | Hide the X button |
593
+ | `children` | `ReactNode` | No | — | Modal body |
594
+ | `portalTarget` | `HTMLElement \| null` | No | — | Custom portal target |
595
+
596
+ **Sub-components:** `Modal.Header`, `Modal.Body`, `Modal.Footer`, `Modal.CloseButton`, `Modal.Title`
597
+
598
+ **Imperative API (`ModalUtils`):**
599
+ - `ModalUtils.addModal(props: ModalProps)` — open a modal
600
+ - `ModalUtils.removeModal()` — close current modal
601
+ - `ModalUtils.removeAllModals()` — clear all
602
+
603
+ **Key features:**
604
+ - Built on Radix UI `Dialog`
605
+ - Sets up `PopupParentContext.Provider` — dropdowns/tooltips inside modals render at correct z-index
606
+ - `ModalContext` provides `closeHandler` to `Modal.CloseButton` — no prop drilling
607
+
608
+ ---
609
+
610
+ ### 23. Pill
611
+
612
+ **File:** `src/components/pill/Pill.tsx` · **Public export:** Yes
613
+
614
+ **Use case:** Filter pills or toggle chips for filter bars where users activate/deactivate multiple categories.
615
+
616
+ | Prop | Type | Required | Default | Description |
617
+ |---|---|---|---|---|
618
+ | `text` | `string` | Yes | — | Label text |
619
+ | `checkbox` | `boolean` | No | — | Renders as checkbox pill |
620
+ | `initialValue` | `boolean` | No | `false` | Initial checked/active state |
621
+ | `onclick` | `(checked: boolean) => void` | No | — | State change callback |
622
+
623
+ **Variants:** Toggle pill (active/inactive) · Checkbox pill (checked/unchecked, with embedded `CheckboxInput`)
624
+
625
+ ---
626
+
627
+ ### 24. Progress
628
+
629
+ **File:** `src/components/progress/Progress.tsx` · **Public export:** Yes
630
+
631
+ **Use case:** Horizontal progress bar for uploads, completion rates, loading indicators.
632
+
633
+ | Prop | Type | Required | Default | Description |
634
+ |---|---|---|---|---|
635
+ | `value` | `number` | No | `0` | Current value |
636
+ | `max` | `number` | No | `100` | Maximum value |
637
+ | `indicatorClassName` | `string` | No | `''` | Class on progress bar |
638
+
639
+ **Key features:**
640
+ - Built on Radix UI `Progress`
641
+ - Animation via `transform: translateX(-{100 - percentage}%)` on indicator
642
+ - Provide `aria-label` via `...rest` for accessibility
643
+
644
+ ---
645
+
646
+ ### 25. SearchBar
647
+
648
+ **File:** `src/components/searchBar/SearchBar.tsx` · **Public export:** Yes
649
+
650
+ **Use case:** Search input that starts compact (icon/label) and expands on click. Used in `TableHeader` and space-efficient search UIs.
651
+
652
+ | Prop | Type | Required | Default | Description |
653
+ |---|---|---|---|---|
654
+ | `searchValue` | `string` | No | — | Controlled search value |
655
+ | `setSearchValue` | `(searchValue: string) => void` | No | — | Value change handler |
656
+ | `placeholderText` | `string` | No | — | Text in inactive/expanded state |
657
+ | `hoverText` | `string` | No | — | Text on hover of inactive button |
658
+ | `alwaysOpen` | `boolean` | No | `false` | Always show expanded input |
659
+
660
+ **Variants:** Collapsible (toggle on click) · `alwaysOpen` (always expanded, no clear button)
661
+
662
+ ---
663
+
664
+ ### 26. Section
665
+
666
+ **File:** `src/components/section/Section.tsx` · **Public export:** Yes
667
+
668
+ **Use case:** Page layout container with heading, optional action button, optionally collapsible content. Primary structural component for organising page regions.
669
+
670
+ | Prop | Type | Required | Default | Description |
671
+ |---|---|---|---|---|
672
+ | `title` | `string` | No | — | Heading text |
673
+ | `headingLevel` | `1 \| 2 \| 3 \| 4` | No | `1` | HTML heading level |
674
+ | `titleIconName` | `IconName` | No | — | Icon beside title |
675
+ | `collapsible` | `boolean` | No | `false` | Enable expand/collapse |
676
+ | `collapsed` | `boolean` | No | `false` | Initial collapsed state |
677
+ | `buttonText` | `string` | No | — | Action button label in header |
678
+ | `buttonOnClick` | `MouseEventHandler<HTMLButtonElement>` | No | — | Action button handler |
679
+ | `buttonVariant` | `ButtonVariant` | No | — | Action button variant |
680
+ | `buttonSize` | `ButtonSize` | No | — | Action button size |
681
+ | `children` | `ReactNode` | No | — | Section content |
682
+
683
+ **Key features:**
684
+ - Renders as `<section>` with implicit ARIA `region` role
685
+ - Collapsible: toggle on click/Enter/Space; `aria-expanded` on toggle; chevron direction indicates state
686
+
687
+ ---
688
+
689
+ ### 27. Separator
690
+
691
+ **File:** `src/components/separator/Separator.tsx` · **Public export:** Yes
692
+
693
+ **Use case:** Visual divider between sections or menu items.
694
+
695
+ **Props:** `orientation: 'horizontal' | 'vertical'` (default horizontal), `decorative: boolean`
696
+
697
+ **Key features:**
698
+ - Built on Radix UI `Separator`
699
+ - `decorative={true}` sets `role="none"` for non-semantic use
700
+
701
+ ---
702
+
703
+ ### 28. Slideover
704
+
705
+ **File:** `src/components/slideover/Slideover.tsx` · **Public export:** Yes
706
+
707
+ **Use case:** Slide-in side panel from the right. Supports stacking multiple slideovers. Used for detail views, edit forms, and drill-down navigation.
708
+
709
+ | Prop | Type | Required | Default | Description |
710
+ |---|---|---|---|---|
711
+ | `title` | `string` | No | — | Header title text |
712
+ | `children` | `ReactNode` | No | — | Main content |
713
+ | `footerContents` | `ReactNode` | No | — | Footer content |
714
+ | `headerIcon` | `IconName` | No | — | Icon in header |
715
+ | `centerHeaderText` | `boolean` | No | `true` | Centers header text |
716
+ | `hideBackButton` | `boolean` | No | — | Hides "Back" button |
717
+
718
+ **Key features:**
719
+ - Renders as `<aside>`
720
+ - "Back" button calls `SlideoverUtils.removeSlideover()` to pop the stack
721
+
722
+ ---
723
+
724
+ ### 29. SlideoverManager
725
+
726
+ **File:** `src/components/slideoverManager/SlideoverManager.tsx` · **Public export:** Yes
727
+
728
+ **Use case:** Mount once at app root. Manages a stack of `Slideover` components. Open/close imperatively from anywhere via `SlideoverUtils`.
729
+
730
+ **`SlideoverUtils` API:**
731
+ - `SlideoverUtils.addSlideover(config: SlideoverProps)` — push new slideover
732
+ - `SlideoverUtils.removeSlideover()` — pop top slideover
733
+ - `SlideoverUtils.removeAllSlideovers()` — clear entire stack
734
+
735
+ **Key features:**
736
+ - PubSub events: `SLIDEOVER.ADD_SLIDEOVER`, `SLIDEOVER.REMOVE_SLIDEOVER`, `SLIDEOVER.REMOVE_ALL_SLIDEOVERS`
737
+ - `PopupParentContext.Provider` wraps manager — popovers inside slideovers render correctly
738
+ - `ds-slideover-manager--active` class triggers overlay when any slideover visible
739
+
740
+ ---
741
+
742
+ ### 30. Table
743
+
744
+ **File:** `src/components/table/Table.tsx` · **Public export:** Yes
745
+ **Also exports:** `GridApiContext`, `DSDefaultColDef`, `DefaultCellRenderer`, `BooleanCellRenderer`
746
+
747
+ **Use case:** Full-featured enterprise data grid wrapping AG Grid Enterprise with design system theming, search, pagination, column visibility, bulk actions, row selection, and table settings — all pre-wired.
748
+
749
+ **Key props:**
750
+
751
+ | Prop | Type | Default | Description |
752
+ |---|---|---|---|
753
+ | `hasSearch` | `boolean` | `true` | Show search bar in header |
754
+ | `headerContent` | `ReactNode` | — | Content in table header |
755
+ | `footerContent` | `ReactNode` | — | Content in table footer |
756
+ | `tableTheme` | `string` | — | `'tidy'` for compact theme |
757
+ | `enableSimultaneousRangeAndRowSelection` | `boolean` | `false` | Range + row selection simultaneously |
758
+ | `onTableSettingsChanged` | `(val: TableSettings) => void` | — | Settings change callback |
759
+ | `...rest` | `AgGridReactProps<TData>` | — | All AG Grid React props |
760
+
761
+ **`TABLE_SPACING` enum:** `XS`, `S`, `M` (default), `L`
762
+
763
+ **Sub-components:**
764
+
765
+ | Name | Description |
766
+ |---|---|
767
+ | `Table.PaginationPanel` | Full pagination UI (controls + size selector + row count) |
768
+ | `Table.PaginationControls` | First/prev/next/last + page number input |
769
+ | `Table.PageSizeSelector` | Dropdown to change rows per page |
770
+ | `Table.RowCountInfo` | "Showing X of Y results" text |
771
+ | `Table.BulkActionsDropdown` | Actions dropdown for selected rows |
772
+ | `Table.HideColumnsDropdown` | Multi-select to toggle column visibility |
773
+ | `Table.ButtonCellRenderer` | Renders a `Button` in a cell (ref: `dsButtonCellRenderer`) |
774
+ | `Table.TableSettingsDropdown` | Settings dropdown (spacing, borders, cell colors) |
775
+
776
+ **Auto-registered cell renderers:**
777
+ - `dsButtonCellRenderer` → `ButtonCellRenderer`
778
+ - `dsInlineTextCellRenderer` → `InlineTextCellRenderer` (editable text in cell)
779
+ - `dsSelectDropdownCellRenderer` → `SelectDropdownCellRenderer` (dropdown in cell)
780
+ - `dsBooleanCellRenderer` → `BooleanCellRenderer` (check-solid icon for `true`, x-solid for `false`, null otherwise; also exported directly as `BooleanCellRenderer`)
781
+
782
+ **Auto-registered column filters:**
783
+ - `dsBooleanFilter` → `BooleanFilter`
784
+ - `dsTimeFilter` → `TimeFilter` (equals/before/after modes)
785
+
786
+ **Key features:**
787
+ - AG Grid Enterprise with `AllEnterpriseModule`
788
+ - Two themes: `defaultTheme` (configurable spacing + borders) and `tidyTheme` (compact)
789
+ - `GridApiContext` makes AG Grid API available to sub-components without prop drilling
790
+ - `DSDefaultColDef` handles wrapped cell values (`{ value: ... }` objects) and cell color styles
791
+
792
+ ---
793
+
794
+ ### 31. Tabs
795
+
796
+ **File:** `src/components/tabs/Tabs.tsx` · **Public export:** Yes
797
+
798
+ **Use case:** Horizontal tab navigation bar. Purely presentational — callers manage active state and content switching.
799
+
800
+ **`Tabs` props:** `children` (ReactNode — `Tabs.Item` elements), `className`, `...rest`
801
+
802
+ **`Tabs.Item` props:**
803
+
804
+ | Prop | Type | Default | Description |
805
+ |---|---|---|---|
806
+ | `active` | `boolean` | `false` | Active/selected state |
807
+ | `iconName` | `IconName` | — | Optional icon |
808
+ | `tabElement` | `'button' \| 'link'` | `'button'` | Render as button or anchor |
809
+ | `tabElementProps` | `ButtonHTMLAttributes \| AnchorHTMLAttributes` | — | Props for inner element |
810
+ | `children` | `ReactNode` | — | Tab label |
811
+
812
+ **Key features:**
813
+ - `<ul role="tablist">`, `<li role="presentation">`, inner element gets `role="tab"` + `aria-selected`
814
+ - Both `button` and `a` variants get identical ARIA attributes
815
+ - Active tab styled with brand color underline via CSS
816
+
817
+ ---
818
+
819
+ ### 32. Tag
820
+
821
+ **File:** `src/components/tag/Tag.tsx` · **Public export:** Yes
822
+
823
+ **Use case:** Non-interactive label/badge for categorization, status indicators, or metadata display.
824
+
825
+ | Prop | Type | Required | Default | Description |
826
+ |---|---|---|---|---|
827
+ | `text` | `string` | Yes | — | Tag label |
828
+ | `color` | `TagColor` | No | `'neutral'` | Color variant |
829
+ | `dotColour` | `DotColour` | No | — | Optional colored dot prefix |
830
+
831
+ **`TagColor` values:** `'neutral'` · `'orange'` · `'blue'` · `'green'` · `'purple'` · `'teal'` · `'salmon'` · `'yellow'`
832
+
833
+ **`DotColour` values:** `'purple'` · `'salmon'` · `'teal'` · `'yellow'` · `'green'` · `'orange'` · `'blue'`
834
+
835
+ **Key features:**
836
+ - Purely presentational `<span>` — no click handling
837
+ - Color styling via tokens: `--tag-{color}-color-text` and `--tag-{color}-color-background`
838
+ - `dotColour` renders small colored circle with `aria-label="{colour} dot"`
839
+
840
+ ---
841
+
842
+ ### 33. Toast
843
+
844
+ **File:** `src/components/toast/Toast.tsx` · **Public export:** Yes
845
+
846
+ **Use case:** Transient notification messages that appear and auto-dismiss. Requires `Toast.Provider` ancestor and `Toast.Viewport` inside same provider.
847
+
848
+ | Prop | Type | Required | Default | Description |
849
+ |---|---|---|---|---|
850
+ | `variant` | `'information' \| 'danger' \| 'success' \| 'warning'` | No | `'information'` | Semantic variant |
851
+ | `children` | `ReactNode` | No | — | Toast message content |
852
+ | `...rest` | `RadixToast.ToastProps` | No | — | Radix props (e.g. `open`, `onOpenChange`, `duration`) |
853
+
854
+ **Static members:** `Toast.Provider`, `Toast.Viewport`
855
+
856
+ **Variant icon map:** `information` → `info`, `danger` → `circle-alert`, `success` → `circle-check`, `warning` → `triangle-alert`
857
+
858
+ **Key features:**
859
+ - Built on Radix UI `Toast`
860
+ - `duration` prop controls auto-dismiss (default 5000ms)
861
+ - Multiple toasts can be open simultaneously
862
+ - Includes built-in close (X) button
863
+
864
+ ---
865
+
866
+ ### 34. Tooltip / TooltipWrapper
867
+
868
+ **Files:** `src/components/tooltip/Tooltip.tsx`, `TooltipWrapper.tsx` · **Public export:** Yes (both)
869
+
870
+ **Use case:**
871
+ - `Tooltip`: Composable tooltip for complex cases (custom trigger/content)
872
+ - `TooltipWrapper`: Convenience wrapper for the common single-element + text tooltip case
873
+
874
+ **`Tooltip` compound components:**
875
+ - `Tooltip.Trigger` — wraps trigger element with `asChild`
876
+ - `Tooltip.Content` — portal + content bubble
877
+
878
+ **`Tooltip.Content` key props:** `children` (required), `shouldShowArrow` (default `true`), `portalProps`, `...rest` (side, align, sideOffset, etc.)
879
+
880
+ **`TooltipWrapper` props:**
881
+
882
+ | Prop | Type | Required | Description |
883
+ |---|---|---|---|
884
+ | `children` | `ReactNode` | Yes | Trigger element |
885
+ | `tooltipContent` | `ReactNode` | Yes | Tooltip content |
886
+ | `delayDuration` | `number` | No | Hover delay in ms (default 400) |
887
+ | `triggerProps` | `TooltipTriggerProps` | No | Props for the trigger |
888
+ | `contentProps` | `Omit<TooltipContentProps, 'children'>` | No | Props for content |
889
+
890
+ **Key features:**
891
+ - Built on Radix UI `Tooltip`
892
+ - Content portal rendered into `PopupParentContext`
893
+ - `TooltipTrigger` uses `asChild` — trigger element receives all focus/hover behaviors
894
+
895
+ ---
896
+
897
+ ### 35. UserDropdown
898
+
899
+ **File:** `src/components/userDropdown/UserDropdown.tsx` · **Public export:** Yes
900
+ **Also exports:** `UserDropdownUserInfoAction` type, logo assets: `ArborLogo`, `GovhubLogo`, `KeyLogo`, `SampeopleLogo`, `RobinLogo`, `TimetablerLogo`
901
+
902
+ **Use case:** Application header user menu. Shows avatar, user name, role, linked apps, optional discovery section, and sign-out.
903
+
904
+ | Prop | Type | Required | Default | Description |
905
+ |---|---|---|---|---|
906
+ | `user` | `UserDropdownUser` | Yes | — | User info (`name`, `subtitle?`, `avatarSrc?`, `avatarInitials?`) |
907
+ | `onSignOut` | `() => void` | Yes | — | Sign-out callback |
908
+ | `logoSrc` | `string` | No | — | App/org logo URL |
909
+ | `sections` | `UserDropdownSection[]` | No | `[]` | App/action sections |
910
+ | `discoverSection` | `{ label, apps, defaultOpen? }` | No | — | Collapsible discovery section |
911
+ | `userInfoAction` | `UserDropdownUserInfoAction` | No | — | Action on user info row |
912
+ | `signOutLabel` | `string` | No | — | Custom sign-out label |
913
+ | `variant` | `'dark' \| 'light'` | No | `'dark'` | Trigger appearance |
914
+
915
+ **`UserDropdownUserInfoAction`:** `{ type: 'link', onClick }` or `{ type: 'menu', items: [{ label, onClick }] }`
916
+
917
+ **Key features:**
918
+ - Built on `Dropdown` compound component
919
+ - `discoverSection` with `defaultOpen` shows collapsible app list
920
+ - Pre-packaged Arbor suite logo assets exported from `index.ts`
921
+
922
+ ---
923
+
924
+ ### 36. DateTimePicker
925
+
926
+ **File:** `src/components/dateTimePicker/DateTimePicker.tsx` · **Public export:** Yes
927
+ **Also exports:** `DateTimePickerProps`, `DateTimePickerDisplayFormat` types
928
+
929
+ **Use case:** Combined date + time picker for forms that need a full datetime value.
930
+
931
+ | Prop | Type | Required | Default | Description |
932
+ |---|---|---|---|---|
933
+ | `value` | `Date` | No | — | Controlled datetime |
934
+ | `defaultValue` | `Date` | No | — | Uncontrolled initial datetime |
935
+ | `onChange` | `(newDate?: Date) => void` | No | — | Called when datetime changes |
936
+ | `displayFormat` | `DateTimePickerDisplayFormat` | No | `'default'` | Date display format |
937
+ | `granularity` | `'minute' \| 'second'` | No | `'minute'` | Time precision |
938
+ | `timeOptions` | `TimeValue[]` | No | — | If provided, renders Combobox time picker instead of native input |
939
+ | `searchType` | `ComboboxSearchType` | No | `'prefix'` | Combobox search strategy (only when `timeOptions` provided) |
940
+ | `highlightStringMatches` | `boolean` | No | `false` | Combobox match highlighting |
941
+ | `placeholder` | `string` | No | — | Date input placeholder |
942
+ | `id` | `string` | No | — | Applied to date input |
943
+ | `hasError` | `boolean` | No | — | Error state |
944
+ | `aria-describedby` | `string` | No | — | ARIA description |
945
+ | `aria-invalid` | `boolean` | No | — | ARIA invalid |
946
+
947
+ ---
948
+
949
+ ### 37. TimeInput
950
+
951
+ **File:** `src/components/formField/inputs/time/TimeInput.tsx` · **Public export:** Yes
952
+ **Also exports:** `TimeInputProps`, `TimeGranularity`, `TimeValue` types
953
+
954
+ **Use case:** Time input with two modes — native browser time input or searchable Combobox-driven picker.
955
+
956
+ **`TimeValue` type:** Template literal `HH:MM` or `HH:MM:SS`
957
+
958
+ | Prop | Type | Required | Default | Description |
959
+ |---|---|---|---|---|
960
+ | `value` | `TimeValue \| ''` | No | — | Controlled time value |
961
+ | `defaultValue` | `TimeValue \| ''` | No | `''` | Uncontrolled initial value |
962
+ | `onValueChange` | `(value: string) => void` | No | — | Change callback |
963
+ | `options` | `TimeValue[]` | No | — | If provided, renders Combobox instead of native input |
964
+ | `granularity` | `'minute' \| 'second'` | No | `'minute'` | Time precision (native mode only) |
965
+ | `hasError` | `boolean` | No | `false` | Error state |
966
+ | `searchType` | `ComboboxSearchType` | No | `'prefix'` | Search strategy (Combobox mode) |
967
+ | `highlightStringMatches` | `boolean` | No | `false` | Highlight matches (Combobox mode) |
968
+ | `disabled`, `id`, `name`, `placeholder`, `aria-*` | — | No | — | Standard HTML input attributes |
969
+
970
+ **Key features:**
971
+ - Native mode: `<input type="time">` with a clock-icon button that focuses and selects hours
972
+ - Combobox mode: renders `Combobox` with `triggerVariant="button"` and clock icon in trigger
973
+ - `forwardRef` — ref attached in native mode only
974
+
975
+ ---
976
+
977
+ ### 38. Toggle
978
+
979
+ **File:** `src/components/toggle/Toggle.tsx` · **Public export:** Yes
980
+
981
+ **Use case:** On/off toggle switch (boolean input). Use in place of a checkbox where a switch metaphor is more appropriate.
982
+
983
+ **Props:** All of Radix UI `Switch.SwitchProps` — key ones:
984
+
985
+ | Prop | Type | Default | Description |
986
+ |---|---|---|---|
987
+ | `checked` | `boolean` | — | Controlled on/off state |
988
+ | `defaultChecked` | `boolean` | — | Uncontrolled initial state |
989
+ | `onCheckedChange` | `(checked: boolean) => void` | — | State change callback |
990
+ | `disabled` | `boolean` | `false` | Disabled state |
991
+ | `required` | `boolean` | — | For form validation |
992
+ | `name` | `string` | — | Form field name |
993
+ | `value` | `string` | `'on'` | Value submitted with form |
994
+
995
+ **Key features:**
996
+ - Radix UI `Switch.Root` + `Switch.Thumb` wrapper
997
+ - CSS: `ds-toggle` (root), `ds-toggle__thumb` (thumb)
998
+ - Pair with a `<label>` or `aria-label` for accessibility
999
+
1000
+ ---
1001
+
1002
+ ### 39. Row
1003
+
1004
+ **File:** `src/components/row/Row.tsx` · **Public export:** Yes
1005
+ **Also exports:** `RowProps` type
1006
+
1007
+ **Use case:** Simple key/value display row for detail panels, info cards, and read-only data views.
1008
+
1009
+ | Prop | Type | Required | Default | Description |
1010
+ |---|---|---|---|---|
1011
+ | `label` | `string` | No | — | Left label text |
1012
+ | `value` | `string` | No | — | Centre value text |
1013
+ | `note` | `string` | No | — | Additional note text |
1014
+ | `onClick` | `MouseEventHandler<HTMLDivElement>` | No | — | Makes row clickable |
1015
+ | `className` | `string` | No | — | Additional CSS class |
1016
+
1017
+ **Key features:**
1018
+ - When `onClick` provided: `ds-row--clickable` class, `tabIndex=0`, keyboard accessible (Enter/Space), renders chevron-right + arrow-right icons on the right edge
1019
+ - NOT suitable for table rows — use for simple key/value display layouts
1020
+
1021
+ ---
1022
+
1023
+ ### 40. SingleUser
1024
+
1025
+ **File:** `src/components/singleUser/SingleUser.tsx` · **Public export:** Yes
1026
+ **Also exports:** `SingleUserProps` type
1027
+
1028
+ **Use case:** Display a person with their avatar and name label, e.g. in staff assignments, contact cards, or user lists.
1029
+
1030
+ | Prop | Type | Required | Default | Description |
1031
+ |---|---|---|---|---|
1032
+ | `label` | `string` | Yes | — | Person's name/label |
1033
+ | `size` | `AvatarSize` | No | `'small'` | Avatar size |
1034
+ | `className` | `string` | No | — | Class on outer `<span>` |
1035
+ | `avatarClassName` | `string` | No | — | Class on Avatar |
1036
+ | `...avatarProps` | `AvatarProps` | No | — | All Avatar props (`src`, `initials`, etc.) |
1037
+
1038
+ **Key features:**
1039
+ - `forwardRef` — ref on outer `<span>`
1040
+ - Avatar has `aria-hidden` + empty `alt` — `label` provides the accessible name
1041
+ - CSS: `ds-single-user`, `ds-single-user__label`
1042
+
1043
+ ---
1044
+
1045
+ ## Utility / Infrastructure (publicly exported)
1046
+
1047
+ | Export | File | Description |
1048
+ |---|---|---|
1049
+ | `SlideoverUtils` | `src/utils/SlideoverUtils.ts` | `addSlideover()`, `removeSlideover()`, `removeAllSlideovers()` — PubSub helpers for `SlideoverManager` |
1050
+ | `GridApiContext` | `src/components/table/GridApiContext.ts` | React context providing AG Grid `GridApi` to table sub-components |
1051
+ | `DSDefaultColDef` | `src/components/table/DSDefaultColDef.ts` | Default AG Grid `ColDef` with DS conventions |
1052
+ | `DefaultCellRenderer` | `src/components/table/cellRenderers/DefaultCellRenderer.tsx` | Standard AG Grid cell renderer |
1053
+ | `BooleanCellRenderer` | `src/components/table/cellRenderers/BooleanCellRenderer.tsx` | Shows check icon (true), x icon (false), or null — also registered as `'dsBooleanCellRenderer'` string |
1054
+
1055
+ **Not exported but notable:**
1056
+ - `ModalUtils` — `addModal()`, `removeModal()`, `removeAllModals()` (mount `ModalManager` to use)
1057
+ - `PopupParentContext` — React context for portal container (used by Modal, Slideover, Dropdown, Tooltip, DatePicker)
1058
+ - `PubSub` — lightweight pub/subscribe for imperative modal/slideover control
1059
+
1060
+ ---
1061
+
1062
+ ## Quick-Reference Summary Table
1063
+
1064
+ | Component | Public | Key Variants | External Lib |
1065
+ |---|---|---|---|
1066
+ | `Badge` | Yes | size: sm/md/lg; 7 colour variants; a11yLabel | — |
1067
+ | `Dot` | Yes | 7 colour variants; optional label | — |
1068
+ | `Avatar` | Yes | size: small/medium/large/extra-large; image/initials/placeholder | — |
1069
+ | `AvatarGroup` | Yes | ascending/descending; overflow count; items or children API | — |
1070
+ | `Banner` | Yes | level: info/neutral/warning/destructive | CVA |
1071
+ | `Button` | Yes | 7 variants; 2 sizes; icon-only; borderless; error | — |
1072
+ | `Card` | Yes | clickable/static/disabled; optional tag | — |
1073
+ | `DatePicker` | Yes | any date-fns format; error state | Radix Popover, react-day-picker |
1074
+ | `Dropdown` | Yes | compound: Trigger/Content/Item/SelectItem/Separator/Group | Radix DropdownMenu |
1075
+ | `EditableText` | Yes | single-line/multiline; controlled editing | — |
1076
+ | `FormField` | No | 6 inputType values; error/helper/description | — |
1077
+ | `Fieldset` | Yes | with/without legend; native disabled | — |
1078
+ | `TextInput` | Yes | size: M/S; error state | — |
1079
+ | `TextArea` | Yes | autoSize; error state | — |
1080
+ | `NumberInput` | Yes | with/without spinners; min/max/step | decimal.js |
1081
+ | `CheckboxInput` | Yes | checked/unchecked/indeterminate; disabled | — |
1082
+ | `CheckboxGroup` | No | fieldset of checkboxes | — |
1083
+ | `RadioButtonInput` | Yes | error state; inline label | — |
1084
+ | `RadioButtonGroup` | No | controlled group with legend | — |
1085
+ | `SelectDropdown` | Yes | single/multi-select; grouping; icons | Radix DropdownMenu |
1086
+ | `ColourPickerDropdown` | Yes | error state; controlled color | @uiw/react-color |
1087
+ | `Heading` | Yes | level: 1–4 (h1–h4) | — |
1088
+ | `Icon` | Yes | ~100 icons; sizes 12/16/24; custom color | lucide-react |
1089
+ | `Modal` + `ModalManager` | Yes | compound sub-components; PubSub imperative control | Radix Dialog |
1090
+ | `Pill` | Yes | toggle / checkbox modes | — |
1091
+ | `Progress` | Yes | value/max controlled | Radix Progress |
1092
+ | `SearchBar` | Yes | collapsible/always-open | — |
1093
+ | `Section` | Yes | collapsible; icon; action button; heading level | — |
1094
+ | `Separator` | Yes | horizontal/vertical; decorative/semantic | Radix Separator |
1095
+ | `Slideover` + `SlideoverManager` | Yes | slide-in panel; stack management; PubSub | — |
1096
+ | `Table` | Yes | AG Grid Enterprise; 2 themes; pagination/bulk/settings | AG Grid Enterprise |
1097
+ | `Tabs` | Yes | button/link tabs; active state; icon | — |
1098
+ | `Tag` | Yes | 8 colors; optional dot color | — |
1099
+ | `Toast` | Yes | 4 variants; Provider + Viewport required | Radix Toast |
1100
+ | `Tooltip` + `TooltipWrapper` | Yes | delay; arrow; composable or convenience API | Radix Tooltip |
1101
+ | `UserDropdown` | Yes | dark/light trigger; sections; discover; link/menu action | Radix DropdownMenu |
1102
+ | `Combobox` | Yes | single/multi; tags or text display; async search; create-new; button or input trigger | Radix Popover |
1103
+ | `DateTimePicker` | Yes | date + time combined; native or Combobox time; granularity | Radix Popover, react-day-picker |
1104
+ | `TimeInput` | Yes | native time input or Combobox-driven; minute/second granularity | — |
1105
+ | `Toggle` | Yes | on/off switch; controlled or uncontrolled | Radix Switch |
1106
+ | `Row` | Yes | clickable or static; label/value/note | — |
1107
+ | `SingleUser` | Yes | avatar + name label; size variants | — |