Skip to content

List View Component Design

  • The target library for this project is the Canary maturity level. This means that the target directories by category are:

    • Components: src/components/canary
    • Types: src/types/canary
    • Styles: src/styles/canary
    • Visual Elements: src/visual-elements/canary
    • Full Sample Stories (ported from src/dev-witness): src/canary-refactor
  • When extracting components from the extras library:

    • Port any dependencies they have from other code in src/components/extras, src/types/extras, src/styles/extras or src/visual-elements/extras to their corresponding canary directories.
  1. Clone the stories under the directory ux-prototype/src/dev-witness/reference/items to a directory called: ux-prototype/src/canary-refactor/reference/items
  2. lint, build and test ux-prototype to ensure that the clone builds and renders.
  3. Update the vrt tests to compare the cloned stories with the original story. They should pass because there are no changes yet.
  4. Port all target components, including all unit tests.
  5. Run the lint, build and test commands (no vrt testing needed) to ensure that the port builds and tests successfully.
  6. Update the cloned stories to use the new canary components. Ensure that the vendored components are not used when a canary component exists.
  7. Run the vrt tests again. They should still pass as the port should be visually unchanged.
  8. If vrt tests don’t pass, report to user for further instructions.

This diagram shows the proposed Canary component hierarchy for the List View project. It covers all four in-scope tiers (1, 2, 3a, 3b) and shows Tier 4 (domain grids) for context. Decisions from the Landscape Analysis are reflected in the structure.

  • Tier 1 — Two cell variants per type: Display (read-only renderer) and Editor (AG Grid native cell editor). CellInteractive deferred to Entity Viewer project. Priority types: text, number, boolean, date, enum, memo (new), color (new).
  • Tier 2 — DataGrid molecule with the three-part config pattern (Static/Init/Runtime), minimal ref (getGridApi, exportDataAsCsv), internal SortMenuHeader sub-component, and AG Grid native row selection (headerCheckbox, selectionColumnDef)
  • Tier 3a — createEntityDataGrid() factory wrapping Tier 2, with entity-level config, model/view props, and the 3a additions (multi-sort, filtering, cell editing lifecycle, getRowClass)
  • Tier 3b — createEntityDataGridShim() wrapping 3a, adding vendored compatibility (row actions, double-click, hasActiveSearch, initialState, extended ref with selectAll/deselectAll/etc.)
  • Composable Hooks — useDirtyTracking() shown as optional composition with Tier 3a (Q9 decision)
  • Tier 4 — Domain grids shown greyed out (out of scope)
  • Cross-tier — DataGrid renders cell Display/Editor atoms via columnDefs
  • ArdaDataGrid -> DataGrid (drop Arda prefix)
  • createArdaEntityDataGrid -> createEntityDataGrid (drop Arda prefix)
  • createFullEntityDataGrid -> createEntityDataGridShim (new name for Tier 3b vendored compatibility layer)

PlantUML diagram

PlantUML diagram

The table below lists every component, type, utility, hook, and style that must be ported to canary, along with its dependencies. Items already in canary (cn, getBrowserTimezone, getTimezoneAbbreviation) are marked with a dagger (*).

