Run 4: Cell Editor Upgrade
Promote callil’s SelectCellEditor from molecules/item-grid/ to atoms/grid/select/ as a first-class canary atom, replacing the jmpicnic EnumCellEditor. Generalizes the options format to accept both SelectOption[] and Record<string, string>.
Working directory: /Users/jmp/code/arda/ux-prototype/ (main clone, branch jmpicnic/component-consolidation)
Governs: analysis/grid-integration.md (decision #2)
Entry Criteria
Section titled “Entry Criteria”| # | Criterion | Verification Command | Expected Output |
|---|---|---|---|
| 1 | Run 3 exit gate passed | bash validate-exit.sh (Run 3) | ALL CHECKS PASSED |
| 2 | Branch jmpicnic/component-consolidation is checked out | git -C /Users/jmp/code/arda/ux-prototype branch --show-current | jmpicnic/component-consolidation |
| 3 | Working tree is clean | git -C /Users/jmp/code/arda/ux-prototype status --porcelain | (empty) |
| 4 | SelectCellEditor molecule exists (Run 3 deliverable) | test -f /Users/jmp/code/arda/ux-prototype/src/components/canary/molecules/item-grid/select-cell-editor.tsx && echo EXISTS | EXISTS |
| 5 | EnumCellEditor atom exists (to be replaced) | test -d /Users/jmp/code/arda/ux-prototype/src/components/canary/atoms/grid/enum && echo EXISTS | EXISTS |
Artifact Specifications
Section titled “Artifact Specifications”| Artifact | Path (relative to ux-prototype/src/) | Format | Description |
|---|---|---|---|
| SelectCellEditor component | components/canary/atoms/grid/select/select-cell-editor.tsx | TSX | Promoted cell editor with generalized options format |
| SelectCellDisplay component | components/canary/atoms/grid/select/select-cell-display.tsx | TSX | Display renderer for select values (migrated from EnumCellDisplay) |
| Factory function | components/canary/atoms/grid/select/select-cell-editor.tsx | TSX | createSelectCellEditor factory with StaticConfig baked in |
| Barrel export | components/canary/atoms/grid/select/index.ts | TS | Public exports for SelectCellEditor, SelectCellDisplay, factory, types |
| Stories | components/canary/atoms/grid/select/select.stories.tsx | TSX | Default, WithManyOptions, KeyboardNavigation, BothFormats |
| Unit tests | components/canary/atoms/grid/select/select.test.tsx | TSX | Highlight cycling, Enter/Escape, ARIA roles, checkmark, options format |
| VRT baseline | Playwright VRT config | PNG | SelectCellEditor popup screenshot |
Task List
Section titled “Task List”| # | Task | Persona | Depends On | Status | Acceptance Criteria |
|---|---|---|---|---|---|
| 4.1 | Create atoms/grid/select/ directory with SelectCellEditor component | front-end-engineer | — | Pending | select-cell-editor.tsx exists with promoted SelectCellEditor; compiles cleanly; accepts both SelectOption[] and Record<string, string> |
| 4.2 | Generalize options format to accept both input types | front-end-engineer | 4.1 | Pending | Component normalizes Record<string, string> to SelectOption[] internally; both formats render identically |
| 4.3 | Add StaticConfig/RuntimeConfig split and createSelectCellEditor factory | front-end-engineer | 4.2 | Pending | SelectCellEditorStaticConfig, SelectCellEditorProps, createSelectCellEditor factory exported; consistent with createTextCellEditor pattern |
| 4.4 | Create SelectCellDisplay for non-editing mode | front-end-engineer | 4.1 | Pending | select-cell-display.tsx renders selected value label; handles missing value gracefully |
| 4.5 | Add stories: Default, WithManyOptions, KeyboardNavigation, BothFormats | front-end-engineer | 4.3, 4.4 | Pending | All 4 stories render in Storybook; stories demonstrate key behaviors |
| 4.6 | Add unit tests: highlight cycling, Enter/Escape, ARIA roles, checkmark, options format | front-end-engineer | 4.3 | Pending | All tests pass; coverage includes both options formats, keyboard navigation wrap-around, cancel via Escape |
| 4.7 | Add play function with step DSL | front-end-engineer | 4.5 | Pending | Play function uses step DSL; steps: open editor -> Arrow Down 3x -> Enter -> verify value changed |
| 4.8 | Update all EnumCellEditor consumers to use SelectCellEditor | front-end-engineer | 4.3 | Pending | All files that import from atoms/grid/enum updated to import from atoms/grid/select; npx tsc --noEmit passes |
| 4.9 | Delete atoms/grid/enum/ directory | front-end-engineer | 4.8 | Pending | Directory removed; no remaining references to EnumCellEditor or createEnumCellEditor in canary code |
| 4.10 | Update canary barrel exports | front-end-engineer | 4.9 | Pending | canary.ts exports SelectCellEditor/SelectCellDisplay/factory/types instead of Enum equivalents |
| 4.11 | Full verification gate | front-end-engineer | 4.1-4.10 | Pending | npm run lint + npx tsc --noEmit + npm run test + npm run build-storybook all pass; no EnumCellEditor references in canary code; existing grid stories with enum columns still render |
| 4.12 | Playwright MCP visual verification | front-end-engineer | 4.11 | Pending | SelectCellEditor popup renders correctly; keyboard navigation works (Arrow Down cycling, Enter to select, Escape to cancel); checkmark on selected item; existing enum grid stories show no regression |
| 4.13 | VRT baseline for SelectCellEditor popup | front-end-engineer | 4.12 | Pending | VRT baseline captured for SelectCellEditor popup story; npx playwright test --project=vrt passes |
Task Details
Section titled “Task Details”Task 4.1: Create SelectCellEditor Atom
Section titled “Task 4.1: Create SelectCellEditor Atom”Source reference: /Users/jmp/code/arda/ux-prototype/src/components/canary/molecules/item-grid/select-cell-editor.tsx (Run 3 deliverable)
Create src/components/canary/atoms/grid/select/select-cell-editor.tsx by promoting the molecule-level SelectCellEditor. The promoted version:
- Moves from
molecules/item-grid/toatoms/grid/select/(the canonical location for grid cell editor atoms). - Retains all existing behavior: keyboard navigation (ArrowUp/ArrowDown cycling, Enter to select, Escape to cancel), checkmark indicator, scroll-into-view, ARIA listbox/option roles,
useGridCellEditorintegration, popup styling (max-height 240px, bg-popover). - The
valuesprop type is generalized in Task 4.2.
Task 4.2: Generalize Options Format
Section titled “Task 4.2: Generalize Options Format”The current SelectCellEditor accepts only SelectOption[] (with { label: string, value: string }). Per the grid-integration decision, generalize to also accept Record<string, string>:
export type SelectOptions = SelectOption[] | Record<string, string>;
// Internal normalization:function normalizeOptions(options: SelectOptions): SelectOption[] { if (Array.isArray(options)) return options; return Object.entries(options).map(([value, label]) => ({ value, label }));}The component normalizes internally, so all rendering and keyboard logic uses SelectOption[] regardless of input format. The Record<string, string> format is a convenience for simple enum-like values (key = stored value, value = display label), matching the pattern used by the existing EnumCellEditor.
Task 4.3: StaticConfig/RuntimeConfig Split and Factory
Section titled “Task 4.3: StaticConfig/RuntimeConfig Split and Factory”Follow the existing canary cell editor convention (see atoms/grid/text/, atoms/grid/enum/ for reference):
/** Design-time configuration baked into the factory. */export interface SelectCellEditorStaticConfig { /** Available options. Can be SelectOption[] or Record<string, string>. */ options: SelectOptions;}
/** Runtime props from AG Grid. */export interface SelectCellEditorProps { value: string | null; onValueChange: (value: string | null) => void; stopEditing: (cancel?: boolean) => void;}
/** Handle exposed via useGridCellEditor. */export interface SelectCellEditorHandle { isCancelAfterEnd: () => boolean;}
/** Factory: bakes static config into a cell editor component. */export function createSelectCellEditor( staticConfig: SelectCellEditorStaticConfig): React.ComponentType<SelectCellEditorProps>;The factory returns a component with options baked in from static config, matching the pattern of createEnumCellEditor, createTextCellEditor, etc.
Task 4.4: Create SelectCellDisplay
Section titled “Task 4.4: Create SelectCellDisplay”Migrate from atoms/grid/enum/enum-cell-display.tsx. The display component renders the label for a stored value:
export interface SelectCellDisplayStaticConfig { options: SelectOptions;}
export interface SelectCellDisplayProps { value: string | null;}Behavior: looks up the value in the normalized options array and renders the label. If value is not found, renders the raw value (or empty for null).
Task 4.5: Add Stories
Section titled “Task 4.5: Add Stories”Create src/components/canary/atoms/grid/select/select.stories.tsx with 4 stories:
- Default: Grid with a select column using 4-5 options. Click a cell to open the editor.
- WithManyOptions: 20+ options to demonstrate scroll behavior with the 240px max-height.
- KeyboardNavigation: Same as Default but the play function demonstrates Arrow Down/Up cycling, Enter to select, Escape to cancel.
- BothFormats: Two grids side by side — one using
SelectOption[], one usingRecord<string, string>— to show both formats produce identical UI.
All stories use the createSelectCellEditor factory with cellEditorPopup: true in the column definition.
Task 4.6: Add Unit Tests
Section titled “Task 4.6: Add Unit Tests”Create src/components/canary/atoms/grid/select/select.test.tsx testing:
- Highlight cycling: Arrow Down wraps from last to first; Arrow Up wraps from first to last.
- Enter selects highlighted: Enter calls
onValueChangewith the highlighted option’s value and callsstopEditing. - Escape cancels: Escape sets
isCancelAfterEndto true and callsstopEditing(true). - ARIA roles: Container has
role="listbox", each option hasrole="option", selected option hasaria-selected="true". - Checkmark rendering: The currently selected value shows a Check icon.
- Both options formats:
Record<string, string>is normalized correctly and renders identically toSelectOption[]. - SelectCellDisplay: Renders label for known value, raw value for unknown, empty for null.
Task 4.7: Add Play Function with Step DSL
Section titled “Task 4.7: Add Play Function with Step DSL”Add to the KeyboardNavigation story:
play: async ({ canvasElement, step }) => { const canvas = within(canvasElement);
await step('Open the select editor', async () => { // Double-click on a cell with the select editor });
await storyStepDelay();
await step('Navigate down 3 times', async () => { await userEvent.keyboard('{ArrowDown}{ArrowDown}{ArrowDown}'); });
await storyStepDelay();
await step('Select with Enter and verify value changed', async () => { await userEvent.keyboard('{Enter}'); // Verify the cell shows the newly selected value });};Task 4.8: Update EnumCellEditor Consumers
Section titled “Task 4.8: Update EnumCellEditor Consumers”Files that reference EnumCellEditor, createEnumCellEditor, or import from atoms/grid/enum:
Canary code (must update):
src/canary.ts— barrel exportssrc/components/canary/molecules/data-grid/data-grid.stories.tsx— story using enum editorsrc/components/canary/atoms/grid/enum/enum.stories.tsx— replaced by new storiessrc/components/canary/atoms/grid/enum/enum.test.tsx— replaced by new tests
Extras code (do NOT modify — extras layer has its own copy):
src/components/extras/atoms/grid/enum/— leave as-is
Canary-refactor code (update if referencing canary enum):
src/canary-refactor/components/columnPresets.tsx— update import
Documentation (update references):
src/docs/workflows/using-the-design-system.mdx— update enum references to select
For each consumer: replace EnumCellEditor with SelectCellEditor, createEnumCellEditor with createSelectCellEditor, EnumCellDisplay with SelectCellDisplay, and update import paths from atoms/grid/enum to atoms/grid/select.
Task 4.9: Delete atoms/grid/enum/ Directory
Section titled “Task 4.9: Delete atoms/grid/enum/ Directory”Remove src/components/canary/atoms/grid/enum/ and all contents:
enum-cell-display.tsxenum-cell-editor.tsxenum.stories.tsxenum.test.tsxindex.ts
Verify no remaining references: grep -r 'atoms/grid/enum' src/ --include='*.ts' --include='*.tsx' | grep -v extras/ | grep -v node_modules
Important: Do NOT delete src/components/extras/atoms/grid/enum/ — the extras layer maintains its own copy.
Task 4.10: Update Canary Barrel Exports
Section titled “Task 4.10: Update Canary Barrel Exports”In src/canary.ts, replace:
// Cell atoms: enumexport { EnumCellDisplay, EnumCellEditor, createEnumCellEditor,} from './components/canary/atoms/grid/enum';export type { EnumCellDisplayProps, EnumCellDisplayStaticConfig, EnumCellEditorProps, EnumCellEditorStaticConfig, EnumCellEditorHandle,} from './components/canary/atoms/grid/enum';With:
// Cell atoms: selectexport { SelectCellDisplay, SelectCellEditor, createSelectCellEditor,} from './components/canary/atoms/grid/select';export type { SelectCellDisplayProps, SelectCellDisplayStaticConfig, SelectCellEditorProps, SelectCellEditorStaticConfig, SelectCellEditorHandle, SelectOption, SelectOptions,} from './components/canary/atoms/grid/select';Task 4.11: Full Verification Gate
Section titled “Task 4.11: Full Verification Gate”Run all checks in sequence:
npm run lint— passnpx tsc --noEmit— passnpm run test— pass (new select tests + existing tests)npm run build-storybook— pass- Verify no
EnumCellEditorreferences remain in canary code:Expected: zero matches.Terminal window grep -r 'EnumCellEditor\|createEnumCellEditor\|atoms/grid/enum' src/ --include='*.ts' --include='*.tsx' | grep -v extras/ | grep -v node_modules - Verify existing grid stories that previously used enum columns still render (the stories should now use SelectCellEditor transparently).
Task 4.12: Playwright MCP Visual Verification
Section titled “Task 4.12: Playwright MCP Visual Verification”Start Storybook (npm run dev in ux-prototype/) and use Playwright MCP to:
- Navigate to SelectCellEditor > Default story. Click a cell to open the popup. Screenshot.
- Navigate to SelectCellEditor > WithManyOptions story. Open popup, scroll to bottom. Screenshot.
- Navigate to SelectCellEditor > KeyboardNavigation story. Run the play function. Verify:
- Arrow Down moves highlight downward
- Highlight wraps from bottom to top
- Enter selects the highlighted option
- Escape cancels without changing value
- Navigate to SelectCellEditor > BothFormats story. Verify both grids render identically.
- Navigate to an existing grid story that previously used EnumCellEditor (e.g., DataGrid stories). Verify no visual regression.
Task 4.13: VRT Baseline
Section titled “Task 4.13: VRT Baseline”Generate VRT baseline for the SelectCellEditor popup:
npx playwright test --project=vrt --grep "SelectCellEditor"This captures the custom dropdown popup appearance (replacing the native <select> that EnumCellEditor used, which VRT never captured because native dropdowns are OS-rendered).
Internal Dependency Graph
Section titled “Internal Dependency Graph”4.1 (create atom) ──→ 4.2 (generalize options) ──→ 4.3 (factory + config)4.1 ──→ 4.4 (display component)
4.3, 4.4 ──→ 4.5 (stories)4.3 ──→ 4.6 (unit tests)4.5 ──→ 4.7 (play function)4.3 ──→ 4.8 (update consumers)4.8 ──→ 4.9 (delete enum/)4.9 ──→ 4.10 (barrel exports)
4.1-4.10 ──→ 4.11 (verification gate)4.11 ──→ 4.12 (Playwright MCP)4.12 ──→ 4.13 (VRT baseline)Tasks 4.1-4.4 form the component creation phase. Tasks 4.5-4.7 form the test/story phase (can overlap with 4.8). Tasks 4.8-4.10 form the migration/cleanup phase. Tasks 4.11-4.13 are sequential verification.
Exit Criteria
Section titled “Exit Criteria”| # | Criterion | Verification Command | Expected Output |
|---|---|---|---|
| 1 | Lint passes | npm run lint (in ux-prototype/) | Exit 0 |
| 2 | TypeScript compiles | npx tsc --noEmit (in ux-prototype/) | Exit 0 |
| 3 | All tests pass | npm run test (in ux-prototype/) | Exit 0, all tests pass |
| 4 | Storybook builds | npm run build-storybook (in ux-prototype/) | Exit 0 |
| 5 | SelectCellEditor atom exists | test -f src/components/canary/atoms/grid/select/index.ts && echo EXISTS | EXISTS |
| 6 | EnumCellEditor directory deleted | test -d src/components/canary/atoms/grid/enum && echo EXISTS || echo DELETED | DELETED |
| 7 | No EnumCellEditor refs in canary code | grep -r 'EnumCellEditor|createEnumCellEditor' src/ --include='*.ts' --include='*.tsx' | grep -v extras/ | grep -v node_modules | wc -l | 0 |
| 8 | No atoms/grid/enum imports in canary code | grep -r 'atoms/grid/enum' src/ --include='*.ts' --include='*.tsx' | grep -v extras/ | grep -v node_modules | wc -l | 0 |
| 9 | New stories render | npm run build-storybook passes (covers story indexing) | Exit 0 |
| 10 | Existing grid stories with enum columns still work | DataGrid stories build + test pass | Included in criteria 3 and 4 |
| 11 | VRT baseline captured | test -f test-results/vrt/*.png && echo EXISTS (path may vary by VRT config) | Baseline PNG exists |
| 12 | canary.ts exports SelectCellEditor | grep 'SelectCellEditor' src/canary.ts | Match found |
| 13 | Extras enum untouched | test -f src/components/extras/atoms/grid/enum/index.ts && echo EXISTS | EXISTS |
Agent Prompt Templates
Section titled “Agent Prompt Templates”front-end-engineer (Tasks 4.1-4.13)
Section titled “front-end-engineer (Tasks 4.1-4.13)”You are promoting the callil SelectCellEditor from a molecule to a first-class canary atom, replacing the EnumCellEditor. Follow the existing cell editor atom conventions in atoms/grid/text/ and atoms/grid/number/ for structure.
Key decisions (already made, do not revisit):
- SelectCellEditor replaces EnumCellEditor entirely (per grid-integration.md decision #2)
- Options accept both
SelectOption[]andRecord<string, string>formats - Follow StaticConfig/RuntimeConfig split with
createSelectCellEditorfactory - Extras layer enum (
src/components/extras/atoms/grid/enum/) is NOT touched
File structure for atoms/grid/select/:
select-cell-editor.tsx -- component + factory + typesselect-cell-display.tsx -- display renderer + typesselect.stories.tsx -- 4 stories with play functions using step DSLselect.test.tsx -- unit testsindex.ts -- barrel exportsConsumer update scope (canary code only, not extras):
src/canary.ts— barrel exportssrc/components/canary/molecules/data-grid/data-grid.stories.tsx— story referencessrc/canary-refactor/components/columnPresets.tsx— import updatesrc/docs/workflows/using-the-design-system.mdx— documentation references
After all changes, run the full verification gate: npm run lint, npx tsc --noEmit, npm run test, npm run build-storybook.
Verify zero EnumCellEditor references remain in canary code (excluding extras/): grep -r 'EnumCellEditor\|createEnumCellEditor\|atoms/grid/enum' src/ --include='*.ts' --include='*.tsx' | grep -v extras/ | grep -v node_modules
Implementation Output
Section titled “Implementation Output”At run completion, write the following to implementation/run-4-cell-editor-upgrade/:
| Artifact | File | Description |
|---|---|---|
| Run summary | summary.md | What was done, decisions made during implementation, deviations from plan |
| Byproducts log | byproducts.md | Discovered issues, TODOs, observations for later runs |
| Validation output | validation-output.txt | Stdout from validate-exit.sh execution |
| Session log | session-log.md | Agent session IDs, timestamps, notable events |
Path: documentation/src/content/docs/roadmap/backlog/requested/callil-consolidation/implementation/run-4-cell-editor-upgrade/
Handoff
Section titled “Handoff”Artifacts Consumed (from previous runs)
Section titled “Artifacts Consumed (from previous runs)”| Artifact | Source Run | Path/Location |
|---|---|---|
| SelectCellEditor molecule (source reference) | Run 3 | src/components/canary/molecules/item-grid/select-cell-editor.tsx |
| EnumCellEditor atom (to be replaced) | Pre-existing | src/components/canary/atoms/grid/enum/ |
| EnumCellDisplay atom (pattern reference) | Pre-existing | src/components/canary/atoms/grid/enum/enum-cell-display.tsx |
| Cell editor factory convention | Pre-existing | src/components/canary/atoms/grid/text/, atoms/grid/number/ |
| Canary barrel exports | Pre-existing | src/canary.ts |
Artifacts Produced (for subsequent runs)
Section titled “Artifacts Produced (for subsequent runs)”| Artifact | Consumer Run | Path/Location |
|---|---|---|
| SelectCellEditor atom | Run 5, Run 6 | src/components/canary/atoms/grid/select/ |
createSelectCellEditor factory | Run 5 (entity-data-grid), Run 6 (item-grid) | src/components/canary/atoms/grid/select/ |
| Updated canary.ts barrel | Run 7 (final barrel review) | src/canary.ts |
| VRT baseline for SelectCellEditor popup | Run 8 (acceptance comparison) | VRT test results |
Copyright: © Arda Systems 2025-2026, All rights reserved