Skip to content

List View Component: Design Completeness Assessment

Date: 2026-03-17

This document assesses whether the canary components defined in the Design (Waves 0-3b) are sufficient to migrate the canary-refactor stories from vendored components to canary equivalents, within the scope of grid data display and editing.

The dev-witness stories render the full vendored ItemsPage production page. This page composes:

  • Grid layer: ItemTableAGGrid wrapping ArdaGrid with items-specific editing, draft management, and 30 column definitions
  • Page chrome: AppSidebar, AppHeader, 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 providers: Auth, order queue, item cards, navigation blocking

The canary-refactor stories (per Workflow step 6) must swap vendored grid components for canary equivalents while retaining vendored imports for non-grid components. The result will be hybrid stories: canary grid infrastructure + vendored domain and page components.

#Component / FeatureClassificationUsed ByRecommendationDecision
1SortMenuHeaderMissing from Tier 2 scopeArdaGrid (internal)Add to Tier 2 scope. See detailed analysis.Agreed
29 Typeahead Cell EditorsDomain-specific (Tier 4)ItemTableAGGridRetain vendored. See detailed analysis.Add to new Section Follow Up Projects (Out of Scope) in the design.md document
3SelectAllHeaderComponentDomain-specific (Tier 4)itemsColumnDefsRetain vendored. See detailed analysis.Replace with AG Grid native configuration in the canary
grid, validate shift-click behavior document
4QuickActionsCellDomain-specific (Tier 4)itemsColumnDefsRetain vendored. See detailed analysis.Add to new Section Follow Up Projects (Out of Scope) in the design.md document
5CardCountCellDomain-specific (Tier 4)itemsColumnDefsRetain vendored. See detailed analysis.Agreed
6NotesCell / CardNotesCellDomain-specific (Tier 4)itemsColumnDefsRetain vendored. See detailed analysis.In Scope, new type of cell (Memo) with its renderer and editor. The renderer should truncate the text to fit in the cell adding ellipsis. When hovered over, it should open an overlay to show the full text after a configurable delay of hover.
7itemsColumnDefs / itemsDefaultColDefDomain-specific (Tier 4)ItemTableAGGridFork into canary-refactor. See detailed analysis.
8Draft lifecycle managementPartially coveredItemTableAGGridHybrid approach. See detailed analysis.Add to new Section Follow Up Projects (Out of Scope) in the design.md document
9Page chrome componentsOut of scopeItemsPageRetain vendored. See detailed analysis.Add to new Section Follow Up Projects (Out of Scope) in the design.md document
10ItemDetailsPanel / ItemFormPanelOut of scopeItemsPageRetain vendored. See detailed analysis.Agreed
11Color cell editor with swatchesDomain-specific (Tier 4)ItemTableAGGridRetain vendored. See detailed analysis.Add to scope as a Color data type with its cell renderer and editor (Color Picker).

What it is: A custom AG Grid header component (~100 lines) that replaces AG Grid’s default column header. It shows the column name, a sort-direction indicator (up/down arrow), and a button that opens a portal dropdown menu with “Sort Ascending”, “Sort Descending”, and “Clear Sort” options.

Why it’s needed: The vendored ArdaGrid sets headerComponent: SortMenuHeader on defaultColDef, meaning every sortable column uses it. Without porting it, columns would fall back to AG Grid’s default header, producing a visually different grid. VRT tests would fail.

Implementation details:

  • Uses IHeaderParams from ag-grid-community
  • Syncs sort state via params.column.addEventListener('sortChanged', ...)
  • Dropdown rendered via createPortal(...) to document.body to escape AG Grid’s overflow clipping
  • Outside-click detection to close the dropdown

Recommendation: Port as an internal sub-component of the Tier 2 DataGrid<T> molecule. It is tightly coupled to DataGrid (set via defaultColDef.headerComponent) and not intended for standalone use. Add it to the Wave 2 scope:

  • Source: Internal to vendored/arda-frontend/components/table/ArdaGrid.tsx (lines 153-257)
  • Target: canary/molecules/data-grid/sort-menu-header.tsx
  • Dependencies: ag-grid-community (IHeaderParams), createPortal from React DOM
  • Tests: Port the existing behavior; add story demonstrating sort menu interaction
  • CSS: Uses .arda-sort-header, .arda-sort-header-text, .arda-sort-header-icon, .arda-sort-header-btn, .arda-sort-menu-dropdown classes from ag-theme-arda.css (already in Wave 0 scope)

