diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 509d831..0df4a51 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -86,6 +86,7 @@ Page-level layout components that define the shell and content structure. Templa - **ListPage** — PageHeader + stat cards + list header with actions + scrollable item list - **FormPage** — PageHeader + optional action bar + optional vertical stepper + constrained-width form content - **DetailPage** — PageHeader + optional action bar (e.g. tabs) + single-column constrained content for viewing records/profiles/documents +- **CenteredPage** — TopBar (optional) + horizontally/vertically centered content, no sidebar. For login, error, onboarding flows Templates have Storybook stories tagged `['autodocs', 'template']` that show realistic "recipe" compositions — full pages built from real components with sample data. These serve as reference implementations for AI coding agents. diff --git a/DESIGN.md b/DESIGN.md index a3ad4b4..ffa84c0 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -1092,6 +1092,26 @@ import { DetailPage } from '@/components/templates/DetailPage' ``` +#### CenteredPage + +Full-page layout with no sidebar and centered content. Use for login, sign-up, error pages, onboarding, or any focused single-task flow. + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `topBar` | `ReactNode` | — | Optional TopBar | +| `maxWidth` | `'sm' \| 'md' \| 'lg' \| 'xl'` | `'md'` | Content max width | +| `children` | `ReactNode` | — | **Required.** Centered content | + +```tsx +import { CenteredPage } from '@/components/templates/CenteredPage' + +} />} maxWidth="md"> + + Login form here + + +``` + --- ## Do's and Don'ts diff --git a/src/components/organisms/SideNav/SideNav.tsx b/src/components/organisms/SideNav/SideNav.tsx index e16d087..dae81b5 100644 --- a/src/components/organisms/SideNav/SideNav.tsx +++ b/src/components/organisms/SideNav/SideNav.tsx @@ -66,8 +66,8 @@ export const SideNav = forwardRef( ref={ref} aria-label="Side navigation" className={cn( - 'flex flex-col bg-nav-bg px-2 py-2 transition-[width] duration-200', - collapsed ? 'w-20 items-center' : 'w-[360px]', + 'flex flex-col overflow-hidden bg-nav-bg px-2 py-2 transition-[width] duration-200', + collapsed ? 'w-20' : 'w-[360px]', className, )} {...props} diff --git a/src/components/templates/CenteredPage/CenteredPage.stories.tsx b/src/components/templates/CenteredPage/CenteredPage.stories.tsx new file mode 100644 index 0000000..bc3465b --- /dev/null +++ b/src/components/templates/CenteredPage/CenteredPage.stories.tsx @@ -0,0 +1,108 @@ +import type { Meta, StoryObj } from '@storybook/react' +import { CenteredPage } from './CenteredPage' +import { TopBar } from '@/components/organisms/TopBar/TopBar' +import { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter } from '@/components/molecules/Card/Card' +import { Input } from '@/components/atoms/Input/Input' +import { Button } from '@/components/atoms/Button/Button' +import { Checkbox } from '@/components/atoms/Checkbox/Checkbox' +import { Alert } from '@/components/molecules/Alert/Alert' +import { NswLogo } from '@/components/templates/_story-helpers' + +const meta: Meta = { + title: 'Templates/CenteredPage', + component: CenteredPage, + tags: ['autodocs', 'template'], + parameters: { + layout: 'fullscreen', + docs: { + description: { + component: 'Full-page layout with no sidebar and horizontally/vertically centered content. Use for login, sign-up, error pages, onboarding, or any focused single-task flow.', + }, + }, + }, +} +export default meta + +type Story = StoryObj + +export const Login: Story = { + name: 'Login page', + render: () => ( + } />} + > + + + Sign in + Enter your credentials to access your account. + + + + + + + + +

+ Don't have an account? Create one +

+
+
+
+ ), +} + +export const ErrorPage: Story = { + name: 'Error page', + render: () => ( + } />} + maxWidth="sm" + > +
+

404

+

Page not found

+

+ The page you're looking for doesn't exist or has been moved. +

+
+ + +
+
+
+ ), +} + +export const Onboarding: Story = { + name: 'Onboarding step', + render: () => ( + } />} + maxWidth="lg" + > + + + Welcome to the platform + Let's set up your workspace. This will only take a minute. + + + + Tell us about your organisation so we can customise your experience. + + + + + +
+ + +
+
+
+
+ ), +} diff --git a/src/components/templates/CenteredPage/CenteredPage.tsx b/src/components/templates/CenteredPage/CenteredPage.tsx new file mode 100644 index 0000000..23fc4cc --- /dev/null +++ b/src/components/templates/CenteredPage/CenteredPage.tsx @@ -0,0 +1,35 @@ +import { forwardRef, type HTMLAttributes, type ReactNode } from 'react' +import { cn } from '@/lib/utils' + +export interface CenteredPageProps extends HTMLAttributes { + /** TopBar component rendered fixed at the top */ + topBar?: ReactNode + /** Horizontally and vertically centered content */ + children: ReactNode + /** Max width of the content area */ + maxWidth?: 'sm' | 'md' | 'lg' | 'xl' +} + +const maxWidthStyles = { + sm: 'max-w-md', + md: 'max-w-xl', + lg: 'max-w-2xl', + xl: 'max-w-4xl', +} + +export const CenteredPage = forwardRef( + ({ topBar, maxWidth = 'md', className, children, ...props }, ref) => { + return ( +
+ {topBar &&
{topBar}
} + +
+
+ {children} +
+
+
+ ) + }, +) +CenteredPage.displayName = 'CenteredPage' diff --git a/src/components/templates/CenteredPage/index.ts b/src/components/templates/CenteredPage/index.ts new file mode 100644 index 0000000..3b6501c --- /dev/null +++ b/src/components/templates/CenteredPage/index.ts @@ -0,0 +1,2 @@ +export { CenteredPage } from './CenteredPage' +export type { CenteredPageProps } from './CenteredPage'