add storybook
This commit is contained in:
2
react-advanced-tag1/.gitignore
vendored
2
react-advanced-tag1/.gitignore
vendored
@@ -22,3 +22,5 @@ dist-ssr
|
|||||||
*.njsproj
|
*.njsproj
|
||||||
*.sln
|
*.sln
|
||||||
*.sw?
|
*.sw?
|
||||||
|
|
||||||
|
*storybook.log
|
||||||
|
|||||||
19
react-advanced-tag1/.storybook/main.ts
Normal file
19
react-advanced-tag1/.storybook/main.ts
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import type { StorybookConfig } from '@storybook/react-vite';
|
||||||
|
|
||||||
|
const config: StorybookConfig = {
|
||||||
|
"stories": [
|
||||||
|
"../src/**/*.mdx",
|
||||||
|
"../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"
|
||||||
|
],
|
||||||
|
"addons": [
|
||||||
|
"@storybook/addon-essentials",
|
||||||
|
"@storybook/addon-onboarding",
|
||||||
|
"@chromatic-com/storybook",
|
||||||
|
"@storybook/experimental-addon-test"
|
||||||
|
],
|
||||||
|
"framework": {
|
||||||
|
"name": "@storybook/react-vite",
|
||||||
|
"options": {}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
export default config;
|
||||||
15
react-advanced-tag1/.storybook/preview.ts
Normal file
15
react-advanced-tag1/.storybook/preview.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import type { Preview } from '@storybook/react'
|
||||||
|
|
||||||
|
const preview: Preview = {
|
||||||
|
parameters: {
|
||||||
|
controls: {
|
||||||
|
matchers: {
|
||||||
|
color: /(background|color)$/i,
|
||||||
|
date: /Date$/i,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
tags: ['autodocs'],
|
||||||
|
}
|
||||||
|
|
||||||
|
export default preview
|
||||||
9
react-advanced-tag1/.storybook/vitest.setup.ts
Normal file
9
react-advanced-tag1/.storybook/vitest.setup.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import { beforeAll } from 'vitest';
|
||||||
|
import { setProjectAnnotations } from '@storybook/react';
|
||||||
|
import * as projectAnnotations from './preview';
|
||||||
|
|
||||||
|
// This is an important step to apply the right configuration when testing your stories.
|
||||||
|
// More info at: https://storybook.js.org/docs/api/portable-stories/portable-stories-vitest#setprojectannotations
|
||||||
|
const project = setProjectAnnotations([projectAnnotations]);
|
||||||
|
|
||||||
|
beforeAll(project.beforeAll);
|
||||||
1824
react-advanced-tag1/package-lock.json
generated
1824
react-advanced-tag1/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -14,7 +14,9 @@
|
|||||||
"test:coverage": "vitest --coverage",
|
"test:coverage": "vitest --coverage",
|
||||||
"test:coverage:report": "vitest --coverage --reporter=html",
|
"test:coverage:report": "vitest --coverage --reporter=html",
|
||||||
"test:coverage:report:open": "vitest --coverage --reporter=html && open coverage/index.html",
|
"test:coverage:report:open": "vitest --coverage --reporter=html && open coverage/index.html",
|
||||||
"prettier": "prettier ./src --write"
|
"prettier": "prettier ./src --write",
|
||||||
|
"storybook": "storybook dev -p 6006",
|
||||||
|
"build-storybook": "storybook build"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"immer": "^10.1.1",
|
"immer": "^10.1.1",
|
||||||
@@ -27,22 +29,39 @@
|
|||||||
"zustand": "^5.0.3"
|
"zustand": "^5.0.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@chromatic-com/storybook": "^3.2.6",
|
||||||
"@eslint/js": "^9.21.0",
|
"@eslint/js": "^9.21.0",
|
||||||
|
"@storybook/addon-essentials": "^8.6.12",
|
||||||
|
"@storybook/addon-onboarding": "^8.6.12",
|
||||||
|
"@storybook/blocks": "^8.6.12",
|
||||||
|
"@storybook/experimental-addon-test": "^8.6.12",
|
||||||
|
"@storybook/react": "^8.6.12",
|
||||||
|
"@storybook/react-vite": "^8.6.12",
|
||||||
|
"@storybook/test": "^8.6.12",
|
||||||
"@testing-library/react": "^16.3.0",
|
"@testing-library/react": "^16.3.0",
|
||||||
"@types/react": "^19.0.10",
|
"@types/react": "^19.0.10",
|
||||||
"@types/react-dom": "^19.0.4",
|
"@types/react-dom": "^19.0.4",
|
||||||
"@vitejs/plugin-react": "^4.3.4",
|
"@vitejs/plugin-react": "^4.3.4",
|
||||||
|
"@vitest/browser": "^3.1.1",
|
||||||
"@vitest/coverage-v8": "^3.1.1",
|
"@vitest/coverage-v8": "^3.1.1",
|
||||||
"@vitest/ui": "^3.1.1",
|
"@vitest/ui": "^3.1.1",
|
||||||
"eslint": "^9.21.0",
|
"eslint": "^9.21.0",
|
||||||
"eslint-plugin-react-hooks": "^5.1.0",
|
"eslint-plugin-react-hooks": "^5.1.0",
|
||||||
"eslint-plugin-react-refresh": "^0.4.19",
|
"eslint-plugin-react-refresh": "^0.4.19",
|
||||||
|
"eslint-plugin-storybook": "^0.12.0",
|
||||||
"globals": "^15.15.0",
|
"globals": "^15.15.0",
|
||||||
"jsdom": "^26.0.0",
|
"jsdom": "^26.0.0",
|
||||||
|
"playwright": "^1.51.1",
|
||||||
"prettier": "3.5.3",
|
"prettier": "3.5.3",
|
||||||
|
"storybook": "^8.6.12",
|
||||||
"typescript": "~5.7.2",
|
"typescript": "~5.7.2",
|
||||||
"typescript-eslint": "^8.24.1",
|
"typescript-eslint": "^8.24.1",
|
||||||
"vite": "^6.2.0",
|
"vite": "^6.2.0",
|
||||||
"vitest": "^3.1.1"
|
"vitest": "^3.1.1"
|
||||||
|
},
|
||||||
|
"eslintConfig": {
|
||||||
|
"extends": [
|
||||||
|
"plugin:storybook/recommended"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,17 @@
|
|||||||
import './ComponentWrapper.css'
|
import './ComponentWrapper.css'
|
||||||
|
|
||||||
interface ComponentWrapperProps {
|
interface ComponentWrapperProps {
|
||||||
|
/** Title to show */
|
||||||
title?: string
|
title?: string
|
||||||
|
/** Description to show */
|
||||||
description?: string
|
description?: string
|
||||||
|
/** Content to show */
|
||||||
children: React.ReactNode
|
children: React.ReactNode
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A wrapper component that displays a title, description, and content.
|
||||||
|
*/
|
||||||
export default function ComponentWrapper({
|
export default function ComponentWrapper({
|
||||||
children,
|
children,
|
||||||
title,
|
title,
|
||||||
|
|||||||
41
react-advanced-tag1/src/common/components/globals/Button.tsx
Normal file
41
react-advanced-tag1/src/common/components/globals/Button.tsx
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import { ComponentPropsWithoutRef, CSSProperties } from 'react'
|
||||||
|
|
||||||
|
interface ButtonProps extends ComponentPropsWithoutRef<'button'> {
|
||||||
|
variant: 'primary' | 'secondary'
|
||||||
|
size: 'sm' | 'md' | 'lg'
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Button = ({
|
||||||
|
children,
|
||||||
|
variant,
|
||||||
|
size,
|
||||||
|
...restProps
|
||||||
|
}: ButtonProps) => {
|
||||||
|
const sizeStyles: Record<ButtonProps['size'], CSSProperties> = {
|
||||||
|
sm: {
|
||||||
|
padding: '0.5rem',
|
||||||
|
},
|
||||||
|
md: {
|
||||||
|
padding: '0.75rem',
|
||||||
|
},
|
||||||
|
lg: {
|
||||||
|
padding: '1rem',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
const variantStyles: Record<ButtonProps['variant'], CSSProperties> = {
|
||||||
|
primary: {
|
||||||
|
backgroundColor: 'blue',
|
||||||
|
},
|
||||||
|
secondary: {
|
||||||
|
backgroundColor: 'gray',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
{...restProps}
|
||||||
|
style={{ ...variantStyles[variant], ...sizeStyles[size] }}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ import { useState } from 'react'
|
|||||||
import './MainLayout.css'
|
import './MainLayout.css'
|
||||||
import Footer from '../common/components/Footer'
|
import Footer from '../common/components/Footer'
|
||||||
import Header from '../common/components/Header'
|
import Header from '../common/components/Header'
|
||||||
|
import { Button } from '../common/components/globals/Button'
|
||||||
|
|
||||||
export default function MainLayout({
|
export default function MainLayout({
|
||||||
children,
|
children,
|
||||||
@@ -24,7 +25,9 @@ export default function MainLayout({
|
|||||||
<div className="content">
|
<div className="content">
|
||||||
{children}
|
{children}
|
||||||
|
|
||||||
<button onClick={handleClick}>Click</button>
|
<Button onClick={handleClick} variant="secondary" size="sm">
|
||||||
|
Click
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Footer />
|
<Footer />
|
||||||
|
|||||||
51
react-advanced-tag1/src/stories/Button.stories.tsx
Normal file
51
react-advanced-tag1/src/stories/Button.stories.tsx
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
import { Button } from '../common/components/globals/Button'
|
||||||
|
import { fn } from '@storybook/test'
|
||||||
|
import { Meta, StoryObj } from '@storybook/react'
|
||||||
|
|
||||||
|
const meta: Meta<typeof Button> = {
|
||||||
|
title: 'Elements/Button',
|
||||||
|
component: Button,
|
||||||
|
argTypes: {
|
||||||
|
variant: { control: 'radio', options: ['primary', 'secondary'] },
|
||||||
|
size: { control: 'radio', options: ['sm', 'md', 'lg'] },
|
||||||
|
},
|
||||||
|
args: {
|
||||||
|
onClick: fn(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export default meta
|
||||||
|
|
||||||
|
type Story = StoryObj<typeof Button>
|
||||||
|
|
||||||
|
export const Primary: Story = {
|
||||||
|
args: {
|
||||||
|
children: 'Primary Button',
|
||||||
|
variant: 'primary',
|
||||||
|
size: 'md',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Secondary: Story = {
|
||||||
|
args: {
|
||||||
|
children: 'Secondary Button',
|
||||||
|
variant: 'secondary',
|
||||||
|
size: 'md',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Small: Story = {
|
||||||
|
args: {
|
||||||
|
children: 'Small Button',
|
||||||
|
variant: 'primary',
|
||||||
|
size: 'sm',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Large: Story = {
|
||||||
|
args: {
|
||||||
|
children: 'Large Button',
|
||||||
|
variant: 'primary',
|
||||||
|
size: 'lg',
|
||||||
|
},
|
||||||
|
}
|
||||||
43
react-advanced-tag1/src/stories/ComponentWrapper.stories.tsx
Normal file
43
react-advanced-tag1/src/stories/ComponentWrapper.stories.tsx
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import ComponentWrapper from '../common/components/ComponentWrapper/ComponentWrapper'
|
||||||
|
import { Meta, StoryObj } from '@storybook/react'
|
||||||
|
|
||||||
|
const meta: Meta<typeof ComponentWrapper> = {
|
||||||
|
title: 'Wrapper/ComponentWrapper',
|
||||||
|
component: ComponentWrapper,
|
||||||
|
argTypes: {
|
||||||
|
title: { control: 'text' },
|
||||||
|
description: { control: 'text' },
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export default meta
|
||||||
|
|
||||||
|
type Story = StoryObj<typeof ComponentWrapper>
|
||||||
|
|
||||||
|
export const Default: Story = {
|
||||||
|
args: {
|
||||||
|
title: 'Default Title',
|
||||||
|
description: 'This is a default description.',
|
||||||
|
children: <p>Default content goes here.</p>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const WithoutTitle: Story = {
|
||||||
|
args: {
|
||||||
|
description: 'This wrapper has no title.',
|
||||||
|
children: <p>Content without a title.</p>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const WithoutDescription: Story = {
|
||||||
|
args: {
|
||||||
|
title: 'Title Only',
|
||||||
|
children: <p>Content without a description.</p>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Empty: Story = {
|
||||||
|
args: {
|
||||||
|
children: <p>Empty wrapper with only content.</p>,
|
||||||
|
},
|
||||||
|
}
|
||||||
15
react-advanced-tag1/src/stories/Footer.stories.tsx
Normal file
15
react-advanced-tag1/src/stories/Footer.stories.tsx
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { Meta, StoryObj } from '@storybook/react'
|
||||||
|
import Footer from '../common/components/Footer'
|
||||||
|
|
||||||
|
const meta: Meta<typeof Footer> = {
|
||||||
|
title: 'Common/Footer',
|
||||||
|
component: Footer,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default meta
|
||||||
|
|
||||||
|
type Story = StoryObj<typeof Footer>
|
||||||
|
|
||||||
|
export const Default: Story = {
|
||||||
|
render: () => <Footer />,
|
||||||
|
}
|
||||||
15
react-advanced-tag1/src/stories/Header.stories.tsx
Normal file
15
react-advanced-tag1/src/stories/Header.stories.tsx
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import Header from '../common/components/Header/index'
|
||||||
|
import { Meta, StoryObj } from '@storybook/react'
|
||||||
|
|
||||||
|
const meta: Meta<typeof Header> = {
|
||||||
|
title: 'Common/Header',
|
||||||
|
component: Header,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default meta
|
||||||
|
|
||||||
|
type Story = StoryObj<typeof Header>
|
||||||
|
|
||||||
|
export const Default: Story = {
|
||||||
|
render: () => <Header />,
|
||||||
|
}
|
||||||
1
react-advanced-tag1/vitest.shims.d.ts
vendored
Normal file
1
react-advanced-tag1/vitest.shims.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/// <reference types="@vitest/browser/providers/playwright" />
|
||||||
34
react-advanced-tag1/vitest.workspace.ts
Normal file
34
react-advanced-tag1/vitest.workspace.ts
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import path from 'node:path';
|
||||||
|
import { fileURLToPath } from 'node:url';
|
||||||
|
|
||||||
|
import { defineWorkspace } from 'vitest/config';
|
||||||
|
|
||||||
|
import { storybookTest } from '@storybook/experimental-addon-test/vitest-plugin';
|
||||||
|
|
||||||
|
const dirname =
|
||||||
|
typeof __dirname !== 'undefined'
|
||||||
|
? __dirname
|
||||||
|
: path.dirname(fileURLToPath(import.meta.url));
|
||||||
|
|
||||||
|
// More info at: https://storybook.js.org/docs/writing-tests/test-addon
|
||||||
|
export default defineWorkspace([
|
||||||
|
'vite.config.ts',
|
||||||
|
{
|
||||||
|
extends: 'vite.config.ts',
|
||||||
|
plugins: [
|
||||||
|
// The plugin will run tests for the stories defined in your Storybook config
|
||||||
|
// See options at: https://storybook.js.org/docs/writing-tests/test-addon#storybooktest
|
||||||
|
storybookTest({ configDir: path.join(dirname, '.storybook') }),
|
||||||
|
],
|
||||||
|
test: {
|
||||||
|
name: 'storybook',
|
||||||
|
browser: {
|
||||||
|
enabled: true,
|
||||||
|
headless: true,
|
||||||
|
provider: 'playwright',
|
||||||
|
instances: [{ browser: 'chromium' }],
|
||||||
|
},
|
||||||
|
setupFiles: ['.storybook/vitest.setup.ts'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
Reference in New Issue
Block a user