Skip to content

Commit 6afa18d

Browse files
jinu2IDCopilot
andauthored
Feat/914 add storybook for component documentation (#1138)
* chore: Add Storybook with Next.js Webpack preset (#914) * chore: Configure Storybook preview with global styles (#914) * feat: Add Button component stories for Storybook (#914) * feat: Add form component stories and Tailwind CSS import (#914) * feat: Add card component stories and emotion dep (#914) * feat: Add modal component stories (#914) * feat: Add navigation stories and organize sidebar (#914) * fix: Update story titles for sidebar grouping (#914) * feat: Add Spinner loading state story (#914) * feat: Add Feedback component story for error states (#914) * chore: Remove example Storybook stories (#914) * feat: Add interaction testing for Input, Checkbox, and Modal (#914) * chore: Add comment for Storybook gitignore entries * fix: Exclude Storybook story files from production type-check next build type-checks every file matched by the tsconfig include glob (src/**/*.tsx), which swept in the new .stories.tsx files. They import @storybook/react, whose types are only reachable via its package.json exports map, unresolvable under the repo's moduleResolution: node. The build failed on the first such file. Exclude src/**/*.stories.* so the production build only type-checks shipped code. Story type-safety will be restored separately via a dedicated .storybook/tsconfig.json. Refs: #914 * fix: Align Storybook test packages to v10 @storybook/test (8.6.15) and @storybook/addon-interactions (8.6.14) were pinned to v8 while the rest of the Storybook stack is on 10.3.6. Mixing majors risks resolution and type incompatibilities. In Storybook 10 the test utilities ship from the core storybook/test entrypoint and interaction instrumentation is built into core, so both v8 packages are redundant. Migrate expect/userEvent/within imports from @storybook/test to storybook/test (input, checkbox, modal stories), drop the @storybook/addon-interactions addon from .storybook/main.ts, and remove both v8 packages. build-storybook and play functions verified passing. Addresses a Copilot review comment on PR #1138. * fix: Add Storybook tsconfig and drop preview @ts-ignore The two // @ts-ignore comments on the CSS side-effect imports in .storybook/preview.ts suppressed nothing useful: the *.css module is already declared in src/@types/assets/index.d.ts. The real unresolved import was @storybook/nextjs, failing only because .storybook/ sat outside any tsconfig and fell back to node module resolution. Add .storybook/tsconfig.json (extends root, moduleResolution: bundler, with the ambient declarations in scope) so every .storybook/ import resolves, and remove both @ts-ignore lines. tsc on the new config and build-storybook both pass. Addresses a Copilot review comment on PR #1138. * Apply suggestions from Copilot code review Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
1 parent 6b06177 commit 6afa18d

20 files changed

Lines changed: 5526 additions & 740 deletions

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,10 @@ eslint-report.json
7171
biome-report.json
7272
score-output.md
7373

74+
# Storybook
75+
*storybook.log
76+
storybook-static
77+
7478
# Design handoff source assets (kept out of repo)
7579
/design_handoff_*/
7680
/*.zip

.storybook/main.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import type { StorybookConfig } from '@storybook/nextjs';
2+
3+
const config: StorybookConfig = {
4+
"stories": [
5+
"../src/**/*.mdx",
6+
"../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"
7+
],
8+
"addons": [
9+
"@storybook/addon-a11y",
10+
"@storybook/addon-docs",
11+
"@storybook/addon-onboarding"
12+
],
13+
"framework": "@storybook/nextjs",
14+
"staticDirs": [
15+
"../public"
16+
],
17+
core: {
18+
disableTelemetry: true,
19+
},
20+
};
21+
export default config;

.storybook/preview.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import type { Preview } from "@storybook/nextjs";
2+
import "../src/assets/css/globals.css";
3+
import "../src/assets/css/tailwind.css";
4+
5+
const preview: Preview = {
6+
parameters: {
7+
controls: {
8+
matchers: {
9+
color: /(background|color)$/i,
10+
date: /Date$/i,
11+
},
12+
},
13+
},
14+
};
15+
16+
export default preview;

.storybook/tsconfig.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"extends": "../tsconfig.json",
3+
"compilerOptions": {
4+
"moduleResolution": "bundler"
5+
},
6+
"include": ["../src/@types/**/*", "**/*.ts"]
7+
}

package-lock.json

