diff --git a/docs/translations/api-docs-joy/autocomplete/autocomplete.json b/docs/translations/api-docs-joy/autocomplete/autocomplete.json index ae572296547d1a..0f57da64cf78f5 100644 --- a/docs/translations/api-docs-joy/autocomplete/autocomplete.json +++ b/docs/translations/api-docs-joy/autocomplete/autocomplete.json @@ -33,7 +33,7 @@ "onChange": "Callback fired when the value changes.

Signature:
function(event: React.SyntheticEvent, value: T | Array<T>, reason: string, details?: string) => void
event: The event source of the callback.
value: The new value of the component.
reason: One of "createOption", "selectOption", "removeOption", "blur" or "clear".", "onClose": "Callback fired when the popup requests to be closed. Use in controlled mode (see open).

Signature:
function(event: React.SyntheticEvent, reason: string) => void
event: The event source of the callback.
reason: Can be: "toggleInput", "escape", "selectOption", "removeOption", "blur".", "onHighlightChange": "Callback fired when the highlight option changes.

Signature:
function(event: React.SyntheticEvent, option: T, reason: string) => void
event: The event source of the callback.
option: The highlighted option.
reason: Can be: "keyboard", "auto", "mouse", "touch".", - "onInputChange": "Callback fired when the input value changes.

Signature:
function(event: React.SyntheticEvent, value: string, reason: string) => void
event: The event source of the callback.
value: The new value of the text input.
reason: Can be: "input" (user input), "reset" (programmatic change), "clear".", + "onInputChange": "Callback fired when the input value changes.

Signature:
function(event: React.SyntheticEvent, value: string, reason: string) => void
event: The event source of the callback.
value: The new value of the text input.
reason: Can be: "input" (user input), "reset" (programmatic change), "clear", "blur", "selectOption", "removeOption"", "onOpen": "Callback fired when the popup requests to be opened. Use in controlled mode (see open).

Signature:
function(event: React.SyntheticEvent) => void
event: The event source of the callback.", "open": "If true, the component is shown.", "openText": "Override the default text for the open popup icon button.
For localization purposes, you can use the provided translations.", diff --git a/docs/translations/api-docs/autocomplete/autocomplete.json b/docs/translations/api-docs/autocomplete/autocomplete.json index 54ebd6ebd1aba9..c3d5fd1d988a97 100644 --- a/docs/translations/api-docs/autocomplete/autocomplete.json +++ b/docs/translations/api-docs/autocomplete/autocomplete.json @@ -44,7 +44,7 @@ "onChange": "Callback fired when the value changes.

Signature:
function(event: React.SyntheticEvent, value: T | Array<T>, reason: string, details?: string) => void
event: The event source of the callback.
value: The new value of the component.
reason: One of "createOption", "selectOption", "removeOption", "blur" or "clear".", "onClose": "Callback fired when the popup requests to be closed. Use in controlled mode (see open).

Signature:
function(event: React.SyntheticEvent, reason: string) => void
event: The event source of the callback.
reason: Can be: "toggleInput", "escape", "selectOption", "removeOption", "blur".", "onHighlightChange": "Callback fired when the highlight option changes.

Signature:
function(event: React.SyntheticEvent, option: T, reason: string) => void
event: The event source of the callback.
option: The highlighted option.
reason: Can be: "keyboard", "auto", "mouse", "touch".", - "onInputChange": "Callback fired when the input value changes.

Signature:
function(event: React.SyntheticEvent, value: string, reason: string) => void
event: The event source of the callback.
value: The new value of the text input.
reason: Can be: "input" (user input), "reset" (programmatic change), "clear".", + "onInputChange": "Callback fired when the input value changes.

Signature:
function(event: React.SyntheticEvent, value: string, reason: string) => void
event: The event source of the callback.
value: The new value of the text input.
reason: Can be: "input" (user input), "reset" (programmatic change), "clear", "blur", "selectOption", "removeOption"", "onOpen": "Callback fired when the popup requests to be opened. Use in controlled mode (see open).

