🐛(react) fix controlled searchable select
Changing the controlled value was setting triggering immediately an onChange event with undefined value. This was due to the fact that when the controlled value was changed SelectMonoAux was searching only in options displayed, where it should in reality be searching accross all options. fixes #162
This commit is contained in:
5
.changeset/tasty-hotels-hunt.md
Normal file
5
.changeset/tasty-hotels-hunt.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@openfun/cunningham-react": patch
|
||||
---
|
||||
|
||||
fix controlled searchable select triggering onChange undefined
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { HTMLAttributes, useEffect } from "react";
|
||||
import React, { HTMLAttributes } from "react";
|
||||
import { UseSelectReturnValue } from "downshift";
|
||||
import classNames from "classnames";
|
||||
import { useCunningham } from ":/components/Provider";
|
||||
@@ -73,17 +73,6 @@ export const SelectMonoAux = ({
|
||||
const { t } = useCunningham();
|
||||
const labelProps = downshiftReturn.getLabelProps();
|
||||
|
||||
// When component is controlled, this useEffect will update the local selected item.
|
||||
useEffect(() => {
|
||||
if (downshiftProps.initialSelectedItem !== undefined) {
|
||||
return;
|
||||
}
|
||||
const optionToSelect = options.find(
|
||||
(option) => optionToValue(option) === value,
|
||||
);
|
||||
downshiftReturn.selectItem(optionToSelect ?? null);
|
||||
}, [value]);
|
||||
|
||||
return (
|
||||
<Field state={state} {...props}>
|
||||
<div
|
||||
|
||||
@@ -4,6 +4,7 @@ import { useCunningham } from ":/components/Provider";
|
||||
import {
|
||||
getOptionsFilter,
|
||||
optionToString,
|
||||
optionToValue,
|
||||
SelectMonoAux,
|
||||
SubProps,
|
||||
} from ":/components/Forms/Select/mono-common";
|
||||
@@ -40,6 +41,17 @@ export const SelectMonoSearchable = (props: SubProps) => {
|
||||
downshiftReturn.inputValue,
|
||||
]);
|
||||
|
||||
// When component is controlled, this useEffect will update the local selected item.
|
||||
useEffect(() => {
|
||||
if (props.downshiftProps.initialSelectedItem !== undefined) {
|
||||
return;
|
||||
}
|
||||
const optionToSelect = props.options.find(
|
||||
(option) => optionToValue(option) === props.value,
|
||||
);
|
||||
downshiftReturn.selectItem(optionToSelect ?? null);
|
||||
}, [props.value, props.options, props.downshiftProps]);
|
||||
|
||||
const inputProps = downshiftReturn.getInputProps({
|
||||
ref: inputRef,
|
||||
disabled: props.disabled,
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { useSelect } from "downshift";
|
||||
import React from "react";
|
||||
import React, { useEffect } from "react";
|
||||
import {
|
||||
optionToString,
|
||||
optionToValue,
|
||||
SelectMonoAux,
|
||||
SubProps,
|
||||
} from ":/components/Forms/Select/mono-common";
|
||||
@@ -13,6 +14,17 @@ export const SelectMonoSimple = (props: SubProps) => {
|
||||
itemToString: optionToString,
|
||||
});
|
||||
|
||||
// When component is controlled, this useEffect will update the local selected item.
|
||||
useEffect(() => {
|
||||
if (props.downshiftProps.initialSelectedItem !== undefined) {
|
||||
return;
|
||||
}
|
||||
const optionToSelect = props.options.find(
|
||||
(option) => optionToValue(option) === props.value,
|
||||
);
|
||||
downshiftReturn.selectItem(optionToSelect ?? null);
|
||||
}, [props.value, props.options, props.downshiftProps]);
|
||||
|
||||
return (
|
||||
<SelectMonoAux
|
||||
{...props}
|
||||
|
||||
@@ -295,6 +295,7 @@ describe("<Select/>", () => {
|
||||
<div>
|
||||
<div>Value = {value}|</div>
|
||||
<Button onClick={() => setValue(undefined)}>Clear</Button>
|
||||
<Button onClick={() => setValue("paris")}>Set Paris</Button>
|
||||
<Select
|
||||
label="City"
|
||||
options={[
|
||||
@@ -367,6 +368,15 @@ describe("<Select/>", () => {
|
||||
|
||||
// Make sure value is cleared.
|
||||
screen.getByText("Value = |");
|
||||
|
||||
// Make sure setting value works
|
||||
const buttonParis = screen.getByRole("button", {
|
||||
name: "Set Paris",
|
||||
});
|
||||
await user.click(buttonParis);
|
||||
|
||||
screen.getByText("Value = paris|");
|
||||
expect(input).toHaveValue("Paris");
|
||||
});
|
||||
it("renders disabled", async () => {
|
||||
render(
|
||||
|
||||
@@ -87,6 +87,12 @@ export const Controlled = () => {
|
||||
onChange={(e) => setValue(e.target.value as string)}
|
||||
/>
|
||||
<Button onClick={() => setValue("")}>Reset</Button>
|
||||
<Button onClick={() => setValue(OPTIONS[0].value)}>
|
||||
Set {OPTIONS[0].label}
|
||||
</Button>
|
||||
<Button onClick={() => setValue(OPTIONS[1].value)}>
|
||||
Set {OPTIONS[1].label}
|
||||
</Button>
|
||||
</div>
|
||||
</CunninghamProvider>
|
||||
);
|
||||
@@ -156,6 +162,12 @@ export const SearchableControlled = () => {
|
||||
onChange={(e) => setValue(e.target.value as string)}
|
||||
/>
|
||||
<Button onClick={() => setValue("")}>Reset</Button>
|
||||
<Button onClick={() => setValue(OPTIONS[0].value)}>
|
||||
Set {OPTIONS[0].label}
|
||||
</Button>
|
||||
<Button onClick={() => setValue(OPTIONS[1].value)}>
|
||||
Set {OPTIONS[1].label}
|
||||
</Button>
|
||||
</div>
|
||||
</CunninghamProvider>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user