Для Π½Π°Ρ‡Π°Π»Π° Ρ€Π°Π·Π²Π΅Ρ€Π½Ρ‘ΠΌ ΠΏΡ€ΠΎΠ΅ΠΊΡ‚ Ρ€Π΅Π°ΠΊΡ‚Π° ΠΈ Π΄ΠΎΠ±Π°Π²ΠΈΠΌ Π² Π½Π΅Π³ΠΎ ΠΊΠ½ΠΎΠΏΠΊΡƒ с описаниСм ΠΏΡ€ΠΈΠ½ΠΈΠΌΠ°Π΅ΠΌΡ‹Ρ… Сю пропсов

components > Button > Button.tsx

import React from 'react'
import { ButtonProps } from './Button.props'
import './Button.css';
import cn from 'classnames';
 
export const Button = ({ appearance = 'primary', children, className, ...props }: ButtonProps) => {
    return (
		<button
			className={cn('button', className, {
				['primary']: appearance == 'primary',
				['ghost']: appearance == 'ghost',
			})}
			{...props}
		>
			{children}
		</button>
	);
}

components > Button > Button.props.ts

import { ButtonHTMLAttributes, DetailedHTMLProps, ReactNode } from 'react';
 
export interface ButtonProps
	extends DetailedHTMLProps<ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement> {
	children: ReactNode;
	appearance?: 'primary' | 'ghost';
}

Для установки сторибука понадобится Π² ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π΅ ввСсти Π΄Π°Π½Π½ΡƒΡŽ ΠΊΠΎΠΌΠ°Π½Π΄Ρƒ:

npx sb init

ПослС установки, Ρƒ нас появится скрипт для запуска сторибука

package.json

"scripts": {
	"start": "react-scripts start",
	"build": "react-scripts build",
	"test": "react-scripts test",
	"eject": "react-scripts eject",
	"sb": "storybook dev -p 6006",
	"build-storybook": "storybook build"
},

Π’Π°ΠΊ ΠΆΠ΅ ΡΡ„ΠΎΡ€ΠΌΠΈΡ€ΡƒΡŽΡ‚ΡΡ Π΄Π²Π° Ρ„Π°ΠΉΠ»Π° ΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΠΈ, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ Π±ΡƒΠ΄ΡƒΡ‚ ΠΎΡ‚Π²Π΅Ρ‡Π°Ρ‚ΡŒ Π·Π° Ρ‚ΠΎ, ΠΊΠ°ΠΊΠΈΠ΅ Ρ„Π°ΠΉΠ»Ρ‹ Π±ΡƒΠ΄ΡƒΡ‚ ΠΈ ΠΏΠΎ ΠΊΠ°ΠΊΠΈΠΌ ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Π°ΠΌ ΠΎΠΏΡ€Π΅Π΄Π΅Π»ΡΡ‚ΡŒΡΡ ΠΊΠ°ΠΊ источники историй для сторибука

.storybook > main.ts

import type { StorybookConfig } from "@storybook/react-webpack5";
 
const config: StorybookConfig = {
  stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|ts|tsx)"],
  addons: [
    "@storybook/addon-links",
    "@storybook/addon-essentials",
    "@storybook/preset-create-react-app",
    "@storybook/addon-interactions",
  ],
  framework: {
    name: "@storybook/react-webpack5",
    options: {},
  },
  docs: {
    autodocs: "tag",
  },
  staticDirs: ["..\\public"],
};
 
export default config;

БохраняСт настройки Π»Π΅ΠΉΠ°ΡƒΡ‚Π°

.storybook > preview.ts

import type { Preview } from "@storybook/react";
 
const preview: Preview = {
  parameters: {
    actions: { argTypesRegex: "^on[A-Z].*" },
    controls: {
      matchers: {
        color: /(background|color)$/i,
        date: /Date$/,
      },
    },
  },
};
 
export default preview;

