Skip to content

feat(calm-hub-ui): mobile responsive layouts for CALM Hub UI#2700

Open
rocketstack-matt wants to merge 16 commits into
finos:mainfrom
rocketstack-matt:claude/calm-hub-ui-mobile-responsive-vrquce
Open

feat(calm-hub-ui): mobile responsive layouts for CALM Hub UI#2700
rocketstack-matt wants to merge 16 commits into
finos:mainfrom
rocketstack-matt:claude/calm-hub-ui-mobile-responsive-vrquce

Conversation

@rocketstack-matt

Copy link
Copy Markdown
Member

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):

  • Responsive Hub and Visualizer layouts driven by a useIsMobile breakpoint hook.
  • Explorer moved into the top navbar with an iOS-style drill-down menu; Hub/Visualizer nav links and the in-canvas zoom controls removed on mobile.
  • Full-bleed architecture render pane; the view-options (Diagram / JSON / Deployments), component search, and timeline all live in a single navbar menu. Timeline is a full-screen view.
  • Menus and detail panels are full-screen push overlays rather than float-overs.
  • Centred CALM logo and device safe-area (notch) handling in the navbar.
  • Control detail view becomes tabbed (Requirement / Configuration) and reformatted for narrow screens.

Closes #2698

Type of Change

  • ✨ New feature (non-breaking change which adds functionality)

Affected Components

  • CALM Hub UI (calm-hub-ui/)

Testing

  • I have tested my changes locally
  • I have added/updated unit tests
  • All existing tests pass

npm run lint, npm test (779 tests) and npm run build all pass for calm-hub-ui.

Checklist

  • My commits follow the conventional commit format
  • I have updated documentation if necessary
  • I have added tests for my changes (if applicable)
  • My changes follow the project's coding standards

Screenshots

Desktop (unchanged) Mobile — diagram Mobile — view menu
Mobile — explorer Mobile — search Mobile — node detail
Mobile — timeline Mobile — compare
Mobile — control requirement Mobile — control configuration

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>
@github-actions github-actions Bot added the calm-hub-ui Affects `calm-hub-ui` label Jun 20, 2026
…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>
@rocketstack-matt rocketstack-matt marked this pull request as ready for review June 20, 2026 16:07
@rocketstack-matt rocketstack-matt requested a review from a team as a code owner June 20, 2026 16:07
Copilot AI review requested due to automatic review settings June 20, 2026 16:07

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 / useIsMobile plus a jsdom matchMedia polyfill 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);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
@markscott-ms

Copy link
Copy Markdown
Contributor

Screenshots look good!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

calm-hub-ui Affects `calm-hub-ui`

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Responsive mobile layout for CALM Hub UI

3 participants