✨(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:
5
.changeset/warm-roses-push.md
Normal file
5
.changeset/warm-roses-push.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"@openfun/cunningham-react": minor
|
||||||
|
---
|
||||||
|
|
||||||
|
add onSearchInputChange callback to searchable select
|
||||||
@@ -49,6 +49,9 @@ export type SelectProps = PropsWithChildren &
|
|||||||
monoline?: boolean;
|
monoline?: boolean;
|
||||||
selectedItemsStyle?: "pills" | "text";
|
selectedItemsStyle?: "pills" | "text";
|
||||||
menuOptionsStyle?: "plain" | "checkbox";
|
menuOptionsStyle?: "plain" | "checkbox";
|
||||||
|
onSearchInputChange?: (event: {
|
||||||
|
target: { value: string | undefined };
|
||||||
|
}) => void;
|
||||||
};
|
};
|
||||||
export const Select = forwardRef<SelectHandle, SelectProps>((props, ref) => {
|
export const Select = forwardRef<SelectHandle, SelectProps>((props, ref) => {
|
||||||
if (props.defaultValue && props.value) {
|
if (props.defaultValue && props.value) {
|
||||||
|
|||||||
@@ -93,6 +93,10 @@ export const SelectMonoSearchable = forwardRef<SelectHandle, SubProps>(
|
|||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
props.onSearchInputChange?.({ target: { value: inputFilter } });
|
||||||
|
}, [inputFilter]);
|
||||||
|
|
||||||
const onInputBlur = () => {
|
const onInputBlur = () => {
|
||||||
setHasInputFocused(false);
|
setHasInputFocused(false);
|
||||||
if (downshiftReturn.selectedItem) {
|
if (downshiftReturn.selectedItem) {
|
||||||
|
|||||||
@@ -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"/>
|
<Story id="components-forms-select-mono--searchable-uncontrolled"/>
|
||||||
</Canvas>
|
</Canvas>
|
||||||
|
|
||||||
|
> You can use `onSearchInputChange` to get the value of the input when the user is typing.
|
||||||
|
|
||||||
## States
|
## States
|
||||||
|
|
||||||
You can use the following props to change the state of the Select component by using the `state` props.
|
You can use the following props to change the state of the Select component by using the `state` props.
|
||||||
|
|||||||
@@ -959,6 +959,64 @@ describe("<Select/>", () => {
|
|||||||
name: "Paris flag",
|
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", () => {
|
describe("Simple", () => {
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import { SelectHandle } from ":/components/Forms/Select/index";
|
|||||||
|
|
||||||
export const SelectMultiSearchable = forwardRef<SelectHandle, SubProps>(
|
export const SelectMultiSearchable = forwardRef<SelectHandle, SubProps>(
|
||||||
(props, ref) => {
|
(props, ref) => {
|
||||||
const [inputValue, setInputValue] = React.useState<string>("");
|
const [inputValue, setInputValue] = React.useState<string>();
|
||||||
const inputRef = useRef<HTMLInputElement>(null);
|
const inputRef = useRef<HTMLInputElement>(null);
|
||||||
const options = React.useMemo(
|
const options = React.useMemo(
|
||||||
() =>
|
() =>
|
||||||
@@ -75,11 +75,11 @@ export const SelectMultiSearchable = forwardRef<SelectHandle, SubProps>(
|
|||||||
...props.selectedItems,
|
...props.selectedItems,
|
||||||
newSelectedItem,
|
newSelectedItem,
|
||||||
]);
|
]);
|
||||||
setInputValue("");
|
setInputValue(undefined);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case useCombobox.stateChangeTypes.InputChange:
|
case useCombobox.stateChangeTypes.InputChange:
|
||||||
setInputValue(newInputValue ?? "");
|
setInputValue(newInputValue);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@@ -124,6 +124,10 @@ export const SelectMultiSearchable = forwardRef<SelectHandle, SubProps>(
|
|||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
props.onSearchInputChange?.({ target: { value: inputValue } });
|
||||||
|
}, [inputValue]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SelectMultiAux
|
<SelectMultiAux
|
||||||
{...props}
|
{...props}
|
||||||
|
|||||||
@@ -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"/>
|
<Story id="components-forms-select-multi--searchable-uncontrolled"/>
|
||||||
</Canvas>
|
</Canvas>
|
||||||
|
|
||||||
|
> You can use `onSearchInputChange` to get the value of the input when the user is typing.
|
||||||
|
|
||||||
## States
|
## States
|
||||||
|
|
||||||
You can use the following props to change the state of the Multi-Select component by using the `state` props.
|
You can use the following props to change the state of the Multi-Select component by using the `state` props.
|
||||||
|
|||||||
@@ -1193,6 +1193,67 @@ describe("<Select multi={true} />", () => {
|
|||||||
expectOptions(["Paris"]);
|
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 () => {
|
it("selects the option on enter", async () => {
|
||||||
const user = userEvent.setup();
|
const user = userEvent.setup();
|
||||||
render(
|
render(
|
||||||
|
|||||||
Reference in New Issue
Block a user