Merge pull request #30 from acid-info/topic-update-dropdown

Update Dropdown component
This commit is contained in:
jeangovil 2023-04-11 01:43:02 +03:30 committed by GitHub
commit e1e280ef00
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 134 additions and 65 deletions

View File

@ -1,15 +1,18 @@
export const dropdownClasses = { export const dropdownClasses = {
root: `lsd-dropdown`, root: `lsd-dropdown`,
trigger: `lsd-dropdown-trigger`, label: 'lsd-dropdown__label',
triggerLabel: `lsd-dropdown-trigger__label`, buttonContainer: `lsd-dropdown__button-container`,
triggerIcons: `lsd-dropdown-trigger-icons`,
triggerIcon: `lsd-dropdown-trigger-icons__icon`, trigger: `lsd-dropdown__trigger`,
triggerMenuIcon: `lsd-dropdown-trigger-icons__menu-icon`, optionLabel: `lsd-dropdown__option-label`,
icons: `lsd-dropdown__icons`,
icon: `lsd-dropdown__icon`,
menuIcon: `lsd-dropdown__menu-icon`,
supportingText: 'lsd-dropdown__supporting-text', supportingText: 'lsd-dropdown__supporting-text',
listBox: 'lsd-dropdown-list-box', listBox: 'lsd-dropdown__list-box',
open: 'lsd-dropdown--open', open: 'lsd-dropdown--open',
error: 'lsd-dropdown--error', error: 'lsd-dropdown--error',

View File

@ -12,6 +12,12 @@ export default {
}, },
defaultValue: 'large', defaultValue: 'large',
}, },
size: {
type: {
name: 'enum',
value: ['small', 'medium', 'large'],
},
},
}, },
} as Meta } as Meta
@ -22,8 +28,9 @@ export const Root: Story<DropdownProps> = (args) => (
) )
Root.args = { Root.args = {
id: 'cryptocurrency',
size: 'large', size: 'large',
label: 'Choose an option', triggerLabel: 'Choose an option',
supportingText: '', supportingText: '',
disabled: false, disabled: false,
error: false, error: false,
@ -34,4 +41,5 @@ Root.args = {
value: `${index}`, value: `${index}`,
name: `Option ${index + 1}`, name: `Option ${index + 1}`,
})), })),
label: 'Cryptocurrency',
} }

View File

@ -12,20 +12,28 @@ export const DropdownStyles = css`
.${dropdownClasses.trigger} { .${dropdownClasses.trigger} {
&:hover, &:hover,
&:focus { &:focus {
.${dropdownClasses.triggerLabel} { .${dropdownClasses.optionLabel} {
text-decoration: underline; text-decoration: underline;
} }
} }
} }
} }
.${dropdownClasses.label} {
display: block;
}
.${dropdownClasses.buttonContainer} {
display: flex;
justify-content: space-between;
}
.${dropdownClasses.trigger} { .${dropdownClasses.trigger} {
width: 100%; width: 100%;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
padding: 10px 14px 10px 18px;
border: none; border: none;
cursor: pointer; cursor: pointer;
@ -36,26 +44,27 @@ export const DropdownStyles = css`
} }
} }
.${dropdownClasses.triggerLabel} { .${dropdownClasses.optionLabel} {
cursor: inherit; cursor: inherit;
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
} }
.${dropdownClasses.triggerIcons} { .${dropdownClasses.icons} {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
justify-content: flex-end; justify-content: flex-end;
min-width: 60px; gap: 8px;
} }
.${dropdownClasses.triggerIcon} { .${dropdownClasses.icon} {
margin-right: 8px; display: flex;
align-items: center;
} }
.${dropdownClasses.triggerMenuIcon} { .${dropdownClasses.menuIcon} {
} }
.${dropdownClasses.supportingText} { .${dropdownClasses.supportingText} {
@ -63,16 +72,14 @@ export const DropdownStyles = css`
} }
.${dropdownClasses.error} { .${dropdownClasses.error} {
.${dropdownClasses.triggerLabel} { .${dropdownClasses.optionLabel} {
text-decoration: line-through; text-decoration: line-through;
} }
} }
.${dropdownClasses.disabled} { .${dropdownClasses.disabled} {
.${dropdownClasses.trigger} { opacity: 0.34;
opacity: 0.34; cursor: initial;
cursor: initial;
}
} }
.${dropdownClasses.listBox} { .${dropdownClasses.listBox} {
@ -90,23 +97,63 @@ export const DropdownStyles = css`
} }
} }
.${dropdownClasses.small} { .${dropdownClasses.large} {
width: 208px;
.${dropdownClasses.label} {
margin: 0 0 6px 18px;
}
.${dropdownClasses.buttonContainer} {
height: 40px;
}
.${dropdownClasses.trigger} { .${dropdownClasses.trigger} {
padding: 6px 10px; padding: 9px 17px;
} }
} }
.${dropdownClasses.medium} { .${dropdownClasses.medium} {
width: 188px;
.${dropdownClasses.label} {
margin: 0 0 6px 14px;
}
.${dropdownClasses.buttonContainer} {
height: 32px;
}
.${dropdownClasses.trigger} { .${dropdownClasses.trigger} {
padding: 6px 12px; padding: 5px 13px;
}
}
.${dropdownClasses.small} {
width: 164px;
.${dropdownClasses.label} {
margin: 0 0 6px 12px;
}
.${dropdownClasses.buttonContainer} {
height: 28px;
}
.${dropdownClasses.trigger} {
padding: 5px 11px;
} }
} }
.${dropdownClasses.outlined} { .${dropdownClasses.outlined} {
border: 1px solid rgb(var(--lsd-border-primary)); .${dropdownClasses.buttonContainer} {
border: 1px solid rgb(var(--lsd-border-primary));
}
} }
.${dropdownClasses.outlinedBottom} { .${dropdownClasses.outlinedBottom} {
border-bottom: 1px solid rgb(var(--lsd-border-primary)); .${dropdownClasses.buttonContainer} {
border-bottom: 1px solid rgb(var(--lsd-border-primary));
}
} }
` `

