Skip to content

Commit

Permalink
feat: storybook과 shadcn을 이용하여 button 컴포넌트 생성 (#3)
Browse files Browse the repository at this point in the history
* chore: story 폴더 삭제

* chore: storybook 작업으로 인한 eslint config 수정

* feat: shadcn button with storybook

* chore: 중복 rule 수정
  • Loading branch information
Yejin0O0 authored Sep 19, 2024
1 parent 69c7811 commit 77ae046
Show file tree
Hide file tree
Showing 35 changed files with 390 additions and 947 deletions.
20 changes: 17 additions & 3 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,23 @@
},
"overrides": [
{
// Slice로 끝나는 파일(리덕스 슬라이스)에 대해서만 no-param-reassign 룰을 끔 => 리덕스 슬라이스에서 state를 직접 변경하는 것을 허용
"files": ["src/**/*Slice.ts"],
"rules": { "no-param-reassign": ["error", { "props": false }] }
"files": [
"*.test.ts",
"*.stories.ts",
// shadcn 컴포넌트
"components/ui/**/*.tsx"
],
"rules": {
// import/no-extraneous-dependencies 규칙 비활성화
"import/no-extraneous-dependencies": "off"
}
},
{
"files": ["components/ui/**/*.tsx"],
"rules": {
// shadcn 컴포넌트 파일 jsx-props-no-spreading 규칙 비활성화
"react/jsx-props-no-spreading": "off"
}
}
]
}
5 changes: 1 addition & 4 deletions .storybook/main.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
import type { StorybookConfig } from '@storybook/nextjs';

const config: StorybookConfig = {
stories: [
'../stories/**/*.mdx',
'../stories/**/*.stories.@(js|jsx|mjs|ts|tsx)',
],
stories: ['../**/*.mdx', '../**/*.stories.@(js|jsx|mjs|ts|tsx)'],
addons: [
'@storybook/addon-onboarding',
'@storybook/addon-links',
Expand Down
2 changes: 2 additions & 0 deletions .storybook/preview.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import '../app/globals.css';

import type { Preview } from '@storybook/react';

const preview: Preview = {
Expand Down
8 changes: 4 additions & 4 deletions app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@

:root {
--background: #ffffff;
--foreground: #171717;
--foreground: #007bff;
}

@media (prefers-color-scheme: dark) {
:root {
--background: #0a0a0a;
--background: #007bff;
--foreground: #ededed;
}
}
Expand All @@ -34,7 +34,7 @@ body {
--card-foreground: 240 10% 3.9%;
--popover: 0 0% 100%;
--popover-foreground: 240 10% 3.9%;
--primary: 240 5.9% 10%;
--primary: 211 100% 50%;
--primary-foreground: 0 0% 98%;
--secondary: 240 4.8% 95.9%;
--secondary-foreground: 240 5.9% 10%;
Expand All @@ -61,7 +61,7 @@ body {
--card-foreground: 0 0% 98%;
--popover: 240 10% 3.9%;
--popover-foreground: 0 0% 98%;
--primary: 0 0% 98%;
--primary: 211 100% 50%;
--primary-foreground: 240 5.9% 10%;
--secondary: 240 3.7% 15.9%;
--secondary-foreground: 0 0% 98%;
Expand Down
101 changes: 101 additions & 0 deletions components/ui/button.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import type { Meta, StoryObj } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import { Button } from './button';

const meta: Meta<typeof Button> = {
title: 'Components/ui/button',
component: Button,
tags: ['autodocs'],
parameters: {
layout: 'centered',
},
argTypes: {
variant: {
control: 'select',
description: 'Button variants',
options: [
'default',
'destructive',
'outline',
'secondary',
'ghost',
'link',
],
},
size: {
control: 'select',
description: 'Button sizes',
options: ['default', 'sm', 'lg', 'icon'],
},
},
};

export default meta;

type Story = StoryObj<typeof meta>;

export const Default: Story = {
args: {
variant: 'default',
size: 'default',
disabled: false,
onClick: action('default click'),
children: 'Default button',
className: 'shadow-lg',
},
};

export const Destructive: Story = {
args: {
variant: 'destructive',
size: 'default',
disabled: false,
onClick: action('destructive click'),
children: 'Destructive button',
className: 'shadow-lg',
},
};

export const Outline: Story = {
args: {
variant: 'outline',
size: 'default',
disabled: false,
onClick: action('outline click'),
children: 'Outline button',
className: 'shadow-lg',
},
};

export const Secondary: Story = {
args: {
variant: 'secondary',
size: 'default',
disabled: false,
onClick: action('secondary click'),
children: 'Secondary button',
className: 'shadow-lg',
},
};

export const Ghost: Story = {
args: {
variant: 'ghost',
size: 'default',
disabled: false,
onClick: action('ghost click'),
children: 'Ghost button',
className: 'shadow-lg',
},
};

export const Link: Story = {
args: {
variant: 'link',
size: 'default',
disabled: false,
onClick: action('link click'),
children: 'Link button',
className: 'shadow-lg',
},
};
56 changes: 56 additions & 0 deletions components/ui/button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import * as React from 'react';
import { Slot } from '@radix-ui/react-slot';
import { cva, type VariantProps } from 'class-variance-authority';

import { cn } from '@/lib/utils';

const buttonVariants = cva(
'inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
{
variants: {
variant: {
default: 'bg-primary text-primary-foreground hover:bg-primary/90',
destructive:
'bg-destructive text-destructive-foreground hover:bg-destructive/90',
outline:
'border border-input bg-background hover:bg-primary/90 hover:text-primary-foreground',
secondary:
'bg-secondary text-secondary-foreground hover:bg-secondary/80',
ghost: 'hover:bg-primary/90 hover:text-primary-foreground',
link: 'text-primary underline-offset-4 hover:underline',
},
size: {
default: 'h-10 px-4 py-2',
sm: 'h-9 rounded-md px-3',
lg: 'h-11 rounded-md px-8',
icon: 'h-10 w-10',
},
},
defaultVariants: {
variant: 'default',
size: 'default',
},
},
);

export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
asChild?: boolean;
}

const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : 'button';
return (
<Comp
className={cn(buttonVariants({ variant, size, className }))}
ref={ref}
{...props}
/>
);
},
);
Button.displayName = 'Button';

export { Button, buttonVariants };
Loading

0 comments on commit 77ae046

Please sign in to comment.