@arbor-education/design-system.components 0.10.0 → 0.11.1

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 (119) 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 +14 -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 +5 -29
  40. package/dist/components/table/Table.d.ts.map +1 -1
  41. package/dist/components/table/Table.js +12 -22
  42. package/dist/components/table/Table.js.map +1 -1
  43. package/dist/components/table/Table.stories.d.ts +4 -0
  44. package/dist/components/table/Table.stories.d.ts.map +1 -1
  45. package/dist/components/table/Table.stories.js +163 -28
  46. package/dist/components/table/Table.stories.js.map +1 -1
  47. package/dist/components/table/Table.test.js +109 -8
  48. package/dist/components/table/Table.test.js.map +1 -1
  49. package/dist/components/table/TableSettingsContext.d.ts +13 -0
  50. package/dist/components/table/TableSettingsContext.d.ts.map +1 -0
  51. package/dist/components/table/TableSettingsContext.js +15 -0
  52. package/dist/components/table/TableSettingsContext.js.map +1 -0
  53. package/dist/components/table/cellRenderers/CheckboxCellRenderer.d.ts +3 -0
  54. package/dist/components/table/cellRenderers/CheckboxCellRenderer.d.ts.map +1 -0
  55. package/dist/components/table/cellRenderers/CheckboxCellRenderer.js +12 -0
  56. package/dist/components/table/cellRenderers/CheckboxCellRenderer.js.map +1 -0
  57. package/dist/components/table/cellRenderers/CheckboxCellRenderer.test.d.ts +2 -0
  58. package/dist/components/table/cellRenderers/CheckboxCellRenderer.test.d.ts.map +1 -0
  59. package/dist/components/table/cellRenderers/CheckboxCellRenderer.test.js +65 -0
  60. package/dist/components/table/cellRenderers/CheckboxCellRenderer.test.js.map +1 -0
  61. package/dist/components/table/tableConsts.d.ts +7 -0
  62. package/dist/components/table/tableConsts.d.ts.map +1 -0
  63. package/dist/components/table/tableConsts.js +8 -0
  64. package/dist/components/table/tableConsts.js.map +1 -0
  65. package/dist/components/table/{BulkActionsDropdown.d.ts → tableControls/BulkActionsDropdown.d.ts} +1 -1
  66. package/dist/components/table/tableControls/BulkActionsDropdown.d.ts.map +1 -0
  67. package/dist/components/table/{BulkActionsDropdown.js → tableControls/BulkActionsDropdown.js} +3 -3
  68. package/dist/components/table/tableControls/BulkActionsDropdown.js.map +1 -0
  69. package/dist/components/table/{HideColumnsDropdown.d.ts → tableControls/HideColumnsDropdown.d.ts} +1 -2
  70. package/dist/components/table/tableControls/HideColumnsDropdown.d.ts.map +1 -0
  71. package/dist/components/table/{HideColumnsDropdown.js → tableControls/HideColumnsDropdown.js} +2 -2
  72. package/dist/components/table/tableControls/HideColumnsDropdown.js.map +1 -0
  73. package/dist/components/table/tableControls/TableControls.d.ts +23 -0
  74. package/dist/components/table/tableControls/TableControls.d.ts.map +1 -0
  75. package/dist/components/table/tableControls/TableControls.js +21 -0
  76. package/dist/components/table/tableControls/TableControls.js.map +1 -0
  77. package/dist/components/table/tableControls/TableControls.test.d.ts +2 -0
  78. package/dist/components/table/tableControls/TableControls.test.d.ts.map +1 -0
  79. package/dist/components/table/tableControls/TableControls.test.js +124 -0
  80. package/dist/components/table/tableControls/TableControls.test.js.map +1 -0
  81. package/dist/components/table/tableControls/TableSettingsDropdown.d.ts.map +1 -0
  82. package/dist/components/table/{TableSettingsDropdown.js → tableControls/TableSettingsDropdown.js} +7 -6
  83. package/dist/components/table/tableControls/TableSettingsDropdown.js.map +1 -0
  84. package/dist/components/table/useTableSettings.d.ts +1 -1
  85. package/dist/components/table/useTableSettings.d.ts.map +1 -1
  86. package/dist/components/table/useTableSettings.js +1 -1
  87. package/dist/components/table/useTableSettings.js.map +1 -1
  88. package/dist/index.css +19 -1
  89. package/dist/index.css.map +1 -1
  90. package/dist/index.d.ts +1 -0
  91. package/dist/index.d.ts.map +1 -1
  92. package/dist/index.js +1 -0
  93. package/dist/index.js.map +1 -1
  94. package/dist/utils/setAgGridLicenseKey.js +1 -1
  95. package/package.json +1 -1
  96. package/src/components/table/DSDefaultColDef.ts +2 -2
  97. package/src/components/table/Table.stories.tsx +202 -35
  98. package/src/components/table/Table.test.tsx +134 -8
  99. package/src/components/table/Table.tsx +12 -22
  100. package/src/components/table/TableSettingsContext.ts +15 -0
  101. package/src/components/table/cellRenderers/CheckboxCellRenderer.test.tsx +74 -0
  102. package/src/components/table/cellRenderers/CheckboxCellRenderer.tsx +28 -0
  103. package/src/components/table/table.scss +23 -1
  104. package/src/components/table/tableConsts.ts +6 -0
  105. package/src/components/table/{BulkActionsDropdown.tsx → tableControls/BulkActionsDropdown.tsx} +2 -2
  106. package/src/components/table/{HideColumnsDropdown.tsx → tableControls/HideColumnsDropdown.tsx} +2 -2
  107. package/src/components/table/tableControls/TableControls.test.tsx +150 -0
  108. package/src/components/table/tableControls/TableControls.tsx +143 -0
  109. package/src/components/table/{TableSettingsDropdown.tsx → tableControls/TableSettingsDropdown.tsx} +2 -1
  110. package/src/components/table/useTableSettings.ts +1 -1
  111. package/src/index.ts +1 -0
  112. package/src/utils/setAgGridLicenseKey.ts +1 -1
  113. package/dist/components/table/BulkActionsDropdown.d.ts.map +0 -1
  114. package/dist/components/table/BulkActionsDropdown.js.map +0 -1
  115. package/dist/components/table/HideColumnsDropdown.d.ts.map +0 -1
  116. package/dist/components/table/HideColumnsDropdown.js.map +0 -1
  117. package/dist/components/table/TableSettingsDropdown.d.ts.map +0 -1
  118. package/dist/components/table/TableSettingsDropdown.js.map +0 -1
  119. /package/dist/components/table/{TableSettingsDropdown.d.ts → tableControls/TableSettingsDropdown.d.ts} +0 -0
