Merge branch 'dev' of github.com:smart-chain-fr/leCoffre-front into dev
This commit is contained in:
commit
c73b78bd26
@ -6,6 +6,7 @@ import { IOption } from "../Dropdown/DropdownMenu/DropdownOption";
|
||||
import SearchBar from "../SearchBar";
|
||||
import Typography, { ETypo, ETypoColor } from "../Typography";
|
||||
import classes from "./classes.module.scss";
|
||||
import { getLabel } from "../Dropdown";
|
||||
|
||||
type IProps = {
|
||||
options: IOption[];
|
||||
@ -51,24 +52,21 @@ export default function Autocomplete(props: IProps) {
|
||||
}, [selectedOptionProps]);
|
||||
|
||||
const handleSelectOption = useCallback(
|
||||
(option: IOption) => {
|
||||
setSelectedOption(option);
|
||||
setSearchValue(getLabel(option) || "");
|
||||
(newOption: IOption, _options: IOption[]) => {
|
||||
setSelectedOption(newOption);
|
||||
setSearchValue(getLabel(newOption) || "");
|
||||
openable.close();
|
||||
},
|
||||
[openable],
|
||||
);
|
||||
|
||||
function getLabel(option: IOption | null): string | null {
|
||||
if (!option) return null;
|
||||
if (typeof option.label === "string") {
|
||||
return option.label;
|
||||
}
|
||||
return `${option.label.text} ${option.label.subtext}`;
|
||||
}
|
||||
|
||||
return (
|
||||
<DropdownMenu options={filteredOptions} openable={openable} onSelect={handleSelectOption} selectedOption={selectedOption}>
|
||||
<DropdownMenu
|
||||
options={filteredOptions}
|
||||
openable={openable}
|
||||
onSelect={handleSelectOption}
|
||||
selectedOptions={selectedOption ? [selectedOption] : []}
|
||||
>
|
||||
<div className={classes["root"]}>
|
||||
{label && (
|
||||
<Typography className={classes["label"]} typo={ETypo.TEXT_MD_REGULAR} color={ETypoColor.CONTRAST_DEFAULT}>
|
||||
@ -76,7 +74,14 @@ export default function Autocomplete(props: IProps) {
|
||||
</Typography>
|
||||
)}
|
||||
</div>
|
||||
<SearchBar placeholder={placeholder} disabled={disabled} onChange={handleSearchChange} value={searchValue} />
|
||||
<SearchBar
|
||||
placeholder={placeholder}
|
||||
disabled={disabled}
|
||||
onChange={handleSearchChange}
|
||||
value={searchValue}
|
||||
onClear={() => setSelectedOption(null)}
|
||||
onFocus={openable.open}
|
||||
/>
|
||||
</DropdownMenu>
|
||||
);
|
||||
}
|
||||
|
@ -0,0 +1,20 @@
|
||||
@import "@Themes/constants.scss";
|
||||
|
||||
.root {
|
||||
width: fit-content;
|
||||
height: 32px;
|
||||
|
||||
display: inline-flex;
|
||||
padding: 4px 12px;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
|
||||
border-radius: var(--input-chip-radius, 360px);
|
||||
border: 1px solid var(--input-chip-default-border, #b7d1f1);
|
||||
background: var(--input-chip-default-background, #e5eefa);
|
||||
|
||||
&:hover {
|
||||
background-color: var(--input-chip-hovered-background);
|
||||
border-color: var(--input-chip-hovered-border);
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
import { XMarkIcon } from "@heroicons/react/24/outline";
|
||||
import classNames from "classnames";
|
||||
import React from "react";
|
||||
|
||||
import IconButton from "../../IconButton";
|
||||
import Typography, { ETypo, ETypoColor } from "../../Typography";
|
||||
import classes from "./classes.module.scss";
|
||||
|
||||
type IProps = {
|
||||
text: string;
|
||||
className?: string;
|
||||
onDelete?: () => void;
|
||||
};
|
||||
|
||||
export default function Chip(props: IProps) {
|
||||
const { className, text, onDelete } = props;
|
||||
|
||||
return (
|
||||
<div className={classNames(classes["root"], className)}>
|
||||
<Typography typo={ETypo.TEXT_MD_SEMIBOLD} color={ETypoColor.INPUT_CHIP_CONTRAST}>
|
||||
{text}
|
||||
</Typography>
|
||||
<IconButton icon={<XMarkIcon />} onClick={onDelete} />
|
||||
</div>
|
||||
);
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
@import "@Themes/constants.scss";
|
||||
|
||||
.root {
|
||||
border-radius: var(--input-radius, 0px);
|
||||
border: 1px solid var(--input-main-border-filled, #6d7e8a);
|
||||
background: var(--input-background, #fff);
|
||||
|
||||
svg {
|
||||
stroke: var(--button-icon-button-default-default);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border-radius: var(--input-radius, 0px);
|
||||
border: 1px solid var(--input-main-border-hovered, #b4bec5);
|
||||
background: var(--input-background, #fff);
|
||||
}
|
||||
|
||||
&[data-has-value="true"] {
|
||||
border-radius: var(--input-radius, 0px);
|
||||
border: 1px solid var(--input-main-border-filled, #6d7e8a);
|
||||
background: var(--input-background, #fff);
|
||||
}
|
||||
|
||||
&[data-is-focused="true"] {
|
||||
border-radius: var(--input-radius, 0px);
|
||||
border: 1px solid var(--input-main-border-focused, #005bcb);
|
||||
background: var(--input-background, #fff);
|
||||
}
|
||||
|
||||
&[data-is-disabled="true"] {
|
||||
opacity: var(--opacity-disabled, 0.3);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
align-content: center;
|
||||
gap: 16px var(--spacing-2, 16px);
|
||||
flex-wrap: wrap;
|
||||
|
||||
min-height: 56px;
|
||||
|
||||
padding: var(--spacing-1-5, 12px) var(--spacing-sm, 8px);
|
||||
|
||||
.input {
|
||||
flex: 1;
|
||||
border: none;
|
||||
|
||||
color: var(--input-placeholder-filled, #24282e);
|
||||
|
||||
/* text/md/semibold */
|
||||
font-family: var(--font-text-family, Poppins);
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
font-weight: var(--font-text-weight-semibold, 600);
|
||||
line-height: normal;
|
||||
letter-spacing: 0.08px;
|
||||
width: 100%;
|
||||
|
||||
&::placeholder {
|
||||
color: var(--input-placeholder-empty, #6d7e8a);
|
||||
/* text/md/regular */
|
||||
font-family: var(--font-text-family, Poppins);
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
font-weight: var(--font-text-weight-regular, 400);
|
||||
line-height: normal;
|
||||
letter-spacing: 0.08px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,90 @@
|
||||
import React, { useCallback, useEffect } from "react";
|
||||
|
||||
import { getLabel } from "../../Dropdown";
|
||||
import { IOption } from "../../Dropdown/DropdownMenu/DropdownOption";
|
||||
import Chip from "../Chip";
|
||||
import classes from "./classes.module.scss";
|
||||
|
||||
type IProps = {
|
||||
selectedOptions: IOption[];
|
||||
onSelectedOptionsChange: (options: IOption[]) => void;
|
||||
onChange?: (input: string) => void;
|
||||
value?: string;
|
||||
placeholder?: string;
|
||||
disabled?: boolean;
|
||||
onClear?: () => void;
|
||||
onFocus?: () => void;
|
||||
onBlur?: () => void;
|
||||
};
|
||||
|
||||
export default function ChipContainer(props: IProps) {
|
||||
const {
|
||||
selectedOptions,
|
||||
onChange,
|
||||
value: propValue,
|
||||
placeholder = "Rechercher",
|
||||
disabled = false,
|
||||
onFocus,
|
||||
onBlur,
|
||||
onSelectedOptionsChange,
|
||||
} = props;
|
||||
|
||||
const [isFocused, setIsFocused] = React.useState(false);
|
||||
const [value, setValue] = React.useState(propValue || "");
|
||||
|
||||
const changeValue = useCallback(
|
||||
(value: string) => {
|
||||
setValue(value);
|
||||
onChange && onChange(value);
|
||||
},
|
||||
[onChange],
|
||||
);
|
||||
|
||||
const handleOnChange = useCallback((event: React.ChangeEvent<HTMLInputElement>) => changeValue(event.target.value), [changeValue]);
|
||||
|
||||
const handleFocus = useCallback(() => {
|
||||
setIsFocused(true);
|
||||
onFocus?.();
|
||||
}, [onFocus]);
|
||||
|
||||
const handleBlur = useCallback(
|
||||
(e: React.FocusEvent<HTMLInputElement, Element>) => {
|
||||
setIsFocused(false);
|
||||
onBlur?.();
|
||||
},
|
||||
[onBlur],
|
||||
);
|
||||
|
||||
const onChipDelete = useCallback(
|
||||
(option: IOption) => {
|
||||
const newSelectedOptions = selectedOptions.filter((selectedOption) => selectedOption.id !== option.id);
|
||||
onSelectedOptionsChange && onSelectedOptionsChange(newSelectedOptions);
|
||||
},
|
||||
[selectedOptions, onSelectedOptionsChange],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (propValue !== undefined) {
|
||||
setValue(propValue);
|
||||
}
|
||||
}, [propValue]);
|
||||
|
||||
return (
|
||||
<div className={classes["root"]} data-is-focused={isFocused} data-has-value={value !== ""} data-is-disabled={disabled}>
|
||||
<div className={classes["content"]}>
|
||||
{selectedOptions.map((option) => (
|
||||
<Chip key={option.id} text={getLabel(option) ?? ""} onDelete={() => onChipDelete(option)} />
|
||||
))}
|
||||
<input
|
||||
type="text"
|
||||
value={value}
|
||||
placeholder={placeholder}
|
||||
className={classes["input"]}
|
||||
onChange={handleOnChange}
|
||||
onFocus={handleFocus}
|
||||
onBlur={handleBlur}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
@import "@Themes/constants.scss";
|
||||
|
||||
.root {
|
||||
.label {
|
||||
padding: 0px var(--spacing-2, 16px);
|
||||
}
|
||||
}
|
@ -0,0 +1,88 @@
|
||||
import useOpenable from "@Front/Hooks/useOpenable";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
|
||||
import DropdownMenu from "../Dropdown/DropdownMenu";
|
||||
import { IOption } from "../Dropdown/DropdownMenu/DropdownOption";
|
||||
import Typography, { ETypo, ETypoColor } from "../Typography";
|
||||
import classes from "./classes.module.scss";
|
||||
import ChipContainer from "./ChipContainer";
|
||||
import { getLabel } from "../Dropdown";
|
||||
|
||||
type IProps = {
|
||||
options: IOption[];
|
||||
placeholder?: string;
|
||||
disabled?: boolean;
|
||||
label?: string;
|
||||
onSelect?: (option: IOption) => void;
|
||||
selectedOptions?: IOption[] | null;
|
||||
};
|
||||
|
||||
export default function AutocompleteMultiSelect(props: IProps) {
|
||||
const { options, placeholder, disabled, label, selectedOptions: selectedOptionsProps } = props;
|
||||
const [selectedOptions, setSelectedOptions] = useState<IOption[] | null>(selectedOptionsProps ?? null);
|
||||
const [searchValue, setSearchValue] = useState("");
|
||||
const [filteredOptions, setFilteredOptions] = useState<IOption[]>(options);
|
||||
const openable = useOpenable({ defaultOpen: false });
|
||||
|
||||
useEffect(() => {
|
||||
if (searchValue) {
|
||||
const filteredOptions = options.filter((option) => getLabel(option)?.toLowerCase().includes(searchValue.toLowerCase()));
|
||||
console.log(filteredOptions);
|
||||
if (filteredOptions.length === 0)
|
||||
return setFilteredOptions([{ id: "no-results", label: "Aucun résulats", notSelectable: true }]);
|
||||
return setFilteredOptions(filteredOptions);
|
||||
}
|
||||
return setFilteredOptions(options);
|
||||
}, [searchValue, options]);
|
||||
|
||||
const handleSearchChange = useCallback(
|
||||
(value: string) => {
|
||||
setSearchValue(value);
|
||||
if (value) {
|
||||
openable.open();
|
||||
} else {
|
||||
openable.close();
|
||||
}
|
||||
},
|
||||
[openable],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
setSelectedOptions(selectedOptionsProps ?? null);
|
||||
}, [selectedOptionsProps]);
|
||||
|
||||
const handleSelectOption = useCallback(
|
||||
(_newOption: IOption, options: IOption[]) => {
|
||||
setSelectedOptions(options);
|
||||
setSearchValue("");
|
||||
openable.close();
|
||||
},
|
||||
[openable],
|
||||
);
|
||||
|
||||
return (
|
||||
<DropdownMenu
|
||||
options={filteredOptions}
|
||||
openable={openable}
|
||||
onSelect={handleSelectOption}
|
||||
selectedOptions={selectedOptions ? selectedOptions : []}>
|
||||
<div className={classes["root"]}>
|
||||
{label && (
|
||||
<Typography className={classes["label"]} typo={ETypo.TEXT_MD_REGULAR} color={ETypoColor.CONTRAST_DEFAULT}>
|
||||
{label}
|
||||
</Typography>
|
||||
)}
|
||||
</div>
|
||||
<ChipContainer
|
||||
placeholder={placeholder}
|
||||
disabled={disabled}
|
||||
onChange={handleSearchChange}
|
||||
value={searchValue}
|
||||
onClear={() => setSelectedOptions(null)}
|
||||
onFocus={openable.open}
|
||||
selectedOptions={selectedOptions ?? []}
|
||||
onSelectedOptionsChange={setSelectedOptions}
|
||||
/>
|
||||
</DropdownMenu>
|
||||
);
|
||||
}
|
@ -1,12 +1,12 @@
|
||||
import classNames from "classnames";
|
||||
import React, { useCallback } from "react";
|
||||
import React, { useCallback, useEffect, useRef } from "react";
|
||||
|
||||
import classes from "./classes.module.scss";
|
||||
import DropdownOption, { IOption } from "./DropdownOption";
|
||||
|
||||
type IProps = {
|
||||
options: IOption[];
|
||||
selectedOption: IOption | null;
|
||||
selectedOptions: IOption[];
|
||||
children: React.ReactNode;
|
||||
openable: {
|
||||
isOpen: boolean;
|
||||
@ -14,21 +14,40 @@ type IProps = {
|
||||
close: () => void;
|
||||
toggle: () => void;
|
||||
};
|
||||
onSelect?: (option: IOption) => void;
|
||||
onSelect?: (newOption: IOption, options: IOption[]) => void;
|
||||
};
|
||||
export default function DropdownMenu(props: IProps) {
|
||||
const { children, options, onSelect, openable, selectedOption } = props;
|
||||
const { children, options, onSelect, openable, selectedOptions } = props;
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
|
||||
const handleSelect = useCallback(
|
||||
(option: IOption) => {
|
||||
onSelect?.(option);
|
||||
const newOptions = selectedOptions.some((selectedOption) => selectedOption.id === option.id)
|
||||
? selectedOptions
|
||||
: [...selectedOptions, option];
|
||||
|
||||
onSelect?.(option, newOptions);
|
||||
openable.close();
|
||||
},
|
||||
[onSelect, openable],
|
||||
[onSelect, openable, selectedOptions],
|
||||
);
|
||||
|
||||
const handleClickOutside = useCallback(
|
||||
(event: MouseEvent) => {
|
||||
if (ref.current && !ref.current.contains(event.target as Node)) {
|
||||
openable.close();
|
||||
}
|
||||
},
|
||||
[openable],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
document.addEventListener("mousedown", handleClickOutside);
|
||||
return () => document.removeEventListener("mousedown", handleClickOutside);
|
||||
}, [handleClickOutside]);
|
||||
|
||||
return (
|
||||
<div className={classNames([classes["root"], openable.isOpen && classes["open"]])}>
|
||||
<div className={classNames([classes["root"], openable.isOpen && classes["open"]])} ref={ref}>
|
||||
{children}
|
||||
<div className={classes["content"]}>
|
||||
{options.map((option) => {
|
||||
@ -39,6 +58,6 @@ export default function DropdownMenu(props: IProps) {
|
||||
);
|
||||
|
||||
function isActive(option: IOption): boolean {
|
||||
return selectedOption?.id === option.id;
|
||||
return selectedOptions.some((selectedOption) => selectedOption.id === option.id);
|
||||
}
|
||||
}
|
||||
|
@ -27,15 +27,19 @@ export default function Dropdown(props: IProps) {
|
||||
}, [selectedOptionProps]);
|
||||
|
||||
const handleOnSelect = useCallback(
|
||||
(option: IOption) => {
|
||||
setSelectedOption(option);
|
||||
onSelect?.(option);
|
||||
(newOption: IOption, _options: IOption[]) => {
|
||||
setSelectedOption(newOption);
|
||||
onSelect?.(newOption);
|
||||
},
|
||||
[onSelect],
|
||||
);
|
||||
|
||||
return (
|
||||
<DropdownMenu options={options} openable={openable} onSelect={handleOnSelect} selectedOption={selectedOption}>
|
||||
<DropdownMenu
|
||||
options={options}
|
||||
openable={openable}
|
||||
onSelect={handleOnSelect}
|
||||
selectedOptions={selectedOption ? [selectedOption] : []}>
|
||||
<div className={classes["root"]}>
|
||||
{label && (
|
||||
<Typography className={classes["label"]} typo={ETypo.TEXT_MD_REGULAR} color={ETypoColor.CONTRAST_DEFAULT}>
|
||||
|
@ -94,6 +94,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
&.default {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
cursor: default;
|
||||
opacity: var(--opacity-disabled, 0.3);
|
||||
|
@ -1,16 +1,21 @@
|
||||
import { MagnifyingGlassIcon, XMarkIcon } from "@heroicons/react/24/outline";
|
||||
import React, { useCallback, useEffect } from "react";
|
||||
|
||||
import classes from "./classes.module.scss";
|
||||
import { MagnifyingGlassIcon, XMarkIcon } from "@heroicons/react/24/outline";
|
||||
|
||||
type IProps = {
|
||||
onChange?: (input: string) => void;
|
||||
value?: string;
|
||||
placeholder?: string;
|
||||
disabled?: boolean;
|
||||
onClear?: () => void;
|
||||
onFocus?: () => void;
|
||||
onBlur?: () => void;
|
||||
};
|
||||
|
||||
export default function SearchBar({ onChange, value: propValue, placeholder = "Rechercher", disabled = false }: IProps) {
|
||||
export default function SearchBar(props: IProps) {
|
||||
const { onChange, value: propValue, placeholder = "Rechercher", disabled = false, onClear, onFocus, onBlur } = props;
|
||||
|
||||
const [isFocused, setIsFocused] = React.useState(false);
|
||||
const [value, setValue] = React.useState(propValue || "");
|
||||
|
||||
@ -22,10 +27,25 @@ export default function SearchBar({ onChange, value: propValue, placeholder = "R
|
||||
[onChange],
|
||||
);
|
||||
|
||||
const handleOnChange = (event: React.ChangeEvent<HTMLInputElement>) => changeValue(event.target.value);
|
||||
const handleFocus = () => setIsFocused(true);
|
||||
const handleBlur = () => setIsFocused(false);
|
||||
const clearValue = () => changeValue("");
|
||||
const handleOnChange = useCallback((event: React.ChangeEvent<HTMLInputElement>) => changeValue(event.target.value), [changeValue]);
|
||||
|
||||
const handleFocus = useCallback(() => {
|
||||
setIsFocused(true);
|
||||
onFocus?.();
|
||||
}, [onFocus]);
|
||||
|
||||
const handleBlur = useCallback(
|
||||
(e: React.FocusEvent<HTMLInputElement, Element>) => {
|
||||
setIsFocused(false);
|
||||
onBlur?.();
|
||||
},
|
||||
[onBlur],
|
||||
);
|
||||
|
||||
const clearValue = useCallback(() => {
|
||||
changeValue("");
|
||||
onClear?.();
|
||||
}, [changeValue, onClear]);
|
||||
|
||||
useEffect(() => {
|
||||
if (propValue !== undefined) {
|
||||
|
@ -159,6 +159,8 @@ export enum ETypoColor {
|
||||
|
||||
DROPDOWN_CONTRAST_DEFAULT = "--dropdown-contrast-default",
|
||||
DROPDOWN_CONTRAST_ACTIVE = "--dropdown-contrast-active",
|
||||
|
||||
INPUT_CHIP_CONTRAST = "--input-chip-contrast",
|
||||
}
|
||||
|
||||
export default function Typography(props: IProps) {
|
||||
|
@ -1,4 +1,6 @@
|
||||
import Alert, { EAlertVariant } from "@Front/Components/DesignSystem/Alert";
|
||||
import Autocomplete from "@Front/Components/DesignSystem/Autocomplete";
|
||||
import AutocompleteMultiSelect from "@Front/Components/DesignSystem/AutocompleteMultiSelect";
|
||||
import Button, { EButtonSize, EButtonstyletype, EButtonVariant } from "@Front/Components/DesignSystem/Button";
|
||||
import CircleProgress from "@Front/Components/DesignSystem/CircleProgress";
|
||||
import Dropdown from "@Front/Components/DesignSystem/Dropdown";
|
||||
@ -19,19 +21,10 @@ import NumberPicker from "@Front/Components/Elements/NumberPicker";
|
||||
import Tabs from "@Front/Components/Elements/Tabs";
|
||||
import DefaultTemplate from "@Front/Components/LayoutTemplates/DefaultTemplate";
|
||||
import useOpenable from "@Front/Hooks/useOpenable";
|
||||
import {
|
||||
ArchiveBoxIcon,
|
||||
ArrowLongLeftIcon,
|
||||
ArrowLongRightIcon,
|
||||
EllipsisHorizontalIcon,
|
||||
PencilSquareIcon,
|
||||
UsersIcon,
|
||||
XMarkIcon,
|
||||
} from "@heroicons/react/24/outline";
|
||||
import { ArchiveBoxIcon, ArrowLongLeftIcon, ArrowLongRightIcon, EllipsisHorizontalIcon, PencilSquareIcon, UsersIcon, XMarkIcon } from "@heroicons/react/24/outline";
|
||||
import { useCallback, useMemo, useState } from "react";
|
||||
|
||||
import classes from "./classes.module.scss";
|
||||
import Autocomplete from "@Front/Components/DesignSystem/Autocomplete";
|
||||
|
||||
export default function DesignSystem() {
|
||||
const { isOpen, open, close } = useOpenable();
|
||||
@ -84,6 +77,29 @@ export default function DesignSystem() {
|
||||
<div className={classes["root"]}>
|
||||
<div />
|
||||
<div className={classes["components"]}>
|
||||
<Typography typo={ETypo.TEXT_LG_BOLD}>Autocomplete Multi Select</Typography>
|
||||
|
||||
<AutocompleteMultiSelect
|
||||
options={[
|
||||
{
|
||||
id: "1",
|
||||
label: "Option 1",
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
label: "Option 2",
|
||||
},
|
||||
{
|
||||
id: "3",
|
||||
label: "Option 3",
|
||||
},
|
||||
{
|
||||
id: "4",
|
||||
label: { text: "Option 4", subtext: "Subtext" },
|
||||
},
|
||||
]}
|
||||
label="Label"
|
||||
/>
|
||||
<Typography typo={ETypo.TEXT_LG_BOLD}>Autocomplete</Typography>
|
||||
<Autocomplete
|
||||
options={[
|
||||
@ -104,7 +120,6 @@ export default function DesignSystem() {
|
||||
label: { text: "Option 4", subtext: "Subtext" },
|
||||
},
|
||||
]}
|
||||
placeholder="Placeholder"
|
||||
label="Label"
|
||||
/>
|
||||
<Typography typo={ETypo.TEXT_LG_BOLD}>Dropdown</Typography>
|
||||
|
Loading…
x
Reference in New Issue
Block a user