Skip to content

Phase 2: Cell Atoms

Port 5 cell type directories from extras and create 2 new cell types (memo, color). Each type has 2 components (Display, Editor) plus an index barrel, tests, and stories.

Corresponds to Wave 1 of the Porting Strategy.

Phase 1 must be complete. Verify by checking artifacts — do NOT re-run tsc or lint.

Verify before starting:

  • PaginationData exists: ls src/types/canary/pagination.ts
  • Formatters exist: ls src/components/canary/atoms/shared/formatters.ts
  • AG theme CSS exists: ls src/styles/canary/ag-theme-arda.css
  • No extras imports in canary files: grep -r '@/components/extras\|@/types/extras' src/types/canary/ src/components/canary/ src/styles/canary/ returns no results
  • Phase 1 exit gate passed: check for npx tsc --noEmit and npm run lint success in the Phase 1 terminal output (do not re-run)

For each type: copy Display + Editor + index + tests + stories from extras/atoms/grid/{type}/ to canary/atoms/grid/{type}/. Update imports per the Porting Guidelines. Do NOT copy {type}-cell-interactive.tsx (deferred to Entity Viewer project per Cell Editing Paths Analysis). Adapt Storybook title paths from Components/Extras/... to Components/Canary/.... Copy, adapt, and improve tests if appropriate.

  • Source: components/extras/atoms/grid/text/
  • Target: components/canary/atoms/grid/text/
  • Files: text-cell-display.tsx, text-cell-editor.tsx, index.ts, text.test.tsx, text.stories.tsx
  • Import updates: formatters@/components/canary/atoms/shared/formatters
  • Dependencies: formatters, cn*
  • Source: components/extras/atoms/grid/number/
  • Target: components/canary/atoms/grid/number/
  • Files: number-cell-display.tsx, number-cell-editor.tsx, index.ts, number.test.tsx, number.stories.tsx
  • Import updates: formatters@/components/canary/atoms/shared/formatters
  • Dependencies: formatters, cn*
  • Source: components/extras/atoms/grid/boolean/
  • Target: components/canary/atoms/grid/boolean/
  • Files: boolean-cell-display.tsx, boolean-cell-editor.tsx, index.ts, boolean.test.tsx, boolean.stories.tsx
  • Import updates: formatters@/components/canary/atoms/shared/formatters
  • Dependencies: formatters, cn*, lucide-react (Check, X icons)
  • Source: components/extras/atoms/grid/date/
  • Target: components/canary/atoms/grid/date/
  • Files: date-cell-display.tsx, date-cell-editor.tsx, index.ts, date.test.tsx, date.stories.tsx
  • Import updates: formatters@/components/canary/atoms/shared/formatters; getBrowserTimezone/getTimezoneAbbreviation already at @/types/canary/date-time (no change)
  • Dependencies: formatters, cn*, getBrowserTimezone*, getTimezoneAbbreviation*
  • Source: components/extras/atoms/grid/enum/
  • Target: components/canary/atoms/grid/enum/
  • Files: enum-cell-display.tsx, enum-cell-editor.tsx, index.ts, enum.test.tsx, enum.stories.tsx
  • Import updates: minimal (enum display has no imports from extras)
  • Dependencies: cn*

Create new cell type for multi-line text (notes, descriptions). Provides three interaction patterns: inline display with hover, AG Grid native textarea editing, and vendored-compatible button + modal. See Vendored Memo Reference for the source implementations.

  • Target: components/canary/atoms/grid/memo/
  • Files: memo-cell-display.tsx, memo-cell-editor.tsx, memo-button-cell.tsx, create-memo-button-cell-editor.tsx, index.ts, memo.test.tsx, memo.stories.tsx

MemoCellDisplay — Inline truncated display with hover overlay:

  • Props: value?: string, maxLength?: number (truncation point), hoverDelay?: number (ms before overlay, default 500)
  • Behavior: Renders truncated text with ellipsis (reuses TextCellDisplay truncation pattern). On hover after hoverDelay, shows full text in a Radix UI Tooltip overlay (per Q12 decision and PQ2 decision). Upgrade to react-hover-card if layout proves too constrained.
  • Dependencies: cn*, @radix-ui/react-tooltip (already installed)