Action required: Update the Design Wave 2 table to include SortMenuHeader.

What they are: 9 domain-specific AG Grid cell editors (SupplierCellEditor, UnitCellEditor, TypeCellEditor, SubTypeCellEditor, UseCaseCellEditor, FacilityCellEditor, DepartmentCellEditor, LocationCellEditor, SublocationCellEditor). Each wraps a domain-specific *Typeahead component that performs debounced API lookups (250ms) and renders a portal dropdown with search results.

Why they can’t be ported: Each editor is bound to a specific API endpoint (ardaClient.lookup* functions) and domain entity type. They require production API context that the Storybook stories mock via MSW handlers. They are Tier 4 (domain-specific) components.

Recommendation: Retain vendored. The canary-refactor ItemTableAGGrid fork will import these editors from vendored paths (@frontend/components/items/*CellEditor). The canary grid infrastructure (Tier 3a/3b) accepts arbitrary cellEditor components via AG Grid’s ColDef, so vendored editors plug into canary grids without modification.

What it is: A custom AG Grid header component (~160 lines) for the selection checkbox column. Implements tri-state logic (all/some/none selected), shift-click range selection, and sync with AG Grid’s selection API.

Why it was originally classified as domain-specific: It is defined in columnPresets.tsx alongside the items-specific column definitions. It uses AG Grid’s GridApi methods (getDisplayedRowCount, getSelectedRows, selectAll, deselectAll, forEachNode) directly.

AG Grid native alternative: Since AG Grid v32+, the Community edition provides this functionality natively via the rowSelection configuration object:

  • rowSelection.headerCheckbox: true — renders a tri-state select-all checkbox in the header (all/some/none)
  • rowSelection.selectAll: 'all' | 'filtered' | 'currentPage' — controls what “select all” means
  • selectionColumnDef — customizes the checkbox column (width, pinning, sortable, custom renderers, tooltips)
  • Shift-click range selection may be supported natively via rowSelection.enableClickSelection

The vendored SelectAllHeaderComponent is a custom reimplementation of functionality that AG Grid already provides out of the box. The custom component adds shift-click range selection logic on top, but the tri-state header checkbox behavior is native.

Recommendation: Replace with AG Grid native rowSelection configuration in the canary DataGrid<T> (Tier 2). Configure headerCheckbox: true and selectionColumnDef to match the vendored visual appearance. Validate that shift-click range selection works natively; if not, document the gap for a follow-up. This eliminates a ~160-line custom component and a vendored dependency from the canary-refactor stories.

References:

What it is: A cell renderer that displays action buttons per row: add to order queue, print labels, preview cards, card history. Requires useItemCards context for kanban card data and multiple async API operations.

Recommendation: Retain vendored. Deeply coupled to the item domain and kanban card system. Not a generic grid concern.

What it is: A cell renderer that lazy-loads the kanban card count for each item via useItemCards context.

Recommendation: Retain vendored. Domain-specific, requires API context.

What they are: Icon-button cell renderers that open a NoteModal for inline editing of item.notes and item.cardNotesDefault fields.

Recommendation: Retain vendored. Domain-specific, requires modal and API save callbacks.

What it is: 30 column definitions that wire cell renderers, formatters, editors, and header components to the AG Grid. Defined in vendored/arda-frontend/components/table/columnPresets.tsx (~1300 lines).

Why it’s needed: Without column definitions, the grid has no columns.

Recommendation: Fork columnPresets.tsx into canary-refactor/components/columnPresets.tsx. Update imports for any canary cell Display/Editor atoms that replace vendored equivalents. Retain vendored imports for domain-specific renderers (#3-6) and editors (#2).

This is the primary integration point where canary Tier 1 atoms meet vendored domain components. See Appendix A for a column-by-column mapping of which columns can use canary atoms vs which must retain vendored components.

What it is: ItemTableAGGrid manages a draft lifecycle: creating draft items per row, batching draft saves, tracking unsaved changes via dirty sets, auto-publishing on row leave, and showing row-level saving/error UI states. This is ~400 lines of domain-specific orchestration built on top of ArdaGrid.

Current canary coverage: The useDirtyTracking<T>() hook (Wave 3a) covers generic dirty state tracking (clone row data, track per-cell edits, save/discard drafts). But the draft creation API calls (createDraftItem, updateItem), row-level status UI, and auto-publish logic are items-specific.

Recommendation: Hybrid approach. The canary-refactor ItemTableAGGrid fork will:

  1. Use createEntityDataGridShim<T>() (Tier 3b) as its base grid
  2. Compose useDirtyTracking<T>() for generic dirty state
  3. Retain the domain-specific draft API logic as vendored code within the fork

What they are: AppSidebar, AppHeader, SidebarProvider, SidebarInset, tab navigation, search bar, column visibility dropdown, action buttons (Add item, Import, Export, Delete).

Recommendation: Retain vendored. These are application-shell components, not grid components. The canary-refactor stories will import them from vendored paths (@frontend/components/*). The grid library has no responsibility for page layout.

What they are: The detail-view panel (opened on row click) and add/edit form panel (opened on “Add item” button). The item-detail.stories.tsx story exercises both.

Recommendation: Retain vendored. These are separate UI layers above the grid. The Entity Viewer project (future) will port the detail panel. The form panel is independent of the grid library.

What it is: An inline <select> element with 10 color options and visual color swatches, built directly into ItemTableAGGrid as a local component (~30 lines). Not an AG Grid cellEditor — it’s rendered via a custom cellRenderer that switches between display and edit mode.

Recommendation: Retain vendored. Domain-specific. The canary enum cell editor is a generic dropdown, not a color-swatch picker.

ClassificationCountItemsAction
Missing from scope1#1 (SortMenuHeader)Add to Tier 2 Wave 2
Domain-specific (Tier 4)7#2, #3, #4, #5, #6, #7, #11Retain vendored in canary-refactor stories
Partially covered1#8 (Draft lifecycle)Hybrid: canary generic hooks + vendored domain logic
Out of scope (not grid)2#9, #10Retain vendored

The canary-refactor stories will be hybrid: canary grid infrastructure (Tiers 2-3b) swapped at the ArdaGrid / ItemTableAGGrid level, with vendored components retained for domain-specific cell renderers/editors, column definitions, page chrome, and panels.

Only #1 (SortMenuHeader) requires a change to the design scope. All other insufficiencies are consistent with the project boundary (Tiers 1-3b in scope, Tier 4 domain grids out of scope).

  • Add SortMenuHeader to the Design Wave 2 table and Component Dependencies table
  • Add SortMenuHeader to the Tier 2 DataGrid<T> porting guideline as an internal sub-component
  • Document the hybrid canary-refactor story architecture in the Porting Strategy section

The following changes were applied to design.md based on the decisions in the insufficiency table above.

DecisionChange Applied to design.md
#1 SortMenuHeader — AgreedAdded SortMenuHeader to Component Dependencies table (Tier 2), Porting Guidelines table, Wave 2 table, and Wave Summary diagram. Updated DataGrid<T> dependencies to include SortMenuHeader.
#2 Typeahead editors — Follow UpAdded to new “Follow Up Projects (Out of Scope)” section as F1.
#3 SelectAllHeaderComponent — Replace with AG Grid nativeUpdated DataGrid<T> Porting Guideline to configure AG Grid native rowSelection with headerCheckbox: true and selectionColumnDef. Updated Tier 2 summary bullet.
#4 QuickActionsCell — Follow UpAdded to “Follow Up Projects” as F2.
#5 CardCountCell — Agreed (retain vendored)No change needed (consistent with existing scope).
#6 NotesCell/CardNotesCell — In scope as Memo typeAdded MemoCellDisplay and MemoCellEditor to Component Dependencies table, Porting Guidelines table, Wave 1 table, and Wave Summary diagram. Updated Tier 1 summary bullet to include memo. Wave 1 description updated from 5 to 7 types.
#7 itemsColumnDefs — No explicit decisionNo change (recommendation to fork into canary-refactor stands).
#8 Draft lifecycle — Follow UpAdded to “Follow Up Projects” as F3.
#9 Page chrome — Follow UpAdded to “Follow Up Projects” as F4.
#10 ItemDetailsPanel/ItemFormPanel — AgreedNo change needed (retain vendored, consistent with scope).
#11 Color editor — In scope as Color typeAdded ColorCellDisplay and ColorCellEditor to Component Dependencies table, Porting Guidelines table, Wave 1 table, and Wave Summary diagram. Updated Tier 1 summary bullet to include color.

Appendix A: Column Definition Migration Map

Section titled “Appendix A: Column Definition Migration Map”

The forked itemsColumnDefs (canary-refactor/components/columnPresets.tsx) is the primary integration point where canary Tier 1 atoms meet vendored domain components. The canary grid infrastructure (DataGrid<T>, createEntityDataGrid<T>()) is agnostic to which cell components are used — it passes the ColDef[] directly to AG Grid. ColDef.cellRenderer and ColDef.cellEditor accept any React component conforming to AG Grid’s interfaces, so canary atoms and vendored components coexist in the same column definitions array.

The table below maps each of the 30 vendored columns to its migration action.

ColumnFieldCurrent Renderer/EditorCanary AtomAction
SKUinternalSKUInline text formatterTextCellDisplayReplace renderer with canary
GL CodegeneralLedgerCodeInline text formatterTextCellDisplayReplace renderer with canary
Sub-locationlocator.subLocationDirect textTextCellDisplayReplace renderer with canary
Sub-Typeclassification.subTypeDirect textTextCellDisplayReplace renderer with canary
Use CaseuseCaseDirect textTextCellDisplayReplace renderer with canary
Departmentlocator.departmentDirect textTextCellDisplayReplace renderer with canary
Facilitylocator.facilityDirect textTextCellDisplayReplace renderer with canary
Supplier SKUprimarySupply.skuDirect textTextCellDisplayReplace renderer with canary
Min Qty AmountminQuantityAmountString number formatterNumberCellDisplayReplace renderer with canary
Order AmountorderQuantityAmountString number formatterNumberCellDisplayReplace renderer with canary
Taxabletaxable”Yes”/“No”BooleanCellDisplayReplace renderer with canary
Order MethodprimarySupply.orderMechanismEnum label mapEnumCellDisplay / EnumCellEditorReplace renderer and editor with canary
Card SizecardSizeEnum label mapEnumCellDisplay / EnumCellEditorReplace renderer and editor with canary
Label SizelabelSizeEnum label mapEnumCellDisplay / EnumCellEditorReplace renderer and editor with canary
Breadcrumb SizebreadcrumbSizeEnum label mapEnumCellDisplay / EnumCellEditorReplace renderer and editor with canary
ColumnFieldCurrent Renderer/EditorCanary AtomAction
NotesnotesNotesCell (icon + modal)MemoButtonCell + createMemoButtonCellEditor()Replace with canary memo button pattern (vendored-compatible icon + modal). MemoCellDisplay available as alternative for inline truncated display.
Card NotescardNotesDefaultCardNotesCell (icon + modal)MemoButtonCell + createMemoButtonCellEditor()Same as Notes — canary memo button pattern.
ColorcolorInline <select> with swatchesColorCellDisplay / ColorCellEditorReplace with canary color type
ColumnFieldCurrent Renderer/EditorAction
SupplierprimarySupply.supplierTypeahead display + SupplierCellEditorCanary TextCellDisplay for display; retain vendored SupplierCellEditor for editing
ColumnFieldCurrent Renderer/EditorWhy Not Canary
Item (name)nameCustom blue clickable text rendererCustom rendering (clickable, opens detail panel) — not a simple text cell
ImageimageUrlGridImage componentImage cell type deferred to F5
Unit PriceprimarySupply.unitCostformatCurrency (Money type)No currency/money cell type in scope
Order CostprimarySupply.orderCostformatCurrencySame — no currency type
CreatedcreatedCoordinatesformatDateTimeDatetime cell type deferred to F5
Lead TimeprimarySupply.averageLeadTimeDuration formatter (“N hours”)No duration cell type in scope
Classificationclassification.type”type - subType” multi-field rollupCustom multi-field rendering
Locationlocator.location”Facility / Dept / Location” rollupCustom multi-field rendering
Quick Actions(virtual)QuickActionsCellDomain-specific (#4), requires kanban context
Card Count(virtual)CardCountCellDomain-specific (#5), requires lazy API fetch
ColumnCurrent RendererAction
Select (checkbox)SelectAllHeaderComponentRemoved from column definitions. Replaced by AG Grid native rowSelection configuration (see Appendix A in design.md).