From a08f5a077540d65e7b59758baf012d7e1b6efc8f Mon Sep 17 00:00:00 2001 From: Mason Hu Date: Wed, 8 Jan 2025 10:00:29 +0200 Subject: [PATCH] feat: expose `onSearch` prop for CustomSelect component Signed-off-by: Mason Hu --- src/components/CustomSelect/CustomSelect.tsx | 4 ++ .../CustomSelectDropdown.tsx | 40 +++++++++++-------- 2 files changed, 28 insertions(+), 16 deletions(-) diff --git a/src/components/CustomSelect/CustomSelect.tsx b/src/components/CustomSelect/CustomSelect.tsx index 80e379f0..684d00db 100644 --- a/src/components/CustomSelect/CustomSelect.tsx +++ b/src/components/CustomSelect/CustomSelect.tsx @@ -30,6 +30,8 @@ export type Props = PropsWithSpread< options: CustomSelectOption[]; // Function to run when select value changes. onChange: (value: string) => void; + // Function to run when the search input changes. If provided, the parent should handle the filtering of options. + onSearch?: (value: string) => void; // id for the select component id?: string | null; // Name for the select element @@ -64,6 +66,7 @@ const CustomSelect = ({ value, options, onChange, + onSearch, id, name, disabled, @@ -183,6 +186,7 @@ const CustomSelect = ({ {(close: () => void) => ( void; + onSearch?: (value: string) => void; onClose: () => void; header?: ReactNode; toggleId: string; @@ -160,13 +161,14 @@ const CustomSelectDropdown: FC = ({ name, options, onSelect, + onSearch, onClose, header, toggleId, }) => { const [search, setSearch] = useState(""); - // track selected option index for keyboard actions - const [selectedIndex, setSelectedIndex] = useState(0); + // track highlighted option index for keyboard actions + const [highlightedOptionIndex, setHighlightedOptionIndex] = useState(0); // use ref to keep a reference to all option HTML elements so we do not need to make DOM calls later for scrolling const optionsRef = useRef([]); const dropdownRef = useRef(null); @@ -215,17 +217,19 @@ const CustomSelectDropdown: FC = ({ // track selected index from key board action and scroll into view if needed useEffect(() => { - optionsRef.current[selectedIndex]?.scrollIntoView({ + optionsRef.current[highlightedOptionIndex]?.scrollIntoView({ block: "nearest", inline: "nearest", }); - }, [selectedIndex]); - - const filteredOptions = options?.filter((option) => { - if (!search || option.disabled) return true; - const searchText = getOptionText(option) || option.value; - return searchText.toLowerCase().includes(search); - }); + }, [highlightedOptionIndex]); + + const filteredOptions = onSearch + ? options + : options?.filter((option) => { + if (!search || option.disabled) return true; + const searchText = getOptionText(option) || option.value; + return searchText.toLowerCase().includes(search); + }); const getNextOptionIndex = (goingUp: boolean, prevIndex: number) => { const increment = goingUp ? -1 : 1; @@ -256,14 +260,14 @@ const CustomSelectDropdown: FC = ({ } if (upDownKeys.includes(event.key)) { - setSelectedIndex((prevIndex) => { + setHighlightedOptionIndex((prevIndex) => { const goingUp = event.key === "ArrowUp"; return getNextOptionIndex(goingUp, prevIndex); }); } - if (event.key === "Enter" && filteredOptions[selectedIndex]) { - onSelect(filteredOptions[selectedIndex].value); + if (event.key === "Enter" && filteredOptions[highlightedOptionIndex]) { + onSelect(filteredOptions[highlightedOptionIndex].value); } if (event.key === "Escape" || event.key === "Tab") { @@ -274,8 +278,12 @@ const CustomSelectDropdown: FC = ({ const handleSearch = (value: string) => { setSearch(value.toLowerCase()); // reset selected index when search text changes - setSelectedIndex(0); + setHighlightedOptionIndex(0); optionsRef.current = []; + + if (onSearch) { + onSearch(value); + } }; const handleSelect = (option: CustomSelectOption) => { @@ -297,7 +305,7 @@ const CustomSelectDropdown: FC = ({ "u-truncate", { disabled: option.disabled, - highlight: idx === selectedIndex && !option.disabled, + highlight: idx === highlightedOptionIndex && !option.disabled, }, )} // adding option elements to a ref array makes it easier to scroll the element later @@ -307,7 +315,7 @@ const CustomSelectDropdown: FC = ({ optionsRef.current[idx] = el; }} role="option" - onMouseMove={() => setSelectedIndex(idx)} + onMouseMove={() => setHighlightedOptionIndex(idx)} >