From 37613de11e2f6639254267e4ea36d21dabcce011 Mon Sep 17 00:00:00 2001 From: Daniel Heath Date: Mon, 10 Oct 2016 10:50:42 +1100 Subject: [PATCH] Allow users to specify noResultsText Currently if there are no search results the message displayed is the same one used when a user hasn't started typing. This change enables users of Async to provide different messaging when a user has not started typing vs when no records matched a search. --- src/Async.js | 33 ++++++++++++++++++++++--- test/Async-test.js | 61 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+), 3 deletions(-) diff --git a/src/Async.js b/src/Async.js index 43b78ce305..a92d8c4466 100644 --- a/src/Async.js +++ b/src/Async.js @@ -8,7 +8,7 @@ const propTypes = { children: React.PropTypes.func.isRequired, // Child function responsible for creating the inner Select component; (props: Object): PropTypes.element ignoreAccents: React.PropTypes.bool, // strip diacritics when filtering; defaults to true ignoreCase: React.PropTypes.bool, // perform case-insensitive filtering; defaults to true - loadingPlaceholder: React.PropTypes.oneOfType([ // replaces the placeholder while options are loading + loadingPlaceholder: React.PropTypes.oneOfType([ // replaces the placeholder while options are loading React.PropTypes.string, React.PropTypes.node ]), @@ -18,11 +18,16 @@ const propTypes = { React.PropTypes.string, React.PropTypes.node ]), + noResultsText: React.PropTypes.oneOfType([ // field noResultsText, displayed when no options come back from the server + React.PropTypes.string, + React.PropTypes.node + ]), searchPromptText: React.PropTypes.oneOfType([ // label to prompt for search input React.PropTypes.string, React.PropTypes.node ]), onInputChange: React.PropTypes.func, // optional for keeping track of what is being typed + value: React.PropTypes.any, // initial field value }; const defaultProps = { @@ -139,12 +144,34 @@ export default class Async extends Component { return this.loadOptions(inputValue); } + inputValue() { + if (this.select) { + return this.select.state.inputValue; + } + return ''; + } + + noResultsText() { + const { loadingPlaceholder, noResultsText, searchPromptText } = this.props; + const { isLoading } = this.state; + + const inputValue = this.inputValue(); + + if (isLoading) { + return loadingPlaceholder; + } + if (inputValue && noResultsText) { + return noResultsText; + } + return searchPromptText; + } + render () { - const { children, loadingPlaceholder, placeholder, searchPromptText } = this.props; + const { children, loadingPlaceholder, placeholder } = this.props; const { isLoading, options } = this.state; const props = { - noResultsText: isLoading ? loadingPlaceholder : searchPromptText, + noResultsText: this.noResultsText(), placeholder: isLoading ? loadingPlaceholder : placeholder, options: isLoading ? [] : options, ref: (ref) => (this.select = ref) diff --git a/test/Async-test.js b/test/Async-test.js index 92e776e4d3..803ef11a1f 100644 --- a/test/Async-test.js +++ b/test/Async-test.js @@ -333,6 +333,67 @@ describe('Async', () => { }); }); + describe('noResultsText', () => { + + beforeEach(() => { + createControl({ + searchPromptText: 'searchPromptText', + loadingPlaceholder: 'loadingPlaceholder', + noResultsText: 'noResultsText', + }); + }); + + describe('before the user inputs text', () => { + it('returns the searchPromptText', () => { + expect(asyncInstance.noResultsText(), 'to equal', 'searchPromptText'); + }); + }); + + describe('while results are loading', () => { + beforeEach((cb) => { + asyncInstance.setState({ + isLoading: true, + }, cb); + }); + it('returns the loading indicator', () => { + asyncInstance.select = { state: { inputValue: 'asdf' } }; + expect(asyncInstance.noResultsText(), 'to equal', 'loadingPlaceholder'); + }); + }); + + describe('after an empty result set loads', () => { + beforeEach((cb) => { + asyncInstance.setState({ + isLoading: false, + }, cb); + }); + + describe('if noResultsText has been provided', () => { + it('returns the noResultsText', () => { + asyncInstance.select = { state: { inputValue: 'asdf' } }; + expect(asyncInstance.noResultsText(), 'to equal', 'noResultsText'); + }); + }); + + describe('if noResultsText is empty', () => { + beforeEach((cb) => { + createControl({ + searchPromptText: 'searchPromptText', + loadingPlaceholder: 'loadingPlaceholder' + }); + asyncInstance.setState({ + isLoading: false, + inputValue: 'asdfkljhadsf' + }, cb); + }); + it('falls back to searchPromptText', () => { + asyncInstance.select = { state: { inputValue: 'asdf' } }; + expect(asyncInstance.noResultsText(), 'to equal', 'searchPromptText'); + }); + }); + }); + }); + describe('children function', () => { it('should allow a custom select type to be rendered', () => { let childProps;