Skip to content

Commit

Permalink
[Autocomplete] Add blurOnSelect prop (#18827)
Browse files Browse the repository at this point in the history
  • Loading branch information
m4theushw authored and oliviertassinari committed Dec 15, 2019
1 parent 3a8f0ea commit 1ccba13
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 2 deletions.
1 change: 1 addition & 0 deletions docs/pages/api/autocomplete.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ You can learn more about the difference by [reading this guide](/guides/minimizi
| <span class="prop-name">autoComplete</span> | <span class="prop-type">bool</span> | <span class="prop-default">false</span> | 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. |
| <span class="prop-name">autoHighlight</span> | <span class="prop-type">bool</span> | <span class="prop-default">false</span> | If `true`, the first option is automatically highlighted. |
| <span class="prop-name">autoSelect</span> | <span class="prop-type">bool</span> | <span class="prop-default">false</span> | 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. |
| <span class="prop-name">blurOnSelect</span> | <span class="prop-type">'mouse'<br>&#124;&nbsp;'touch'<br>&#124;&nbsp;bool</span> | <span class="prop-default">false</span> | Control if the input should be blurred when an option is selected:<br>- `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. |
| <span class="prop-name">classes</span> | <span class="prop-type">object</span> | | Override or extend the styles applied to the component. See [CSS API](#css) below for more details. |
| <span class="prop-name">clearOnEscape</span> | <span class="prop-type">bool</span> | <span class="prop-default">false</span> | If `true`, clear all values when the user presses escape and the popup is closed. |
| <span class="prop-name">clearText</span> | <span class="prop-type">string</span> | <span class="prop-default">'Clear'</span> | Override the default text for the *clear* icon button.<br>For localization purposes, you can use the provided [translations](/guides/localization/). |
Expand Down
10 changes: 10 additions & 0 deletions packages/material-ui-lab/src/Autocomplete/Autocomplete.js
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@ const Autocomplete = React.forwardRef(function Autocomplete(props, ref) {
autoComplete = false,
autoHighlight = false,
autoSelect = false,
blurOnSelect = false,
classes,
className,
clearOnEscape = false,
Expand Down Expand Up @@ -459,6 +460,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.
Expand Down
77 changes: 75 additions & 2 deletions packages/material-ui-lab/src/Autocomplete/Autocomplete.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -564,8 +564,8 @@ describe('<Autocomplete />', () => {
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');
});
Expand Down Expand Up @@ -779,4 +779,77 @@ describe('<Autocomplete />', () => {
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(
<Autocomplete
options={options}
getOptionLabel={option => option.name}
renderInput={params => <TextField {...params} autoFocus />}
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(
<Autocomplete
options={options}
getOptionLabel={option => option.name}
renderInput={params => <TextField {...params} autoFocus />}
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(
<Autocomplete
options={options}
getOptionLabel={option => option.name}
renderInput={params => <TextField {...params} autoFocus />}
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;
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
Expand Down
18 changes: 18 additions & 0 deletions packages/material-ui-lab/src/useAutocomplete/useAutocomplete.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ export default function useAutocomplete(props) {
autoComplete = false,
autoHighlight = false,
autoSelect = false,
blurOnSelect = false,
clearOnEscape = false,
debug = false,
defaultValue,
Expand Down Expand Up @@ -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 => {
Expand Down Expand Up @@ -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,
Expand Down

0 comments on commit 1ccba13

Please sign in to comment.