Storybook UI Testing Plan¶
Reference: How to automate UI tests with GitHub Actions
Hosting Strategy — Single SWA, /storybook/ subdirectory¶
The Storybook static output is co-hosted with MkDocs on the existing Azure Static Web App.
How it works:
- MkDocs builds to
site/— already the SWAapp_location - Storybook builds to
storybook-static/(viabuild-storybook -o storybook-static) - CI copies
storybook-static/→site/storybook/before the SWA deploy step - SWA serves the entire
site/tree;/storybook/is just a subdirectory — no extra infra needed
SWA config changes required (docs/staticwebapp.config.json):
- The current
navigationFallbackrewrites all unmatched paths to/index.html(MkDocs root). Storybook's ownindex.htmlmust be excluded from this fallback, otherwise deep-linking inside Storybook breaks. - Add
/storybook/*to thenavigationFallback.excludelist. - Auth (
/*→ authenticated) already covers/storybook/*— no extra route rules needed.
Result: Storybook reachable at https://docs.sven-relijveld.com/storybook/ (same auth, same SWA, zero extra cost).
Reference: Azure SWA officially recommends separate instances per app, but the subdirectory + copy approach is a well-established pattern for related static assets (docs + design system) served under the same auth boundary. See: https://learn.microsoft.com/en-us/answers/questions/2260777/is-it-possible-to-host-multiple-static-app-in-sing
Phase 1 — Foundation ✅ DONE (PR #390, 2026-05-06)¶
- Storybook 10 +
@storybook/react-vite, Tailwind v4, ThemeProvider wrapper - 8 shadcn/ui primitive stories:
Button,Card,Badge,Input,Dialog,Tabs,Avatar,Alert - 6 portfolio section stories:
Navigation,HeroSection,Footer,ProjectsSection,ExperienceSection,SkillsSection - Vitest browser integration (
@vitest/browser+ Playwright), 45 interaction tests passing - Scripts:
storybook(dev),storybook:build(static output) docs/staticwebapp.config.json:/storybook/*excluded from nav fallback
Phase 2 — CI + Hosting ✅ DONE (PR #390, 2026-05-06)¶
- Chromatic job in
test.yml, wired into gate required check deploy-docs.yml: builds Storybook, copiesstorybook-static/→site/storybook/before SWA deploy- Storybook served at
https://docs.sven-relijveld.com/storybook/
Phase 2.5 — Custom Theming ✅ DONE (2026-05-07, current branch)¶
tokens.ts— design tokens extracted fromindex.css(colors, typography, radius)lightTheme.ts/darkTheme.ts— Storybook UI chrome themed to match sven-relijveld.commanager.ts— applies theme on load based onprefers-color-scheme- OneDNA logo: pink logo for light chrome, white logo for dark chrome
- Canvas toolbar toggle (sun/moon) controls component rendering theme independently
Phase 3 — Missing Component Stories ✅ DONE¶
3a — Portfolio Sections ✅¶
| Component | Status |
|---|---|
about-section.tsx |
✅ Default + DarkMode |
contact-section.tsx |
✅ Default + DarkMode + FilledForm .play() |
awards-section.tsx |
✅ Default + DarkMode |
certifications-section.tsx |
✅ Done |
how-its-made-section.tsx |
✅ Default + DarkMode |
3b — Dialog / Modal Components ✅¶
| Component | Status |
|---|---|
cv-download-dialog.tsx |
✅ DownloadOnly + WithGeneration + DialogOpen .play() feature-flag variants |
admin-modal.tsx |
✅ Closed + Open + OpenAdminPortalButton .play() |
3c — shadcn/ui Primitives (documentation value)¶
Worth adding for design-system docs — these are interactive and have meaningful variants:
| Component | Priority |
|---|---|
accordion.tsx |
MEDIUM |
select.tsx |
MEDIUM |
dropdown-menu.tsx |
MEDIUM |
sheet.tsx |
MEDIUM |
table.tsx |
MEDIUM |
checkbox.tsx |
LOW |
switch.tsx |
LOW |
radio-group.tsx |
LOW |
textarea.tsx |
LOW |
Not worth adding: separator, skeleton, label, aspect-ratio, collapsible, resizable, scroll-area, toaster, toggle, toggle-group, menubar, context-menu, hover-card, input-otp, breadcrumb, command, navigation-menu, popover, progress, slider, pagination
3d — Not worth adding (pages + pure wrappers)¶
- Pages (
home.tsx,about.tsx,projects.tsx, etc.) — better covered by E2E theme-provider.tsx— context only, no UImode-toggle.tsx— thin wrapper around theme context
Deferred / Out of Scope¶
- Separate SWA instance — not needed; subdirectory approach covers the use case at zero extra cost