@@ -0,0 +1,404 @@
1
+ # Figma Design Feasibility Assessment
2
+ ## Design: Node 7154:58899 — The Marksheet Column Builder Thing
3
+ **Written by:** Sophia Petrillo, Componentspert, age none-of-your-business
4
+ **Date:** 2026-02-18
5
+
6
+ ---
7
+
8
+ ## What Is This Thing, Anyway?
9
+
10
+ Picture it: Sicily, 1961. My Aunt Galina needed a new kitchen. She had tiles, she had grout, she had a good oven. What she didn't have was someone to tell her honestly what she was missing and what would take three weeks to build from scratch. She ended up with half a kitchen for six months because nobody did the assessment properly.
11
+
12
+ That is not happening here. I read the whole Figma. Every last pixel. And here is what I found.
13
+
14
+ The design (Figma node 7154:58899, titled "3.1" - very descriptive, thank you designers) is a **marksheet column configuration builder** - a tool that lets users configure columns in a marksheet: what data they collect, who can see them, whether they're editable, and how they're formatted. The full page layout includes:
15
+
16
+ 1. A **top navigation bar** - school logo, nav items with chevrons, global search, an avatar, and the Arbor logo. You know, the usual shell stuff.
17
+ 2. A **sidebar / side navigation** - icon-only nav with a little red notification badge. Very cute.
18
+ 3. **Breadcrumbs** with a copy icon, because someone always wants to copy the URL.
19
+ 4. A **page H1 heading** with a primary green action button.
20
+ 5. A **tab bar** - seven tabs. One active. Very normal.
21
+ 6. A **main content area** with a column builder table - rows, columns, headers, draggable rows. More on that later. Much more.
22
+ 7. A **contextual right-hand panel** at roughly 382px wide - a sidebar within the page that contains a search bar, a dropdown button, and a stack of collapsible filter sections with pill-checkbox option lists and "load more" buttons.
23
+
24
+ Alright. Let's get into it.
25
+
26
+ ---
27
+
28
+ ## Section 1: What We Already Have — Use These As-Is, Don't Reinvent Them
29
+
30
+ ### Button (`src/components/button/Button.tsx`)
31
+
32
+ Oh, Button. My Button. The workhorse of this library.
33
+
34
+ The design uses three variants and I am pleased to report we support all of them:
35
+
36
+ - **Primary** (`variant="primary"`, green background) - the big page-heading call-to-action
37
+ - **Secondary** (`variant="secondary"`, white with border) - the "menu button" in the contextual panel
38
+ - **Tertiary** (`variant="tertiary"`, grey background) - the little "load more" buttons at the bottom of each widget section
39
+
40
+ Button also supports `iconLeftName` and `iconRightName` for icon placement, and `size="S"` vs `size="M"` for the two button sizes you see in this design. The pill-area "load more" buttons are small (S), the page heading button is medium (M).
41
+
42
+ One reminder since I know someone is going to do this: the prop is **`variant`**, not `type`. `type` is what you pass to a `<button>` HTML element. We are not animals. Use `variant`.
43
+
44
+ ### Tabs + Tabs.Item (`src/components/tabs/Tabs.tsx`, `src/components/tabs/TabsItem.tsx`)
45
+
46
+ Seven tabs. One active with a green underline. Six inactive in grey text. This is a textbook `Tabs` + `Tabs.Item` situation.
47
+
48
+ `Tabs` renders a `<ul role="tablist">`. `Tabs.Item` renders the individual tabs with `active` prop (controlled - YOU manage which one is active, not the component, which is how it should be). The active tab gets the green underline via `ds-tabs-item__tab--active`. Clean. Correct. No drama.
49
+
50
+ ```tsx
51
+ <Tabs>
52
+ <Tabs.Item active={activeTab === 'settings'} tabElementProps={{ onClick: () => setActiveTab('settings') }}>
53
+ Settings
54
+ </Tabs.Item>
55
+ <Tabs.Item active={activeTab === 'other'} tabElementProps={{ onClick: () => setActiveTab('other') }}>
56
+ Other
57
+ </Tabs.Item>
58
+ </Tabs>
59
+ ```
60
+
61
+ ### Section (`src/components/section/Section.tsx`)
62
+
63
+ The contextual panel on the right side is full of these little collapsible widget blocks - an H3 heading with a chevron, a sub-heading, a pile of pill-checkboxes, a "load more" button. That is `Section` with `collapsible={true}`. It already handles the expand/collapse chevron internally, it already takes a `buttonText` for the heading area, it already takes `headingLevel`.
64
+
65
+ ```tsx
66
+ <Section
67
+ title="Demographics"
68
+ headingLevel={3}
69
+ collapsible
70
+ />
71
+ ```
72
+
73
+ Put your pill-checkboxes in the children. Put your "load more" button in the children too - the `buttonText` prop on Section puts a button in the *heading* area, so for a button centered below the pill list, just drop a `<Button variant="tertiary">` in the children. No modifications needed.
74
+
75
+ One thing worth knowing: `Section`'s `collapsed` prop is an **initial value**, not a controlled prop. The component goes uncontrolled after mount. If you need to programmatically collapse all sections from a parent button, that's currently not supported. File a ticket if it comes up.
76
+
77
+ ### Heading (`src/components/heading/Heading.tsx`)
78
+
79
+ The page H1, the H3 widget headings, the H4 sub-headings. All map to `Heading` with `level` prop set to 1, 3, or 4 respectively. Use `Heading.InnerContainer` for left/right layout within a heading. You're welcome.
80
+
81
+ ### Pill (`src/components/pill/Pill.tsx`)
82
+
83
+ Picture it: a filter panel in Sicily. Actually no, there were no filter panels in Sicily. But IF there had been, they would have looked exactly like these pill-checkboxes. Little rounded things you can click, with a checkbox inside, that toggle between selected (green border, green text) and unselected (white background, grey border).
84
+
85
+ This is `Pill` with `checkbox={true}`. The component handles its own checked/unchecked state internally, fires an `onclick` callback when state changes, and accepts `initialValue` to start checked. Green selected state maps to `ds-pill__checked`, white unselected maps to `ds-pill__unchecked`. It is an exact match.
86
+
87
+ ```tsx
88
+ <Pill text="SEN Monitoring" checkbox onclick={(checked) => handleFilterChange('sen', checked)} />
89
+ <Pill text="PP" checkbox onclick={(checked) => handleFilterChange('pp', checked)} />
90
+ ```
91
+
92
+ One mild annoyance: the callback prop is `onclick` with a lowercase 'c', which is not how React normally does things. It's not wrong, it just looks wrong. Don't lose sleep over it, but maybe file a consistency cleanup ticket when you have five minutes.
93
+
94
+ ### SelectDropdown (`src/components/formField/inputs/selectDropdown/SelectDropdown.tsx`)
95
+
96
+ The "Data Collection" and "Visibility" columns in the column builder are select dropdowns. `SelectDropdown` takes `options` (array of `{value, label}` objects), `onSelectionChange`, `placeholder`, and optionally `multiple`. Exactly what you need.
97
+
98
+ ```tsx
99
+ <SelectDropdown
100
+ placeholder="Select"
101
+ options={[{ value: 'teacher', label: 'Teacher' }, { value: 'admin', label: 'Admin' }]}
102
+ onSelectionChange={(values) => handleVisibilityChange(rowId, values)}
103
+ />
104
+ ```
105
+
106
+ ### CheckboxInput (`src/components/formField/inputs/checkbox/CheckboxInput.tsx`)
107
+
108
+ Used in two places in this design:
109
+
110
+ 1. **Inside Pill** - you don't touch this directly, Pill handles it
111
+ 2. **The "Editable" column** in the builder table - just a standalone checkbox per row
112
+ 3. **The bulk-select header checkbox** - the one next to "Marksheet Columns" with a little chevron-down for "select all" options
113
+
114
+ For the bulk-select header, use `CheckboxInput` with `indeterminate` prop - this handles the partial-selection state (some but not all rows selected). Combine it with a `Dropdown` for the "select all / select none" chevron options.
115
+
116
+ ### Dropdown (`src/components/dropdown/Dropdown.tsx`)
117
+
118
+ The contextual panel's "menu button" (secondary button with text and an expand icon) triggers a dropdown. `Dropdown` is a Radix UI wrapper. You need `Dropdown.Trigger` wrapping a `Button`, `Dropdown.Content`, and as many `Dropdown.Item` children as you have menu options.
119
+
120
+ ### Icon (`src/components/icon/Icon.tsx`)
121
+
122
+ The design is full of icons. Let me check them off:
123
+
124
+ - `chevron-down`, `chevron-up` - yes, we have these (ChevronDown, ChevronUp from Lucide)
125
+ - `search` - yes
126
+ - `grip-vertical` - yes (GripVertical) - this is your drag handle icon
127
+ - `list`, `layout-list` - yes (List, LayoutList)
128
+ - `info` - yes
129
+ - `copy` - yes - for the breadcrumb copy button
130
+ - `x` - yes - for the search clear button
131
+
132
+ The bespoke navigation icons (side menu hamburger, the star/favourite, calendar, notification bell, emergency alert triangle, help circle) that live in the side nav are Figma design assets specific to the Arbor app shell. They are NOT our icon library's problem. The consuming application handles those.
133
+
134
+ ### Table + AG Grid (`src/components/table/Table.tsx`)
135
+
136
+ I owe you an apology and a correction in the same breath, so here it is.
137
+
138
+ The column builder table in the main content area — rows, columns, headers, a drag handle, checkboxes in cells, select dropdowns in cells — that is a table. It looks like a table. It walks like a table. It has a header row and data rows. It IS a table. Use our `Table` component, which wraps AG Grid Enterprise.
139
+
140
+ My earlier draft said to build a custom list with `dnd-kit`. That was wrong. I was embarrassed. I got over it. Here is the correct answer:
141
+
142
+ **Use `Table` with:**
143
+
144
+ 1. **AG Grid row dragging** - no third-party DnD library needed. AG Grid handles this natively. Set `rowDragManaged={true}` on the `Table`, and add `rowDrag: true` to the "Marksheet Columns" column definition. The `GripVertical` icon in the design is just the drag handle affordance — AG Grid renders this automatically when row drag is configured. Done. No `dnd-kit`. No drama.
145
+
146
+ 2. **A custom theme** - the builder table has a different visual style from a standard data table (white card background, tighter custom row layout, no standard AG Grid chrome). Pass a custom theme via the `tableTheme` prop, or build a new Sass theme alongside the existing `defaultTheme` and `tidyTheme` in `src/components/table/theme/`.
147
+
148
+ 3. **Custom cell renderers** for every non-standard column:
149
+ - **Marksheet Columns cell**: a `cellRenderer` that renders a text label. AG Grid handles the row drag handle icon automatically when `rowDrag: true` is on this column.
150
+ - **Data Collection + Visibility cells**: a `cellRenderer` wrapping `SelectDropdown` — the pattern is already established with `Table.SelectDropdownCellRenderer` in `src/components/table/cellRenderers/SelectDropdownCellRenderer.tsx`. Use or extend that.
151
+ - **Editable cell**: a `cellRenderer` wrapping `CheckboxInput`. Build a small `CheckboxCellRenderer` component in `src/components/table/cellRenderers/` — this is a tiny file, ten lines.
152
+ - **Formatting cell**: a `cellRenderer` placeholder for the future formatting picker. Render a disabled button or empty cell for now.
153
+ - **Delete cell**: a `cellRenderer` wrapping a `Button` — `Table.ButtonCellRenderer` already exists for exactly this pattern.
154
+
155
+ 4. **The header row bulk-select checkbox**: use AG Grid's built-in `headerCheckboxSelection` on the Marksheet Columns column definition, plus `checkboxSelection: true` on the same column. AG Grid gives you indeterminate state for free. You do NOT need to wire up `CheckboxInput` + `Dropdown` manually for this — AG Grid's header checkbox handles it. If you want the chevron-dropdown "select all / select none" options next to it, that is a custom `headerComponent` on the column definition.
156
+
157
+ The column structure maps to AG Grid `columnDefs` like so:
158
+
159
+ ```tsx
160
+ const columnDefs = [
161
+ {
162
+ field: 'name',
163
+ headerName: 'Marksheet Columns',
164
+ width: 238,
165
+ rowDrag: true,
166
+ checkboxSelection: true,
167
+ headerCheckboxSelection: true,
168
+ cellRenderer: MarksheetColumnCellRenderer,
169
+ },
170
+ {
171
+ field: 'dataCollection',
172
+ headerName: 'Data Collection',
173
+ flex: 1,
174
+ cellRenderer: Table.SelectDropdownCellRenderer, // or extend it
175
+ },
176
+ {
177
+ field: 'visibility',
178
+ headerName: 'Visibility',
179
+ flex: 1,
180
+ cellRenderer: Table.SelectDropdownCellRenderer,
181
+ },
182
+ {
183
+ field: 'editable',
184
+ headerName: 'Editable',
185
+ width: 60,
186
+ cellRenderer: CheckboxCellRenderer, // new, tiny, build it
187
+ },
188
+ {
189
+ field: 'formatting',
190
+ headerName: 'Formatting',
191
+ width: 111,
192
+ cellRenderer: FormattingCellRenderer, // placeholder for now
193
+ },
194
+ {
195
+ field: 'actions',
196
+ headerName: '',
197
+ cellRenderer: Table.ButtonCellRenderer,
198
+ },
199
+ ];
200
+
201
+ <Table
202
+ rowData={columnData}
203
+ columnDefs={columnDefs}
204
+ rowDragManaged
205
+ hasSearch={false}
206
+ tableTheme="builder" // custom theme you'll need to create
207
+ />
208
+ ```
209
+
210
+ This is application-level work, but it is structured application-level work using tools we already have. The effort is **Medium**, not High, and it is not reinventing anything.
211
+
212
+ ---
213
+
214
+ ## Section 2: Almost There — Minor Issues or Gotchas
215
+
216
+ ### SearchBar (`src/components/searchBar/SearchBar.tsx`)
217
+
218
+ Here is the situation, and it is mildly annoying: `SearchBar` EXISTS. It is a real file. It does exactly what this design needs - there is one in the top navigation and one in the contextual panel. But somebody forgot to put it in `src/index.ts`.
219
+
220
+ It is not exported. Consumers cannot use it.
221
+
222
+ **The fix:** Add one line to `src/index.ts`. That is it. Someone do this, it is five minutes of work.
223
+
224
+ ```ts
225
+ export { SearchBar } from 'Components/searchBar/SearchBar';
226
+ ```
227
+
228
+ **One behavioral note:** The Figma shows the contextual panel search bar always visible - not the "click the icon to expand" pattern. `SearchBar` in its current form starts collapsed unless you pass `searchValue`. In the "inactive with placeholder" state, you pass `placeholderText` to get a button that looks like a search field. This is close enough but may need minor styling work to match the design exactly.
229
+
230
+ ### Section — It Has Only One Header Button Slot
231
+
232
+ `Section` supports one button in the heading area via `buttonText`. The design uses this appropriately for the most part. But some widget sections have a sub-heading row (like "Demographics" in bold at 13px) that sits below the main collapsible heading and above the pill list.
233
+
234
+ That sub-heading is just content inside `children`. Compose it yourself:
235
+
236
+ ```tsx
237
+ <Section title="Filters" headingLevel={3} collapsible>
238
+ <p className="my-widget-sub-heading">Demographics</p>
239
+ <div className="my-pill-list">
240
+ <Pill text="SEN Monitoring" checkbox />
241
+ {/* ... */}
242
+ </div>
243
+ <Button variant="tertiary" size="S">Load More</Button>
244
+ </Section>
245
+ ```
246
+
247
+ No library changes needed. Just compose it.
248
+
249
+ ### Section — Controlled Collapse Not Supported
250
+
251
+ The `collapsed` prop on `Section` sets the initial state. After that, the component is uncontrolled. If the consuming page needs a "collapse all" or "expand all" control, the current API does not support it. This may or may not matter for this design - assess with the product team. If it matters, it needs a library ticket and a new `isCollapsed` controlled prop.
252
+
253
+ ---
254
+
255
+ ## Section 3: Build It From Scratch — These Don't Exist Yet
256
+
257
+ ### App Shell Navigation (Top Nav + Side Nav)
258
+
259
+ I'll keep this short because it doesn't need much: the top navigation bar and the icon-only side navigation are the Arbor application shell. They live in the consuming application. They are not the component library's job. Move on.
260
+
261
+ ### Breadcrumbs
262
+
263
+ Picture it: Sicily, 1954. My mother would leave a trail of breadcrumbs across the kitchen floor so she could remember where she'd been. We called this "making a mess." In the digital world, they call it UX. Same thing.
264
+
265
+ There is no `Breadcrumbs` component in this library. The design shows the classic pattern: one or more ancestor links (grey text), a " /" separator, the current page label (bold, dark), and a copy-to-clipboard icon button.
266
+
267
+ **To build:** It is not complicated. Compose from:
268
+ - Anchor tags or `Button` with `variant="text-link"` for the ancestor links
269
+ - A plain `<span>` with " /" for the separator
270
+ - Bold text for the current page
271
+ - `Button` with an icon for the copy action
272
+
273
+ If breadcrumbs are needed in multiple places across the product, scaffold a proper `Breadcrumbs` component with `yarn create-component Breadcrumbs`. If it is only needed here, inline it. Effort: **Low to Medium**.
274
+
275
+ ### Formatting Column Picker
276
+
277
+ The "Formatting" column in the builder table shows a placeholder (`visible=non-existant` is literally what the Figma node says, bless). This appears to be a future feature for configuring number formatting, percentages, etc. It is highly domain-specific to marksheets. Build it in the application when the requirements are clear. Not a library concern.
278
+
279
+ ### Contextual Panel Container
280
+
281
+ The right-hand contextual panel is a fixed-width (~382px) sidebar that sits to the right of the main content. It has:
282
+ - A sticky/scrollable container with padding
283
+ - A search bar + dropdown button at the top
284
+ - A stack of collapsible `Section` widgets below
285
+
286
+ There is no "ContextualPanel" or "SidebarPanel" component in the library. The **contents** are all existing components. The **container** is layout CSS that the consuming page provides.
287
+
288
+ This is low complexity. It is a `div` with `position: fixed` or similar, a defined width, overflow-y scroll, and the right background color. The `Section`, `Pill`, `Button`, and `SearchBar` components do all the work inside it. Effort: **Low**.
289
+
290
+ ### Notification Badge
291
+
292
+ The notifications icon in the side nav shows a little red badge with the number 6. There is no `Badge` component in this library. This is an application shell concern, not a library concern. If a `Badge` is eventually needed broadly, scaffold one. For now, it is the consuming app's problem.
293
+
294
+ ---
295
+
296
+ ## Section 4: Tricky Bits — Pay Attention Here
297
+
298
+ ### 1. Drag-and-Drop Row Ordering
299
+
300
+ The column builder rows have a `GripVertical` icon (grab handle). Users can drag rows to reorder columns.
301
+
302
+ Good news: this is handled entirely by AG Grid. Set `rowDragManaged={true}` on the `Table` and `rowDrag: true` on the Marksheet Columns `ColDef`. AG Grid renders the drag handle, manages the drag interaction, and fires `onRowDragEnd` with the new row order when the user drops. No third-party DnD library. No `dnd-kit`. We already own AG Grid Enterprise — use what we paid for.
303
+
304
+ The one thing to watch: `rowDragManaged` works well when the row data lives in AG Grid's internal state. If your row data is managed externally (e.g., in a server-synced state store), use `onRowDragEnd` to read `api.getDisplayedRowAtIndex()` for the new order and update your external state accordingly.
305
+
306
+ ### 2. Pill Filter State Management
307
+
308
+ Each contextual widget section shows some pill-checkboxes. The `Pill` component is uncontrolled - it manages its own checked state. This is fine when pills act independently. But if you need to:
309
+ - Reset all filters
310
+ - Reflect server-side state
311
+ - Drive selections from URL params
312
+
313
+ ...then you have a problem. `Pill`'s `initialValue` only sets the initial state, not controlled state. You may need to use `key` prop tricks to force re-initialization, or manage state externally and use a custom pill-like element built from `CheckboxInput`. Assess your requirements before you commit.
314
+
315
+ ### 3. Bulk-Select Header Checkbox
316
+
317
+ The "Marksheet Columns" header has a checkbox with indeterminate state (some rows selected, not all) and a chevron-down for "select all / select none" options.
318
+
319
+ The checkbox itself: use AG Grid's `headerCheckboxSelection: true` on the column definition. AG Grid gives you checked, unchecked, and indeterminate states for free, wired to the row selection model. Do NOT manually build this with `CheckboxInput` — AG Grid already does it.
320
+
321
+ The chevron dropdown next to it: this is a custom `headerComponent` on the column definition. You can render a `Dropdown` with `Dropdown.Trigger` / `Dropdown.Content` inside the custom header. It is a small amount of custom code but it is clean and isolated.
322
+
323
+ ### 4. Programmatic Expand/Collapse of Sections
324
+
325
+ If the design ever needs a "collapse all" or "expand all" control for the contextual panel widgets, `Section` currently does not support this. The `collapsed` prop is initial-value-only. If this comes up in user testing or requirements refinement, file a ticket for a controlled `isCollapsed` prop on `Section`.
326
+
327
+ ### 5. Always-Visible Search in Contextual Panel
328
+
329
+ `SearchBar` starts in a collapsed/inactive state and expands when clicked. The Figma shows the contextual panel search always expanded. Using `SearchBar` here works if you accept the expand-on-first-click behavior, or you can use a `TextInput` with a `search` icon wrapper if you need it always open. Clarify with design which behavior is intended.
330
+
331
+ ---
332
+
333
+ ## Section 5: The Verdict
334
+
335
+ Picture it: Sicily, 1967. An architect came to our village and told us we needed to tear down half the buildings to build the community center we wanted. My mother looked at him and said, "Sir, we have everything we need already. We just need someone to put it together properly and also build that one new room in the back." He was very embarrassed.
336
+
337
+ That is this design.
338
+
339
+ ### Is It Buildable With Our Existing Components?
340
+
341
+ **Yes. Absolutely yes. High confidence.**
342
+
343
+ The individual UI elements are almost entirely covered by what we already have:
344
+
345
+ | Design Element | Library Component | Status |
346
+ |---|---|---|
347
+ | Primary / Secondary / Tertiary buttons | `Button` (variant prop) | Ready |
348
+ | Tab bar with active green underline | `Tabs` + `Tabs.Item` | Ready |
349
+ | Collapsible widget sections | `Section` (collapsible mode) | Ready |
350
+ | Page / widget / sub headings | `Heading` (levels 1, 3, 4) | Ready |
351
+ | Pill-checkbox filter options | `Pill` (checkbox={true}) | Ready |
352
+ | Column builder table structure | `Table` (AG Grid) | Ready — needs custom theme + cell renderers |
353
+ | Row drag-and-drop reordering | `Table` with `rowDragManaged` | Ready — AG Grid built-in, no extra library |
354
+ | Data Collection + Visibility cells | `Table.SelectDropdownCellRenderer` (extend) | Ready |
355
+ | Editable column cell | New `CheckboxCellRenderer` (tiny) | Needs building |
356
+ | Delete column cell | `Table.ButtonCellRenderer` | Ready |
357
+ | Bulk-select header checkbox | AG Grid `headerCheckboxSelection` | Ready — AG Grid built-in |
358
+ | Search (contextual panel + top nav) | `SearchBar` | Exists, just not exported |
359
+ | Dropdown menus | `Dropdown` | Ready |
360
+ | All design system icons | `Icon` | Ready |
361
+ | App shell navigation | Not library scope | N/A |
362
+
363
+ ### What Actually Needs Work
364
+
365
+ | Item | Where It Lives | Effort |
366
+ |---|---|---|
367
+ | Export `SearchBar` from `src/index.ts` | Library | Trivial (one line) |
368
+ | Breadcrumbs component | Library (new) or app inline | Low-Medium |
369
+ | Contextual panel layout container | Consuming application | Low |
370
+ | Column builder table (custom theme + cell renderers) | Application + light library work | Medium |
371
+ | `CheckboxCellRenderer` for Editable column | Library (`src/components/table/cellRenderers/`) | Low (tiny component) |
372
+ | Formatting column picker | Consuming application | High (domain-specific, future feature) |
373
+ | Notification badge on nav icon | Consuming app (shell) | Low |
374
+
375
+ ### Summary Verdict
376
+
377
+ The library is in excellent shape for this design. The component-level gaps are minimal: one missing export (`SearchBar`), one potentially new `Breadcrumbs` component, and one tiny new cell renderer (`CheckboxCellRenderer`). Everything else is already here.
378
+
379
+ The column builder table — which I originally and wrongly wrote off as a custom DnD list requiring a separate library — is properly an AG Grid table using our existing `Table` component. Row dragging is built into AG Grid. Cell renderers for the select dropdowns are already partially established. The remaining work is a custom theme and a handful of cell renderer components, which is **Medium effort**, not High.
380
+
381
+ The contextual panel is all existing components in a layout container. The state management for filter selections and section collapse is application logic, not library gaps.
382
+
383
+ This is a feature build with a well-stocked toolbox. Unlike Rocco with the gate — we have the materials AND we know what bus to take.
384
+
385
+ Go build it. But not without reading Section 4 first.
386
+
387
+ ---
388
+
389
+ ## Appendix: Files Referenced in This Assessment
390
+
391
+ Every file I looked at before forming these opinions:
392
+
393
+ - `/Users/angusfraser/Projects/design-system.components/src/components/button/Button.tsx`
394
+ - `/Users/angusfraser/Projects/design-system.components/src/components/tabs/Tabs.tsx`
395
+ - `/Users/angusfraser/Projects/design-system.components/src/components/tabs/TabsItem.tsx`
396
+ - `/Users/angusfraser/Projects/design-system.components/src/components/section/Section.tsx`
397
+ - `/Users/angusfraser/Projects/design-system.components/src/components/heading/Heading.tsx`
398
+ - `/Users/angusfraser/Projects/design-system.components/src/components/pill/Pill.tsx`
399
+ - `/Users/angusfraser/Projects/design-system.components/src/components/formField/inputs/selectDropdown/SelectDropdown.tsx`
400
+ - `/Users/angusfraser/Projects/design-system.components/src/components/formField/inputs/checkbox/CheckboxInput.tsx`
401
+ - `/Users/angusfraser/Projects/design-system.components/src/components/searchBar/SearchBar.tsx`
402
+ - `/Users/angusfraser/Projects/design-system.components/src/components/dropdown/Dropdown.tsx`
403
+ - `/Users/angusfraser/Projects/design-system.components/src/components/icon/allowedIcons.tsx`
404
+ - `/Users/angusfraser/Projects/design-system.components/src/index.ts`