Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Autocomplete] Add blurOnSelect prop #18827

Merged
merged 3 commits into from
Dec 15, 2019
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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">'pointer'<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 only on touch devices. - `pointer` the input is blurred only on pointing devices. |
| <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 @@ -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 only on touch devices.
* - `pointer` the input is blurred only on pointing devices.
*/
blurOnSelect: PropTypes.oneOfType([PropTypes.oneOf(['pointer', 'touch']), PropTypes.bool]),
/**
* Override or extend the styles applied to the component.
* See [CSS API](#css) below for more details.
Expand Down
73 changes: 73 additions & 0 deletions packages/material-ui-lab/src/Autocomplete/Autocomplete.test.js
Original file line number Diff line number Diff line change
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 firstItem = getByRole('option');
expect(textbox).to.have.focus;
fireEvent.click(firstItem);
expect(textbox).to.not.have.focus;

const opener = queryByTitle('Open');
fireEvent.click(opener);
expect(textbox).to.have.focus;
firstItem = getByRole('option');
fireEvent.touchStart(firstItem);
fireEvent.click(firstItem);
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 firstItem = getByRole('option');
fireEvent.click(firstItem);
expect(textbox).to.have.focus;

const opener = queryByTitle('Open');
fireEvent.click(opener);
firstItem = getByRole('option');
fireEvent.touchStart(firstItem);
fireEvent.click(firstItem);
expect(textbox).to.not.have.focus;
});

it('[blurOnSelect="pointer"] 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="pointer"
/>,
);
const textbox = getByRole('textbox');
let firstItem = getByRole('option');
fireEvent.touchStart(firstItem);
fireEvent.click(firstItem);
expect(textbox).to.have.focus;

const opener = queryByTitle('Open');
fireEvent.click(opener);
firstItem = getByRole('option');
fireEvent.click(firstItem);
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 only on touch devices.
* - `pointer` the input is blurred only on pointing devices.
oliviertassinari marked this conversation as resolved.
Show resolved Hide resolved
*/
blurOnSelect?: 'touch' | 'pointer' | 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 === 'pointer' && !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