Skip to content

Commit

Permalink
Merge pull request #16 from SystemConsultantGroup/feat/7-dropdown
Browse files Browse the repository at this point in the history
[FEAT] 드롭다운 컴포넌트
  • Loading branch information
moony1204 authored Jul 31, 2024
2 parents f3970ce + df50cf6 commit 2f60363
Show file tree
Hide file tree
Showing 6 changed files with 209 additions and 5 deletions.
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
"postcss": "^8.4.39",
"postcss-preset-mantine": "^1.15.0",
"postcss-simple-vars": "^7.0.1",
"prettier": "^3.3.2",
"prettier": "^3.3.3",
"storybook": "^8.2.2",
"storybook-dark-mode": "^4.0.2",
"ts-jest": "^29.2.2",
Expand Down
62 changes: 62 additions & 0 deletions src/components/common/Dropdown/Dropdown.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
.dropdownToggle {
padding: 10px 12px;
width: 300px;
background-color: var(--color-surfaceContainerLowest);
border: 1px solid var(--color-onSurface);
border-radius: 8px;
font-weight: 500;
font-size: 14px;
line-height: 20px;
letter-spacing: 0.4px;
color: var(--color-onSurface);
}

.dropdownToggle:hover {
background-color: var(--color-surfaceBright);
color: var(--color-onSurfaceVariant);
}

.dropdownToggle.opened {
border-radius: 8px 8px 0 0;
border-bottom: none;
}

.toggleIcon {
width: 24px;
height: 24px;
left: 90%;
}

.dropdownList {
border-radius: 0 0 8px 8px;
padding: 0;
margin: 0;
border: 1px solid var(--color-onSurface);
}

.dropdownItem {
padding: 8px 12px;
cursor: pointer;
background-color: var(--color-surfaceContainerLowest);
font-weight: 500;
font-size: 14px;
line-height: 20px;
letter-spacing: 0.4px;
width: 298px;
}

.dropdownItem:hover {
background-color: var(--color-surfaceBright);
color: var(--color-onSurfaceVariant);
}

.dropdownList .dropdownItem:last-child {
border-bottom-left-radius: 8px;
border-bottom-right-radius: 8px;
border-top-left-radius: 0;
border-top-right-radius: 0;
}

.dropdownList .dropdownItem:not(:last-child) {
border-radius: 0;
}
23 changes: 23 additions & 0 deletions src/components/common/Dropdown/Dropdown.story.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import type { Meta, StoryObj } from "@storybook/react";
import { Dropdown } from "./Dropdown";

const meta = {
title: "Components/Dropdown",
component: Dropdown,
parameters: {
layout: "centered",
},
tags: ["autodocs"],
argTypes: {},
args: {},
} satisfies Meta<typeof Dropdown>;

export default meta;
type Story = StoryObj<typeof meta>;

export const Default: Story = {
args: {
placeholder: "선택해주세요",
options: ["연도", "작성자", "제목"],
},
};
44 changes: 44 additions & 0 deletions src/components/common/Dropdown/Dropdown.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import React from "react";
import { render, screen, fireEvent } from "@/utils/test-utils";
import { Dropdown } from "./Dropdown";
import "@testing-library/jest-dom";

describe("Dropdown component", () => {
const options = ["연도", "작성자", "제목"];

it("renders correctly with the given placeholder", () => {
render(<Dropdown placeholder="선택해주세요" options={options} />);
expect(screen.getByRole("button", { name: "선택해주세요" })).toBeInTheDocument();
});

it("toggles the dropdown menu when clicked", () => {
render(<Dropdown placeholder="선택해주세요" options={options} />);
const toggleButton = screen.getByRole("button", { name: "선택해주세요" });

// Initially closed
expect(screen.queryByText("연도")).not.toBeInTheDocument();

// Open dropdown
fireEvent.click(toggleButton);
expect(screen.getByText("연도")).toBeInTheDocument();

// Close dropdown
fireEvent.click(toggleButton);
expect(screen.queryByText("연도")).not.toBeInTheDocument();
});

it("selects an option and closes the dropdown", () => {
render(<Dropdown placeholder="선택해주세요" options={options} />);
const toggleButton = screen.getByRole("button", { name: "선택해주세요" });

// Open dropdown
fireEvent.click(toggleButton);
const option = screen.getByText("연도");
fireEvent.click(option);

// Check selected option
expect(screen.getByRole("button", { name: "연도" })).toBeInTheDocument();
// Check dropdown closed
expect(screen.queryByText("작성자")).not.toBeInTheDocument();
});
});
75 changes: 75 additions & 0 deletions src/components/common/Dropdown/Dropdown.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import React, { useState } from "react";
import { Button, Menu, MenuTarget, MenuDropdown, MenuItem } from "@mantine/core";
import classes from "./Dropdown.module.css";

interface DropdownProps {
options: string[];
placeholder: string;
}

export function Dropdown({ options, placeholder }: DropdownProps) {
const [selectedOption, setSelectedOption] = useState<string | null>(null);
const [opened, setOpened] = useState<boolean>(false);

const handleOptionClick = (option: string) => {
setSelectedOption(option);
setOpened(false);
};

return (
<Menu offset={0} opened={opened} onChange={setOpened}>
<MenuTarget>
<Button
justify="space-between"
className={`${classes.dropdownToggle} ${opened ? classes.opened : ""}`}
onClick={() => setOpened(!opened)}
leftSection={<span className={classes.buttonLabel}>{selectedOption || placeholder}</span>}
rightSection={
<span className={classes.toggleIcon}>
{opened ? (
/* opened dropdown = Lets Icons v1.0 Single Arrow Arrow_drop_up */
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12.1921 9.23047L15.9065 13.6879C16.3408 14.2089 15.9702 15 15.292 15L8.70803 15C8.02976 15 7.65924 14.2089 8.09346 13.6879L11.8079 9.23047C11.9079 9.11053 12.0921 9.11053 12.1921 9.23047Z"
fill="#222222"
/>
</svg>
) : (
/* closed dropdown = Lets Icons v1.0 Single Arrow Arrow_drop_down */
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M11.8079 14.7695L8.09346 10.3121C7.65924 9.79109 8.02976 9 8.70803 9L15.292 9C15.9702 9 16.3408 9.79108 15.9065 10.3121L12.1921 14.7695C12.0921 14.8895 11.9079 14.8895 11.8079 14.7695Z"
fill="#222222"
/>
</svg>
)}
</span>
}
></Button>
</MenuTarget>
<MenuDropdown className={classes.dropdownList}>
{options.map((option) => (
<MenuItem
key={option}
className={classes.dropdownItem}
onClick={() => handleOptionClick(option)}
>
{option}
</MenuItem>
))}
</MenuDropdown>
</Menu>
);
}

0 comments on commit 2f60363

Please sign in to comment.