Repository: mui/material-ui (TypeScript)
Date: 2019-12-16 21:56:56+00:00
Task type: Feature

Issue:
[Autocomplete] Provide options to control behaviour when cleared
<!-- Provide a general summary of the feature in the Title above -->

<!--
  Thank you very much for contributing to Material-UI by creating an issue! ❤️
  To avoid duplicate issues we ask you to check off the following list.
-->

<!-- Checked checkbox should look like this: [x] -->

- [X] I have searched the [issues](https://github.com/mui-org/material-ui/issues) of this repository and believe that this is not a duplicate.

## Summary 💡

While this issue is similar to #18280 I don't believe its a duplicate.

It would be good if developers had the ability to customize the behavior of the autocomplete when the selection is cleared via the Clear button.

Options:
- `openOnClear={false}` - revert the Autocomplete to it's null/placeholder state
- `onClear` event handler

## Examples 🌈

```js
<Autocomplete
    options={["A", "B", "C", "D"]}
    openOnClear={false}
    renderInput={params => (
        <TextField {...params} label="Letter" variant="outlined" fullWidth />
    )}
/>
```



Issue discussion:
@Monbrey
- What's your motivation for such behavior?
- What's wrong with `disableOpenOnFocus`?
- What about a solution like this one: https://github.com/mui-org/material-ui/issues/18815#issuecomment-565599719? Basically, we could give a `reason` argument to the `onOpen` callback and you could ignore the "clear" events (require to control the open state).
I'm not really sure how to respond to my "motivation" other than to say that clearing a value doesn't always imply the user wants to select another one - if they did, I would think they'd be less likely to focus specifically on the clear button, and would just open the select anywhere.

Regarding `disableOpenOnFocus` - I did have some luck preventing it from opening by binding its boolean to a state value which was updated onChange - if the value became null, don't open the dropdown. However this doesn't prevent the Autocomplete input from being focused on clear, and doesn't revert it to the placeholder state.

The `reason` argument might also help in a similar way, however I tried a similar state-based approach with the `onOpen` and `onChange` events and encountered the same focus-related issues as above.
Thanks for the extra information, I better understand the motivation now. My previous suggestions were a bit off the target. I can see two different levels of solution.

1. We might want to change the default behavior to match the one you are describing. It does sound better.
2. We can add a reason argument to the onChange event. You would be able to switch the open prop.
What do you think of the following diff?
```diff
diff --git a/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.js b/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.js
index ca8afabdf..4466e4d50 100644
--- a/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.js
+++ b/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.js
@@ -108,6 +108,7 @@ export default function useAutocomplete(props) {
     setDefaultId(`mui-autocomplete-${Math.round(Math.random() * 1e5)}`);
   }, []);

+  const ignoreFocus = React.useRef(false);
   const firstFocus = React.useRef(true);
   const inputRef = React.useRef(null);
   const listboxRef = React.useRef(null);
@@ -509,10 +510,8 @@ export default function useAutocomplete(props) {
   };

   const handleClear = event => {
+    ignoreFocus.current = true;
     handleValue(event, multiple ? [] : null);
-    if (disableOpenOnFocus) {
-      handleClose();
-    }
     setInputValue('');
   };

@@ -614,14 +613,17 @@ export default function useAutocomplete(props) {
   const handleFocus = event => {
     setFocused(true);

-    if (!disableOpenOnFocus) {
+    if (!disableOpenOnFocus && !ignoreFocus.current) {
       handleOpen(event);
     }
   };

   const handleBlur = event => {
     setFocused(false);
     firstFocus.current = true;
+    ignoreFocus.current = false;

     if (debug && inputValue !== '') {
       return;
@@ -706,8 +708,6 @@ export default function useAutocomplete(props) {
   });

   const handlePopupIndicator = event => {
-    inputRef.current.focus();
-
     if (open) {
       handleClose(event);
     } else {
@@ -715,13 +715,14 @@ export default function useAutocomplete(props) {
     }
   };

+  // Prevent input blur when interacting with the combobox
   const handleMouseDown = event => {
     if (event.target.nodeName !== 'INPUT') {
-      // Prevent blur
       event.preventDefault();
     }
   };

+  // Focus the input when first interacting with the combobox
   const handleClick = () => {
     if (
       firstFocus.current &&
```

Do you want to submit a pull request? It would also be great to add a new test case :).
> What do you think of the following diff?

