Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/components/ui/dropdown/dropdown.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const meta: Meta<typeof Dropdown> = {
tags: ['autodocs'],
args: {
name: 'status',
areaLabel: '카테고리',
ariaLabel: '카테고리',
placeholder: '카테고리를 선택하세요',
values: CATEGORY_CODE, // 기본값
},
Expand Down Expand Up @@ -44,6 +44,6 @@ export const Small: Story = {

export const WithDefaultValue: Story = {
args: {
defaultValue: CATEGORY_CODE[1],
selected: CATEGORY_CODE[1],
},
};
2 changes: 1 addition & 1 deletion src/components/ui/dropdown/dropdown.styles.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export const DROPDOWN_STYLE = {
base: 'flex-1 text-left min-w-[110px] focus-visible:outline-red-300',
base: 'relative flex-1 text-left min-w-[110px] focus-visible:outline-red-300',
md: 'base-input !pr-10',
sm: 'rounded-md bg-gray-100 py-1.5 pl-3 pr-7 text-body-s font-bold',
} as const;
Expand Down
26 changes: 14 additions & 12 deletions src/components/ui/dropdown/dropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,38 +4,40 @@ import useEscapeKey from '@/hooks/useEscapeKey';
import useSafeRef from '@/hooks/useSafeRef';
import useToggle from '@/hooks/useToggle';
import { cn } from '@/lib/utils/cn';
import { useState } from 'react';
import { DROPDOWN_ICON_STYLE, DROPDOWN_ITEM_STYLE, DROPDOWN_STYLE } from './dropdown.styles';
import useDropdown from './hooks/useDropdown';

interface DropdownProps<T extends string> {
name: string;
areaLabel: string;
ariaLabel: string;
selected: T | undefined; // Controlled value
values: readonly T[];
size?: 'md' | 'sm';
defaultValue?: T;
placeholder?: string;
className?: string;
onChange: (value: T) => void;
}

// EX : <Dropdown name="formName" areaLabel="접근성라벨" values={ADDRESS_CODE} />
// EX : <Dropdown name="formName" ariaLabel="접근성라벨" values={ADDRESS_CODE} />
const Dropdown = <T extends string>({
name,
areaLabel: label,
ariaLabel,
values,
size = 'md',
defaultValue,
selected,
placeholder = '선택해주세요',
className,
onChange,
}: DropdownProps<T>) => {
const { value: isOpen, toggle, setClose } = useToggle();
const [selected, setSelected] = useState<T | undefined>(defaultValue);
const [attachDropdownRef, dropdownRef] = useSafeRef<HTMLDivElement>();
const [attachTriggerRef, triggerRef] = useSafeRef<HTMLButtonElement>();
const [attachListRef, listRef] = useSafeRef<HTMLDivElement>();
const handleSelect = (value: T) => {
setSelected(value);

const handleSelect = (val: T) => {
onChange(val);
setClose();
// triggerRef.current?.focus();
};

const { cursorIndex, position } = useDropdown({
Expand All @@ -57,7 +59,7 @@ const Dropdown = <T extends string>({
ref={attachTriggerRef}
type='button'
aria-expanded={isOpen}
aria-label={label}
aria-label={ariaLabel}
className={cn(
DROPDOWN_STYLE['base'],
size === 'md' ? DROPDOWN_STYLE['md'] : DROPDOWN_STYLE['sm']
Expand All @@ -68,7 +70,7 @@ const Dropdown = <T extends string>({
<Icon
iconName={isOpen ? 'dropdownUp' : 'dropdownDown'}
iconSize={size === 'md' ? 'sm' : 'x-sm'}
ariaLabel='옵션선택'
decorative
className={cn(
DROPDOWN_ICON_STYLE['base'],
size === 'md' ? DROPDOWN_ICON_STYLE['md'] : DROPDOWN_ICON_STYLE['sm']
Expand All @@ -81,7 +83,7 @@ const Dropdown = <T extends string>({
<div
ref={attachListRef}
role='listbox'
aria-label={label}
aria-label={ariaLabel}
className={cn(
'scroll-bar absolute z-[1] max-h-56 w-full rounded-md border border-gray-300 bg-white shadow-inset-top',
position === 'top' ? 'bottom-[calc(100%+8px)]' : 'top-[calc(100%+8px)]'
Expand Down
3 changes: 1 addition & 2 deletions src/components/ui/dropdown/hooks/useDropdown.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,13 @@ const useDropdown = <T extends string>({
onSelect,
}: UseDropdownProps<T>) => {
const position = useDropdownPosition(triggerRef);
const { cursorIndex, setCursorIndex } = useKeyboardNavigation({ isOpen, values, onSelect });
const { cursorIndex } = useKeyboardNavigation({ isOpen, values, onSelect });

useDropdownScroll(listRef, cursorIndex);

return {
cursorIndex,
position,
setCursorIndex,
};
};

Expand Down
6 changes: 3 additions & 3 deletions src/components/ui/dropdown/hooks/useDropdownPosition.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { RefObject, useEffect, useState } from 'react';

const useDropdownPosition = (triggerRef: RefObject<HTMLButtonElement>) => {
const useDropdownPosition = (triggerRef: RefObject<HTMLButtonElement>, threshold = 300) => {
const [position, setPosition] = useState<'top' | 'bottom'>('bottom');

useEffect(() => {
Expand All @@ -10,7 +10,7 @@ const useDropdownPosition = (triggerRef: RefObject<HTMLButtonElement>) => {
const updatePosition = () => {
const rect = trigger.getBoundingClientRect();
const viewportHeight = window.innerHeight;
setPosition(viewportHeight - rect.bottom < 300 ? 'top' : 'bottom');
setPosition(viewportHeight - rect.bottom < threshold ? 'top' : 'bottom');
};

updatePosition();
Expand All @@ -20,7 +20,7 @@ const useDropdownPosition = (triggerRef: RefObject<HTMLButtonElement>) => {
window.removeEventListener('resize', updatePosition);
window.removeEventListener('scroll', updatePosition, true);
};
}, [triggerRef]);
}, [triggerRef, threshold]);

return position;
};
Expand Down
2 changes: 1 addition & 1 deletion src/components/ui/icon/icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ interface IconProps extends React.HTMLAttributes<HTMLSpanElement> {
iconSize?: IconSize; // 모바일 기본 사이즈
bigScreenSize?: IconResponsiveSize; // PC에서 사이즈 다를때 사용
className?: string;
ariaLabel: string; // 접근성 라벨
ariaLabel?: string; // 접근성 라벨
decorative?: boolean;
}

Expand Down
Loading