feat(shell): fluid layout with collapsible sidebar drawer + fix query-tool MultiSelect
Convert portal shell from block-centered (max-width 1600px) layout to full-viewport fluid flexbox with collapsible sidebar: desktop push-mode (240px → 0), mobile overlay drawer with backdrop. Rename .content → .shell-content to avoid CSS collision with page-level classes. Override page-level max-width constraints when embedded in shell. Also replace native <select multiple> in query-tool with shared MultiSelect component for equipment and workcenter group filters, matching resource-status/history UX. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-02-11
|
||||
@@ -0,0 +1,89 @@
|
||||
## Context
|
||||
|
||||
The portal shell (`frontend/src/portal-shell/App.vue` + `frontend/src/portal-shell/style.css`) currently uses a block-centered layout: `.shell` has `max-width: 1600px; margin: 0 auto; padding: 20px`, `.shell-main` uses `display: grid; grid-template-columns: 220px minmax(0,1fr)`, and both `.sidebar` and `.content` are styled as bordered, rounded white cards. Page modules add their own `max-width` (1680-1900px) and `padding`.
|
||||
|
||||
The result is a segmented, boxed appearance that wastes screen real estate. The migration to full-screen fluid layout with a collapsible drawer addresses this.
|
||||
|
||||
**Constraints:**
|
||||
- No new dependencies (pure Vue 3 + Tailwind CSS 3 + vanilla CSS)
|
||||
- Sidebar navigation items are text-only (no icons) — icon-only collapsed mode is not viable
|
||||
- Pages must still render correctly standalone (outside the portal shell) for development
|
||||
- Existing color scheme and navigation hierarchy must be preserved
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
- Full-viewport fluid layout with no max-width constraints at shell or page level
|
||||
- Collapsible sidebar: push-mode on desktop (content resizes), overlay-mode on mobile
|
||||
- Smooth 300ms transitions for sidebar open/close
|
||||
- Sidebar state persistence within browser session
|
||||
- Keyboard accessibility (Escape closes mobile drawer)
|
||||
- Maintain all existing navigation, routing, and page functionality
|
||||
|
||||
**Non-Goals:**
|
||||
- Icon-only collapsed sidebar mode (nav items have no icons; complete hide is chosen)
|
||||
- Redesigning individual page components or card layouts within pages (deferred to Phase 2)
|
||||
- Adding new dependencies (headlessui, shadcn, etc.)
|
||||
- Server-side changes or API modifications
|
||||
- Full ARIA focus-trap for mobile overlay (simple Escape + backdrop click suffices)
|
||||
- Verifying pages not registered in shell route contracts (e.g. routes with missing contract warnings)
|
||||
|
||||
## Decisions
|
||||
|
||||
### D1: Flexbox over CSS Grid for `.shell-main`
|
||||
|
||||
**Choice**: Replace `display: grid; grid-template-columns: 220px minmax(0,1fr)` with `display: flex`.
|
||||
|
||||
**Rationale**: CSS Grid `grid-template-columns` cannot be smoothly animated with CSS transitions. Flexbox with `width` + `min-width` transitions on the sidebar provides smooth animated collapse/expand. The layout is a simple two-column split, which flexbox handles naturally.
|
||||
|
||||
### D2: Sidebar collapses to 0 width (complete hide)
|
||||
|
||||
**Choice**: Desktop collapsed state sets sidebar `width: 0; min-width: 0; overflow: hidden`.
|
||||
|
||||
**Rationale**: The current navigation items are text-only with no icons. An icon-only strip would require adding icons to every nav item — a separate design effort. Complete hide is the simplest approach that provides maximum content space.
|
||||
|
||||
### D3: Mobile overlay vs desktop push
|
||||
|
||||
**Choice**: Desktop uses push mode (content resizes via flex). Mobile (<=900px) uses fixed-position overlay with backdrop.
|
||||
|
||||
**Rationale**: On desktop, push mode provides a stable layout without content obscuring. On mobile, the viewport is too narrow for push mode — overlay maximizes both sidebar and content usability. The 900px breakpoint matches the existing responsive threshold.
|
||||
|
||||
### D4: JavaScript viewport detection instead of pure CSS media queries
|
||||
|
||||
**Choice**: Use a `resize` event listener to set `isMobile` ref, then apply CSS classes based on state.
|
||||
|
||||
**Rationale**: The sidebar has three distinct behaviors: desktop-expanded, desktop-collapsed, and mobile-overlay. Pure CSS media queries cannot differentiate between "desktop-collapsed" and "mobile-hidden" since both have zero sidebar width. JavaScript state allows clean separation of desktop collapse (user choice) and mobile overlay (viewport-driven).
|
||||
|
||||
### D5: `sessionStorage` for sidebar preference
|
||||
|
||||
**Choice**: Persist collapsed/expanded state in `sessionStorage` (not `localStorage`).
|
||||
|
||||
**Rationale**: Session-scoped persistence means the user's choice persists across page navigations and refreshes within the same tab, but new tabs start with sidebar expanded. This matches the ephemeral nature of a UI layout preference. The codebase already uses `sessionStorage` for recovery keys in `NativeRouteView.vue`.
|
||||
|
||||
### D6: Page-level max-width override via scoped selectors
|
||||
|
||||
**Choice**: Add `.shell-content .xxx-page { max-width: none; }` rules in `frontend/src/portal-shell/style.css` rather than modifying each page's CSS. Include `.shell-content .tables-page .container` for the tables module where `max-width` is on the inner `.container` element, not the page wrapper.
|
||||
|
||||
**Rationale**: This keeps all shell-level layout overrides in one file, preserves standalone page rendering (pages still have their own max-width when accessed directly), and avoids touching 10+ page CSS files.
|
||||
|
||||
### D7: Content area background color
|
||||
|
||||
**Choice**: Change `.shell-content` background from `#ffffff` to `#f5f7fa` (app background color).
|
||||
|
||||
**Rationale**: With borders and border-radius removed, the content area merges visually with the page background. Using the app bg color instead of white allows individual page cards (`.section-card`, `.header-gradient`) to stand out on their own. This maintains the card-on-background visual hierarchy.
|
||||
|
||||
### D8: Rename `.content` to `.shell-content` to prevent CSS collision
|
||||
|
||||
**Choice**: Rename the shell's main content area class from `.content` to `.shell-content` in both the template and CSS.
|
||||
|
||||
**Rationale**: The tables module (`tables/App.vue:81`) and potentially other page modules also use a `.content` class. If the shell rewrites `.content` with `flex: 1; overflow-y: auto`, it will leak into page-level `.content` elements and cause layout breakage. Using `.shell-content` scopes the styles unambiguously to the shell layer. This is a low-cost rename (one template attribute + CSS find-and-replace in a single file) with high defensive value.
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- **[Text wrapping during animation]** → Sidebar text may wrap awkwardly during 300ms width transition. Mitigate with `white-space: nowrap; overflow: hidden` on the sidebar.
|
||||
- **[Double scrollbar]** → Content area gets `overflow-y: auto`, but some pages have `min-height: 100vh`. Mitigate with `.shell-content .resource-page { min-height: auto }` overrides.
|
||||
- **[Residual gutter after collapse]** → If legacy `.shell-main` `gap: 12px` is left in place during flex migration, collapsed sidebar still leaves visible empty space. Mitigate by removing `gap` from `.shell-main`.
|
||||
- **[Health popup z-index clash]** → `.health-popup` uses `z-index: 30` which is below mobile sidebar overlay `z-index: 40`. Mitigate by bumping health popup to `z-index: 50`.
|
||||
- **[Wide content readability]** → Removing all max-width means tables and cards stretch on ultra-wide monitors. Accepted trade-off per user preference. Individual page teams can re-add max-width later if needed.
|
||||
- **[sessionStorage loss]** → If user clears session data, sidebar preference resets. Acceptable — sidebar defaults to expanded which is a safe fallback.
|
||||
- **[CSS class collision]** → Shell `.content` class collides with page-level `.content` (e.g. tables module). Mitigate by renaming to `.shell-content` (Decision D8).
|
||||
@@ -0,0 +1,35 @@
|
||||
## Why
|
||||
|
||||
The portal shell just completed an iframe-to-Vue migration but still uses a block/card-based layout with `max-width: 1600px` centered shell, fixed `220px` sidebar, and rounded-corner card containers. This creates a segmented "boxed" appearance with wasted screen real estate and occasional card overflow issues. Converting to a full-screen fluid layout with a collapsible sidebar drawer maximizes usable space and provides a modern dashboard experience.
|
||||
|
||||
> **Scope**: This change is **Portal Shell Modernize Phase 1** — it targets the shell layout layer only (header, sidebar, content wrapper). Page-level component redesign (cards, tables, charts within individual pages) is out of scope and should be addressed in a separate Phase 2 change.
|
||||
|
||||
## What Changes
|
||||
|
||||
- **BREAKING**: Remove `max-width` constraints from the portal shell (`.shell` 1600px) and all page-level wrappers (`.dashboard` 1800px, `.qc-gate-page` 1900px, `.job-query-page` 1680px, etc.) — content fills available viewport width
|
||||
- Remove border-radius and border from the shell header, sidebar, and content area — edge-to-edge fluid appearance
|
||||
- Convert `.shell-main` from CSS Grid (`220px | 1fr`) to Flexbox with animated sidebar width transitions
|
||||
- Add collapsible sidebar: desktop push-mode (width animates from 240px to 0), mobile overlay-mode (slide-in drawer with backdrop)
|
||||
- Add hamburger toggle button in the shell header
|
||||
- Add sidebar state persistence via `sessionStorage`
|
||||
- Add mobile overlay backdrop with fade transition
|
||||
- Add keyboard accessibility (Escape to close mobile drawer)
|
||||
- Maintain existing gradient color scheme, navigation hierarchy, card-level styling within pages
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
- `collapsible-sidebar-drawer`: Sidebar collapse/expand behavior, toggle button, mobile overlay, state persistence, keyboard accessibility, smooth transitions
|
||||
|
||||
### Modified Capabilities
|
||||
- `spa-shell-navigation`: Shell layout changes from block-centered grid to full-screen fluid flexbox; sidebar becomes collapsible; content area becomes scrollable flex child
|
||||
- `tailwind-design-system`: CSS variables updated (`--portal-shell-max-width` removed), `.u-content-shell` utility changed from max-width to full-width
|
||||
|
||||
## Impact
|
||||
|
||||
- **Portal shell** (`frontend/src/portal-shell/App.vue`, `frontend/src/portal-shell/style.css`): Major layout restructure — template adds toggle button, overlay, class bindings; CSS rewrites `.shell`, `.shell-main`, `.sidebar`; `.content` renamed to `.shell-content` to avoid class collision with page-level `.content` (e.g. in tables module)
|
||||
- **Global styles** (`frontend/src/styles/tailwind.css`): CSS variable updates, utility class changes
|
||||
- **Shell-registered page modules only**: Page-level max-width and padding overridden when embedded in shell via `.shell-content .xxx-page` selectors (standalone rendering unaffected). Verification scope limited to pages registered in shell route contracts — unregistered routes (e.g. missing contract warnings) are excluded
|
||||
- **Health popup**: z-index adjustment needed to stay above new sidebar overlay layer
|
||||
- **No new dependencies**: Pure Vue + Tailwind + CSS, no additional libraries
|
||||
- **New test file**: `frontend/tests/portal-shell-sidebar.test.js` — automated tests (Node `node:test` harness) for sidebar collapse/expand, mobile overlay, and sessionStorage persistence
|
||||
@@ -0,0 +1,67 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Sidebar SHALL be collapsible via a toggle button
|
||||
The portal shell SHALL provide a toggle button in the header that collapses and expands the sidebar. On desktop viewports (>900px), collapsing SHALL animate the sidebar width from 240px to 0px using push mode, causing the content area to resize and fill the freed space. Expanding SHALL reverse the animation.
|
||||
|
||||
#### Scenario: Desktop sidebar collapse
|
||||
- **WHEN** a user clicks the sidebar toggle button on a desktop viewport
|
||||
- **AND** the sidebar is currently expanded
|
||||
- **THEN** the sidebar width SHALL animate to 0px over 300ms
|
||||
- **THEN** the content area SHALL expand to fill the full viewport width
|
||||
|
||||
#### Scenario: Desktop sidebar expand
|
||||
- **WHEN** a user clicks the sidebar toggle button on a desktop viewport
|
||||
- **AND** the sidebar is currently collapsed
|
||||
- **THEN** the sidebar width SHALL animate to 240px over 300ms
|
||||
- **THEN** the content area SHALL shrink to accommodate the sidebar
|
||||
|
||||
### Requirement: Mobile sidebar SHALL use overlay drawer mode
|
||||
On mobile viewports (<=900px), the sidebar SHALL behave as a fixed-position overlay drawer that slides in from the left, with a semi-transparent backdrop covering the content area.
|
||||
|
||||
#### Scenario: Mobile sidebar open
|
||||
- **WHEN** a user taps the toggle button on a mobile viewport
|
||||
- **AND** the sidebar is currently hidden
|
||||
- **THEN** the sidebar SHALL slide in from the left as a fixed overlay (280px width)
|
||||
- **THEN** a semi-transparent backdrop SHALL appear behind the sidebar and above the content
|
||||
|
||||
#### Scenario: Mobile sidebar close via backdrop
|
||||
- **WHEN** a user taps the backdrop overlay
|
||||
- **THEN** the sidebar SHALL slide out to the left
|
||||
- **THEN** the backdrop SHALL fade out
|
||||
|
||||
#### Scenario: Mobile sidebar close via Escape key
|
||||
- **WHEN** the mobile sidebar overlay is open
|
||||
- **AND** a user presses the Escape key
|
||||
- **THEN** the sidebar SHALL close
|
||||
|
||||
#### Scenario: Mobile sidebar closes on navigation
|
||||
- **WHEN** the mobile sidebar overlay is open
|
||||
- **AND** a user clicks a navigation link
|
||||
- **THEN** the sidebar SHALL automatically close after the route change
|
||||
|
||||
### Requirement: Sidebar state SHALL persist within the browser session
|
||||
The collapsed/expanded state of the desktop sidebar SHALL be persisted to `sessionStorage` so that the preference survives page refreshes within the same browser tab.
|
||||
|
||||
#### Scenario: State persistence on refresh
|
||||
- **WHEN** a user collapses the sidebar and refreshes the page
|
||||
- **THEN** the sidebar SHALL remain collapsed after reload
|
||||
|
||||
#### Scenario: New tab starts expanded
|
||||
- **WHEN** a user opens the portal in a new browser tab
|
||||
- **THEN** the sidebar SHALL default to expanded regardless of other tabs' state
|
||||
|
||||
### Requirement: Sidebar transitions SHALL respect reduced-motion preference
|
||||
All sidebar transitions (width animation, overlay slide, backdrop fade) SHALL be disabled when the user has `prefers-reduced-motion: reduce` enabled.
|
||||
|
||||
#### Scenario: Reduced motion disables sidebar animation
|
||||
- **WHEN** the user's OS or browser has reduced-motion enabled
|
||||
- **AND** the user toggles the sidebar
|
||||
- **THEN** the sidebar state SHALL change instantly without animation
|
||||
|
||||
### Requirement: Toggle button SHALL be accessible
|
||||
The sidebar toggle button SHALL include `aria-label` and `aria-expanded` attributes for screen reader accessibility.
|
||||
|
||||
#### Scenario: Screen reader announces toggle state
|
||||
- **WHEN** a screen reader user focuses the toggle button
|
||||
- **THEN** the button SHALL announce its current expanded/collapsed state via `aria-expanded`
|
||||
- **THEN** the button SHALL have an `aria-label` describing its purpose
|
||||
@@ -0,0 +1,26 @@
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Portal SHALL provide a SPA shell driven by Vue Router
|
||||
The portal frontend SHALL use a single SPA shell entry and Vue Router to render page modules without iframe embedding, and SHALL route each page through native route-view integration. The shell layout SHALL use a full-viewport fluid layout with flexbox, removing all max-width constraints and block-centered styling. The main content area (`.shell-content`) SHALL fill available space as a flex child, and the sidebar SHALL be a collapsible flex child that pushes content when expanded on desktop. The content area class SHALL be `.shell-content` (not `.content`) to avoid CSS collision with page-level `.content` classes.
|
||||
|
||||
#### Scenario: Drawer navigation renders integrated route view
|
||||
- **WHEN** a user clicks a sidebar page entry whose migration mode is `native`
|
||||
- **THEN** the active route SHALL be updated through Vue Router
|
||||
- **THEN** the main content area SHALL render the corresponding page module inside shell route-view without iframe usage
|
||||
- **THEN** the content area SHALL fill the available viewport width minus the sidebar width (if sidebar is expanded)
|
||||
|
||||
#### Scenario: Shell layout fills full viewport
|
||||
- **WHEN** the portal shell renders
|
||||
- **THEN** the shell SHALL span the full viewport width with no max-width constraint
|
||||
- **THEN** the header SHALL span edge-to-edge with no border-radius
|
||||
- **THEN** the sidebar and content area SHALL have no outer borders or border-radius
|
||||
|
||||
#### Scenario: Page-level max-width constraints are removed when embedded
|
||||
- **WHEN** a page module registered in the shell route contracts renders inside `.shell-content`
|
||||
- **THEN** page-level max-width constraints SHALL be overridden to allow full-width rendering
|
||||
- **THEN** page-level duplicate padding SHALL be removed to avoid double spacing
|
||||
- **THEN** standalone page rendering (outside the shell) SHALL remain unaffected
|
||||
|
||||
#### Scenario: Shell content class avoids collision with page-level classes
|
||||
- **WHEN** a page module that uses its own `.content` class renders inside the shell
|
||||
- **THEN** the shell's content wrapper (`.shell-content`) SHALL NOT interfere with the page's `.content` styling
|
||||
@@ -0,0 +1,15 @@
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Frontend styles SHALL be governed by Tailwind design tokens
|
||||
The frontend SHALL define a Tailwind-based design token system for color, spacing, typography, radius, and elevation to ensure consistent styling across modules. The `--portal-shell-max-width` CSS variable SHALL be set to `none` to support the fluid layout. A `--portal-sidebar-width` variable SHALL be added for sidebar width reference. The `.u-content-shell` utility class SHALL use `width: 100%` instead of `max-width` constraint.
|
||||
|
||||
#### Scenario: Shared token usage across modules
|
||||
- **WHEN** two report modules render equivalent UI elements (e.g., card, filter chip, primary button)
|
||||
- **THEN** they SHALL use the same token-backed style semantics
|
||||
- **THEN** visual output SHALL remain consistent across modules
|
||||
|
||||
#### Scenario: Fluid layout tokens
|
||||
- **WHEN** the portal shell renders
|
||||
- **THEN** `--portal-shell-max-width` SHALL resolve to `none`
|
||||
- **THEN** `--portal-sidebar-width` SHALL resolve to `240px`
|
||||
- **THEN** `.u-content-shell` SHALL apply `width: 100%` without max-width constraint
|
||||
@@ -0,0 +1,69 @@
|
||||
## 1. Sidebar State Management (App.vue script)
|
||||
|
||||
- [x] 1.1 Add `sidebarCollapsed`, `sidebarMobileOpen`, `isMobile` refs to `App.vue` `<script setup>`
|
||||
- [x] 1.2 Implement `toggleSidebar()`, `closeMobileSidebar()`, `checkViewport()` functions
|
||||
- [x] 1.3 Add `sessionStorage` load/save for sidebar collapsed preference (`portal-shell:sidebar-collapsed`)
|
||||
- [x] 1.4 Register `resize` listener in `onMounted`, clean up in `onUnmounted`
|
||||
- [x] 1.5 Add Escape key handler to close mobile sidebar overlay
|
||||
- [x] 1.6 Add `closeMobileSidebar()` call to existing route watcher for auto-close on navigation
|
||||
|
||||
## 2. Template Restructure (App.vue template)
|
||||
|
||||
- [x] 2.1 Add `.shell-header-left` wrapper with hamburger toggle `<button>` (inline SVG, `aria-label`, `aria-expanded`)
|
||||
- [x] 2.2 Add `:class` binding on `.shell` root: `{ 'sidebar-collapsed': sidebarCollapsed && !isMobile }`
|
||||
- [x] 2.3 Add `:class` bindings on `<aside class="sidebar">`: `sidebar--collapsed`, `sidebar--mobile-open`, `sidebar--mobile-closed`
|
||||
- [x] 2.4 Add `<Transition name="overlay-fade">` wrapped `<div class="sidebar-overlay">` before `<main>`, shown when `isMobile && sidebarMobileOpen`
|
||||
- [x] 2.5 Rename `<section class="content">` to `<section class="shell-content">` in the template
|
||||
|
||||
## 3. Shell CSS Rewrite (`frontend/src/portal-shell/style.css`)
|
||||
|
||||
- [x] 3.1 Rename all `.content` selectors to `.shell-content` throughout `style.css` (`.content`, `.content .xxx-page`, etc.)
|
||||
- [x] 3.2 `.shell`: remove `max-width: 1600px`, `padding: 20px`, `margin: 0 auto`; add `display: flex; flex-direction: column`
|
||||
- [x] 3.3 `.shell-header`: remove `border-radius: 12px`; adjust `padding` to `12px 20px`; add `flex-shrink: 0`
|
||||
- [x] 3.4 Add `.shell-header-left` styles (flex, align-items center, gap 12px)
|
||||
- [x] 3.5 Add `.sidebar-toggle` button styles (36x36, border/bg rgba white, hover, focus-visible outline)
|
||||
- [x] 3.6 `.shell-main`: replace `display: grid; grid-template-columns` with `display: flex; flex: 1; overflow: hidden`; remove legacy `gap: 12px` to prevent residual gutter when sidebar collapses
|
||||
- [x] 3.7 `.sidebar`: rewrite to `width: 240px; min-width: 240px; border-right; overflow-y: auto; flex-shrink: 0; transition: width/min-width/padding 0.3s; white-space: nowrap` — remove border-radius, border, sticky, height: fit-content
|
||||
- [x] 3.8 Add `.sidebar--collapsed` styles: `width: 0; min-width: 0; padding: 0; border-right: none; overflow: hidden`
|
||||
- [x] 3.9 Add `.sidebar--mobile-closed` styles: fixed position, `transform: translateX(-100%)`, 280px width, z-index 40
|
||||
- [x] 3.10 Add `.sidebar--mobile-open` styles: fixed position, `transform: translateX(0)`, box-shadow, z-index 40
|
||||
- [x] 3.11 Add `.sidebar-overlay` styles: fixed inset 0, z-index 35, `background: rgba(0,0,0,0.4)`
|
||||
- [x] 3.12 Add `overlay-fade` transition classes (enter/leave opacity 0.3s)
|
||||
- [x] 3.13 `.shell-content`: remove `border`, `border-radius: 10px`, `min-height: 70vh`; add `flex: 1; min-width: 0; overflow-y: auto`; change background to `#f5f7fa`
|
||||
- [x] 3.14 Bump `.health-popup` z-index from `30` to `50`
|
||||
|
||||
## 4. Page-Level Overrides (`frontend/src/portal-shell/style.css`)
|
||||
|
||||
- [x] 4.1 Add `.shell-content .resource-page, .shell-content .dashboard, .shell-content .qc-gate-page, .shell-content .job-query-page, .shell-content .excel-query-page, .shell-content .query-tool-page, .shell-content .tmtt-page, .shell-content .tables-page` override: `max-width: none; min-height: auto`
|
||||
- [x] 4.2 Add `.shell-content .tables-page .container` override: `max-width: none` (tables module has max-width on inner `.container`, not on page wrapper)
|
||||
- [x] 4.3 Add `.shell-content .resource-page` override: `padding: 0` (remove duplicate padding)
|
||||
|
||||
## 5. Responsive and Accessibility (`frontend/src/portal-shell/style.css`)
|
||||
|
||||
- [x] 5.1 Simplify `@media (max-width: 900px)` block — remove grid/sidebar rules (now JS-driven); keep header/content padding adjustments only
|
||||
- [x] 5.2 Extend `@media (prefers-reduced-motion: reduce)` to include `.sidebar`, `.sidebar--mobile-*`, `.sidebar-overlay`, `.overlay-fade-*`, `.sidebar-toggle`
|
||||
|
||||
## 6. CSS Variables Update (`frontend/src/styles/tailwind.css`)
|
||||
|
||||
- [x] 6.1 Change `--portal-shell-max-width` from `1600px` to `none`
|
||||
- [x] 6.2 Add `--portal-sidebar-width: 240px` and `--shell-header-height: 56px`
|
||||
- [x] 6.3 Update `.u-content-shell` utility: replace `max-width` with `width: 100%`
|
||||
|
||||
## 7. Automated Tests
|
||||
|
||||
- [x] 7.1 Create `frontend/tests/portal-shell-sidebar.test.js` using existing Node `node:test` harness
|
||||
- [x] 7.2 Test: desktop sidebar collapse — toggle sets `sidebarCollapsed` to true, sidebar gets `sidebar--collapsed` class
|
||||
- [x] 7.3 Test: mobile overlay close via backdrop — clicking overlay calls `closeMobileSidebar()`, `sidebarMobileOpen` becomes false
|
||||
- [x] 7.4 Test: mobile overlay close via Escape — pressing Escape when overlay open closes sidebar
|
||||
- [x] 7.5 Test: sessionStorage persistence — collapsing sidebar writes to sessionStorage; mounting with stored value restores collapsed state
|
||||
|
||||
## 8. Manual Verification
|
||||
|
||||
- [x] 8.1 Run `npm run build` — confirm no build errors
|
||||
- [x] 8.2 Test desktop: sidebar expanded → toggle collapse → toggle expand (smooth 300ms animation)
|
||||
- [x] 8.3 Test mobile (<= 900px): toggle opens overlay drawer with backdrop → tap backdrop closes → Escape closes
|
||||
- [x] 8.4 Test route navigation auto-closes mobile sidebar
|
||||
- [x] 8.5 Test health popup z-index: open health popup while mobile sidebar is open → popup stays on top
|
||||
- [x] 8.6 Test shell-registered page modules render fluid (no max-width centering) within the shell
|
||||
- [x] 8.7 Test `prefers-reduced-motion`: all transitions disabled
|
||||
- [x] 8.8 Verify tables module `.content` class is not affected by shell `.shell-content` styles
|
||||
Reference in New Issue
Block a user