From f26b266b1175ab5e8b4b74e316c084c199d275bf Mon Sep 17 00:00:00 2001 From: Matheus Wichman Date: Fri, 13 Dec 2019 15:07:45 -0300 Subject: [PATCH 1/3] [Autocomplete] Add blurOnSelect prop --- .../src/Autocomplete/Autocomplete.js | 1 + .../src/Autocomplete/Autocomplete.test.js | 73 +++++++++++++++++++ .../src/useAutocomplete/useAutocomplete.js | 18 +++++ 3 files changed, 92 insertions(+) diff --git a/packages/material-ui-lab/src/Autocomplete/Autocomplete.js b/packages/material-ui-lab/src/Autocomplete/Autocomplete.js index d6f1d1a5a1a313..aae6ff62273a6d 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, classes, className, clearOnEscape = false, diff --git a/packages/material-ui-lab/src/Autocomplete/Autocomplete.test.js b/packages/material-ui-lab/src/Autocomplete/Autocomplete.test.js index 1132b7ed002450..d2696fd4ab49ee 100644 --- a/packages/material-ui-lab/src/Autocomplete/Autocomplete.test.js +++ b/packages/material-ui-lab/src/Autocomplete/Autocomplete.test.js @@ -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 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( + option.name} + renderInput={params => } + 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="event"] should only blur the input when an option is clicked', () => { + const options = [{ name: 'foo' }]; + const { getByRole, queryByTitle } = render( + option.name} + renderInput={params => } + 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; + }); + }); }); diff --git a/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.js b/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.js index 5b4ef231c45e85..a2b6f4c94ca019 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 === 'pointer' && !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, From 29af7a2a578892188f354676fb0fa032ea3013c3 Mon Sep 17 00:00:00 2001 From: Matheus Wichman Date: Fri, 13 Dec 2019 21:25:56 -0300 Subject: [PATCH 2/3] [Autocomplete] Add docs about blurOnSelect prop --- docs/pages/api/autocomplete.md | 1 + .../material-ui-lab/src/Autocomplete/Autocomplete.js | 11 ++++++++++- .../src/Autocomplete/Autocomplete.test.js | 2 +- .../src/useAutocomplete/useAutocomplete.d.ts | 9 +++++++++ 4 files changed, 21 insertions(+), 2 deletions(-) diff --git a/docs/pages/api/autocomplete.md b/docs/pages/api/autocomplete.md index 340066ee750df6..2d07fbd95c2f1d 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 | 'pointer'
| '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 only on touch devices. - `pointer` the input is blurred only on pointing devices. | | 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 aae6ff62273a6d..7b687ae4ce7363 100644 --- a/packages/material-ui-lab/src/Autocomplete/Autocomplete.js +++ b/packages/material-ui-lab/src/Autocomplete/Autocomplete.js @@ -218,7 +218,7 @@ const Autocomplete = React.forwardRef(function Autocomplete(props, ref) { autoComplete = false, autoHighlight = false, autoSelect = false, - blurOnSelect, + blurOnSelect = false, classes, className, clearOnEscape = false, @@ -467,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. diff --git a/packages/material-ui-lab/src/Autocomplete/Autocomplete.test.js b/packages/material-ui-lab/src/Autocomplete/Autocomplete.test.js index d2696fd4ab49ee..26e1b160ec584d 100644 --- a/packages/material-ui-lab/src/Autocomplete/Autocomplete.test.js +++ b/packages/material-ui-lab/src/Autocomplete/Autocomplete.test.js @@ -829,7 +829,7 @@ describe('', () => { expect(textbox).to.not.have.focus; }); - it('[blurOnSelect="event"] should only blur the input when an option is clicked', () => { + it('[blurOnSelect="pointer"] should only blur the input when an option is clicked', () => { const options = [{ name: 'foo' }]; const { getByRole, queryByTitle } = render( Date: Sat, 14 Dec 2019 12:47:11 +0100 Subject: [PATCH 3/3] rename pointer to mouse --- docs/pages/api/autocomplete.md | 2 +- .../src/Autocomplete/Autocomplete.js | 6 +-- .../src/Autocomplete/Autocomplete.test.js | 38 +++++++++---------- .../src/useAutocomplete/useAutocomplete.d.ts | 6 +-- .../src/useAutocomplete/useAutocomplete.js | 2 +- 5 files changed, 27 insertions(+), 27 deletions(-) diff --git a/docs/pages/api/autocomplete.md b/docs/pages/api/autocomplete.md index 2d07fbd95c2f1d..d5a96d3226c1d4 100644 --- a/docs/pages/api/autocomplete.md +++ b/docs/pages/api/autocomplete.md @@ -27,7 +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 | 'pointer'
| '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 only on touch devices. - `pointer` the input is blurred only on pointing devices. | +| 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 7b687ae4ce7363..438adc1de06f8d 100644 --- a/packages/material-ui-lab/src/Autocomplete/Autocomplete.js +++ b/packages/material-ui-lab/src/Autocomplete/Autocomplete.js @@ -472,10 +472,10 @@ Autocomplete.propTypes = { * * - `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. + * - `touch` the input is blurred after a touch event. + * - `mouse` the input is blurred after a mouse event. */ - blurOnSelect: PropTypes.oneOfType([PropTypes.oneOf(['pointer', 'touch']), PropTypes.bool]), + 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 26e1b160ec584d..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'); }); @@ -792,17 +792,17 @@ describe('', () => { />, ); const textbox = getByRole('textbox'); - let firstItem = getByRole('option'); + let firstOption = getByRole('option'); expect(textbox).to.have.focus; - fireEvent.click(firstItem); + fireEvent.click(firstOption); 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); + firstOption = getByRole('option'); + fireEvent.touchStart(firstOption); + fireEvent.click(firstOption); expect(textbox).to.not.have.focus; }); @@ -817,38 +817,38 @@ describe('', () => { />, ); const textbox = getByRole('textbox'); - let firstItem = getByRole('option'); - fireEvent.click(firstItem); + let firstOption = getByRole('option'); + fireEvent.click(firstOption); expect(textbox).to.have.focus; const opener = queryByTitle('Open'); fireEvent.click(opener); - firstItem = getByRole('option'); - fireEvent.touchStart(firstItem); - fireEvent.click(firstItem); + firstOption = getByRole('option'); + fireEvent.touchStart(firstOption); + fireEvent.click(firstOption); expect(textbox).to.not.have.focus; }); - it('[blurOnSelect="pointer"] should only blur the input when an option is clicked', () => { + 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="pointer" + blurOnSelect="mouse" />, ); const textbox = getByRole('textbox'); - let firstItem = getByRole('option'); - fireEvent.touchStart(firstItem); - fireEvent.click(firstItem); + let firstOption = getByRole('option'); + fireEvent.touchStart(firstOption); + fireEvent.click(firstOption); expect(textbox).to.have.focus; const opener = queryByTitle('Open'); fireEvent.click(opener); - firstItem = getByRole('option'); - fireEvent.click(firstItem); + 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 3d14f57d68b0e9..250312f748a3d1 100644 --- a/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.d.ts +++ b/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.d.ts @@ -40,10 +40,10 @@ export interface UseAutocompleteProps { * * - `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. + * - `touch` the input is blurred after a touch event. + * - `mouse` the input is blurred after a mouse event. */ - blurOnSelect?: 'touch' | 'pointer' | true | false; + 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 a2b6f4c94ca019..ca8afabdf98834 100644 --- a/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.js +++ b/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.js @@ -680,7 +680,7 @@ export default function useAutocomplete(props) { if ( blurOnSelect === true || (blurOnSelect === 'touch' && isTouch.current) || - (blurOnSelect === 'pointer' && !isTouch.current) + (blurOnSelect === 'mouse' && !isTouch.current) ) { inputRef.current.blur(); }