refactor(gui-v2): cleanup(a) — co-locate Plan 2's 6 stories per amended §6 (_helpers stays)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
75
apps/app/src/components-v2/layout/AppSidebar.stories.ts
Normal file
75
apps/app/src/components-v2/layout/AppSidebar.stories.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
import type { Meta, StoryObj } from '@storybook/vue3-vite'
|
||||
import { navFixture, orgA, userFixture, withPinia } from '@/stories/v2/_helpers'
|
||||
import AppSidebar from '@/components-v2/layout/AppSidebar.vue'
|
||||
import { useAuthStore } from '@/stores/useAuthStore'
|
||||
import { useShellUiStore } from '@/stores/useShellUiStore'
|
||||
|
||||
/**
|
||||
* AppSidebar composes SidebarHeader + SidebarNav + WorkspaceSwitcher and
|
||||
* reads useShellUiStore (sidebarCollapsed / mobileOpen) plus, transitively
|
||||
* via WorkspaceSwitcher, useAuthStore. Each story seeds both stores on a
|
||||
* fresh Pinia. The desktop <aside> is `hidden lg:flex`, so view at a
|
||||
* viewport ≥ 1024px to see the permanent column.
|
||||
*/
|
||||
const meta: Meta<typeof AppSidebar> = {
|
||||
title: 'v2 Shell/AppSidebar',
|
||||
component: AppSidebar,
|
||||
tags: ['autodocs'],
|
||||
}
|
||||
|
||||
export default meta
|
||||
type Story = StoryObj<typeof AppSidebar>
|
||||
|
||||
export const Expanded: Story = {
|
||||
args: { groups: navFixture },
|
||||
decorators: [
|
||||
withPinia(() => {
|
||||
const auth = useAuthStore()
|
||||
|
||||
auth.user = userFixture
|
||||
auth.organisations = [orgA]
|
||||
|
||||
const shellUi = useShellUiStore()
|
||||
|
||||
shellUi.sidebarCollapsed = false
|
||||
}),
|
||||
],
|
||||
render: args => ({
|
||||
components: { AppSidebar },
|
||||
setup() {
|
||||
return { args }
|
||||
},
|
||||
template: `
|
||||
<div class="flex h-[600px]">
|
||||
<AppSidebar :groups="args.groups" />
|
||||
</div>
|
||||
`,
|
||||
}),
|
||||
}
|
||||
|
||||
export const Collapsed: Story = {
|
||||
args: { groups: navFixture },
|
||||
decorators: [
|
||||
withPinia(() => {
|
||||
const auth = useAuthStore()
|
||||
|
||||
auth.user = userFixture
|
||||
auth.organisations = [orgA]
|
||||
|
||||
const shellUi = useShellUiStore()
|
||||
|
||||
shellUi.sidebarCollapsed = true
|
||||
}),
|
||||
],
|
||||
render: args => ({
|
||||
components: { AppSidebar },
|
||||
setup() {
|
||||
return { args }
|
||||
},
|
||||
template: `
|
||||
<div class="flex h-[600px]">
|
||||
<AppSidebar :groups="args.groups" />
|
||||
</div>
|
||||
`,
|
||||
}),
|
||||
}
|
||||
70
apps/app/src/components-v2/layout/AppTopbar.stories.ts
Normal file
70
apps/app/src/components-v2/layout/AppTopbar.stories.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
import type { Meta, StoryObj } from '@storybook/vue3-vite'
|
||||
import { orgA, userFixture, withPinia } from '@/stories/v2/_helpers'
|
||||
import AppTopbar from '@/components-v2/layout/AppTopbar.vue'
|
||||
import { useAuthStore } from '@/stores/useAuthStore'
|
||||
import { useShellUiStore } from '@/stores/useShellUiStore'
|
||||
|
||||
/**
|
||||
* AppTopbar reads useShellUiStore (theme / density), useAuthStore (user +
|
||||
* currentOrganisation) and useBreadcrumb (route-driven — router is global
|
||||
* in preview.ts). Each story seeds both stores on a fresh Pinia.
|
||||
*/
|
||||
function seedAuth(): void {
|
||||
const auth = useAuthStore()
|
||||
|
||||
auth.user = userFixture
|
||||
auth.organisations = [orgA]
|
||||
}
|
||||
|
||||
const meta: Meta<typeof AppTopbar> = {
|
||||
title: 'v2 Shell/AppTopbar',
|
||||
component: AppTopbar,
|
||||
tags: ['autodocs'],
|
||||
render: () => ({
|
||||
components: { AppTopbar },
|
||||
template: `
|
||||
<div class="min-h-[200px]">
|
||||
<AppTopbar />
|
||||
</div>
|
||||
`,
|
||||
}),
|
||||
}
|
||||
|
||||
export default meta
|
||||
type Story = StoryObj<typeof AppTopbar>
|
||||
|
||||
export const Default: Story = {
|
||||
decorators: [withPinia(seedAuth)],
|
||||
}
|
||||
|
||||
/**
|
||||
* Dark mode is scoped to the story's own subtree via a `.dark` wrapper
|
||||
* (Aura darkModeSelector is the `.dark` class — see plugins/primevue).
|
||||
* Mutating <html> instead would leak into every other story stacked on
|
||||
* the same autodocs page.
|
||||
*/
|
||||
export const DarkTheme: Story = {
|
||||
decorators: [
|
||||
withPinia(() => {
|
||||
seedAuth()
|
||||
useShellUiStore().setTheme('dark')
|
||||
}),
|
||||
],
|
||||
render: () => ({
|
||||
components: { AppTopbar },
|
||||
template: `
|
||||
<div class="dark min-h-[200px] bg-[var(--p-content-background)]">
|
||||
<AppTopbar />
|
||||
</div>
|
||||
`,
|
||||
}),
|
||||
}
|
||||
|
||||
export const CompactDensity: Story = {
|
||||
decorators: [
|
||||
withPinia(() => {
|
||||
seedAuth()
|
||||
useShellUiStore().setDensity('compact')
|
||||
}),
|
||||
],
|
||||
}
|
||||
84
apps/app/src/components-v2/layout/RightDrawer.stories.ts
Normal file
84
apps/app/src/components-v2/layout/RightDrawer.stories.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
import type { Meta, StoryObj } from '@storybook/vue3-vite'
|
||||
import { defineComponent } from 'vue'
|
||||
import { withPinia } from '@/stories/v2/_helpers'
|
||||
import RightDrawer from '@/components-v2/layout/RightDrawer.vue'
|
||||
import { registerDrawerComponent } from '@/composables/drawerRegistry'
|
||||
import { useShellUiStore } from '@/stores/useShellUiStore'
|
||||
|
||||
/**
|
||||
* RightDrawer is fully store-driven via useShellUiStore().drawer +
|
||||
* the drawerRegistry singleton. Each seed registers a demo body under
|
||||
* "SbDemo" (idempotent overwrite — registry is a module singleton) and
|
||||
* optionally seeds openDrawer() so the teleported PrimeVue Drawer is
|
||||
* visible. Chrome keys title/flush are passed in the props object.
|
||||
*/
|
||||
const SbDemoBody = defineComponent({
|
||||
name: 'SbDemoBody',
|
||||
template: '<div class="p-2 text-sm">Demo drawer body</div>',
|
||||
})
|
||||
|
||||
const meta: Meta<typeof RightDrawer> = {
|
||||
title: 'v2 Shell/RightDrawer',
|
||||
component: RightDrawer,
|
||||
tags: ['autodocs'],
|
||||
}
|
||||
|
||||
export default meta
|
||||
type Story = StoryObj<typeof RightDrawer>
|
||||
|
||||
export const Closed: Story = {
|
||||
decorators: [
|
||||
withPinia(() => {
|
||||
registerDrawerComponent('SbDemo', SbDemoBody)
|
||||
}),
|
||||
],
|
||||
render: () => ({
|
||||
components: { RightDrawer },
|
||||
template: `
|
||||
<div class="min-h-[200px]">
|
||||
<p class="text-sm text-[var(--p-text-muted-color)]">Drawer is closed (store seeded but openDrawer not called).</p>
|
||||
<RightDrawer />
|
||||
</div>
|
||||
`,
|
||||
}),
|
||||
}
|
||||
|
||||
export const OpenWithBody: Story = {
|
||||
decorators: [
|
||||
withPinia(() => {
|
||||
registerDrawerComponent('SbDemo', SbDemoBody)
|
||||
|
||||
const shellUi = useShellUiStore()
|
||||
|
||||
shellUi.openDrawer('SbDemo', { title: 'Details' })
|
||||
}),
|
||||
],
|
||||
render: () => ({
|
||||
components: { RightDrawer },
|
||||
template: `
|
||||
<div class="min-h-[200px]">
|
||||
<RightDrawer />
|
||||
</div>
|
||||
`,
|
||||
}),
|
||||
}
|
||||
|
||||
export const Flush: Story = {
|
||||
decorators: [
|
||||
withPinia(() => {
|
||||
registerDrawerComponent('SbDemo', SbDemoBody)
|
||||
|
||||
const shellUi = useShellUiStore()
|
||||
|
||||
shellUi.openDrawer('SbDemo', { title: 'Flush', flush: true })
|
||||
}),
|
||||
],
|
||||
render: () => ({
|
||||
components: { RightDrawer },
|
||||
template: `
|
||||
<div class="min-h-[200px]">
|
||||
<RightDrawer />
|
||||
</div>
|
||||
`,
|
||||
}),
|
||||
}
|
||||
71
apps/app/src/components-v2/layout/SidebarNav.stories.ts
Normal file
71
apps/app/src/components-v2/layout/SidebarNav.stories.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import type { Meta, StoryObj } from '@storybook/vue3-vite'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { navFixture } from '@/stories/v2/_helpers'
|
||||
import SidebarNav from '@/components-v2/layout/SidebarNav.vue'
|
||||
|
||||
/**
|
||||
* SidebarNav needs no Pinia, but vue-router is installed app-wide in
|
||||
* preview.ts (the component uses useRoute + RouterLink). The
|
||||
* WithActiveItem story pushes the `events` route in setup so the
|
||||
* Evenementen item renders in its active state.
|
||||
*/
|
||||
const meta: Meta<typeof SidebarNav> = {
|
||||
title: 'v2 Shell/SidebarNav',
|
||||
component: SidebarNav,
|
||||
tags: ['autodocs'],
|
||||
argTypes: {
|
||||
collapsed: { control: 'boolean' },
|
||||
},
|
||||
}
|
||||
|
||||
export default meta
|
||||
type Story = StoryObj<typeof SidebarNav>
|
||||
|
||||
export const Expanded: Story = {
|
||||
args: { groups: navFixture, collapsed: false },
|
||||
render: args => ({
|
||||
components: { SidebarNav },
|
||||
setup() {
|
||||
return { args }
|
||||
},
|
||||
template: `
|
||||
<div class="w-64 bg-[var(--p-content-background)]">
|
||||
<SidebarNav :groups="args.groups" :collapsed="args.collapsed" />
|
||||
</div>
|
||||
`,
|
||||
}),
|
||||
}
|
||||
|
||||
export const Collapsed: Story = {
|
||||
args: { groups: navFixture, collapsed: true },
|
||||
render: args => ({
|
||||
components: { SidebarNav },
|
||||
setup() {
|
||||
return { args }
|
||||
},
|
||||
template: `
|
||||
<div class="w-16 bg-[var(--p-content-background)]">
|
||||
<SidebarNav :groups="args.groups" :collapsed="args.collapsed" />
|
||||
</div>
|
||||
`,
|
||||
}),
|
||||
}
|
||||
|
||||
export const WithActiveItem: Story = {
|
||||
args: { groups: navFixture, collapsed: false },
|
||||
render: args => ({
|
||||
components: { SidebarNav },
|
||||
setup() {
|
||||
const router = useRouter()
|
||||
|
||||
router.push({ name: 'events' })
|
||||
|
||||
return { args }
|
||||
},
|
||||
template: `
|
||||
<div class="w-64 bg-[var(--p-content-background)]">
|
||||
<SidebarNav :groups="args.groups" :collapsed="args.collapsed" />
|
||||
</div>
|
||||
`,
|
||||
}),
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
import type { Meta, StoryObj } from '@storybook/vue3-vite'
|
||||
import { orgA, orgB, orgC, userFixture, withPinia } from '@/stories/v2/_helpers'
|
||||
import WorkspaceSwitcher from '@/components-v2/layout/WorkspaceSwitcher.vue'
|
||||
import { useAuthStore } from '@/stores/useAuthStore'
|
||||
|
||||
/**
|
||||
* WorkspaceSwitcher reads useAuthStore (organisations + computed
|
||||
* currentOrganisation). Each story gets a fresh Pinia via withPinia and
|
||||
* seeds the auth store by direct assignment after the store is created.
|
||||
*/
|
||||
const meta: Meta<typeof WorkspaceSwitcher> = {
|
||||
title: 'v2 Shell/WorkspaceSwitcher',
|
||||
component: WorkspaceSwitcher,
|
||||
tags: ['autodocs'],
|
||||
argTypes: {
|
||||
collapsed: { control: 'boolean' },
|
||||
},
|
||||
render: args => ({
|
||||
components: { WorkspaceSwitcher },
|
||||
setup() {
|
||||
return { args }
|
||||
},
|
||||
|
||||
// Rail width tracks collapsed so both states read correctly.
|
||||
template: `
|
||||
<div class="bg-[var(--p-content-background)]" :class="args.collapsed ? 'w-16' : 'w-64'">
|
||||
<WorkspaceSwitcher :collapsed="args.collapsed" />
|
||||
</div>
|
||||
`,
|
||||
}),
|
||||
}
|
||||
|
||||
export default meta
|
||||
type Story = StoryObj<typeof WorkspaceSwitcher>
|
||||
|
||||
export const SingleOrg: Story = {
|
||||
decorators: [
|
||||
withPinia(() => {
|
||||
const auth = useAuthStore()
|
||||
|
||||
auth.user = userFixture
|
||||
auth.organisations = [orgA]
|
||||
}),
|
||||
],
|
||||
}
|
||||
|
||||
export const MultiOrg: Story = {
|
||||
decorators: [
|
||||
withPinia(() => {
|
||||
const auth = useAuthStore()
|
||||
|
||||
auth.user = userFixture
|
||||
auth.organisations = [orgA, orgB, orgC]
|
||||
}),
|
||||
],
|
||||
}
|
||||
|
||||
export const Collapsed: Story = {
|
||||
args: { collapsed: true },
|
||||
decorators: [
|
||||
withPinia(() => {
|
||||
const auth = useAuthStore()
|
||||
|
||||
auth.user = userFixture
|
||||
auth.organisations = [orgA, orgB, orgC]
|
||||
}),
|
||||
],
|
||||
}
|
||||
Reference in New Issue
Block a user