Skip to content

Commit

Permalink
feat: expose onSearch prop for CustomSelect component
Browse files Browse the repository at this point in the history
Signed-off-by: Mason Hu <[email protected]>
  • Loading branch information
mas-who committed Jan 8, 2025
1 parent 0c4b80a commit a08f5a0
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 16 deletions.
4 changes: 4 additions & 0 deletions src/components/CustomSelect/CustomSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -64,6 +66,7 @@ const CustomSelect = ({
value,
options,
onChange,
onSearch,
id,
name,
disabled,
Expand Down Expand Up @@ -183,6 +186,7 @@ const CustomSelect = ({
{(close: () => void) => (
<CustomSelectDropdown
searchable={searchable}
onSearch={onSearch}
name={name || ""}
options={options || []}
onSelect={handleSelect}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export type Props = {
name: string;
options: CustomSelectOption[];
onSelect: (value: string) => void;
onSearch?: (value: string) => void;
onClose: () => void;
header?: ReactNode;
toggleId: string;
Expand Down Expand Up @@ -160,13 +161,14 @@ const CustomSelectDropdown: FC<Props> = ({
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<HTMLLIElement[]>([]);
const dropdownRef = useRef<HTMLDivElement>(null);
Expand Down Expand Up @@ -215,17 +217,19 @@ const CustomSelectDropdown: FC<Props> = ({

// 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;
Expand Down Expand Up @@ -256,14 +260,14 @@ const CustomSelectDropdown: FC<Props> = ({
}

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") {
Expand All @@ -274,8 +278,12 @@ const CustomSelectDropdown: FC<Props> = ({
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) => {
Expand All @@ -297,7 +305,7 @@ const CustomSelectDropdown: FC<Props> = ({
"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
Expand All @@ -307,7 +315,7 @@ const CustomSelectDropdown: FC<Props> = ({
optionsRef.current[idx] = el;
}}
role="option"
onMouseMove={() => setSelectedIndex(idx)}
onMouseMove={() => setHighlightedOptionIndex(idx)}
>
<span
className={classnames({
Expand Down

0 comments on commit a08f5a0

Please sign in to comment.