Skip to content

PRD: Resizable & reorderable dashboard panels #75

@reynsu

Description

@reynsu

Generated from a grilling session. Decision record lives in the dashboard-ui repo: `reactlens-dashboard-ui/docs/adr/0001-resizable-reorderable-panels.md`. This feature is implemented in `@reynsu/reactlens-dashboard-ui` and shipped to the host as a prebuilt bundle.

Problem Statement

The dashboard's three columns — Tests, Preview, Inspector (Tree/Diagnostics tabs) — have fixed widths and a fixed left-to-right order. Depending on what I'm doing (reading long diagnoses, watching the preview, scanning the tree) I want to give one panel more room or move it where my attention is, and right now I can't.

Solution

Let the operator resize a column by dragging the border it shares with its neighbour, and reorder the three columns by dragging a grip in each panel's header. Sizes and order persist across reloads. Active only at ≥900px (the 3-column layout); the existing narrow/stacked layout is untouched. Built with `react-resizable-panels` (resize + persistence) + `@dnd-kit` (reorder), added as devDependencies of the dashboard-ui package and bundled into `dist/web` — the project consuming reactlens installs nothing extra.

User Stories

  1. As a developer, I want to drag the border between two panels, so that I can widen one and narrow its neighbour.
  2. As a developer reading a long diagnosis, I want to widen the Inspector, so that I can read the root cause and patch without wrapping.
  3. As a developer, I want to widen the Preview, so that I can see the browser frame larger.
  4. As a developer, I want to drag a panel by a grip in its header, so that I can change the order of the three columns.
  5. As a developer, I want only a dedicated grip to start a drag, so that clicking tests, typing in search, or switching tabs never triggers an accidental reorder.
  6. As a developer, I want the rest of each panel to stay fully clickable and scrollable, so that resizing/reordering doesn't cost me normal interaction.
  7. As a developer, I want my layout (sizes + order) to persist across reloads and across runs, so that I set it up once and it stays.
  8. As a developer, I want a panel to never shrink to nothing, so that I can't accidentally lose a panel I then can't find.
  9. As a developer, I want all three panels always visible (no collapse-to-zero), so that there's no hidden state to recover from.
  10. As a developer on a narrow screen (<900px), I want the existing stacked layout (Tests strip on top) unchanged, so that mobile/narrow behaviour isn't broken by drag affordances that make no sense stacked.
  11. As a keyboard user, I want to reorder panels without a mouse, so that the feature is accessible (dnd-kit keyboard sensors).
  12. As a developer, I want the resize handle to be discoverable (cursor + hover feedback) and on-theme, so that it reads as part of the dashboard.
  13. As a developer, I want the panel order to round-trip exactly from localStorage, so that a corrupt/missing value falls back to the default Tests|Preview|Inspector instead of breaking the layout.
  14. As a developer, I want Tree and Diagnostics to remain tabs inside the Inspector, so that the deliberate P0 docs: amend ADR + CONTEXT for the diagnosis-prompts + diff-core extractions #3 decision (neither scrolls the other out of view) is preserved.
  15. As a consumer of reactlens, I want to install nothing extra, so that the dashboard keeps shipping as a single prebuilt bundle.

Implementation Decisions

(Full rationale + rejected alternatives in the dashboard-ui ADR 0001.)

  • Panels = the three existing columns: Tests | Preview | Inspector. Tree/Diagnostics stay tabs inside Inspector (P0 docs: amend ADR + CONTEXT for the diagnosis-prompts + diff-core extractions #3 preserved).
  • Libraries: `react-resizable-panels` (drag-resize handles, percentage sizes with per-panel `minSize`, size persistence via `autoSaveId`) + `@dnd-kit` (accessible column reorder). Added as devDependencies of `@reynsu/reactlens-dashboard-ui` and bundled. A docking lib (dockview/mosaic) was rejected (heavy, imposes its own tab/panel model + theme); native HTML5 DnD was rejected (rough a11y/UX).
  • Reorder affordance: a dedicated grip (⠿) in each panel's header bar (tests-head / preview meta / tablist), wired to dnd-kit listeners. The whole panel is not a drag source.
  • Responsive gate: the `PanelGroup` + DnD layout is mounted only at ≥900px; below 900px the current CSS-grid layout and its media queries are kept verbatim, with no grips/handles.
  • Persistence: sizes via `autoSaveId`; order under a dedicated localStorage key (global, not per-run). localStorage = local, consistent with sovereignty (reactlens ADR-0003).
  • Minimums only, no collapse: each panel gets a `minSize` derived from today's `minmax` floors (~220 / 360 / 320px). All three stay visible.
  • Deep module `panel-layout`: owns order + sizes + localStorage read/write/validate (default fallback on corrupt/missing). Testable in isolation, decoupled from the lib wiring.

Testing Decisions

Good tests assert external behaviour, not the libraries' internals (jsdom has no real layout, so resize geometry is verified manually).

  • `panel-layout` (unit, pure): asserts order/size round-trip through localStorage; a missing or corrupt stored value falls back to the default `Tests|Preview|Inspector`; an unknown panel id is dropped/ignored rather than crashing. Prior art: the dashboard-ui component/unit tests (vitest + jsdom).
  • Plus manual verification on the live dashboard (:5173): drag-resize between panels, reorder via grip, reload to confirm persistence, shrink to confirm mins, narrow below 900px to confirm the stacked layout is untouched, keyboard reorder.

Out of Scope

Further Notes

  • Pure dashboard-frontend change: no event-protocol (§9), host, or diagnosis-path impact.
  • At ≥900px the layout container changes from the `.layout` CSS grid to a `PanelGroup`; the <900px grid + media queries stay as-is.
  • Coheres with the in-progress design pass (underline tabs, accent-soft selection, themed RunPicker): the resize handle + grip should use the same theme tokens.

🤖 Generated with Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    ready-for-agentPRD/issue is fully specified and ready for an agent to pick up and execute

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions