-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: update checkbox component and dependencies
- Add checkbox dependencies - Fix checkbox element positioning
- Loading branch information
1 parent
fba28cc
commit a6a2a04
Showing
14 changed files
with
788 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import type { StorybookConfig } from '@storybook/react-vite'; | ||
|
||
export default { | ||
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'], | ||
addons: [ | ||
'@storybook/addon-onboarding', | ||
'@storybook/addon-links', | ||
'@storybook/addon-essentials', | ||
'@storybook/addon-interactions', | ||
], | ||
framework: { | ||
name: '@storybook/react-vite', | ||
options: {}, | ||
}, | ||
} satisfies StorybookConfig; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import type { Preview } from '@storybook/react'; | ||
import 'sanitize.css'; | ||
|
||
const preview: Preview = { | ||
parameters: { | ||
actions: { argTypesRegex: '^on[A-Z].*' }, | ||
controls: { | ||
matchers: { | ||
color: /(background|color)$/i, | ||
date: /Date$/i, | ||
}, | ||
}, | ||
}, | ||
}; | ||
|
||
export default preview; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
declare module '*.module.css'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
{ | ||
"name": "@sipe-team/checkbox", | ||
"description": "Checkbox for Sipe Design System", | ||
"version": "0.0.0", | ||
"license": "MIT", | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/sipe-team/3-2_side" | ||
}, | ||
"type": "module", | ||
"exports": "./src/index.ts", | ||
"files": ["dist"], | ||
"scripts": { | ||
"build": "tsup", | ||
"build:storybook": "storybook build", | ||
"dev:storybook": "storybook dev -p 6006", | ||
"lint:biome": "pnpm exec biome lint", | ||
"lint:eslint": "pnpm exec eslint --flag unstable_ts_config", | ||
"test": "vitest", | ||
"typecheck": "tsc", | ||
"prepack": "pnpm run build" | ||
}, | ||
"dependencies": { | ||
"@radix-ui/react-checkbox": "^1.0.4", | ||
"@radix-ui/react-slot": "^1.0.2", | ||
"@sipe-team/tokens": "workspace:^", | ||
"@sipe-team/typography": "workspace:^", | ||
"clsx": "^2.1.1", | ||
"lucide-react": "^0.344.0" | ||
}, | ||
"devDependencies": { | ||
"@storybook/addon-essentials": "catalog:", | ||
"@storybook/addon-interactions": "catalog:", | ||
"@storybook/addon-links": "catalog:", | ||
"@storybook/blocks": "catalog:", | ||
"@storybook/react": "catalog:", | ||
"@storybook/react-vite": "catalog:", | ||
"@storybook/test": "catalog:", | ||
"@testing-library/jest-dom": "catalog:", | ||
"@testing-library/react": "catalog:", | ||
"@testing-library/user-event": "^14.5.2", | ||
"@types/react": "^18.3.12", | ||
"happy-dom": "catalog:", | ||
"react": "^18.3.1", | ||
"storybook": "catalog:", | ||
"tsup": "catalog:", | ||
"typescript": "catalog:", | ||
"vitest": "catalog:" | ||
}, | ||
"peerDependencies": { | ||
"react": ">= 18" | ||
}, | ||
"publishConfig": { | ||
"access": "public", | ||
"registry": "https://npm.pkg.github.com", | ||
"exports": { | ||
".": { | ||
"import": { | ||
"types": "./dist/index.d.ts", | ||
"default": "./dist/index.js" | ||
}, | ||
"require": { | ||
"types": "./dist/index.d.cts", | ||
"default": "./dist/index.cjs" | ||
} | ||
}, | ||
"./styles.css": "./dist/index.css" | ||
} | ||
}, | ||
"sideEffects": false | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
.root { | ||
all: unset; | ||
display: flex; | ||
align-items: center; | ||
gap: var(--padding); | ||
} | ||
|
||
.label { | ||
display: flex; | ||
align-items: center; | ||
gap: var(--padding); | ||
margin-left: var(--margin); | ||
} | ||
|
||
.checkbox { | ||
all: unset; | ||
width: var(--size); | ||
height: var(--size); | ||
border-radius: 4px; | ||
border: 2px solid var(--border-color); | ||
background-color: var(--background-color); | ||
display: flex; | ||
align-items: center; | ||
justify-content: center; | ||
cursor: pointer; | ||
} | ||
|
||
.checkbox[data-disabled] { | ||
cursor: not-allowed; | ||
opacity: 0.4; | ||
} | ||
|
||
.checkbox[data-state="checked"] { | ||
border-color: var(--checked-border-color); | ||
background-color: var(--checked-background-color); | ||
} | ||
|
||
.indicator { | ||
color: var(--indicator-color); | ||
height: var(--indicator-size); | ||
width: var(--indicator-size); | ||
display: flex; | ||
align-items: center; | ||
justify-content: center; | ||
line-height: 0; | ||
} | ||
|
||
.group { | ||
display: flex; | ||
flex-direction: column; | ||
gap: 8px; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
import type { Meta, StoryObj } from '@storybook/react'; | ||
import { useState } from 'react'; | ||
import { Checkbox, CheckboxGroup } from './Checkbox'; | ||
|
||
const meta = { | ||
title: 'Components/Checkbox', | ||
component: Checkbox, | ||
parameters: { | ||
layout: 'centered', | ||
}, | ||
argTypes: { | ||
size: { | ||
description: '체크박스의 크기를 지정합니다', | ||
options: ['sm', 'md', 'lg'], | ||
control: { type: 'radio' }, | ||
}, | ||
disabled: { | ||
description: '체크박스의 비활성화 상태를 지정합니다', | ||
control: { type: 'boolean' }, | ||
}, | ||
checked: { | ||
description: '체크박스의 선택 상태를 지정합니다', | ||
control: { type: 'boolean' }, | ||
}, | ||
label: { | ||
description: '체크박스의 레이블을 지정합니다', | ||
control: 'text', | ||
}, | ||
}, | ||
} satisfies Meta<typeof Checkbox>; | ||
|
||
export default meta; | ||
|
||
type Story = StoryObj<typeof meta>; | ||
|
||
export const Basic: Story = { | ||
args: { | ||
label: '체크박스', | ||
size: 'md', | ||
}, | ||
}; | ||
|
||
export const Sizes: Story = { | ||
args: { | ||
label: '체크박스', | ||
}, | ||
render: (args) => ( | ||
<div style={{ display: 'flex', flexDirection: 'column', gap: '16px' }}> | ||
<Checkbox {...args} size="sm" label="Small" /> | ||
<Checkbox {...args} size="md" label="Medium (Default)" /> | ||
<Checkbox {...args} size="lg" label="Large" /> | ||
</div> | ||
), | ||
}; | ||
|
||
export const States: Story = { | ||
args: { | ||
label: '체크박스', | ||
}, | ||
render: (args) => ( | ||
<div style={{ display: 'flex', flexDirection: 'column', gap: '16px' }}> | ||
<Checkbox {...args} label="Unchecked (Default)" /> | ||
<Checkbox {...args} label="Checked" defaultChecked /> | ||
<Checkbox {...args} label="Disabled" disabled /> | ||
<Checkbox {...args} label="Disabled & Checked" disabled defaultChecked /> | ||
</div> | ||
), | ||
}; | ||
|
||
export const Controlled: Story = { | ||
args: { | ||
label: 'Controlled Checkbox', | ||
}, | ||
render: (args) => { | ||
const [checked, setChecked] = useState<boolean | 'indeterminate'>(false); | ||
|
||
return ( | ||
<Checkbox | ||
{...args} | ||
checked={checked} | ||
onCheckedChange={(state) => setChecked(state)} | ||
/> | ||
); | ||
}, | ||
}; | ||
|
||
export const Group: Story = { | ||
args: { | ||
size: 'md', | ||
}, | ||
render: (args) => { | ||
const [selected, setSelected] = useState<string[]>([]); | ||
|
||
return ( | ||
<CheckboxGroup value={selected} onChange={setSelected}> | ||
<Checkbox {...args} value="apple" label="사과" /> | ||
<Checkbox {...args} value="banana" label="바나나" /> | ||
<Checkbox {...args} value="orange" label="오렌지" /> | ||
</CheckboxGroup> | ||
); | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
import { render, screen } from '@testing-library/react'; | ||
import userEvent from '@testing-library/user-event'; | ||
import { expect, test } from 'vitest'; | ||
import { useState } from 'react'; | ||
import { Checkbox, CheckboxGroup } from './Checkbox'; | ||
|
||
test('체크박스를 클릭하면 상태가 변경된다.', async () => { | ||
const user = userEvent.setup(); | ||
render(<Checkbox label="테스트" />); | ||
|
||
const checkbox = screen.getByRole('checkbox', { name: '테스트' }); | ||
expect(checkbox).not.toBeChecked(); | ||
|
||
await user.click(checkbox); | ||
expect(checkbox).toBeChecked(); | ||
}); | ||
|
||
test('disabled 상태의 체크박스는 클릭할 수 없다.', async () => { | ||
const user = userEvent.setup(); | ||
render(<Checkbox disabled label="테스트" />); | ||
|
||
const checkbox = screen.getByRole('checkbox', { name: '테스트' }); | ||
expect(checkbox).toBeDisabled(); | ||
|
||
await user.click(checkbox); | ||
expect(checkbox).not.toBeChecked(); | ||
}); | ||
|
||
test('size를 주입하지 않으면 기본값 md로 설정된다.', () => { | ||
render(<Checkbox label="테스트" />); | ||
|
||
const checkbox = screen.getByRole('checkbox'); | ||
expect(checkbox).toHaveStyle({ width: '20px', height: '20px' }); | ||
}); | ||
|
||
test.each([ | ||
{ size: 'sm', expected: '16px' }, | ||
{ size: 'md', expected: '20px' }, | ||
{ size: 'lg', expected: '24px' }, | ||
] as const)('size가 $size일 때 체크박스 크기가 $expected로 설정된다.', ({ size, expected }) => { | ||
render(<Checkbox size={size} label="테스트" />); | ||
|
||
const checkbox = screen.getByRole('checkbox'); | ||
expect(checkbox).toHaveStyle({ width: expected, height: expected }); | ||
}); | ||
|
||
test('체크박스 그룹에서 여러 항목을 선택할 수 있다.', async () => { | ||
const user = userEvent.setup(); | ||
|
||
const TestComponent = () => { | ||
const [selected, setSelected] = useState<string[]>([]); | ||
return ( | ||
<CheckboxGroup value={selected} onChange={setSelected}> | ||
<Checkbox value="1" label="항목 1" /> | ||
<Checkbox value="2" label="항목 2" /> | ||
<Checkbox value="3" label="항목 3" /> | ||
</CheckboxGroup> | ||
); | ||
}; | ||
|
||
render(<TestComponent />); | ||
|
||
const checkbox1 = screen.getByRole('checkbox', { name: '항목 1' }); | ||
const checkbox2 = screen.getByRole('checkbox', { name: '항목 2' }); | ||
|
||
await user.click(checkbox1); | ||
expect(checkbox1).toBeChecked(); | ||
expect(checkbox2).not.toBeChecked(); | ||
|
||
await user.click(checkbox2); | ||
expect(checkbox1).toBeChecked(); | ||
expect(checkbox2).toBeChecked(); | ||
|
||
await user.click(checkbox1); | ||
expect(checkbox1).not.toBeChecked(); | ||
expect(checkbox2).toBeChecked(); | ||
}); | ||
|
||
test('체크박스 그룹에서 value prop으로 선택된 항목을 제어할 수 있다.', async () => { | ||
const selectedValues = ['1', '3']; | ||
|
||
render( | ||
<CheckboxGroup value={selectedValues}> | ||
<Checkbox value="1" label="항목 1" /> | ||
<Checkbox value="2" label="항목 2" /> | ||
<Checkbox value="3" label="항목 3" /> | ||
</CheckboxGroup> | ||
); | ||
|
||
const checkbox1 = screen.getByRole('checkbox', { name: '항목 1' }); | ||
const checkbox2 = screen.getByRole('checkbox', { name: '항목 2' }); | ||
const checkbox3 = screen.getByRole('checkbox', { name: '항목 3' }); | ||
|
||
expect(checkbox1).toHaveAttribute('data-state', 'checked'); | ||
expect(checkbox2).toHaveAttribute('data-state', 'unchecked'); | ||
expect(checkbox3).toHaveAttribute('data-state', 'checked'); | ||
}); |
Oops, something went wrong.