diff --git a/docs/pages/api/autocomplete.md b/docs/pages/api/autocomplete.md index 573ed86a480c3d..d2077b0bd06121 100644 --- a/docs/pages/api/autocomplete.md +++ b/docs/pages/api/autocomplete.md @@ -45,6 +45,7 @@ You can learn more about the difference by [reading this guide](/guides/minimizi | freeSolo | bool | false | If `true`, the Autocomplete is free solo, meaning that the user input is not bound to provided options. | | getOptionDisabled | func | | Used to determine the disabled state for a given option. | | getOptionLabel | func | x => x | Used to determine the string value for a given option. It's used to fill the input (and the list box options if `renderOption` is not provided). | +| getOptionSelected | func | | Used to determine if an option is selected. Uses strict equality by default. | | groupBy | func | | If provided, the options will be grouped under the returned string. The groupBy value is also used as the text for group headings when `renderGroup` is not provided.

**Signature:**
`function(options: any) => string`
*options:* The option to group. | | id | string | | This prop is used to help implement the accessibility logic. If you don't provide this prop. It falls back to a randomly generated id. | | includeInputInList | bool | false | If `true`, the highlight can move to the input. | diff --git a/docs/src/pages/components/autocomplete/Asynchronous.js b/docs/src/pages/components/autocomplete/Asynchronous.js index 191fb07914c2ac..3054441053b7d3 100644 --- a/docs/src/pages/components/autocomplete/Asynchronous.js +++ b/docs/src/pages/components/autocomplete/Asynchronous.js @@ -55,6 +55,7 @@ export default function Asynchronous() { onClose={() => { setOpen(false); }} + getOptionSelected={(option, value) => option.name === value.name} getOptionLabel={option => option.name} options={options} loading={loading} diff --git a/docs/src/pages/components/autocomplete/Asynchronous.tsx b/docs/src/pages/components/autocomplete/Asynchronous.tsx index acd8730746721c..fb9c2d4b5a848c 100644 --- a/docs/src/pages/components/autocomplete/Asynchronous.tsx +++ b/docs/src/pages/components/autocomplete/Asynchronous.tsx @@ -59,6 +59,7 @@ export default function Asynchronous() { onClose={() => { setOpen(false); }} + getOptionSelected={(option, value) => option.name === value.name} getOptionLabel={option => option.name} options={options} loading={loading} diff --git a/packages/material-ui-lab/src/Autocomplete/Autocomplete.js b/packages/material-ui-lab/src/Autocomplete/Autocomplete.js index 02b14e1979135c..991500fc58a886 100644 --- a/packages/material-ui-lab/src/Autocomplete/Autocomplete.js +++ b/packages/material-ui-lab/src/Autocomplete/Autocomplete.js @@ -236,6 +236,7 @@ const Autocomplete = React.forwardRef(function Autocomplete(props, ref) { freeSolo = false, getOptionDisabled, getOptionLabel = x => x, + getOptionSelected, groupBy, id: idProp, includeInputInList = false, @@ -553,6 +554,11 @@ Autocomplete.propTypes = { * It's used to fill the input (and the list box options if `renderOption` is not provided). */ getOptionLabel: PropTypes.func, + /** + * Used to determine if an option is selected. + * Uses strict equality by default. + */ + getOptionSelected: PropTypes.func, /** * If provided, the options will be grouped under the returned string. * The groupBy value is also used as the text for group headings when `renderGroup` is not provided. diff --git a/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.d.ts b/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.d.ts index d67795e0b62f72..310167c6c7145a 100644 --- a/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.d.ts +++ b/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.d.ts @@ -90,6 +90,11 @@ export interface UseAutocompleteProps { * It's used to fill the input (and the list box options if `renderOption` is not provided). */ getOptionLabel?: (option: any) => string; + /** + * Used to determine if an option is selected. + * Uses strict equality by default. + */ + getOptionSelected?: (option: any, value: any) => boolean; /** * If provided, the options will be grouped under the returned string. * The groupBy value is also used as the text for group headings when `renderGroup` is not provided. diff --git a/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.js b/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.js index a8fd29e1aeabaa..95d714e81d4ec9 100644 --- a/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.js +++ b/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.js @@ -83,6 +83,7 @@ export default function useAutocomplete(props) { freeSolo = false, getOptionDisabled, getOptionLabel = x => x, + getOptionSelected = (option, value) => option === value, groupBy, id: idProp, includeInputInList = false, @@ -239,7 +240,9 @@ export default function useAutocomplete(props) { options.filter(option => { if ( filterSelectedOptions && - (multiple ? value.indexOf(option) !== -1 : value === option) + (multiple ? value : [value]).some( + value2 => value2 !== null && getOptionSelected(option, value2), + ) ) { return false; } @@ -416,7 +419,15 @@ export default function useAutocomplete(props) { if (multiple) { const item = newValue; newValue = Array.isArray(value) ? [...value] : []; - const itemIndex = value.indexOf(item); + + let itemIndex = -1; + // To replace with .findIndex() once we stop IE 11 support. + for (let i = 0; i < newValue.length; i += 1) { + if (getOptionSelected(item, newValue[i])) { + itemIndex = i; + } + } + if (itemIndex === -1) { newValue.push(item); } else { @@ -791,7 +802,9 @@ export default function useAutocomplete(props) { }, }), getOptionProps: ({ index, option }) => { - const selected = multiple ? value.indexOf(option) !== -1 : value === option; + const selected = (multiple ? value : [value]).some( + value2 => value2 != null && getOptionSelected(option, value2), + ); const disabled = getOptionDisabled ? getOptionDisabled(option) : false; return {