feat(layout): Plan 2.5 P4 — WorkspaceSwitcher no-sub + SidebarNav APP_NAVIGATION

Per RFC-WS-PRIMEVUE-PLAN-2-5 §4 AD-2.5-W1 and AD-2.5-B1, §5.4 Fix 4.

Changes:
- WorkspaceSwitcher: sub field removed from template, WorkspaceDisplay
  type, and buildDisplay derivation. Stories did not carry sub args
  (auto-derived from seeded org.role); no WithSub story existed. New
  regression spec (WorkspaceSwitcher.spec.ts) locks the no-sub render.
- SidebarNav: now consumes APP_NAVIGATION from src/config/navigation.ts
  as the single source of truth (shared with breadcrumb derivation in
  useNavBreadcrumb). The groups: V2NavGroup[] prop is removed; render
  walks top-level NavItems (branch nodes render label-heading + children;
  leaf nodes render as rows; items without routeName render as
  non-clickable dormant placeholders). Previous nav data source:
  groups prop fed by useV2Nav(orgNavItems) in OrganizerLayoutV2.
- APP_NAVIGATION expanded with 7 entries to preserve visual sidebar
  continuity (Evenementen at top-level + Beheer branch with 5 children).
  All new entries use routeName: undefined until the corresponding v2
  page lands (TODOs noted per entry); only Dashboard maps to v2-dashboard.
- AppSidebar: groups prop removed; passes only :collapsed to SidebarNav.
- OrganizerLayoutV2: useV2Nav(orgNavItems) plumbing retired; the layout
  now renders <AppSidebar /> with no nav-data wiring.
- Tests: AppSidebar.spec drops the "passes groups prop to SidebarNav"
  assertion; OrganizerLayoutV2.spec drops the "forwards orgNavItems"
  assertion. New WorkspaceSwitcher no-sub regression spec (+2 tests).
- Storybook: SidebarNav.stories and AppSidebar.stories updated to no
  longer thread navFixture/groups; WithActiveItem pushes v2-dashboard.

Position of WorkspaceSwitcher (Fix 3), workspace dropdown panel (Fix 5),
and AppBreadcrumb wiring (Fix 2) remain unchanged in P4 — both lands in
P5. The legacy useBreadcrumb composable also remains untouched until P5
(atomic with AppTopbar refactor).

Orphans flagged for follow-up cleanup (intentionally not deleted in P4):
useV2Nav composable + spec, V2NavGroup/V2NavItem types, sidebarNavActive
helper + spec, navFixture in stories/v2/_helpers.ts.

Suite delta: 575 → 575 (+2 WorkspaceSwitcher no-sub spec, -1 AppSidebar
groups-prop assertion, -1 OrganizerLayoutV2 groups-forward assertion).
vue-tsc clean. Scoped ESLint clean (0 errors).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-20 18:14:31 +02:00
parent d0dd45c03a
commit 864cc558e2
10 changed files with 339 additions and 178 deletions

View File

@@ -7,13 +7,15 @@ import OrganizerLayoutV2 from '@/layouts/OrganizerLayoutV2.vue'
import AppShellV2 from '@/layouts/components/AppShellV2.vue'
// Stub the 3 leaf shell components: this test verifies COMPOSITION
// (right component in right slot + nav data forwarded), not their
// internals (those have their own unit tests). Stubs keep jsdom free
// of PrimeVue teleport/overlay/breakpoint machinery.
// (right component in right slot), not their internals (those have
// their own unit tests). Stubs keep jsdom free of PrimeVue teleport/
// overlay/breakpoint machinery.
//
// Plan 2.5 P4 (AD-2.5-B1): AppSidebar no longer takes a `groups` prop
// — SidebarNav reads APP_NAVIGATION from `@/config/navigation` directly.
const AppSidebarStub = defineComponent({
name: 'AppSidebarStub',
props: { groups: { type: Array, default: () => [] } },
template: '<aside data-testid="sidebar-stub" :data-groups-count="groups.length" />',
template: '<aside data-testid="sidebar-stub" />',
})
const AppTopbarStub = defineComponent({
@@ -59,15 +61,6 @@ describe('OrganizerLayoutV2 (wired shell)', () => {
expect(wrapper.find('[data-testid="page"]').exists()).toBe(true)
})
it('forwards orgNavItems folded into V2NavGroup[] to AppSidebar :groups', async () => {
const wrapper = await mountLayout()
const sidebar = wrapper.find('[data-testid="sidebar-stub"]')
// orgNavItems has link entries + a {heading:'Beheer'} → useV2Nav folds
// into ≥1 group. A non-zero count proves the nav chain is wired.
expect(Number(sidebar.attributes('data-groups-count'))).toBeGreaterThan(0)
})
it('no longer renders the Plan-1 skeleton placeholders', async () => {
const wrapper = await mountLayout()