Skip to content

List View Component: Landscape Analysis

Date: 2026-03-16

Inventory of List and Grid Usage and Candidate Components

Section titled “Inventory of List and Grid Usage and Candidate Components”

This analysis inventories all existing list and grid surfaces across the Arda front-end ecosystem to inform the scope of the List View Component project (GitHub #565). Three areas were surveyed:

  1. Production pages in arda-frontend-app that render entity lists.
  2. Storybook stories in ux-prototype that demonstrate list or grid behavior.
  3. Candidate components in ux-prototype that could serve as a basis for the new Canary-tier List View components.

The key finding is that two parallel grid implementations exist: a monolithic ArdaGrid wrapper in arda-frontend-app (85 KB) and a well-structured component hierarchy in ux-prototype/extras. The extras hierarchy already follows the Arda Component Design Guidelines (Static / Init / Runtime configuration, factory pattern) and has been exercised with two entity types (Items and Suppliers). However, all of this work lives in the Extras library and must not be referenced from the new Canary components per the project requirements.

The proposed Canary architecture defines five tiers. This project will deliver Tiers 1, 2, 3a, and 3b. Tier 4 (domain-specific grids) will be implemented later as part of individual domain stories.

1. Pages in arda-frontend-app That Display Entity Lists

Section titled “1. Pages in arda-frontend-app That Display Entity Lists”

Six pages render collections of entities. Only the Items page uses AG Grid in production; the remaining pages use TanStack React Table, Shadcn table primitives, or plain .map() layouts.

PageRouteEntityGrid TechnologyNotes
Itemsapp/items/Inventory itemsArdaGrid (AG Grid) + legacy ItemTable (TanStack)Primary grid — multi-select, sort, filter, pagination, cell editing. ItemTableAGGrid.tsx is ~75 KB.
Order Queueapp/order-queue/Orders to placeCustom .map() grouped layoutGrouped by order method and supplier. Not a tabular grid.
Receivingapp/receiving/Items in transitCustom .map() grouped layout with tabsTabs: Waiting / Received / Recently Fulfilled.
Dashboardapp/dashboard/Recent ordersTanStack React Table + Shadcn primitivesRead-only summary table.
Company Settingsapp/company-settings/UsersCustom .map() listAvatar + role badge layout, not tabular.
Items Grid Testapp/items-grid-test/Items (mock)ArdaGrid (AG Grid)Non-production test page.
FileRole
/arda-frontend-app/src/components/table/ArdaGrid.tsx (~85 KB)AG Grid forwardRef wrapper with sorting, filtering, pagination, cell editing, column persistence, and custom row actions.
/arda-frontend-app/src/components/table/columnPresets.tsxColumn definition presets for Items and Orders.
/arda-frontend-app/src/components/ui/table.tsxShadcn/Radix HTML table primitives (Table, TableHeader, TableBody, TableRow, TableCell).

2. Stories in ux-prototype That Show Entity Lists

Section titled “2. Stories in ux-prototype That Show Entity Lists”
Storybook PathComponentStoriesCoverage
Components/Extras/Molecules/DataGridArdaDataGrid13Loading, empty, error, pagination, selection, cell editing, persistence, images
Components/Extras/Organisms/Shared/Entity Data GridcreateArdaEntityDataGrid7Dirty tracking, column visibility, pagination, editing
Components/Extras/Organisms/Reference/Items/Items Data GridArdaItemsDataGrid6Item-specific columns, editing, selection
Components/Extras/Organisms/Reference/Business Affiliates/Suppliers Data GridArdaSupplierDataGrid6Supplier-specific columns, editing, selection
Components/Extras/Molecules/TableArdaTable3Clickable rows, active state, empty state
Use Case IDTitleComponentGrid Component
BA-0001-0001View Suppliers ListSuppliersPageArdaGrid<BusinessAffiliateWithRoles>
BA-0001-0003Toggle Column VisibilityToggleColumnsSuppliersPageAG Grid + column visibility dropdown
BA-0001-0006PaginationSuppliersPageAG Grid with pagination
(Page)Suppliers List ViewSuppliersPage (with sidebar)ArdaGrid<BusinessAffiliateWithRoles>
SectionStoryComponent
Dev Witness/Reference/ItemsItems GridItemsPage (Next.js app route reproduction)
Archive/Applications/DesignItems Data GridArdaItemsDataGrid (legacy design explorations)

3. Candidate Components for List View Implementation

Section titled “3. Candidate Components for List View Implementation”

The ux-prototype Extras library contains a component hierarchy that, combined with the vendored ArdaGrid, informs the proposed five-tier Canary architecture.

TierNameAtomic LevelExtras SourceVendored SourceRoleIn Scope
1Cell RenderersAtomsArdaTextCellDisplay, ArdaDateCellEditor, ArdaBooleanCellInteractive, etc.Pluggable AG Grid cell components by data type. Each type provides Display, Editor, and Interactive variants. Priority subset: text, number, boolean, date, enum.Yes
2Data GridMoleculeArdaDataGridCore AG Grid wrapper. Handles columns, selection, pagination, persistence, loading/error/empty overlays. Stays close to native AG Grid API.Yes
3aEntity Data Grid (Base)Organism (shared)createArdaEntityDataGrid<T>()Generic factory adding column visibility, entity-typed callbacks, and composable dirty tracking via hook. Returns { Component } per Arda factory convention. Includes selected enhancements from vendored grid (multi-sort, filtering, cell editing lifecycle).Yes
3bEntity Data Grid (Full)Organism (shared)ArdaGrid<T>Built on top of Tier 3a. Provides full compatibility with the current vendored ArdaGrid component — drop-in replacement or with minimal modifications. Adds row actions, row double-click, getRowClass, hasActiveSearch, selection convenience ref methods.Yes
4Domain GridsOrganism (domain)ArdaItemsDataGrid, ArdaSupplierDataGridConcrete instances with pre-configured column definitions and domain-specific cell renderers. Implemented per domain story, not as part of this project.No
  • Tier 2 wraps AG Grid directly and exposes a minimal, idiomatic API.
  • Tier 3a wraps Tier 2 via factory, adding entity-level orchestration and selected vendored features that belong at the entity level (multi-sort, filtering, cell editing lifecycle).
  • Tier 3b wraps Tier 3a, layering on the remaining vendored ArdaGrid features (row actions, row double-click, getRowClass, hasActiveSearch, selection convenience methods) to provide a migration-compatible component.
  • Tier 4 (out of scope) uses the Tier 3a or 3b factory to create domain-specific grid instances.

All grid-tier components follow a three-part configuration structure:

  • StaticConfig — design-time settings: column definitions, row height, selection mode, entity ID accessor.
  • InitConfig — one-time initialization: onGridReady callback.
  • RuntimeConfig — per-render props: rowData, loading, pagination state, event handlers.
PresetLocation
Items/ux-prototype/src/components/extras/molecules/data-grid/presets/items/
Suppliers/ux-prototype/src/components/extras/molecules/data-grid/presets/suppliers/
Common utilities/ux-prototype/src/components/extras/molecules/data-grid/presets/common/
ComponentLocationRole
createArdaEntityViewer<T>()/ux-prototype/src/components/extras/organisms/shared/entity-viewer/Detail/form view factory (tabbed or continuous-scroll layouts, edit/display modes, validation, dirty checking). Complements the data grid for list + detail patterns.
ArdaTable/ux-prototype/src/components/extras/molecules/table/Lightweight HTML table compound component (no AG Grid dependency). Suitable for simple, non-editable tabular displays.

A copy of the arda-frontend-app grid is vendored at /ux-prototype/src/vendored/arda-frontend/components/table/ArdaGrid.tsx and used by several use-case stories. This serves as a reference for feature parity but should not be the basis for new Canary components.

The Canary section (/ux-prototype/src/components/canary/) contains only placeholder files. A detail-field atom is the only real implementation. The List View components would be among the first substantive Canary entries.

#QuestionDecision
1Should the Canary List View replicate the full four-tier hierarchy from Extras, or start with fewer tiers?Five tiers (1, 2, 3a, 3b, 4). This project delivers Tiers 1, 2, 3a, and 3b. Tier 3a corresponds to the Extras entity grid with selected enhancements. Tier 3b provides full vendored ArdaGrid compatibility. Tier 4 (domain grids) will be implemented later per domain story.
2Should the vendored ArdaGrid from arda-frontend-app inform feature requirements, or should only the Extras hierarchy be considered?Both.
3How should column presets be organized — bundled with the grid component or separate?Deferred. They will be located with the domain stories rather than in the library.
4Is the ArdaTable (non-AG-Grid) in scope for this project, or only the AG Grid-based List View?AG Grid only.
5What is the migration path for arda-frontend-app pages currently using the monolithic ArdaGrid?Out of scope of this project.

The following questions must be resolved before the design document can be finalized. Each question includes options with trade-offs and a recommendation.

Q6 — Feature Parity with Vendored ArdaGrid

Section titled “Q6 — Feature Parity with Vendored ArdaGrid”

The vendored ArdaGrid (from arda-frontend-app) exposes features not present in the Extras ArdaDataGrid molecule. Since Decision 2 established that both implementations inform requirements, the question is which specific features the Canary Tier 2 molecule should absorb.

Features present in vendored ArdaGrid but absent from Extras ArdaDataGrid:

FeaturePropsPurposeAG Grid Best PracticeDecision
Multi-sortenableMultiSort, onSortChangedAllow sorting by multiple columns simultaneously (Ctrl+click). The vendored grid maps enableMultiSort to AG Grid’s multiSortKey: 'ctrl' and includes a custom SortMenuHeader dropdown.Native support via multiSortKey prop. The custom sort-menu header is an Arda addition — AG Grid’s built-in header menu already supports multi-sort when multiSortKey is set.Tier 3a
FilteringenableFiltering, onFilterChangedEnable per-column client-side filters. The vendored grid sets filter: true on defaultColDef and forwards onFilterChanged with the full filter model via api.getFilterModel().Native support. Set filter: true on defaultColDef for default text filters, or specify filter: 'agNumberColumnFilter' etc. per column. AG Grid manages filter UI and state natively.Tier 3a
Row actionsenableRowActions, rowActions[]Render a dropdown menu of actions (edit, delete, etc.) in a pinned-right column. Each action has a label, optional icon, and onClick(rowData) handler.No native equivalent. This is a custom abstraction — a pinned column with a cellRenderer that renders React buttons. A common pattern in AG Grid apps but always requires custom implementation.Tier 3b. Create ActionCellRenderer Atom or Molecule component that can be injected in 3a as a special column. 3b should use the component to support the vendored api
Row double-clickonRowDoubleClickedRespond to double-click on a row (e.g., open detail view). Pure passthrough of AG Grid’s native event.Native event. Pass onRowDoubleClicked directly to AgGridReact. No abstraction needed.Tier 3b
Cell editing lifecycleonCellEditingStarted, onCellEditingStopped, onCellFocusedTrack when cells enter/exit edit mode and receive focus. All three are direct passthroughs of AG Grid native events with no wrapper logic.Native events. Pass directly to AgGridReact. Useful for coordinating external state with in-cell editing, but most editing workflows can rely on onCellValueChanged alone.Tier 3a
Row class callbackgetRowClassApply dynamic CSS classes to rows based on row data (e.g., highlight rows with errors). The vendored grid conditionally enables this only when cell editing is active.Native prop. Pass getRowClass directly to AgGridReact. The conditional gating to cell-editing mode is an Arda-specific choice, not an AG Grid requirement.Tier 3a
Active search indicatorhasActiveSearchControl which empty-state message is shown: “No items found” (when a search is active) vs. the default empty-state component. Has no interaction with AG Grid’s API.No AG Grid equivalent. This is purely application-level UI state that determines which empty overlay to render. Could be implemented as a prop on any wrapper or handled by the consumer.Tier 3b only (vendored compatibility shim). Not needed in Tier 3a — consumers use the existing emptyStateComponent prop (inherited from Tier 2) to pass whichever empty-state node they want.
Selection convenience (ref)selectAll(), deselectAll(), getSelectedRows()Imperative methods on the component ref for programmatic row selection. All delegate directly to gridApi.selectAll(), gridApi.deselectAll(), and gridApi.getSelectedRows().Thin wrappers over native GridApi methods. AG Grid’s own API is the canonical way to do this. Exposing them on a wrapper ref is a convenience — consumers can achieve the same via getGridApi().Tier 3b (ref extension)
Grid state initializationinitialState: GridStateRestore grid state (column widths, visibility, sort order, filters) at creation time. Pure passthrough of AG Grid v31+ initialState prop.Native prop (AG Grid v31+). Pass directly to AgGridReact. This is AG Grid’s recommended approach for state restoration, replacing the older columnApi.applyColumnState() pattern.Tier 3b

Options:

  • (a) Full parity — Absorb all vendored features into the Canary molecule.
    • Pro: The Canary component becomes a drop-in replacement for any current grid usage.
    • Con: Increases the molecule’s surface area significantly. Some features (filtering, row actions) may be better composed at a higher tier.
  • (b) Core subset — Include multi-sort, filtering, row double-click, getRowClass, and initialState. Defer row actions, cell editing lifecycle callbacks, and hasActiveSearch to higher tiers or later iterations.
    • Pro: Keeps the molecule focused on data display concerns. Features like row actions are UI chrome that belong at organism level.
    • Con: Consumers needing row actions must build their own wrapper or wait for Tier 3 support.
  • (c) Minimal — match current Extras — Only add features when a concrete use case demands them.
    • Pro: Smallest initial scope. Avoids speculative API surface.
    • Con: Known production features would be missing from the start, requiring rework when arda-frontend-app eventually migrates.

Recommendation: Superseded by the Tier 3a / 3b split. Per the feature-level decisions above: Tier 2 stays close to native AG Grid. Tier 3a absorbs multi-sort, filtering, and cell editing lifecycle. Tier 3b absorbs row actions, row double-click, getRowClass, hasActiveSearch, and selection convenience methods — providing full vendored ArdaGrid compatibility.

Q7 — AtomProps<V> Dependency for Cell Atoms

Section titled “Q7 — AtomProps<V> Dependency for Cell Atoms”

Tier 1 interactive cell components (*CellInteractive) extend AtomProps<V> from lib/data-types/atom-types.ts. This interface provides mode: AtomMode, onChange(original, current), onComplete, onCancel, errors, label, and labelPosition. Adopting it in Canary creates a dependency on the broader atom type system.

Options:

  • (a) Preserve AtomProps<V> dependency — Canary cell atoms import and extend the existing AtomProps<V> interface.
    • Pro: Consistency with the existing atom system. Interactive cells and standalone form atoms share the same contract, enabling reuse in both grid and form contexts.
    • Con: The List View library is coupled to the atom type system. Changes to AtomProps<V> ripple into grid cells. The label/labelPosition fields are irrelevant inside a grid context.
  • (b) Define a grid-specific base type — Create a GridCellProps<V> interface that contains only the fields relevant to grid cells (value, onChange, mode, errors, editable). Optionally make it structurally compatible with AtomProps<V> so the same component can satisfy both.
    • Pro: The grid library is self-contained. The interface is tailored to the grid context — no unused fields.
    • Con: Two similar but distinct base types exist. Structural compatibility may drift over time without explicit enforcement.
  • (c) No base type — each cell defines its own props — Remove the shared base entirely; each cell type defines exactly the props it needs.
    • Pro: Maximum flexibility per cell type.
    • Con: No guaranteed contract across cell types. Generic cell-agnostic logic (e.g., error display, mode switching) cannot rely on a common shape.

Recommendation: (b) Grid-specific base type. A GridCellProps<V> with value, onChange, mode, errors, and editable keeps the grid library self-contained while preserving a uniform contract across cell types. Structural compatibility with AtomProps<V> can be documented as a convention without creating an import dependency.

The Extras library has 10 cell type directories (text, date, time, datetime, number, boolean, enum, url, image, custom), each with 3 variants (Display, Editor, Interactive) plus a factory helper. This totals approximately 30 components with tests and stories.

Options:

  • (a) All 10 types — Deliver every cell type in the first pass.
    • Pro: Complete from day one. No follow-up work needed for cell types.
    • Con: Large scope — many types (time, datetime, custom) may not be exercised by any current use case. Risk of building untested API surface.
  • (b) Priority subset of 5 — Deliver text, number, boolean, date, and enum. These cover the column types used in the Items and Suppliers grids. Add url, image, time, datetime, and custom in a later iteration.
    • Pro: Covers all production use cases with half the component count. Remaining types can be added when a real consumer needs them.
    • Con: Consumers needing URL or image columns must use AG Grid’s native renderers or custom components until the remaining types ship.
  • (c) Priority subset of 3 — Deliver only text, number, and boolean. Minimal viable cell type set.
    • Pro: Fastest delivery. Forces early validation of the cell type pattern before scaling.
    • Con: Too limited for the Suppliers grid (which uses date and enum columns). Would block real use cases.

Recommendation: (b) Priority subset of 5. Text, number, boolean, date, and enum cover the two known domain grids (Items and Suppliers). This delivers a meaningful library while keeping scope bounded. The pattern established by these five types makes adding the remaining five straightforward.

Dirty tracking (unsaved changes detection, draft save/discard) currently lives in Tier 3 (createArdaEntityDataGrid). The vendored ArdaGrid has no dirty tracking at all. This feature adds significant complexity to the factory: it clones row data, tracks per-cell edits, and exposes saveAllDrafts/discardAllDrafts on the ref.

Options:

  • (a) Include in Canary Tier 3 — The createEntityDataGrid factory provides dirty tracking as it does today.
    • Pro: Consistent with the Extras design. Any entity grid gets dirty tracking automatically.
    • Con: Consumers who do not need dirty tracking (read-only grids, server-side-save grids) still carry the complexity. The factory becomes harder to understand and test.
  • (b) Extract as a composable hook — Move dirty tracking into a standalone useDirtyTracking<T>() hook that can be optionally composed with the Tier 3 factory or used independently.
    • Pro: Tier 3 stays focused on entity-grid orchestration. Dirty tracking becomes opt-in. The hook is testable in isolation.
    • Con: Consumers who want dirty tracking must wire it up themselves instead of getting it for free.
  • (c) Defer to Tier 4 (domain grids) — Remove dirty tracking from the shared library entirely. Each domain grid decides whether to implement it.
    • Pro: Simplest Tier 3. Domain grids own their editing lifecycle.
    • Con: Dirty tracking logic will be duplicated across domain grids. The pattern was already generalized in Extras, so pushing it back to domain level is a regression.

Recommendation: (b) Composable hook. A useDirtyTracking<T>() hook keeps the Tier 3 factory lean while making dirty tracking easy to adopt. The Tier 3 factory can document a “with dirty tracking” composition example. This avoids forcing the feature on read-only consumers and avoids duplicating it across domain grids.

The two existing implementations expose different ref APIs:

MethodVendored ArdaGridExtras ArdaDataGridExtras Tier 3Proposed Tier
getGridApi()YesYes(via Tier 2)2
exportDataAsCsv()YesYes(via Tier 2)2
refreshData()Yes3b
getSelectedRows()Yes3b
selectAll()Yes3b
deselectAll()Yes3b
saveAllDrafts()YesHook
getHasUnsavedChanges()YesHook
discardAllDrafts()YesHook

Options:

  • (a) Rich ref at Tier 2 — Include getGridApi, exportDataAsCsv, getSelectedRows, selectAll, deselectAll, and refreshData on the molecule ref. Tier 3a/3b extend with additional methods.
    • Pro: Convenience methods available at the lowest level. Matches vendored ArdaGrid behavior.
    • Con: selectAll/deselectAll/getSelectedRows are trivially achieved via getGridApi(). Adding them to the ref creates redundant API surface that must be maintained.
  • (b) Minimal ref at Tier 2, extended at Tier 3b — Tier 2 exposes only getGridApi() and exportDataAsCsv(). Tier 3a inherits the Tier 2 ref unchanged. Tier 3b adds selection convenience methods (selectAll, deselectAll, getSelectedRows, refreshData) for vendored ArdaGrid compatibility. Dirty tracking methods live on the useDirtyTracking hook return value, not on the component ref.
    • Pro: Small, stable ref at Tier 2. Tier 3b provides the full convenience API expected by ArdaGrid consumers. Clean separation of concerns.
    • Con: Consumers using Tier 3a who want selectAll must use getGridApi().
  • (c) No ref at Tier 2 — Use only callback props for all interactions. No imperative API.
    • Pro: Purely declarative component. Easier to reason about in React.
    • Con: Some operations (CSV export, programmatic selection) are inherently imperative and awkward to express as props. Breaks from AG Grid’s own imperative patterns.

Recommendation: (b) Minimal ref at Tier 2, extended at Tier 3b. getGridApi() plus exportDataAsCsv() at Tier 2 gives consumers full power without bloating the ref surface. Tier 3b adds the vendored convenience methods for migration compatibility. Dirty tracking methods (per Q9) live on the hook, not on the ref.

#SummaryRecommendationDecision
Q6Feature parity with vendored ArdaGridSuperseded by Tier 3a/3b split. Tier 2 stays native. Tier 3a adds multi-sort, filtering, cell editing lifecycle. Tier 3b adds row actions, double-click, getRowClass, hasActiveSearch.See per-feature decisions in Q6 table.
Q7AtomProps<V> dependency for cell atomsGrid-specific GridCellProps<V> base type, structurally compatible with AtomProps<V> but no import dependency.Deferred. GridCellProps<V> is not needed in this project — CellInteractive (the only consumer) is out of scope. See Cell Editing Paths Analysis. To be revisited when Entity Viewer is ported.
Q8Scope of Tier 1 cell typesPriority subset of 5: text, number, boolean, date, enum. Add remaining 5 later.Agreed. Additional deliverable of a project plan to add the rest of cell variants based on the state at the end of the project. Design the overall component structure to support easy extensibility of cell types in the library or by consumers of Tier 2 & Tier 3 components.
Q9Dirty tracking locationComposable useDirtyTracking<T>() hook, separate from Tier 3a factory.Agreed.
Q10Ref API surface per tierMinimal Tier 2 ref (getGridApi, exportDataAsCsv). Tier 3a inherits unchanged. Tier 3b extends with vendored convenience methods. Dirty tracking on hook, not ref.Agreed. See detailed references in Q6 table.

During the design review, an apparent dependency between {Type}CellInteractive and {Type}CellEditor was investigated. The finding is that these are two independent editing mechanisms used in completely separate contexts. They never interact and cannot conflict.

Path 1: AG Grid Native Cell Editing (CellEditor)

Section titled “Path 1: AG Grid Native Cell Editing (CellEditor)”

Used inside the Data Grid molecule (Tier 2) and entity data grid factories (Tiers 3a/3b).

  • Trigger: User double-clicks a cell in the AG Grid.
  • Mechanism: AG Grid replaces the cell renderer with the cellEditor component. The editor implements a getValue() ref handle. When editing ends (blur, Enter, Escape), AG Grid calls getValue() and fires onCellValueChanged.
  • Configuration: enableCellEditing prop on DataGrid<T> sets AG Grid options (singleClickEdit: false, stopEditingWhenCellsLoseFocus: true). The enhanceEditableColumnDefs() function sets editable: true, valueGetter, valueSetter, and cellEditor on individual columns.
  • Editor components: {Type}CellEditor (e.g., ArdaTextCellEditor), plus domain-specific editors like SelectCellEditor and TypeaheadCellEditor.
  • Scope: In scope for this project (Tiers 1-3b).

Path 2: Mode-Based Inline Editing (CellInteractive)

Section titled “Path 2: Mode-Based Inline Editing (CellInteractive)”

Used inside the Entity Viewer organism — a form/detail panel, not a grid.

  • Trigger: Parent component (Entity Viewer shell) sets mode: 'edit' on the field.
  • Mechanism: {Type}CellInteractive extends AtomProps<V> and manages its own display/edit state. In display mode it renders {Type}CellDisplay; in edit mode it renders an internal {Type}CellInlineEditor function (not the AG Grid CellEditor). Uses onComplete/onCancel callbacks for edit lifecycle.
  • Configuration: The Entity Viewer’s field descriptor specifies a component property pointing to a CellInteractive component.
  • Scope: Out of scope for this project. The Entity Viewer is a separate deliverable.
  • CellInteractive is never used as a cellRenderer in any AG Grid column definition. A codebase search confirmed zero usages.
  • CellInteractive does not import or delegate to CellEditor. It has its own internal inline editor.
  • The two paths are architecturally isolated: AG Grid editing operates within the grid’s cell lifecycle; mode-based editing operates within the Entity Viewer’s form lifecycle.
  • GridCellProps<V> (the proposed canary base type for Interactive cells) is only needed by CellInteractive, which has no consumer in this project.

Since CellInteractive has no consumer within the grid context (Tiers 1-3b), it should be deferred to the Entity Viewer project. This reduces Tier 1 from 3 variants per type (Display, Editor, Interactive) to 2 variants (Display, Editor), and eliminates the need for GridCellProps<V> in this project.

During implementation planning, the vendored codebase was inspected for existing memo/notes and color cell implementations that can serve as the basis for the new canary atoms.

Three vendored components form the notes editing pattern:

  • NotesCell (~90 lines, columnPresets.tsx lines 883-976): A cell renderer that shows a chat bubble icon (HiOutlineChatBubbleBottomCenterText) when notes exist, or a dash button when empty. Clicking opens NoteModal via createPortal to document.body. Receives onNotesSave callback via params.context. Event propagation stopped to prevent grid row selection.
  • CardNotesCell (~90 lines, columnPresets.tsx lines 979-1072): Identical pattern to NotesCell but for item.cardNotesDefault field.
  • NoteModal (~138 lines, vendored/.../common/NoteModal.tsx): Full-screen overlay modal (500px, centered, dark backdrop). Props: isOpen, editable, initialValue, onSave, title. Textarea with min-height 100px, resizable. Done button saves; X button closes.

The existing TextCellDisplay in extras also has a truncation pattern: value.slice(0, maxLength) + '…' with CSS truncate class.

Canary approach: Three memo components cover both new and vendored-compatible patterns:

  • MemoCellDisplay — truncated text + Radix HoverCard hover overlay (new pattern)
  • MemoCellEditor — AG Grid textarea editor, draws from NoteModal textarea pattern
  • MemoButtonCell — vendored-compatible icon-button + modal pattern, with onSave callback (framework-agnostic). A createMemoButtonCellEditor() factory wraps it for AG Grid.

Two vendored implementations:

  • ColorCellEditor (~125 lines, ItemTableAGGrid.tsx lines 157-282): Class-based AG Grid editor using raw DOM (not React). Implements full ICellEditorParams lifecycle. Native <select> with 10 options. Auto-opens dropdown on focus. Commits and stops editing on selection change.
  • Color renderer (~30 lines, columnPresets.tsx lines 1787-1817): 4x4px rounded swatch (backgroundColor: hex) + label. Hardcoded colorMap:
RED: #EF4444, GREEN: #22C55E, BLUE: #3B82F6, YELLOW: #FDE047,
ORANGE: #F97316, PURPLE: #A855F7, PINK: #EC4899, GRAY: #6B7280,
BLACK: #000000, WHITE: #FFFFFF
  • colorOptions (8 entries in constants.ts): Missing BLACK and WHITE vs the editor’s 10.

Canary approach: React components replacing raw-DOM vendored code:

  • ColorCellDisplay — replicates vendored swatch + label pattern
  • ColorCellEditor — React forwardRef with shadcn/Radix Select or Popover, configurable colors: ColorOption[] prop, vendored 10-color map as default
  • Resolve Q6–Q10 decisions before proceeding to the design document.
  • Review the Extras hierarchy in the Storybook site to assess visual and behavioral completeness.
  • Define acceptance criteria for the first Canary List View deliverable.