ComponentSource LocationDependencies
Shared — Types & Utilities
PaginationDatatypes/extras/model/general/pagination.tsNone
formatterscomponents/extras/atoms/shared/formatters.tsNone (pure functions)
cn*types/canary/utils.tsclsx, tailwind-merge (npm)
getBrowserTimezone*types/canary/date-time.tsNone
getTimezoneAbbreviation*types/canary/date-time.tsNone
Shared — Hooks
useColumnPersistencecomponents/extras/molecules/data-grid/use-column-persistence.tsag-grid-community (types only)
useDirtyTracking<T>NewNone (pure React state)
Shared — Styles
ag-theme-arda.cssstyles/ag-theme-arda.cssAG Grid CSS variables
Tier 1 — Cell Atoms (per type: text, number, boolean, date, enum)
{Type}CellDisplaycomponents/extras/atoms/grid/{type}/formatters (type-specific formatter); date also uses getBrowserTimezone*
{Type}CellEditorcomponents/extras/atoms/grid/{type}/cn*; date also uses toDateInputValue (formatter), getBrowserTimezone*, getTimezoneAbbreviation*
Tier 1 — New Cell Types (from completeness assessment)
MemoCellDisplayNewcn*, @radix-ui/react-tooltip (already installed). Truncates text with ellipsis; shows full text in Radix Tooltip overlay after configurable delay.
MemoCellEditorNewcn*. Multi-line textarea for AG Grid cell editing. Based on vendored NoteModal textarea pattern (min-height 100px, resizable).
MemoButtonCellNew (based on vendored NotesCell)cn*, lucide-react, createPortal. Icon button + modal overlay for viewing/editing long text. Accepts onSave?(value: string) => void callback. Framework-agnostic.
createMemoButtonCellEditor()NewMemoButtonCell. Factory wrapping MemoButtonCell as AG Grid cell editor with getValue() ref handle. Follows create{Type}CellEditor pattern.
ColorCellDisplayNew (based on vendored color renderer)cn*. Renders color swatch (4x4px rounded square, backgroundColor: hex) + label. Based on vendored columnPresets.tsx pattern.
ColorCellEditorNew (based on vendored ColorCellEditor)cn*, shadcn/Radix Select or Popover. React forwardRef replacing vendored raw-DOM class-based editor. Configurable colors: ColorOption[] prop. Default: vendored 10-color map.
Tier 1 — Special
ActionCellRenderer<T>New (Tier 3b)cn*, RowAction<T> type, lucide-react (icons)
Tier 2 — Molecule
SortMenuHeaderInternal to vendored/arda-frontend/components/table/ArdaGrid.tsx (lines 153-257)ag-grid-community (IHeaderParams), createPortal (React DOM). CSS: .arda-sort-* classes from ag-theme-arda.css.
DataGrid<T>components/extras/molecules/data-grid/data-grid.tsxag-grid-react, ag-grid-community, ag-theme-arda.css, PaginationData, useColumnPersistence, SortMenuHeader. Native rowSelection config for header checkbox.
Tier 3a — Organism Base
createEntityDataGrid<T>()components/extras/organisms/shared/entity-data-grid/ (export renamed from createArdaEntityDataGrid)DataGrid<T>, PaginationData, ag-grid-community (types: ColDef)
Tier 3b — Organism Full
createEntityDataGridShim<T>()New (wraps Tier 3a)createEntityDataGrid<T>(), ActionCellRenderer<T>, RowAction<T>, ag-grid-community (types: GridState)

PlantUML diagram

