diff --git a/docs/pages/api/autocomplete.md b/docs/pages/api/autocomplete.md index 340066ee750df6..d5a96d3226c1d4 100644 --- a/docs/pages/api/autocomplete.md +++ b/docs/pages/api/autocomplete.md @@ -27,6 +27,7 @@ You can learn more about the difference by [reading this guide](/guides/minimizi | autoComplete | bool | false | If `true`, the portion of the selected suggestion that has not been typed by the user, known as the completion string, appears inline after the input cursor in the textbox. The inline completion string is visually highlighted and has a selected state. | | autoHighlight | bool | false | If `true`, the first option is automatically highlighted. | | autoSelect | bool | false | If `true`, the selected option becomes the value of the input when the Autocomplete loses focus unless the user chooses a different option or changes the character string in the input. | +| blurOnSelect | 'mouse'
| 'touch'
| bool
| false | Control if the input should be blurred when an option is selected:
- `false` the input is not blurred. - `true` the input is always blurred. - `touch` the input is blurred after a touch event. - `mouse` the input is blurred after a mouse event. | | classes | object | | Override or extend the styles applied to the component. See [CSS API](#css) below for more details. | | clearOnEscape | bool | false | If `true`, clear all values when the user presses escape and the popup is closed. | | clearText | string | 'Clear' | Override the default text for the *clear* icon button.
For localization purposes, you can use the provided [translations](/guides/localization/). | diff --git a/packages/material-ui-lab/src/Autocomplete/Autocomplete.js b/packages/material-ui-lab/src/Autocomplete/Autocomplete.js index d6f1d1a5a1a313..438adc1de06f8d 100644 --- a/packages/material-ui-lab/src/Autocomplete/Autocomplete.js +++ b/packages/material-ui-lab/src/Autocomplete/Autocomplete.js @@ -218,6 +218,7 @@ const Autocomplete = React.forwardRef(function Autocomplete(props, ref) { autoComplete = false, autoHighlight = false, autoSelect = false, + blurOnSelect = false, classes, className, clearOnEscape = false, @@ -466,6 +467,15 @@ Autocomplete.propTypes = { * a different option or changes the character string in the input. */ autoSelect: PropTypes.bool, + /** + * Control if the input should be blurred when an option is selected: + * + * - `false` the input is not blurred. + * - `true` the input is always blurred. + * - `touch` the input is blurred after a touch event. + * - `mouse` the input is blurred after a mouse event. + */ + blurOnSelect: PropTypes.oneOfType([PropTypes.oneOf(['mouse', 'touch']), PropTypes.bool]), /** * Override or extend the styles applied to the component. * See [CSS API](#css) below for more details. diff --git a/packages/material-ui-lab/src/Autocomplete/Autocomplete.test.js b/packages/material-ui-lab/src/Autocomplete/Autocomplete.test.js index 1132b7ed002450..62b4b928d402da 100644 --- a/packages/material-ui-lab/src/Autocomplete/Autocomplete.test.js +++ b/packages/material-ui-lab/src/Autocomplete/Autocomplete.test.js @@ -564,8 +564,8 @@ describe('', () => { fireEvent.click(input); const listbox = getByRole('listbox'); - const firstItem = listbox.querySelector('li'); - fireEvent.click(firstItem); + const firstOption = listbox.querySelector('li'); + fireEvent.click(firstOption); expect(handleChange.args[0][1]).to.equal('one'); }); @@ -779,4 +779,77 @@ describe('', () => { expect(handleInputChange.args[0][2]).to.equal('reset'); }); }); + + describe('prop: blurOnSelect', () => { + it('[blurOnSelect=true] should blur the input when clicking or touching options', () => { + const options = [{ name: 'foo' }]; + const { getByRole, queryByTitle } = render( + option.name} + renderInput={params => } + blurOnSelect + />, + ); + const textbox = getByRole('textbox'); + let firstOption = getByRole('option'); + expect(textbox).to.have.focus; + fireEvent.click(firstOption); + expect(textbox).to.not.have.focus; + + const opener = queryByTitle('Open'); + fireEvent.click(opener); + expect(textbox).to.have.focus; + firstOption = getByRole('option'); + fireEvent.touchStart(firstOption); + fireEvent.click(firstOption); + expect(textbox).to.not.have.focus; + }); + + it('[blurOnSelect="touch"] should only blur the input when an option is touched', () => { + const options = [{ name: 'foo' }]; + const { getByRole, queryByTitle } = render( + option.name} + renderInput={params => } + blurOnSelect="touch" + />, + ); + const textbox = getByRole('textbox'); + let firstOption = getByRole('option'); + fireEvent.click(firstOption); + expect(textbox).to.have.focus; + + const opener = queryByTitle('Open'); + fireEvent.click(opener); + firstOption = getByRole('option'); + fireEvent.touchStart(firstOption); + fireEvent.click(firstOption); + expect(textbox).to.not.have.focus; + }); + + it('[blurOnSelect="mouse"] should only blur the input when an option is clicked', () => { + const options = [{ name: 'foo' }]; + const { getByRole, queryByTitle } = render( + option.name} + renderInput={params => } + blurOnSelect="mouse" + />, + ); + const textbox = getByRole('textbox'); + let firstOption = getByRole('option'); + fireEvent.touchStart(firstOption); + fireEvent.click(firstOption); + expect(textbox).to.have.focus; + + const opener = queryByTitle('Open'); + fireEvent.click(opener); + firstOption = getByRole('option'); + fireEvent.click(firstOption); + expect(textbox).to.not.have.focus; + }); + }); }); diff --git a/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.d.ts b/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.d.ts index 0628df0b15c714..250312f748a3d1 100644 --- a/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.d.ts +++ b/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.d.ts @@ -35,6 +35,15 @@ export interface UseAutocompleteProps { * a different option or changes the character string in the input. */ autoSelect?: boolean; + /** + * Control if the input should be blurred when an option is selected: + * + * - `false` the input is not blurred. + * - `true` the input is always blurred. + * - `touch` the input is blurred after a touch event. + * - `mouse` the input is blurred after a mouse event. + */ + blurOnSelect?: 'touch' | 'mouse' | true | false; /** * If `true`, clear all values when the user presses escape and the popup is closed. */ diff --git a/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.js b/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.js index 5b4ef231c45e85..ca8afabdf98834 100644 --- a/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.js +++ b/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.js @@ -71,6 +71,7 @@ export default function useAutocomplete(props) { autoComplete = false, autoHighlight = false, autoSelect = false, + blurOnSelect = false, clearOnEscape = false, debug = false, defaultValue, @@ -666,9 +667,25 @@ export default function useAutocomplete(props) { setHighlightedIndex(index, 'mouse'); }; + const isTouch = React.useRef(false); + + const handleOptionTouchStart = () => { + isTouch.current = true; + }; + const handleOptionClick = event => { const index = Number(event.currentTarget.getAttribute('data-option-index')); selectNewValue(event, filteredOptions[index]); + + if ( + blurOnSelect === true || + (blurOnSelect === 'touch' && isTouch.current) || + (blurOnSelect === 'mouse' && !isTouch.current) + ) { + inputRef.current.blur(); + } + + isTouch.current = false; }; const handleTagDelete = index => event => { @@ -814,6 +831,7 @@ export default function useAutocomplete(props) { id: `${id}-option-${index}`, onMouseOver: handleOptionMouseOver, onClick: handleOptionClick, + onTouchStart: handleOptionTouchStart, 'data-option-index': index, 'aria-disabled': disabled, 'aria-selected': selected,