Look interesting - if I'm reading it correctly it appears to be changing the default behaviour as you suggested. I'll try this out then have a go at doing the PR myself - I don't have a lot of experience writing test cases for TS/JS but it never hurts to learn.

Solution patch:
diff --git a/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.js b/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.js
--- a/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.js
+++ b/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.js
@@ -108,6 +108,7 @@ export default function useAutocomplete(props) {
     setDefaultId(`mui-autocomplete-${Math.round(Math.random() * 1e5)}`);
   }, []);
 
+  const ignoreFocus = React.useRef(false);
   const firstFocus = React.useRef(true);
   const inputRef = React.useRef(null);
   const listboxRef = React.useRef(null);
@@ -509,10 +510,8 @@ export default function useAutocomplete(props) {
   };
 
   const handleClear = event => {
+    ignoreFocus.current = true;
     handleValue(event, multiple ? [] : null);
-    if (disableOpenOnFocus) {
-      handleClose();
-    }
     setInputValue('');
   };
 
@@ -614,7 +613,7 @@ export default function useAutocomplete(props) {
   const handleFocus = event => {
     setFocused(true);
 
-    if (!disableOpenOnFocus) {
+    if (!disableOpenOnFocus && !ignoreFocus.current) {
       handleOpen(event);
     }
   };
@@ -622,6 +621,7 @@ export default function useAutocomplete(props) {
   const handleBlur = event => {
     setFocused(false);
     firstFocus.current = true;
+    ignoreFocus.current = false;
 
     if (debug && inputValue !== '') {
       return;
@@ -706,8 +706,6 @@ export default function useAutocomplete(props) {
   });
 
   const handlePopupIndicator = event => {
-    inputRef.current.focus();
-
     if (open) {
       handleClose(event);
     } else {
@@ -715,13 +713,14 @@ export default function useAutocomplete(props) {
     }
   };
 
+  // Prevent input blur when interacting with the combobox
   const handleMouseDown = event => {
     if (event.target.nodeName !== 'INPUT') {
-      // Prevent blur
       event.preventDefault();
     }
   };
 
+  // Focus the input when first interacting with the combobox
   const handleClick = () => {
     if (
       firstFocus.current &&


Test patch:
diff --git a/packages/material-ui-lab/src/Autocomplete/Autocomplete.test.js b/packages/material-ui-lab/src/Autocomplete/Autocomplete.test.js
--- a/packages/material-ui-lab/src/Autocomplete/Autocomplete.test.js
+++ b/packages/material-ui-lab/src/Autocomplete/Autocomplete.test.js
@@ -205,6 +205,27 @@ describe('<Autocomplete />', () => {
       expect(handleOpen.callCount).to.equal(1);
     });
 
+    it('does not open on clear', () => {
+      const handleOpen = spy();
+      const handleChange = spy();
+      const { container } = render(
+        <Autocomplete
+          onOpen={handleOpen}
+          onChange={handleChange}
+          open={false}
+          options={['one', 'two']}
+          value="one"
+          renderInput={params => <TextField {...params} />}
+        />,
+      );
+
+      const clear = container.querySelector('button');
+      fireEvent.click(clear);
+
+      expect(handleOpen.callCount).to.equal(0);
+      expect(handleChange.callCount).to.equal(1);
+    });
+
     ['ArrowDown', 'ArrowUp'].forEach(key => {
       it(`opens on ${key} when focus is on the textbox without moving focus`, () => {
         const handleOpen = spy();