Repository: mlaursen/react-md (ts)

Issue:
[Autocompletes] Allow showing matches on focus with empty value
### Description

This is a suggestion to fit some use cases where we have an autocomplete with only few data.
Having a prop (defaulting to false) to show the matches on focus even with empty data would be helpful in my use-case.

Any though about it? Please feel free to reject to proposition if you think it will not be helpful.

The affected part would be the handle focus function:
```javascript
_handleFocus = (e) => {
   // [...]

   // Adding an extra flag to allow showing data with empty data
   const hasValidValue = !!value || showMatchesWithEmptyData;

   this.setState({
     matchIndex: -1,
     visible: !this.state.manualFocus && hasValidValue && !!this.state.matches.length,
     manualFocus: false,
     focus: true,
   });
 };
```

### Images/Screenshots
N/A

### Link to a gist or code sample where the issue can be reproduced
N/A

### Version
- React - *
- React-MD - ^1.5.0
- Browser - All

Pull request description:
[Autocompletes] Add showUnfilteredData flag to show data on focus without value
Resolves #781

This Pull Request aims to add the `showUnfilteredData` flag to the autocompletes props. It will be used to show the data on focus and change event even if no value is provided to the text box.
Note that the rest of the behavior should stay the same. 

As it is my first Pull Request on the repository please do not hesitate to point out things that I forgot or against the conventions. Please also feel free to reject the Pull Request if you think I am doing it completely wrong.


Also I noticed two things while working on the feature:
- The autocomplete without filter has inconsistent behavior
  - On focus it does not show the data
  - On change with an empty value it shows the data
- There is a trailing space in `docs/src/components/App/Footer/Contribute.jsx`
  - triggers some warning affecting readability

I can fix the second issue in another Pull Request. As for the first one do we want to behavior to be the same as with the `showUnfilteredData` flag? Maybe should we open another issue if we don't plan to do it in this Pull Request.


Patch:
diff --git a/src/js/Autocompletes/Autocomplete.d.ts b/src/js/Autocompletes/Autocomplete.d.ts
index 68990e54d3..b63e0b7418 100644
--- a/src/js/Autocompletes/Autocomplete.d.ts
+++ b/src/js/Autocompletes/Autocomplete.d.ts
@@ -50,6 +50,7 @@ export interface AutocompleteProps extends BaseMenuProps {
   helpText?: string;
   helpOnFocus?: boolean;
   error?: boolean;
+  showUnfilteredData?: boolean;
 }
 
 interface AutocompleteComponent extends React.ComponentClass<AutocompleteProps> {
diff --git a/src/js/Autocompletes/Autocomplete.js b/src/js/Autocompletes/Autocomplete.js
index 97500e29b4..c6e1e62ab2 100644
--- a/src/js/Autocompletes/Autocomplete.js
+++ b/src/js/Autocompletes/Autocomplete.js
@@ -487,6 +487,12 @@ export default class Autocomplete extends PureComponent {
      * @see {@link TextFields#toolbar}
      */
     toolbar: PropTypes.bool,
+
+    /**
+     * Boolean if the list of data should be shown on focus when no filter value has been
+     * provided.
+     */
+    showUnfilteredData: PropTypes.bool,
   };
 
   static defaultProps = {
@@ -502,6 +508,7 @@ export default class Autocomplete extends PureComponent {
     autoComplete: 'off',
     repositionOnScroll: true,
     repositionOnResize: true,
+    showUnfilteredData: false,
     inlineSuggestionPadding: 6,
   };
 
@@ -513,12 +520,13 @@ export default class Autocomplete extends PureComponent {
       data,
       dataLabel,
       filter,
+      showUnfilteredData,
     } = props;
 
     let matches = [];
     if (defaultValue && filter) {
       matches = filter(data, defaultValue, dataLabel);
-    } else if (!filter) {
+    } else if (!filter || showUnfilteredData) {
       matches = data;
     }
 
@@ -582,7 +590,15 @@ export default class Autocomplete extends PureComponent {
   };
 
   _handleChange = (value, event) => {
-    const { onChange, filter, findInlineSuggestion, data, dataLabel, inline } = this.props;
+    const {
+      onChange,
+      filter,
+      findInlineSuggestion,
+      data,
+      dataLabel,
+      inline,
+      showUnfilteredData,
+    } = this.props;
 
     if (onChange) {
       onChange(value, event);
@@ -594,7 +610,9 @@ export default class Autocomplete extends PureComponent {
     }
 
     let { visible } = this.state;
-    let matches = value || !filter ? this.state.matches : [];
+    const hasValidValue = value || showUnfilteredData;
+
+    let matches = hasValidValue || !filter ? data : [];
     if (value && filter) {
       matches = filter(data, value, dataLabel);
     }
@@ -620,9 +638,11 @@ export default class Autocomplete extends PureComponent {
       return;
     }
 
+    const hasValidValue = !!value || this.props.showUnfilteredData;
+
     this.setState({
       matchIndex: -1,
-      visible: !this.state.manualFocus && !!value && !!this.state.matches.length,
+      visible: !this.state.manualFocus && hasValidValue && !!this.state.matches.length,
       manualFocus: false,
       focus: true,
     });
@@ -984,6 +1004,7 @@ export default class Autocomplete extends PureComponent {
       onKeyDown,
       onMouseDown,
       onChange,
+      showUnfilteredData,
       /* eslint-enable no-unused-vars */
       ...props
     } = this.props;


Test patch:
diff --git a/src/js/Autocompletes/__tests__/Autocomplete.js b/src/js/Autocompletes/__tests__/Autocomplete.js
index 13bf0918e4..515e0349fb 100644
--- a/src/js/Autocompletes/__tests__/Autocomplete.js
+++ b/src/js/Autocompletes/__tests__/Autocomplete.js
@@ -160,6 +160,36 @@ describe('Autocomplete', () => {
     expect(autocomplete.state('focus')).toBe(false);
   });
 
+  it('should set the visible state to true on focus with a value', () => {
+    const data = ['Hello'];
+    const autocomplete = mount(<Autocomplete id="test" data={data} defaultValue="he" />);
+    const field = autocomplete.find('input');
+    expect(field.length).toBe(1);
+
+    field.simulate('focus');
+    expect(autocomplete.state('visible')).toBe(true);
+  });
+
+  it('should set the visible state to false on focus without value', () => {
+    const data = ['Hello'];
+    const autocomplete = mount(<Autocomplete id="test" data={data} defaultValue="" />);
+    const field = autocomplete.find('input');
+    expect(field.length).toBe(1);
+
+    field.simulate('focus');
+    expect(autocomplete.state('visible')).toBe(false);
+  });
+
+  it('should set the visible state to true on focus without value if the showUnfilteredData flag is set to true', () => {
+    const data = ['Hello'];
+    const autocomplete = mount(<Autocomplete id="test" data={data} defaultValue="" showUnfilteredData />);
+    const field = autocomplete.find('input');
+    expect(field.length).toBe(1);
+
+    field.simulate('focus');
+    expect(autocomplete.state('visible')).toBe(true);
+  });
+
   it('should call the onKeyDown prop when the onKeyDown event is fired on the text field', () => {
     const onKeyDown = jest.fn();
     const autocomplete = mount(<Autocomplete id="test" data={[]} onKeyDown={onKeyDown} />);