Skip to content

Commit

Permalink
[Autocomplete] add reason prop for resetInputValue of useAutocomplete (
Browse files Browse the repository at this point in the history
  • Loading branch information
binh1298 committed May 12, 2023
1 parent 110fba8 commit a8ba538
Show file tree
Hide file tree
Showing 8 changed files with 64 additions and 43 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
"onChange": "Callback fired when the value changes.<br><br><strong>Signature:</strong><br><code>function(event: React.SyntheticEvent, value: T | Array&lt;T&gt;, reason: string, details?: string) =&gt; void</code><br><em>event:</em> The event source of the callback.<br><em>value:</em> The new value of the component.<br><em>reason:</em> One of &quot;createOption&quot;, &quot;selectOption&quot;, &quot;removeOption&quot;, &quot;blur&quot; or &quot;clear&quot;.",
"onClose": "Callback fired when the popup requests to be closed. Use in controlled mode (see open).<br><br><strong>Signature:</strong><br><code>function(event: React.SyntheticEvent, reason: string) =&gt; void</code><br><em>event:</em> The event source of the callback.<br><em>reason:</em> Can be: <code>&quot;toggleInput&quot;</code>, <code>&quot;escape&quot;</code>, <code>&quot;selectOption&quot;</code>, <code>&quot;removeOption&quot;</code>, <code>&quot;blur&quot;</code>.",
"onHighlightChange": "Callback fired when the highlight option changes.<br><br><strong>Signature:</strong><br><code>function(event: React.SyntheticEvent, option: T, reason: string) =&gt; void</code><br><em>event:</em> The event source of the callback.<br><em>option:</em> The highlighted option.<br><em>reason:</em> Can be: <code>&quot;keyboard&quot;</code>, <code>&quot;auto&quot;</code>, <code>&quot;mouse&quot;</code>, <code>&quot;touch&quot;</code>.",
"onInputChange": "Callback fired when the input value changes.<br><br><strong>Signature:</strong><br><code>function(event: React.SyntheticEvent, value: string, reason: string) =&gt; void</code><br><em>event:</em> The event source of the callback.<br><em>value:</em> The new value of the text input.<br><em>reason:</em> Can be: <code>&quot;input&quot;</code> (user input), <code>&quot;reset&quot;</code> (programmatic change), <code>&quot;clear&quot;</code>.",
"onInputChange": "Callback fired when the input value changes.<br><br><strong>Signature:</strong><br><code>function(event: React.SyntheticEvent, value: string, reason: string) =&gt; void</code><br><em>event:</em> The event source of the callback.<br><em>value:</em> The new value of the text input.<br><em>reason:</em> Can be: <code>&quot;input&quot;</code> (user input), <code>&quot;reset&quot;</code> (programmatic change), <code>&quot;clear&quot;</code>, <code>&quot;blur&quot;</code>, <code>&quot;selectOption&quot;</code>, <code>&quot;removeOption&quot;</code>",
"onOpen": "Callback fired when the popup requests to be opened. Use in controlled mode (see open).<br><br><strong>Signature:</strong><br><code>function(event: React.SyntheticEvent) =&gt; void</code><br><em>event:</em> The event source of the callback.",
"open": "If <code>true</code>, the component is shown.",
"openText": "Override the default text for the <em>open popup</em> icon button.<br>For localization purposes, you can use the provided <a href=\"/material-ui/guides/localization/\">translations</a>.",
Expand Down
2 changes: 1 addition & 1 deletion docs/translations/api-docs/autocomplete/autocomplete.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
"onChange": "Callback fired when the value changes.<br><br><strong>Signature:</strong><br><code>function(event: React.SyntheticEvent, value: T | Array&lt;T&gt;, reason: string, details?: string) =&gt; void</code><br><em>event:</em> The event source of the callback.<br><em>value:</em> The new value of the component.<br><em>reason:</em> One of &quot;createOption&quot;, &quot;selectOption&quot;, &quot;removeOption&quot;, &quot;blur&quot; or &quot;clear&quot;.",
"onClose": "Callback fired when the popup requests to be closed. Use in controlled mode (see open).<br><br><strong>Signature:</strong><br><code>function(event: React.SyntheticEvent, reason: string) =&gt; void</code><br><em>event:</em> The event source of the callback.<br><em>reason:</em> Can be: <code>&quot;toggleInput&quot;</code>, <code>&quot;escape&quot;</code>, <code>&quot;selectOption&quot;</code>, <code>&quot;removeOption&quot;</code>, <code>&quot;blur&quot;</code>.",
"onHighlightChange": "Callback fired when the highlight option changes.<br><br><strong>Signature:</strong><br><code>function(event: React.SyntheticEvent, option: T, reason: string) =&gt; void</code><br><em>event:</em> The event source of the callback.<br><em>option:</em> The highlighted option.<br><em>reason:</em> Can be: <code>&quot;keyboard&quot;</code>, <code>&quot;auto&quot;</code>, <code>&quot;mouse&quot;</code>, <code>&quot;touch&quot;</code>.",
"onInputChange": "Callback fired when the input value changes.<br><br><strong>Signature:</strong><br><code>function(event: React.SyntheticEvent, value: string, reason: string) =&gt; void</code><br><em>event:</em> The event source of the callback.<br><em>value:</em> The new value of the text input.<br><em>reason:</em> Can be: <code>&quot;input&quot;</code> (user input), <code>&quot;reset&quot;</code> (programmatic change), <code>&quot;clear&quot;</code>.",
"onInputChange": "Callback fired when the input value changes.<br><br><strong>Signature:</strong><br><code>function(event: React.SyntheticEvent, value: string, reason: string) =&gt; void</code><br><em>event:</em> The event source of the callback.<br><em>value:</em> The new value of the text input.<br><em>reason:</em> Can be: <code>&quot;input&quot;</code> (user input), <code>&quot;reset&quot;</code> (programmatic change), <code>&quot;clear&quot;</code>, <code>&quot;blur&quot;</code>, <code>&quot;selectOption&quot;</code>, <code>&quot;removeOption&quot;</code>",
"onOpen": "Callback fired when the popup requests to be opened. Use in controlled mode (see open).<br><br><strong>Signature:</strong><br><code>function(event: React.SyntheticEvent) =&gt; void</code><br><em>event:</em> The event source of the callback.",
"open": "If <code>true</code>, the component is shown.",
"openOnFocus": "If <code>true</code>, the popup will open on input focus.",
Expand Down
10 changes: 8 additions & 2 deletions packages/mui-base/src/useAutocomplete/useAutocomplete.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,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,
Expand Down Expand Up @@ -321,7 +321,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;
Expand Down
10 changes: 5 additions & 5 deletions packages/mui-base/src/useAutocomplete/useAutocomplete.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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],
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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))) {
Expand Down Expand Up @@ -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');
Expand Down
39 changes: 23 additions & 16 deletions packages/mui-joy/src/Autocomplete/Autocomplete.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1844,24 +1844,31 @@ describe('Joy <Autocomplete />', () => {

it('provides a reason on select reset', () => {
const handleInputChange = spy();
const options = [{ name: 'foo' }];
render(
<Autocomplete
onInputChange={handleInputChange}
openOnFocus
options={options}
getOptionLabel={(option) => 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 (
<React.Fragment>
<Autocomplete
onInputChange={handleInputChange}
openOnFocus
options={options}
getOptionLabel={(option) => option.name}
value={value}
/>
<button onClick={() => setValue(options[1])}>Reset</button>
</React.Fragment>
);
}
render(<MyComponent />);
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');
});
});

Expand Down
2 changes: 1 addition & 1 deletion packages/mui-joy/src/Autocomplete/Autocomplete.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -976,7 +976,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,
/**
Expand Down
2 changes: 1 addition & 1 deletion packages/mui-material/src/Autocomplete/Autocomplete.js
Original file line number Diff line number Diff line change
Expand Up @@ -989,7 +989,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,
/**
Expand Down
40 changes: 24 additions & 16 deletions packages/mui-material/src/Autocomplete/Autocomplete.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2268,24 +2268,32 @@ describe('<Autocomplete />', () => {

it('provides a reason on select reset', () => {
const handleInputChange = spy();
const options = [{ name: 'foo' }];
render(
<Autocomplete
onInputChange={handleInputChange}
openOnFocus
options={options}
getOptionLabel={(option) => option.name}
renderInput={(params) => <TextField {...params} 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 (
<React.Fragment>
<Autocomplete
onInputChange={handleInputChange}
openOnFocus
options={options}
getOptionLabel={(option) => option.name}
renderInput={(params) => <TextField {...params} autoFocus />}
value={value}
/>
<button onClick={() => setValue(options[1])}>Reset</button>
</React.Fragment>
);
}
render(<MyComponent />);
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');
});
});

Expand Down

0 comments on commit a8ba538

Please sign in to comment.