ComponentRecommended ActionDecision
Shared — Types & Utilities
PaginationDataCopy from types/extras/model/general/pagination.ts to types/canary/pagination.ts. Pure interface, no changes needed.
formattersCopy from extras/atoms/shared/formatters.ts to canary/atoms/shared/formatters.ts. Pure functions, no changes needed.Correct
cn*Already in canary (types/canary/utils.ts). No action.Correct
getBrowserTimezone*Already in canary (types/canary/date-time.ts). No action.Correct
getTimezoneAbbreviation*Already in canary (types/canary/date-time.ts). No action.Correct
Shared — Hooks
useColumnPersistencePort from extras/molecules/data-grid/use-column-persistence.ts to canary/molecules/data-grid/use-column-persistence.ts. Update AG Grid type imports (remain ag-grid-community, no change needed).Correct
useDirtyTracking<T>Create new in canary/organisms/shared/entity-data-grid/use-dirty-tracking.ts. Extract dirty tracking logic (clone row data, track per-cell edits, save/discard drafts) from existing create-entity-data-grid.tsx into a standalone composable hook.Correct
Shared — Styles
ag-theme-arda.cssCopy from styles/ag-theme-arda.css to styles/canary/ag-theme-arda.css. Verify AG Grid CSS variable references resolve correctly.Correct
Tier 1 — Cell Atoms (per type: text, number, boolean, date, enum)
{Type}CellDisplayCopy from extras/atoms/grid/{type}/ to canary/atoms/grid/{type}/. Update imports: @/components/extras/atoms/shared/formatters to @/components/canary/atoms/shared/formatters. Date type also update getBrowserTimezone import (already canary, no change).Correct
{Type}CellEditorCopy from extras/atoms/grid/{type}/ to canary/atoms/grid/{type}/. Update imports: cn (already canary, no change). Date type also update formatter and timezone imports.Correct
Tier 1 — New Cell Types (from completeness assessment)
MemoCellDisplayCreate new in canary/atoms/grid/memo/. Truncates text with ellipsis. On hover, shows full text in Radix Tooltip overlay after configurable delay (per Q12 decision).
MemoCellEditorCreate new in canary/atoms/grid/memo/. Multi-line textarea for AG Grid cell editing. Draw from vendored NoteModal textarea pattern: min-height 100px, resizable, placeholder “Add a note…”. Implements getValue() ref handle.
MemoButtonCellCreate new in canary/atoms/grid/memo/. Based on vendored NotesCell (~90 lines in columnPresets.tsx): icon button (chat bubble or dash) that opens a modal overlay for viewing/editing. Modal based on vendored NoteModal pattern (500px centered, textarea, Done/Close). Accepts onSave?(value: string) => void callback — framework-agnostic, no AG Grid coupling.
createMemoButtonCellEditor()Create new factory in canary/atoms/grid/memo/. Wraps MemoButtonCell as an AG Grid-compatible cell editor with getValue() ref handle and stopEditing integration. Follows existing create{Type}CellEditor(config) pattern.
ColorCellDisplayCreate new in canary/atoms/grid/color/. Based on vendored color renderer in columnPresets.tsx: 4x4px rounded swatch (backgroundColor: hex) + label text. Vendored 10-color hex map as default. Fallback to gray for unmapped values.
ColorCellEditorCreate new in canary/atoms/grid/color/. React forwardRef replacing vendored raw-DOM class-based ColorCellEditor. Uses shadcn/Radix Select or Popover for dropdown. Configurable colors: ColorOption[] prop with vendored 10-color map as default. Implements getValue() ref handle.
Tier 1 — Special
ActionCellRenderer<T>Create new in canary/atoms/grid/action/action-cell-renderer.tsx. Implements pinned-right column with dropdown menu. Depends on RowAction<T> type (defined alongside), cn*, lucide-react.Correct
Tier 2 — Molecule
SortMenuHeaderPort from vendored/arda-frontend/components/table/ArdaGrid.tsx (lines 153-257) to canary/molecules/data-grid/sort-menu-header.tsx. Internal sub-component of DataGrid<T>, not exported independently.
DataGrid<T>Copy from extras/molecules/data-grid/data-grid.tsx to canary/molecules/data-grid/data-grid.tsx. Update imports: PaginationData from types/canary/pagination, useColumnPersistence from canary path, ag-theme-arda.css from styles/canary/. Add SortMenuHeader as internal defaultColDef.headerComponent. Configure AG Grid native rowSelection with headerCheckbox: true and selectionColumnDef (replaces vendored SelectAllHeaderComponent). Keep ag-grid-react and ag-grid-community imports unchanged.
Tier 3a — Organism Base
createEntityDataGrid<T>()Copy from extras/organisms/shared/entity-data-grid/create-entity-data-grid.tsx. Rename from createArdaEntityDataGrid to createEntityDataGrid (drop Arda prefix per canary naming — the @arda-cards/design-system package scope makes the prefix redundant). Update imports: DataGrid<T> from canary Tier 2, PaginationData from canary types. Extract dirty tracking into useDirtyTracking hook (optional composition). Add Tier 3a features: enableMultiSort/onSortChanged, enableFiltering/onFilterChanged, onCellEditingStarted/onCellEditingStopped/onCellFocused, getRowClass.
Tier 3b — Organism Full
createEntityDataGridShim<T>()Create new wrapping createEntityDataGrid<T>(). Name: createEntityDataGridShim (vendored ArdaGrid compatibility layer). Add Tier 3b features: enableRowActions/rowActions (via ActionCellRenderer), onRowDoubleClicked, hasActiveSearch, initialState: GridState, extended ref (refreshData, getSelectedRows, selectAll, deselectAll).

The porting strategy needs to enable incremental testing of the ported components. The waves below correspond to Step 4 of the General Workflow. Steps 1-3 (clone stories, VRT setup) happen before Wave 0. Steps 5-8 (lint/build/test, story swapping, VRT verification) happen after Wave 3b. Based on this the waves for porting should be:

Port types, utilities, and styles that have no component dependencies. These are leaf nodes in the dependency graph.

ItemSourceTargetNotes
PaginationDatatypes/extras/model/general/pagination.tstypes/canary/pagination.tsPure interface, no dependencies
formatterscomponents/extras/atoms/shared/formatters.tscomponents/canary/atoms/shared/formatters.tsPure functions, no dependencies
ag-theme-arda.cssstyles/ag-theme-arda.cssstyles/canary/ag-theme-arda.cssCopy; verify AG Grid CSS variable references

Verification:

  • tsc --noEmit passes. No visual output yet.
  • Add unit tests for each component if applicable, run the unit tests and verify that they pass.

