Skip to content

Commit

Permalink
[Feature] DropDown 컴포넌트 구현 (#61)
Browse files Browse the repository at this point in the history
* feat : dropdown 컴포넌트 제작

* feat : 아이콘 추가, 상태에 따른 색상 변경

* feat : useDropDownState, useClickOutside 추가

* feat: 드롭다운 스토리북 작성, 인터페이스 설명 작성

* feat : 스토리북 a11y false 로, 컴포넌트 인터페이스 수정

* refactor: useFlatChildren 추가, padding 수정

* feat: 예시 추가, import 경로 수정

* refactor : 변경된가이드대로 디자인 변경

* feat : 화살표에 대한 키보드 엔터 동작 추가

* feat: 드롭다운 컨텐츠 영역 absolute 로 조정

* feat: zIndex 토큰 추가, dropdownContent 로 수정

* feat : 필요없는 onMouseEnter 삭제

* refactor : 모바일 여부에 따라 이벤트 타입 변경

* refactor : 코드 리뷰 반영

* refactor : 컴파운드 패턴에서 일반 컴포넌트 방식으로 변경

* feat : 리뷰 반영

* fix : ReactNode 타입 import 하는 것으로 수정

* fix : 순서 수정

* fix : playwright 관련 워크플로우 수정

* feat: playwright 설치
  • Loading branch information
SeieunYoo authored Jun 28, 2024
1 parent 3530dbc commit 1849e8e
Show file tree
Hide file tree
Showing 30 changed files with 816 additions and 35 deletions.
6 changes: 6 additions & 0 deletions apps/wow-docs/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import Checkbox from "wowds-ui/Checkbox";
import Chip from "wowds-ui/Chip";
import DropDown from "wowds-ui/DropDown";
import DropDownOption from "wowds-ui/DropDownOption";
import MultiGroup from "wowds-ui/MultiGroup";
import RadioButton from "wowds-ui/RadioButton";
import RadioGroup from "wowds-ui/RadioGroup";
Expand All @@ -15,6 +17,10 @@ const Home = () => {
<RadioButton label="1학년" value="1학년" />
<RadioButton label="2학년" value="2학년" />
</RadioGroup>
<DropDown placeholder="목록을 입력하세요">
<DropDownOption text="option 1" value="option 1" />
<DropDownOption text="option 1" value="option 2" />
</DropDown>
<MultiGroup variant="checkbox">
<Checkbox children="checkbox1" value="checkbox1" />
<Checkbox children="checkbox2" value="checkbox2" />
Expand Down
4 changes: 4 additions & 0 deletions apps/wow-docs/styled-system/tokens/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,10 @@ const tokens = {
value: "1.2px",
variable: "var(--border-widths-arrow)",
},
"zIndex.dropdown": {
value: 10,
variable: "var(--z-index-dropdown)",
},
"breakpoints.sm": {
value: "640px",
variable: "var(--breakpoints-sm)",
Expand Down
4 changes: 4 additions & 0 deletions apps/wow-docs/styled-system/tokens/tokens.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ export type Token =
| "radii.full"
| "borderWidths.button"
| "borderWidths.arrow"
| "zIndex.dropdown"
| "breakpoints.sm"
| "breakpoints.md"
| "breakpoints.lg"
Expand Down Expand Up @@ -304,6 +305,8 @@ export type RadiusToken = "sm" | "md" | "full";

export type BorderWidthToken = "button" | "arrow";

export type ZIndexToken = "dropdown";

export type BreakpointToken = "sm" | "md" | "lg" | "xl" | "2xl";

export type SizeToken =
Expand All @@ -319,6 +322,7 @@ export type Tokens = {
spacing: SpacingToken;
radii: RadiusToken;
borderWidths: BorderWidthToken;
zIndex: ZIndexToken;
breakpoints: BreakpointToken;
sizes: SizeToken;
} & { [token: string]: never };
Expand Down
1 change: 1 addition & 0 deletions apps/wow-docs/styled-system/types/prop-type.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type { CssProperties } from "./system-types";
import type { Tokens } from "../tokens/index";

export interface UtilityValues {
zIndex: Tokens["zIndex"];
top: Tokens["spacing"];
left: Tokens["spacing"];
insetInline: Tokens["spacing"];
Expand Down
4 changes: 3 additions & 1 deletion apps/wow-docs/styled-system/types/style-props.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7168,7 +7168,9 @@ export interface SystemProperties {
*
* @see https://developer.mozilla.org/docs/Web/CSS/z-index
*/
zIndex?: ConditionalValue<CssProperties["zIndex"] | AnyString>;
zIndex?: ConditionalValue<
UtilityValues["zIndex"] | CssVars | CssProperties["zIndex"] | AnyString
>;
/**
* The non-standard **`zoom`** CSS property can be used to control the magnification level of an element. `transform: scale()` should be used instead of this property, if possible. However, unlike CSS Transforms, `zoom` affects the layout size of the element.
*
Expand Down
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,6 @@
"postcss": "^8.4.38",
"prettier": "^3.2.5",
"rollup": "^4.17.2",
"rollup-jest": "^3.1.0",
"rollup-plugin-peer-deps-external": "^2.2.4",
"rollup-plugin-preserve-directives": "^0.4.0",
"rollup-plugin-visualizer": "^5.12.0",
Expand Down
2 changes: 2 additions & 0 deletions packages/theme/src/tokens/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ import { colors, gradients } from "./color.ts";
import { radii } from "./radius.ts";
import { spacing } from "./space.ts";
import { borderWidths } from "./stroke.ts";
import { zIndex } from "./zIndex.ts";

export const tokens = defineTokens({
colors,
gradients,
spacing,
radii,
borderWidths,
zIndex,
});
8 changes: 8 additions & 0 deletions packages/theme/src/tokens/zIndex.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { defineTokens } from "@pandacss/dev";
import { zIndex as wowZIndex } from "wowds-tokens";

export const zIndex = defineTokens.zIndex({
dropdown: {
value: wowZIndex.dropdown,
},
});
42 changes: 42 additions & 0 deletions packages/wow-icons/src/component/DownArrow.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { forwardRef } from "react";
import { color } from "wowds-tokens";

import type { IconProps } from "@/types/Icon.ts";

const DownArrow = forwardRef<SVGSVGElement, IconProps>(
(
{
className,
width = "20",
height = "20",
viewBox = "0 0 20 20",
stroke = "white",
...rest
},
ref
) => {
return (
<svg
aria-label="down-arrow icon"
className={className}
fill="none"
height={height}
ref={ref}
viewBox={viewBox}
width={width}
xmlns="http://www.w3.org/2000/svg"
{...rest}
>
<path
d="M16 9L10 14L4 9"
stroke={color[stroke]}
strokeLinejoin="bevel"
strokeWidth="1.4"
/>
</svg>
);
}
);

DownArrow.displayName = "DownArrow";
export default DownArrow;
1 change: 1 addition & 0 deletions packages/wow-icons/src/component/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export { default as Check } from "./Check.tsx";
export { default as DownArrow } from "./DownArrow.tsx";
export { default as RightArrow } from "./RightArrow.tsx";
3 changes: 3 additions & 0 deletions packages/wow-icons/src/svg/down-arrow.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions packages/wow-tokens/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ export * as radius from "./radius.ts";
export * as space from "./space.ts";
export * as stroke from "./stroke.ts";
export * as typography from "./typography.ts";
export * as zIndex from "./zIndex.ts";
1 change: 1 addition & 0 deletions packages/wow-tokens/src/zIndex.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const dropdown = 10;
13 changes: 12 additions & 1 deletion packages/wow-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,16 @@
"require": "./dist/MultiGroup.cjs",
"import": "./dist/MultiGroup.js"
},
"./DropDownOption": {
"types": "./dist/components/DropDown/DropDownOption.d.ts",
"require": "./dist/DropDownOption.cjs",
"import": "./dist/DropDownOption.js"
},
"./DropDown": {
"types": "./dist/components/DropDown/index.d.ts",
"require": "./dist/DropDown.cjs",
"import": "./dist/DropDown.js"
},
"./Chip": {
"types": "./dist/components/Chip/index.d.ts",
"require": "./dist/Chip.cjs",
Expand Down Expand Up @@ -101,7 +111,8 @@
"plop": "^4.0.1",
"rollup-plugin-peer-deps-external": "^2.2.4",
"storybook": "^8.1.9",
"typescript": "^5.3.3"
"typescript": "^5.3.3",
"playwright": "1.45.0"
},
"dependencies": {
"wowds-icons": "workspace:^"
Expand Down
2 changes: 2 additions & 0 deletions packages/wow-ui/rollup.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ export default {
RadioButton: "./src/components/RadioGroup/RadioButton",
RadioGroup: "./src/components/RadioGroup/RadioGroup",
MultiGroup: "./src/components/MultiGroup",
DropDownOption: "./src/components/DropDown/DropDownOption",
DropDown: "./src/components/DropDown",
Chip: "./src/components/Chip",
Checkbox: "./src/components/Checkbox",
Button: "./src/components/Button",
Expand Down
193 changes: 193 additions & 0 deletions packages/wow-ui/src/components/DropDown/DropDown.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
import type { Meta, StoryObj } from "@storybook/react";
import { useState } from "react";

import DropDown from "@/components/DropDown";
import DropDownOption from "@/components/DropDown/DropDownOption";

const meta = {
title: "UI/DropDown",
component: DropDown,
tags: ["autodocs"],
parameters: {
componentSubtitle: "드롭다운 컴포넌트",
a11y: {
config: {
rules: [{ id: "color-contrast", enabled: false }],
},
},
},
argTypes: {
children: {
description: "DropDownOption 들을 children 컴포넌트로 받습니다.",
table: {
type: { summary: "ReactNode" },
},
control: false,
},
trigger: {
description: "드롭다운을 열기 위한 외부 트리거 요소입니다.",
table: {
type: { summary: "ReactElement" },
},
control: false,
},
label: {
description:
"외부 트리거를 사용하지 않는 경우 레이블을 사용할 수 있습니다.",
table: {
type: { summary: "ReactNode" },
},
control: { type: "text" },
},
placeholder: {
description:
"외부 트리거를 사용하지 않는 경우 선택되지 않았을 때 표시되는 플레이스홀더입니다.",
table: {
type: { summary: "string" },
},
control: { type: "text" },
},
value: {
description: "현재 선택된 값을 나타냅니다.",
table: {
type: { summary: "string" },
},
control: { type: "text" },
},
defaultValue: {
description: "초기 선택된 값을 나타냅니다.",
table: {
type: { summary: "string" },
},
control: { type: "text" },
},
onChange: {
description: "값이 변경될 때 호출되는 함수입니다.",
table: {
type: { summary: "(value: string) => void" },
},
action: "changed",
},
},
} satisfies Meta<typeof DropDown>;

export default meta;

type Story = StoryObj<typeof meta>;

export const Primary: Story = {
args: {
children: (
<>
<DropDownOption text="option 1" value="option 1" />
<DropDownOption text="option 2" value="option 2" />
</>
),
label: "Select an Option",
placeholder: "Please select",
},
parameters: {
docs: {
description: {
story: "기본적인 드롭다운 컴포넌트입니다.",
},
},
},
};

export const WithTrigger: Story = {
args: {
children: (
<>
<DropDownOption text="option 1" value="option 1" />
<DropDownOption text="option 2" value="option 2" />
</>
),
trigger: <button>Open Dropdown</button>,
},
parameters: {
docs: {
description: {
story: "버튼 트리거를 사용하여 드롭다운을 열 수 있는 컴포넌트입니다.",
},
},
},
};

export const WithDefaultValue: Story = {
args: {
children: (
<>
<DropDownOption text="option 1" value="option 1" />
<DropDownOption text="option 2" value="option 2" />
</>
),
label: "Select an Option",
placeholder: "Please select",
defaultValue: "Option 2",
},
parameters: {
docs: {
description: {
story: "초기 선택된 값이 설정된 드롭다운 컴포넌트입니다.",
},
},
},
};

const ControlledTextField = () => {
const [selectedValue, setSelectedValue] = useState("");

const handleChange = (value: string) => {
setSelectedValue(value);
};

return (
<DropDown
label="Select an Option"
placeholder="Choose..."
value={selectedValue}
onChange={handleChange}
>
<DropDownOption text="option 1" value="option 1" />
<DropDownOption text="option 2" value="option 2" />
</DropDown>
);
};

export const ControlledValue: Story = {
render: () => <ControlledTextField />,
args: {},
parameters: {
docs: {
description: {
story: "외부에서 제어되는 값을 가진 드롭다운 컴포넌트입니다.",
},
},
},
};

export const ManyOptions: Story = {
args: {
children: (
<>
{Array.from({ length: 20 }, (_, index) => (
<DropDownOption
key={index}
text={`Option ${index + 1}`}
value={`Option ${index + 1}`}
/>
))}
</>
),
label: "Select an Option",
placeholder: "Please select",
},
parameters: {
docs: {
description: {
story: "많은 옵션을 가진 드롭다운 컴포넌트입니다.",
},
},
},
};
Loading

0 comments on commit 1849e8e

Please sign in to comment.