Repository: mui/material-ui (TypeScript)
Date: 2019-11-21 11:18:10+00:00
Task type: Bug Fix

Issue:
[Autocomplete] Search includes object key values as match
## Current Behavior 😯

Developer supplies an array of objects, that has the shape of:
```
[
  { 
    code: 'AD',
    label: 'Andorra',
    phone: '376',
   },
// etc
]
```
When the user types 'code' or 'label' or 'phone', all values are still present and none of the objects are filtered, because the key of the object is included in the search term.

## Expected Behavior 🤔
Given the same array of objects, I would expect that typing 'a' would filter all objects whose values do not contain 'a'. Right now all values are still shown because the letter 'a' is present in all objects (because of the 'label' property)

## Steps to Reproduce 🕹

Steps:

1. Visit https://material-ui.com/components/autocomplete/#country-select
2. In the 'Choose a country' input field, type in 'label', 'code' or 'phone'. 
3. You will still see _all_ values, none of them are filtered.

## Context 🔦

We need the object properties in our code to make know what data we're dealing with, but when the user searches information - they don't care about the properties, it should be limited to the actual values.

I hope that because this component is still in the lab, we can change this without having to add an extra prop. Otherwise I would recommend having an extra prop along the lines of 'onlyFilterObjectProperties' or whatever. I'm sure you can think of a better value.

## Your Environment 🌎
Live environment on https://material-ui.com/components/autocomplete/#country-select


Issue discussion:
@FlorisWarmenhoven Thanks for the bug report. The best option is to provide your own filter and stringify method. https://material-ui.com/components/autocomplete/#custom-filter

However, I do think that we can improve the current tradeoff. I would imagine something like this:

```diff
diff --git a/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.js b/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.js
index 2bdc11a27..f833a0c0c 100644
--- a/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.js
+++ b/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.js
@@ -11,12 +11,30 @@ function stripDiacritics(string) {
     : string;
 }

+function defaultStringify(value) {
+  if (value == null) {
+    return '';
+  }
+
+  if (typeof value === 'string') {
+    return value;
+  }
+
+  if (typeof value === 'object') {
+    return Object.keys(value)
+      .map(key => value[key])
+      .join(' ');
+  }
+
+  return JSON.stringify(value);
+}
+
 export function createFilterOptions(config = {}) {
   const {
     ignoreAccents = true,
     ignoreCase = true,
     matchFrom = 'any',
-    stringify = JSON.stringify,
+    stringify = defaultStringify,
     trim = false,
   } = config;
```

I like your proposed solution. If I understand it correctly, if an array of objects is passed in - you will stringify the keys only - instead of stringifying the entire object.

Looking forward to this change being live.
@FlorisWarmenhoven Yes, this can already be implemented userland with a custom filter.

I have added the `good first issue` flag in case an "external" contributor wants to work on it. It would be a nice polish.
@oliviertassinari  i would love to do this , can I take this up ?
@mandrin17299 I'm not in charge here - but it seems to me like they flagged it as "Good first issue" so that people like yourself can pick it up. So I'd say: go for it! 

Edit: sorry didn't mean to close!


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
@@ -11,12 +11,30 @@ function stripDiacritics(string) {
     : string;
 }
 
+function defaultStringify(value) {
+  if (value == null) {
+    return '';
+  }
+
+  if (typeof value === 'string') {
+    return value;
+  }
+
+  if (typeof value === 'object') {
+    return Object.keys(value)
+      .map(key => value[key])
+      .join(' ');
+  }
+
+  return JSON.stringify(value);
+}
+
 export function createFilterOptions(config = {}) {
   const {
     ignoreAccents = true,
     ignoreCase = true,
     matchFrom = 'any',
-    stringify = JSON.stringify,
+    stringify = defaultStringify,
     trim = false,
   } = config;
 


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
@@ -658,4 +658,38 @@ describe('<Autocomplete />', () => {
       expect(textbox.value).to.equal('');
     });
   });
+
+  describe('prop: filterOptions', () => {
+    it('should ignore object keys by default', () => {
+      const { queryAllByRole, getByRole } = render(
+        <Autocomplete
+          options={[
+            {
+              value: 'one',
+              label: 'One',
+            },
+            {
+              value: 'two',
+              label: 'Two',
+            },
+          ]}
+          getOptionLabel={option => option.name}
+          renderInput={params => <TextField autoFocus {...params} />}
+        />,
+      );
+      let options;
+      const textbox = getByRole('textbox');
+
+      options = queryAllByRole('option');
+      expect(options.length).to.equal(2);
+
+      fireEvent.change(textbox, { target: { value: 'value' } });
+      options = queryAllByRole('option');
+      expect(options.length).to.equal(0);
+
+      fireEvent.change(textbox, { target: { value: 'one' } });
+      options = queryAllByRole('option');
+      expect(options.length).to.equal(1);
+    });
+  });
 });