add storybook

This commit is contained in:
e560248
2025-04-08 15:47:25 +02:00
parent 456def177a
commit e6f57d3cbc
15 changed files with 2085 additions and 17 deletions

View File

@@ -22,3 +22,5 @@ dist-ssr
*.njsproj
*.sln
*.sw?
*storybook.log

View 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;

View 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

View 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);

File diff suppressed because it is too large Load Diff

View File

@@ -14,7 +14,9 @@
"test:coverage": "vitest --coverage",
"test:coverage:report": "vitest --coverage --reporter=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": {
"immer": "^10.1.1",
@@ -27,22 +29,39 @@
"zustand": "^5.0.3"
},
"devDependencies": {
"@chromatic-com/storybook": "^3.2.6",
"@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",
"@types/react": "^19.0.10",
"@types/react-dom": "^19.0.4",
"@vitejs/plugin-react": "^4.3.4",
"@vitest/browser": "^3.1.1",
"@vitest/coverage-v8": "^3.1.1",
"@vitest/ui": "^3.1.1",
"eslint": "^9.21.0",
"eslint-plugin-react-hooks": "^5.1.0",
"eslint-plugin-react-refresh": "^0.4.19",
"eslint-plugin-storybook": "^0.12.0",
"globals": "^15.15.0",
"jsdom": "^26.0.0",
"playwright": "^1.51.1",
"prettier": "3.5.3",
"storybook": "^8.6.12",
"typescript": "~5.7.2",
"typescript-eslint": "^8.24.1",
"vite": "^6.2.0",
"vitest": "^3.1.1"
},
"eslintConfig": {
"extends": [
"plugin:storybook/recommended"
]
}
}

View File

@@ -1,10 +1,17 @@
import './ComponentWrapper.css'
interface ComponentWrapperProps {
/** Title to show */
title?: string
/** Description to show */
description?: string
/** Content to show */
children: React.ReactNode
}
/**
* A wrapper component that displays a title, description, and content.
*/
export default function ComponentWrapper({
children,
title,

View 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>
)
}

View File

@@ -2,6 +2,7 @@ import { useState } from 'react'
import './MainLayout.css'
import Footer from '../common/components/Footer'
import Header from '../common/components/Header'
import { Button } from '../common/components/globals/Button'
export default function MainLayout({
children,
@@ -24,7 +25,9 @@ export default function MainLayout({
<div className="content">
{children}
<button onClick={handleClick}>Click</button>
<Button onClick={handleClick} variant="secondary" size="sm">
Click
</Button>
</div>
<Footer />

View 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',
},
}

View 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>,
},
}

View 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 />,
}

View 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
View File

@@ -0,0 +1 @@
/// <reference types="@vitest/browser/providers/playwright" />

View 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'],
},
},
]);