Wave 1 — Tier 1 Cell Atoms (5 priority types)

Section titled “Wave 1 — Tier 1 Cell Atoms (5 priority types)”

Port 7 cell type directories: 5 ported from extras (text, number, boolean, date, enum) and 2 new (memo, color). Each type has 2 components (Display, Editor) plus an index barrel. CellInteractive is deferred to the Entity Viewer project (see Cell Editing Paths Analysis). For ported types, copy existing tests and stories, adapting imports and Storybook title paths to canary conventions. For new types (memo, color), create tests and stories from scratch.

ItemSourceTargetDependencies (from Wave 0)
TextCellDisplayextras/atoms/grid/text/canary/atoms/grid/text/formatters
TextCellEditorsamesamecn*
NumberCellDisplayextras/atoms/grid/number/canary/atoms/grid/number/formatters
NumberCellEditorsamesamecn*
BooleanCellDisplayextras/atoms/grid/boolean/canary/atoms/grid/boolean/formatters, lucide-react
BooleanCellEditorsamesamecn*
DateCellDisplayextras/atoms/grid/date/canary/atoms/grid/date/formatters, getBrowserTimezone*
DateCellEditorsamesamecn*, formatters, getBrowserTimezone*, getTimezoneAbbreviation*
EnumCellDisplayextras/atoms/grid/enum/canary/atoms/grid/enum/None
EnumCellEditorsamesamecn*
MemoCellDisplayNewcanary/atoms/grid/memo/cn*, @radix-ui/react-tooltip (already installed)
MemoCellEditorNewsamecn*
MemoButtonCellNew (from vendored NotesCell)samecn*, lucide-react, createPortal
createMemoButtonCellEditor()NewsameMemoButtonCell
ColorCellDisplayNew (from vendored renderer)canary/atoms/grid/color/cn*
ColorCellEditorNew (from vendored editor)samecn*, shadcn/Radix primitives

Verification: Unit tests for each cell type. Storybook stories render in Components/Canary/Atoms/Grid/{Type}.

Port the core AG Grid wrapper and its column persistence hook. Copy existing tests and stories, adapting imports and Storybook title paths. Improve if appropriate.

ItemSourceTargetDependencies
useColumnPersistenceextras/molecules/data-grid/use-column-persistence.tscanary/molecules/data-grid/use-column-persistence.tsag-grid-community (types only)
SortMenuHeadervendored/arda-frontend/components/table/ArdaGrid.tsxcanary/molecules/data-grid/sort-menu-header.tsxag-grid-community (IHeaderParams), createPortal. CSS from ag-theme-arda.css (Wave 0).
DataGrid<T>extras/molecules/data-grid/data-grid.tsxcanary/molecules/data-grid/data-grid.tsxag-grid-react, ag-grid-community, ag-theme-arda.css (Wave 0), PaginationData (Wave 0), useColumnPersistence, SortMenuHeader. Native rowSelection config.

Persistence strategy: When the consuming application manages column visibility via its own state store (e.g., Redux), omit the persistenceKey prop and drive visibility entirely through EntityGridViewProps.columnVisibility. The useColumnPersistence hook activates only when persistenceKey is provided, so the two mechanisms cannot conflict. See Redux State Management Analysis for details.

Verification: Unit tests. Storybook stories render in Components/Canary/Molecules/DataGrid with loading, empty, error, pagination, selection, and cell editing states. ALL Cell atoms from Wave 1 should be used in column definitions to show rendering and editing in place in the grid. Story tests should be added if necessary.

Port the factory and the composable dirty tracking hook. Copy existing tests and stories, adapting imports and Storybook title paths. Improve if appropriate.

ItemSourceTargetDependencies
useDirtyTracking<T>New (extracted from extras entity-data-grid)canary/organisms/shared/entity-data-grid/use-dirty-tracking.tsNone (pure React state)
createEntityDataGrid<T>()extras/organisms/shared/entity-data-grid/canary/organisms/shared/entity-data-grid/DataGrid<T> (Wave 2), PaginationData (Wave 0), ag-grid-community (types). Add 3a features: multi-sort, filtering, cell editing lifecycle, getRowClass.

Verification: Unit tests. Storybook stories render in Components/Canary/Organisms/Shared/Entity Data Grid. Dirty tracking testable in isolation via hook tests.

Port the vendored-compatible wrapper and the action cell renderer.

