Repository: mui/material-ui (TypeScript)
Date: 2020-01-13 14:30:51+00:00
Task type: Bug Fix

Issue:
[Autocomplete] freeSolo (tags) does not not allow enter the same value more then once
In autocomplete with multiple values (tags), on the `feeSolo` version,  if you try to insert the same value twice, the first value is deleted.

## Current Behavior 😯
If I add tag and try to add it again it deletes the first one.

## Expected Behavior 🤔
It should allow insert the same value many times.

## Steps to Reproduce 🕹
https://material-ui.com/components/autocomplete/#multiple-values
in the `freeSolo` version:
1. add the tag `aa`
2. try adding the tag `aa` again
3. instead of adding it, it will delete the first `aa` tag

![tags](https://user-images.githubusercontent.com/10796525/71444577-0897fc80-271b-11ea-966c-78fd4dc492b3.gif)



Issue discussion:
@adica What's your use case?
not sure if I need the double tag, but definitely it should not delete the previous one..
Ok, it should probably clear the input and keep the existing values.
@oliviertassinari I'm in doubt if this should happen only with custom values or also when the user clicks in an option which matches its search but already added.
@oliviertassinari yep, this behaviour would be nice to have, I guess. Any updates regarding this issue?
> not sure if I need the double tag, but definitely it should not delete the previous one.

@adica What do you think of this patch? Do you want to submit a pull request :)?

```diff
diff --git a/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.js b/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.js
index de0bfd292..7eabcb117 100644
--- a/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.js
+++ b/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.js
@@ -430,7 +430,7 @@ export default function useAutocomplete(props) {
     setValue(newValue);
   };

-  const selectNewValue = (event, newValue) => {
+  const selectNewValue = (event, newValue, origin = 'option') => {
     if (multiple) {
       const item = newValue;
       newValue = Array.isArray(value) ? [...value] : [];
@@ -439,7 +439,7 @@ export default function useAutocomplete(props) {

       if (itemIndex === -1) {
         newValue.push(item);
-      } else {
+      } else if (origin !== 'freeSolo') {
         newValue.splice(itemIndex, 1);
       }
     }
@@ -597,7 +597,7 @@ export default function useAutocomplete(props) {
             // Allow people to add new values before they submit the form.
             event.preventDefault();
           }
-          selectNewValue(event, inputValue);
+          selectNewValue(event, inputValue, 'freeSolo');
         }
         break;
       case 'Escape':
```

> I'm in doubt if this should happen only with custom values or also when the user clicks in an option which matches its search but already added.

@m4theushw I would rather encourage the `filterSelectedOptions` prop. Alternatively, we could push #19064 further and allow developers to ignore the delete events. What do you think?

> this behaviour would be nice to have, I guess. Any updates regarding this issue?

@gmltA could you be more specific?
@oliviertassinari I meant this one for `freeSolo` case
> Ok, it should probably clear the input and keep the existing values.
@gmltA Cool, so we have found a consensus.
@oliviertassinari pushing #19064 will give more flexibility. 
@m4theushw I agree. Still, shouldn't we implement both?
@oliviertassinari Yes, I'll give some time and see if any author wants to contribute. If not I can help.
@m4theushw Awesome. Thank you for the help on this component :).

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
@@ -430,7 +430,7 @@ export default function useAutocomplete(props) {
     setValue(newValue);
   };
 
-  const selectNewValue = (event, newValue) => {
+  const selectNewValue = (event, newValue, origin = 'option') => {
     if (multiple) {
       const item = newValue;
       newValue = Array.isArray(value) ? [...value] : [];
@@ -439,7 +439,7 @@ export default function useAutocomplete(props) {
 
       if (itemIndex === -1) {
         newValue.push(item);
-      } else {
+      } else if (origin !== 'freeSolo') {
         newValue.splice(itemIndex, 1);
       }
     }
@@ -597,7 +597,7 @@ export default function useAutocomplete(props) {
             // Allow people to add new values before they submit the form.
             event.preventDefault();
           }
-          selectNewValue(event, inputValue);
+          selectNewValue(event, inputValue, 'freeSolo');
         }
         break;
       case 'Escape':


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
@@ -814,6 +814,27 @@ describe('<Autocomplete />', () => {
       fireEvent.keyDown(document.activeElement, { key: 'Enter' });
       expect(handleChange.callCount).to.equal(1);
     });
+
+    it('should not delete exiting tag when try to add it twice', () => {
+      const handleChange = spy();
+      const options = ['one', 'two'];
+      const { container } = render(
+        <Autocomplete
+          defaultValue={options}
+          options={options}
+          onChange={handleChange}
+          freeSolo
+          renderInput={params => <TextField {...params} autoFocus />}
+          multiple
+        />,
+      );
+      fireEvent.change(document.activeElement, { target: { value: 'three' } });
+      fireEvent.keyDown(document.activeElement, { key: 'Enter' });
+      expect(container.querySelectorAll('[class*="MuiChip-root"]')).to.have.length(3);
+      fireEvent.change(document.activeElement, { target: { value: 'three' } });
+      fireEvent.keyDown(document.activeElement, { key: 'Enter' });
+      expect(container.querySelectorAll('[class*="MuiChip-root"]')).to.have.length(3);
+    });
   });
 
   describe('prop: onInputChange', () => {