Skip to content

Commit eee397e

Browse files
authored
Merge pull request #142 from Boggle-Boggle/feat/137 : 셀렉트 컴포넌트 구현
[Feat/137] 셀렉트 컴포넌트 구현
2 parents 843b9ac + 3b02a48 commit eee397e

File tree

8 files changed

+213
-1
lines changed

8 files changed

+213
-1
lines changed

.storybook/preview.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ const preview: Preview = {
1010
date: /Date$/i,
1111
},
1212
},
13+
options: {
14+
storySort: {
15+
order: ['Components', ['Button', 'Checkbox', 'Radio', 'Switch', 'Icons']],
16+
},
17+
},
1318
},
1419
};
1520

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
type CheckBoxProps = {
2+
size?: 'small' | 'medium';
3+
variant?: 'primary' | 'grey';
4+
checked?: boolean;
5+
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
6+
};
7+
8+
const CheckBox = ({ size = 'medium', variant = 'primary', checked = false, onChange }: CheckBoxProps) => {
9+
const variantClass =
10+
variant === 'primary'
11+
? 'border-neutral-40 peer-checked:bg-primary '
12+
: 'border-neutral-60 peer-checked:bg-neutral-80 ';
13+
const sizeClass = size === 'small' ? 'h-3 w-3 text-body2' : 'h-6 w-6 text-title2';
14+
15+
return (
16+
<label htmlFor="checkbox">
17+
<input id="checkbox" type="checkbox" className="peer sr-only" checked={checked} onChange={onChange} />
18+
<span
19+
className={`flex items-center justify-center rounded-[4px] border-[1.5px] text-neutral-0 peer-checked:border-transparent ${variantClass} ${sizeClass}`}
20+
>
21+
{checked && '✓'}
22+
</span>
23+
</label>
24+
);
25+
};
26+
27+
export default CheckBox;

src/components/refactor/Radio.tsx

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
type RadioProps = {
2+
id: string;
3+
name: string;
4+
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
5+
size?: 'small' | 'medium';
6+
variant?: 'primary' | 'grey';
7+
checked: boolean;
8+
};
9+
10+
const Radio = ({ id, name, checked, onChange, size = 'medium', variant = 'primary' }: RadioProps) => {
11+
const variantInnerClass = variant === 'primary' ? 'bg-primary' : 'bg-neutral-80 ';
12+
const variantOuterClass =
13+
variant === 'primary'
14+
? 'border-neutral-40 peer-checked:border-primary'
15+
: 'border-neutral-80 peer-checked:border-neutral-80';
16+
17+
const sizeInnerClass = size === 'small' ? 'h-[10.7px] w-[10.7px]' : 'h-[15px] w-[15px]';
18+
const sizeOuterClass = size === 'small' ? 'h-4 w-4' : 'h-6 w-6';
19+
20+
return (
21+
<label htmlFor={id}>
22+
<input id={id} name={name} type="radio" checked={checked} onChange={onChange} className="peer sr-only" />
23+
<span
24+
className={`flex items-center justify-center rounded-full border bg-neutral-0 ${sizeOuterClass} ${variantOuterClass} `}
25+
>
26+
{checked && <span className={`block rounded-full ${sizeInnerClass} ${variantInnerClass}`} />}
27+
</span>
28+
</label>
29+
);
30+
};
31+
32+
export default Radio;

src/components/refactor/Switch.tsx

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
type SwitchProps = {
2+
checked?: boolean;
3+
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
4+
};
5+
6+
const Switch = ({ checked = false, onChange }: SwitchProps) => {
7+
return (
8+
<label htmlFor="switch">
9+
<input
10+
id="switch"
11+
type="checkbox"
12+
className="peer sr-only"
13+
checked={checked}
14+
onChange={onChange}
15+
aria-label="스위치"
16+
/>
17+
<span className="relative block h-5 w-9 rounded-[23px] bg-neutral-40 p-[1px] peer-checked:bg-primary">
18+
<span
19+
className={`${checked ? 'left-[1.063rem]' : 'left-[0.063rem]'} absolute size-[1.125rem] rounded-full border-neutral-0 bg-neutral-0 transition-all duration-300`}
20+
/>
21+
</span>
22+
</label>
23+
);
24+
};
25+
26+
export default Switch;
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ const buttonArgs: React.ComponentProps<typeof Button> = {
4141
type: 'submit',
4242
};
4343

44-
export const dafault: Story = {
44+
export const Default: Story = {
4545
args: {
4646
...buttonArgs,
4747
},

src/stories/Checkbox.stories.tsx

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import type { Meta, StoryObj } from '@storybook/react-vite';
2+
3+
import CheckBox from 'components/refactor/CheckBox';
4+
5+
const meta = {
6+
title: 'Components/Checkbox',
7+
component: CheckBox,
8+
tags: ['autodocs'],
9+
argTypes: {
10+
size: {
11+
control: { type: 'radio' },
12+
options: ['small', 'medium'],
13+
},
14+
variant: {
15+
control: { type: 'radio' },
16+
options: ['primary', 'grey'],
17+
},
18+
checked: {
19+
control: 'boolean',
20+
},
21+
onChange: { action: 'changed' },
22+
},
23+
} satisfies Meta<typeof CheckBox>;
24+
25+
export default meta;
26+
type Story = StoryObj<typeof meta>;
27+
28+
export const Default: Story = {
29+
args: {
30+
size: 'small',
31+
variant: 'grey',
32+
checked: true,
33+
onChange: () => {},
34+
},
35+
};

src/stories/Radio.stories.tsx

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/* eslint-disable react/jsx-props-no-spreading */
2+
import type { Meta, StoryObj } from '@storybook/react-vite';
3+
4+
import { useState } from 'react';
5+
6+
import Radio from 'components/refactor/Radio';
7+
8+
const meta: Meta<typeof Radio> = {
9+
title: 'Components/Radio',
10+
component: Radio,
11+
tags: ['autodocs'],
12+
argTypes: {
13+
size: {
14+
control: 'radio',
15+
options: ['small', 'medium'],
16+
},
17+
variant: {
18+
control: 'radio',
19+
options: ['primary', 'grey'],
20+
},
21+
},
22+
};
23+
24+
export default meta;
25+
type Story = StoryObj<typeof Radio>;
26+
27+
const Template: Story['render'] = (args) => {
28+
const [selected, setSelected] = useState('option1');
29+
30+
return (
31+
<div className="flex flex-col">
32+
<Radio
33+
{...args}
34+
id="option1"
35+
name="group"
36+
checked={selected === 'option1'}
37+
onChange={() => setSelected('option1')}
38+
/>
39+
<div className="h-2" />
40+
<Radio
41+
{...args}
42+
id="option2"
43+
name="group"
44+
checked={selected === 'option2'}
45+
onChange={() => setSelected('option2')}
46+
/>
47+
</div>
48+
);
49+
};
50+
51+
export const Default: Story = {
52+
render: Template,
53+
args: {
54+
size: 'medium',
55+
variant: 'primary',
56+
},
57+
};

src/stories/Switch.stories.tsx

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/* eslint-disable react/jsx-props-no-spreading */
2+
import type { Meta, StoryObj } from '@storybook/react-vite';
3+
4+
import { useState } from 'react';
5+
6+
import Switch from 'components/refactor/Switch';
7+
8+
const meta: Meta<typeof Switch> = {
9+
title: 'Components/Switch',
10+
component: Switch,
11+
tags: ['autodocs'],
12+
argTypes: {
13+
checked: {
14+
control: 'boolean',
15+
},
16+
},
17+
};
18+
19+
export default meta;
20+
type Story = StoryObj<typeof Switch>;
21+
22+
const Template: Story['render'] = () => {
23+
const [selected, setSelected] = useState(false);
24+
25+
return <Switch checked={selected} onChange={() => setSelected(!selected)} />;
26+
};
27+
28+
export const Default: Story = {
29+
render: Template,
30+
};

0 commit comments

Comments
 (0)