fix(ui): secondary sidebar hard-forces the app rail collapsed, preserving the user's pin (PAP-10694)

A secondary sidebar (company settings, plugin routeSidebar) now ALWAYS collapses
the app sidebar to its rail and hides its expand toggle — overriding the user's
pin while the route is active — but the force is ephemeral and never mutates the
persisted expanded/collapsed preference, so leaving the route restores exactly
what the user chose.

Previously the collapse was a soft route-request that the user pin won over, so a
user who pinned the sidebar expanded saw both sidebars (240+240) on settings/
plugin routes and could not get the intended rail.

- SidebarContext: new ephemeral `forceCollapsed` + `collapseLocked`; precedence
  becomes force > user pin > route request > default. `toggleCollapsed` is a
  no-op while forced (won't expand or write the pin).
- Layout: drives `setForceCollapsed(hasSecondarySidebar)` instead of the soft
  `setRouteRequestsCollapsed`.
- Sidebar: hides the expand/collapse affordance while `collapseLocked`.
- Plugin docs (SPEC, authoring guide, SDK README) updated to the hard-force rule.
- Tests: context forced-collapse precedence/lock/restore/mobile; Sidebar hidden
  toggle; Layout mock updated.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip committed 2026-06-09 15:56:14 +00:00
1 parent 9ca8cfd15d
commit 2dee4ba32e
9 files changed
+132 -31

No files matched your search

+1 -1
View File
@@ -240,7 +240,7 @@ Adds a navigation-style entry to the main company sidebar navigation area, rende
A contextual sidebar shown while the current route is a plugin page route with the same `routePath`. Use this for full-page plugin workspaces that need their own local navigation. It does **not** replace the app sidebar: the host collapses the main `<Sidebar/>` to its 64px icon rail (still hover/peek-able) and renders your `routeSidebar` in a secondary pane beside it, producing `[ app rail ][ your sidebar ][ content ]`. Receives `PluginRouteSidebarProps` with `context.companyId` and `context.companyPrefix` set to the active company. Requires the `ui.sidebar.register` capability.
Do **not** mount `RequestCollapsedSidebar` (or otherwise try to collapse the app sidebar) from a `routeSidebar` plugin — the host drives the collapse automatically while your route is active and restores the user's preference when they navigate away. A user's explicit pin (expanded or collapsed) always wins over this route-driven collapse.
Do **not** mount `RequestCollapsedSidebar` (or otherwise try to collapse the app sidebar) from a `routeSidebar` plugin — the host drives the collapse automatically while your route is active and restores the user's preference when they navigate away. The collapse is a hard invariant: a secondary sidebar always forces the app rail collapsed (hiding its expand toggle), overriding any user pin, but it never mutates the user's saved expanded/collapsed preference — that is restored as soon as they leave your route.
#### `sidebarPanel`