feat(calm-hub-ui): mobile responsive layouts for CALM Hub UI#2700
Open
rocketstack-matt wants to merge 16 commits into
Open
feat(calm-hub-ui): mobile responsive layouts for CALM Hub UI#2700rocketstack-matt wants to merge 16 commits into
rocketstack-matt wants to merge 16 commits into
Conversation
The Hub used a rigid three-column flex layout (tree navigation, content, details panel) with fixed/percentage widths that could not fit on phones or tablet-portrait viewports. - Add a useMediaQuery/useIsMobile hook (test-safe; guards window.matchMedia) - Hub: below the lg breakpoint the tree navigation becomes an off-canvas drawer with a dimmed backdrop, toggled by an "Explore" button; the details Sidebar becomes a full-screen overlay. Desktop behaviour is unchanged. - Sidebar (details panel): responsive width (w-full on mobile, w-96 on lg+) - GlobalSearchBar: responsive input width and viewport-bounded dropdown - SectionHeader: allow the breadcrumb and tabs to wrap instead of overflowing - Compare view: stack the two diff graphs vertically on narrow viewports - Add matchMedia polyfill to the vitest setup and tests for the new hook and the Hub mobile drawer behaviour Signed-off-by: Matthew Bain <matt@rocketstack.co>
Follow-up to the responsive layout work, focused on the diagram experience and a regression it exposed. - Fix mobile data-loading regression: the URL deep-link / global-search loading lives inside TreeNavigation, which was unmounted on mobile until the drawer opened, so direct links and search results loaded nothing. The mobile drawer now keeps TreeNavigation mounted and slides it off-canvas via transform instead of unmounting. - Hide the minimap on mobile in the single (ArchitectureGraph/PatternGraph) and compare (DiffGraph) views — it was oversized and overlapped the canvas. - Collapse the node search/type-filter bar to a single icon on mobile, expanding on tap. - Tuck the timeline behind a floating clock button that opens it as a bottom sheet (TimelineBar gains an initialExpanded prop for the sheet), instead of permanently occupying the short viewport. - Make the diagram tabs icon-only on mobile and trim the Explore trigger so the section header stops wrapping to extra lines. Signed-off-by: Matthew Bain <matt@rocketstack.co>
On mobile the tree-navigation ("Explore") menu and the node/relationship
detail panel were partial floating cards over a dimmed backdrop. Promote
them to full-screen panels that slide in (the menu from the left, the
detail from the right), each dismissed via its own header control rather
than a backdrop tap.
- Hub: tree-navigation panel is now fixed inset-0 full-screen (kept mounted
and slid off-canvas so deep-link/search loading still runs); the details
overlay is full-screen with a slide-in-right animation and no backdrop.
- Sidebar: drop the card padding/rounding/shadow on mobile so it fills the
screen edge-to-edge; keep the desktop card (lg:p-4 / lg:rounded-box).
- Add a slide-in-right keyframe utility for the detail push animation.
- Update the Hub mobile tests to assert open/closed via the dialog role.
Signed-off-by: Matthew Bain <matt@rocketstack.co>
Replace the nested expanding tree with a native-style drill-down on mobile: each tap pushes the next level as a flat list (Namespaces -> namespace -> resource type -> resource; Control Domains -> domain -> control), with a back button to pop a level and a leaf tap that loads the resource. - Add MobileNavMenu: a self-contained drill-down navigator that fetches each level on demand, navigates to the chosen resource URL, and (like the desktop tree) loads deep-linked / globally-searched resources via a URL-params effect so direct links still work on mobile. - Extract the shared resource-loading/version-resolution helpers into navigation-loaders.ts, consumed by both TreeNavigation (desktop) and MobileNavMenu (mobile), removing the duplicated logic. - Hub: render MobileNavMenu in the mobile panel (TreeNavigation stays for desktop); the menu closes itself when a resource is chosen. - Tests for MobileNavMenu drill-down/back/deep-link; update Hub mobile tests. Signed-off-by: Matthew Bain <matt@rocketstack.co>
…mobile zoom controls - Navbar: remove the Hub and Visualizer links. Add an "Explore" button (shown when the page provides a handler) that toggles the explorer. - Hub: the navbar Explore button opens the mobile drill-down panel on mobile and toggles the desktop sidebar on desktop; remove the in-content Explore button. The Visualizer keeps no nav links (reachable via /visualizer). - Diagram: hide the ReactFlow zoom controls on mobile (single, pattern, and compare views) — pinch-to-zoom is the native touch interface; controls remain on desktop. - Tests: Navbar mock exposes the Explore toggle; add a desktop navbar-toggle test; update mobile tests accordingly. Signed-off-by: Matthew Bain <matt@rocketstack.co>
…plorer search - Diagram: make the render pane full-bleed (drop the section-header bar and card chrome) and move the Diagram/JSON/Deployments switch plus the breadcrumb into a hamburger menu pinned to the top-right of the canvas. Offset the in-canvas node search so it clears the menu. - Mobile search: add an always-visible search bar to the mobile explorer (MobileNavMenu); while a query is present the results take over the explorer body. Search stays in the navbar on desktop (hidden below lg), unchanged. - Add ExplorerSearch (inline, takeover results) alongside the existing navbar GlobalSearchBar. - Tests for ExplorerSearch; stub it in the MobileNavMenu test; adjust the DiagramSection view-mode assertions for the new menu. Signed-off-by: Matthew Bain <matt@rocketstack.co>
Open the diagram's view-options (breadcrumb + Diagram/JSON/Deployments) as a full-screen overlay on mobile, consistent with the explorer and detail panels, instead of a small dropdown. Desktop keeps the dropdown. Signed-off-by: Matthew Bain <matt@rocketstack.co>
The view-options hamburger no longer floats over the render pane; it is portalled into a navbar slot (#navbar-actions) so it sits in the top nav on both mobile and desktop. Falls back to inline rendering when no navbar slot is present (e.g. in isolated component tests). Signed-off-by: Matthew Bain <matt@rocketstack.co>
…view icon Desktop keeps its original section header with inline Diagram/JSON/Deployments tabs and card chrome — the full-bleed render pane, navbar view-options menu and full-screen view overlay now apply on mobile only. The mobile navbar trigger shows the icon of the currently active view instead of a generic hamburger. Signed-off-by: Matthew Bain <matt@rocketstack.co>
…menu On mobile the in-canvas node search is replaced by a "Search components" section inside the view-options menu. A NodeSearchProvider context lets the menu drive the graph's search/type-filter without prop-drilling; the graph hides its in-canvas search bar when the context is external. Desktop and the standalone Visualizer keep the in-canvas search (graph-local state). Signed-off-by: Matthew Bain <matt@rocketstack.co>
Centre the CALM logo in the navbar, respect device safe-area insets so the bar never slides under the notch or overflows the viewport, restyle the mobile view-options menu to match the explorer rows, and fold the timeline action into that menu instead of a separate navbar button. Signed-off-by: Matthew Bain <matt@rocketstack.co>
Replace the bottom-sheet timeline with a full-screen overlay that matches the view-options menu chrome (header bar with title and close button), giving the moment list and change details the full viewport on mobile. Signed-off-by: Matthew Bain <matt@rocketstack.co>
On mobile, present the control's Requirement and Configuration as two tabs instead of stacked panels, and reformat each pane (stacked header, view toggle, and horizontally scrollable version pickers) to suit narrow screens. Desktop keeps the existing two-panel layout. Signed-off-by: Matthew Bain <matt@rocketstack.co>
…mobile-responsive-vrquce Signed-off-by: Matthew Bain <matt@rocketstack.co> # Conflicts: # calm-hub-ui/index.html # calm-hub-ui/src/hub/components/tree-navigation/TreeNavigation.tsx
…h renders Add a Responsive Design section covering the useIsMobile breakpoint hook, the mobile-only-must-not-change-desktop rule, and the established mobile patterns (full-screen overlays, navbar-hosted actions, drill-down explorer, tabbed detail views, safe-area handling). Strengthen the Testing section to require verifying both desktop and mobile renders, with the matchMedia mocking pattern for unit tests. Signed-off-by: Matthew Bain <matt@rocketstack.co>
Contributor
There was a problem hiding this comment.
Pull request overview
Adds mobile-responsive behavior across CALM Hub UI by introducing a shared useIsMobile breakpoint hook and reworking navigation, diagram controls, and detail views to be touch-friendly on small screens while aiming to preserve the desktop layout.
Changes:
- Introduces
useMediaQuery/useIsMobileplus a jsdommatchMediapolyfill and associated unit tests. - Reworks Hub navigation for mobile (full-screen drill-down explorer + explorer-hosted search) and updates diagram/timeline/view menus for mobile overlays.
- Adjusts Visualizer/ReactFlow components (minimap/controls/search) to behave appropriately on mobile, including shared node-search state via context.
Reviewed changes
Copilot reviewed 29 out of 29 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| calm-hub-ui/vitest.setup.ts | Adds matchMedia polyfill for jsdom to support responsive tests. |
| calm-hub-ui/src/visualizer/components/sidebar/Sidebar.tsx | Makes sidebar responsive (full-width on mobile, fixed width on desktop). |
| calm-hub-ui/src/visualizer/components/reactflow/SearchBar.tsx | Adds mobile “collapsed-to-icon” behavior and forceExpanded mode. |
| calm-hub-ui/src/visualizer/components/reactflow/PatternGraph.tsx | Uses shared node-search context and hides minimap/controls on mobile. |
| calm-hub-ui/src/visualizer/components/reactflow/node-search-context.tsx | Adds context for externally-managed node search state (mobile view menu). |
| calm-hub-ui/src/visualizer/components/reactflow/ArchitectureGraph.tsx | Uses shared node-search context and hides minimap/controls on mobile. |
| calm-hub-ui/src/index.css | Adds slide-in animation utility for mobile push overlays. |
| calm-hub-ui/src/hub/Hub.tsx | Adds mobile explorer overlay + mobile detail sidebar overlay; wires navbar “Explore”. |
| calm-hub-ui/src/hub/Hub.test.tsx | Adds desktop/navbar explorer toggle test and mobile drill-down panel tests. |
| calm-hub-ui/src/hub/components/tree-navigation/TreeNavigation.tsx | Extracts URL/type mapping and loading helpers to shared module. |
| calm-hub-ui/src/hub/components/tree-navigation/navigation-loaders.ts | New shared loader/mapping helpers used by both desktop and mobile nav. |
| calm-hub-ui/src/hub/components/tree-navigation/MobileNavMenu.tsx | New iOS-style drill-down navigation for mobile, including deep-link loading. |
| calm-hub-ui/src/hub/components/tree-navigation/MobileNavMenu.test.tsx | Unit tests for drill-down navigation and deep-link loading behavior. |
| calm-hub-ui/src/hub/components/section-header/SectionHeader.tsx | Improves header wrapping/padding for narrow viewports. |
| calm-hub-ui/src/hub/components/diagram-section/timeline/TimelineBar.tsx | Adds initialExpanded to support mobile timeline sheet without persisting state. |
| calm-hub-ui/src/hub/components/diagram-section/DiagramSection.tsx | Adds mobile view-options menu + navbar-portal actions + full-screen timeline overlay + external node search. |
| calm-hub-ui/src/hub/components/control-detail-section/ControlDetailSection.tsx | Adds mobile tabbed Requirement/Configuration layout; refactors shared UI builders. |
| calm-hub-ui/src/hub/components/control-detail-section/ControlDetailSection.test.tsx | Adds tests for new mobile tabbed control detail layout. |
| calm-hub-ui/src/hooks/useMediaQuery.ts | New responsive breakpoint hook (useIsMobile at Tailwind lg). |
| calm-hub-ui/src/hooks/useMediaQuery.test.ts | Unit tests for media-query hook behavior and updates. |
| calm-hub-ui/src/diff/Diff.css | Adds mobile stacking layout for compare panels. |
| calm-hub-ui/src/diff/components/DiffGraph.tsx | Hides minimap/controls on mobile for diff graphs. |
| calm-hub-ui/src/components/navbar/Navbar.tsx | Reworks navbar layout (center logo, explorer toggle, navbar action portal, hide global search on mobile). |
| calm-hub-ui/src/components/navbar/Navbar.css | Makes logo shrinkable and adds safe-area padding. |
| calm-hub-ui/src/components/navbar/GlobalSearchBar.tsx | Shrinks input on narrow viewports and bounds dropdown width. |
| calm-hub-ui/src/components/navbar/ExplorerSearch.tsx | New explorer-hosted search with debounce, keyboard nav, and grouped inline results. |
| calm-hub-ui/src/components/navbar/ExplorerSearch.test.tsx | Unit tests for explorer search debounce/results/keyboard/error handling. |
| calm-hub-ui/index.html | Enables viewport-fit=cover for iOS safe-area support. |
| calm-hub-ui/AGENTS.md | Documents responsive patterns, useIsMobile usage, and expectations for testing desktop+mobile. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
44
to
46
| <div className="hidden lg:flex"> | ||
| <ul className="menu menu-horizontal px-1"> | ||
| <li> | ||
| <NavLink className="btn-ghost btn text-primary" to="/"> | ||
| Hub | ||
| </NavLink> | ||
| </li> | ||
| <li> | ||
| <NavLink className="btn-ghost btn text-primary" to="/visualizer"> | ||
| Visualizer | ||
| </NavLink> | ||
| </li> | ||
| </ul> | ||
| <GlobalSearchBar /> | ||
| </div> |
| )} | ||
| </div> | ||
| <div className="navbar-center absolute left-1/2 -translate-x-1/2"> | ||
| <a className="btn btn-ghost min-w-0 px-1"> |
Comment on lines
+157
to
+158
| if (!versions || versions.length === 0) throw new Error('No versions found'); | ||
| return String(versions[versions.length - 1]); |
Comment on lines
+205
to
+211
| } else if (e.key === 'Escape') { | ||
| handleClear(); | ||
| } | ||
| }, | ||
| // eslint-disable-next-line react-hooks/exhaustive-deps | ||
| [active, flatResults, selectedIndex, navigateToResult] | ||
| ); |
Comment on lines
+24
to
+30
| const mediaQueryList = window.matchMedia(query); | ||
| const handleChange = () => setMatches(mediaQueryList.matches); | ||
|
|
||
| // Sync immediately in case the query changed between render and effect. | ||
| handleChange(); | ||
| mediaQueryList.addEventListener('change', handleChange); | ||
| return () => mediaQueryList.removeEventListener('change', handleChange); |
Contributor
There was a problem hiding this comment.
The comment refers to Safari < 14, which is 6 years old and unsupported by Apple.
https://developer.mozilla.org/en-US/docs/Web/API/MediaQueryList#browser_compatibility
No need to change, in my opinion
|
|
||
| /* Stack the two comparison graphs vertically on narrow viewports so each one | ||
| * has enough room to be legible instead of being squeezed side by side. */ | ||
| @media (width <= 1023px) { |
Address Copilot review feedback: reorder handleClear above handleKeyDown and add it to the dependency array instead of disabling react-hooks/exhaustive-deps (the project forbids suppressing that rule), and memoise flatResults so the keyboard-handler dependencies stay stable. Signed-off-by: Matthew Bain <matt@rocketstack.co>
Contributor
|
Screenshots look good! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Makes the CALM Hub UI mobile responsive. The Hub, Visualizer, architecture diagram, explorer navigation, timeline, and control-detail views previously assumed a desktop viewport and were largely unusable on a phone. This reworks the layouts so the whole app is touch-friendly on small screens, without changing the desktop experience.
Highlights (all mobile-only unless noted):
useIsMobilebreakpoint hook.Closes #2698
Type of Change
Affected Components
calm-hub-ui/)Testing
npm run lint,npm test(779 tests) andnpm run buildall pass forcalm-hub-ui.Checklist
Screenshots