Add page templates, overhaul DESIGN.md, and fix SideNav text alignment

Introduce AppShell, DashboardPage, ListPage, and FormPage template
components with Storybook recipe stories for AI agent consumption.
Thoroughly update DESIGN.md with all missing components, corrected
token values, and page layout conventions. Fix SideNav button items
defaulting to centered text.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-03 15:11:01 +10:00
parent d915443b8c
commit 95f72407f8
15 changed files with 1469 additions and 113 deletions

View File

@@ -0,0 +1,95 @@
import type { Meta, StoryObj } from '@storybook/react'
import { useState } from 'react'
import { AppShell } from './AppShell'
import { TopBar } from '@/components/organisms/TopBar/TopBar'
import { SideNav, SideNavItem, SideNavGroup, SideNavDivider } from '@/components/organisms/SideNav/SideNav'
import { Avatar } from '@/components/atoms/Avatar/Avatar'
import { IconButton } from '@/components/atoms/IconButton/IconButton'
import { PageHeader } from '@/components/organisms/PageHeader/PageHeader'
import { Menu, Search, Bell, Home, FileText, LayoutGrid, Settings, Users, Link } from 'lucide-react'
const NswLogo = () => (
<div className="flex size-7 items-center justify-center rounded bg-white/20 text-caption font-bold text-white">NSW</div>
)
const meta: Meta<typeof AppShell> = {
title: 'Templates/AppShell',
component: AppShell,
tags: ['autodocs', 'template'],
parameters: {
layout: 'fullscreen',
docs: {
description: {
component: 'Application shell layout that composes TopBar + SideNav + scrollable content area. All page templates should be rendered inside an AppShell.',
},
},
},
}
export default meta
type Story = StoryObj<typeof AppShell>
const SampleTopBar = ({ onMenuClick }: { onMenuClick?: () => void }) => (
<TopBar
title="My Application"
leading={<IconButton icon={<Menu />} aria-label="Toggle menu" variant="tertiary" onClick={onMenuClick} />}
logo={<NswLogo />}
>
<IconButton icon={<Search />} aria-label="Search" variant="tertiary" />
<IconButton icon={<Bell />} aria-label="Notifications" variant="tertiary" />
<Avatar initials="MM" size="sm" />
</TopBar>
)
const SampleSideNav = ({ collapsed }: { collapsed: boolean }) => (
<SideNav collapsed={collapsed}>
<SideNavItem icon={<Home />} active>My status</SideNavItem>
<SideNavItem icon={<FileText />}>My details</SideNavItem>
<SideNavItem icon={<LayoutGrid />}>Workspace</SideNavItem>
<SideNavDivider />
<SideNavGroup icon={<Users />} label="PDP" defaultOpen>
<SideNavItem>My PDP</SideNavItem>
<SideNavItem>PDP guide</SideNavItem>
<SideNavItem>Management</SideNavItem>
</SideNavGroup>
<SideNavItem icon={<Link />}>Resources</SideNavItem>
<SideNavItem icon={<Settings />}>Settings</SideNavItem>
</SideNav>
)
export const Default: Story = {
render: () => {
const [collapsed, setCollapsed] = useState(false)
return (
<AppShell
topBar={<SampleTopBar onMenuClick={() => setCollapsed(!collapsed)} />}
sideNav={<SampleSideNav collapsed={collapsed} />}
sideNavCollapsed={collapsed}
>
<PageHeader title="Dashboard" subtitle="Welcome back, Myra McKay" />
<div className="p-6">
<div className="rounded-lg border border-border bg-surface p-8 text-center text-text-secondary">
Page content goes here
</div>
</div>
</AppShell>
)
},
}
export const Collapsed: Story = {
render: () => (
<AppShell
topBar={<SampleTopBar />}
sideNav={<SampleSideNav collapsed />}
sideNavCollapsed
>
<PageHeader title="Dashboard" subtitle="SideNav collapsed to icon-only mode" />
<div className="p-6">
<div className="rounded-lg border border-border bg-surface p-8 text-center text-text-secondary">
Content area is wider with collapsed sidebar
</div>
</div>
</AppShell>
),
}