И ΡƒΠΆΠ΅ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ сСйчас Π² ΠΏΠ°ΠΏΠΊΠ΅, Π³Π΄Π΅ Ρƒ нас располагаСтся элСмСнт, ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ Ρ€Π΅Π°Π»ΠΈΠ·ΠΎΠ²Π°Ρ‚ΡŒ страницу сторибука с элСмСнтом

Button.stories.js

import { Button } from "./Button";
 
export default {
    // Π·Π°Π³ΠΎΠ»ΠΎΠ²ΠΎΠΊ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Π° Папка (Π½Π΅ΠΎΠ±ΡΠ·Π°Ρ‚Π΅Π»ΡŒΠ½ΠΎ) - ΠšΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚
    title: 'UI/Button',
    // сам ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚
    component: Button
}
 
// сам ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ Π±ΡƒΠ΄Π΅Ρ‚ Π²Ρ‹Π²ΠΎΠ΄ΠΈΡ‚ΡŒΡΡ Π½Π° экран
export const DefaultButton = () => <Button>Кнопка</Button>

Но Π±ΠΎΠ»Π΅Π΅ ΠΏΡ€Π°Π²ΠΈΠ»ΡŒΠ½Ρ‹ΠΌ Π΄ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ΠΌ элСмСнта Π² сторисы Π±ΡƒΠ΄Π΅Ρ‚ ΡΡ‡ΠΈΡ‚Π°Ρ‚ΡŒΡΡ Π΄ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ Π΅Π³ΠΎ Ρ‡Π΅Ρ€Π΅Π· Ρ‚Π΅ΠΌΠΏΠ»Π΅ΠΉΡ‚, Ρ‡Ρ‚ΠΎ ΠΏΠΎΠ·Π²ΠΎΠ»ΠΈΡ‚ Π½Π°ΠΌ динамичСски ΠΌΠ΅Π½ΡΡ‚ΡŒ Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ΠΎΠ² ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚ΠΎΠ² прямо Π² сторибукС

Button.stories.js

import { Button } from './Button';
 
export default {
	title: 'Button',
	component: Button,
};
 
// создаём шаблон для занСсСния Π² Π½Π΅Π³ΠΎ пропсов
const Template = (arg) => <Button {...arg} />;
 
// дальшС привязываСм контСкст шаблона
export const Default = Template.bind({});
 
// дальшС ΠΏΠ΅Ρ€Π΅Π΄Π°Ρ‘ΠΌ Π΄Π΅Ρ„ΠΎΠ»Ρ‚Π½Ρ‹Π΅ Π°Ρ€Π³ΡƒΠΌΠ΅Π½Ρ‚Ρ‹ Π² ΠΊΠ½ΠΎΠΏΠΊΡƒ
Default.args = {
	children: 'Кнопка',
	appearance: 'primary',
};

И Ρ‚ΡƒΡ‚ ΠΌΡ‹ Π²ΠΈΠ΄ΠΈΠΌ ΠΊΠΎΠ½ΠΊΡ€Π΅Ρ‚Π½Ρ‹Π΅ Ρ‚ΠΈΠΏΡ‹ пропсов, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ Π²Ρ‹Π±Ρ€Π°Ρ‚ΡŒ. Π­Ρ‚ΠΎ Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚ благодаря Ρ‚ΠΎΠΌΡƒ, Ρ‡Ρ‚ΠΎ TS описал интСрфСйс ΠΏΡ€ΠΈΠ½ΠΈΠΌΠ°Π΅ΠΌΡ‹Ρ… пропсов ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚ΠΎΠΌ

Π’Π°ΠΊ ΠΆΠ΅ ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ ΡΠΎΠ·Π΄Π°Ρ‚ΡŒ нСсколько Π²Π°Ρ€ΠΈΠ°Π½Ρ‚ΠΎΠ² Π½Π°ΡˆΠΈΡ… ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚ΠΎΠ², Ρ‡Ρ‚ΠΎΠ±Ρ‹ Π½Π΅ ΠΌΠ΅Π½ΡΡ‚ΡŒ пропсы Π²Ρ€ΡƒΡ‡Π½ΡƒΡŽ