ItemSourceTargetDependencies
ActionCellRenderer<T>Newcanary/atoms/grid/action/action-cell-renderer.tsxcn*, RowAction<T> type, lucide-react
createEntityDataGridShim<T>()New (wraps Tier 3a)canary/organisms/shared/entity-data-grid-shim/createEntityDataGrid<T>() (Wave 3a), ActionCellRenderer, ag-grid-community (types: GridState). Adds: row actions, double-click, hasActiveSearch, initialState, extended ref.

Verification: Unit tests. Storybook stories demonstrate vendored ArdaGrid feature parity. Clone dev-witness/reference/items stories to canary-refactor/reference/items and swap the grid component. VRT comparison should pass.

PlantUML diagram

The following items were identified during the Completeness Assessment as necessary for full migration of vendored components but outside the scope of this project. They are recorded here for future planning.

#ItemDescriptionSource
F1Typeahead Cell Editors9 domain-specific AG Grid cell editors (SupplierCellEditor, UnitCellEditor, TypeCellEditor, SubTypeCellEditor, UseCaseCellEditor, FacilityCellEditor, DepartmentCellEditor, LocationCellEditor, SublocationCellEditor). Each wraps a typeahead component with debounced API-backed search. Requires a generic TypeaheadCellEditor pattern in the component library, plus domain-specific API bindings.Completeness #2
F2QuickActionsCellDomain-specific cell renderer with order-queue, print, and preview buttons per row. Requires useItemCards context, kanban API integration, and order queue state.Completeness #4
F3Draft Lifecycle ManagementItems-specific editing orchestration: draft creation per row, batched API saves, auto-publish on row leave, row-level saving/error UI states. The generic useDirtyTracking<T>() hook covers dirty state; the domain-specific API integration and row-level UI need separate work.Completeness #8
F4Page Chrome ComponentsAppSidebar, AppHeader, SidebarProvider, tab navigation, search bar, column visibility dropdown. Application-shell components required for full page rendering but not grid-specific.Completeness #9
F5Remaining Cell Types5 additional cell types deferred from the priority subset: url, image, time, datetime, custom. To be added based on consumer demand.Analysis Q8
F6CellInteractive + Entity Viewer{Type}CellInteractive variants and GridCellProps<V> base type. Deferred until the Entity Viewer project is ported to canary.Cell Editing Paths Analysis

The following questions must be resolved before the implementation plan can be finalized.

#QuestionContextDecision
1Existing canary-refactor state — There is already a canary-refactor/reference/items/item-detail.stories.tsx with forked components (ItemDetailPage, ItemDetailsPanel, ItemsPage). Should the implementation plan treat this as existing work to preserve and build on, or start fresh?The items-grid.stories.tsx from dev-witness has no canary-refactor counterpart yet.Remove existing canary-refactor/reference/items code and start fresh
2Story cloning scope — The General Workflow step 1 says clone dev-witness/reference/items. That directory has two stories: item-detail.stories.tsx and items-grid.stories.tsx. The item-detail story is about a detail panel (not a grid). Should the plan clone both, or only items-grid.stories.tsx since this project is about the List View component?The item-detail story uses the vendored ArdaGrid indirectly (the items page wraps it), so it would exercise the grid too.Both.
3Barrel exports — Should the ported canary components be added to src/canary.ts (the canary barrel) as they are completed per wave, or all at once at the end? The current canary.ts only has placeholder exports plus the ArdaDetailField atom.Affects whether waves are independently shippable.At the end. Placeholder exports and the placeholder components themselves should be removed
4ESLint boundary rules — The current ESLint config blocks stable code from importing canary. Are there rules blocking canary from importing extras? If so, the plan needs to ensure zero cross-references between canary and extras components (only canary-to-canary and canary-to-npm).Determines whether a “copy + update imports” approach works or if we need to verify ESLint passes per wave.Yes, neither canary nor stable can import from extras. Update the rules if necessary.
5Agent team execution — Is this plan intended for a single agent working sequentially through the waves, or for an agent team (via launch-team) with parallel workers per wave?Affects task granularity and whether the plan should include a worktree strategy section.Use the project-decomposition skill to decide. Record your rationale in the planning document.

The following ambiguities were identified during a consistency review of the design document.

