️ feat: add hideValues prop to hide the values when on multiple

This commit is contained in:
Wahyu Kurniawan 2024-02-24 13:48:57 +07:00
parent 410def0750
commit 54c2e8ec0b
No known key found for this signature in database
GPG Key ID: 040A1549143A8E33
4 changed files with 294 additions and 23 deletions

View File

@ -34,7 +34,7 @@ export const selectTheme = tv({
], ],
icon: ['text-elements-mid-em'], icon: ['text-elements-mid-em'],
helperIcon: [], helperIcon: [],
helperText: ['flex', 'gap-2', 'items-center', 'text-elements-danger'], helperText: ['flex', 'gap-2', 'items-center', 'text-elements-low-em'],
popover: [ popover: [
'z-20', 'z-20',
'absolute', 'absolute',
@ -70,12 +70,8 @@ export const selectTheme = tv({
container: [], container: [],
}, },
}, },
state: { error: {
default: { true: {
inputWrapper: '',
helperText: ['text-elements-low-em'],
},
error: {
inputWrapper: [ inputWrapper: [
'outline', 'outline',
'outline-offset-0', 'outline-offset-0',
@ -121,6 +117,11 @@ export const selectTheme = tv({
input: ['cursor-pointer'], input: ['cursor-pointer'],
}, },
}, },
hideValues: {
true: {
input: ['placeholder:text-elements-mid-em'],
},
},
}, },
compoundVariants: [ compoundVariants: [
{ {
@ -143,6 +144,7 @@ export const selectTheme = tv({
variant: 'default', variant: 'default',
size: 'md', size: 'md',
state: 'default', state: 'default',
error: false,
isOpen: false, isOpen: false,
hasValue: false, hasValue: false,
}, },

View File

@ -91,6 +91,10 @@ interface SelectProps
* The helper text of the select * The helper text of the select
*/ */
helperText?: string; helperText?: string;
/**
* Show the values of the select if it's multiple
*/
hideValues?: boolean;
} }
export const Select = ({ export const Select = ({
@ -99,7 +103,7 @@ export const Select = ({
searchable = false, searchable = false,
clearable, clearable,
size, size,
state, error,
orientation = 'horizontal', orientation = 'horizontal',
variant, variant,
label, label,
@ -107,9 +111,10 @@ export const Select = ({
leftIcon, leftIcon,
rightIcon, rightIcon,
helperText, helperText,
hideValues = false,
placeholder: placeholderProp = 'Select an option', placeholder: placeholderProp = 'Select an option',
}: SelectProps) => { }: SelectProps) => {
const theme = selectTheme({ size, state, variant, orientation }); const theme = selectTheme({ size, error, variant, orientation });
const [inputValue, setInputValue] = useState(''); const [inputValue, setInputValue] = useState('');
const [selectedItem, setSelectedItem] = useState<SelectOption | null>(null); const [selectedItem, setSelectedItem] = useState<SelectOption | null>(null);
@ -273,20 +278,28 @@ export const Select = ({
const renderHelperText = useMemo( const renderHelperText = useMemo(
() => ( () => (
<div className={theme.helperText()}> <div className={theme.helperText()}>
{state === 'error' && {error &&
cloneIcon(<WarningIcon className={theme.helperIcon()} />, { cloneIcon(<WarningIcon className={theme.helperIcon()} />, {
'aria-hidden': true, 'aria-hidden': true,
})} })}
<p>{helperText}</p> <p>{helperText}</p>
</div> </div>
), ),
[cloneIcon, state, theme, helperText], [cloneIcon, error, theme, helperText],
); );
const isMultipleHasValue = multiple && selectedItems.length > 0; const isMultipleHasValue = multiple && selectedItems.length > 0;
const isMultipleHasValueButNotSearchable = const isMultipleHasValueButNotSearchable =
multiple && !searchable && selectedItems.length > 0; multiple && !searchable && selectedItems.length > 0;
const placeholder = isMultipleHasValueButNotSearchable ? '' : placeholderProp; const displayPlaceholder = useMemo(() => {
if (hideValues && isMultipleHasValue) {
return `${selectedItems.length} selected`;
}
if (isMultipleHasValueButNotSearchable) {
return '';
}
return placeholderProp;
}, [hideValues, multiple, selectedItems.length, placeholderProp]);
return ( return (
<div className={theme.container()}> <div className={theme.container()}>
@ -297,7 +310,7 @@ export const Select = ({
<div <div
ref={inputWrapperRef} ref={inputWrapperRef}
className={theme.inputWrapper({ className={theme.inputWrapper({
hasValue: isMultipleHasValue, hasValue: isMultipleHasValue && !hideValues,
})} })}
onClick={() => !dropdownOpen && openMenu()} onClick={() => !dropdownOpen && openMenu()}
> >
@ -306,6 +319,7 @@ export const Select = ({
{/* Multiple input values */} {/* Multiple input values */}
{isMultipleHasValue && {isMultipleHasValue &&
!hideValues &&
selectedItems.map((item, index) => ( selectedItems.map((item, index) => (
<SelectValue <SelectValue
key={`selected-item-${index}`} key={`selected-item-${index}`}
@ -319,15 +333,21 @@ export const Select = ({
{/* Single input value or searchable area */} {/* Single input value or searchable area */}
<input <input
{...getInputProps(getDropdownProps())} {...getInputProps(getDropdownProps())}
placeholder={placeholder} placeholder={displayPlaceholder}
// Control readOnly based on searchable // Control readOnly based on searchable
readOnly={!searchable} readOnly={!searchable || hideValues}
className={cn(theme.input({ searchable }), { className={cn(
// Make the input width smaller because we don't need it (not searchable) theme.input({
'w-6': isMultipleHasValueButNotSearchable, searchable,
// Add margin to the X icon hideValues: hideValues && selectedItems.length > 0,
'ml-6': isMultipleHasValueButNotSearchable && clearable, }),
})} {
// Make the input width smaller because we don't need it (not searchable)
'w-6': isMultipleHasValueButNotSearchable && !hideValues,
// Add margin to the X icon
'ml-6': isMultipleHasValueButNotSearchable && clearable,
},
)}
/> />
{/* Right icon */} {/* Right icon */}
@ -339,12 +359,12 @@ export const Select = ({
{/* Popover */} {/* Popover */}
<ul <ul
{...getMenuProps()} {...getMenuProps({ ref: popoverRef }, { suppressRefError: true })}
id="popover" id="popover"
ref={popoverRef} ref={popoverRef}
className={cn(theme.popover({ isOpen }), { className={cn(theme.popover({ isOpen }), {
// Position the popover based on the dropdown position // Position the popover based on the dropdown position
'top-[12.5%]': dropdownPosition === 'bottom' && !label, 'top-[27.5%]': dropdownPosition === 'bottom' && !label,
'top-[35%]': dropdownPosition === 'bottom' && label, 'top-[35%]': dropdownPosition === 'bottom' && label,
'top-[42.5%]': dropdownPosition === 'bottom' && label && description, 'top-[42.5%]': dropdownPosition === 'bottom' && label && description,
'bottom-[92.5%]': dropdownPosition === 'top' && !label, 'bottom-[92.5%]': dropdownPosition === 'top' && !label,

View File

@ -31,6 +31,7 @@ import {
import { renderDefaultTag, renderMinimalTag } from './renders/tag'; import { renderDefaultTag, renderMinimalTag } from './renders/tag';
import { renderToast, renderToastsWithCta } from './renders/toast'; import { renderToast, renderToastsWithCta } from './renders/toast';
import { renderTooltips } from './renders/tooltip'; import { renderTooltips } from './renders/tooltip';
import { renderDropdowns } from './renders/dropdown';
type ValuePiece = Date | null; type ValuePiece = Date | null;
type Value = ValuePiece | [ValuePiece, ValuePiece]; type Value = ValuePiece | [ValuePiece, ValuePiece];
@ -270,6 +271,14 @@ const Page: React.FC = () => {
<div className="w-full h border border-gray-200 px-20 my-10" /> <div className="w-full h border border-gray-200 px-20 my-10" />
{/* Dropdown */}
<div className="flex flex-col gap-10 items-center justify-between">
<h1 className="text-2xl font-bold">Dropdown / Select</h1>
{renderDropdowns()}
</div>
<div className="w-full h border border-gray-200 px-20 my-10" />
{/* Inline notification */} {/* Inline notification */}
<div className="flex flex-col gap-10 items-center justify-between"> <div className="flex flex-col gap-10 items-center justify-between">
<h1 className="text-2xl font-bold">Inline Notification</h1> <h1 className="text-2xl font-bold">Inline Notification</h1>

View File

@ -0,0 +1,240 @@
import React from 'react';
import { PencilIcon } from 'components/shared/CustomIcon';
import { SelectOption, Select } from 'components/shared/Select';
export const DROPDOWN_ITEMS: SelectOption[] = [
{
value: 'apple',
label: 'Apple',
description: 'Apple is fruit',
leftIcon: <PencilIcon />,
},
{
value: 'banana',
label: 'Banana',
description: 'Banana is fruit',
leftIcon: <PencilIcon />,
},
{
value: 'orange',
label: 'Orange',
description: 'Orange is fruit',
leftIcon: <PencilIcon />,
},
{
value: 'watermelon',
label: 'Watermelon',
description: 'Watermelon is fruit',
disabled: true,
leftIcon: <PencilIcon />,
},
];
export const renderDropdowns = () => (
<>
<p className="text-sm text-center text-gray-500 -mb-8">Single Small</p>
<div className="flex gap-4 flex-wrap justify-center">
<Select size="sm" placeholder="Default" options={DROPDOWN_ITEMS} />
<Select
size="sm"
placeholder="Clearable"
clearable
options={DROPDOWN_ITEMS}
/>
<Select
size="sm"
searchable
placeholder="Searchable"
options={DROPDOWN_ITEMS}
/>
<Select
size="sm"
placeholder="Vertical"
orientation="vertical"
options={DROPDOWN_ITEMS}
/>
</div>
<p className="text-sm text-center text-gray-500 -mb-8">Single Medium</p>
<div className="flex gap-4 flex-wrap justify-center">
<Select placeholder="Default" options={DROPDOWN_ITEMS} />
<Select placeholder="Clearable" clearable options={DROPDOWN_ITEMS} />
<Select searchable placeholder="Searchable" options={DROPDOWN_ITEMS} />
<Select
placeholder="Vertical"
orientation="vertical"
options={DROPDOWN_ITEMS}
/>
</div>
<p className="text-sm text-center text-gray-500 -mb-8">Multiple Small</p>
<div className="flex gap-4 flex-wrap justify-center">
<Select
multiple
size="sm"
placeholder="Default"
options={DROPDOWN_ITEMS}
/>
<Select
multiple
size="sm"
placeholder="Clearable"
clearable
options={DROPDOWN_ITEMS}
/>
<Select
searchable
multiple
size="sm"
placeholder="Searchable"
options={DROPDOWN_ITEMS}
/>
<Select
multiple
size="sm"
placeholder="Vertical"
orientation="vertical"
options={DROPDOWN_ITEMS}
/>
<Select
multiple
hideValues
size="sm"
orientation="vertical"
placeholder="Hide values"
options={DROPDOWN_ITEMS}
/>
</div>
<p className="text-sm text-center text-gray-500 -mb-8">Multiple Medium</p>
<div className="flex gap-4 flex-wrap justify-center">
<Select multiple placeholder="Default" options={DROPDOWN_ITEMS} />
<Select
multiple
placeholder="Clearable"
clearable
options={DROPDOWN_ITEMS}
/>
<Select
searchable
multiple
placeholder="Searchable"
options={DROPDOWN_ITEMS}
/>
<Select
multiple
placeholder="Vertical"
orientation="vertical"
options={DROPDOWN_ITEMS}
/>
<Select
multiple
hideValues
orientation="vertical"
placeholder="Hide values"
options={DROPDOWN_ITEMS}
/>
</div>
<p className="text-sm text-center text-gray-500 -mb-4">
Single With label, description, and helper text
</p>
<div className="flex gap-4 flex-wrap justify-center">
<Select
label="Default"
description="Single select component"
helperText="This is a helper text"
options={DROPDOWN_ITEMS}
/>
<Select
label="Clearable"
description="Single select component"
helperText="This is a helper text"
clearable
options={DROPDOWN_ITEMS}
/>
<Select
searchable
label="Searchable"
description="Single select component"
helperText="This is a helper text"
options={DROPDOWN_ITEMS}
/>
<Select
label="Vertical"
description="Single select component"
helperText="This is a helper text"
orientation="vertical"
options={DROPDOWN_ITEMS}
/>
</div>
<p className="text-sm text-center text-gray-500 -mb-4">
Multiple With label, description, and helper text
</p>
<div className="flex gap-4 flex-wrap justify-center">
<Select
label="Default"
description="Multiple select component"
helperText="This is a helper text"
multiple
options={DROPDOWN_ITEMS}
/>
<Select
label="Clearable"
description="Multiple select component"
helperText="This is a helper text"
multiple
clearable
options={DROPDOWN_ITEMS}
/>
<Select
searchable
label="Searchable"
description="Multiple select component"
helperText="This is a helper text"
multiple
options={DROPDOWN_ITEMS}
/>
<Select
label="Vertical"
description="Multiple select component"
helperText="This is a helper text"
multiple
orientation="vertical"
options={DROPDOWN_ITEMS}
/>
</div>
<p className="text-sm text-center text-gray-500 -mb-4">
Error With label, description, and helper text
</p>
<div className="flex gap-4 flex-wrap justify-center">
<Select
error
label="Default"
description="Multiple select component"
helperText="This is a helper text"
options={DROPDOWN_ITEMS}
/>
<Select
error
label="Clearable"
description="Multiple select component"
helperText="This is a helper text"
clearable
options={DROPDOWN_ITEMS}
/>
<Select
error
searchable
label="Searchable"
description="Multiple select component"
helperText="This is a helper text"
options={DROPDOWN_ITEMS}
/>
<Select
error
label="Vertical"
description="Multiple select component"
helperText="This is a helper text"
orientation="vertical"
options={DROPDOWN_ITEMS}
/>
</div>
</>
);