MemoCellEditor — AG Grid native textarea editor:

  • Props: value?: string, stopEditing?(cancel?: boolean): void
  • Behavior: Multi-line <textarea> for AG Grid cell editing. Draws from vendored NoteModal textarea pattern: min-height 100px, resizable, placeholder “Add a note…”. Auto-focuses on mount. Enter submits (with Shift+Enter for newline), Escape cancels.
  • Ref: getValue(): string | undefined
  • Dependencies: cn*

MemoButtonCell — Vendored-compatible icon-button + modal:

  • Props: value?: string, editable?: boolean, onSave?(value: string) => void, title?: string
  • Behavior: Based on vendored NotesCell (~90 lines). Renders icon button (chat bubble when value exists, dash when empty). Clicking opens a modal overlay (based on vendored NoteModal: 500px centered, dark backdrop, textarea or read-only view, Done/Close buttons). Calls onSave on Done. Event propagation stopped to prevent grid row selection.
  • This component is framework-agnostic — no AG Grid coupling. Usable in grid cells, forms, or standalone.
  • Dependencies: cn*, lucide-react, createPortal

createMemoButtonCellEditor(config?) — AG Grid adapter factory:

  • Wraps MemoButtonCell as an AG Grid-compatible cell editor
  • Follows existing create{Type}CellEditor(config) pattern
  • Integrates getValue() ref handle and stopEditing with MemoButtonCell’s onSave callback
  • Consumer uses it in column definitions: { field: 'notes', cellEditor: createMemoButtonCellEditor() }

Create new cell type for color values with swatch display and picker editor. See Vendored Color Reference for the source implementations.

  • Target: components/canary/atoms/grid/color/
  • Files: color-cell-display.tsx, color-cell-editor.tsx, index.ts, color.test.tsx, color.stories.tsx

ColorCellDisplay — Based on vendored color renderer:

  • Props: value?: string (color enum value, e.g., 'RED'), colorMap?: Record<string, { hex: string; name: string }>
  • Behavior: Renders a 4x4px rounded swatch (backgroundColor: hex) + label text. Based on vendored columnPresets.tsx pattern. Default color map: vendored 10-color set (RED: #EF4444, GREEN: #22C55E, BLUE: #3B82F6, YELLOW: #FDE047, ORANGE: #F97316, PURPLE: #A855F7, PINK: #EC4899, GRAY: #6B7280, BLACK: #000000, WHITE: #FFFFFF). Fallback to gray for unmapped values.
  • Dependencies: cn*

ColorCellEditor — React replacement for vendored raw-DOM editor:

  • StaticConfig props: colors: ColorOption[] where ColorOption = { value: string; label: string; hex: string }. Configurable palette per Q13 decision. Default: vendored 10-color map.
  • Props: value?: string, stopEditing?(cancel?: boolean): void
  • Behavior: Dropdown with color swatches using shadcn/Radix Select or Popover. Click to select. Escape cancels. Replaces vendored class-based raw-DOM ColorCellEditor (~125 lines) with a React forwardRef component.
  • Ref: getValue(): string | undefined
  • Dependencies: cn*, shadcn/Radix primitives
  • 7 cell type directories exist under components/canary/atoms/grid/
  • Ported types (text, number, boolean, date, enum): Display, Editor, index barrel, test file, story file
  • Memo type: Display, Editor, MemoButtonCell, createMemoButtonCellEditor factory, index barrel, test file, story file
  • Color type: Display, Editor, index barrel, test file, story file
  • No *-cell-interactive.tsx files ported
  • No imports from extras or vendored in any canary file
  • All unit tests pass: npm run test
  • All stories render in Storybook under Components/Canary/Atoms/Grid/{Type}
  • npm run lint passes