#QuestionContextDecision
6Workflow vs Waves relationship — The General Workflow (steps 1-8) describes an end-to-end process: clone stories, VRT setup, port components, swap stories, VRT verify. The Porting Strategy defines Waves 0-3b for the porting step. It is not explicit that Waves 0-3b are substeps of Workflow step 4, or how Workflow steps 1-3 (pre-porting setup) and 5-8 (post-porting integration) map to the wave structure. Should the implementation plan treat the Workflow as the outer structure with waves nested inside step 4, or merge them into a single flat sequence?An agent executing this needs to know when to do VRT setup (step 3) and story swapping (step 6) relative to the waves.All waves are part of Step 4
7GridCellProps<V> missing onComplete and onCancel — The class diagram shows GridCellProps<V> with value, onChange, mode, errors, editable. But every extras Interactive cell uses onComplete (commit edit on blur/Enter) and onCancel (revert on Escape) from AtomProps<V>. If GridCellProps<V> omits these, Interactive cells lose their edit commit/cancel mechanism. Should onComplete and onCancel be added to GridCellProps<V>?These are grid-relevant fields used in every Interactive cell’s inline editor. Without them, the edit lifecycle is broken.Moot — GridCellProps<V> and CellInteractive are both deferred per Q8 decision.
8False dependency: Interactive -> Editor — The class diagram and Component Dependencies table show {Type}CellInteractive depending on {Type}CellEditor. In the actual source code, Interactive does NOT import Editor — it has its own internal inline editor function ({Type}CellInlineEditor). CellEditor is a separate component used by AG Grid’s native double-click-to-edit mechanism. These are two independent editing paths. Should the diagram and dependency table be corrected to remove the Interactive -> Editor dependency?Affects wave dependency ordering and the agent’s understanding of the component relationship. The current depiction would cause an agent to believe Editor must be ported before Interactive, which is not true.CellInteractive has no consumer in this project — defer to Entity Viewer project. Remove from scope, diagram, and dependency table. See Cell Editing Paths Analysis.
9PaginationData path flattening — Source is types/extras/model/general/pagination.ts (nested under model/general/). Target is types/canary/pagination.ts (flat). The nesting is dropped without explanation. Is the flat target path intentional?An agent replicating the extras structure would create types/canary/model/general/pagination.ts unless told otherwise.Flatten the directory structure
10Test and story porting — The design says “Port all target components, including all unit tests” (Workflow step 4) and Wave verifications mention “Unit tests” and “Storybook stories”. But neither the Porting Guidelines nor Wave tables mention the existing {type}.test.tsx and {type}.stories.tsx files. Should the plan: (a) copy existing tests and stories, updating imports and Storybook title paths, or (b) write new tests and stories from scratch?Each extras cell type has a .test.tsx and .stories.tsx file. Copying and adapting is faster. Writing from scratch ensures canary conventions are followed.Copy, adapt and improve if appropriate
11Q7 formal resolution — The analysis Decision Summary shows Q7 (AtomProps<V> dependency for cell atoms) with no decision recorded. But the design uses GridCellProps<V> (the Q7 recommendation) and marks it “Correct” in Porting Guidelines. Should Q7 be formally resolved in the analysis document before the implementation plan is created?Without formal resolution, the implementation plan references an unresolved decision.Done. Q7 resolved as “Deferred” in analysis Decision Summary.

The following questions arise from the scope additions based on the Completeness Assessment decisions.

#QuestionContextDecision
12Memo cell hover overlay implementation — The MemoCellDisplay should show full text in a hover overlay after a configurable delay. Should this use: (a) a Radix UI Tooltip/HoverCard primitive (consistent with shadcn/ui patterns), (b) a custom portal-based overlay (like SortMenuHeader), or (c) AG Grid’s built-in tooltipValueGetter + tooltipComponent?AG Grid has native tooltip support that avoids rendering custom React components for hover. But its styling is limited. Radix gives full control but adds a dependency.Option (a)
13Color palette configuration — The ColorCellEditor needs a predefined color palette. Should the palette be: (a) hardcoded in the component (the vendored implementation has 10 colors: RED, GREEN, BLUE, YELLOW, ORANGE, PURPLE, PINK, GRAY, BLACK, WHITE), (b) configurable via a StaticConfig prop (colors: ColorOption[]), or (c) both (default palette + override)?The vendored implementation hardcodes 10 colors with hex values and labels. A configurable palette is more reusable but adds API surface.Configurable. The Hosting Story should provide the colors to display. The default should be a continuos palette. O.K. to source it from ShadCN or Radix if available
14Native row selection shift-click validation — Decision #3 replaces SelectAllHeaderComponent with AG Grid native rowSelection config. The vendored component includes custom shift-click range selection logic (~40 lines). Does AG Grid’s native enableClickSelection support shift-click range selection out of the box, or is this a gap that needs a follow-up?If AG Grid doesn’t support shift-click natively, the canary grid would have a behavioral regression. This should be validated before implementation, not discovered during VRT.Resolved — no gap. AG Grid Community natively supports shift-click range selection on checkboxes in multiRow mode. No Enterprise license needed. See Appendix A.
15Class diagram update — The class diagram still shows the pre-completeness-assessment state (no Memo, Color, SortMenuHeader). Should the diagram be updated now, or deferred to the implementation plan phase?Updating now keeps the design document self-consistent. Deferring avoids rework if more changes emerge.Update now.

