Files
suaveplan/openspec/changes/enterprise-browser-react-kanban/proposal.md
2026-05-27 23:29:53 +01:00

6.0 KiB

OpenSpec Change: enterprise-browser-react-kanban

  • Change ID: enterprise-browser-react-kanban
  • Package: @suaveplan/react-kanban
  • Category: browser
  • Target path: browser/react/react-kanban
  • Status: IN PROGRESS
  • Date: 2026-04-24
  • Reference spec: /mnt/s/web/suaveplan/enterprise/react-kanban.md

Summary

Deliver @suaveplan/react-kanban on the genesis branch at enterprise quality. The package provides a headless useKanban hook with pure helpers (findCardColumn, isColumnAtWipLimit, moveCardInBoard, moveColumnInBoard, validateMove), plus reference Board, Column, Card components with Storybook stories. Genesis fixes the broken export map (missing ./core/column/column and ./core/card/card source files), splits the over-cap board.ts (320 lines) and board.test.ts (541 lines), adds Zod schemas for board state, OTel metrics, keyboard-drag support, and axe-core clean stories. No react-dom in runtime deps.


Current-state audit (genesis)

Fresh build — does not exist on genesis yet.


Available workspace deps on genesis

Package Role
@suaveplan/error (workspace:^) WIP_LIMIT_EXCEEDED, INVALID_MOVE, CARD_NOT_FOUND
@suaveplan/logger (workspace:^) Structured logging
@suaveplan/schema-registry (workspace:^) Board / card schema registration
zod (catalog:forms) Board, column, card Zod schemas
@opentelemetry/api (catalog:observability) Move / WIP-violation metrics
tslib (catalog:build) Runtime helpers

Dev: vitest, @vitest/coverage-istanbul, happy-dom, @testing-library/react, @suaveplan/testing, @suaveplan/testing-dom.

No react-dom in runtime dependencies — peer only.


Preserved public API surface

// Hook
function useKanban(config: KanbanConfig): [KanbanState, KanbanActions]

// Pure helpers
function findCardColumn(board: KanbanBoard, cardId: string): KanbanColumn | null
function isColumnAtWipLimit(column: KanbanColumn): boolean
function moveCardInBoard(board: KanbanBoard, source: DragSource, dest: DropTarget): KanbanBoard
function moveColumnInBoard(board: KanbanBoard, fromIdx: number, toIdx: number): KanbanBoard
function validateMove(board: KanbanBoard, source: DragSource, dest: DropTarget): MoveResult

// Reference components (new)
function Board(props: BoardProps): JSX.Element
function Column(props: ColumnProps): JSX.Element
function Card(props: CardProps): JSX.Element

// Types
interface KanbanBoard { columns: KanbanColumn[] }
interface KanbanColumn { id: string; title: string; wipLimit?: number; cards: KanbanCard[] }
interface KanbanCard<T = unknown> { id: string; title: string; data?: T }
interface KanbanConfig { board: KanbanBoard; onMove?(src: DragSource, dst: DropTarget): void; enforceWipLimits?: boolean }
interface KanbanState { board: KanbanBoard; dragging: DragSource | null }
interface KanbanActions { moveCard(src: DragSource, dst: DropTarget): MoveResult; moveColumn(from: number, to: number): void }

Subpath exports: ".", "./core/board/board", "./core/column/column", "./core/card/card", "./utils/drag-drop", "./components/board".


New additions for genesis

1. Implement the missing Column and Card modules

Main-branch exported ./core/column/column and ./core/card/card from package.json but had no source files. Genesis ships src/core/column/column.ts and src/core/card/card.ts with typed interfaces, helpers, and tests.

2. Split board.ts and board.test.ts

board.ts (320 lines) → board.ts + moves.ts + wip.ts + swimlanes.ts, each ≤ 300 lines. board.test.ts (541 lines) → split by responsibility: board.test.ts, moves.test.ts, wip.test.ts, each ≤ 500 lines.

3. Reference Board, Column, Card components + stories

src/components/ — reference visual components built on useKanban. Stories: default, playground, WIP-limit enforcement, swimlane grouping.

4. Keyboard-drag support

Arrow-key move (card left/right/up/down) wired in useKanban; reference components respect it. Axe-core clean.

5. Zod schemas

KanbanBoardSchema, KanbanColumnSchema, KanbanCardSchema registered in @suaveplan/schema-registry.

6. OTel metrics

Counter kanban.move_total{success}, counter kanban.wip_violation_total, histogram kanban.move_latency_ms.

7. Error codes

WIP_LIMIT_EXCEEDED, INVALID_MOVE, CARD_NOT_FOUND.


File layout

browser/react/react-kanban/
  package.json
  tsconfig.json
  vitest.config.ts
  src/
    index.ts
    error-codes.ts
    core/
      board/
        board.ts              (useKanban hook, ≤300 lines)
        board.test.ts
        board.md
        moves.ts              (move helpers)
        moves.test.ts
        wip.ts                (WIP-limit enforcement)
        wip.test.ts
        swimlanes.ts          (swimlane grouping)
        swimlanes.test.ts
        index.ts
      column/
        column.ts
        column.test.ts
        column.md
        column.types.ts
        index.ts
      card/
        card.ts
        card.test.ts
        card.md
        card.types.ts
        index.ts
    components/
      board/
        board.tsx
        board.stories.tsx
        board.test.tsx
        board.md
        index.ts
      column/
        column.tsx
        column.stories.tsx
        column.test.tsx
        column.md
        index.ts
      card/
        card.tsx
        card.stories.tsx
        card.test.tsx
        card.md
        index.ts
    utils/
      drag-drop/
        drag-drop.ts
        drag-drop.test.ts
        drag-drop.md
        index.ts
    schema/
      board-schema/
        board-schema.ts
        board-schema.md
        index.ts
    types/
      common.ts
      common.md
    constants/
      index.ts
  tests/
    integration.test.ts

Impact

Downstream: any Suaveplan browser app with a kanban board.

Breaking changes: None — new components and modules are additive. board.ts split is internal.

Prerequisites: @suaveplan/error, @suaveplan/schema-registry on Verdaccio.