Lines changed: 4715 additions & 738 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,15 @@
3131
"generate:military-systems": "tsx -r dotenv/config scripts/generate-military-systems.ts",
3232
"generate:career-pathways": "tsx -r dotenv/config scripts/generate-career-pathways.ts",
3333
"generate:tech-pathways": "tsx -r dotenv/config scripts/generate-tech-pathways.ts",
34-
"generate:curriculum-gap-report": "tsx -r dotenv/config scripts/generate-curriculum-gap-report.ts"
34+
"generate:curriculum-gap-report": "tsx -r dotenv/config scripts/generate-curriculum-gap-report.ts",
35+
"storybook": "storybook dev -p 6006",
36+
"build-storybook": "storybook build"
3537
},
3638
"dependencies": {
3739
"@ai-sdk/azure": "^2.0.69",
3840
"@ai-sdk/google": "^2.0.32",
3941
"@ai-sdk/openai": "^2.0.67",
42+
"@emotion/is-prop-valid": "^1.4.0",
4043
"@google-cloud/text-to-speech": "^6.4.0",
4144
"@google/genai": "1.40.0",
4245
"@googlemaps/react-wrapper": "^1.1.35",
@@ -117,6 +120,10 @@
117120
"@commitlint/config-conventional": "^20.4.0",
118121
"@fullhuman/postcss-purgecss": "^4.1.3",
119122
"@playwright/test": "^1.49.0",
123+
"@storybook/addon-a11y": "^10.3.6",
124+
"@storybook/addon-docs": "^10.3.6",
125+
"@storybook/addon-onboarding": "^10.3.6",
126+
"@storybook/nextjs": "^10.3.6",
120127
"@svgr/webpack": "^8.1.0",
121128
"@tailwindcss/typography": "^0.5.2",
122129
"@testing-library/dom": "^10.4.0",
@@ -144,6 +151,7 @@
144151
"postcss": "^8.4.12",
145152
"prisma": "^6.10.1",
146153
"rimraf": "^3.0.2",
154+
"storybook": "^10.3.6",
147155
"tailwindcss": "^3.0.23",
148156
"ts-complex": "^1.0.0",
149157
"ts-node": "^10.9.2",
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import type { Meta, StoryObj } from '@storybook/react';
2+
import Breadcrumb from './index';
3+
4+
const meta: Meta<typeof Breadcrumb> = {
5+
title: 'UI/Navigation/Breadcrumb',
6+
component: Breadcrumb,
7+
tags: ['autodocs'],
8+
argTypes: {
9+
showTitle: {
10+
control: 'boolean',
11+
},
12+
},
13+
};
14+
15+
export default meta;
16+
type Story = StoryObj<typeof Breadcrumb>;
17+
18+
export const Default: Story = {
19+
args: {
20+
pages: [
21+
{ path: '/', label: 'Home' },
22+
{ path: '/programs', label: 'Programs' },
23+
],
24+
currentPage: 'Web Development',
25+
showTitle: true,
26+
},
27+
};
28+
29+
export const WithoutTitle: Story = {
30+
args: {
31+
pages: [
32+
{ path: '/', label: 'Home' },
33+
{ path: '/programs', label: 'Programs' },
34+
],
35+
currentPage: 'Web Development',
36+
showTitle: false,
37+
},
38+
};
39+
40+
export const DeepNesting: Story = {
41+
args: {
42+
pages: [
43+
{ path: '/', label: 'Home' },
44+
{ path: '/programs', label: 'Programs' },
45+
{ path: '/programs/web-development', label: 'Web Development' },
46+
],
47+
currentPage: 'Module 1',
48+
showTitle: true,
49+
title: 'JavaScript Fundamentals',
50+
},
51+
};
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import type { Meta, StoryObj } from '@storybook/react';
2+
import MainMenu from './index';
3+
4+
const sampleMenu = [
5+
{ id: 1, label: 'Home', path: '/' },
6+
{ id: 2, label: 'About', path: '/about' },
7+
{ id: 3, label: 'Programs', path: '/programs', submenu: [
8+
{ id: 31, label: 'Web Development', path: '/programs/web-development' },
9+
{ id: 32, label: 'Cybersecurity', path: '/programs/cybersecurity' },
10+
]},
11+
{ id: 4, label: 'Projects', path: '/projects' },
12+
{ id: 5, label: 'Contact', path: '/contact' },
13+
];
14+
15+
const meta: Meta<typeof MainMenu> = {
16+
title: 'UI/Navigation/MainMenu',
17+
component: MainMenu,
18+
tags: ['autodocs'],
19+
argTypes: {
20+
hoverStyle: {
21+
control: 'select',
22+
options: ['A', 'B'],
23+
},
24+
color: {
25+
control: 'select',
26+
options: ['light', 'dark'],
27+
},
28+
align: {
29+
control: 'select',
30+
options: ['left', 'right', 'center'],
31+
},
32+
},
33+
};
34+
35+
export default meta;
36+
type Story = StoryObj<typeof MainMenu>;
37+
38+
export const Dark: Story = {
39+
args: {
40+
menu: sampleMenu,
41+
color: 'dark',
42+
hoverStyle: 'A',
43+
align: 'left',
44+
},
45+
};
46+
47+
export const Light: Story = {
48+
args: {
49+
menu: sampleMenu,
50+
color: 'light',
51+
hoverStyle: 'A',
52+
align: 'left',
53+
},
54+
parameters: {
55+
backgrounds: { default: 'dark' },
56+
},
57+
decorators: [
58+
(Story) => (
59+
<div style={{ backgroundColor: '#1a1a2e', padding: '1rem' }}>
60+
<Story />
61+
</div>
62+
),
63+
],
64+
};
65+
66+
export const WithHoverStyleB: Story = {
67+
args: {
68+
menu: sampleMenu,
69+
color: 'dark',
70+
hoverStyle: 'B',
71+
align: 'center',
72+
},
73+
};
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import type { Meta, StoryObj } from '@storybook/react';
2+
import ProgramCard from './index';
3+
4+
const meta: Meta<typeof ProgramCard> = {
5+
title: 'UI/Cards/ProgramCard',
6+
component: ProgramCard,
7+
tags: ['autodocs'],
8+
};
9+
10+
export default meta;
11+
type Story = StoryObj<typeof ProgramCard>;
12+
13+
export const Default: Story = {
14+
args: {
15+
program: {
16+
slug: 'web-development',
17+
title: 'Web Development',
18+
description: 'Learn full-stack web development with modern tools and frameworks.',
19+
},
20+
},
21+
};
22+
23+
export const LongDescription: Story = {
24+
args: {
25+
program: {
26+
slug: 'cybersecurity',
27+
title: 'Cybersecurity Operations',
28+
description: 'Master the fundamentals of cybersecurity including network defense, threat analysis, incident response, and security operations for enterprise environments.',
29+
},
30+
},
31+
};
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import type { Meta, StoryObj } from '@storybook/react';
2+
import Button from './index';
3+
4+
const meta: Meta<typeof Button> = {
5+
title: 'UI/Button',
6+
component: Button,
7+
tags: ['autodocs'],
8+
argTypes: {
9+
variant: {
10+
control: 'select',
11+
options: ['contained', 'outlined', 'texted'],
12+
},
13+
color: {
14+
control: 'select',
15+
options: ['primary', 'light'],
16+
},
17+
size: {
18+
control: 'select',
19+
options: ['xs', 'sm', 'md', 'lg'],
20+
},
21+
fullwidth: {
22+
control: 'boolean',
23+
},
24+
disabled: {
25+
control: 'boolean',
26+
},
27+
active: {
28+
control: 'boolean',
29+
},
30+
},
31+
};
32+
33+
export default meta;
34+
type Story = StoryObj<typeof Button>;
35+
36+
export const Primary: Story = {
37+
args: {
38+
children: 'Get Started',
39+
variant: 'contained',
40+
color: 'primary',
41+
size: 'md',
42+
},
43+
};
44+
45+
export const Outlined: Story = {
46+
args: {
47+
children: 'Learn More',
48+
variant: 'outlined',
49+
color: 'primary',
50+
size: 'md',
51+
},
52+
};
53+
54+
export const Light: Story = {
55+
args: {
56+
children: 'Sign Up',
57+
variant: 'contained',
58+
color: 'light',
59+
size: 'md',
60+
},
61+
};
62+
63+
export const Small: Story = {
64+
args: {
65+
children: 'Details',
66+
variant: 'contained',
67+
color: 'primary',
68+
size: 'sm',
69+
},
70+
};
71+
72+
export const Disabled: Story = {
73+
args: {
74+
children: 'Unavailable',
75+
variant: 'contained',
76+
color: 'primary',
77+
size: 'md',
78+
disabled: true,
79+
},
80+
};
81+
82+
export const FullWidth: Story = {
83+
args: {
84+
children: 'Submit Application',
85+
variant: 'contained',
86+
color: 'primary',
87+
size: 'md',
88+
fullwidth: true,
89+
},
90+
};
91+
92+
export const AsLink: Story = {
93+
args: {
94+
children: 'Visit Page',
95+
variant: 'outlined',
96+
color: 'primary',
97+
size: 'md',
98+
path: '/about',
99+
},
100+
};

0 commit comments

Comments
 (0)