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:
Lucas Oliveira 2024-07-25 18:37:59 -03:00 committed by GitHub
commit 6a482f36c4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 67 additions and 8 deletions

View file

@ -48,6 +48,7 @@
"esbuild": "^0.21.5",
"file-saver": "^2.0.5",
"framer-motion": "^11.2.10",
"fuse.js": "^7.0.0",
"lodash": "^4.17.21",
"lucide-react": "^0.395.0",
"million": "^3.1.11",
@ -7804,6 +7805,15 @@
"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": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz",

View file

@ -43,6 +43,7 @@
"esbuild": "^0.21.5",
"file-saver": "^2.0.5",
"framer-motion": "^11.2.10",
"fuse.js": "^7.0.0",
"lodash": "^4.17.21",
"lucide-react": "^0.395.0",
"million": "^3.1.11",

View file

@ -1,5 +1,7 @@
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 { cn } from "../../utils/utils";
import { default as ForwardedIconComponent } from "../genericIconComponent";
@ -8,7 +10,6 @@ import {
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
} from "../ui/command";
@ -24,6 +25,7 @@ export default function Dropdown({
isLoading,
value,
options,
combobox = true,
onSelect,
editNode = false,
id = "",
@ -35,6 +37,31 @@ export default function Dropdown({
const PopoverContentDropdown =
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 (
<>
{Object.keys(options ?? [])?.length > 0 ? (
@ -60,11 +87,14 @@ export default function Dropdown({
editNode ? "input-edit-node" : "py-2",
)}
>
<span data-testid={`value-dropdown-` + id}>
<span
className="truncate"
data-testid={`value-dropdown-` + id}
>
{value &&
value !== "" &&
options.find((option) => option === value)
? options.find((option) => option === value)
filteredOptions.find((option) => option === value)
? filteredOptions.find((option) => option === value)
: "Choose an option..."}
</span>
@ -86,11 +116,21 @@ export default function Dropdown({
}
>
<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>
<CommandEmpty>No values found.</CommandEmpty>
<CommandGroup defaultChecked={false}>
{options?.map((option, id) => (
{filteredOptions?.map((option, id) => (
<CommandItem
key={id}
value={option}
@ -98,8 +138,16 @@ export default function Dropdown({
onSelect(currentValue);
setOpen(false);
}}
className="items-center truncate"
data-testid={`${option}-${id ?? ""}-option`}
>
{customValue === option ? (
<span className="text-muted-foreground">
Text:&nbsp;
</span>
) : (
<></>
)}
{option}
<ForwardedIconComponent
name="Check"

View file

@ -1,4 +1,3 @@
import { ColDef } from "ag-grid-community";
import { ReactElement, ReactNode, SetStateAction } from "react";
import { ReactFlowJsonObject } from "reactflow";
import { InputOutput } from "../../constants/enums";
@ -54,6 +53,7 @@ export type DropDownComponentType = {
disabled?: boolean;
isLoading?: boolean;
value: string;
combobox?: boolean;
options: string[];
onSelect: (value: string) => void;
editNode?: boolean;