Skip to content

Commit f537c63

Browse files
committed
feat: adding storybook support
1 parent 5cb9186 commit f537c63

28 files changed

+721
-10
lines changed
Lines changed: 1 addition & 0 deletions
Loading
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import type { StorybookConfig } from "@storybook/react-vite";
2+
3+
const config: StorybookConfig = {
4+
stories: ["../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"],
5+
addons: [],
6+
framework: {
7+
name: "@storybook/react-vite",
8+
options: {},
9+
},
10+
async viteFinal(config) {
11+
const { default: tailwindcss } = await import("@tailwindcss/vite");
12+
config.plugins = config.plugins || [];
13+
config.plugins.push(tailwindcss());
14+
return config;
15+
},
16+
};
17+
export default config;
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import type { Preview } from "@storybook/react-vite";
2+
import "../src/styles.css";
3+
4+
const preview: Preview = {
5+
parameters: {
6+
controls: {
7+
matchers: {
8+
color: /(background|color)$/i,
9+
date: /Date$/i,
10+
},
11+
},
12+
},
13+
};
14+
15+
export default preview;
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import type { Meta, StoryObj } from "@storybook/react-vite";
2+
import { fn } from "storybook/test";
3+
4+
import { Button } from "./button";
5+
6+
const meta = {
7+
title: "Form/Button",
8+
component: Button,
9+
parameters: {
10+
layout: "centered",
11+
},
12+
tags: ["autodocs"],
13+
args: { onClick: fn() },
14+
} satisfies Meta<typeof Button>;
15+
16+
export default meta;
17+
type Story = StoryObj<typeof meta>;
18+
19+
export const Primary: Story = {
20+
args: {
21+
variant: "primary",
22+
children: "Primary Button",
23+
},
24+
};
25+
26+
export const Secondary: Story = {
27+
args: {
28+
variant: "secondary",
29+
children: "Secondary Button",
30+
},
31+
};
32+
33+
export const Danger: Story = {
34+
args: {
35+
variant: "danger",
36+
children: "Delete Account",
37+
},
38+
};
39+
40+
export const Small: Story = {
41+
args: {
42+
size: "small",
43+
children: "Small Button",
44+
},
45+
};
46+
47+
export const Medium: Story = {
48+
args: {
49+
size: "medium",
50+
children: "Medium Button",
51+
},
52+
};
53+
54+
export const Large: Story = {
55+
args: {
56+
size: "large",
57+
children: "Large Button",
58+
},
59+
};
60+
61+
export const Disabled: Story = {
62+
args: {
63+
variant: "primary",
64+
children: "Disabled Button",
65+
disabled: true,
66+
},
67+
};
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import React from "react";
2+
3+
export interface ButtonProps {
4+
variant?: "primary" | "secondary" | "danger";
5+
size?: "small" | "medium" | "large";
6+
children: React.ReactNode;
7+
onClick?: () => void;
8+
disabled?: boolean;
9+
type?: "button" | "submit" | "reset";
10+
className?: string;
11+
}
12+
13+
export const Button: React.FC<ButtonProps> = ({
14+
variant = "primary",
15+
size = "medium",
16+
children,
17+
onClick,
18+
disabled = false,
19+
type = "button",
20+
className = "",
21+
}) => {
22+
const baseStyles =
23+
"font-medium rounded-lg transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:opacity-50 disabled:cursor-not-allowed";
24+
25+
const variantStyles = {
26+
primary:
27+
"bg-blue-600 text-white hover:bg-blue-700 focus:ring-blue-500 dark:bg-blue-500 dark:hover:bg-blue-600",
28+
secondary:
29+
"bg-gray-200 text-gray-900 hover:bg-gray-300 focus:ring-gray-500 dark:bg-gray-700 dark:text-gray-100 dark:hover:bg-gray-600",
30+
danger:
31+
"bg-red-600 text-white hover:bg-red-700 focus:ring-red-500 dark:bg-red-500 dark:hover:bg-red-600",
32+
};
33+
34+
const sizeStyles = {
35+
small: "px-3 py-1.5 text-sm",
36+
medium: "px-4 py-2 text-base",
37+
large: "px-6 py-3 text-lg",
38+
};
39+
40+
return (
41+
<button
42+
type={type}
43+
onClick={onClick}
44+
disabled={disabled}
45+
className={`${baseStyles} ${variantStyles[variant]} ${sizeStyles[size]} ${className}`}
46+
>
47+
{children}
48+
</button>
49+
);
50+
};
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import type { Meta, StoryObj } from "@storybook/react-vite";
2+
3+
import { Dialog } from "./dialog";
4+
import { Button } from "./button";
5+
6+
const meta = {
7+
title: "Form/Dialog",
8+
component: Dialog,
9+
parameters: {
10+
layout: "centered",
11+
},
12+
tags: ["autodocs"],
13+
} satisfies Meta<typeof Dialog>;
14+
15+
export default meta;
16+
type Story = StoryObj<typeof meta>;
17+
18+
export const Default: Story = {
19+
args: {
20+
title: "User Profile",
21+
children: (
22+
<div className="space-y-4">
23+
<p className="text-gray-700 dark:text-gray-300">
24+
This is a simple dialog component with a title and content area.
25+
</p>
26+
</div>
27+
),
28+
},
29+
};
30+
31+
export const WithFooter: Story = {
32+
args: {
33+
title: "Confirm Action",
34+
children: (
35+
<div className="space-y-4">
36+
<p className="text-gray-700 dark:text-gray-300">
37+
Are you sure you want to proceed with this action?
38+
</p>
39+
</div>
40+
),
41+
footer: (
42+
<div className="flex gap-3 justify-end">
43+
<Button variant="secondary" size="medium">
44+
Cancel
45+
</Button>
46+
<Button variant="primary" size="medium">
47+
Confirm
48+
</Button>
49+
</div>
50+
),
51+
},
52+
};
53+
54+
export const Form: Story = {
55+
args: {
56+
title: "Create Account",
57+
children: (
58+
<div className="space-y-4 min-w-80">
59+
<div className="flex flex-col gap-2">
60+
<label className="text-sm font-medium text-gray-700 dark:text-gray-200">
61+
Email
62+
</label>
63+
<input
64+
type="email"
65+
placeholder="[email protected]"
66+
className="px-4 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100"
67+
/>
68+
</div>
69+
<div className="flex flex-col gap-2">
70+
<label className="text-sm font-medium text-gray-700 dark:text-gray-200">
71+
Password
72+
</label>
73+
<input
74+
type="password"
75+
placeholder="••••••••"
76+
className="px-4 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100"
77+
/>
78+
</div>
79+
</div>
80+
),
81+
footer: (
82+
<div className="flex gap-3 justify-end">
83+
<Button variant="secondary" size="medium">
84+
Cancel
85+
</Button>
86+
<Button variant="primary" size="medium">
87+
Create Account
88+
</Button>
89+
</div>
90+
),
91+
},
92+
};
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import React from "react";
2+
3+
export interface DialogProps {
4+
title: string;
5+
children: React.ReactNode;
6+
footer?: React.ReactNode;
7+
className?: string;
8+
}
9+
10+
export const Dialog: React.FC<DialogProps> = ({
11+
title,
12+
children,
13+
footer,
14+
className = "",
15+
}) => {
16+
return (
17+
<div
18+
className={`bg-white dark:bg-gray-800 rounded-xl shadow-2xl border border-gray-200 dark:border-gray-700 overflow-hidden ${className}`}
19+
>
20+
<div className="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
21+
<h2 className="text-xl font-semibold text-gray-900 dark:text-gray-100">
22+
{title}
23+
</h2>
24+
</div>
25+
<div className="px-6 py-6">{children}</div>
26+
{footer && (
27+
<div className="px-6 py-4 bg-gray-50 dark:bg-gray-900 border-t border-gray-200 dark:border-gray-700">
28+
{footer}
29+
</div>
30+
)}
31+
</div>
32+
);
33+
};
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
export { Input } from "./input";
2+
export type { InputProps } from "./input";
3+
4+
export { RadioGroup } from "./radio-group";
5+
export type { RadioGroupProps, RadioOption } from "./radio-group";
6+
7+
export { Slider } from "./slider";
8+
export type { SliderProps } from "./slider";
9+
10+
export { Dialog } from "./dialog";
11+
export type { DialogProps } from "./dialog";
12+
13+
export { Button } from "./button";
14+
export type { ButtonProps } from "./button";
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import type { Meta, StoryObj } from "@storybook/react-vite";
2+
import { fn } from "storybook/test";
3+
4+
import { Input } from "./input";
5+
6+
const meta = {
7+
title: "Form/Input",
8+
component: Input,
9+
parameters: {
10+
layout: "centered",
11+
},
12+
tags: ["autodocs"],
13+
args: { onChange: fn() },
14+
} satisfies Meta<typeof Input>;
15+
16+
export default meta;
17+
type Story = StoryObj<typeof meta>;
18+
19+
export const Default: Story = {
20+
args: {
21+
label: "Email Address",
22+
id: "email",
23+
placeholder: "Enter your email",
24+
},
25+
};
26+
27+
export const Required: Story = {
28+
args: {
29+
label: "First Name",
30+
id: "firstName",
31+
placeholder: "John",
32+
required: true,
33+
},
34+
};
35+
36+
export const WithValue: Story = {
37+
args: {
38+
label: "Last Name",
39+
id: "lastName",
40+
value: "Doe",
41+
placeholder: "Enter last name",
42+
},
43+
};
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import React from "react";
2+
3+
export interface InputProps {
4+
label: string;
5+
id: string;
6+
value?: string;
7+
onChange?: (value: string) => void;
8+
placeholder?: string;
9+
required?: boolean;
10+
className?: string;
11+
}
12+
13+
export const Input: React.FC<InputProps> = ({
14+
label,
15+
id,
16+
value = "",
17+
onChange,
18+
placeholder,
19+
required = false,
20+
className = "",
21+
}) => {
22+
return (
23+
<div className={`flex flex-col gap-2 ${className}`}>
24+
<label
25+
htmlFor={id}
26+
className="text-sm font-medium text-gray-700 dark:text-gray-200"
27+
>
28+
{label}
29+
{required && <span className="text-red-500 ml-1">*</span>}
30+
</label>
31+
<input
32+
type="text"
33+
id={id}
34+
value={value}
35+
onChange={(e) => onChange?.(e.target.value)}
36+
placeholder={placeholder}
37+
required={required}
38+
className="px-4 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500 dark:focus:ring-blue-400 transition-colors"
39+
/>
40+
</div>
41+
);
42+
};

0 commit comments

Comments
 (0)