Skip to content

fix(dashboard): replace GridStack with dnd-kit + CSS Grid bento#104

Merged
pratikbodkhe merged 2 commits into
mainfrom
fix/widget-canvas-sizing
Jun 7, 2026
Merged

fix(dashboard): replace GridStack with dnd-kit + CSS Grid bento#104
pratikbodkhe merged 2 commits into
mainfrom
fix/widget-canvas-sizing

Conversation

@pratikbodkhe

Copy link
Copy Markdown
Contributor

Problem

OIL Board widgets clipped their content (internal scrollbars, dead space). The "morning fix" (`sizeToContent: true`) made it worse: every card collapsed to one 86px row with content hidden underneath.

Root cause (proven against gridstack 12.6.0 source)

`GridStack.resizeToContent` measures `.grid-stack-item-content`'s `firstElementChild` and assumes a single content child. In `widget-shell.tsx` that first child is the absolute-positioned action toolbar (~24px), not the body, so GridStack sized every widget to ~1 row and `overflow: hidden` clipped the rest. GridStack also never re-measures when async React Query content loads (its only ResizeObserver watches the grid for width changes).

This is a model mismatch: fixed-row grids (GridStack, react-grid-layout) treat content-hugging as a fragile bolt-on.

Fix

Drop GridStack. Content-driven CSS Grid bento + dnd-kit:

  • Row heights come from content via the browser's layout engine. No JS height measurement, no clipping.
  • `align-items: stretch` keeps adjacent unequal cards the same band height (aligned, not ragged).
  • Per-type `colSpan`/`rowSpan` footprints; reorder via dnd-kit `SortableContext`; width toggle cycles colSpan.
  • Removed the `gridstack` dependency and all gridstack CSS.

Store moves from GridStack `{x,y,w,h}` to a `colSpan`/`rowSpan` footprint model (persist v3 migration drops legacy fields).

Tests (TDD)

  • New `widget-bento.spec.ts` (5/5): engine = css-grid, footprint col-spans, and a no-clip gate verified to fail against the old GridStack code first.
  • Rewritten `widgets.spec.ts` (28/28) to the new model.
  • Visually verified on the live authenticated dashboard at 1440px.

Design doc

`docs/superpowers/specs/2026-06-07-widget-dashboard-bento-design.md`

Note on local full-suite run

Full `pnpm test:e2e` locally shows 16 failures unrelated to this change and pre-existing: timezone off-by-one (machine is JST, config pins no `timezoneId`, e.g. issue due-date "Jun 15" -> "Jun 14"), plus email-backend / guest-share-token / service-role / inbox env cases. All are in share/auth/inbox/rbac/email/issue-detail paths, none reachable from dashboard widget code or `.widget-grid` CSS. They pass in UTC CI.

GridStack sized each widget from .grid-stack-item-content's firstElementChild,
which in widget-shell is the absolute-positioned action toolbar (~24px), not the
body. Every card collapsed to one 86px row and its real content was clipped
behind overflow:hidden; sizeToContent made it worse (one-shot measure vs async
React Query content).

Replace GridStack with a content-driven CSS Grid bento: row heights come from
content (no JS measurement, no clipping), dnd-kit handles reorder, and per-type
col/row span footprints keep adjacent unequal cards aligned. Drop the gridstack
dependency and all gridstack CSS.

Widget store moves from GridStack {x,y,w,h} layouts to a colSpan/rowSpan
footprint model (persist v3 migration drops legacy fields). Tests rewritten to
the new contract; new widget-bento.spec asserts no widget clips its content.
@pratikbodkhe pratikbodkhe merged commit 2428733 into main Jun 7, 2026
18 checks passed
@pratikbodkhe pratikbodkhe deleted the fix/widget-canvas-sizing branch June 7, 2026 14:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant