You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Make the desktop left navigation foldable without adding collapse/expand buttons.
The user controls the navigation width by dragging its vertical frame border. A sufficiently narrow drag converts the current two-panel sidebar into a compact icon rail. Dragging the rail or its focused drawer wider converts it back into the normal two-panel sidebar.
The compact state must remain fully navigable: clicking a rail icon opens that section as a docked, full-height focused drawer beside the rail. The drawer consumes layout width and pushes the SQL/Dashboard work surface inward; it must not float over or obscure the work surface.
Mobile already uses a different navigation model and is explicitly out of scope.
Phases
Added during /ship (2026-07-29). Branch model: one branch and one PR per phase off fresh main — phases 1 and 2 are additive/inert and land green on their own, phase 3 turns the
feature on, phase 4 closes the issue. The <!-- ship-log --> comment is the state of record.
uses the remaining vertical height below the header;
consumes normal layout width;
pushes the Query/Dashboard surface instead of covering it;
shows exactly one section;
contains that section's title, search and content;
does not repeat Databases | Dashboards or Queries/Library | History switcher buttons;
remains open after selecting a database, table, Dashboard, query or history item;
retains the section's expansion, search and scroll state;
uses the same application navigation commands as the wide sidebar;
never executes hidden Dashboards merely because the Dashboards section is visible.
Clicking the active rail icon closes the focused drawer and leaves the rail visible.
Clicking a different rail icon switches the drawer content in place without first closing the drawer.
Escape may close the focused drawer when focus is inside it, but must not also cancel a running query or trigger another global Escape action.
Section-specific behaviour
Databases
Show the existing schema search and database/table/column tree with existing click, double-click, Shift-click, drag and documentation actions unchanged.
Dashboards
Show the Dashboard hierarchy from #426 with its search, expansion, keyboard and navigation semantics unchanged.
Queries / Library
Before #427, show the existing Queries collection.
After #427, show only the zero-owner Library projection and update the rail label/tooltip to Library. The foldable shell must not introduce a separate data projection or duplicate ownership logic.
History
Show the existing history search/list and opening behaviour unchanged.
Drag-controlled state transitions
Use the vertical navigation frame border as a resize separator.
The transition must feel deliberate and must not flicker near a single threshold. Use named constants and hysteresis rather than deriving state directly from every pointer pixel.
Recommended starting values, adjustable only through real-browser layout verification:
mode, wideWidthPx and focusedDrawerWidthPx are browser preferences;
focusedSection is session UI state and need not reopen automatically after reload;
none of this state belongs in StoredWorkspaceV3, Dashboard documents or query specs;
selected Databases/Dashboards and Queries/Library/History roles continue to use their existing UI-state ownership.
The persisted preference decoder must clamp invalid or obsolete values safely.
Shared navigation state
Wide and focused presentations are two views over the same navigation state.
Preserve across transitions:
schema search text;
schema database/table expansion;
loaded-column cache;
schema scroll position;
Dashboard search and expansion;
Dashboard keyboard/current-row state;
Queries/Library search and editing state where safe;
History search;
upper/lower split percentage;
current application surface and editor/result state.
Do not create separate competing expansion/search stores for wide and focused presentations.
A transition may move DOM focus to the corresponding control in the new presentation, but it must not silently reset the user's state.
Interaction with drag-and-drop assignment
The implementation must remain compatible with #428.
When a Library-query drag is active in rail mode:
the Dashboards rail icon can be exposed as a valid hover launcher;
bounded hover may open the docked Dashboards focused drawer;
the user can then drop on Dashboard, Panels or Filters targets;
leaving/cancelling the drag clears temporary hover state;
this must never turn the drawer into an overlay.
This integration may be completed by #428 if #428 has not yet landed, but the left-navigation API must provide a deterministic openFocusedSection('dashboards') seam.
Main-surface resizing
Every width or mode change must notify/reflow the active centre surface correctly:
CodeMirror/editor geometry;
result table and virtualised rows;
Query editor/results splitter;
Dashboard layout engines and charts;
sticky Dashboard/filter/toolbars;
optional right inspector.
Use existing resize observers/layout hooks rather than arbitrary delayed resize events.
Left and right docked panels may be open simultaneously. Clamp widths so the centre retains a documented minimum usable width.
Keyboard and accessibility
The vertical frame border is a keyboard-operable separator:
announce semantic mode changes through an appropriate live/status mechanism without excessive chatter.
Rail buttons must expose aria-expanded and aria-controls for the focused drawer.
Closing a focused drawer returns focus to its rail icon. Converting to wide mode restores focus to the corresponding section tab/search/tree when possible.
Mobile
At the existing mobile breakpoint:
do not render the desktop rail or desktop focused drawer;
retain the established mobile segmented and bottom navigation;
ignore desktop folding preferences for effective mobile layout;
preserve those preferences for the next desktop session.
Suggested code boundaries
App shell layout controller
Owns:
explicit wide/rail mode;
widths and thresholds;
vertical resize separator;
focused drawer host;
preference persistence;
centre-width clamping and resize notifications.
Navigation section registry
Maps rail sections to existing views and labels without duplicating their domain state.
Existing navigation views
Continue to own their tree/list rendering, search and application actions. They must not manipulate shell widths directly.
Delivery phases
Each phase's definition of done is its implement list below, the ## Tests subsections it
names, and the subset of ## Acceptance criteria it claims. ## Non-goals applies to all four.
Phase 1 — Layout core and preferences
Pure layout-decision logic plus its persisted preferences. No visible change.
Implement:
src/core/left-nav-layout.ts (new, pure, 100% covered): the named constants
(LEFT_RAIL_PX, LEFT_FOLD_THRESHOLD_PX, LEFT_WIDE_THRESHOLD_PX, LEFT_PANEL_MIN_PX, LEFT_PANEL_MAX_PX), the LeftNavigationMode / LeftNavigationSection types, the hysteresis reducer mapping (current mode, proposed px) to the next mode and width,
wide-width clamping, and the keyboard step resolver (small step, large step, Home folds
to rail, End restores wide);
src/state.ts: mode, wideWidthPx and focusedDrawerWidthPx as persisted browser
preferences, focusedSection as session-only UI state, their KEYS entries, and decoders
that clamp invalid or obsolete values back to the documented defaults;
src/application/app-preferences.ts: the new keys added to PreferenceKey.
Tests: ## Tests → State and preferences, in full — including the assertion that none of
this state reaches StoredWorkspaceV3, Dashboard documents or query specs.
Also here, because the mobile rule is a layout decision and not a rendering one: effectiveLeftNavigationLayout(layout, isMobile), the projection that makes mobile ignore
rail mode and any open drawer while leaving both persisted widths untouched.
Moved out of this phase during implementation: the centre-width clamp. Review established
that a single total-width-in/total-width-out signature is the wrong shape — there is no inverse
from a total back to (mode, panel width), so it can return a width no mode can render (220px
is neither the rail, nor a legal wide sidebar, nor rail+drawer). Phase 3 owns it, as
layout-in/layout-out: phase 3 is what turns the feature on, and parking a required safety
constraint in phase 4 would leave a merged interval in which left and right docked panels can
starve the centre surface. Deferring an unused primitive out of phase 1 is CLAUDE.md hard rule
5; deferring a safety constraint past the feature it protects is not. (This paragraph said
"phase 4" until phase 2 reconciled it — the phase 3 and 4 sections below, and the <!-- ship-log --> comment, have said phase 3 since phase 1 shipped.)
Acceptance subset: claims none of the eight global criteria; all eight defer to phases 3
and 4. This phase's gate is the State-and-preferences list plus 100% coverage of the new core
module.
Phase 2 — Navigation section registry
Make the four navigation sections hostable by any container, over one shared store per
section. The wide sidebar renders exactly as it does today.
Implement:
the navigation section registry: each LeftNavigationSection mapped to its label, icon,
accessible label and its persistent host element — built once and never rebuilt,
extending the buildSidebarUpper pattern (Add a Databases/Dashboards switcher and dashboard hierarchy tree #426) from Databases/Dashboards to the lower
pane's Library/History;
src/ui/app-shell.ts: compose today's two-pane sidebar out of the registry's hosts;
no second competing search/expansion/scroll store — the registry hands out the same hosts
both presentations will later use, so state survives by construction.
Tests: extend tests/unit/app-shell.test.ts and tests/unit/sidebar-upper.test.ts — the wide
pane composition, and exactly one host instance per section. ## Tests → Wide state
bullets 1–4.
Acceptance subset: claims none; this phase's gate is "existing navigation behaviour is
unchanged".
Phase 3 — Rail, focused drawer and the resize separator
The feature turns on.
Implement:
src/ui/left-rail.ts: the LEFT_RAIL_PX rail with four launchers reusing the existing database / dashboard / layers / history icon primitives, tooltips, distinct hover /
keyboard-focus / active-drawer states, aria-expanded + aria-controls, a width that
cannot stretch to the drawer's, and no internal text labels; mounted below the full-width
header;
Deviation, shipped instead of a separate src/ui/left-drawer.ts: the existing .sidebar element is RE-PRESENTED as the drawer via one data-nav-mode attribute
('wide' | 'rail' | 'drawer'), written by one function (app-shell.ts's applyEffectiveLeftNavigationLayout) that is the sole authority for every presentation
attribute. No section host ever moves between containers — phase 2's persistent-host
guarantee ("wide and focused presentations share and preserve all navigation state") holds
by construction rather than by a second module's own restoration logic. See the <!-- ship-log --> comment's phase-3 handoff for the full rationale;
src/ui/app-shell.ts: render either the wide sidebar or rail + drawer from the explicit
shell mode, keeping every section host mounted across the transition;
Deviation, shipped instead of a new splitters.ts axis: a dedicated src/ui/left-nav-separator.ts module REPLACES splitters.ts's old 'col' axis outright
(deleted, not left beside the new module) — feeding proposed widths through phase 1's
reducer via a resize session, with safe termination on blur/visibilitychange. A fifth
competing axis would have kept two sidebar-width authorities (the old bare clamp and the
new mode reducer) alive at once;
the separator itself: role="separator", aria-orientation="vertical", tabindex="0", aria-valuemin / aria-valuemax / aria-valuenow, Left/Right, Shift+Left/Right, Home, End, and a status announcement on semantic mode change only;
Escape closes the focused drawer when focus is inside it without reaching any global Escape
action; closing returns focus to the rail icon; converting to wide restores focus to the
corresponding section tab, search or tree;
mobile: neither rail nor focused drawer renders below the breakpoint, and the desktop
preferences are preserved for the next desktop session;
the centre-width clamp, as a layout-in/layout-out function so its result always names a
renderable (mode, panel width) rather than a bare total no mode can express. Moved out of
phase 1 (its caller lives here, and the total-in/total-out shape it had was wrong) but
deliberately not deferred to phase 4: this is the phase that turns the feature on, and
shipping activation without it would leave a merged interval where left and right docked
panels can starve the centre surface. A required safety constraint cannot land after the
feature it protects;
mode-aware aria-valuetext on the separator. The numeric range is honest at both
extremes, but its interior is not continuous (49–179 is no resting width in any mode) and one
number can mean either a wide sidebar or rail+drawer, so a bare aria-valuenow does not
communicate position.
Tests: ## Tests → Rail state, Focused drawer and Drag transitions in full, plus Wide state bullet 5.
Acceptance subset: claims bullets 1–6 and 8. Defers bullet 7 (header, main surface and
optional right inspector resize correctly) to phase 4.
Phase 4 — Reflow, integration and verification
Implement:
reflow the active centre surface on every width or mode change through the existing
resize observers and layout hooks — CodeMirror geometry, the result table and its
virtualised rows, the editor/results splitter, Dashboard layout engines and charts, sticky
Dashboard/filter toolbars, and the optional right inspector — never arbitrary delayed
resize events;
verify the centre-width clamp landed in phase 3 holds up under real reflow;
the Add Library drag assignment to Dashboard panels and variables #428 integration: during an active Library-query drag in rail mode, bounded hover on the
Dashboards rail icon opens the docked Dashboards drawer via openFocusedSection('dashboards'),
drops land on Dashboard / Panels / Filters targets, leaving or cancelling the drag clears the
temporary hover state, and the drawer never becomes an overlay;
tests/e2e/left-nav-fold.spec.js and a real-browser verification pass — happy-dom sees no
CSS layout, so the recommended threshold values are confirmed here.
Tests: ## Tests → Reflow and regressions, in full, plus the new e2e spec.
Acceptance subset: claims bullet 7 and re-verifies 1–8 end to end. This phase closes the
issue.
Tests
State and preferences
fresh desktop session starts from the documented default;
valid mode/width preferences restore;
invalid widths clamp safely;
left state is never written to workspace JSON;
mobile ignores desktop effective state while preserving preferences.
Wide state
rail is absent;
upper and lower panes render together;
switchers and horizontal splitter remain;
existing navigation behaviour is unchanged;
dragging left below the fold threshold enters rail mode once.
Rail state
rail starts below the full-width header;
exactly four section icons remain visible;
icons remain visible while a drawer is open;
active icon is marked;
clicking active icon closes the drawer;
clicking another icon switches content;
rail never expands to drawer width.
Focused drawer
drawer is docked and reduces centre width;
no overlay/backdrop is created;
no redundant role switchers render inside it;
selected resources do not auto-close it;
search/expansion/scroll state survives section and mode changes;
Escape and focus restoration are correct.
Drag transitions
wide -> rail uses hysteresis without oscillation;
rail -> wide restores the prior width;
focused drawer can fold closed or expand to wide;
pointer cancellation and window blur terminate drag safely;
keyboard separator operations match pointer transitions.
Reflow and regressions
editor, results and Dashboard resize correctly;
a running query is not cancelled by folding navigation;
Query/Dashboard surface state is preserved;
right inspector can coexist;
database tree and Dashboard tree gestures are unchanged;
Goal
Make the desktop left navigation foldable without adding collapse/expand buttons.
The user controls the navigation width by dragging its vertical frame border. A sufficiently narrow drag converts the current two-panel sidebar into a compact icon rail. Dragging the rail or its focused drawer wider converts it back into the normal two-panel sidebar.
The compact state must remain fully navigable: clicking a rail icon opens that section as a docked, full-height focused drawer beside the rail. The drawer consumes layout width and pushes the SQL/Dashboard work surface inward; it must not float over or obscure the work surface.
Mobile already uses a different navigation model and is explicitly out of scope.
Phases
Added during
/ship(2026-07-29). Branch model: one branch and one PR per phase off freshmain— phases 1 and 2 are additive/inert and land green on their own, phase 3 turns thefeature on, phase 4 closes the issue. The
<!-- ship-log -->comment is the state of record.Desktop shell geometry
The application header owns the complete top row across the viewport.
Below the header, the horizontal shell is:
The rail, sidebar, focused drawer, main surface and right inspector all begin below the header.
The left navigation must participate in normal layout. No left-navigation state uses an overlay or backdrop on desktop.
Two presentation states
Wide state
The compact rail is absent.
Show the established two stacked navigation panes:
After #427, the lower pane becomes:
Requirements:
>|<buttons for folding the left navigation.Rail state
Show a narrow vertical rail below the header.
Recommended visual width:
The rail contains four section launchers:
Open Databases navigationOpen Dashboards navigationOpen Queries navigationbefore #427,Open Library navigationafter #427Open query HistoryRequirements:
Focused drawer behaviour
Clicking a rail section opens that section as a docked drawer immediately to the right of the rail.
Examples:
The focused drawer:
Databases | DashboardsorQueries/Library | Historyswitcher buttons;Clicking the active rail icon closes the focused drawer and leaves the rail visible.
Clicking a different rail icon switches the drawer content in place without first closing the drawer.
Escape may close the focused drawer when focus is inside it, but must not also cancel a running query or trigger another global Escape action.
Section-specific behaviour
Databases
Show the existing schema search and database/table/column tree with existing click, double-click, Shift-click, drag and documentation actions unchanged.
Dashboards
Show the Dashboard hierarchy from #426 with its search, expansion, keyboard and navigation semantics unchanged.
Queries / Library
Before #427, show the existing Queries collection.
After #427, show only the zero-owner Library projection and update the rail label/tooltip to Library. The foldable shell must not introduce a separate data projection or duplicate ownership logic.
History
Show the existing history search/list and opening behaviour unchanged.
Drag-controlled state transitions
Use the vertical navigation frame border as a resize separator.
The transition must feel deliberate and must not flicker near a single threshold. Use named constants and hysteresis rather than deriving state directly from every pointer pixel.
Recommended starting values, adjustable only through real-browser layout verification:
Wide -> rail
While dragging the wide sidebar border left:
Rail -> wide
While dragging the rail's right frame border to the right:
Focused drawer resize
When a focused drawer is open, its right border is the active navigation resize separator.
Do not rely on CSS width alone to decide the semantic mode after every repaint. The mode is explicit application-shell state.
Layout state
Suggested shell state:
Persistence:
mode,wideWidthPxandfocusedDrawerWidthPxare browser preferences;focusedSectionis session UI state and need not reopen automatically after reload;StoredWorkspaceV3, Dashboard documents or query specs;The persisted preference decoder must clamp invalid or obsolete values safely.
Shared navigation state
Wide and focused presentations are two views over the same navigation state.
Preserve across transitions:
Do not create separate competing expansion/search stores for wide and focused presentations.
A transition may move DOM focus to the corresponding control in the new presentation, but it must not silently reset the user's state.
Interaction with drag-and-drop assignment
The implementation must remain compatible with #428.
When a Library-query drag is active in rail mode:
This integration may be completed by #428 if #428 has not yet landed, but the left-navigation API must provide a deterministic
openFocusedSection('dashboards')seam.Main-surface resizing
Every width or mode change must notify/reflow the active centre surface correctly:
Use existing resize observers/layout hooks rather than arbitrary delayed resize events.
Left and right docked panels may be open simultaneously. Clamp widths so the centre retains a documented minimum usable width.
Keyboard and accessibility
The vertical frame border is a keyboard-operable separator:
Keys:
Rail buttons must expose
aria-expandedandaria-controlsfor the focused drawer.Closing a focused drawer returns focus to its rail icon. Converting to wide mode restores focus to the corresponding section tab/search/tree when possible.
Mobile
At the existing mobile breakpoint:
Suggested code boundaries
App shell layout controller
Owns:
Navigation section registry
Maps rail sections to existing views and labels without duplicating their domain state.
Existing navigation views
Continue to own their tree/list rendering, search and application actions. They must not manipulate shell widths directly.
Delivery phases
Each phase's definition of done is its implement list below, the
## Testssubsections itnames, and the subset of
## Acceptance criteriait claims.## Non-goalsapplies to all four.Phase 1 — Layout core and preferences
Pure layout-decision logic plus its persisted preferences. No visible change.
Implement:
src/core/left-nav-layout.ts(new, pure, 100% covered): the named constants(
LEFT_RAIL_PX,LEFT_FOLD_THRESHOLD_PX,LEFT_WIDE_THRESHOLD_PX,LEFT_PANEL_MIN_PX,LEFT_PANEL_MAX_PX), theLeftNavigationMode/LeftNavigationSectiontypes, thehysteresis reducer mapping
(current mode, proposed px)to the next mode and width,wide-width clamping, and the keyboard step resolver (small step, large step,
Homefoldsto rail,
Endrestores wide);src/state.ts:mode,wideWidthPxandfocusedDrawerWidthPxas persisted browserpreferences,
focusedSectionas session-only UI state, theirKEYSentries, and decodersthat clamp invalid or obsolete values back to the documented defaults;
src/application/app-preferences.ts: the new keys added toPreferenceKey.Tests:
## Tests→ State and preferences, in full — including the assertion that none ofthis state reaches
StoredWorkspaceV3, Dashboard documents or query specs.Also here, because the mobile rule is a layout decision and not a rendering one:
effectiveLeftNavigationLayout(layout, isMobile), the projection that makes mobile ignorerail mode and any open drawer while leaving both persisted widths untouched.
Moved out of this phase during implementation: the centre-width clamp. Review established
that a single total-width-in/total-width-out signature is the wrong shape — there is no inverse
from a total back to
(mode, panel width), so it can return a width no mode can render (220pxis neither the rail, nor a legal wide sidebar, nor rail+drawer). Phase 3 owns it, as
layout-in/layout-out: phase 3 is what turns the feature on, and parking a required safety
constraint in phase 4 would leave a merged interval in which left and right docked panels can
starve the centre surface. Deferring an unused primitive out of phase 1 is CLAUDE.md hard rule
5; deferring a safety constraint past the feature it protects is not. (This paragraph said
"phase 4" until phase 2 reconciled it — the phase 3 and 4 sections below, and the
<!-- ship-log -->comment, have said phase 3 since phase 1 shipped.)Acceptance subset: claims none of the eight global criteria; all eight defer to phases 3
and 4. This phase's gate is the State-and-preferences list plus 100% coverage of the new core
module.
Phase 2 — Navigation section registry
Make the four navigation sections hostable by any container, over one shared store per
section. The wide sidebar renders exactly as it does today.
Implement:
LeftNavigationSectionmapped to its label, icon,accessible label and its persistent host element — built once and never rebuilt,
extending the
buildSidebarUpperpattern (Add a Databases/Dashboards switcher and dashboard hierarchy tree #426) from Databases/Dashboards to the lowerpane's Library/History;
src/ui/app-shell.ts: compose today's two-pane sidebar out of the registry's hosts;both presentations will later use, so state survives by construction.
Tests: extend
tests/unit/app-shell.test.tsandtests/unit/sidebar-upper.test.ts— the widepane composition, and exactly one host instance per section.
## Tests→ Wide statebullets 1–4.
Acceptance subset: claims none; this phase's gate is "existing navigation behaviour is
unchanged".
Phase 3 — Rail, focused drawer and the resize separator
The feature turns on.
Implement:
src/ui/left-rail.ts: theLEFT_RAIL_PXrail with four launchers reusing the existingdatabase/dashboard/layers/historyicon primitives, tooltips, distinct hover /keyboard-focus / active-drawer states,
aria-expanded+aria-controls, a width thatcannot stretch to the drawer's, and no internal text labels; mounted below the full-width
header;
src/ui/left-drawer.ts: the existing.sidebarelement is RE-PRESENTED as the drawer via onedata-nav-modeattribute(
'wide' | 'rail' | 'drawer'), written by one function (app-shell.ts'sapplyEffectiveLeftNavigationLayout) that is the sole authority for every presentationattribute. No section host ever moves between containers — phase 2's persistent-host
guarantee ("wide and focused presentations share and preserve all navigation state") holds
by construction rather than by a second module's own restoration logic. See the
<!-- ship-log -->comment's phase-3 handoff for the full rationale;openFocusedSection(section)on the app controller — the deterministic seam Add Library drag assignment to Dashboard panels and variables #428 requires;src/ui/app-shell.ts: render either the wide sidebar or rail + drawer from the explicitshell mode, keeping every section host mounted across the transition;
splitters.tsaxis: a dedicatedsrc/ui/left-nav-separator.tsmodule REPLACESsplitters.ts's old'col'axis outright(deleted, not left beside the new module) — feeding proposed widths through phase 1's
reducer via a resize session, with safe termination on
blur/visibilitychange. A fifthcompeting axis would have kept two sidebar-width authorities (the old bare clamp and the
new mode reducer) alive at once;
role="separator",aria-orientation="vertical",tabindex="0",aria-valuemin/aria-valuemax/aria-valuenow, Left/Right, Shift+Left/Right,Home,End, and a status announcement on semantic mode change only;action; closing returns focus to the rail icon; converting to wide restores focus to the
corresponding section tab, search or tree;
preferences are preserved for the next desktop session;
renderable
(mode, panel width)rather than a bare total no mode can express. Moved out ofphase 1 (its caller lives here, and the total-in/total-out shape it had was wrong) but
deliberately not deferred to phase 4: this is the phase that turns the feature on, and
shipping activation without it would leave a merged interval where left and right docked
panels can starve the centre surface. A required safety constraint cannot land after the
feature it protects;
aria-valuetexton the separator. The numeric range is honest at bothextremes, but its interior is not continuous (49–179 is no resting width in any mode) and one
number can mean either a wide sidebar or rail+drawer, so a bare
aria-valuenowdoes notcommunicate position.
Tests:
## Tests→ Rail state, Focused drawer and Drag transitions in full, plusWide state bullet 5.
Acceptance subset: claims bullets 1–6 and 8. Defers bullet 7 (header, main surface and
optional right inspector resize correctly) to phase 4.
Phase 4 — Reflow, integration and verification
Implement:
resize observers and layout hooks — CodeMirror geometry, the result table and its
virtualised rows, the editor/results splitter, Dashboard layout engines and charts, sticky
Dashboard/filter toolbars, and the optional right inspector — never arbitrary delayed
resize events;
Dashboards rail icon opens the docked Dashboards drawer via
openFocusedSection('dashboards'),drops land on Dashboard / Panels / Filters targets, leaving or cancelling the drag clears the
temporary hover state, and the drawer never becomes an overlay;
tests/e2e/left-nav-fold.spec.jsand a real-browser verification pass — happy-dom sees noCSS layout, so the recommended threshold values are confirmed here.
Tests:
## Tests→ Reflow and regressions, in full, plus the new e2e spec.Acceptance subset: claims bullet 7 and re-verifies 1–8 end to end. This phase closes the
issue.
Tests
State and preferences
Wide state
Rail state
Focused drawer
Drag transitions
Reflow and regressions
Acceptance criteria
Non-goals