feat(dashboards): move tile editor to a drawer with docked settings panel - #2721
feat(dashboards): move tile editor to a drawer with docked settings panel#2721elizabetdev wants to merge 24 commits into
Conversation
…anel Convert the dashboard tile create/edit modal into a right-side drawer. Display Settings (and the heatmap variant) now dock as a full-height side panel beside the editor/preview instead of stacking a second drawer, so the tile stays visible while its options change and a single Esc closes only the panel. The display-type tabs sit in a full-width bar above the editor+panel row so they never wrap, and switching to a tab without settings (Search / Patterns / Markdown) or the other variant closes the non-applicable panel. Co-authored-by: Cursor <cursoragent@cursor.com>
🦋 Changeset detectedLatest commit: dbf20de The changes in this PR will be included in the next version bump. This PR includes changesets to release 3 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.
|
🔴 Tier 4 — CriticalTouches auth, data models, config, tasks, OTel pipeline, ClickHouse, or CI/CD. Why this tier:
Review process: Deep review from a domain expert. Synchronous walkthrough may be required. Stats
|
Greptile SummaryThe PR moves dashboard tile editing into a right-side drawer and consolidates display, row-click, and per-series settings into an integrated settings rail.
Confidence Score: 5/5The PR appears safe to merge. No blocking failure remains in the previously reported Escape-handling paths.
|
| Filename | Overview |
|---|---|
| packages/app/src/DBDashboardPage.tsx | Replaces the tile modal with a drawer and disables drawer-level Escape dismissal while a docked settings panel is active. |
| packages/app/src/components/DBEditTimeChartForm/EditTimeChartForm.tsx | Coordinates the settings rail, drawer state, and nested-overlay Escape ownership; the previously reported Escape paths are addressed. |
| packages/app/src/components/ChartDisplaySettingsDrawer.tsx | Converts display settings from a separately applied drawer into a live-updating section embedded in the tile settings rail. |
| packages/app/src/components/DBEditTimeChartForm/TileSettingsRail.tsx | Introduces the unified docked shell for display and row-click settings. |
| packages/app/src/components/DBEditTimeChartForm/ChartSeriesEditor.tsx | Reworks series editing into compact expandable cards with popover-based formatting and color controls. |
| packages/app/src/components/DBEditTimeChartForm/tests/DBEditTimeChartForm.test.tsx | Adds regression coverage for panel ownership, nested overlays, and sequential panel-then-drawer Escape dismissal. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart LR
Dashboard[Dashboard] --> Drawer[Tile editor drawer]
Drawer --> Editor[Chart editor and preview]
Drawer --> Rail[Docked tile settings rail]
Rail --> Display[Display settings]
Rail --> RowClick[Row-click action]
Editor --> Popover[Per-series popovers]
Popover -->|First Escape| Editor
Rail -->|First Escape| Drawer
Drawer -->|Next Escape| Dashboard
Reviews (19): Last reviewed commit: "feat(dashboards): restructure tile edito..." | Re-trigger Greptile
E2E Test Results❌ 1 test failed • 264 passed • 1 skipped • 1098s
Tests ran across 4 shards in parallel. |
|
<!-- deep-review --> Deep Review🔴 P0/P1 -- must fix
🟡 P2 -- recommended
🔵 P3 nitpicks (4)
Reviewers (9): correctness, adversarial, testing, maintainability, project-standards, kieran-typescript, julik-frontend-races, agent-native, learnings-researcher. Testing gaps:
Note: |
The dashboard tile editor was converted from a Modal to a Drawer, with Display Settings now rendered as a docked side panel instead of a nested Drawer. Update the shared ChartEditorComponent page object to match: - Preview table helpers now target .mantine-Drawer-body instead of .mantine-Modal-body - Display Settings helpers match either the dashboard side panel (data-testid) or the Chart Explorer drawer (role=dialog) so both contexts work Co-authored-by: Cursor <cursoragent@cursor.com>
setGroupByColumnsOnLeft still targeted the Display Settings dialog directly, which times out in the dashboard where Display Settings is now a docked side panel. Route it through the shared displaySettingsContainer locator so it matches either the panel or the drawer. Co-authored-by: Cursor <cursoragent@cursor.com>
…p swallowing nested Esc The tile editor's Esc interceptor and tab-change cleanup close the settings panels via the bare useDisclosure close(), bypassing each child's handleClose reset. Abandoned sub-form edits therefore stayed latched and were written into the tile config by the next Apply. Reset the sub-form on the closed→open transition in both ChartDisplaySettingsDrawer and HeatmapSettingsDrawer so every dismiss path behaves like cancel. Also stop the capture-phase Esc handler from calling stopPropagation on every Escape: bail out when the key originated inside a nested overlay (popover dropdown, code editor, select listbox, dialog) or was already handled, so those widgets can consume their own Esc instead of having it swallowed before they ever see it. Co-authored-by: Cursor <cursoragent@cursor.com>
The tile editor's own Drawer has role="dialog", so target.closest() matched it and the handler bailed before stopping propagation, closing the whole editor on Esc. Ignore a match that wraps the form (the containing drawer) and only bail for overlays genuinely nested inside the panel. Co-authored-by: Cursor <cursoragent@cursor.com>
The chart-wide/per-series number format E2E helper still used the native
selectOption API against getByLabel('Output format'), which now resolves to
both the Mantine Select input and its listbox (strict-mode violation). Open the
combobox and click the option, matching the pattern used by the other Select
helpers in this component.
Co-authored-by: Cursor <cursoragent@cursor.com>
|
<!-- deep-review --> Deep Review
✅ No critical issues found. 🟡 P2 — recommended
🔵 P3 nitpicks (1)
Reviewers (0): none — the persona sub-agent fan-out could not run. Findings are from a direct read of Testing gaps:
|
The docked settings panel reported its open state to the containing tile Drawer through a passive effect, so closeOnEscape flipped to false only after paint. An Esc pressed right after opening a panel still reached Mantine's capture-phase handler while closeOnEscape was true and tore down the entire editor. Report the panel state from a layout effect (pre-paint) and attach the panel's own Esc listener the same way, preventDefault after closing so any late listener bails on the defaultPrevented guard, and only close the panel that is actually open. Add regression coverage for behavior that had none: the docked-panel Esc contract in DBEditTimeChartForm (isDashboardForm) and the asPanel render path in ChartDisplaySettingsDrawer. Co-authored-by: Cursor <cursoragent@cursor.com>
- Narrow the settings-panel Esc exemption from a bare [aria-expanded="true"] to combobox-like controls only, so a focused Accordion.Control in the preview no longer swallows Esc and freezes the panel open. - Reset the parent drawer's settingsPanelOpen flag when the form unmounts with a panel still docked, so its Esc-to-close is not left disabled. - Make HeatmapSettingsDrawer's reset edge-triggered on the closed->open transition (wasOpenedRef), matching ChartDisplaySettingsDrawer, so a source/field change while the panel is open no longer wipes unapplied edits. - Give SettingsSidePanel an accessible landmark: role="region" labelled by a heading title, restoring the accessible name the replaced Drawer provided. Add regression tests for the narrowed Esc exemption (combobox stays open, non-combobox disclosure closes) and the panel's labelled-region role. Co-authored-by: Cursor <cursoragent@cursor.com>
…ction The dashboard's tile-selection Escape hotkey (useTileSelection) fired with Mantine's default preventDefault, marking the keypress handled on documentElement before it reached the docked settings panel's window listener, which bails on event.defaultPrevented. With the tile-editor Drawer's closeOnEscape disabled while a panel is open, Esc then did nothing. Make the selection-clear hotkey passive (preventDefault:false) so the panel keeps owning Esc. Add regression coverage (useTileSelection Esc clears selection while leaving defaultPrevented false; the panel closes on Esc inside a real Drawer) and give the per-series number-format disclaimer an icon on the shared warning Alert. Co-authored-by: Cursor <cursoragent@cursor.com>
…drawer Co-authored-by: Cursor <cursoragent@cursor.com> # Conflicts: # packages/app/src/DBDashboardPage.tsx
|
<!-- deep-review --> Deep Review
✅ No critical issues found. No P0/P1: the Esc-ownership handshake, the 🟡 P2 -- recommended
🔵 P3 nitpicks (7)
Reviewers (3): orchestrator direct file analysis, testing, maintainability. Four dispatched reviewers — correctness, adversarial, kieran-typescript, julik-frontend-races — had not returned when this report was due, so their coverage is missing; the P0/P1-clean verdict rests only on the three above. Testing gaps:
|
… under lint warning cap The main merge pushed the app's eslint warning total to 741, one over the --max-warnings 740 cap, failing CI lint. Construct a fully-typed Dashboard in the Escape hotkey test instead of casting through `unknown`, removing the warning and returning to 740. Co-authored-by: Cursor <cursoragent@cursor.com>
…wer Escape The kiosk-mode Escape hotkey was registered as ['Escape', exitKioskMode], which fired on every Escape (even when not in kiosk mode) and preventDefaulted by default. Because Mantine's useHotkeys listens on documentElement and bubbles before window-level Esc handlers, this marked every Escape as handled, so the tile editor's docked settings panel — which bails on event.defaultPrevented — could never close, and the drawer's closeOnEscape stayed disabled behind it. Escape appeared completely dead while a settings panel was open. Only consume Escape while actually in kiosk mode and never preventDefault, so Esc falls through to the editor's panel (close panel) and then the drawer (close drawer) as expected. Adds regression tests for the kiosk hotkey and the two-step panel/drawer Escape contract. Co-authored-by: Cursor <cursoragent@cursor.com>
Deep Review
Scope reconstructed by reading: ✅ No critical issues found. Two things I specifically tried to break and could not: (1) two panels docked at once — on the Heatmap tab 🟡 P2 -- recommended
🔵 P3 nitpicks (5)
Reviewers (0 of 7 returned): correctness, adversarial, julik-frontend-races, testing, maintainability, kieran-typescript, project-standards were all dispatched; none reported before the deadline, so all findings above are the orchestrator's own direct code reading. Re-run in an environment with a working shell for the full merge. Testing gaps:
|
jordan-simonovski
left a comment
There was a problem hiding this comment.
I'd only address the one blocking issue here, but everything LGTM.
My only other concern is that the files are getting a bit unwieldy, but that's non-blocking.
| // above: the panel must own Esc from the very first frame it is visible, so | ||
| // an Esc pressed immediately after opening closes the panel rather than | ||
| // doing nothing (or racing the drawer's own listener). | ||
| useIsomorphicEffect(() => { |
There was a problem hiding this comment.
issue (blocking): a nested overlay with role="dialog" is no longer exempt, so one Esc dismisses both it and the docked panel.
Failure mode. Open a Line tile → Display Settings (panel docks) → change Output format to Bytes but don't Apply → click a series' number-format button, so Series Display Settings opens over the top → press Esc to back out of that drawer. The drawer closes (its own capture-phase listener) and the docked panel closes on the same keypress, discarding the unapplied Bytes edit. Same with Esc on the "discard changes?" confirm: backing out of the confirm silently also throws away the panel's edits.
| const target = e.target; | ||
| if ( | ||
| target instanceof HTMLElement && | ||
| target.closest( |
There was a problem hiding this comment.
suggestion (non-blocking): Could we use Mantine's own open-combobox marker?
| disclaimer={ | ||
| isPerSeriesNumberFormatAllowed ? ( | ||
| <Alert | ||
| variant="warning" |
There was a problem hiding this comment.
praise: nice cleanup swapping variant="outline" color="yellow" for the semantic variant="warning" + icon.
…nspector rail Consolidate the tile editor's Display Settings, Row Click, per-series color, and number-format drawers into a single docked settings rail plus row-anchored popovers. All sub-edits now write live to the form draft with the tile's Save/Cancel as the single commit point, and series rows are compact with an expand toggle and kebab menu. Co-authored-by: Cursor <cursoragent@cursor.com>
…design Rework the dashboard widget tile editor (EditTimeChartForm) to follow the new Add Widget Editor layout: - Move the widget title into a top header bar with a close button, and lift Save/Cancel into that header (hidden in the in-body action bar). - Split the editor column into a sunken-gray query-builder region and a white preview region; give the viz-type tabs bar and the tile settings rail the same sunken-gray background (--color-bg-sunken). - Render each series as a bordered card with a header row (name/alias/format/ color/actions) instead of divider-separated blocks. - Unify the query "Row 1": Data Source + Builder/SQL/PromQL mode toggle + Run, hiding the duplicate Run in the action bar for the builder view. - Drop the dividers around the chart-level Group By and the Add Series footer. Chart Explorer (non-dashboard) layout is unchanged. Co-authored-by: Cursor <cursoragent@cursor.com>
Summary
Moves dashboard tile creation/editing from a centered modal into a right-side drawer, and reworks Display Settings so it no longer stacks a second drawer on top of the editor.
Closes HDX-4897
Why
Previously the tile editor was a
Modaland Display Settings opened as a separateDraweron top of it. With two stacked overlays, a single Esc press closed both at once, and the nested drawer-on-drawer felt wrong. This makes the editor a single drawer with settings docked inside it.What changed
EditTileModalis nowEditTileDrawer(right-side,90%, full-height flex body). Unsaved-changes confirm, z-index/ZIndexContext, andIsolatedChartSyncProviderbehavior are preserved.HeatmapSettingsDrawer) uses the same docked panel via a new sharedSettingsSidePanelshell. Its use on the Search page (DBSearchHeatmapChart) is unchanged (still a Drawer,asPanelis opt-in).border-leftmeets the tab underline cleanly (semantic--color-bordertoken).isDashboardForm; there the editor still uses the overlay Drawer.Screenshots
Before
After
Testing
make ci-lintequivalent:yarn lint:fix+yarn tsc --noEmitclean.ChartDisplaySettingsDrawer,DBSearchHeatmapChart,DBEditTimeChartFormsuites pass.Changeset
.changeset/tile-editor-drawer.md—@hyperdx/apppatch.Made with Cursor