Skip to content

feat: Dashboard enterprise-grade organization with drag-and-drop#1972

Open
alex-fedotyev wants to merge 7 commits intomainfrom
feat/dashboard-dnd-organization
Open

feat: Dashboard enterprise-grade organization with drag-and-drop#1972
alex-fedotyev wants to merge 7 commits intomainfrom
feat/dashboard-dnd-organization

Conversation

@alex-fedotyev
Copy link
Contributor

@alex-fedotyev alex-fedotyev commented Mar 23, 2026

Summary

Implements the remaining features from #1929: drag-and-drop tile organization, section reordering, tab and group container types, and UX polish.

Builds on #1926 (authoring UX + DashboardContainer abstraction).

Changes by commit

  1. feat: Extend DashboardContainer schema for tab and group types

    • Adds 'tab' and 'group' to the container type enum
    • Adds activeTabId for tab state, parentId for child tab containers
  2. feat: Add @dnd-kit drag-and-drop infrastructure for dashboards

    • @dnd-kit/core, @dnd-kit/sortable, @dnd-kit/utilities
    • DashboardDndContext.tsx: provider, drag handles, drop zones, sortable wrappers
    • pointerWithin collision detection for correct cross-section targeting
  3. feat: Add GroupContainer and TabContainer components

    • GroupContainer: bordered, always-visible, drag handle, inline rename
    • TabContainer: multi-tab bar with add (+), rename (double-click), remove (x), tab switching
  4. feat: Add drag handle to SectionHeader, fix theme-aware borders

    • Grip icon on hover, var(--mantine-color-default-border) for light/dark themes
  5. feat: Integrate DnD, container types, and polish into dashboard page

    • Cross-section tile drag, section reorder, container type rendering
    • Delete confirmation dialog, empty placeholders, select-and-group (Shift+click → Cmd+G)
  6. test: Add tests for container types, reordering, and grouping — 14 new tests

  7. feat: Implement multi-tab container support

    • parentId on containers links child tabs to parent tab set
    • Auto-creates initial tab on tab container creation
    • Add/rename/delete tabs, active tab tracking, tile migration on tab delete
    • Render-prop pattern: only active tab's tiles visible

Verified via Playwright MCP on Vercel preview

Feature Result
Drag tile Section → Group ✅ containerId updated
Drag tile Group → Section ✅ reverse works
Section reorder C→A ✅ order changed A,B,C → C,A,B
Shift+click selection ✅ blue outlines + "2 tiles selected" bar
Cmd+G group ✅ creates section, assigns tiles
Delete confirmation ✅ shows name + tile count
Empty placeholders ✅ dashed border + hint
Group container ✅ bordered, drag handle, inline rename
Multi-tab container 🔄 CI passes, Vercel testing pending

Test plan

  • 39 unit tests pass (25 existing + 14 new)
  • 691 common-utils tests pass
  • All app unit tests pass
  • All 4 E2E test shards pass (91 tests)
  • ESLint clean, no TypeScript errors
  • Backward compatible

Closes #1929

@changeset-bot
Copy link

changeset-bot bot commented Mar 23, 2026

⚠️ No Changeset found

Latest commit: 2682bfe

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@vercel
Copy link

vercel bot commented Mar 23, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
hyperdx-oss Ready Ready Preview, Comment Mar 24, 2026 1:23am

Request Review

@github-actions
Copy link
Contributor

github-actions bot commented Mar 23, 2026

Knip - Unused Code Analysis

0 change in total issues (240 on main → 240 on PR)

Category main PR Diff
Unused files 9 9 0
Unused dependencies 14 14 0
Unused devDependencies 20 20 0
Unlisted dependencies 14 14 0
Unresolved imports 2 2 0
Unlisted binaries 2 2 0
Unused exports 132 132 0
Unused exported types 41 41 0
Unused enum members 2 2 0
Duplicate exports 4 4 0
What is this?

Knip finds unused files, dependencies, and exports in your codebase.
This comment compares the PR branch against main to detect regressions.

Run yarn knip locally to see full details.

@github-actions
Copy link
Contributor

github-actions bot commented Mar 23, 2026