View File

@ -14,11 +14,12 @@ export type DropdownProps = Omit<
React.HTMLAttributes<HTMLDivElement>, React.HTMLAttributes<HTMLDivElement>,
'label' | 'disabled' | 'value' | 'onChange' 'label' | 'disabled' | 'value' | 'onChange'
> & { > & {
label: string label?: React.ReactNode
error?: boolean error?: boolean
disabled?: boolean disabled?: boolean
supportingText?: string supportingText?: string
size?: 'small' | 'medium' | 'large' size?: 'small' | 'medium' | 'large'
triggerLabel: string
multi?: boolean multi?: boolean
options?: DropdownOption[] options?: DropdownOption[]
@ -35,6 +36,7 @@ export const Dropdown: React.FC<DropdownProps> & {
error = false, error = false,
disabled = false, disabled = false,
supportingText, supportingText,
triggerLabel,
value = [], value = [],
onChange, onChange,
@ -43,7 +45,7 @@ export const Dropdown: React.FC<DropdownProps> & {
variant = 'outlined', variant = 'outlined',
...props ...props
}) => { }) => {
const ref = useRef<HTMLButtonElement>(null) const containerRef = useRef<HTMLDivElement>(null)
const [open, setOpen] = useState(false) const [open, setOpen] = useState(false)
const { select, isSelected, selected } = useSelect(options, value, { const { select, isSelected, selected } = useSelect(options, value, {
@ -62,8 +64,11 @@ export const Dropdown: React.FC<DropdownProps> & {
if (disabled && open) setOpen(false) if (disabled && open) setOpen(false)
}, [open, disabled]) }, [open, disabled])
const buttonId = props?.id ?? (props.id || 'dropdown') + '-input'
return ( return (
<div <div
ref={containerRef}
{...props} {...props}
className={clsx( className={clsx(
props.className, props.className,
@ -72,50 +77,56 @@ export const Dropdown: React.FC<DropdownProps> & {
error && dropdownClasses.error, error && dropdownClasses.error,
disabled && dropdownClasses.disabled, disabled && dropdownClasses.disabled,
open && dropdownClasses.open, open && dropdownClasses.open,
variant === 'outlined'
? dropdownClasses.outlined
: dropdownClasses.outlinedBottom,
)} )}
> >
<button {label && (
ref={ref}
className={clsx(
dropdownClasses.trigger,
variant === 'outlined'
? dropdownClasses.outlined
: dropdownClasses.outlinedBottom,
)}
onClick={onTrigger}
>
<Typography <Typography
color="primary" htmlFor={buttonId}
className={dropdownClasses.label}
variant="label2"
component="label" component="label"
variant={size === 'large' ? 'label1' : 'label2'}
className={dropdownClasses.triggerLabel}
> >
{selected.length > 0 {label}
? selected.map((opt) => opt.name).join(', ')
: label}
</Typography> </Typography>
<div className={dropdownClasses.triggerIcons}> )}
{error && ( <div className={dropdownClasses.buttonContainer}>
<ErrorIcon <button
color="primary" id={buttonId}
className={dropdownClasses.triggerIcon} className={clsx(dropdownClasses.trigger)}
/> onClick={onTrigger}
)} >
<Typography
{open ? ( color="primary"
<ArrowUpIcon component="label"
color="primary" variant={size === 'large' ? 'label1' : 'label2'}
className={dropdownClasses.triggerMenuIcon} className={dropdownClasses.optionLabel}
/> >
) : ( {selected.length > 0
<ArrowDownIcon ? selected.map((opt) => opt.name).join(', ')
color="primary" : triggerLabel}
className={dropdownClasses.triggerMenuIcon} </Typography>
/> <div className={dropdownClasses.icons}>
)} {error && (
</div> <ErrorIcon color="primary" className={dropdownClasses.icon} />
</button> )}
{open ? (
<ArrowUpIcon
color="primary"
className={dropdownClasses.menuIcon}
/>
) : (
<ArrowDownIcon
color="primary"
className={dropdownClasses.menuIcon}
/>
)}
</div>
</button>
</div>
{supportingText && ( {supportingText && (
<Typography <Typography
variant={size === 'large' ? 'label1' : 'label2'} variant={size === 'large' ? 'label1' : 'label2'}
@ -128,7 +139,7 @@ export const Dropdown: React.FC<DropdownProps> & {
<Portal id="dropdown"> <Portal id="dropdown">
<ListBox <ListBox
handleRef={ref} handleRef={containerRef}
open={open} open={open}
onClose={() => setOpen(false)} onClose={() => setOpen(false)}
className={dropdownClasses.listBox} className={dropdownClasses.listBox}