From a6a2a04f399b0ab4d5a241dd1129fe0c6dc4027d Mon Sep 17 00:00:00 2001 From: JIHOON LEE Date: Mon, 20 Jan 2025 22:57:01 +0900 Subject: [PATCH 01/10] feat: update checkbox component and dependencies - Add checkbox dependencies - Fix checkbox element positioning --- packages/checkbox/.storybook/main.ts | 15 ++ packages/checkbox/.storybook/preview.ts | 16 ++ packages/checkbox/global.d.ts | 1 + packages/checkbox/package.json | 71 ++++++ packages/checkbox/src/Checkbox.module.css | 52 ++++ packages/checkbox/src/Checkbox.stories.tsx | 102 ++++++++ packages/checkbox/src/Checkbox.test.tsx | 97 +++++++ packages/checkbox/src/Checkbox.tsx | 131 ++++++++++ packages/checkbox/src/index.ts | 1 + packages/checkbox/tsconfig.json | 3 + packages/checkbox/tsup.config.ts | 3 + packages/checkbox/vitest.config.ts | 11 + packages/checkbox/vitest.setup.ts | 1 + pnpm-lock.yaml | 284 +++++++++++++++++++++ 14 files changed, 788 insertions(+) create mode 100644 packages/checkbox/.storybook/main.ts create mode 100644 packages/checkbox/.storybook/preview.ts create mode 100644 packages/checkbox/global.d.ts create mode 100644 packages/checkbox/package.json create mode 100644 packages/checkbox/src/Checkbox.module.css create mode 100644 packages/checkbox/src/Checkbox.stories.tsx create mode 100644 packages/checkbox/src/Checkbox.test.tsx create mode 100644 packages/checkbox/src/Checkbox.tsx create mode 100644 packages/checkbox/src/index.ts create mode 100644 packages/checkbox/tsconfig.json create mode 100644 packages/checkbox/tsup.config.ts create mode 100644 packages/checkbox/vitest.config.ts create mode 100644 packages/checkbox/vitest.setup.ts diff --git a/packages/checkbox/.storybook/main.ts b/packages/checkbox/.storybook/main.ts new file mode 100644 index 0000000..2f0a252 --- /dev/null +++ b/packages/checkbox/.storybook/main.ts @@ -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; diff --git a/packages/checkbox/.storybook/preview.ts b/packages/checkbox/.storybook/preview.ts new file mode 100644 index 0000000..70220c4 --- /dev/null +++ b/packages/checkbox/.storybook/preview.ts @@ -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; \ No newline at end of file diff --git a/packages/checkbox/global.d.ts b/packages/checkbox/global.d.ts new file mode 100644 index 0000000..60260a3 --- /dev/null +++ b/packages/checkbox/global.d.ts @@ -0,0 +1 @@ +declare module '*.module.css'; diff --git a/packages/checkbox/package.json b/packages/checkbox/package.json new file mode 100644 index 0000000..26472ea --- /dev/null +++ b/packages/checkbox/package.json @@ -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 +} diff --git a/packages/checkbox/src/Checkbox.module.css b/packages/checkbox/src/Checkbox.module.css new file mode 100644 index 0000000..b98d3fa --- /dev/null +++ b/packages/checkbox/src/Checkbox.module.css @@ -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; +} diff --git a/packages/checkbox/src/Checkbox.stories.tsx b/packages/checkbox/src/Checkbox.stories.tsx new file mode 100644 index 0000000..2262683 --- /dev/null +++ b/packages/checkbox/src/Checkbox.stories.tsx @@ -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; + +export default meta; + +type Story = StoryObj; + +export const Basic: Story = { + args: { + label: '체크박스', + size: 'md', + }, +}; + +export const Sizes: Story = { + args: { + label: '체크박스', + }, + render: (args) => ( +
+ + + +
+ ), +}; + +export const States: Story = { + args: { + label: '체크박스', + }, + render: (args) => ( +
+ + + + +
+ ), +}; + +export const Controlled: Story = { + args: { + label: 'Controlled Checkbox', + }, + render: (args) => { + const [checked, setChecked] = useState(false); + + return ( + setChecked(state)} + /> + ); + }, +}; + +export const Group: Story = { + args: { + size: 'md', + }, + render: (args) => { + const [selected, setSelected] = useState([]); + + return ( + + + + + + ); + }, +}; \ No newline at end of file diff --git a/packages/checkbox/src/Checkbox.test.tsx b/packages/checkbox/src/Checkbox.test.tsx new file mode 100644 index 0000000..fbadfb3 --- /dev/null +++ b/packages/checkbox/src/Checkbox.test.tsx @@ -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(); + + 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(); + + const checkbox = screen.getByRole('checkbox', { name: '테스트' }); + expect(checkbox).toBeDisabled(); + + await user.click(checkbox); + expect(checkbox).not.toBeChecked(); +}); + +test('size를 주입하지 않으면 기본값 md로 설정된다.', () => { + render(); + + 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(); + + const checkbox = screen.getByRole('checkbox'); + expect(checkbox).toHaveStyle({ width: expected, height: expected }); +}); + +test('체크박스 그룹에서 여러 항목을 선택할 수 있다.', async () => { + const user = userEvent.setup(); + + const TestComponent = () => { + const [selected, setSelected] = useState([]); + return ( + + + + + + ); + }; + + render(); + + 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( + + + + + + ); + + 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'); +}); \ No newline at end of file diff --git a/packages/checkbox/src/Checkbox.tsx b/packages/checkbox/src/Checkbox.tsx new file mode 100644 index 0000000..1ffc37f --- /dev/null +++ b/packages/checkbox/src/Checkbox.tsx @@ -0,0 +1,131 @@ +import * as CheckboxPrimitive from '@radix-ui/react-checkbox'; +import { color, fontSize } from '@sipe-team/tokens'; +import { Typography } from '@sipe-team/typography'; +import { Check } from 'lucide-react'; +import { type ComponentPropsWithoutRef, type ElementRef, forwardRef, useId, Children, isValidElement, cloneElement } from 'react'; +import styles from './Checkbox.module.css'; + +export type CheckboxSize = 'sm' | 'md' | 'lg'; + +export interface CheckboxProps + extends Omit, 'asChild'> { + label?: string; + size?: CheckboxSize; +} + +export interface CheckboxGroupProps { + children: React.ReactNode; + defaultValue?: string[]; + name?: string; + onChange?: (value: string[]) => void; + value?: string[]; +} + +const getSizeStyles = (size: CheckboxSize) => { + switch (size) { + case 'sm': + return { + checkboxSize: '16px', + indicatorSize: '12px', + fontSize: fontSize[12], + padding: '4px', + }; + case 'lg': + return { + checkboxSize: '24px', + indicatorSize: '18px', + fontSize: fontSize[16], + padding: '8px', + }; + default: + return { + checkboxSize: '20px', + indicatorSize: '14px', + fontSize: fontSize[14], + padding: '6px', + }; + } +}; + +export const Checkbox = forwardRef< + ElementRef, + CheckboxProps +>(({ className, size = 'md', label, style, id: providedId, ...props }, ref) => { + const generatedId = useId(); + const id = providedId || generatedId; + const { checkboxSize, indicatorSize, fontSize, padding } = getSizeStyles(size); + + return ( +
+ + + + + + {label && ( + + + + )} +
+ ); +}); + +Checkbox.displayName = 'Checkbox'; + +export const CheckboxGroup = forwardRef( + ({ children, name, onChange, value }, ref) => { + const handleCheckedChange = (checked: boolean, itemValue: string | undefined) => { + if (!onChange || !itemValue) return; + + const newValues = checked + ? [...(value || []), itemValue] + : (value || []).filter((v) => v !== itemValue); + + onChange(newValues); + }; + + const mappedChildren = Children.map(children, (child) => { + if (!isValidElement(child)) return child; + + const checked = value?.includes(child.props.value); + + return cloneElement(child, { + checked, + name, + onCheckedChange: (checked: boolean) => handleCheckedChange(checked, child.props.value), + }); + }); + + return ( +
+ {mappedChildren} +
+ ); + } +); + +CheckboxGroup.displayName = 'CheckboxGroup'; diff --git a/packages/checkbox/src/index.ts b/packages/checkbox/src/index.ts new file mode 100644 index 0000000..f5c939f --- /dev/null +++ b/packages/checkbox/src/index.ts @@ -0,0 +1 @@ +export * from './Checkbox'; diff --git a/packages/checkbox/tsconfig.json b/packages/checkbox/tsconfig.json new file mode 100644 index 0000000..4082f16 --- /dev/null +++ b/packages/checkbox/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "../../tsconfig.json" +} diff --git a/packages/checkbox/tsup.config.ts b/packages/checkbox/tsup.config.ts new file mode 100644 index 0000000..ee4b117 --- /dev/null +++ b/packages/checkbox/tsup.config.ts @@ -0,0 +1,3 @@ +import defaultConfig from '../../tsup.config'; + +export default defaultConfig; diff --git a/packages/checkbox/vitest.config.ts b/packages/checkbox/vitest.config.ts new file mode 100644 index 0000000..a917827 --- /dev/null +++ b/packages/checkbox/vitest.config.ts @@ -0,0 +1,11 @@ +import { defineProject, mergeConfig } from 'vitest/config'; +import defaultConfig from '../../vitest.config'; + +export default mergeConfig( + defaultConfig, + defineProject({ + test: { + setupFiles: './vitest.setup.ts', + }, + }), +); diff --git a/packages/checkbox/vitest.setup.ts b/packages/checkbox/vitest.setup.ts new file mode 100644 index 0000000..7b0828b --- /dev/null +++ b/packages/checkbox/vitest.setup.ts @@ -0,0 +1 @@ +import '@testing-library/jest-dom'; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8cc3691..9a6da92 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -551,6 +551,79 @@ importers: specifier: 'catalog:' version: 2.1.8(@types/node@22.10.1)(happy-dom@15.11.7) + packages/checkbox: + dependencies: + '@radix-ui/react-checkbox': + specifier: ^1.0.4 + version: 1.1.3(@types/react-dom@18.3.1)(@types/react@18.3.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': + specifier: ^1.0.2 + version: 1.1.0(@types/react@18.3.13)(react@18.3.1) + '@sipe-team/tokens': + specifier: workspace:^ + version: link:../tokens + '@sipe-team/typography': + specifier: workspace:^ + version: link:../typography + clsx: + specifier: ^2.1.1 + version: 2.1.1 + lucide-react: + specifier: ^0.344.0 + version: 0.344.0(react@18.3.1) + devDependencies: + '@storybook/addon-essentials': + specifier: 'catalog:' + version: 8.4.6(@types/react@18.3.13)(storybook@8.4.7(prettier@2.8.8)) + '@storybook/addon-interactions': + specifier: 'catalog:' + version: 8.4.6(storybook@8.4.7(prettier@2.8.8)) + '@storybook/addon-links': + specifier: 'catalog:' + version: 8.4.6(react@18.3.1)(storybook@8.4.7(prettier@2.8.8)) + '@storybook/blocks': + specifier: 'catalog:' + version: 8.4.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@8.4.7(prettier@2.8.8)) + '@storybook/react': + specifier: 'catalog:' + version: 8.4.6(@storybook/test@8.4.6(storybook@8.4.7(prettier@2.8.8)))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@8.4.7(prettier@2.8.8))(typescript@5.7.2) + '@storybook/react-vite': + specifier: 'catalog:' + version: 8.4.6(@storybook/test@8.4.6(storybook@8.4.7(prettier@2.8.8)))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@4.28.0)(storybook@8.4.7(prettier@2.8.8))(typescript@5.7.2)(vite@5.4.11(@types/node@22.10.1)) + '@storybook/test': + specifier: 'catalog:' + version: 8.4.6(storybook@8.4.7(prettier@2.8.8)) + '@testing-library/jest-dom': + specifier: 'catalog:' + version: 6.6.3 + '@testing-library/react': + specifier: 'catalog:' + version: 16.0.1(@testing-library/dom@10.4.0)(@types/react-dom@18.3.1)(@types/react@18.3.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@testing-library/user-event': + specifier: ^14.5.2 + version: 14.5.2(@testing-library/dom@10.4.0) + '@types/react': + specifier: ^18.3.12 + version: 18.3.13 + happy-dom: + specifier: 'catalog:' + version: 15.11.7 + react: + specifier: ^18.3.1 + version: 18.3.1 + storybook: + specifier: 'catalog:' + version: 8.4.7(prettier@2.8.8) + tsup: + specifier: 'catalog:' + version: 8.3.5(jiti@2.4.1)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.7.2)(yaml@2.6.1) + typescript: + specifier: 'catalog:' + version: 5.7.2 + vitest: + specifier: 'catalog:' + version: 2.1.8(@types/node@22.10.1)(happy-dom@15.11.7) + packages/divider: dependencies: clsx: @@ -1938,6 +2011,22 @@ packages: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} + '@radix-ui/primitive@1.1.1': + resolution: {integrity: sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA==} + + '@radix-ui/react-checkbox@1.1.3': + resolution: {integrity: sha512-HD7/ocp8f1B3e6OHygH0n7ZKjONkhciy1Nh0yuBgObqThc3oyx+vuMfFHKAknXRHHWVE9XvXStxJFyjUmB8PIw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-compose-refs@1.1.0': resolution: {integrity: sha512-b4inOtiaOnYf9KWyO3jAeeCG6FeyfY6ldiEPanbUjWd+xIk5wZeHa8yVwmrJ2vderhu/BQvzCrJI0lHd+wIiqw==} peerDependencies: @@ -1947,6 +2036,50 @@ packages: '@types/react': optional: true + '@radix-ui/react-compose-refs@1.1.1': + resolution: {integrity: sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-context@1.1.1': + resolution: {integrity: sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-presence@1.1.2': + resolution: {integrity: sha512-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-primitive@2.0.1': + resolution: {integrity: sha512-sHCWTtxwNn3L3fH8qAfnF3WbUZycW93SM1j3NFDzXBiz8D6F5UTTy8G1+WFEaiCdvCVRJWj6N2R4Xq6HdiHmDg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-slot@1.1.0': resolution: {integrity: sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==} peerDependencies: @@ -1956,6 +2089,60 @@ packages: '@types/react': optional: true + '@radix-ui/react-slot@1.1.1': + resolution: {integrity: sha512-RApLLOcINYJA+dMVbOju7MYv1Mb2EBp2nH4HdDzXTSyaR5optlm6Otrz1euW3HbdOR8UmmFK06TD+A9frYWv+g==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-callback-ref@1.1.0': + resolution: {integrity: sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-controllable-state@1.1.0': + resolution: {integrity: sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-layout-effect@1.1.0': + resolution: {integrity: sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-previous@1.1.0': + resolution: {integrity: sha512-Z/e78qg2YFnnXcW88A4JmTtm4ADckLno6F7OXotmkQfeuCVaKuYzqAATPhVzl3delXE7CxIV8shofPn3jPc5Og==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-size@1.1.0': + resolution: {integrity: sha512-XW3/vWuIXHa+2Uwcc2ABSfcCledmXhhQPlGbfcRXbiUQI5Icjcg19BGCZVKKInYbvUCut/ufbbLLPFC5cbb1hw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@rollup/pluginutils@5.1.3': resolution: {integrity: sha512-Pnsb6f32CD2W3uCaLZIzDmeFyQ2b8UWMFI7xtwUezpcGBDVDW6y9XgAWIlARiGAo6eNF5FK5aQTr0LFyNyqq5A==} engines: {node: '>=14.0.0'} @@ -3866,6 +4053,11 @@ packages: lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + lucide-react@0.344.0: + resolution: {integrity: sha512-6YyBnn91GB45VuVT96bYCOKElbJzUHqp65vX8cDcu55MQL9T969v4dhGClpljamuI/+KMO9P6w9Acq1CVQGvIQ==} + peerDependencies: + react: ^16.5.1 || ^17.0.0 || ^18.0.0 + lz-string@1.5.0: resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} hasBin: true @@ -5737,12 +5929,61 @@ snapshots: '@pkgjs/parseargs@0.11.0': optional: true + '@radix-ui/primitive@1.1.1': {} + + '@radix-ui/react-checkbox@1.1.3(@types/react-dom@18.3.1)(@types/react@18.3.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.1 + '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.13)(react@18.3.1) + '@radix-ui/react-context': 1.1.1(@types/react@18.3.13)(react@18.3.1) + '@radix-ui/react-presence': 1.1.2(@types/react-dom@18.3.1)(@types/react@18.3.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.0.1(@types/react-dom@18.3.1)(@types/react@18.3.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.13)(react@18.3.1) + '@radix-ui/react-use-previous': 1.1.0(@types/react@18.3.13)(react@18.3.1) + '@radix-ui/react-use-size': 1.1.0(@types/react@18.3.13)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.13 + '@types/react-dom': 18.3.1 + '@radix-ui/react-compose-refs@1.1.0(@types/react@18.3.13)(react@18.3.1)': dependencies: react: 18.3.1 optionalDependencies: '@types/react': 18.3.13 + '@radix-ui/react-compose-refs@1.1.1(@types/react@18.3.13)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.13 + + '@radix-ui/react-context@1.1.1(@types/react@18.3.13)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.13 + + '@radix-ui/react-presence@1.1.2(@types/react-dom@18.3.1)(@types/react@18.3.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.13)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.13)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.13 + '@types/react-dom': 18.3.1 + + '@radix-ui/react-primitive@2.0.1(@types/react-dom@18.3.1)(@types/react@18.3.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-slot': 1.1.1(@types/react@18.3.13)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.13 + '@types/react-dom': 18.3.1 + '@radix-ui/react-slot@1.1.0(@types/react@18.3.13)(react@18.3.1)': dependencies: '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.13)(react@18.3.1) @@ -5750,6 +5991,45 @@ snapshots: optionalDependencies: '@types/react': 18.3.13 + '@radix-ui/react-slot@1.1.1(@types/react@18.3.13)(react@18.3.1)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.13)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.13 + + '@radix-ui/react-use-callback-ref@1.1.0(@types/react@18.3.13)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.13 + + '@radix-ui/react-use-controllable-state@1.1.0(@types/react@18.3.13)(react@18.3.1)': + dependencies: + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.13)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.13 + + '@radix-ui/react-use-layout-effect@1.1.0(@types/react@18.3.13)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.13 + + '@radix-ui/react-use-previous@1.1.0(@types/react@18.3.13)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.13 + + '@radix-ui/react-use-size@1.1.0(@types/react@18.3.13)(react@18.3.1)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.13)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.13 + '@rollup/pluginutils@5.1.3(rollup@4.28.0)': dependencies: '@types/estree': 1.0.6 @@ -7988,6 +8268,10 @@ snapshots: dependencies: yallist: 3.1.1 + lucide-react@0.344.0(react@18.3.1): + dependencies: + react: 18.3.1 + lz-string@1.5.0: {} magic-string@0.27.0: From 5fda386a8c764b2a09fc23e229773f1a4d92bbbc Mon Sep 17 00:00:00 2001 From: JIHOON LEE Date: Tue, 21 Jan 2025 15:45:31 +0900 Subject: [PATCH 02/10] refactor: improve type safety for Checkbox component - Ensure compatibility with Radix UI's CheckedState type - Enhance type safety with exactOptionalPropertyTypes option --- packages/checkbox/src/Checkbox.tsx | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/packages/checkbox/src/Checkbox.tsx b/packages/checkbox/src/Checkbox.tsx index 1ffc37f..ca86695 100644 --- a/packages/checkbox/src/Checkbox.tsx +++ b/packages/checkbox/src/Checkbox.tsx @@ -11,6 +11,7 @@ export interface CheckboxProps extends Omit, 'asChild'> { label?: string; size?: CheckboxSize; + value?: string; } export interface CheckboxGroupProps { @@ -98,8 +99,8 @@ Checkbox.displayName = 'Checkbox'; export const CheckboxGroup = forwardRef( ({ children, name, onChange, value }, ref) => { - const handleCheckedChange = (checked: boolean, itemValue: string | undefined) => { - if (!onChange || !itemValue) return; + const handleCheckedChange = (checked: CheckboxPrimitive.CheckedState, itemValue: string | undefined) => { + if (!onChange || !itemValue || typeof checked !== 'boolean') return; const newValues = checked ? [...(value || []), itemValue] @@ -109,15 +110,18 @@ export const CheckboxGroup = forwardRef( }; const mappedChildren = Children.map(children, (child) => { - if (!isValidElement(child)) return child; + if (!isValidElement(child)) return child; - const checked = value?.includes(child.props.value); + const checked = value?.includes(child.props.value || '') || false; return cloneElement(child, { checked, name, - onCheckedChange: (checked: boolean) => handleCheckedChange(checked, child.props.value), - }); + onCheckedChange: (state: CheckboxPrimitive.CheckedState) => { + handleCheckedChange(state, child.props.value); + child.props.onCheckedChange?.(state); + }, + } as Partial); }); return ( @@ -128,4 +132,4 @@ export const CheckboxGroup = forwardRef( } ); -CheckboxGroup.displayName = 'CheckboxGroup'; +CheckboxGroup.displayName = 'CheckboxGroup'; \ No newline at end of file From 3f35e13c74750efa74b18fc88c959ac6b1370291 Mon Sep 17 00:00:00 2001 From: JIHOON LEE Date: Wed, 22 Jan 2025 14:31:17 +0900 Subject: [PATCH 03/10] feat: remove react-slot --- packages/checkbox/package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/checkbox/package.json b/packages/checkbox/package.json index 26472ea..f148c33 100644 --- a/packages/checkbox/package.json +++ b/packages/checkbox/package.json @@ -22,7 +22,6 @@ }, "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", From 35f9f4cc6cc09f294f653990857563d1279602b9 Mon Sep 17 00:00:00 2001 From: JIHOON LEE Date: Wed, 22 Jan 2025 14:42:30 +0900 Subject: [PATCH 04/10] fix: add focus style to checkbox component --- packages/checkbox/src/Checkbox.module.css | 5 +++++ packages/checkbox/src/Checkbox.tsx | 1 + 2 files changed, 6 insertions(+) diff --git a/packages/checkbox/src/Checkbox.module.css b/packages/checkbox/src/Checkbox.module.css index b98d3fa..ac1d5bf 100644 --- a/packages/checkbox/src/Checkbox.module.css +++ b/packages/checkbox/src/Checkbox.module.css @@ -25,6 +25,11 @@ cursor: pointer; } +.checkbox:focus-visible { + border-color: var(--focus-border-color); + outline: none; +} + .checkbox[data-disabled] { cursor: not-allowed; opacity: 0.4; diff --git a/packages/checkbox/src/Checkbox.tsx b/packages/checkbox/src/Checkbox.tsx index ca86695..8652edf 100644 --- a/packages/checkbox/src/Checkbox.tsx +++ b/packages/checkbox/src/Checkbox.tsx @@ -70,6 +70,7 @@ export const Checkbox = forwardRef< '--checked-border-color': color.cyan300, '--checked-background-color': color.cyan300, '--indicator-color': color.white, + '--focus-border-color': color.cyan500, ...style, } as React.CSSProperties } From 78c4bf2b27f0dda1c02b805894f384af01228b7d Mon Sep 17 00:00:00 2001 From: JIHOON LEE Date: Wed, 22 Jan 2025 14:49:02 +0900 Subject: [PATCH 05/10] fix: update module css --- packages/checkbox/src/Checkbox.module.css | 2 +- packages/checkbox/src/Checkbox.tsx | 1 + pnpm-lock.yaml | 3 --- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/checkbox/src/Checkbox.module.css b/packages/checkbox/src/Checkbox.module.css index ac1d5bf..fd47cdd 100644 --- a/packages/checkbox/src/Checkbox.module.css +++ b/packages/checkbox/src/Checkbox.module.css @@ -16,7 +16,7 @@ all: unset; width: var(--size); height: var(--size); - border-radius: 4px; + border-radius: var(--border-radius); border: 2px solid var(--border-color); background-color: var(--background-color); display: flex; diff --git a/packages/checkbox/src/Checkbox.tsx b/packages/checkbox/src/Checkbox.tsx index 8652edf..e38caf5 100644 --- a/packages/checkbox/src/Checkbox.tsx +++ b/packages/checkbox/src/Checkbox.tsx @@ -62,6 +62,7 @@ export const Checkbox = forwardRef< style={ { '--margin': '4px', + '--border-radius': '4px', '--padding': padding, '--size': checkboxSize, '--indicator-size': indicatorSize, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9a6da92..734e2f2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -556,9 +556,6 @@ importers: '@radix-ui/react-checkbox': specifier: ^1.0.4 version: 1.1.3(@types/react-dom@18.3.1)(@types/react@18.3.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-slot': - specifier: ^1.0.2 - version: 1.1.0(@types/react@18.3.13)(react@18.3.1) '@sipe-team/tokens': specifier: workspace:^ version: link:../tokens From 3d41c3c9c1c3ae6c723950fd9632cabdca425b99 Mon Sep 17 00:00:00 2001 From: JIHOON LEE Date: Wed, 22 Jan 2025 23:19:43 +0900 Subject: [PATCH 06/10] feat(checkbox): apply PR review feedback - move non-variable elements to module.css for better separation --- packages/checkbox/src/Checkbox.module.css | 16 ++++++++-------- packages/checkbox/src/Checkbox.tsx | 10 +--------- 2 files changed, 9 insertions(+), 17 deletions(-) diff --git a/packages/checkbox/src/Checkbox.module.css b/packages/checkbox/src/Checkbox.module.css index fd47cdd..72bf224 100644 --- a/packages/checkbox/src/Checkbox.module.css +++ b/packages/checkbox/src/Checkbox.module.css @@ -3,22 +3,22 @@ display: flex; align-items: center; gap: var(--padding); + margin: 4px; } .label { display: flex; align-items: center; gap: var(--padding); - margin-left: var(--margin); } .checkbox { all: unset; width: var(--size); height: var(--size); - border-radius: var(--border-radius); - border: 2px solid var(--border-color); - background-color: var(--background-color); + border-radius: 4px; + border: 2px solid #a1a1aa; + background-color: #ffffff; display: flex; align-items: center; justify-content: center; @@ -26,7 +26,7 @@ } .checkbox:focus-visible { - border-color: var(--focus-border-color); + border-color: #06b6d4; outline: none; } @@ -36,12 +36,12 @@ } .checkbox[data-state="checked"] { - border-color: var(--checked-border-color); - background-color: var(--checked-background-color); + border-color: #00ffff; + background-color: #00ffff; } .indicator { - color: var(--indicator-color); + color: #ffffff; height: var(--indicator-size); width: var(--indicator-size); display: flex; diff --git a/packages/checkbox/src/Checkbox.tsx b/packages/checkbox/src/Checkbox.tsx index e38caf5..05629ce 100644 --- a/packages/checkbox/src/Checkbox.tsx +++ b/packages/checkbox/src/Checkbox.tsx @@ -1,5 +1,5 @@ import * as CheckboxPrimitive from '@radix-ui/react-checkbox'; -import { color, fontSize } from '@sipe-team/tokens'; +import { fontSize } from '@sipe-team/tokens'; import { Typography } from '@sipe-team/typography'; import { Check } from 'lucide-react'; import { type ComponentPropsWithoutRef, type ElementRef, forwardRef, useId, Children, isValidElement, cloneElement } from 'react'; @@ -61,17 +61,9 @@ export const Checkbox = forwardRef< className={styles.root} style={ { - '--margin': '4px', - '--border-radius': '4px', '--padding': padding, '--size': checkboxSize, '--indicator-size': indicatorSize, - '--border-color': color.gray400, - '--background-color': color.white, - '--checked-border-color': color.cyan300, - '--checked-background-color': color.cyan300, - '--indicator-color': color.white, - '--focus-border-color': color.cyan500, ...style, } as React.CSSProperties } From 991cf2b81ad07d628c8a68c42694ad35f5c5e286 Mon Sep 17 00:00:00 2001 From: JIHOON LEE Date: Wed, 22 Jan 2025 23:27:08 +0900 Subject: [PATCH 07/10] feat(checkbox): apply PR review feedback - Replace logical operators with ternary operators for clarity - Clean up type imports and React imports for better organization --- packages/checkbox/src/Checkbox.tsx | 64 +++++++++++++++++------------- 1 file changed, 37 insertions(+), 27 deletions(-) diff --git a/packages/checkbox/src/Checkbox.tsx b/packages/checkbox/src/Checkbox.tsx index 05629ce..6dbbd3e 100644 --- a/packages/checkbox/src/Checkbox.tsx +++ b/packages/checkbox/src/Checkbox.tsx @@ -2,7 +2,17 @@ import * as CheckboxPrimitive from '@radix-ui/react-checkbox'; import { fontSize } from '@sipe-team/tokens'; import { Typography } from '@sipe-team/typography'; import { Check } from 'lucide-react'; -import { type ComponentPropsWithoutRef, type ElementRef, forwardRef, useId, Children, isValidElement, cloneElement } from 'react'; +import { + type ComponentPropsWithoutRef, + type ElementRef, + type ReactNode, + type CSSProperties, + Children, + cloneElement, + forwardRef, + isValidElement, + useId, +} from 'react'; import styles from './Checkbox.module.css'; export type CheckboxSize = 'sm' | 'md' | 'lg'; @@ -15,7 +25,7 @@ export interface CheckboxProps } export interface CheckboxGroupProps { - children: React.ReactNode; + children: ReactNode; defaultValue?: string[]; name?: string; onChange?: (value: string[]) => void; @@ -53,38 +63,31 @@ export const Checkbox = forwardRef< CheckboxProps >(({ className, size = 'md', label, style, id: providedId, ...props }, ref) => { const generatedId = useId(); - const id = providedId || generatedId; + const id = providedId ?? generatedId; const { checkboxSize, indicatorSize, fontSize, padding } = getSizeStyles(size); return (
- + - {label && ( + {label ? ( - )} + ) : null}
); }); @@ -93,20 +96,27 @@ Checkbox.displayName = 'Checkbox'; export const CheckboxGroup = forwardRef( ({ children, name, onChange, value }, ref) => { - const handleCheckedChange = (checked: CheckboxPrimitive.CheckedState, itemValue: string | undefined) => { - if (!onChange || !itemValue || typeof checked !== 'boolean') return; + const handleCheckedChange = ( + checked: CheckboxPrimitive.CheckedState, + itemValue: string | undefined, + ) => { + if (!onChange || !itemValue || typeof checked !== 'boolean') { + return; + } const newValues = checked - ? [...(value || []), itemValue] - : (value || []).filter((v) => v !== itemValue); + ? [...(value ?? []), itemValue] + : (value ?? []).filter((v) => v !== itemValue); onChange(newValues); }; const mappedChildren = Children.map(children, (child) => { - if (!isValidElement(child)) return child; + if (!isValidElement(child)) { + return child; + } - const checked = value?.includes(child.props.value || '') || false; + const checked = value?.includes(child.props.value ?? '') ?? false; return cloneElement(child, { checked, @@ -115,7 +125,7 @@ export const CheckboxGroup = forwardRef( handleCheckedChange(state, child.props.value); child.props.onCheckedChange?.(state); }, - } as Partial); + }); }); return ( @@ -123,7 +133,7 @@ export const CheckboxGroup = forwardRef( {mappedChildren} ); - } + }, ); CheckboxGroup.displayName = 'CheckboxGroup'; \ No newline at end of file From 9fbce40e08d9ead5eb5bad52a7b5a684fc69b8ef Mon Sep 17 00:00:00 2001 From: JIHOON LEE Date: Wed, 22 Jan 2025 23:28:36 +0900 Subject: [PATCH 08/10] feat(checkbox): update test scenarios to reflect new requirements --- packages/checkbox/src/Checkbox.test.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/checkbox/src/Checkbox.test.tsx b/packages/checkbox/src/Checkbox.test.tsx index fbadfb3..9740a90 100644 --- a/packages/checkbox/src/Checkbox.test.tsx +++ b/packages/checkbox/src/Checkbox.test.tsx @@ -4,7 +4,7 @@ import { expect, test } from 'vitest'; import { useState } from 'react'; import { Checkbox, CheckboxGroup } from './Checkbox'; -test('체크박스를 클릭하면 상태가 변경된다.', async () => { +test('체크박스를 클릭하면 체크 상태가 활성화된다.', async () => { const user = userEvent.setup(); render(); @@ -15,7 +15,7 @@ test('체크박스를 클릭하면 상태가 변경된다.', async () => { expect(checkbox).toBeChecked(); }); -test('disabled 상태의 체크박스는 클릭할 수 없다.', async () => { +test('disabled 상태의 체크박스를 클릭해도 체크 상태가 바뀌지 않는다.', async () => { const user = userEvent.setup(); render(); From 630d3ee2dbbfcdb37e6bf22dc12a34358dcf82e4 Mon Sep 17 00:00:00 2001 From: JIHOON LEE Date: Wed, 22 Jan 2025 23:30:00 +0900 Subject: [PATCH 09/10] chore: enforce EndOfLine (EOL) settings for consistency --- packages/checkbox/.storybook/preview.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/checkbox/.storybook/preview.ts b/packages/checkbox/.storybook/preview.ts index 70220c4..554d4b8 100644 --- a/packages/checkbox/.storybook/preview.ts +++ b/packages/checkbox/.storybook/preview.ts @@ -13,4 +13,5 @@ const preview: Preview = { }, }; -export default preview; \ No newline at end of file +export default preview; + From b1925b8eb63614cc6513c357917ae2770f0e9511 Mon Sep 17 00:00:00 2001 From: JIHOON LEE Date: Wed, 22 Jan 2025 23:36:44 +0900 Subject: [PATCH 10/10] feat: use toBeChecked instead of toHaveAttribute --- packages/checkbox/src/Checkbox.test.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/checkbox/src/Checkbox.test.tsx b/packages/checkbox/src/Checkbox.test.tsx index 9740a90..554f967 100644 --- a/packages/checkbox/src/Checkbox.test.tsx +++ b/packages/checkbox/src/Checkbox.test.tsx @@ -91,7 +91,7 @@ test('체크박스 그룹에서 value prop으로 선택된 항목을 제어할 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'); -}); \ No newline at end of file + expect(checkbox1).toBeChecked(); + expect(checkbox2).not.toBeChecked(); + expect(checkbox3).toBeChecked(); +});