Newer
Older
import React from 'react';
import { twMerge } from 'tailwind-merge';
import { useSelect } from 'downshift';
import { Icon } from '@/shared/Icon';
type SelectProps = {
options: Array<{ id: number | string; name: string }>;
selectedId: number | string | null;
onChange: (selectedId: number | string) => void;
width?: string | number;
listClassName?: string;
testId?: string;
};
export const Select = ({
options,
selectedId,
onChange,
width = '100%',
listClassName = '',
testId = 'select-component',
}: SelectProps): React.JSX.Element => {
const selectedOption = options.find(option => option.id === selectedId) || null;
const {
isOpen,
highlightedIndex,
getToggleButtonProps,
getMenuProps,
getItemProps,
selectedItem,
} = useSelect({
items: options,
selectedItem: selectedOption,
onSelectedItemChange: ({ selectedItem: newSelectedItem }) => {
if (newSelectedItem) {
onChange(newSelectedItem.id);
}
},
itemToString: item => (item ? item.name : ''),
});
const widthStyle = typeof width === 'number' ? { width: `${width}px` } : { width };
return (
<div
data-testid={testId}
className={twMerge(
'relative rounded-t bg-white text-xs shadow-primary',
!isOpen && 'rounded-b',
)}
style={widthStyle}
>
<div
className={twMerge(
'flex cursor-pointer flex-row items-center justify-between rounded-t p-2',
)}
{...getToggleButtonProps()}
>
<span data-testid="dropdown-button-name" className="font-medium">
{selectedItem ? selectedItem.name : 'Select an option'}
</span>
<Icon
name="chevron-down"
className={twMerge('arrow-button h-6 w-6 fill-primary-500', isOpen && 'rotate-180')}
/>
</div>
<ul
className={twMerge(
'absolute z-20 overflow-auto rounded-b bg-white shadow-lg',
!isOpen && 'hidden',
listClassName,
)}
style={widthStyle}
{...getMenuProps()}
>
{isOpen &&
options.map((item, index) => (
<li
className={twMerge(
'border-t',
highlightedIndex === index && 'text-primary-500',
selectedItem?.id === item.id && 'font-bold',
'flex flex-col p-2 shadow-sm',
)}
key={item.id}
{...getItemProps({ item, index })}
>
<span>{item.name}</span>
</li>
))}
</ul>
</div>
);
};