4.0 KiB
4.0 KiB
Tasks: enterprise-react-dnd
Change ID: enterprise-react-dnd
Package: @suaveplan/react-dnd
Target path: react/ui/react-dnd
Prerequisite packages on Verdaccio: @suaveplan/error, @suaveplan/types
0. Pre-flight (non-negotiable)
- 0.1 Confirm clean working tree (
git statusreports nothing) and no in-flight worktrees with uncommitted work (git worktree listthengit statusin each) - 0.2 If any worktrees exist under
.claude/worktrees/, account for each one: still-active agent, work-pending-merge, or stale-and-prune. Stale worktrees MUST be removed (git worktree remove) before opening a new one — code that has been lost twice on main is the failure mode this guards against. - 0.3 Confirm the package set in
MANIFEST.mdmatchesfind packages -maxdepth 3 -name package.jsoncount. Drift here means a prior change archived without regenerating the manifest.
1. Scaffold
- Create directory tree per proposal layout.
- Write
package.jsonat0.1.0with"sideEffects": false, exports map. - Dependencies:
@suaveplan/error workspace:^,@suaveplan/types workspace:^,zod catalog:forms,@opentelemetry/api catalog:observability,tslib catalog:build. - Write
tsconfig.json,vitest.config.ts, scripts.
2. Error codes and types
- Define
DndErrorextendingSuaveplanError; codes:DND_CONTEXT_MISSING,DND_INVALID_DROP_TYPE. - Define all public types:
DragData,DragState,DropEvent,DraggableProps,DroppableProps,SortableListProps,DndContextValue,DndContextProps,DndBackend.
3. Backends
HTML5 backend
- Uses
dragstart,drag,dragend,dragover,dropevents. - Write test (jsdom supports HTML5 drag — verify event handlers wired), md.
Pointer backend
- Uses
pointerdown,pointermove,pointerupevents; calculates offset for visual ghost. - Write test, md.
Keyboard backend
- Arrow keys select item; Space/Enter confirms drop; Escape cancels.
prevent-in-inputguard: no action whenevent.targetisINPUT,TEXTAREA, or[contenteditable].- Write test (keyboard sequence), md.
4. DndContext and DndProvider
DndProvider— acceptsbackendprop ('html5' | 'pointer' | 'keyboard', default'html5'). Initialises selected backend, exposes context.useContextDnd— throwsDndError(DND_CONTEXT_MISSING)outside provider.- Tracks
activeDrag: DragState | null; emits OTeldnd.dropspan on successful drop withdnd.drag_type+dnd.droppable_id. - Write test + md.
5. Draggable
- Wires backend drag-start/end; applies
aria-grabbed="true/false"; acceptsdata: DragData,type: string,disabledprops. - Write test, stories (default, disabled, playground), md.
6. Droppable
- Accepts
acceptTypes: string[]; filters drag data by type; firesonDrop(event: DropEvent); appliesaria-dropeffect;isOverstate for hover UI. - Throws
DndError(DND_INVALID_DROP_TYPE)ifacceptTypesis empty array. - Write test, stories (default, type-filtered, playground), md.
7. SortableList
- Generic
SortableList<T>— rendersitemsarray, wires each asDraggable+Droppable; callsonReorder(newItems: T[])on drop. - Keyboard backend support: arrow keys move focus, Space confirms reorder.
- Write test, stories (default, keyboard-reorder, large-list), md.
8. E2E
e2e/dnd.spec.ts— Playwright:- HTML5: drag item to droppable, verify
onDropcalled (intercept via page evaluate). - Keyboard: Tab to draggable, Space to grab, Arrow key to reorder, Space to drop.
- Type filter: drag wrong-type item to droppable, verify drop rejected.
- HTML5: drag item to droppable, verify
9. Verify and publish
bunx turbo lint typecheck test --filter=@suaveplan/react-dnd— green.bunx turbo test:coverage --filter=@suaveplan/react-dnd— 100%.bun scripts/publish.ts --package @suaveplan/react-dnd.- Archive change folder.