`Button.stories.js

import { Button } from './Button';
 
export default {
	title: 'Button',
	component: Button,
};
 
const Template = (arg) => <Button {...arg} />;
 
export const Primary = Template.bind({});
 
Primary.args = {
	children: 'Кнопка',
	appearance: 'primary',
};
 
export const Ghost = Template.bind({});
 
Ghost.args = {
	children: 'Кнопка',
	appearance: 'ghost',
};

Но! Если ΠΌΡ‹ пишСм ΠΏΡ€ΠΎΠ΅ΠΊΡ‚ Π±Π΅Π· TS, Ρ‚ΠΎ Ρ‚ΠΈΠΏΡ‹ Π²ΠΎΠ·ΠΌΠΎΠΆΠ½Ρ‹Ρ… Π°Ρ€Π³ΡƒΠΌΠ΅Π½Ρ‚ΠΎΠ² ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ ΠΏΡ€ΠΎΠΏΠΈΡΠ°Ρ‚ΡŒ ΡΠ°ΠΌΠΎΡΡ‚ΠΎΡΡ‚Π΅Π»ΡŒΠ½ΠΎ Ρ‡Π΅Ρ€Π΅Π· Π·Π°Π΄Π°Π½ΠΈΠ΅ для ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Π° своих свойств Π²Π½ΡƒΡ‚Ρ€ΠΈ свойства argTypes

import { Button } from './Button';
 
export default {
	title: 'Button',
	component: Button,
	// Ρ‚ΠΈΠΏΡ‹ Π°Ρ€Π³ΡƒΠΌΠ΅Π½Ρ‚ΠΎΠ² ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Π°
	argTypes: {
		// Π°Ρ€Π³ΡƒΠΌΠ΅Π½Ρ‚ оформлСния
		appearance: {
			type: 'string', // Ρ‚ΠΈΠΏ Π°Ρ€Π³ΡƒΠΌΠ΅Π½Ρ‚Π°
			description: 'Π’Π°Ρ€ΠΈΠ°Π½Ρ‚ внСшнСго Π²ΠΈΠ΄Π° ΠΊΠ½ΠΎΠΏΠΊΠΈ', // описаниС
			defaultValue: 'primary', // Π΄Π΅Ρ„ΠΎΠ»Ρ‚Π½ΠΎΠ΅ Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅
			options: ['primary', 'ghost'], // Π²ΠΎΠ·ΠΌΠΎΠΆΠ½Ρ‹Π΅ ΠΎΠΏΡ†ΠΈΠΈ
			control: { // способ ΠΏΠ΅Ρ€Π΅ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΡ ΠΎΠΏΡ†ΠΈΠΉ
				type: 'radio',
			},
		},
		children: {
			type: 'string',
			name: 'label', // имя Π² сторибукС
			defaultValue: 'Кнопка',
		},
	},
};
 
const Template = (arg) => <Button {...arg} />;
 
export const Primary = Template.bind({});
 
Primary.args = {
	children: 'Кнопка',
	appearance: 'primary',
};
 
export const Ghost = Template.bind({});
 
Ghost.args = {
	children: 'Кнопка',
	appearance: 'ghost',
};

Но Ρ‚Π°ΠΊ ΠΆΠ΅ ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ ΡΠ΄Π΅Π»Π°Ρ‚ΡŒ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΡƒ Ρ‚ΠΈΠΏΠΎΠ² с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ PropTypes Π²Π½ΡƒΡ‚Ρ€ΠΈ самого ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Π°, Ρ‡Ρ‚ΠΎ Ρ‚Π°ΠΊ ΠΆΠ΅ ΠΏΠΎΠ·Π²ΠΎΠ»ΠΈΡ‚ Π·Π°ΠΌΠ΅Π½ΠΈΡ‚ΡŒ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΎΠ½Π°Π» интСрфСйса для отобраТСния пропсов Π² сторибукС