PR Review

  • variant="subtle" on ActionIcon in GroupContainer.tsx:83,105 → Replace with variant="secondary" or variant="link". The code style guide explicitly forbids subtle, light, filled, outline, and default variants for Button/ActionIcon — only primary, secondary, danger, link are permitted.

  • ⚠️ GroupContainer.tsx is 347 lines → AGENTS.md mandates files under 300 lines. Split tab-bar rendering into a TabBarHeader sub-component.

  • ⚠️ c.tabs = undefined as never in useDashboardContainers.tsx:224 → This bypasses TypeScript. The DashboardContainerSchema marks tabs as .optional(), so the Immer draft type should already allow deletion. Remove the cast and just delete (c as any).tabs or fix the Immer draft type.

  • ⚠️ selectedTileIds Set churn causes all tiles to re-render on every selectionhandleTileSelect creates new Set(prev) on each call, producing a new reference that invalidates the renderTileComponent useCallback. On large dashboards, every shift-click re-renders every tile. Consider storing selection in a useRef and using a separate selectedTileIds render signal, or using useMemo with a stable equality check.

  • ℹ️ Tab title auto-numbering produces duplicates after deletion (handleAddTab:187) → Tab ${existingTabs.length + 1} collides with existing "Tab N" titles when a tab was deleted previously. Low severity (cosmetic), but worth a quick fix (e.g., find the max existing index + 1).

@github-actions
Copy link
Contributor

github-actions bot commented Mar 23, 2026

E2E Test Results

All tests passed • 91 passed • 3 skipped • 963s

Status Count
✅ Passed 91
❌ Failed 0
⚠️ Flaky 2
⏭️ Skipped 3

Tests ran across 4 shards in parallel.

View full report →

@alex-fedotyev alex-fedotyev force-pushed the feat/dashboard-dnd-organization branch from 9d1a5c2 to 0d2b11f Compare March 23, 2026 20:56
@alex-fedotyev alex-fedotyev force-pushed the feat/dashboard-dnd-organization branch 2 times, most recently from a7afc78 to fd8799e Compare March 23, 2026 21:14
@alex-fedotyev alex-fedotyev force-pushed the feat/dashboard-dnd-organization branch from fd8799e to 20a628e Compare March 23, 2026 21:37
@alex-fedotyev alex-fedotyev force-pushed the feat/dashboard-dnd-organization branch from d65d046 to 0fb0522 Compare March 23, 2026 21:47
@alex-fedotyev alex-fedotyev force-pushed the feat/dashboard-dnd-organization branch from 0fb0522 to e38c98b Compare March 23, 2026 21:51
@alex-fedotyev alex-fedotyev force-pushed the feat/dashboard-dnd-organization branch from e38c98b to 74ee92a Compare March 23, 2026 21:56
@alex-fedotyev alex-fedotyev force-pushed the feat/dashboard-dnd-organization branch from 74ee92a to 4aee30f Compare March 23, 2026 22:02
@alex-fedotyev alex-fedotyev force-pushed the feat/dashboard-dnd-organization branch from 4aee30f to ae5981a Compare March 23, 2026 22:20
@alex-fedotyev alex-fedotyev force-pushed the feat/dashboard-dnd-organization branch from ae5981a to 82579ce Compare March 23, 2026 23:35
Add 'group' type alongside 'section'. Groups optionally support tabs
via inline tabs:[{id,title}] array — 2+ tabs renders a tab bar, 0-1
is a plain group. Tiles get optional tabId for tab assignment.
activeTabId persisted to server (Grafana/Kibana shared state pattern).
DashboardDndContext (116 lines): provider for sortable container reorder.
DashboardDndComponents (98 lines): EmptyContainerPlaceholder with
centered [+ Add] button, SortableSectionWrapper. DragHandleProps typed.
GroupContainer (347 lines): bordered container with smart tab support.
Plain group: header with + menu (Add Tile / Add Tab).
Tabbed group (2+ tabs): tab bar with hover-only x, inline +, rename.
First 'Add Tab' auto-creates two tabs. Removing to 1 disables tabs.
Render-prop children(activeTabId). Theme-aware borders.
Grip icon on hover. DragHandleProps. Theme-aware borders.
useDashboardContainers (275 lines): section/group CRUD, tab management.
Add Tab auto-creates two tabs, delete to 1 disables tabs. Always-confirm
delete (even empty containers). Shared makeId from tilePositioning.

useTileSelection (74 lines): Shift+click + Cmd+G grouping.
Floating drag bar: absolute positioned, no content shift on hover.
Group rendering: plain or tabbed based on tabs array length.
Move to Section: shows group tab entries as 'Group > Tab Name'.
Tile auto-repositions at bottom of target grid on move.
Add menu: Section / Group only (no separate Tab Container).
Delete confirmation always shown. Empty placeholders with [+ Add] button.
Select-and-group: Shift+click tiles then Cmd+G.
49 tests: schema validation (section/group only, rejects tab type),
group with optional tabs, tab tile filtering, tabId cleanup, cross-
container moves, section reorder, tile grouping.
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.

Dashboard: Enterprise-grade organization (sections, tabs, groups, drag-and-drop)

1 participant