The dev-witness stories (items-grid.stories.tsx and item-detail.stories.tsx) render the full vendored ItemsPage production page. This page is a large component (~1200 lines) that composes:

  • Grid layer: ItemTableAGGrid wrapping ArdaGrid with items-specific editing, draft management, and 30 column definitions
  • Page chrome: Sidebar, header, tabs, search bar, column visibility dropdown, action buttons
  • Panels: ItemDetailsPanel (read-only detail view), ItemFormPanel (add/edit form)
  • Modals: Cards preview, import, delete confirmation
  • Context: Auth, order queue, item cards, navigation blocking

The canary-refactor stories (per Workflow step 6) must swap vendored grid components for canary equivalents. The following table lists components/features required by the stories that are not covered by the canary porting scope (Waves 0-3b).

#Component / FeatureUsed ByWhy It’s NeededStatus in Canary Scope
1SortMenuHeader — Custom AG Grid header component with sort-direction indicator and portal dropdown menuArdaGrid (Tier 2 internal)Every sortable column uses this header. Without it, columns fall back to AG Grid’s default header (different visual appearance, VRT will fail).Not listed as a ported component. It’s an internal sub-component of the vendored ArdaGrid. Must be ported as part of the Tier 2 DataGrid<T> molecule or extracted as a separate atom.
29 Typeahead Cell EditorsSupplierCellEditor, UnitCellEditor, TypeCellEditor, SubTypeCellEditor, UseCaseCellEditor, FacilityCellEditor, DepartmentCellEditor, LocationCellEditor, SublocationCellEditorItemTableAGGridRequired for in-table cell editing. All use the vendored *Typeahead components with API-backed search.Not in scope. These are domain-specific (Tier 4) editors. The canary Tier 1 cell editors (text, number, boolean, date, enum) don’t cover typeahead-based editors.
3SelectAllHeaderComponent — Custom header checkbox with tri-state (all/some/none) and shift-click range selectionitemsColumnDefs (select column)Required for the row selection checkbox column. Without it, selection UX differs from vendored behavior.Not listed. It’s a custom cell renderer defined in columnPresets.tsx. Domain-specific (Tier 4).
4QuickActionsCell — Cell renderer with order-queue, print, and preview buttonsitemsColumnDefs (actions column)Requires useItemCards context, kanban API, order queue integration. Renders action buttons per row.Not in scope. Domain-specific renderer requiring production API context.
5CardCountCell — Lazy-loads kanban card count per itemitemsColumnDefs (card count column)Requires useItemCards context and lazy API fetch.Not in scope. Domain-specific.
6NotesCell / CardNotesCell — Icon button opening a modal for editing notesitemsColumnDefs (notes columns)Requires NoteModal component and save-to-API callbacks.Not in scope. Domain-specific.
7itemsColumnDefs and itemsDefaultColDef — 30 column definitions with custom renderers, formatters, editorsItemTableAGGridThe column configuration that wires all cell renderers and editors to the grid. Without it, the grid has no columns.Not in scope (Decision Q3: column presets located with domain stories, not in the library). Must be provided by the canary-refactor story layer.
8Draft lifecycle managementcreateDraftItem, updateItem API calls, dirty set tracking, batched saves, auto-publish on row leaveItemTableAGGridItems-specific editing orchestration built on top of ArdaGrid. The canary useDirtyTracking<T>() hook covers generic dirty tracking, but the draft creation/API integration is domain-specific.Partially covered. useDirtyTracking<T>() handles generic dirty state. Draft API calls are domain-specific and must remain in the canary-refactor story layer.
9Page chrome componentsAppSidebar, AppHeader, SidebarProvider, tab navigation, search bar, column visibility dropdownItemsPageThe full page layout wrapping the grid. These are vendored UI components not related to the grid library.Out of scope for the grid library. The canary-refactor stories must either: (a) continue importing these from vendored, or (b) fork them into canary-refactor components.
10ItemDetailsPanel / ItemFormPanel — Detail view and add/edit form panelsItemsPageOpened by row click and “Add item” button. Not grid components, but the item-detail story exercises them.Out of scope for the grid library. Must remain vendored in the canary-refactor stories.
11Color cell editor with swatches — Inline <select> with 10 color options and color swatch displayItemTableAGGrid (inline editor)Domain-specific select editor built into ItemTableAGGrid, not using the canary enum cell editor pattern.Not covered by canary enum editor. The vendored implementation is a plain <select> element, not an AG Grid cellEditor component. Domain-specific.

