(react) add onSearchInputChange callback to searchable select

We want to be able to notify an event when the search term of the
searchable select gets updated.
This commit is contained in:
Nathan Vasse
2024-02-26 14:41:15 +01:00
committed by NathanVss
parent 680365a117
commit 04ab3306e2
8 changed files with 142 additions and 3 deletions

View File

@@ -49,6 +49,9 @@ export type SelectProps = PropsWithChildren &
monoline?: boolean;
selectedItemsStyle?: "pills" | "text";
menuOptionsStyle?: "plain" | "checkbox";
onSearchInputChange?: (event: {
target: { value: string | undefined };
}) => void;
};
export const Select = forwardRef<SelectHandle, SelectProps>((props, ref) => {
if (props.defaultValue && props.value) {

View File

@@ -93,6 +93,10 @@ export const SelectMonoSearchable = forwardRef<SelectHandle, SubProps>(
},
}));
useEffect(() => {
props.onSearchInputChange?.({ target: { value: inputFilter } });
}, [inputFilter]);
const onInputBlur = () => {
setHasInputFocused(false);
if (downshiftReturn.selectedItem) {

View File

@@ -40,6 +40,8 @@ You can enable the text live filtering simply by using the `searchable` props.
<Story id="components-forms-select-mono--searchable-uncontrolled"/>
</Canvas>
> You can use `onSearchInputChange` to get the value of the input when the user is typing.
## States
You can use the following props to change the state of the Select component by using the `state` props.

View File

@@ -959,6 +959,64 @@ describe("<Select/>", () => {
name: "Paris flag",
});
});
it("get the search term using onSearchInputChange", async () => {
const user = userEvent.setup();
let searchTerm: string | undefined;
render(
<CunninghamProvider>
<Select
label="City"
options={[
{
label: "Paris",
value: "paris",
},
{
label: "Panama",
value: "panama",
},
{
label: "London",
value: "london",
},
{
label: "New York",
value: "new_york",
},
{
label: "Tokyo",
value: "tokyo",
},
]}
searchable={true}
onSearchInputChange={(e) => {
searchTerm = e.target.value;
}}
/>
</CunninghamProvider>,
);
const input = screen.getByRole("combobox", {
name: "City",
});
await user.click(input);
expect(searchTerm).toBeUndefined();
await user.type(input, "Pa");
expect(searchTerm).toEqual("Pa");
await user.type(input, "r", { skipClick: true });
expect(searchTerm).toEqual("Par");
// Select option.
const option: HTMLLIElement = screen.getByRole("option", {
name: "Paris",
});
await user.click(option);
expect(searchTerm).toBeUndefined();
});
});
describe("Simple", () => {

View File

@@ -16,7 +16,7 @@ import { SelectHandle } from ":/components/Forms/Select/index";
export const SelectMultiSearchable = forwardRef<SelectHandle, SubProps>(
(props, ref) => {
const [inputValue, setInputValue] = React.useState<string>("");
const [inputValue, setInputValue] = React.useState<string>();
const inputRef = useRef<HTMLInputElement>(null);
const options = React.useMemo(
() =>
@@ -75,11 +75,11 @@ export const SelectMultiSearchable = forwardRef<SelectHandle, SubProps>(
...props.selectedItems,
newSelectedItem,
]);
setInputValue("");
setInputValue(undefined);
}
break;
case useCombobox.stateChangeTypes.InputChange:
setInputValue(newInputValue ?? "");
setInputValue(newInputValue);
break;
default:
break;
@@ -124,6 +124,10 @@ export const SelectMultiSearchable = forwardRef<SelectHandle, SubProps>(
},
}));
useEffect(() => {
props.onSearchInputChange?.({ target: { value: inputValue } });
}, [inputValue]);
return (
<SelectMultiAux
{...props}

View File

@@ -26,6 +26,8 @@ You can enable the text live filtering simply by using the `searchable` props.
<Story id="components-forms-select-multi--searchable-uncontrolled"/>
</Canvas>
> You can use `onSearchInputChange` to get the value of the input when the user is typing.
## States
You can use the following props to change the state of the Multi-Select component by using the `state` props.

View File

@@ -1193,6 +1193,67 @@ describe("<Select multi={true} />", () => {
expectOptions(["Paris"]);
});
it("get the search term using onSearchInputChange", async () => {
const user = userEvent.setup();
let searchTerm: string | undefined;
render(
<CunninghamProvider>
<Select
label="Cities"
options={[
{
label: "Paris",
value: "paris",
},
{
label: "Panama",
value: "panama",
},
{
label: "London",
value: "london",
},
{
label: "New York",
value: "new_york",
},
{
label: "Tokyo",
value: "tokyo",
},
]}
searchable={true}
multi={true}
onSearchInputChange={(e) => {
searchTerm = e.target.value;
}}
/>
</CunninghamProvider>,
);
const input = screen.getByRole("combobox", {
name: "Cities",
});
expect(searchTerm).toBeUndefined();
await user.click(input);
expect(searchTerm).toBeUndefined();
await user.type(input, "Pa");
expect(searchTerm).toEqual("Pa");
await user.type(input, "r");
expect(searchTerm).toEqual("Par");
expectOptions(["Paris"]);
const option: HTMLLIElement = screen.getByRole("option", {
name: "Paris",
});
await user.click(option);
expect(searchTerm).toBeUndefined();
});
it("selects the option on enter", async () => {
const user = userEvent.setup();
render(