Files
cunningham/packages/react/src/components/Forms/Select/multi-simple.tsx
Nathan Vasse 111bb677c4 ♻️(react) improve select element keys
It was considered as a bad practice to include array indexes in list
elements keys even though it was made this way in the documentation. We
removed them and also decided to drop the use of options value in the
keys too to the profit of optionToValue which can never be undefined or null.
This changes caused test crashing due to the fact that it made the useSelect
onStateChange trigger a onBlur event that was causing the first option of the
list to be selected automatically, which was a really weird behavior. Before
it was not happening because newSelectedItem was not defined, using the
new keys made it defined, we don't 100% understand why yet.
2023-09-18 17:01:21 +02:00

100 lines
3.3 KiB
TypeScript

import React from "react";
import { useMultipleSelection, useSelect } from "downshift";
import {
getMultiOptionsFilter,
SelectMultiAux,
SubProps,
} from ":/components/Forms/Select/multi-common";
import { optionToString } from ":/components/Forms/Select/mono-common";
export const SelectMultiSimple = (props: SubProps) => {
const options = React.useMemo(
() => props.options.filter(getMultiOptionsFilter(props.selectedItems, "")),
[props.selectedItems],
);
const useMultipleSelectionReturn = useMultipleSelection({
selectedItems: props.selectedItems,
onStateChange({ selectedItems: newSelectedItems, type }) {
switch (type) {
case useMultipleSelection.stateChangeTypes.SelectedItemKeyDownBackspace:
case useMultipleSelection.stateChangeTypes.SelectedItemKeyDownDelete:
case useMultipleSelection.stateChangeTypes.DropdownKeyDownBackspace:
case useMultipleSelection.stateChangeTypes.FunctionRemoveSelectedItem:
props.onSelectedItemsChange(newSelectedItems ?? []);
break;
default:
break;
}
},
});
const downshiftReturn = useSelect({
items: options,
itemToString: optionToString,
defaultHighlightedIndex: 0, // after selection, highlight the first item.
stateReducer: (state, actionAndChanges) => {
const { changes, type } = actionAndChanges;
switch (type) {
case useSelect.stateChangeTypes.ToggleButtonKeyDownEnter:
case useSelect.stateChangeTypes.ToggleButtonKeyDownSpaceButton:
case useSelect.stateChangeTypes.ItemClick:
return {
...changes,
isOpen: true, // keep the menu open after selection.
highlightedIndex: 0, // with the first option highlighted.
};
}
return changes;
},
onStateChange: ({ type, selectedItem: newSelectedItem }) => {
switch (type) {
case useSelect.stateChangeTypes.ToggleButtonKeyDownEnter:
case useSelect.stateChangeTypes.ToggleButtonKeyDownSpaceButton:
case useSelect.stateChangeTypes.ItemClick:
if (newSelectedItem) {
props.onSelectedItemsChange([
...props.selectedItems,
newSelectedItem,
]);
}
break;
default:
break;
}
},
isItemDisabled: (item) => !!item.disabled,
});
return (
<SelectMultiAux
{...props}
options={options}
labelAsPlaceholder={props.selectedItems.length === 0}
selectedItems={props.selectedItems}
downshiftReturn={{
...downshiftReturn,
wrapperProps: {
onClick: () => {
if (!props.disabled) {
downshiftReturn.toggleMenu();
}
},
},
toggleButtonProps: downshiftReturn.getToggleButtonProps({
...useMultipleSelectionReturn.getDropdownProps({
preventKeyAction: downshiftReturn.isOpen,
}),
disabled: props.disabled,
onClick: (e: React.MouseEvent): void => {
// As the wrapper also has an onClick handler, we need to stop the event propagation here on it will toggle
// twice the menu opening which will ... do nothing :).
e.stopPropagation();
},
}),
}}
useMultipleSelectionReturn={useMultipleSelectionReturn}
/>
);
};