The canary components (Waves 0-3b) provide the grid infrastructure: DataGrid<T> molecule, createEntityDataGrid<T>() factory, createEntityDataGridShim<T>() vendored compatibility layer, 5 basic cell type Display+Editor atoms, pagination, dirty tracking hook.

However, the canary-refactor stories render the full vendored ItemsPage, which has extensive domain-specific components above the grid infrastructure layer. The canary-refactor stories can swap the grid infrastructure (Tiers 2-3b) but must retain vendored imports for:

  • All domain-specific cell editors (#2) and custom cell renderers (#3-6)
  • Column definitions (#7)
  • Page chrome (#9) and panels (#10)

This is consistent with the project scope (Tiers 1-3b in scope, Tier 4 domain grids out of scope). The canary-refactor stories will be hybrid: canary grid infrastructure + vendored domain components + vendored page chrome.

Insufficiency #1 (SortMenuHeader) is the only item that should be in scope but is missing from the design. It is an internal sub-component of ArdaGrid that must be ported as part of Tier 2.

Appendix A: AG Grid Row Selection Capabilities

Section titled “Appendix A: AG Grid Row Selection Capabilities”

This appendix documents the AG Grid Community and Enterprise row selection features relevant to this project. It resolves Q14 and informs the decision to replace the vendored SelectAllHeaderComponent with AG Grid native configuration.

AG Grid Community — Row Selection Features

Section titled “AG Grid Community — Row Selection Features”

AG Grid Community (ag-grid-community) provides all the row selection features needed by this project. No Enterprise license is required.

FeatureConfigurationAvailable In
Multi-row selection with checkboxesrowSelection: { mode: 'multiRow' }Community
Shift-click range selection on checkboxesBuilt-in behavior in multiRow modeCommunity
Header checkbox (tri-state: all/some/none)rowSelection: { headerCheckbox: true }Community
Select-all modesrowSelection: { selectAll: 'all' | 'filtered' | 'currentPage' }Community
Click-to-select without checkboxrowSelection: { enableClickSelection: true }Community
Checkbox column customizationselectionColumnDef: { width, pinned, sortable, ... }Community

The AG Grid documentation explicitly states:

“Ranges of rows can be selected by holding down Shift while clicking on checkboxes. This behaviour also applies when Click Selection is enabled, and in Group Selection.”

This means the vendored SelectAllHeaderComponent (~160 lines) reimplements functionality that AG Grid Community already provides natively: tri-state header checkbox and shift-click range selection on row checkboxes.

AG Grid Enterprise — Additional Selection Features (Not Required)

Section titled “AG Grid Enterprise — Additional Selection Features (Not Required)”

AG Grid Enterprise adds cell-level selection features that are not needed by this project:

FeatureDescriptionAvailable In
Cell Range SelectionSelect rectangular regions of cells (spreadsheet-like drag selection)Enterprise
Range Selection APIProgrammatic cell range manipulation, copy/paste rangesEnterprise
Fill HandleDrag cell corner to auto-fill adjacent cells (Excel-style)Enterprise

These features operate at the cell level, not the row level, and are unrelated to the row selection behavior used by the Items grid.

Section titled “Recommended Configuration for Canary DataGrid<T>”

The canary Tier 2 DataGrid<T> molecule should configure row selection as follows:

const rowSelection = useMemo(() => ({
mode: 'multiRow' as const,
headerCheckbox: true,
selectAll: 'all' as const,
}), []);
const selectionColumnDef = useMemo(() => ({
pinned: 'left' as const,
width: 50,
suppressHeaderMenuButton: true,
}), []);

This replaces the vendored SelectAllHeaderComponent entirely and provides:

  • Tri-state header checkbox (all/some/none)
  • Shift-click range selection on row checkboxes
  • Configurable checkbox column (width, pinning)