From 48d20bc226c655be79e619a1c487fc7c6540a25a Mon Sep 17 00:00:00 2001 From: ghdtjgus76 Date: Thu, 13 Jun 2024 15:27:24 +0900 Subject: [PATCH 01/19] Squashed commit of refactor/switch-component --- .../components/Checkbox/Checkbox.stories.tsx | 20 +- .../wow-ui/src/components/Checkbox/index.tsx | 10 +- .../src/components/Switch/Switch.stories.tsx | 72 +++++-- .../src/components/Switch/Switch.test.tsx | 8 +- .../wow-ui/src/components/Switch/index.tsx | 200 +++++++++++------- packages/wow-ui/src/hooks/useCheckedState.ts | 17 +- 6 files changed, 213 insertions(+), 114 deletions(-) diff --git a/packages/wow-ui/src/components/Checkbox/Checkbox.stories.tsx b/packages/wow-ui/src/components/Checkbox/Checkbox.stories.tsx index 31aadaeb..00d829bd 100644 --- a/packages/wow-ui/src/components/Checkbox/Checkbox.stories.tsx +++ b/packages/wow-ui/src/components/Checkbox/Checkbox.stories.tsx @@ -23,8 +23,7 @@ const meta = { }, }, disabled: { - description: - "disabled는 체크박스가 비활성화되어 있는지 여부를 나타냅니다.", + description: "체크박스가 비활성화되어 있는지 여부를 나타냅니다.", table: { type: { summary: "boolean" }, defaultValue: { summary: "false" }, @@ -34,7 +33,7 @@ const meta = { }, }, checked: { - description: "checked는 외부에서 제어할 활성 상태를 나타냅니다.", + description: "외부에서 제어할 활성 상태를 나타냅니다.", table: { type: { summary: "boolean" }, }, @@ -51,7 +50,7 @@ const meta = { control: false, }, onChange: { - description: "외부 활성 상태가 변경될 때 호출될 콜백 함수를 나타냅니다.", + description: "외부 활성 상태가 변경될 때 호출되는 함수입니다.", table: { type: { summary: "() => void" }, defaultValue: { summary: null }, @@ -61,7 +60,7 @@ const meta = { }, }, onClick: { - description: "체크박스 클릭 시 동작할 이벤트입니다.", + description: "체크박스 클릭 시 호출되는 함수입니다.", table: { type: { summary: "() => void" }, defaultValue: { summary: null }, @@ -72,7 +71,7 @@ const meta = { }, onKeyDown: { description: - "체크박스에 포커스 됐을 때 엔터 키 또는 스페이스 바를 눌렀을 때 동작할 이벤트입니다.", + "체크박스에 포커스 됐을 때 엔터 키 또는 스페이스 바를 눌렀을 때 호출되는 함수입니다.", table: { type: { summary: "() => void" }, defaultValue: { summary: null }, @@ -130,6 +129,15 @@ const meta = { }, control: false, }, + className: { + description: "체크박스에 전달하는 커스텀 클래스를 설정합니다.", + table: { + type: { summary: "string" }, + }, + control: { + type: "text", + }, + }, }, } satisfies Meta; diff --git a/packages/wow-ui/src/components/Checkbox/index.tsx b/packages/wow-ui/src/components/Checkbox/index.tsx index 9ccf41f2..1703c216 100644 --- a/packages/wow-ui/src/components/Checkbox/index.tsx +++ b/packages/wow-ui/src/components/Checkbox/index.tsx @@ -18,15 +18,15 @@ import { useCheckedState } from "@/hooks"; * @param {boolean} [defaultChecked=false] 체크박스가 처음에 활성화되어 있는지 여부. * @param {boolean} [disabled=false] 체크박스가 비활성화되어 있는지 여부. * @param {boolean} [checked] 외부에서 제어할 활성 상태. - * @param {() => void} [onChange] 외부 활성 상태가 변경될 때 호출될 콜백 함수. - * @param {() => void} [onClick] 체크박스 클릭 시 동작할 이벤트. - * @param {() => void} [onKeyDown] 체크박스에 포커스 됐을 때 엔터 키 또는 스페이스 바를 눌렀을 때 동작할 이벤트. + * @param {() => void} [onChange] 외부 활성 상태가 변경될 때 호출되는 함수. + * @param {() => void} [onClick] 체크박스 클릭 시 호출되는 함수. + * @param {() => void} [onKeyDown] 체크박스에 포커스 됐을 때 엔터 키 또는 스페이스 바를 눌렀을 때 호출되는 함수. * @param {() => void} [onMouseEnter] 마우스가 체크박스 위로 진입할 때 호출되는 함수. * @param {() => void} [onMouseLeave] 마우스가 체크박스에서 벗어날 때 호출되는 함수. * @param {"vertical" | "horizontal"} [position="horizontal"] 체크박스와 텍스트의 배치를 설정. 'vertical' 또는 'horizontal' 값을 가집니다. * @param {InputHTMLAttributes} [inputProps] 체크박스의 기본 input 요소에 전달할 추가 속성들. - * @param {CSSProperties} [style] 체크박스의 커스텀 스타일을 설정합니다. - * @param {string} [className] 체크박스에 전달하는 커스텀 클래스를 설정합니다. + * @param {CSSProperties} [style] 체크박스의 커스텀 스타일. + * @param {string} [className] 체크박스에 전달하는 커스텀 클래스. * @param {React.ReactNode} [children] 체크박스 오른쪽이나 위쪽에 들어갈 텍스트. * @param {ComponentPropsWithoutRef} rest 렌더링된 요소 또는 컴포넌트에 전달할 추가 props. * @param {ComponentPropsWithRef["ref"]} ref 렌더링된 요소 또는 컴포넌트에 연결할 ref. diff --git a/packages/wow-ui/src/components/Switch/Switch.stories.tsx b/packages/wow-ui/src/components/Switch/Switch.stories.tsx index 6a5a89dd..4ca0a70f 100644 --- a/packages/wow-ui/src/components/Switch/Switch.stories.tsx +++ b/packages/wow-ui/src/components/Switch/Switch.stories.tsx @@ -12,8 +12,7 @@ const meta = { }, argTypes: { defaultChecked: { - description: - "defaultChecked는 스위치가 처음에 활성화되어 있는지 여부를 나타냅니다.", + description: "스위치가 처음에 활성화되어 있는지 여부를 나타냅니다.", table: { type: { summary: "boolean" }, defaultValue: { summary: "false" }, @@ -22,9 +21,8 @@ const meta = { type: "boolean", }, }, - isDisabled: { - description: - "isDisabled는 스위치가 비활성화되어 있는지 여부를 나타냅니다.", + disabled: { + description: "스위치가 비활성화되어 있는지 여부를 나타냅니다.", table: { type: { summary: "boolean" }, defaultValue: { summary: "false" }, @@ -33,8 +31,8 @@ const meta = { type: "boolean", }, }, - isChecked: { - description: "isChecked는 외부에서 제어할 활성 상태를 나타냅니다.", + checked: { + description: "외부에서 제어할 활성 상태를 나타냅니다.", table: { type: { summary: "boolean" }, defaultValue: { summary: "false" }, @@ -54,7 +52,7 @@ const meta = { }, }, onChange: { - description: "외부 활성 상태가 변경될 때 호출될 콜백 함수를 나타냅니다.", + description: "외부 활성 상태가 변경될 때 호출되는 함수입니다.", table: { type: { summary: "() => void" }, defaultValue: { summary: null }, @@ -64,7 +62,7 @@ const meta = { }, }, onClick: { - description: "스위치 클릭 시 동작할 이벤트입니다.", + description: "스위치를 클릭했을 때 호출되는 함수입니다.", table: { type: { summary: "() => void" }, defaultValue: { summary: null }, @@ -75,7 +73,7 @@ const meta = { }, onKeyDown: { description: - "스위치가 포커스됐을 때 엔터 키 또는 스페이스 바를 눌렀을 때 동작할 이벤트입니다.", + "스위치가 포커스됐을 때 엔터 키 또는 스페이스 바를 눌렀을 때 호출되는 함수입니다.", table: { type: { summary: "() => void" }, defaultValue: { summary: null }, @@ -84,6 +82,52 @@ const meta = { type: "function", }, }, + onMouseEnter: { + description: "마우스가 스위치 위로 진입할 때 호출되는 함수입니다.", + table: { + type: { summary: "() => void" }, + defaultValue: { summary: null }, + control: { + type: "function", + }, + }, + }, + onMouseLeave: { + description: "마우스가 스위치에서 벗어날 때 호출되는 함수입니다.", + table: { + type: { summary: "() => void" }, + defaultValue: { summary: null }, + control: { + type: "function", + }, + }, + }, + inputProps: { + description: + "스위치의 기본 input 요소에 전달할 추가 속성들을 나타냅니다.", + table: { + type: { summary: "InputHTMLAttributes" }, + defaultValue: { summary: "{}" }, + }, + control: false, + }, + style: { + description: "스위치의 커스텀 스타일을 설정합니다.", + table: { + type: { summary: "CSSProperties" }, + defaultValue: { summary: "{}" }, + }, + control: false, + }, + className: { + description: "스위치에 전달하는 커스텀 클래스를 설정합니다.", + table: { + type: { summary: "string" }, + }, + control: { + type: "text", + }, + }, }, } satisfies Meta; @@ -103,7 +147,7 @@ export const DefaultChecked: Story = { export const Disabled: Story = { args: { - isDisabled: true, + disabled: true, }, }; @@ -114,13 +158,13 @@ export const WithText: Story = { }; const ControlledSwitch = () => { - const [isChecked, setIsChecked] = useState(false); + const [checked, setChecked] = useState(false); const handleChange = () => { - setIsChecked((prev) => !prev); + setChecked((prev) => !prev); }; - return ; + return ; }; export const ControlledState: Story = { diff --git a/packages/wow-ui/src/components/Switch/Switch.test.tsx b/packages/wow-ui/src/components/Switch/Switch.test.tsx index 35fdffca..89682194 100644 --- a/packages/wow-ui/src/components/Switch/Switch.test.tsx +++ b/packages/wow-ui/src/components/Switch/Switch.test.tsx @@ -76,7 +76,7 @@ describe("disabled", () => { let rendered: RenderResult; beforeEach(() => { - rendered = render(); + rendered = render(); }); it("should render with attributes aria-disabled to be true", () => { @@ -155,11 +155,11 @@ describe("external control and events", () => { }); it("should toggle external checked state when onClick event fired", async () => { - let isChecked = false; + let checked = false; const handleChange = () => { - isChecked = !isChecked; + checked = !checked; }; - const rendered = render(); + const rendered = render(); const switchComponent = rendered.getByRole("checkbox"); switchComponent.onchange = handleChange; diff --git a/packages/wow-ui/src/components/Switch/index.tsx b/packages/wow-ui/src/components/Switch/index.tsx index bd66913d..aa987f92 100644 --- a/packages/wow-ui/src/components/Switch/index.tsx +++ b/packages/wow-ui/src/components/Switch/index.tsx @@ -2,107 +2,109 @@ import { cva } from "@styled-system/css"; import { Flex, styled } from "@styled-system/jsx"; -import type { ComponentPropsWithRef, KeyboardEvent, ReactNode } from "react"; -import { forwardRef, useEffect, useState } from "react"; +import type { CSSProperties, InputHTMLAttributes, ReactNode } from "react"; +import { forwardRef, useId } from "react"; + +import useCheckedState from "@/hooks/useCheckedState"; /** - * @template T 렌더링할 요소 또는 컴포넌트 타입 + * @description on, off 두 가지 상태로 설정할 수 있는 스위치 컴포넌트입니다. * * @param {boolean} [defaultChecked=false] 스위치가 처음에 활성화되어 있는지 여부. - * @param {boolean} [isDisabled=false] 스위치가 비활성화되어 있는지 여부. - * @param {boolean} [isChecked=false] 외부에서 제어할 활성 상태. - * @param {() => void} [onChange] 외부 활성 상태가 변경될 때 호출될 콜백 함수. - * @param {ReactNode} [text] 스위치 오른 쪽에 들어갈 텍스트. - * @param {() => void} [onClick] 스위치 클릭 시 동작할 이벤트. - * @param {() => void} [onKeyDown] 스위치가 포커스됐을 때 엔터 키 또는 스페이스 바를 눌렀을 때 동작할 이벤트. + * @param {boolean} [disabled=false] 스위치가 비활성화되어 있는지 여부. + * @param {boolean} [checked] 외부에서 제어할 활성 상태. + * @param {ReactNode} [text] 스위치 오른쪽에 들어갈 텍스트. + * @param {() => void} [onChange] 외부 활성 상태가 변경될 때 호출되는 함수. + * @param {() => void} [onClick] 스위치를 클릭했을 때 호출되는 함수. + * @param {() => void} [onKeyDown] 스위치가 포커스됐을 때 엔터 키 또는 스페이스 바를 눌렀을 때 호출되는 함수. + * @param {() => void} [onMouseEnter] 마우스가 스위치 위로 진입할 때 호출되는 함수. + * @param {() => void} [onMouseLeave] 마우스가 스위치에서 벗어날 때 호출되는 함수. + * @param {InputHTMLAttributes} [inputProps] 스위치의 기본 input 요소에 전달할 추가 속성들. + * @param {CSSProperties} [style] 스위치의 커스텀 스타일. + * @param {string} [className] 스위치에 전달하는 커스텀 클래스. * @param {ComponentPropsWithoutRef} rest 렌더링된 요소 또는 컴포넌트에 전달할 추가 props. * @param {ComponentPropsWithRef["ref"]} ref 렌더링된 요소 또는 컴포넌트에 연결할 ref. */ export interface SwitchProps { defaultChecked?: boolean; - isDisabled?: boolean; - isChecked?: boolean; + disabled?: boolean; + checked?: boolean; text?: ReactNode; + onChange?: () => void; onClick?: () => void; onKeyDown?: () => void; - onChange?: () => void; + onMouseEnter?: () => void; + onMouseLeave?: () => void; + inputProps?: InputHTMLAttributes; + style?: CSSProperties; + className?: string; } -const SwitchIcon = ({ - isDisabled, - isChecked, -}: { - isDisabled: boolean; - isChecked: boolean; -}) => { - return ( - - ); -}; - -const Switch = forwardRef( +const Switch = forwardRef( ( { defaultChecked = false, - isDisabled = false, - isChecked: isCheckedProp, + disabled = false, + checked: checkedProp, text = "", + onChange, onClick, onKeyDown, - onChange, + onMouseEnter, + onMouseLeave, + inputProps, ...rest }: SwitchProps, - ref: ComponentPropsWithRef<"input">["ref"] + ref ) => { - const [isChecked, setIsChecked] = useState(() => - isCheckedProp ? isCheckedProp : defaultChecked - ); - - useEffect(() => { - if (isCheckedProp !== undefined) { - setIsChecked(isCheckedProp); - } - }, [isCheckedProp]); - - const handleClick = () => { - onChange ? onChange() : setIsChecked((prev) => !prev); - onClick?.(); - }; - - const handleKeyDown = (event: KeyboardEvent) => { - if (event.key === "Enter" || event.key === " ") { - event.preventDefault(); + const defaultId = `switch-${useId()}`; + const id = inputProps?.id ?? defaultId; - onChange ? onChange() : setIsChecked((prev) => !prev); - onKeyDown?.(); - } - }; + const { + checked, + pressed, + handleClick, + handleKeyDown, + handleKeyUp, + handleMouseUp, + handleMouseDown, + } = useCheckedState({ + defaultChecked, + checked: checkedProp, + disabled, + onChange, + onClick, + onKeyDown, + }); return ( - + - - + {!!text && {text}} @@ -110,7 +112,29 @@ const Switch = forwardRef( } ); -const switchStyle = cva({ +Switch.displayName = "Switch"; +export default Switch; + +const SwitchIcon = ({ + disabled, + checked, + pressed, +}: { + disabled: boolean; + checked: boolean; + pressed: boolean; +}) => { + return ( + + ); +}; + +const labelStyle = cva({ base: { width: "3.25rem !important", height: "1.75rem !important", @@ -123,15 +147,32 @@ const switchStyle = cva({ type: { checked: { bgColor: "primary", - _hover: { bgColor: "blueHover" }, - _pressed: { bgColor: "bluePressed" }, + _hover: { + bgColor: "blueHover", + _pressed: { + bgColor: "bluePressed", + }, + }, + _pressed: { + bgColor: "bluePressed", + }, }, unchecked: { bgColor: "outline", - _hover: { bgColor: "sub" }, - _pressed: { bgColor: "lightDisabled" }, + _hover: { + bgColor: "sub", + _pressed: { + bgColor: "bluePressed", + }, + }, + _pressed: { + bgColor: "bluePressed", + }, + }, + disabled: { + bgColor: "lightDisabled", + cursor: "none", }, - disabled: { bgColor: "lightDisabled" }, }, }, defaultVariants: { @@ -146,6 +187,7 @@ const inputStyle = cva({ height: 0, overflow: "hidden", position: "absolute", + cursor: "inherit", }, }); @@ -163,6 +205,11 @@ const switchIconStyle = cva({ checked: { left: "1.625rem", bg: "backgroundNormal", + _hover: { + _pressed: { + bg: "blueBackgroundPressed", + }, + }, _pressed: { bg: "blueBackgroundPressed", }, @@ -170,6 +217,11 @@ const switchIconStyle = cva({ unchecked: { left: "0.125rem", bg: "backgroundNormal", + _hover: { + _pressed: { + bg: "monoBackgroundPressed", + }, + }, _pressed: { bg: "monoBackgroundPressed", }, @@ -183,7 +235,3 @@ const switchIconStyle = cva({ type: "checked", }, }); - -Switch.displayName = "Switch"; - -export default Switch; diff --git a/packages/wow-ui/src/hooks/useCheckedState.ts b/packages/wow-ui/src/hooks/useCheckedState.ts index b19ac2aa..5e32d7de 100644 --- a/packages/wow-ui/src/hooks/useCheckedState.ts +++ b/packages/wow-ui/src/hooks/useCheckedState.ts @@ -18,12 +18,17 @@ const useCheckedState = ({ onClick, onKeyDown, }: CheckedStateProps) => { - const [checked, setChecked] = useState(() => - checkedProp !== undefined ? checkedProp : defaultChecked + const [checked, setChecked] = useState( + checkedProp ? checkedProp : defaultChecked ); - const [pressed, setPressed] = useState(false); + useEffect(() => { + if (checkedProp !== undefined) { + setChecked(checkedProp); + } + }, [checkedProp]); + const handleMouseDown = () => { if (!disabled) setPressed(true); }; @@ -32,12 +37,6 @@ const useCheckedState = ({ if (!disabled) setPressed(false); }; - useEffect(() => { - if (checkedProp !== undefined) { - setChecked(checkedProp); - } - }, [checkedProp]); - const handleClick = () => { onChange ? onChange() : setChecked((prev) => !prev); onClick?.(); From 0f254770039030fd3148f8c4758ff336aca57f17 Mon Sep 17 00:00:00 2001 From: ghdtjgus76 Date: Thu, 13 Jun 2024 18:19:39 +0900 Subject: [PATCH 02/19] =?UTF-8?q?chore:=20package.json=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/codegen/package.json | 1 - pnpm-lock.yaml | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/codegen/package.json b/packages/codegen/package.json index 9bc361bd..8aa3a29f 100644 --- a/packages/codegen/package.json +++ b/packages/codegen/package.json @@ -1,6 +1,5 @@ { "name": "codegen", - "version": "0.0.0", "description": "", "main": "index.js", "type": "module", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9094c483..afd7db24 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -14,9 +14,6 @@ importers: react-dom: specifier: ^18.2.0 version: 18.2.0(react@18.2.0) - wowds-icons: - specifier: workspace:^ - version: link:packages/wow-icons devDependencies: '@babel/preset-env': specifier: ^7.24.5 @@ -259,6 +256,9 @@ importers: react: specifier: ^18.2.0 version: 18.2.0 + wowds-icons: + specifier: workspace:^ + version: link:../wow-icons devDependencies: '@chromatic-com/storybook': specifier: ^1.3.3 From a5ee7f155f9dbfdcf624be85ec483413033482b0 Mon Sep 17 00:00:00 2001 From: ghdtjgus76 Date: Thu, 13 Jun 2024 19:39:04 +0900 Subject: [PATCH 03/19] =?UTF-8?q?chore:=20Story=20=ED=85=9C=ED=94=8C?= =?UTF-8?q?=EB=A6=BF=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/codegen/templates/Story.tsx.hbs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/codegen/templates/Story.tsx.hbs b/packages/codegen/templates/Story.tsx.hbs index abb27659..f299b128 100644 --- a/packages/codegen/templates/Story.tsx.hbs +++ b/packages/codegen/templates/Story.tsx.hbs @@ -19,7 +19,7 @@ const meta = { }, argTypes: { props1: { - description: "props1은 default 값이 있는 필수적인 속성입니다."", + description: "props1은 default 값이 있는 필수적인 속성입니다.", table: { // type, 필수가 아닐 경우 required 삭제 type: { summary: "string", required: true }, From b1536723865120249a6e8ac55d0e24dca2a42b0b Mon Sep 17 00:00:00 2001 From: ghdtjgus76 Date: Thu, 13 Jun 2024 19:39:53 +0900 Subject: [PATCH 04/19] =?UTF-8?q?feat:=20MultiGroup=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wow-ui/src/components/Checkbox/index.tsx | 12 +- .../MultiGroup/MultiGroup.stories.tsx | 274 ++++++++++++++++++ .../MultiGroup/MultiGroupContext.ts | 17 ++ .../src/components/MultiGroup/index.tsx | 89 ++++++ .../wow-ui/src/components/Switch/index.tsx | 20 +- packages/wow-ui/src/hooks/useCheckedState.ts | 42 ++- 6 files changed, 436 insertions(+), 18 deletions(-) create mode 100644 packages/wow-ui/src/components/MultiGroup/MultiGroup.stories.tsx create mode 100644 packages/wow-ui/src/components/MultiGroup/MultiGroupContext.ts create mode 100644 packages/wow-ui/src/components/MultiGroup/index.tsx diff --git a/packages/wow-ui/src/components/Checkbox/index.tsx b/packages/wow-ui/src/components/Checkbox/index.tsx index 1703c216..c21af3c0 100644 --- a/packages/wow-ui/src/components/Checkbox/index.tsx +++ b/packages/wow-ui/src/components/Checkbox/index.tsx @@ -18,6 +18,7 @@ import { useCheckedState } from "@/hooks"; * @param {boolean} [defaultChecked=false] 체크박스가 처음에 활성화되어 있는지 여부. * @param {boolean} [disabled=false] 체크박스가 비활성화되어 있는지 여부. * @param {boolean} [checked] 외부에서 제어할 활성 상태. + * @param {string} value 체크박스 값. * @param {() => void} [onChange] 외부 활성 상태가 변경될 때 호출되는 함수. * @param {() => void} [onClick] 체크박스 클릭 시 호출되는 함수. * @param {() => void} [onKeyDown] 체크박스에 포커스 됐을 때 엔터 키 또는 스페이스 바를 눌렀을 때 호출되는 함수. @@ -35,6 +36,7 @@ export interface CheckboxProps extends PropsWithChildren { defaultChecked?: boolean; disabled?: boolean; checked?: boolean; + value: string; onChange?: () => void; onClick?: () => void; onKeyDown?: () => void; @@ -50,8 +52,9 @@ const Checkbox = forwardRef( ( { defaultChecked = false, - disabled = false, + disabled: disabledProp = false, checked: checkedProp, + value, onClick, onChange, children, @@ -68,6 +71,7 @@ const Checkbox = forwardRef( const { checked, pressed, + disabled, handleClick, handleKeyDown, handleKeyUp, @@ -76,7 +80,8 @@ const Checkbox = forwardRef( } = useCheckedState({ defaultChecked, checked: checkedProp, - disabled, + disabled: disabledProp, + value, onChange, onClick, onKeyDown, @@ -112,7 +117,8 @@ const Checkbox = forwardRef( type: disabled ? "disabled" : checked ? "checked" : "default", })} {...inputProps} - onClick={handleClick} + value={value} + onClick={() => handleClick(value)} /> {checked && ( <>{Story()}], +} satisfies Meta; + +export default meta; + +type Story = StoryObj; + +export const VerticalCheckboxMultiGroup: Story = { + render: () => ( + + + + + + + ), + args: { + children: <>, + variant: "checkbox", + }, +}; + +export const HorizontalCheckboxMultiGroup: Story = { + render: () => ( + + + + + + + ), + args: { + children: <>, + variant: "checkbox", + }, +}; + +export const TextWithVerticalCheckboxMultiGroup: Story = { + render: () => ( + + + + + + + ), + args: { + children: <>, + variant: "checkbox", + }, +}; + +export const TextWithHorizontalCheckboxMultiGroup: Story = { + render: () => ( + + + + + + + ), + args: { + children: <>, + variant: "checkbox", + }, +}; + +export const TextWithHorizontalWithGapCheckboxMultiGroup: Story = { + render: () => ( + + + + + + + ), + args: { + children: <>, + variant: "checkbox", + }, +}; + +export const DisabledCheckboxMultiGroup = { + render: () => ( + + + + + + + ), + args: { + children: <>, + variant: "checkbox", + }, +}; + +export const WithDefaultValueCheckboxMultiGroup = { + render: () => ( + + + + + + + ), + args: { + children: <>, + variant: "checkbox", + }, +}; + +const ControlledCheckboxState = () => { + const [checked, setChecked] = useState(["checkbox1", "checkbox3"]); + + const handleChange = (value: string) => { + if (!checked.includes(value)) { + setChecked((prev) => [...prev, value]); + } else { + setChecked((prev) => prev.filter((item) => item !== value)); + } + }; + + return ( + + + + + + + ); +}; + +export const ControlledCheckboxMultiGroup: Story = { + render: () => , + args: { + children: <>, + variant: "switch", + }, +}; + +export const SwitchMultiGroup: Story = { + render: () => ( + + + + + + ), + args: { + children: <>, + variant: "switch", + }, +}; + +export const DisabledSwitchMultiGroup: Story = { + render: () => ( + + + + + + ), + args: { + children: <>, + variant: "switch", + }, +}; + +export const TextWithSwitchMultiGroup: Story = { + render: () => ( + + + + + + ), + args: { + children: <>, + variant: "switch", + }, +}; + +export const WithDefaultValueSwitchMultiGroup: Story = { + render: () => ( + + + + + + ), + args: { + children: <>, + variant: "switch", + }, +}; + +const ControlledSwitchState = () => { + const [checked, setChecked] = useState(["switch1", "switch2"]); + + const handleChange = (value: string) => { + if (!checked.includes(value)) { + setChecked((prev) => [...prev, value]); + } else { + setChecked((prev) => prev.filter((item) => item !== value)); + } + }; + + return ( + + + + + + ); +}; + +export const ControlledSwitchMultiGroup: Story = { + render: () => , + args: { + children: <>, + variant: "switch", + }, +}; diff --git a/packages/wow-ui/src/components/MultiGroup/MultiGroupContext.ts b/packages/wow-ui/src/components/MultiGroup/MultiGroupContext.ts new file mode 100644 index 00000000..5c92e909 --- /dev/null +++ b/packages/wow-ui/src/components/MultiGroup/MultiGroupContext.ts @@ -0,0 +1,17 @@ +import { createContext } from "react"; + +export type MultiGroupContextProps = { + name?: string; + checked?: string[]; + onChange?: (value: string) => void; + disabled?: boolean; +}; + +const MultiGroupContext = createContext({ + name: "MultiGroupName", + checked: undefined, + onChange: undefined, + disabled: false, +}); + +export default MultiGroupContext; diff --git a/packages/wow-ui/src/components/MultiGroup/index.tsx b/packages/wow-ui/src/components/MultiGroup/index.tsx new file mode 100644 index 00000000..192442e0 --- /dev/null +++ b/packages/wow-ui/src/components/MultiGroup/index.tsx @@ -0,0 +1,89 @@ +"use client"; + +import { Flex } from "@styled-system/jsx"; +import type { CSSProperties, ReactNode } from "react"; +import { useCallback, useEffect, useMemo, useState } from "react"; + +import MultiGroupContext from "@/components/MultiGroup/MultiGroupContext"; + +export type VariantType = "checkbox" | "switch"; +type PositionType = "vertical" | "horizontal"; + +/** + * @description 여러 체크박스, 또는 스위치 컴포넌트를 하나의 그룹으로 묶는 컴포넌트입니다. + */ +export interface MultiGroupProps { + variant: T; + position?: T extends "checkbox" ? PositionType : undefined; + gap?: T extends "checkbox" ? number : undefined; + children: ReactNode; + name?: string; + defaultValue?: string[]; + checked?: string[]; + onChange?: (value: string) => void; + disabled?: boolean; + className?: string; + style?: CSSProperties; +} + +const MultiGroup = ({ + position, + gap: gapProp, + children, + name, + defaultValue, + checked: checkedProp, + onChange, + disabled, + ...rest +}: MultiGroupProps) => { + const [checked, setChecked] = useState( + checkedProp || defaultValue || [] + ); + + useEffect(() => { + if (checkedProp !== undefined) { + setChecked(checkedProp); + } + }, [checkedProp]); + + const handleChange = useCallback( + (value: string) => { + if (onChange) { + onChange(value); + } else { + if (!checked.includes(value)) { + setChecked((prev) => [...prev, value]); + } else { + setChecked((prev) => prev.filter((item) => item !== value)); + } + } + }, + [checked, onChange] + ); + + const contextValue = useMemo( + () => ({ + name, + checked, + onChange: handleChange, + disabled, + }), + [name, checked, handleChange, disabled] + ); + + return ( + + + {children} + + + ); +}; + +export default MultiGroup; diff --git a/packages/wow-ui/src/components/Switch/index.tsx b/packages/wow-ui/src/components/Switch/index.tsx index aa987f92..f706918d 100644 --- a/packages/wow-ui/src/components/Switch/index.tsx +++ b/packages/wow-ui/src/components/Switch/index.tsx @@ -13,7 +13,8 @@ import useCheckedState from "@/hooks/useCheckedState"; * @param {boolean} [defaultChecked=false] 스위치가 처음에 활성화되어 있는지 여부. * @param {boolean} [disabled=false] 스위치가 비활성화되어 있는지 여부. * @param {boolean} [checked] 외부에서 제어할 활성 상태. - * @param {ReactNode} [text] 스위치 오른쪽에 들어갈 텍스트. + * @param {ReactNode} [label] 스위치 오른쪽에 들어갈 텍스트. + * @param {string} value 스위치 컴포넌트 값. * @param {() => void} [onChange] 외부 활성 상태가 변경될 때 호출되는 함수. * @param {() => void} [onClick] 스위치를 클릭했을 때 호출되는 함수. * @param {() => void} [onKeyDown] 스위치가 포커스됐을 때 엔터 키 또는 스페이스 바를 눌렀을 때 호출되는 함수. @@ -29,7 +30,8 @@ export interface SwitchProps { defaultChecked?: boolean; disabled?: boolean; checked?: boolean; - text?: ReactNode; + label?: ReactNode; + value: string; onChange?: () => void; onClick?: () => void; onKeyDown?: () => void; @@ -44,9 +46,10 @@ const Switch = forwardRef( ( { defaultChecked = false, - disabled = false, + disabled: disabledProp = false, checked: checkedProp, - text = "", + label = "", + value, onChange, onClick, onKeyDown, @@ -63,6 +66,7 @@ const Switch = forwardRef( const { checked, pressed, + disabled, handleClick, handleKeyDown, handleKeyUp, @@ -71,7 +75,8 @@ const Switch = forwardRef( } = useCheckedState({ defaultChecked, checked: checkedProp, - disabled, + disabled: disabledProp, + value, onChange, onClick, onKeyDown, @@ -102,11 +107,12 @@ const Switch = forwardRef( aria-label={inputProps?.["aria-label"] ?? "switch"} className={inputStyle()} type="checkbox" - onClick={handleClick} + value={value} + onClick={() => handleClick(value)} /> - {!!text && {text}} + {!!label && {label}} ); } diff --git a/packages/wow-ui/src/hooks/useCheckedState.ts b/packages/wow-ui/src/hooks/useCheckedState.ts index 5e32d7de..bf499295 100644 --- a/packages/wow-ui/src/hooks/useCheckedState.ts +++ b/packages/wow-ui/src/hooks/useCheckedState.ts @@ -1,10 +1,13 @@ import type { KeyboardEvent } from "react"; -import { useEffect, useState } from "react"; +import { useContext, useEffect, useState } from "react"; + +import MultiGroupContext from "@/components/MultiGroup/MultiGroupContext"; interface CheckedStateProps { defaultChecked?: boolean; checked?: boolean; disabled?: boolean; + value: string; onChange?: () => void; onClick?: () => void; onKeyDown?: () => void; @@ -13,21 +16,43 @@ interface CheckedStateProps { const useCheckedState = ({ defaultChecked = false, checked: checkedProp, - disabled, + disabled: disabledProp, + value, onChange, onClick, onKeyDown, }: CheckedStateProps) => { + const group = useContext(MultiGroupContext); + const groupOnChange = group.onChange; + const groupChecked = group.checked?.includes(value); + const disabled = group.disabled || disabledProp || false; + const [checked, setChecked] = useState( - checkedProp ? checkedProp : defaultChecked + groupChecked || checkedProp || defaultChecked ); const [pressed, setPressed] = useState(false); useEffect(() => { - if (checkedProp !== undefined) { + if (groupChecked !== undefined) { + setChecked(groupChecked); + } + }, [groupChecked]); + + useEffect(() => { + if (groupChecked === undefined && checkedProp !== undefined) { setChecked(checkedProp); } - }, [checkedProp]); + }, [checkedProp, groupChecked]); + + const toggleCheckedState = (value: string) => { + if (groupOnChange) { + if (!disabled) groupOnChange(value); + } else if (onChange) { + onChange(); + } else { + setChecked((prev) => !prev); + } + }; const handleMouseDown = () => { if (!disabled) setPressed(true); @@ -37,8 +62,8 @@ const useCheckedState = ({ if (!disabled) setPressed(false); }; - const handleClick = () => { - onChange ? onChange() : setChecked((prev) => !prev); + const handleClick = (value: string) => { + toggleCheckedState(value); onClick?.(); }; @@ -52,7 +77,7 @@ const useCheckedState = ({ if (event.key === "Enter" || event.key === " ") { event.preventDefault(); setPressed(true); - onChange ? onChange() : setChecked((prev) => !prev); + toggleCheckedState(value); onKeyDown?.(); } }; @@ -60,6 +85,7 @@ const useCheckedState = ({ return { checked, pressed, + disabled, handleClick, handleKeyDown, handleMouseDown, From 2d7d00f14c393cda3e1f849a9ae723c3a2de681e Mon Sep 17 00:00:00 2001 From: ghdtjgus76 Date: Thu, 13 Jun 2024 19:40:15 +0900 Subject: [PATCH 05/19] =?UTF-8?q?chore:=20Checkbox=20=EC=8A=A4=ED=86=A0?= =?UTF-8?q?=EB=A6=AC=20=EB=B0=8F=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/Checkbox/Checkbox.stories.tsx | 12 +++++++++++- .../wow-ui/src/components/Checkbox/Checkbox.test.tsx | 6 +++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/packages/wow-ui/src/components/Checkbox/Checkbox.stories.tsx b/packages/wow-ui/src/components/Checkbox/Checkbox.stories.tsx index 00d829bd..b69cb803 100644 --- a/packages/wow-ui/src/components/Checkbox/Checkbox.stories.tsx +++ b/packages/wow-ui/src/components/Checkbox/Checkbox.stories.tsx @@ -148,18 +148,21 @@ type Story = StoryObj; export const Default: Story = { args: { defaultChecked: false, + value: "checkbox", }, }; export const Checked: Story = { args: { defaultChecked: true, + value: "checkbox", }, }; export const Disabled: Story = { args: { disabled: true, + value: "checkbox", }, }; @@ -168,6 +171,7 @@ export const Vertical: Story = { checked: true, children: "string", position: "vertical", + value: "checkbox", }, }; @@ -176,6 +180,7 @@ export const Horizontal: Story = { checked: true, children: "string", position: "horizontal", + value: "checkbox", }, }; @@ -186,9 +191,14 @@ const ControlledCheckBox = () => { setIsChecked((prev) => !prev); }; - return ; + return ( + + ); }; export const ControlledState: Story = { render: () => , + args: { + value: "checkbox", + }, }; diff --git a/packages/wow-ui/src/components/Checkbox/Checkbox.test.tsx b/packages/wow-ui/src/components/Checkbox/Checkbox.test.tsx index 40aff07c..0cd5c543 100644 --- a/packages/wow-ui/src/components/Checkbox/Checkbox.test.tsx +++ b/packages/wow-ui/src/components/Checkbox/Checkbox.test.tsx @@ -7,7 +7,11 @@ import Checkbox from "./index"; describe("Checkbox component", () => { const renderCheckbox = (props: Partial = {}): RenderResult => { - return render(Text); + return render( + + Text + + ); }; test("toggles checked state when clicked", async () => { From 0716ad6c2bc97afcb6d62c79f74c397324124213 Mon Sep 17 00:00:00 2001 From: ghdtjgus76 Date: Thu, 13 Jun 2024 19:40:33 +0900 Subject: [PATCH 06/19] =?UTF-8?q?chore:=20Switch=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EC=8A=A4=ED=86=A0=EB=A6=AC=20=EB=B0=8F=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/Switch/Switch.stories.tsx | 18 +++++++++++++----- .../src/components/Switch/Switch.test.tsx | 16 ++++++++-------- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/packages/wow-ui/src/components/Switch/Switch.stories.tsx b/packages/wow-ui/src/components/Switch/Switch.stories.tsx index 4ca0a70f..f95014b9 100644 --- a/packages/wow-ui/src/components/Switch/Switch.stories.tsx +++ b/packages/wow-ui/src/components/Switch/Switch.stories.tsx @@ -41,14 +41,14 @@ const meta = { type: "boolean", }, }, - text: { + label: { description: "스위치 오른쪽에 들어갈 텍스트입니다.", table: { type: { summary: "string" }, defaultValue: { summary: null }, }, control: { - type: "text", + type: "label", }, }, onChange: { @@ -136,24 +136,29 @@ export default meta; type Story = StoryObj; export const Primary: Story = { - args: {}, + args: { + value: "switch", + }, }; export const DefaultChecked: Story = { args: { defaultChecked: true, + value: "switch", }, }; export const Disabled: Story = { args: { disabled: true, + value: "switch", }, }; export const WithText: Story = { args: { - text: "Text", + label: "Label", + value: "switch", }, }; @@ -164,9 +169,12 @@ const ControlledSwitch = () => { setChecked((prev) => !prev); }; - return ; + return ; }; export const ControlledState: Story = { render: () => , + args: { + value: "switch", + }, }; diff --git a/packages/wow-ui/src/components/Switch/Switch.test.tsx b/packages/wow-ui/src/components/Switch/Switch.test.tsx index 89682194..5504494f 100644 --- a/packages/wow-ui/src/components/Switch/Switch.test.tsx +++ b/packages/wow-ui/src/components/Switch/Switch.test.tsx @@ -7,7 +7,7 @@ describe("toggle", () => { let rendered: RenderResult; beforeEach(() => { - rendered = render(); + rendered = render(); }); it("should render with attributes aria-checked to be false, aria-disabled to be false by default", () => { @@ -17,8 +17,8 @@ describe("toggle", () => { expect(switchComponent).toHaveAttribute("aria-disabled", "false"); }); - it("should render text", () => { - expect(rendered.getByText("Text")).toBeInTheDocument(); + it("should render label", () => { + expect(rendered.getByText("Label")).toBeInTheDocument(); }); it("should toggle state when onClick event is fired", async () => { @@ -61,7 +61,7 @@ describe("when defaultChecked is true", () => { let rendered: RenderResult; beforeEach(() => { - rendered = render(); + rendered = render(); }); it("should render with attributes aria-checked to be true, aria-disabled to be false", () => { @@ -76,7 +76,7 @@ describe("disabled", () => { let rendered: RenderResult; beforeEach(() => { - rendered = render(); + rendered = render(); }); it("should render with attributes aria-disabled to be true", () => { @@ -129,7 +129,7 @@ describe("external control and events", () => { let rendered: RenderResult; it("should fire external onClick event", async () => { - rendered = render(); + rendered = render(); const switchComponent = rendered.getByRole("checkbox"); const onClickHandler = jest.fn(); switchComponent.onclick = onClickHandler; @@ -142,7 +142,7 @@ describe("external control and events", () => { }); it("should fire external onKeyDown event", async () => { - rendered = render(); + rendered = render(); const switchComponent = rendered.getByRole("checkbox"); const onKeyDownHandler = jest.fn(); switchComponent.onkeydown = onKeyDownHandler; @@ -159,7 +159,7 @@ describe("external control and events", () => { const handleChange = () => { checked = !checked; }; - const rendered = render(); + const rendered = render(); const switchComponent = rendered.getByRole("checkbox"); switchComponent.onchange = handleChange; From 060ce5e4f2fae855212b2681a95aaac567f935dc Mon Sep 17 00:00:00 2001 From: ghdtjgus76 Date: Thu, 13 Jun 2024 19:40:51 +0900 Subject: [PATCH 07/19] =?UTF-8?q?feat:=20MultiGroup=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EB=B9=8C=EB=93=9C=20=EC=84=A4=EC=A0=95=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/wow-ui/package.json | 5 +++++ packages/wow-ui/rollup.config.js | 1 + 2 files changed, 6 insertions(+) diff --git a/packages/wow-ui/package.json b/packages/wow-ui/package.json index 18e4d638..2db443e3 100644 --- a/packages/wow-ui/package.json +++ b/packages/wow-ui/package.json @@ -40,6 +40,11 @@ "require": "./dist/RadioGroup.cjs", "import": "./dist/RadioGroup.js" }, + "./MultiGroup": { + "types": "./dist/components/MultiGroup/index.d.ts", + "require": "./dist/MultiGroup.cjs", + "import": "./dist/MultiGroup.js" + }, "./Chip": { "types": "./dist/components/Chip/index.d.ts", "require": "./dist/Chip.cjs", diff --git a/packages/wow-ui/rollup.config.js b/packages/wow-ui/rollup.config.js index 88b998ba..9f2fce16 100644 --- a/packages/wow-ui/rollup.config.js +++ b/packages/wow-ui/rollup.config.js @@ -23,6 +23,7 @@ export default { Switch: "./src/components/Switch", RadioButton: "./src/components/RadioGroup/RadioButton", RadioGroup: "./src/components/RadioGroup/RadioGroup", + MultiGroup: "./src/components/MultiGroup", Chip: "./src/components/Chip", Checkbox: "./src/components/Checkbox", Button: "./src/components/Button", From 8c091a73fb6427e2573c9a763b3ab9fac51fcb98 Mon Sep 17 00:00:00 2001 From: ghdtjgus76 Date: Thu, 13 Jun 2024 19:41:06 +0900 Subject: [PATCH 08/19] =?UTF-8?q?feat:=20docs=EC=97=90=20MultiGroup=20?= =?UTF-8?q?=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wow-docs/app/page.tsx | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/apps/wow-docs/app/page.tsx b/apps/wow-docs/app/page.tsx index 41023bfb..2ea73d92 100644 --- a/apps/wow-docs/app/page.tsx +++ b/apps/wow-docs/app/page.tsx @@ -1,5 +1,6 @@ import Checkbox from "wowds-ui/Checkbox"; import Chip from "wowds-ui/Chip"; +import MultiGroup from "wowds-ui/MultiGroup"; import RadioButton from "wowds-ui/RadioButton"; import RadioGroup from "wowds-ui/RadioGroup"; import Switch from "wowds-ui/Switch"; @@ -7,13 +8,25 @@ import Switch from "wowds-ui/Switch"; const Home = () => { return ( <> - + - + + + + + + + + + + + + + ); }; From cb7fd70464a85f116f63934ea039ab3591a42613 Mon Sep 17 00:00:00 2001 From: ghdtjgus76 Date: Fri, 14 Jun 2024 01:29:33 +0900 Subject: [PATCH 09/19] =?UTF-8?q?comment:=20jsdoc=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MultiGroup/MultiGroupContext.ts | 29 ++++++++++++++----- .../src/components/MultiGroup/index.tsx | 16 +++++++++- 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/packages/wow-ui/src/components/MultiGroup/MultiGroupContext.ts b/packages/wow-ui/src/components/MultiGroup/MultiGroupContext.ts index 5c92e909..e25325c2 100644 --- a/packages/wow-ui/src/components/MultiGroup/MultiGroupContext.ts +++ b/packages/wow-ui/src/components/MultiGroup/MultiGroupContext.ts @@ -1,17 +1,30 @@ import { createContext } from "react"; -export type MultiGroupContextProps = { - name?: string; - checked?: string[]; - onChange?: (value: string) => void; - disabled?: boolean; -}; +import type { MultiGroupProps, VariantType } from "@/components/MultiGroup"; + +/** + * @description 여러 체크박스 또는 스위치 컴포넌트 사이 공유되는 MultiGroupContext의 속성을 정의합니다. + * + * @template T 체크박스 또는 스위치 타입. + * + * @param {string} [name] 그룹명. + * @param {string[]} [checked] 외부에서 제어할 활성 상태. + * @param {(value: string) => void} [onChange] 외부 활성 상태가 변경될 때 호출되는 함수. + * @param {boolean} [disabled] 그룹 내 모든 체크박스 또는 스위치가 비활성화되어 있는지 여부. + */ +export type MultiGroupContextProps = Pick< + MultiGroupProps, + "name" | "checked" | "onChange" | "disabled" +>; -const MultiGroupContext = createContext({ +const defaultContextValue: MultiGroupContextProps = { name: "MultiGroupName", checked: undefined, onChange: undefined, disabled: false, -}); +}; + +const MultiGroupContext = + createContext>(defaultContextValue); export default MultiGroupContext; diff --git a/packages/wow-ui/src/components/MultiGroup/index.tsx b/packages/wow-ui/src/components/MultiGroup/index.tsx index 192442e0..5471fd60 100644 --- a/packages/wow-ui/src/components/MultiGroup/index.tsx +++ b/packages/wow-ui/src/components/MultiGroup/index.tsx @@ -10,7 +10,21 @@ export type VariantType = "checkbox" | "switch"; type PositionType = "vertical" | "horizontal"; /** - * @description 여러 체크박스, 또는 스위치 컴포넌트를 하나의 그룹으로 묶는 컴포넌트입니다. + * @description 여러 체크박스 또는 스위치 컴포넌트를 하나의 그룹으로 묶는 컴포넌트입니다. + * + * @template T 체크박스 또는 스위치 타입. + * + * @param {T} variant 체크박스 또는 스위치 타입. + * @param {T extends "checkbox" ? PositionType : undefined} [position] 체크박스 그룹의 방향. (가로 또는 세로). + * @param {T extends "checkbox" ? number : undefined} [gap] 체크박스 사이의 간격. + * @param {ReactNode} children 그룹 내에 포함될 체크박스 또는 스위치 컴포넌트들. + * @param {string} [name] 그룹명. + * @param {string[]} [defaultValue] 기본으로 선택된 값들의 배열. + * @param {string[]} [checked] 외부에서 제어할 활성 상태. + * @param {(value: string) => void} [onChange] 외부 활성 상태가 변경될 때 호출되는 함수. + * @param {boolean} [disabled] 그룹 내 모든 체크박스 또는 스위치가 비활성화되어 있는지 여부. + * @param {string} [className] 그룹에 전달하는 커스텀 클래스. + * @param {CSSProperties} [style] 그룹의 커스텀 스타일. */ export interface MultiGroupProps { variant: T; From 1a34c54feed5359a9ca48cfdd67f286e6e9fbe37 Mon Sep 17 00:00:00 2001 From: ghdtjgus76 Date: Fri, 14 Jun 2024 02:40:15 +0900 Subject: [PATCH 10/19] =?UTF-8?q?docs:=20=EC=8A=A4=ED=86=A0=EB=A6=AC=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EB=AC=B8=EC=84=9C=ED=99=94=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MultiGroup/MultiGroup.stories.tsx | 171 +++++++++++------- 1 file changed, 109 insertions(+), 62 deletions(-) diff --git a/packages/wow-ui/src/components/MultiGroup/MultiGroup.stories.tsx b/packages/wow-ui/src/components/MultiGroup/MultiGroup.stories.tsx index 63d3d14c..72f2c529 100644 --- a/packages/wow-ui/src/components/MultiGroup/MultiGroup.stories.tsx +++ b/packages/wow-ui/src/components/MultiGroup/MultiGroup.stories.tsx @@ -10,76 +10,123 @@ const meta = { component: MultiGroup, tags: ["autodocs"], parameters: { - // 컴포넌트 부제목 작성 ex. 버튼 컴포넌트 componentSubtitle: "멀티 그룹 컴포넌트", - // 색 대조 관련 웹 접근성 테스트 불가하게 하는 속성 - // 필요 없으면 지우기 - //a11y: { - // config: { - // rules: [{ id: "color-contrast", enabled: false }], - // }, - //}, }, - // argTypes: { - // props1: { - // description: "props1은 default 값이 있는 필수적인 속성입니다.", - // table: { - // // type, 필수가 아닐 경우 required 삭제 - // type: { summary: "string", required: true }, - // // defaultValue (Optional) - // defaultValue: { summary: "기본 값" }, - // }, - // control: { - // // Args Table에서 사용자가 조작할 수 있는 타입 - // // select일 경우 options를 배열로 제공 - // // 그 외 text, number, boolean, color, check, radio 등 존재 - // // control 불가능하게 하고 싶을 경우 객체가 아니라 false로 설정 - // // ex. control: false, - // // 참고 : https://storybook.js.org/docs/react/essentials/controls - // type: "select", - // options: ["option1", "option2"], - // }, - // }, - // }, - // decorators (Optional) - // 공통적으로 적용하고 싶은 컴포넌트 설정 - // decorators: [(Story) => <>{Story()}], + argTypes: { + variant: { + description: + "체크박스 또는 스위치 타입입니다. 'checkbox' 또는 'switch' 값을 가집니다.", + table: { + type: { summary: "checkbox | switch", required: true }, + defaultValue: { summary: null }, + }, + control: "radio", + options: ["checkbox", "switch"], + }, + position: { + description: + "체크박스 그룹의 방향입니다. 'vertical' 또는 'horizontal' 값을 가집니다.", + table: { + type: { summary: "vertical | horizontal" }, + defaultValue: { summary: null }, + }, + control: "radio", + options: ["vertical", "horizontal"], + if: { arg: "variant", eq: "checkbox" }, + }, + gap: { + description: "체크박스 사이의 간격입니다.", + table: { + type: { summary: "number" }, + defaultValue: { summary: null }, + }, + control: { + type: "number", + }, + if: { arg: "variant", eq: "checkbox" }, + }, + children: { + description: "그룹 내에 포함될 체크박스 또는 스위치 컴포넌트들입니다.", + table: { + type: { summary: "ReactNode", required: true }, + defaultValue: { summary: null }, + }, + control: false, + }, + name: { + description: "그룹명입니다.", + table: { + type: { summary: "string" }, + defaultValue: { summary: null }, + }, + control: { + type: "text", + }, + }, + defaultValue: { + description: "기본으로 선택된 값들의 배열입니다.", + table: { + type: { summary: "string[]" }, + defaultValue: { summary: null }, + }, + control: { + type: "array", + }, + }, + checked: { + description: "외부에서 제어할 활성 상태입니다.", + table: { + type: { summary: "string[]" }, + defaultValue: { summary: null }, + }, + control: { + type: "array", + }, + }, + onChange: { + description: "외부 활성 상태가 변경될 때 호출되는 함수입니다.", + table: { + type: { summary: "(value: string) => void" }, + defaultValue: { summary: null }, + }, + control: false, + }, + disabled: { + description: + "그룹 내 모든 체크박스 또는 스위치가 비활성화되어 있는지 여부입니다.", + table: { + type: { summary: "boolean" }, + defaultValue: { summary: false }, + }, + control: { + type: "boolean", + }, + }, + className: { + description: "그룹에 전달하는 커스텀 클래스입니다.", + table: { + type: { summary: "string" }, + defaultValue: { summary: null }, + }, + control: { + type: "text", + }, + }, + style: { + description: "그룹의 커스텀 스타일입니다.", + table: { + type: { summary: "CSSProperties" }, + defaultValue: { summary: null }, + }, + control: false, + }, + }, } satisfies Meta; export default meta; type Story = StoryObj; -export const VerticalCheckboxMultiGroup: Story = { - render: () => ( - - - - - - - ), - args: { - children: <>, - variant: "checkbox", - }, -}; - -export const HorizontalCheckboxMultiGroup: Story = { - render: () => ( - - - - - - - ), - args: { - children: <>, - variant: "checkbox", - }, -}; - export const TextWithVerticalCheckboxMultiGroup: Story = { render: () => ( From 07d2b082baa8715863b20281182ac017524efe42 Mon Sep 17 00:00:00 2001 From: ghdtjgus76 Date: Fri, 14 Jun 2024 13:05:37 +0900 Subject: [PATCH 11/19] =?UTF-8?q?test:=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/MultiGroup/MultiGroup.test.tsx | 297 ++++++++++++++++++ 1 file changed, 297 insertions(+) create mode 100644 packages/wow-ui/src/components/MultiGroup/MultiGroup.test.tsx diff --git a/packages/wow-ui/src/components/MultiGroup/MultiGroup.test.tsx b/packages/wow-ui/src/components/MultiGroup/MultiGroup.test.tsx new file mode 100644 index 00000000..2bde8262 --- /dev/null +++ b/packages/wow-ui/src/components/MultiGroup/MultiGroup.test.tsx @@ -0,0 +1,297 @@ +import { render, screen, waitFor } from "@testing-library/react"; +import fireEvent from "@testing-library/user-event"; +import { useState } from "react"; + +import Checkbox from "@/components/Checkbox"; +import MultiGroup from "@/components/MultiGroup"; +import Switch from "@/components/Switch"; + +describe("multi group with checkbox", () => { + it("should render checkbox components with children", () => { + render( + + + + + + ); + const checkbox1 = screen.getByText("checkbox1"); + const checkbox2 = screen.getByText("checkbox1"); + const checkbox3 = screen.getByText("checkbox1"); + + expect(checkbox1).toBeInTheDocument(); + expect(checkbox2).toBeInTheDocument(); + expect(checkbox3).toBeInTheDocument(); + }); + + it("should render checkbox components with its own state", () => { + const rendered = render( + + + + + + ); + const checkboxes = rendered.getAllByRole("checkbox"); + + expect(checkboxes[0]).toHaveAttribute("aria-disabled", "false"); + expect(checkboxes[1]).toHaveAttribute("aria-disabled", "true"); + expect(checkboxes[2]).toHaveAttribute("aria-disabled", "false"); + + expect(checkboxes[0]).toHaveAttribute("aria-checked", "false"); + expect(checkboxes[1]).toHaveAttribute("aria-checked", "false"); + expect(checkboxes[2]).toHaveAttribute("aria-checked", "false"); + }); + + it("should render checkbox components with external value provided", async () => { + const rendered = render( + + + + + + ); + const checkboxes = rendered.getAllByRole("checkbox"); + + expect(checkboxes[0]).toHaveAttribute("aria-checked", "false"); + expect(checkboxes[1]).toHaveAttribute("aria-checked", "true"); + expect(checkboxes[2]).toHaveAttribute("aria-checked", "true"); + }); + + it("should render checkbox components with default value provided", () => { + const rendered = render( + + + + + + ); + const checkboxes = rendered.getAllByRole("checkbox"); + + expect(checkboxes[0]).toHaveAttribute("aria-checked", "true"); + expect(checkboxes[1]).toHaveAttribute("aria-checked", "false"); + expect(checkboxes[2]).toHaveAttribute("aria-checked", "true"); + }); + + it("should toggle state when onChange event fired", async () => { + const Component = () => { + const [checked, setChecked] = useState(["checkbox3"]); + const handleChange = (value: string) => { + if (!checked.includes(value)) { + setChecked((prev) => [...prev, value]); + } else { + setChecked((prev) => prev.filter((item) => item !== value)); + } + }; + + return ( + + + + + + ); + }; + const rendered = render(); + const checkboxes = rendered.getAllByRole("checkbox"); + + fireEvent.click(checkboxes[0]!); + fireEvent.click(checkboxes[1]!); + fireEvent.click(checkboxes[2]!); + + await waitFor(() => { + expect(checkboxes[0]).toHaveAttribute("aria-checked", "true"); + expect(checkboxes[1]).toHaveAttribute("aria-checked", "true"); + expect(checkboxes[2]).toHaveAttribute("aria-checked", "false"); + }); + }); + + it("should not toggle state when toggle is disabled", async () => { + const Component = () => { + const [checked, setChecked] = useState(["checkbox3"]); + const handleChange = (value: string) => { + if (!checked.includes(value)) { + setChecked((prev) => [...prev, value]); + } else { + setChecked((prev) => prev.filter((item) => item !== value)); + } + }; + + return ( + + + + + + ); + }; + const rendered = render(); + const checkboxes = rendered.getAllByRole("checkbox"); + + fireEvent.click(checkboxes[0]!); + fireEvent.click(checkboxes[1]!); + fireEvent.click(checkboxes[2]!); + + await waitFor(() => { + expect(checkboxes[0]).toHaveAttribute("aria-disabled", "true"); + expect(checkboxes[1]).toHaveAttribute("aria-disabled", "true"); + expect(checkboxes[2]).toHaveAttribute("aria-disabled", "true"); + + expect(checkboxes[0]).toHaveAttribute("aria-checked", "false"); + expect(checkboxes[1]).toHaveAttribute("aria-checked", "false"); + expect(checkboxes[2]).toHaveAttribute("aria-checked", "true"); + }); + }); +}); + +describe("multi group with switch", () => { + it("should render switch components with children", () => { + render( + + + + + + ); + const switch1 = screen.getByText("switch1"); + const switch2 = screen.getByText("switch2"); + const switch3 = screen.getByText("switch3"); + + expect(switch1).toBeInTheDocument(); + expect(switch2).toBeInTheDocument(); + expect(switch3).toBeInTheDocument(); + }); + + it("should render switch components with its own state", () => { + const rendered = render( + + + + + + ); + const switches = rendered.getAllByRole("checkbox"); + + expect(switches[0]).toHaveAttribute("aria-disabled", "false"); + expect(switches[1]).toHaveAttribute("aria-disabled", "true"); + expect(switches[2]).toHaveAttribute("aria-disabled", "false"); + + expect(switches[0]).toHaveAttribute("aria-checked", "false"); + expect(switches[1]).toHaveAttribute("aria-checked", "false"); + expect(switches[2]).toHaveAttribute("aria-checked", "false"); + }); + + it("should render switch components with external value provided", async () => { + const rendered = render( + + + + + + ); + const switches = rendered.getAllByRole("checkbox"); + + expect(switches[0]).toHaveAttribute("aria-checked", "false"); + expect(switches[1]).toHaveAttribute("aria-checked", "true"); + expect(switches[2]).toHaveAttribute("aria-checked", "true"); + }); + + it("should render switch components with default value provided", () => { + const rendered = render( + + + + + + ); + const switches = rendered.getAllByRole("checkbox"); + + expect(switches[0]).toHaveAttribute("aria-checked", "true"); + expect(switches[1]).toHaveAttribute("aria-checked", "false"); + expect(switches[2]).toHaveAttribute("aria-checked", "true"); + }); + + it("should toggle state when onChange event fired", async () => { + const Component = () => { + const [checked, setChecked] = useState(["switch3"]); + const handleChange = (value: string) => { + if (!checked.includes(value)) { + setChecked((prev) => [...prev, value]); + } else { + setChecked((prev) => prev.filter((item) => item !== value)); + } + }; + + return ( + + + + + + ); + }; + const rendered = render(); + const switches = rendered.getAllByRole("checkbox"); + + fireEvent.click(switches[0]!); + fireEvent.click(switches[1]!); + fireEvent.click(switches[2]!); + + await waitFor(() => { + expect(switches[0]).toHaveAttribute("aria-checked", "true"); + expect(switches[1]).toHaveAttribute("aria-checked", "true"); + expect(switches[2]).toHaveAttribute("aria-checked", "false"); + }); + }); + + it("should not toggle state when toggle is disabled", async () => { + const Component = () => { + const [checked, setChecked] = useState(["switch3"]); + const handleChange = (value: string) => { + if (!checked.includes(value)) { + setChecked((prev) => [...prev, value]); + } else { + setChecked((prev) => prev.filter((item) => item !== value)); + } + }; + + return ( + + + + + + ); + }; + const rendered = render(); + const switches = rendered.getAllByRole("checkbox"); + + fireEvent.click(switches[0]!); + fireEvent.click(switches[1]!); + fireEvent.click(switches[2]!); + + await waitFor(() => { + expect(switches[0]).toHaveAttribute("aria-disabled", "true"); + expect(switches[1]).toHaveAttribute("aria-disabled", "true"); + expect(switches[2]).toHaveAttribute("aria-disabled", "true"); + + expect(switches[0]).toHaveAttribute("aria-checked", "false"); + expect(switches[1]).toHaveAttribute("aria-checked", "false"); + expect(switches[2]).toHaveAttribute("aria-checked", "true"); + }); + }); +}); From eeb3f7bd3f293961f2d8a49be0a0a3341291beb5 Mon Sep 17 00:00:00 2001 From: ghdtjgus76 Date: Fri, 14 Jun 2024 13:07:30 +0900 Subject: [PATCH 12/19] =?UTF-8?q?chore:=20=EC=9E=90=EC=9E=98=ED=95=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=EC=82=AC=ED=95=AD=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/wow-ui/src/components/MultiGroup/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/wow-ui/src/components/MultiGroup/index.tsx b/packages/wow-ui/src/components/MultiGroup/index.tsx index 5471fd60..ea85e7aa 100644 --- a/packages/wow-ui/src/components/MultiGroup/index.tsx +++ b/packages/wow-ui/src/components/MultiGroup/index.tsx @@ -42,7 +42,7 @@ export interface MultiGroupProps { const MultiGroup = ({ position, - gap: gapProp, + gap, children, name, defaultValue, @@ -89,7 +89,7 @@ const MultiGroup = ({ return ( From 688c4f5accf93b0b222131f7242f392103e8bdc3 Mon Sep 17 00:00:00 2001 From: ghdtjgus76 Date: Mon, 17 Jun 2024 13:08:35 +0900 Subject: [PATCH 13/19] =?UTF-8?q?chore:=20useCheckedState=20=ED=9B=85?= =?UTF-8?q?=EC=97=90=EC=84=9C=20disabled=20=EC=83=81=ED=83=9C=20=EA=B3=A0?= =?UTF-8?q?=EB=A0=A4=ED=95=A0=20=EC=88=98=20=EC=9E=88=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/wow-ui/src/hooks/useCheckedState.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/wow-ui/src/hooks/useCheckedState.ts b/packages/wow-ui/src/hooks/useCheckedState.ts index bf499295..66fafa62 100644 --- a/packages/wow-ui/src/hooks/useCheckedState.ts +++ b/packages/wow-ui/src/hooks/useCheckedState.ts @@ -45,8 +45,10 @@ const useCheckedState = ({ }, [checkedProp, groupChecked]); const toggleCheckedState = (value: string) => { + if (disabled) return; + if (groupOnChange) { - if (!disabled) groupOnChange(value); + groupOnChange(value); } else if (onChange) { onChange(); } else { From b46863e03e5ab12490813b87cb929fc1ccc9106f Mon Sep 17 00:00:00 2001 From: ghdtjgus76 Date: Mon, 17 Jun 2024 13:13:40 +0900 Subject: [PATCH 14/19] =?UTF-8?q?chore:=20group=20context=20=EC=83=81?= =?UTF-8?q?=ED=83=9C=20=EA=B5=AC=EC=A1=B0=20=EB=B6=84=ED=95=B4=20=ED=95=A0?= =?UTF-8?q?=EB=8B=B9=EC=9C=BC=EB=A1=9C=20=EA=B0=80=EC=A0=B8=EC=98=A4?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/wow-ui/src/hooks/useCheckedState.ts | 24 ++++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/packages/wow-ui/src/hooks/useCheckedState.ts b/packages/wow-ui/src/hooks/useCheckedState.ts index 66fafa62..83229755 100644 --- a/packages/wow-ui/src/hooks/useCheckedState.ts +++ b/packages/wow-ui/src/hooks/useCheckedState.ts @@ -22,27 +22,31 @@ const useCheckedState = ({ onClick, onKeyDown, }: CheckedStateProps) => { - const group = useContext(MultiGroupContext); - const groupOnChange = group.onChange; - const groupChecked = group.checked?.includes(value); - const disabled = group.disabled || disabledProp || false; + const { + onChange: groupOnChange, + checked: groupCheckedValues, + disabled: groupDisabled, + } = useContext(MultiGroupContext); + + const groupCheckedValue = groupCheckedValues?.includes(value); + const disabled = groupDisabled || disabledProp || false; const [checked, setChecked] = useState( - groupChecked || checkedProp || defaultChecked + groupCheckedValue || checkedProp || defaultChecked ); const [pressed, setPressed] = useState(false); useEffect(() => { - if (groupChecked !== undefined) { - setChecked(groupChecked); + if (groupCheckedValue !== undefined) { + setChecked(groupCheckedValue); } - }, [groupChecked]); + }, [groupCheckedValue]); useEffect(() => { - if (groupChecked === undefined && checkedProp !== undefined) { + if (groupCheckedValue === undefined && checkedProp !== undefined) { setChecked(checkedProp); } - }, [checkedProp, groupChecked]); + }, [checkedProp, groupCheckedValue]); const toggleCheckedState = (value: string) => { if (disabled) return; From 2ee9814fbfac7510aeb9cef7c26bfc078299edf1 Mon Sep 17 00:00:00 2001 From: ghdtjgus76 Date: Mon, 17 Jun 2024 13:14:42 +0900 Subject: [PATCH 15/19] =?UTF-8?q?chore:=20MultiGroup=20=EC=9E=98=EB=AA=BB?= =?UTF-8?q?=EB=90=9C=20variant=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wow-docs/app/page.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/wow-docs/app/page.tsx b/apps/wow-docs/app/page.tsx index 2ea73d92..14f502cf 100644 --- a/apps/wow-docs/app/page.tsx +++ b/apps/wow-docs/app/page.tsx @@ -21,7 +21,7 @@ const Home = () => { - + From da36e9ed85619c1c42b33529b02b2c02846dc406 Mon Sep 17 00:00:00 2001 From: ghdtjgus76 Date: Mon, 17 Jun 2024 13:16:52 +0900 Subject: [PATCH 16/19] =?UTF-8?q?chore:=20=EC=83=81=ED=83=9C=EA=B0=92=20?= =?UTF-8?q?=EB=84=A4=EC=9D=B4=EB=B0=8D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/wow-ui/src/hooks/useCheckedState.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/wow-ui/src/hooks/useCheckedState.ts b/packages/wow-ui/src/hooks/useCheckedState.ts index 83229755..d85a8f99 100644 --- a/packages/wow-ui/src/hooks/useCheckedState.ts +++ b/packages/wow-ui/src/hooks/useCheckedState.ts @@ -31,20 +31,20 @@ const useCheckedState = ({ const groupCheckedValue = groupCheckedValues?.includes(value); const disabled = groupDisabled || disabledProp || false; - const [checked, setChecked] = useState( + const [checkedValue, setCheckedValue] = useState( groupCheckedValue || checkedProp || defaultChecked ); const [pressed, setPressed] = useState(false); useEffect(() => { if (groupCheckedValue !== undefined) { - setChecked(groupCheckedValue); + setCheckedValue(groupCheckedValue); } }, [groupCheckedValue]); useEffect(() => { if (groupCheckedValue === undefined && checkedProp !== undefined) { - setChecked(checkedProp); + setCheckedValue(checkedProp); } }, [checkedProp, groupCheckedValue]); @@ -56,7 +56,7 @@ const useCheckedState = ({ } else if (onChange) { onChange(); } else { - setChecked((prev) => !prev); + setCheckedValue((prev) => !prev); } }; @@ -89,7 +89,7 @@ const useCheckedState = ({ }; return { - checked, + checked: checkedValue, pressed, disabled, handleClick, From ef8e679279637aeb3c963f7d94fd09db695dde80 Mon Sep 17 00:00:00 2001 From: ghdtjgus76 Date: Mon, 17 Jun 2024 13:25:17 +0900 Subject: [PATCH 17/19] =?UTF-8?q?chore:=20=EA=B0=9C=EB=B3=84=20=EC=8A=A4?= =?UTF-8?q?=EC=9C=84=EC=B9=98,=20=EC=B2=B4=ED=81=AC=EB=B0=95=EC=8A=A4?= =?UTF-8?q?=EC=97=90=EB=8A=94=20value=20default=20=EA=B0=92=20=EC=A7=80?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/wow-ui/src/components/Checkbox/index.tsx | 4 ++-- packages/wow-ui/src/components/Switch/index.tsx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/wow-ui/src/components/Checkbox/index.tsx b/packages/wow-ui/src/components/Checkbox/index.tsx index c21af3c0..48d5bf1a 100644 --- a/packages/wow-ui/src/components/Checkbox/index.tsx +++ b/packages/wow-ui/src/components/Checkbox/index.tsx @@ -36,7 +36,7 @@ export interface CheckboxProps extends PropsWithChildren { defaultChecked?: boolean; disabled?: boolean; checked?: boolean; - value: string; + value?: string; onChange?: () => void; onClick?: () => void; onKeyDown?: () => void; @@ -54,7 +54,7 @@ const Checkbox = forwardRef( defaultChecked = false, disabled: disabledProp = false, checked: checkedProp, - value, + value = "checkbox", onClick, onChange, children, diff --git a/packages/wow-ui/src/components/Switch/index.tsx b/packages/wow-ui/src/components/Switch/index.tsx index f706918d..54583d88 100644 --- a/packages/wow-ui/src/components/Switch/index.tsx +++ b/packages/wow-ui/src/components/Switch/index.tsx @@ -31,7 +31,7 @@ export interface SwitchProps { disabled?: boolean; checked?: boolean; label?: ReactNode; - value: string; + value?: string; onChange?: () => void; onClick?: () => void; onKeyDown?: () => void; @@ -49,7 +49,7 @@ const Switch = forwardRef( disabled: disabledProp = false, checked: checkedProp, label = "", - value, + value = "switch", onChange, onClick, onKeyDown, From d43be0786317c28dd58819ae939cfeb3295cbce9 Mon Sep 17 00:00:00 2001 From: ghdtjgus76 Date: Sat, 22 Jun 2024 19:02:35 +0900 Subject: [PATCH 18/19] =?UTF-8?q?comment:=20gap,=20position=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20=EC=A3=BC=EC=84=9D=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wow-ui/src/components/MultiGroup/MultiGroup.stories.tsx | 5 +++-- packages/wow-ui/src/components/MultiGroup/index.tsx | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/wow-ui/src/components/MultiGroup/MultiGroup.stories.tsx b/packages/wow-ui/src/components/MultiGroup/MultiGroup.stories.tsx index 72f2c529..4b5279dd 100644 --- a/packages/wow-ui/src/components/MultiGroup/MultiGroup.stories.tsx +++ b/packages/wow-ui/src/components/MultiGroup/MultiGroup.stories.tsx @@ -25,7 +25,7 @@ const meta = { }, position: { description: - "체크박스 그룹의 방향입니다. 'vertical' 또는 'horizontal' 값을 가집니다.", + "체크박스 그룹의 방향입니다. 'vertical' 또는 'horizontal' 값을 가집니다. variant가 switch인 경우 사용할 수 없습니다.", table: { type: { summary: "vertical | horizontal" }, defaultValue: { summary: null }, @@ -35,7 +35,8 @@ const meta = { if: { arg: "variant", eq: "checkbox" }, }, gap: { - description: "체크박스 사이의 간격입니다.", + description: + "체크박스 사이의 간격입니다. variant가 switch인 경우 사용할 수 없습니다.", table: { type: { summary: "number" }, defaultValue: { summary: null }, diff --git a/packages/wow-ui/src/components/MultiGroup/index.tsx b/packages/wow-ui/src/components/MultiGroup/index.tsx index ea85e7aa..9d7ac130 100644 --- a/packages/wow-ui/src/components/MultiGroup/index.tsx +++ b/packages/wow-ui/src/components/MultiGroup/index.tsx @@ -16,7 +16,9 @@ type PositionType = "vertical" | "horizontal"; * * @param {T} variant 체크박스 또는 스위치 타입. * @param {T extends "checkbox" ? PositionType : undefined} [position] 체크박스 그룹의 방향. (가로 또는 세로). + * @throws {Error} position은 variant가 "switch"인 경우 사용할 수 없습니다. * @param {T extends "checkbox" ? number : undefined} [gap] 체크박스 사이의 간격. + * @throws {Error} gap은 variant가 "switch"인 경우 사용할 수 없습니다. * @param {ReactNode} children 그룹 내에 포함될 체크박스 또는 스위치 컴포넌트들. * @param {string} [name] 그룹명. * @param {string[]} [defaultValue] 기본으로 선택된 값들의 배열. From 38e12d32859073c506b3670cc9b0c7f4eb230a43 Mon Sep 17 00:00:00 2001 From: ghdtjgus76 Date: Mon, 24 Jun 2024 00:35:23 +0900 Subject: [PATCH 19/19] =?UTF-8?q?fix:=20=EB=A8=B8=EC=A7=80=20=EC=97=90?= =?UTF-8?q?=EB=9F=AC=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/wow-ui/src/components/Switch/index.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/wow-ui/src/components/Switch/index.tsx b/packages/wow-ui/src/components/Switch/index.tsx index 1405db6d..e8be8ee1 100644 --- a/packages/wow-ui/src/components/Switch/index.tsx +++ b/packages/wow-ui/src/components/Switch/index.tsx @@ -98,8 +98,6 @@ const Switch = forwardRef( onMouseUp={handleMouseUp} >