Skip to content

Commit

Permalink
refactor: dropdown 컴포넌트
Browse files Browse the repository at this point in the history
  • Loading branch information
bottlewook committed Jan 21, 2024
1 parent 00dd486 commit 19a7a3c
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 57 deletions.
31 changes: 20 additions & 11 deletions src/app/search/page.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
'use client';

import { useState, useRef } from 'react';
import { useState } from 'react';

import classNames from 'classnames/bind';

import Text from '@/components/shared/text/Text';
import { SEARCH_FILTER_MAP } from '@constants/searchByMap';
import Drawer from '@shared/drawer/Drawer';
import Dropdown from '@shared/dropdown/Dropdown';
import Header from '@shared/header/Header';
import ProductArticle from '@shared/product-article/ProductArticle';
import SearchBar from '@shared/search-bar/SearchBar';
import Spacing from '@shared/spacing/Spacing';
import Text from '@shared/text/Text';

import styles from './page.module.scss';

Expand Down Expand Up @@ -51,22 +52,31 @@ const productArticleData = [
];

const options = [
{ label: '조회순', value: '1' },
{ label: '위반제품순', value: '2' },
{ label: '최신순', value: '3' },
{ label: '추천순', value: '4' },
{ label: '조회순', value: 'view' },
{ label: '위반제품순', value: 'violations' },
{ label: '최신순', value: 'latest' },
{ label: '추천순', value: 'recommended' },
];

function SearchPage() {
const dropdownRef = useRef<HTMLInputElement>(null);

const [selectedLabel, setSelectedLabel] = useState(options[0].label);
// TODO: 쿼리스트링을 필터 값 받아오기 ex. view, violations, latest, recommended
const query = '';
const initialLabel = query === '' ? '조회순' : SEARCH_FILTER_MAP[query];
const [selectedLabel, setSelectedLabel] = useState<string | number>(initialLabel);
const [isOpenFilterDrawer, setIsOpenFilterDrawer] = useState(false);

const handleFilterClick = () => {
setIsOpenFilterDrawer((prev) => { return !prev; });
};

const handleSelectedValue = (value: string | number) => {
setSelectedLabel(value);
};

// TODO: 분류 옵션에 따른 데이터 패칭 작업
// const sortOption = options.find((option) => { return option.label === selectedLabel; })
// console.log(options.find((option) => { return option.label === selectedLabel; }));

return (
<>
<Header isDisplayLogo={false} displayRightIconType="filter" onFilterClick={handleFilterClick} />
Expand All @@ -79,8 +89,7 @@ function SearchPage() {
options={options}
selectedLabel={selectedLabel}
type="favorite"
ref={dropdownRef}
setSelectedLabel={setSelectedLabel}
handleSelectedValue={handleSelectedValue}
/>
</div>
<div className={cx('productArticleContainer')}>
Expand Down
57 changes: 45 additions & 12 deletions src/components/shared/dropdown/Dropdown.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
width: 80px;
}

&.profile {
width: 100%;
}

.selectedValue {
display: flex;
align-items: center;
Expand All @@ -16,22 +20,46 @@
border-radius: 8px;
background-color: var(--tertiary-200);
}

&.profile {
width: 100%;
max-width: 327px;
padding: 8px 16px;
border: 1px solid var(--gray-100);
border-radius: 8px;
}
}

.menu {
position: absolute;
z-index: 3;
top: 34px;
width: 80px;
border: 1px solid var(--tertiary-100);
border-radius: 8px;
box-sizing: border-box;

&.favorite {
top: 34px;
width: 80px;
border: 1px solid var(--tertiary-100);
border-radius: 8px;
background-color: var(--tertiary-200);
color: var(--tertiary-400);
}

&.profile {
top: 45px;
width: 100%;
max-width: 327px;
border: 1px solid var(--gray-100);
border-radius: 8px;
}

.item {
button {
display: block;
width: 100%;
height: 100%;
text-align: start;
}

&.favorite {
padding: 4px 8px;
border-bottom: 1px solid var(--tertiary-100);
Expand All @@ -41,16 +69,21 @@
&:last-child {
border: none;
}
}

label {
&.favorite {
font-size: inherit;
}
}
&.profile {
display: flex;
box-sizing: border-box;
height: 48px;
padding: 8px 16px;
border-bottom: 1px solid var(--tertiary-100);
color: var(--gray-300);
font-size: 14px;
place-items: center center;

.input[type="radio"] {
display: none;
&:hover {
background-color: var(--gray);
}
}
}
}
}
50 changes: 16 additions & 34 deletions src/components/shared/dropdown/Dropdown.tsx
Original file line number Diff line number Diff line change
@@ -1,38 +1,36 @@
'use client';

import { InputHTMLAttributes, forwardRef, useState } from 'react';
import { ButtonHTMLAttributes, useState } from 'react';

import classNames from 'classnames/bind';

import useOutsideClick from '@/hooks/useOutsideClick';
import Expand from '@components/icons/Expand';
import useOutsideClick from '@hooks/useOutsideClick';
import Text from '@shared/text/Text';

import styles from './Dropdown.module.scss';

const cx = classNames.bind(styles);

interface Option {
interface IOption {
label: string
value: string | number | undefined
}

interface DropdownProps extends InputHTMLAttributes<HTMLInputElement> {
options: Option[]
interface DropdownProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'type'> {
options: IOption[]
selectedLabel: string | number
value?: string | number
type: 'favorite'
setSelectedLabel: React.Dispatch<React.SetStateAction<string>>
type: 'favorite' | 'profile'
handleSelectedValue: (value: string | number) => void
}

const Dropdown = forwardRef<HTMLInputElement, DropdownProps>(({
function Dropdown({
selectedLabel,
type,
options,
value,
setSelectedLabel,
handleSelectedValue,
...props
}, ref) => {
}: DropdownProps) {
const [isOpen, setIsOpen] = useState(false);

const openDropdownMenu = () => {
Expand All @@ -43,13 +41,13 @@ const Dropdown = forwardRef<HTMLInputElement, DropdownProps>(({
setIsOpen(false);
};

const handleLabelClick = (newLabel: string) => {
setSelectedLabel(() => { return newLabel; });
const containerRef = useOutsideClick(closeDropdownMenu);

const onClickDropdownItem = (value: string | number) => {
handleSelectedValue(value);
closeDropdownMenu();
};

const containerRef = useOutsideClick(closeDropdownMenu);

return (
<div className={cx('container', { [type]: true })} ref={containerRef}>
<button onClick={openDropdownMenu} className={cx('selectedValue', { [type]: true })}>
Expand All @@ -61,30 +59,14 @@ const Dropdown = forwardRef<HTMLInputElement, DropdownProps>(({
{options.map((option) => {
return (
<li key={option.value} className={cx('item', { [type]: true })}>
<input
className={cx('input')}
id={option.label}
type="radio"
ref={ref}
value={option.value}
checked={selectedLabel === option.label}
{...props}
/>
<label
className={cx('label', { [type]: true })}
htmlFor={option.label}
onClick={() => { return handleLabelClick(option.label); }}
role="presentation"
>
{option.label}
</label>
<button type="button" onClick={() => { return onClickDropdownItem(option.label); }} {...props}>{option.label}</button>
</li>
);
})}
</ul>
)}
</div>
);
});
}

export default Dropdown;
6 changes: 6 additions & 0 deletions src/constants/searchByMap.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export const SEARCH_FILTER_MAP = {
view: '조회순',
violations: '위반제품순',
latest: '최신순',
recommended: '추천순',
} as const;

0 comments on commit 19a7a3c

Please sign in to comment.