Feat(design-system): Dropdown 구현#61
Conversation
Walkthrough새로운 Dropdown React 컴포넌트를 추가하고 Storybook 스토리를 작성했으며, Icon 컴포넌트에 회전 애니메이션 옵션을 추가하고, 컴포넌트 인덱스에 재-exports 및 패키지의 TypeScript 설정에 Changes
Sequence Diagram(s)sequenceDiagram
actor 사용자 as U
participant Dropdown as D
participant 부모 as P
Note over D: 트리거 버튼(아이콘 포함)
U->>D: 트리거 클릭
D->>D: isOpen 토글 (아이콘 회전: hasRotateAnimation 시 애니메이션)
alt 옵션 선택
U->>D: 옵션 클릭
D->>P: onChange(selected)
D->>D: isOpen = false
else 아이템 추가 (onAddItem 제공 && limit 미도달)
U->>D: "Add item" 클릭
D->>P: onAddItem()
D->>D: isOpen = false
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Pre-merge checks (2 passed, 3 warnings)❌ Failed checks (3 warnings)
✅ Passed checks (2 passed)
Poem
Tip 👮 Agentic pre-merge checks are now available in preview!Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.
Example: reviews:
pre_merge_checks:
custom_checks:
- name: "Undocumented Breaking Changes"
mode: "warning"
instructions: |
Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).Please share your feedback with us on this Discord post. ✨ Finishing Touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
✅ Storybook chromatic 배포 확인: |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (5)
packages/design-system/tsconfig.json (1)
5-5: check-types 전용 tsconfig 분리
- packages/design-system/tsconfig.build.json 생성:
{ "extends": "./tsconfig.json", "compilerOptions": { "rootDir": "src", "outDir": "dist" }, "include": ["src"], "exclude": ["**/*.stories.*", "**/__tests__/**", ".storybook", "turbo"] }- package.json의 check-types 스크립트를
tsc -p tsconfig.build.json으로 변경 (build 스크립트(vite build)는 그대로 유지)- (선택) package.json의 files에
"dist"만 포함하거나 .npmignore를 활용해 .storybook·turbo 등 불필요 파일 배포 제외packages/design-system/src/components/dropdown/Dropdown.tsx (4)
40-41: 고정 폭 제거로 재사용성 향상(w-[24.8rem] → w-full).
현재 고정 폭 때문에 className로 폭 제어가 어렵습니다. 부모 컨테이너의 폭을 따르도록 내부도 w-full 권장.- className={`body4-r flex h-[4.4rem] w-[24.8rem] items-center justify-between rounded-[4px] border px-[0.8rem] py-[1.2rem] transition-colors duration-200 ${isOpen ? 'border-main500' : 'border-gray200'}`} + className={`body4-r flex h-[4.4rem] w-full items-center justify-between rounded-[4px] border px-[0.8rem] py-[1.2rem] transition-colors duration-200 ${isOpen ? 'border-main500' : 'border-gray200'}`} @@ - <div className="common-shadow absolute z-10 mt-[1.5rem] w-[24.8rem] rounded-[0.4rem] bg-white"> + <div className="common-shadow absolute z-10 mt-[1.5rem] w-full rounded-[0.4rem] bg-white">Also applies to: 56-56
7-8: onChange 타입과 구현 불일치: null을 보낼 길이 없습니다.
현재 컴포넌트 내부에서는 null을 전달하지 않으므로 API를 간결하게 하거나(추천) clear 동작을 설계해야 합니다.옵션 A(간결화, 권장): onChange에서 null 제거
- onChange: (selected: string | null) => void; + onChange: (selected: string) => void; @@ - const handleSelect = (option: string) => { - onChange(option); + const handleSelect = (option: string) => { + onChange(option); setIsOpen(false); };옵션 B(클리어 지원): clear 버튼/메뉴아이템을 추가하고 onChange(null) 호출을 실제로 구현.
원하시면 B안으로 패치 제안 드리겠습니다.Also applies to: 27-30
35-36: 외부 클릭·ESC로 닫힘 처리 추가 제안.
드롭다운은 보통 바깥 클릭/ESC로 닫힙니다. 간단한 ref+이벤트 바인딩으로 해결 가능.-import { useId, useState } from 'react'; +import { useEffect, useId, useRef, useState } from 'react'; @@ - return ( - <div className={`relative w-full ${className}`}> + const containerRef = useRef<HTMLDivElement>(null); + useEffect(() => { + if (!isOpen) return; + const onDocClick = (e: MouseEvent) => { + if (!containerRef.current?.contains(e.target as Node)) setIsOpen(false); + }; + const onKey = (e: KeyboardEvent) => { + if (e.key === 'Escape') setIsOpen(false); + }; + document.addEventListener('mousedown', onDocClick); + document.addEventListener('keydown', onKey); + return () => { + document.removeEventListener('mousedown', onDocClick); + document.removeEventListener('keydown', onKey); + }; + }, [isOpen]); + + return ( + <div ref={containerRef} className={`relative w-full ${className}`}>Also applies to: 55-56, 69-81
1-3: 가능하면 Radix UI Select/DropdownMenu 채택 고려.
진입장벽 대비 성숙한 접근성(포커스 이동, 타입어헤드, 스크린리더)과 포탈·경계 처리까지 무료로 얻습니다. #25에서도 Radix를 이미 사용 중입니다.대안: @radix-ui/react-select로 마이그레이션(Trigger, Content, Item 구성). 필요시 초기 마이그레이션 코드 초안 제공 가능합니다.
Also applies to: 35-86
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
packages/design-system/src/components/dropdown/Dropdown.tsx(1 hunks)packages/design-system/src/components/index.ts(1 hunks)packages/design-system/tsconfig.json(1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: constantly-dev
PR: Pinback-Team/pinback-client#2
File: pnpm-workspace.yaml:3-3
Timestamp: 2025-08-18T13:48:59.065Z
Learning: constantly-dev는 docs 디렉터리를 컨벤션 문서 추가용으로 사용할 예정이라고 명시했습니다.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: storybook
🔇 Additional comments (1)
packages/design-system/src/components/index.ts (1)
5-5: 공개 export 연동 OK.
드롭다운 기본 export를 컴포넌트 인덱스에 안전하게 노출했습니다. 배럴 내 다른 default export 스타일과도 일관적입니다.
| const Dropdown = ({ | ||
| options, | ||
| selectedValue, | ||
| onChange, | ||
| placeholder, | ||
| onAddItem, | ||
| addItemLabel, | ||
| limit, | ||
| className = '', | ||
| }: DropdownProps) => { |
There was a problem hiding this comment.
빈 라벨/플레이스홀더 노출 위험 수정: 기본 placeholder·addItemLabel 기본값을 부여하세요.
addItemLabel이 미지정이면 “빈 버튼”이 렌더링되고, placeholder 미지정 시 빈 텍스트가 나옵니다.
- placeholder,
+ placeholder = '선택하세요',
onAddItem,
- addItemLabel,
+ addItemLabel = '추가',
@@
- <span className={selectedValue ? 'text-black' : 'text-font-gray-3'}>
- {selectedValue || placeholder}
+ <span className={selectedValue !== null ? 'text-black' : 'text-font-gray-3'}>
+ {selectedValue ?? placeholder}
</span>
@@
- {addItemLabel}
+ {addItemLabel}Also applies to: 42-44, 69-81
🤖 Prompt for AI Agents
In packages/design-system/src/components/dropdown/Dropdown.tsx around lines
15-24 (and also address similar spots at 42-44 and 69-81), the component can
render an empty placeholder or an empty "add" button when placeholder or
addItemLabel are not provided; update the props destructuring to supply sensible
defaults (e.g., placeholder: 'Select...', addItemLabel: 'Add') or otherwise
ensure fallback strings are used wherever placeholder or addItemLabel are
rendered so the UI never shows an empty label/button; apply the same default or
fallback logic to the other affected blocks mentioned.
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (5)
packages/design-system/src/components/dropdown/Dropdown.stories.tsx (5)
63-63: any 제거로 타입 안전성 확보
ControlledRender의args: any는 스토리-컴포넌트 간 계약을 흐립니다. 컴포넌트 기반으로 정확히 타이핑하세요.적용 제안:
-function ControlledRender(args: any) { +function ControlledRender(args: ComponentProps<typeof Dropdown>) {
63-75: 스토리 Controls와 내부 state 동기화 누락스토리 Controls에서
selectedValue를 변경해도 내부value가 초기값 이후로 갱신되지 않습니다. 동기화가 필요합니다.적용 제안:
function ControlledRender(args: ComponentProps<typeof Dropdown>) { const [value, setValue] = useState<string | null>(args.selectedValue ?? null); + useEffect(() => { + setValue(args.selectedValue ?? null); + }, [args.selectedValue]); + return ( <div className="h-[20rem]"> <Dropdown {...args} selectedValue={value}
24-27: selectedValue 컨트롤 비활성화 권장(혼동 방지)해당 스토리는 내부에서 제어하고 있어 Controls의 text 입력이 오작동/혼선을 유발할 수 있습니다. 비활성화가 안전합니다.
적용 제안:
- selectedValue: { - control: 'text', - description: '현재 선택된 값(제어 컴포넌트에서 사용).', - }, + selectedValue: { + control: false, + description: '현재 선택된 값(제어 컴포넌트에서 사용).', + },
91-101: alert 대신 Action Logger 사용Chromatic/CI 환경과 상호작용 로그 일관성을 위해
alert는 지양하고 argTypes의 action 바인딩에 맡기세요.적용 제안:
export const WithAddItemAlert: Story = { name: '추가 버튼 클릭 시 (test Alert)', args: { options: ['사과', '바나나'], limit: 5, addItemLabel: '새 항목 추가', - onAddItem: () => { - alert('추가 버튼이 클릭되었습니다.'); - }, }, render: ControlledRender, };원하시면
play함수를 추가해 “추가” 버튼 클릭 액션을 상호작용 테스트로 기록하도록 도와드릴게요.
1-2: 스토리북 타입 import 경로 통일
- packages/design-system/package.json devDependencies에
@storybook/react추가- 아래 스토리 파일들에서
로 변경- import type { Meta, StoryObj } from '@storybook/react-vite'; + import type { Meta, StoryObj } from '@storybook/react';
• src/components/dropdown/Dropdown.stories.tsx
• src/components/level/Level.stories.tsx
• src/components/toast/Toast.stories.tsx
• src/components/progress/Progress.stories.tsx
• src/components/textarea/Textarea.stories.tsx
• src/components/input/Input.stories.tsx
• src/components/button/Button.stories.tsx
• src/components/chip/Chip.stories.tsx
• src/components/card/Card.stories.tsx
• src/components/badge/Badge.stories.tsx
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
packages/design-system/src/components/dropdown/Dropdown.stories.tsx(1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: constantly-dev
PR: Pinback-Team/pinback-client#2
File: pnpm-workspace.yaml:3-3
Timestamp: 2025-08-18T13:48:59.065Z
Learning: constantly-dev는 docs 디렉터리를 컨벤션 문서 추가용으로 사용할 예정이라고 명시했습니다.
🪛 GitHub Check: lint
packages/design-system/src/components/dropdown/Dropdown.stories.tsx
[warning] 63-63:
Unexpected any. Specify a different type
🔇 Additional comments (1)
packages/design-system/src/components/dropdown/Dropdown.stories.tsx (1)
5-18: 스토리 구성/문서화 전반은 명확합니다메타/argTypes/데모 시나리오(기본, 초기 선택, 제한 도달, 다수 옵션) 구성이 깔끔하고, 문서 설명도 충분합니다.
Also applies to: 19-49, 50-57, 80-89, 104-119, 121-127
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (4)
packages/design-system/src/components/dropdown/Dropdown.stories.tsx (4)
1-2: any 제거 + StoryFn 타입 적용, 그리고 외부 컨트롤 변경과 상태 동기화
- lint 경고(Unexpected any)를 없애고, 스토리 render 콜백을 Storybook 타입으로 명확히 하는 편이 좋습니다.
- Controls 패널에서 selectedValue를 바꿨을 때 내부 state가 동기화되지 않는 문제도 useEffect로 해결하세요.
- import type { Meta, StoryObj } from '@storybook/react-vite'; - import { useState } from 'react'; + import type { Meta, StoryObj, StoryFn } from '@storybook/react-vite'; + import { useEffect, useState } from 'react'; @@ -function ControlledRender(args: any) { - const [value, setValue] = useState<string | null>(args.selectedValue ?? null); - - return ( +const ControlledRender: StoryFn<typeof Dropdown> = (args) => { + const [value, setValue] = useState<string | null>(args.selectedValue ?? null); + useEffect(() => { + setValue(args.selectedValue ?? null); + }, [args.selectedValue]); + + return ( <div className="h-[25rem]"> <Dropdown {...args} selectedValue={value} onChange={(next: string | null) => { setValue(next); args.onChange?.(next); }} /> </div> ); -} +};Also applies to: 63-76
24-31: selectedValue를 text 컨트롤 대신 select 컨트롤로 제한selectedValue는 options 중 하나(또는 null)여야 하므로 text 컨트롤은 잘못된 상태를 만들 수 있습니다. 기본 args와 일치하도록 select 컨트롤을 사용하면 UX와 일관성이 좋아집니다.
selectedValue: { - control: 'text', - description: '현재 선택된 값(제어 컴포넌트에서 사용).', + control: { type: 'select' }, + options: [null, '사과', '바나나', '체리', '포도'], + description: '현재 선택된 값(제어 컴포넌트에서 사용).', },
85-96: WithAddItemAlert: alert 대신 addon-actions 활용 권장argTypes에서 이미 onAddItem을 action으로 설정했으므로, 스토리에서 alert를 직접 띄우지 않아도 액션 패널로 충분히 관찰 가능합니다. Chromatic 등 자동화에도 영향을 덜 줍니다.
export const WithAddItemAlert: Story = { name: '추가 버튼 클릭 시 (test Alert)', args: { options: ['사과', '바나나'], limit: 5, addItemLabel: '새 항목 추가', - onAddItem: () => { - alert('추가 버튼이 클릭되었습니다.'); - }, }, render: ControlledRender, };
5-9: meta 선언은 ‘satisfies’로 타입 보존
const meta = { ... } satisfies Meta<typeof Dropdown>패턴을 쓰면 과잉 확장을 막으면서도 추론된 리터럴 타입을 보존할 수 있어 Storybook 설정 실수를 조기에 잡기 좋습니다.-const meta: Meta<typeof Dropdown> = { +const meta = { title: 'Components/Dropdown', component: Dropdown, tags: ['autodocs'], parameters: { @@ -}; +} satisfies Meta<typeof Dropdown>;Also applies to: 57-57
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
packages/design-system/src/components/dropdown/Dropdown.stories.tsx(1 hunks)packages/design-system/src/components/dropdown/Dropdown.tsx(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- packages/design-system/src/components/dropdown/Dropdown.tsx
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: constantly-dev
PR: Pinback-Team/pinback-client#2
File: pnpm-workspace.yaml:3-3
Timestamp: 2025-08-18T13:48:59.065Z
Learning: constantly-dev는 docs 디렉터리를 컨벤션 문서 추가용으로 사용할 예정이라고 명시했습니다.
🪛 GitHub Check: lint
packages/design-system/src/components/dropdown/Dropdown.stories.tsx
[warning] 63-63:
Unexpected any. Specify a different type
jjangminii
left a comment
There was a problem hiding this comment.
인터페이스가 명확하고 필수/옵셔널 props 구분 및 사용 예시가 좋아서 사용할 때 좋을 것 같아요 고생하셨습니다-!
지금 단뎨에선 props 분기가 더 간단할것 같다고 생각이 드네요 추후 확장된다면 합성 패턴도 고민해보는게 좋겠네요-!!
jllee000
left a comment
There was a problem hiding this comment.
깔끔하니 고생 많으셨습니다!
간단한 질문만 확인해주시고,, 아이콘의 애니메이션 적용 관련해서 남겨주신 부분에 대한 저의 생각도 공유해보았습니다!
| width={16} | ||
| height={16} | ||
| // TODO: Icon 컴포넌트 내부에서 animation 관련 처리 고민하기 | ||
| // rotate={isOpen ? 180 : undefined} | ||
| className={`transform transition-transform duration-200 ${isOpen ? 'rotate-180' : ''}`} |
There was a problem hiding this comment.
이부분도 고민해보았는데요!
isOpen에 따라서 회전 180이냐 0이냐 rotate 상태가 바뀌니까 이걸 transition 처리로 스무스(?)하게만 적용해주면 되는거라!
<Icon name="ic_arrow_down_disable" rotate={isOpen ? 180 : 0} animateRotate />
애니메이션 줄 지 의사를 boolean으로 전달후에
Icon 내에서 animateRotate 에 따른
combined 객체에 분기를 처리해두는 방향은 어떠한 지 제안합니다!
const combined = clsx( 'inline-block', rotateClass, animateRotate && 'transform transition-transform duration-200', className );
There was a problem hiding this comment.
오 이렇게 처리해도 가능하겠네요! 이렇게 Icon파일에서 한 번에 적용하면 오히려 모든 icon의 rotate animation이 통일되니까 디자인 시스템의 의미도 갖을 것 같아요!
저는 좋은 생각 같은데 다른 분들의 생각은 어떠신가요? 괜찮으시다면 반영하겠습니다!
There was a problem hiding this comment.
(회전이나 애니메이션 들이 아이콘 단위로 많이 쓰이지는 않지만)
사이드바에도 비슷한 아이콘 회전이 있는 거 같아서! 이렇게 관리해도 좋을 듯해요!
animateRotate추가하고 combined분기만 하면 되니, 코드 수정에도 엄청 큰 공수가 들지는 않아서!!
| </button> | ||
|
|
||
| {isOpen && ( | ||
| <div className="common-shadow ds-scrollbar absolute z-10 mt-[1.5rem] h-[20.4rem] w-full overflow-y-auto rounded-[0.4rem] bg-white p-[0.8rem]"> |
There was a problem hiding this comment.
ds-scrollbar로 커스텀 스크롤바 스타일이 적용될 수있군녀 알아갑니다!
| addItemLabel?: string; | ||
| limit?: number; |
There was a problem hiding this comment.
리밋 속성을 통해서, 카테고리 추가 노출여부를 제어할 수 있어서! 확장성 측면에서 좋은 것 같네요!
++ 대신 디자인적으로 궁금한 점이, 카테고리 리스트개수가 적은 경우에도 그 박스의 height가 고정값일까요?
There was a problem hiding this comment.
++ 대신 디자인적으로 궁금한 점이, 카테고리 리스트개수가 적은 경우에도 그 박스의 height가 고정값일까요?
네네 일단 그렇게 구현이 되어있어요. 리스트가 많아지는 경우에 스크롤을 보여주려면 box에 height값을 줄 수 밖에 없어서..!
개수에 따라 height를 다르게 분기처리 할 수 있기는 하지만 현재도 괜찮다고 일단 생각이 드는데 어떻게 생각하시나요??
애매하다면 QA때 디자인 분들께 질문 드려도 좋을 것 같아요 👍
There was a problem hiding this comment.
아하, 그러네요 스크롤 적용하려면 Height 지정을 해두긴 하네요,, 그렇다고 개수에 따라 height 분기하는 건 오히려 너무 불필요한 거 같아서! 저는 좋아요! QA때 디자이너 분들 피드백에 따라 추후에 수정해보는 걸로 해용!
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (3)
packages/design-system/src/icons/components/icon.tsx (3)
2-3: React 기본 import 대신 타입 전용 import로 전환해 런타임 의존 제거
JSX 자동 런타임에서는 기본 import가 불필요하고, 이 파일에서는 타입 위치에서만 사용됩니다. 타입 전용 import로 교체해 번들 영향 최소화하세요.-import React from 'react'; +import type { SVGProps } from 'react';
58-68: IconProps: SVGProps로 치환하고 className 중복 선언 제거 제안
className은 이미SVGProps<SVGSVGElement>에 포함됩니다. 중복 선언을 제거하고 위의 타입 import와 일관되게 사용하세요.-interface IconProps extends React.SVGProps<SVGSVGElement> { +interface IconProps extends SVGProps<SVGSVGElement> { name: IconName; size?: number | string; width?: number | string; height?: number | string; color?: IconColor; - className?: string; rotate?: IconRotate; hasRotateAnimation?: boolean; ariaHidden?: boolean; }
94-99: Tailwind: transform 유틸은 불필요 — transition-transform만으로 충분
rotate-*유틸이transform을 이미 설정합니다. 중복 클래스를 제거해 약간의 CSS 절감이 가능합니다.- hasRotateAnimation && 'transform transition-transform duration-200', + hasRotateAnimation && 'transition-transform duration-200',
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
packages/design-system/src/components/dropdown/Dropdown.tsx(1 hunks)packages/design-system/src/icons/components/icon.tsx(4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- packages/design-system/src/components/dropdown/Dropdown.tsx
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: constantly-dev
PR: Pinback-Team/pinback-client#2
File: pnpm-workspace.yaml:3-3
Timestamp: 2025-08-18T13:48:59.065Z
Learning: constantly-dev는 docs 디렉터리를 컨벤션 문서 추가용으로 사용할 예정이라고 명시했습니다.
🔇 Additional comments (1)
packages/design-system/src/icons/components/icon.tsx (1)
78-79: 기본값(false) 선택 적절 — 기존 API와의 호환 유지
새 prop 미지정 시 동작이 변하지 않아 안전합니다. LGTM.
📌 Related Issues
📄 Tasks
⭐ PR Point (To Reviewer)
현재 figma기준으로 Dropdown이 일부 domain에 종속된다고 생각할 수 있을 것 같아요. 하지만 이후에 재사용 되는 경우 해당 디자인을 최대한 활용하는 방향으로 디자이너 분들과 이야기를 통해 이를 최대한 공통으로 쓸 수 있도록 설계하여 pds에 추가하였어요.
📌 설계에 대한 고민
일단 pds에 추가한 컴포넌트인 만큼 이를 어떻게 설계할지 고민이 되었어요. 단순 options만 보여주는 dropdown이면 쉬웠겠지만, 아래 추가하는 버튼이 따로 존재했고 이는 이후 확장되는 컴포넌트에서는 사용되지 않을 가능성도 있기 때문에 분기가 필요할 것 같았어요.
이를 위해 제가 생각한 방법은 2가지 정도였어요.
최종적으로 2번을 선택했어요. 그 이유는 합성 컴포넌트 패턴을 쓰는 이유가 대표적으로 props drilling 방지, 공통된 컴포넌트 순서 변경 사용 시 용이 등이 있을 것 같은데 해당 컴포넌트에서는 조합으로 순서 변경을 잘하게 할 필요가 없다고 생각했어요. 단순 추가 버튼이 있고 없고 정도였기 때문이에요!
그래서 일단 2번으로 분기 처리를 했고, 이후에 만약 더 Dropdown이 복잡해지거나 조합을 했을 때의 장점이 생기도록 변경이 된다면 다시 재설계를 고민해볼게요 👍
📌 인터페이스 및 사용방법
인터페이스는 아래와 같아요.
기본적으로
options, selectedValue, onChange, placeholder가 필수이고 나머지는optional이에요!사용 방법은 아래 예시로 대체할게요!
📷 Screenshot
UI에 대한 자세한 설명 및 확인은 크로마틱을 참고해주세요!
bandicam.2025-09-07.13-25-55-640.mp4
Summary by CodeRabbit
신기능
문서
작업