feat: add custom value on dropown ui (#2961)
* Added fuzzy search and custom value on search on Dropdown Component * added allowCustom prop to allow custom values on dropdownComponent * changed type of allowCustom * added custom to custom field on dropdown * Fixed empty search not showing all of the options * Added Text on the left of the label
This commit is contained in:
parent
02a879f330
commit
6a482f36c4
4 changed files with 67 additions and 8 deletions
10
src/frontend/package-lock.json
generated
10
src/frontend/package-lock.json
generated
|
|
@ -48,6 +48,7 @@
|
||||||
"esbuild": "^0.21.5",
|
"esbuild": "^0.21.5",
|
||||||
"file-saver": "^2.0.5",
|
"file-saver": "^2.0.5",
|
||||||
"framer-motion": "^11.2.10",
|
"framer-motion": "^11.2.10",
|
||||||
|
"fuse.js": "^7.0.0",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"lucide-react": "^0.395.0",
|
"lucide-react": "^0.395.0",
|
||||||
"million": "^3.1.11",
|
"million": "^3.1.11",
|
||||||
|
|
@ -7804,6 +7805,15 @@
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/fuse.js": {
|
||||||
|
"version": "7.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-7.0.0.tgz",
|
||||||
|
"integrity": "sha512-14F4hBIxqKvD4Zz/XjDc3y94mNZN6pRv3U13Udo0lNLCWRBUsrMv2xwcF/y/Z5sV6+FQW+/ow68cHpm4sunt8Q==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/gauge": {
|
"node_modules/gauge": {
|
||||||
"version": "3.0.2",
|
"version": "3.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz",
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,7 @@
|
||||||
"esbuild": "^0.21.5",
|
"esbuild": "^0.21.5",
|
||||||
"file-saver": "^2.0.5",
|
"file-saver": "^2.0.5",
|
||||||
"framer-motion": "^11.2.10",
|
"framer-motion": "^11.2.10",
|
||||||
|
"fuse.js": "^7.0.0",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"lucide-react": "^0.395.0",
|
"lucide-react": "^0.395.0",
|
||||||
"million": "^3.1.11",
|
"million": "^3.1.11",
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
import { PopoverAnchor } from "@radix-ui/react-popover";
|
import { PopoverAnchor } from "@radix-ui/react-popover";
|
||||||
import { useRef, useState } from "react";
|
import Fuse from "fuse.js";
|
||||||
|
import { cloneDeep } from "lodash";
|
||||||
|
import { ChangeEvent, useEffect, useRef, useState } from "react";
|
||||||
import { DropDownComponentType } from "../../types/components";
|
import { DropDownComponentType } from "../../types/components";
|
||||||
import { cn } from "../../utils/utils";
|
import { cn } from "../../utils/utils";
|
||||||
import { default as ForwardedIconComponent } from "../genericIconComponent";
|
import { default as ForwardedIconComponent } from "../genericIconComponent";
|
||||||
|
|
@ -8,7 +10,6 @@ import {
|
||||||
Command,
|
Command,
|
||||||
CommandEmpty,
|
CommandEmpty,
|
||||||
CommandGroup,
|
CommandGroup,
|
||||||
CommandInput,
|
|
||||||
CommandItem,
|
CommandItem,
|
||||||
CommandList,
|
CommandList,
|
||||||
} from "../ui/command";
|
} from "../ui/command";
|
||||||
|
|
@ -24,6 +25,7 @@ export default function Dropdown({
|
||||||
isLoading,
|
isLoading,
|
||||||
value,
|
value,
|
||||||
options,
|
options,
|
||||||
|
combobox = true,
|
||||||
onSelect,
|
onSelect,
|
||||||
editNode = false,
|
editNode = false,
|
||||||
id = "",
|
id = "",
|
||||||
|
|
@ -35,6 +37,31 @@ export default function Dropdown({
|
||||||
|
|
||||||
const PopoverContentDropdown =
|
const PopoverContentDropdown =
|
||||||
children || editNode ? PopoverContent : PopoverContentWithoutPortal;
|
children || editNode ? PopoverContent : PopoverContentWithoutPortal;
|
||||||
|
|
||||||
|
const [customValue, setCustomValue] = useState("");
|
||||||
|
const [filteredOptions, setFilteredOptions] = useState(options);
|
||||||
|
|
||||||
|
const fuse = new Fuse(options, { keys: ["name", "value"] });
|
||||||
|
|
||||||
|
const searchRoleByTerm = async (event: ChangeEvent<HTMLInputElement>) => {
|
||||||
|
const value = event.target.value;
|
||||||
|
const searchValues = fuse.search(value);
|
||||||
|
const filtered = searchValues.map((search) => search.item);
|
||||||
|
if (!filtered.includes(value) && combobox) filtered.push(value);
|
||||||
|
setFilteredOptions(value ? filtered : options);
|
||||||
|
setCustomValue(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (open) {
|
||||||
|
const filtered = cloneDeep(options);
|
||||||
|
if (customValue === value && combobox) {
|
||||||
|
filtered.push(customValue);
|
||||||
|
}
|
||||||
|
setFilteredOptions(filtered);
|
||||||
|
}
|
||||||
|
}, [open]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{Object.keys(options ?? [])?.length > 0 ? (
|
{Object.keys(options ?? [])?.length > 0 ? (
|
||||||
|
|
@ -60,11 +87,14 @@ export default function Dropdown({
|
||||||
editNode ? "input-edit-node" : "py-2",
|
editNode ? "input-edit-node" : "py-2",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<span data-testid={`value-dropdown-` + id}>
|
<span
|
||||||
|
className="truncate"
|
||||||
|
data-testid={`value-dropdown-` + id}
|
||||||
|
>
|
||||||
{value &&
|
{value &&
|
||||||
value !== "" &&
|
value !== "" &&
|
||||||
options.find((option) => option === value)
|
filteredOptions.find((option) => option === value)
|
||||||
? options.find((option) => option === value)
|
? filteredOptions.find((option) => option === value)
|
||||||
: "Choose an option..."}
|
: "Choose an option..."}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
|
|
@ -86,11 +116,21 @@ export default function Dropdown({
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Command>
|
<Command>
|
||||||
<CommandInput placeholder="Search options..." className="h-9" />
|
<div className="flex items-center border-b px-3">
|
||||||
|
<ForwardedIconComponent
|
||||||
|
name="search"
|
||||||
|
className="mr-2 h-4 w-4 shrink-0 opacity-50"
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
onChange={searchRoleByTerm}
|
||||||
|
placeholder="Search options..."
|
||||||
|
className="flex h-9 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<CommandList>
|
<CommandList>
|
||||||
<CommandEmpty>No values found.</CommandEmpty>
|
<CommandEmpty>No values found.</CommandEmpty>
|
||||||
<CommandGroup defaultChecked={false}>
|
<CommandGroup defaultChecked={false}>
|
||||||
{options?.map((option, id) => (
|
{filteredOptions?.map((option, id) => (
|
||||||
<CommandItem
|
<CommandItem
|
||||||
key={id}
|
key={id}
|
||||||
value={option}
|
value={option}
|
||||||
|
|
@ -98,8 +138,16 @@ export default function Dropdown({
|
||||||
onSelect(currentValue);
|
onSelect(currentValue);
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
}}
|
}}
|
||||||
|
className="items-center truncate"
|
||||||
data-testid={`${option}-${id ?? ""}-option`}
|
data-testid={`${option}-${id ?? ""}-option`}
|
||||||
>
|
>
|
||||||
|
{customValue === option ? (
|
||||||
|
<span className="text-muted-foreground">
|
||||||
|
Text:
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
<></>
|
||||||
|
)}
|
||||||
{option}
|
{option}
|
||||||
<ForwardedIconComponent
|
<ForwardedIconComponent
|
||||||
name="Check"
|
name="Check"
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
import { ColDef } from "ag-grid-community";
|
|
||||||
import { ReactElement, ReactNode, SetStateAction } from "react";
|
import { ReactElement, ReactNode, SetStateAction } from "react";
|
||||||
import { ReactFlowJsonObject } from "reactflow";
|
import { ReactFlowJsonObject } from "reactflow";
|
||||||
import { InputOutput } from "../../constants/enums";
|
import { InputOutput } from "../../constants/enums";
|
||||||
|
|
@ -54,6 +53,7 @@ export type DropDownComponentType = {
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
isLoading?: boolean;
|
isLoading?: boolean;
|
||||||
value: string;
|
value: string;
|
||||||
|
combobox?: boolean;
|
||||||
options: string[];
|
options: string[];
|
||||||
onSelect: (value: string) => void;
|
onSelect: (value: string) => void;
|
||||||
editNode?: boolean;
|
editNode?: boolean;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue