refactor: Unify section/group into single Group with collapsible/bordered options#2015
refactor: Unify section/group into single Group with collapsible/bordered options#2015alex-fedotyev wants to merge 6 commits intofeat/dashboard-dnd-organizationfrom
Conversation
🦋 Changeset detectedLatest commit: d3cf78a The changes in this PR will be included in the next version bump. This PR includes changesets to release 4 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
PR Review
|
|
@claude code review |
|
Claude finished @alex-fedotyev's task in 2m 11s —— View job Code Review
Overall this is a clean unification with good backward-compat handling. Three issues from the previous automated review remain unaddressed, plus a new bug I found in the rename path. Issues1. Bug:
|
|
@claude code review |
|
Claude finished @alex-fedotyev's task in 3m 12s —— View job Code Review
The previous automated review flagged 4 issues. All 4 have been addressed in this revision — nice cleanup. Findings below. Previously-flagged issues: all resolved ✅
Remaining issues1. Accessibility regression (still unaddressed from original automated review)
// current
<IconChevronRight
onClick={onToggle}
/>
// fix: wrap or add attributes
<IconChevronRight
role="button"
tabIndex={0}
aria-expanded={!isCollapsed}
? 'Expand group' : 'Collapse group'}
onKeyDown={e => (e.key === 'Enter' || e.key === ' ') && onToggle()}
onClick={onToggle}
...
/>2. Keyboard-focusable invisible controls
// add tabIndex alongside the existing opacity/pointerEvents
style={{ opacity: showControls ? 1 : 0, pointerEvents: showControls ? 'auto' : 'none' }}
// + tabIndex={showControls ? 0 : -1}3. Stale URL collapse state when re-enabling collapsible (low severity)
Minor observationsStale
Shared active tab state ( What looks good
|
|
@claude - review code |
|
Claude finished @alex-fedotyev's task in 2m 40s —— View job Code Review
All issues from the previous two review rounds are resolved. Three new findings below. Previously flagged issues: all resolved ✅
New issues1. Tab rename button (pencil icon) still keyboard-focusable when hidden
// current — stays in tab order when invisible
<ActionIcon
variant="subtle"
size="sm"
style={{ opacity: showControls ? 1 : 0, pointerEvents: showControls ? 'auto' : 'none' }}
...
>
// fix — one line addition
tabIndex={showControls ? 0 : -1}2. Spurious alert dot when container has no tabs
In practice this only occurs for containers created before the tab system (legacy containers), but the empty-string sentinel is a code smell. Filtering it out explicitly is cleaner: // current
alertingTabIds.add(tile.tabId ?? firstTabId ?? '');
// fix — only add a real id; skip if there's nothing to attribute to
const attributedTabId = tile.tabId ?? firstTabId;
if (attributedTabId) alertingTabIds.add(attributedTabId);3.
|
Add optional fields to DashboardContainerSchema:
- tabs: array of {id, title} for tabbed groups (tab bar at 2+)
- activeTabId: persisted active tab selection
- collapsible: whether the group can be collapsed (default true)
- bordered: whether to show a border (default true)
Remove the dead `type` discriminator field ('section' | 'group') —
containers are now defined by their properties. Old dashboards with
`type: 'section'` still parse (Zod strips unknown keys). The field
can be re-added as a discriminated union if semantically different
container kinds are ever needed.
Also add `tabId` to TileSchema so tiles can reference a specific tab.
- DashboardDndContext: wraps dashboard in DndContext + SortableContext for container drag-and-drop reordering - DashboardDndComponents: SortableContainerWrapper (sortable item with drag handle props), EmptyContainerPlaceholder (shown when a container or tab has no tiles, with an Add button) - DragHandleProps type exported for GroupContainer integration
Unified component for all dashboard container types. Features: - Collapse chevron with keyboard/screen reader support (role, tabIndex, aria-expanded, aria-label, Enter/Space handler) - Optional border toggle - Tab bar (appears at 2+ tabs) with inline rename, delete confirmation - Collapsed state shows pipe-separated tab names (max 4, then "...") - Alert indicators: red dot on tabs/header for active alerts - Overflow menu: Add Tab, Collapse by Default, Disable/Enable Collapse, Hide/Show Border, Delete Group (divider guarded) - Hidden controls removed from keyboard tab order (tabIndex toggle) - Fixed header height prevents UI jump on collapse/expand Deletes SectionHeader.tsx and its tests — all functionality merged into GroupContainer.
useDashboardContainers (301 lines): Container CRUD + tab lifecycle - Add/rename/delete containers - Toggle collapsed, collapsible, bordered - Add/rename/delete tabs with tile migration - Tab change persistence - Container.title synced with tabs[0] on rename and delete useTileSelection (74 lines): Multi-select + Cmd+G grouping - Shift+click tile selection - Cmd+G groups selected tiles into a new container - Assigns tabId to newly grouped tiles
Wire GroupContainer, useDashboardContainers, useTileSelection, and DnD context into DBDashboardPage: - Add @dnd-kit dependencies (core, sortable, utilities) - Single "New Group" in Add menu (replaces Section + Group) - Container rendering with collapsible/bordered/tabbed support - Tile move dropdown with IconCornerDownRight and tab targets - URL-based collapse state (cleared on collapsible toggle) - Alert indicator computation: alertingTabIds per container from tile alert state, passed to GroupContainer - Auto-expand on collapsible disable (prevents stuck-collapsed) - Tile positioning updated for container-aware layout
GroupContainer.test.tsx (329 lines, 18 tests): - Collapsible: chevron show/hide, children toggle, onToggle callback - Bordered: border style present/absent - Collapsed tab summary: pipe-separated names, no summary when expanded or single-tab - Tab bar: renders at 2+ tabs, plain header at 1 tab - Tab delete: confirmation flow, rejected confirmation - Alert indicators: dot on collapsed header, per-tab dot in expanded bar dashboardSections.test.tsx (556 lines, 56 tests): - Schema validation: containers without type field, backward compat with extra fields, tabs, collapsible/bordered options - Tile grouping logic, tab filtering, container authoring operations - Group tab operations: creation, add/delete tabs, rename sync
027b142 to
d3cf78a
Compare
Summary
Merges the separate "section" and "group" container types into a single Group concept with configurable options:
This addresses the UX concern from #1972 that "section" and "group" are near-synonyms that force users to choose between two similar concepts.
Screenshots
Dashboard with groups and tabs (expanded):
Group overflow menu (Add Tab, Collapse, Border, Delete):
Collapsed group with pipe-separated tab names (Overview | Latency | Errors):
Commits (review guide)
types.ts: new optional fields (tabs, collapsible, bordered), type field removalDashboardDndContext.tsx,DashboardDndComponents.tsx: sortable wrappers, drag handleGroupContainer.tsx: unified component with tabs, collapse, borders, alerts, a11yuseDashboardContainers.tsx: container/tab CRUD.useTileSelection.ts: multi-select + Cmd+GDBDashboardPage.tsx: wiring, alert computation, DnD, tile positioningGroupContainer.test.tsx(18 tests),dashboardSections.test.tsx(56 tests)Key decisions
typediscriminator — containers defined by properties. Extensible via discriminated union later.AlertState.ALERTtiles. Shows on both expanded tab bar and collapsed header.Backward compatibility
type: 'section'containers parse successfully (Zod strips extra field)collapsible/borderedfields are optional —undefinedtreated astrueBuilds on #1972
Test plan