Signature:
function(event: React.SyntheticEvent) => void
event: The event source of the callback.", "open": "If true, the component is shown.", "openOnFocus": "If true, the popup will open on input focus.", diff --git a/packages/mui-base/src/useAutocomplete/useAutocomplete.d.ts b/packages/mui-base/src/useAutocomplete/useAutocomplete.d.ts index de334a48690467..7ef2a796f81645 100644 --- a/packages/mui-base/src/useAutocomplete/useAutocomplete.d.ts +++ b/packages/mui-base/src/useAutocomplete/useAutocomplete.d.ts @@ -248,7 +248,7 @@ export interface UseAutocompleteProps< * * @param {React.SyntheticEvent} event The event source of the callback. * @param {string} value The new value of the text input. - * @param {string} reason Can be: `"input"` (user input), `"reset"` (programmatic change), `"clear"`. + * @param {string} reason Can be: `"input"` (user input), `"reset"` (programmatic change), `"clear"`, `"blur"`, `"selectOption"`, `"removeOption"` */ onInputChange?: ( event: React.SyntheticEvent, @@ -320,7 +320,13 @@ export type AutocompleteCloseReason = | 'selectOption' | 'removeOption' | 'blur'; -export type AutocompleteInputChangeReason = 'input' | 'reset' | 'clear'; +export type AutocompleteInputChangeReason = + | 'input' + | 'reset' + | 'clear' + | 'blur' + | 'selectOption' + | 'removeOption'; export type AutocompleteGetTagProps = ({ index }: { index: number }) => { key: number; diff --git a/packages/mui-base/src/useAutocomplete/useAutocomplete.js b/packages/mui-base/src/useAutocomplete/useAutocomplete.js index 8af5d5cbec66bc..8cc02e5f719f6b 100644 --- a/packages/mui-base/src/useAutocomplete/useAutocomplete.js +++ b/packages/mui-base/src/useAutocomplete/useAutocomplete.js @@ -164,7 +164,7 @@ export default function useAutocomplete(props) { const [focused, setFocused] = React.useState(false); const resetInputValue = React.useCallback( - (event, newValue) => { + (event, newValue, reason) => { // retain current `inputValue` if new option isn't selected and `clearOnBlur` is false // When `multiple` is enabled, `newValue` is an array of all selected items including the newly selected item const isOptionSelected = multiple ? value.length < newValue.length : newValue !== null; @@ -188,7 +188,7 @@ export default function useAutocomplete(props) { setInputValueState(newInputValue); if (onInputChange) { - onInputChange(event, newInputValue, 'reset'); + onInputChange(event, newInputValue, reason); } }, [getOptionLabel, inputValue, multiple, onInputChange, setInputValueState, clearOnBlur, value], @@ -247,7 +247,7 @@ export default function useAutocomplete(props) { return; } - resetInputValue(null, value); + resetInputValue(null, value, 'reset'); }, [value, resetInputValue, focused, previousProps.value, freeSolo]); const listboxAvailable = open && filteredOptions.length > 0 && !readOnly; @@ -682,7 +682,7 @@ export default function useAutocomplete(props) { } } - resetInputValue(event, newValue); + resetInputValue(event, newValue, reason); handleValue(event, newValue, reason, { option }); if (!disableCloseOnSelect && (!event || (!event.ctrlKey && !event.metaKey))) { @@ -938,7 +938,7 @@ export default function useAutocomplete(props) { } else if (autoSelect && freeSolo && inputValue !== '') { selectNewValue(event, inputValue, 'blur', 'freeSolo'); } else if (clearOnBlur) { - resetInputValue(event, value); + resetInputValue(event, value, 'blur'); } handleClose(event, 'blur'); diff --git a/packages/mui-joy/src/Autocomplete/Autocomplete.test.tsx b/packages/mui-joy/src/Autocomplete/Autocomplete.test.tsx index a0d977163bb241..5dda4d9eb75c81 100644 --- a/packages/mui-joy/src/Autocomplete/Autocomplete.test.tsx +++ b/packages/mui-joy/src/Autocomplete/Autocomplete.test.tsx @@ -1843,24 +1843,31 @@ describe('Joy ', () => { it('provides a reason on select reset', () => { const handleInputChange = spy(); - const options = [{ name: 'foo' }]; - render( - option.name} - autoFocus - />, - ); - const textbox = screen.getByRole('combobox'); + const options = [{ name: 'foo' }, { name: 'bar' }]; - fireEvent.keyDown(textbox, { key: 'ArrowDown' }); - fireEvent.keyDown(textbox, { key: 'Enter' }); + function MyComponent() { + const [value, setValue] = React.useState(options[0]); + return ( + + option.name} + value={value} + /> + + + ); + } + render(); + const resetBtn = screen.getByText('Reset'); - expect(handleInputChange.callCount).to.equal(1); - expect(handleInputChange.args[0][1]).to.equal(options[0].name); - expect(handleInputChange.args[0][2]).to.equal('reset'); + fireEvent.click(resetBtn); + + expect(handleInputChange.callCount).to.equal(4); + expect(handleInputChange.args[3][1]).to.equal(options[1].name); + expect(handleInputChange.args[3][2]).to.equal('reset'); }); }); diff --git a/packages/mui-joy/src/Autocomplete/Autocomplete.tsx b/packages/mui-joy/src/Autocomplete/Autocomplete.tsx index 077782a0e69de9..043f10ee01b76f 100644 --- a/packages/mui-joy/src/Autocomplete/Autocomplete.tsx +++ b/packages/mui-joy/src/Autocomplete/Autocomplete.tsx @@ -975,7 +975,7 @@ Autocomplete.propTypes /* remove-proptypes */ = { * * @param {React.SyntheticEvent} event The event source of the callback. * @param {string} value The new value of the text input. - * @param {string} reason Can be: `"input"` (user input), `"reset"` (programmatic change), `"clear"`. + * @param {string} reason Can be: `"input"` (user input), `"reset"` (programmatic change), `"clear"`, `"blur"`, `"selectOption"`, `"removeOption"` */ onInputChange: PropTypes.func, /** diff --git a/packages/mui-material/src/Autocomplete/Autocomplete.js b/packages/mui-material/src/Autocomplete/Autocomplete.js index bc1723d38cea36..eaa2049a16c4ed 100644 --- a/packages/mui-material/src/Autocomplete/Autocomplete.js +++ b/packages/mui-material/src/Autocomplete/Autocomplete.js @@ -988,7 +988,7 @@ Autocomplete.propTypes /* remove-proptypes */ = { * * @param {React.SyntheticEvent} event The event source of the callback. * @param {string} value The new value of the text input. - * @param {string} reason Can be: `"input"` (user input), `"reset"` (programmatic change), `"clear"`. + * @param {string} reason Can be: `"input"` (user input), `"reset"` (programmatic change), `"clear"`, `"blur"`, `"selectOption"`, `"removeOption"` */ onInputChange: PropTypes.func, /** diff --git a/packages/mui-material/src/Autocomplete/Autocomplete.test.js b/packages/mui-material/src/Autocomplete/Autocomplete.test.js index 0315f2c653166a..b0337a910554f1 100644 --- a/packages/mui-material/src/Autocomplete/Autocomplete.test.js +++ b/packages/mui-material/src/Autocomplete/Autocomplete.test.js @@ -2266,24 +2266,32 @@ describe('', () => { it('provides a reason on select reset', () => { const handleInputChange = spy(); - const options = [{ name: 'foo' }]; - render( - option.name} - renderInput={(params) => } - />, - ); - const textbox = screen.getByRole('combobox'); + const options = [{ name: 'foo' }, { name: 'bar' }]; - fireEvent.keyDown(textbox, { key: 'ArrowDown' }); - fireEvent.keyDown(textbox, { key: 'Enter' }); + function MyComponent() { + const [value, setValue] = React.useState(options[0]); + return ( + + option.name} + renderInput={(params) => } + value={value} + /> + + + ); + } + render(); + const resetBtn = screen.getByText('Reset'); - expect(handleInputChange.callCount).to.equal(1); - expect(handleInputChange.args[0][1]).to.equal(options[0].name); - expect(handleInputChange.args[0][2]).to.equal('reset'); + fireEvent.click(resetBtn); + + expect(handleInputChange.callCount).to.equal(3); + expect(handleInputChange.args[2][1]).to.equal(options[1].name); + expect(handleInputChange.args[2][2]).to.equal('reset'); }); });