From 3fc464bb8ce6e5578330d0c07e98474e6a149bf9 Mon Sep 17 00:00:00 2001 From: Anthony Le Courric Date: Mon, 25 Sep 2023 16:03:54 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B(react)=20fix=20some=20edge=20cases?= =?UTF-8?q?=20with=20searchable=20Select?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - rerender mutated options when the menu is opened - keep the filter value when the menu is opened and rerender by a mutated options --- .changeset/tall-bats-wink.md | 5 + .../Forms/Select/mono-searchable.tsx | 29 ++++- .../components/Forms/Select/mono-simple.tsx | 15 ++- .../src/components/Forms/Select/mono.spec.tsx | 106 +++++++++++++++++- .../src/components/Forms/Select/mono.tsx | 31 ++++- 5 files changed, 171 insertions(+), 15 deletions(-) create mode 100644 .changeset/tall-bats-wink.md diff --git a/.changeset/tall-bats-wink.md b/.changeset/tall-bats-wink.md new file mode 100644 index 0000000..d321175 --- /dev/null +++ b/.changeset/tall-bats-wink.md @@ -0,0 +1,5 @@ +--- +"@openfun/cunningham-react": minor +--- + +fix some edge effects on the mono select component diff --git a/packages/react/src/components/Forms/Select/mono-searchable.tsx b/packages/react/src/components/Forms/Select/mono-searchable.tsx index 7db2206..865b550 100644 --- a/packages/react/src/components/Forms/Select/mono-searchable.tsx +++ b/packages/react/src/components/Forms/Select/mono-searchable.tsx @@ -13,19 +13,19 @@ export const SelectMonoSearchable = (props: SubProps) => { const { t } = useCunningham(); const [optionsToDisplay, setOptionsToDisplay] = useState(props.options); const [hasInputFocused, setHasInputFocused] = useState(false); + const [inputFilter, setInputFilter] = useState(); const inputRef = useRef(null); const downshiftReturn = useCombobox({ ...props.downshiftProps, items: optionsToDisplay, itemToString: optionToString, onInputValueChange: (e) => { - setOptionsToDisplay(props.options.filter(getOptionsFilter(e.inputValue))); + setInputFilter(e.inputValue); if (!e.inputValue) { downshiftReturn.selectItem(null); } }, }); - const [labelAsPlaceholder, setLabelAsPlaceholder] = useState( !downshiftReturn.selectedItem, ); @@ -43,21 +43,38 @@ export const SelectMonoSearchable = (props: SubProps) => { // When component is controlled, this useEffect will update the local selected item. useEffect(() => { - if (props.downshiftProps.initialSelectedItem !== undefined) { + if (inputFilter) { return; } + + const selectedItem = downshiftReturn.selectedItem + ? optionToValue(downshiftReturn.selectedItem) + : undefined; + const optionToSelect = props.options.find( (option) => optionToValue(option) === props.value, ); + + // Already selected + if (optionToSelect && selectedItem === props.value) { + return; + } + downshiftReturn.selectItem(optionToSelect ?? null); - }, [props.value, props.options, props.downshiftProps]); + }, [props.value, props.options, inputFilter]); // Even there is already a value selected, when opening the combobox menu we want to display all available choices. useEffect(() => { if (downshiftReturn.isOpen) { - setOptionsToDisplay(props.options); + setOptionsToDisplay( + inputFilter + ? props.options.filter(getOptionsFilter(inputFilter)) + : props.options, + ); + } else { + setInputFilter(undefined); } - }, [downshiftReturn.isOpen]); + }, [downshiftReturn.isOpen, props.options, inputFilter]); const onInputBlur = () => { setHasInputFocused(false); diff --git a/packages/react/src/components/Forms/Select/mono-simple.tsx b/packages/react/src/components/Forms/Select/mono-simple.tsx index 22ed327..65cdbdc 100644 --- a/packages/react/src/components/Forms/Select/mono-simple.tsx +++ b/packages/react/src/components/Forms/Select/mono-simple.tsx @@ -16,14 +16,21 @@ export const SelectMonoSimple = (props: SubProps) => { // When component is controlled, this useEffect will update the local selected item. useEffect(() => { - if (props.downshiftProps.initialSelectedItem !== undefined) { - return; - } + const selectedItem = downshiftReturn.selectedItem + ? optionToValue(downshiftReturn.selectedItem) + : undefined; + const optionToSelect = props.options.find( (option) => optionToValue(option) === props.value, ); + + // Already selected + if (optionToSelect && selectedItem === props.value) { + return; + } + downshiftReturn.selectItem(optionToSelect ?? null); - }, [props.value, props.options, props.downshiftProps]); + }, [props.value, props.options]); return ( ", () => { city: null, }); }); + + [ + { + defaultValue: "panama", + type: "default value", + expected: "Panama", + }, + { + value: "panama", + type: "value", + expected: "Panama", + }, + { + type: "without values props", + expected: "", + }, + ].forEach(({ type, expected, ...props }) => { + it(`render mutated option when select open and keep the filter activated with ${type}`, async () => { + const myOptions = [ + { + label: "Paris", + value: "paris", + }, + { + label: "Panama", + value: "panama", + }, + { + label: "London", + value: "london", + }, + ]; + + const Wrapper = ({ options }: { options: Option[] }) => { + return ( +