From e4aa40211840bb99c7186a10adc457741d35a463 Mon Sep 17 00:00:00 2001 From: Emiliano Santi Date: Tue, 22 Mar 2016 20:54:32 -0300 Subject: [PATCH 1/4] [Added] Trigger event with query when Enter key is pressed --- examples/async-data/app.js | 75 ++++++++++++++++++------------ lib/Autocomplete.js | 6 ++- lib/__tests__/Autocomplete-test.js | 19 +++++++- package.json | 3 +- 4 files changed, 70 insertions(+), 33 deletions(-) diff --git a/examples/async-data/app.js b/examples/async-data/app.js index c1ff0b17..7e34805f 100644 --- a/examples/async-data/app.js +++ b/examples/async-data/app.js @@ -7,7 +7,8 @@ let App = React.createClass({ getInitialState () { return { unitedStates: getStates(), - loading: false + loading: false, + valueEntered: '' } }, @@ -15,42 +16,58 @@ let App = React.createClass({ return (

Async Data

-

Autocomplete works great with async data by allowing you to pass in items. The onChange event provides you the value to make a server request with, then change state and pass in new items, it will attempt to autocomplete the first one.

- - item.name} - onSelect={(value, item) => { - // set the menu to only the selected item - this.setState({ unitedStates: [ item ] }) - // or you could reset it to a default list again - // this.setState({ unitedStates: getStates() }) - }} - onChange={(event, value) => { - this.setState({loading: true}) - fakeRequest(value, (items) => { - this.setState({ unitedStates: items, loading: false }) - }) - }} - renderItem={(item, isHighlighted) => ( -
{item.name}
- )} - /> + + {this.renderMessage()}
) + }, + + renderMessage () { + let dataToRender = null; + + if (this.state.valueEntered) { + dataToRender =

Your search {this.state.valueEntered} is not in the list of states. Making service call...

; + } + + return dataToRender; + }, + + getAutocompleteProps () { + return { + labelText: "Choose a state from the US", + inputProps: { name: "US state" }, + ref: "autocomplete", + items: this.state.unitedStates, + getItemValue: (item) => item.name, + onSelect: (value, item) => { + // set the menu to only the selected item + this.setState({ unitedStates: [ item ] }) + // or you could reset it to a default list again + // this.setState({ unitedStates: getStates() }) + }, + onChange: (event, value) => { + this.setState({loading: true}) + fakeRequest(value, (items) => { + this.setState({unitedStates: items, loading: false}) + }) + }, + onSearch: (value) => { + this.setState({valueEntered: value}) + }, + renderItem: (item, isHighlighted) => ( +
{item.name}
+ ) + }; } }) diff --git a/lib/Autocomplete.js b/lib/Autocomplete.js index fe479517..1fe6e4b7 100644 --- a/lib/Autocomplete.js +++ b/lib/Autocomplete.js @@ -10,6 +10,7 @@ let Autocomplete = React.createClass({ initialValue: React.PropTypes.any, onChange: React.PropTypes.func, onSelect: React.PropTypes.func, + onSearch: React.PropTypes.func, shouldItemRender: React.PropTypes.func, renderItem: React.PropTypes.func.isRequired, menuStyle: React.PropTypes.object, @@ -145,7 +146,10 @@ let Autocomplete = React.createClass({ this.setState({ isOpen: false }, () => { - this.refs.input.select() + if (this.props.onSearch) + this.props.onSearch(this.state.value); + else + this.refs.input.select(); }) } else { diff --git a/lib/__tests__/Autocomplete-test.js b/lib/__tests__/Autocomplete-test.js index 0ed62408..92c6938e 100644 --- a/lib/__tests__/Autocomplete-test.js +++ b/lib/__tests__/Autocomplete-test.js @@ -3,6 +3,7 @@ import ReactDOM from 'react-dom' import TestUtils from 'react-addons-test-utils' import jsdom from 'mocha-jsdom'; import chai from 'chai'; +import sinon from 'sinon'; const expect = chai.expect; import chaiEnzyme from 'chai-enzyme' import { ok, equal } from 'assert'; @@ -12,6 +13,8 @@ import { getStates, matchStateToTerm, sortStates, styles } from '../utils' chai.use(chaiEnzyme()); +let onSearchStub = sinon.stub(); + function AutocompleteComponentJSX (extraProps) { return ( ArrowUp event handlers', () => { }); describe('Autocomplete kewDown->Enter event handlers', () => { - - var autocompleteWrapper = mount(AutocompleteComponentJSX({})); + var autocompleteWrapper = mount(AutocompleteComponentJSX({ + onSearch: onSearchStub + })); var autocompleteInputWrapper = autocompleteWrapper.find('input'); + it('should do nothing if the menu is closed', () => { autocompleteWrapper.setState({'isOpen': false}); autocompleteWrapper.simulate('keyDown', { key : 'Enter', keyCode: 13, which: 13 }); @@ -188,6 +193,16 @@ describe('Autocomplete kewDown->Enter event handlers', () => { }); + it('should call onSearch prop when no item has been selected and then the Enter key is hit', () => { + autocompleteWrapper.setState({'isOpen': true, highlightedIndex: null}); + autocompleteInputWrapper.simulate('focus'); + autocompleteInputWrapper.simulate('change', { target: { value: 'TEST' } }); + autocompleteInputWrapper.simulate('keyDown', { key : 'Enter', keyCode: 13, which: 13 }); + + expect(onSearchStub.called).to.equal(true); + expect(onSearchStub.calledWith('TEST')).to.equal(true); + }); + it('should update input value from selected menu item and close the menu', () => { autocompleteWrapper.setState({'isOpen': true}); autocompleteInputWrapper.simulate('focus'); diff --git a/package.json b/package.json index e08e8de3..24ca63ca 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,8 @@ "mocha-jsdom": "^1.1.0", "rackt-cli": "^0.4.0", "react": "^0.14.0", - "react-addons-test-utils": "^0.14.7" + "react-addons-test-utils": "^0.14.7", + "sinon": "^1.17.3" }, "tags": [ "react", From eecee25074dede11fba4a9591282b0fee1c24e5f Mon Sep 17 00:00:00 2001 From: Emiliano Santi Date: Tue, 22 Mar 2016 21:07:51 -0300 Subject: [PATCH 2/4] [changed] Fixing console warning - React.render is deprecated. Please use ReactDOM.render --- examples/async-data/app.js | 3 ++- examples/custom-menu/app.js | 3 ++- examples/static-data/app.js | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/examples/async-data/app.js b/examples/async-data/app.js index 7e34805f..d215a03e 100644 --- a/examples/async-data/app.js +++ b/examples/async-data/app.js @@ -1,4 +1,5 @@ import React from 'react' +import ReactDOM from 'react-dom' import Autocomplete from '../../lib/index' import { getStates, matchStateToTerm, sortStates, styles, fakeRequest } from '../../lib/utils' @@ -71,5 +72,5 @@ let App = React.createClass({ } }) -React.render(, document.getElementById('container')) +ReactDOM.render(, document.getElementById('container')) diff --git a/examples/custom-menu/app.js b/examples/custom-menu/app.js index 1ea62c24..1699fc62 100644 --- a/examples/custom-menu/app.js +++ b/examples/custom-menu/app.js @@ -1,4 +1,5 @@ import React from 'react' +import ReactDOM from 'react-dom' import Autocomplete from '../../lib/index' import { getStates, matchStateToTerm, sortStates, styles, fakeRequest } from '../../lib/utils' @@ -75,6 +76,6 @@ let App = React.createClass({ } }) -React.render(, document.getElementById('container')) +ReactDOM.render(, document.getElementById('container')) diff --git a/examples/static-data/app.js b/examples/static-data/app.js index 9def5b08..613d259f 100644 --- a/examples/static-data/app.js +++ b/examples/static-data/app.js @@ -1,4 +1,5 @@ import React from 'react' +import ReactDOM from 'react-dom' import { getStates, matchStateToTerm, sortStates, styles } from '../../lib/utils' import Autocomplete from '../../lib/index' @@ -33,5 +34,5 @@ let App = React.createClass({ } }) -React.render(, document.getElementById('container')) +ReactDOM.render(, document.getElementById('container')) From d0c5e93da17cebf10921559edcf07afe0fdd77e0 Mon Sep 17 00:00:00 2001 From: Emiliano Santi Date: Fri, 25 Mar 2016 01:05:44 -0300 Subject: [PATCH 3/4] Revert "[changed] Fixing console warning - React.render is deprecated. Please use ReactDOM.render" This reverts commit eecee25074dede11fba4a9591282b0fee1c24e5f. --- examples/async-data/app.js | 3 +-- examples/custom-menu/app.js | 3 +-- examples/static-data/app.js | 3 +-- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/examples/async-data/app.js b/examples/async-data/app.js index d215a03e..7e34805f 100644 --- a/examples/async-data/app.js +++ b/examples/async-data/app.js @@ -1,5 +1,4 @@ import React from 'react' -import ReactDOM from 'react-dom' import Autocomplete from '../../lib/index' import { getStates, matchStateToTerm, sortStates, styles, fakeRequest } from '../../lib/utils' @@ -72,5 +71,5 @@ let App = React.createClass({ } }) -ReactDOM.render(, document.getElementById('container')) +React.render(, document.getElementById('container')) diff --git a/examples/custom-menu/app.js b/examples/custom-menu/app.js index 1699fc62..1ea62c24 100644 --- a/examples/custom-menu/app.js +++ b/examples/custom-menu/app.js @@ -1,5 +1,4 @@ import React from 'react' -import ReactDOM from 'react-dom' import Autocomplete from '../../lib/index' import { getStates, matchStateToTerm, sortStates, styles, fakeRequest } from '../../lib/utils' @@ -76,6 +75,6 @@ let App = React.createClass({ } }) -ReactDOM.render(, document.getElementById('container')) +React.render(, document.getElementById('container')) diff --git a/examples/static-data/app.js b/examples/static-data/app.js index 613d259f..9def5b08 100644 --- a/examples/static-data/app.js +++ b/examples/static-data/app.js @@ -1,5 +1,4 @@ import React from 'react' -import ReactDOM from 'react-dom' import { getStates, matchStateToTerm, sortStates, styles } from '../../lib/utils' import Autocomplete from '../../lib/index' @@ -34,5 +33,5 @@ let App = React.createClass({ } }) -ReactDOM.render(, document.getElementById('container')) +React.render(, document.getElementById('container')) From 6469f22d57e952562d9f831566344e7dd5961702 Mon Sep 17 00:00:00 2001 From: Emiliano Santi Date: Fri, 25 Mar 2016 11:14:43 -0300 Subject: [PATCH 4/4] [Added] Trigger event with query when Enter key is pressed - Code Review changes. --- examples/async-data/app.js | 76 ++++++++++++------------------ lib/Autocomplete.js | 12 +++-- lib/__tests__/Autocomplete-test.js | 8 ++-- 3 files changed, 43 insertions(+), 53 deletions(-) diff --git a/examples/async-data/app.js b/examples/async-data/app.js index 7e34805f..2b964e28 100644 --- a/examples/async-data/app.js +++ b/examples/async-data/app.js @@ -7,8 +7,7 @@ let App = React.createClass({ getInitialState () { return { unitedStates: getStates(), - loading: false, - valueEntered: '' + loading: false } }, @@ -22,52 +21,37 @@ let App = React.createClass({ a server request with, then change state and pass in new items, it will attempt to autocomplete the first one.

- - {this.renderMessage()} + item.name} + onSelect={(value, item) => { + // set the menu to only the selected item + this.setState({ unitedStates: [ item ] }) + // or you could reset it to a default list again + // this.setState({ unitedStates: getStates() }) + }} + onChange={(event, value) => { + this.setState({loading: true}) + fakeRequest(value, (items) => { + this.setState({ unitedStates: items, loading: false }) + }) + }} + onSubmit={(value, setValue) => { + setValue(value.split(',').reverse().join()) + }} + renderItem={(item, isHighlighted) => ( +
{item.name}
+ )} + /> ) - }, - - renderMessage () { - let dataToRender = null; - - if (this.state.valueEntered) { - dataToRender =

Your search {this.state.valueEntered} is not in the list of states. Making service call...

; - } - - return dataToRender; - }, - - getAutocompleteProps () { - return { - labelText: "Choose a state from the US", - inputProps: { name: "US state" }, - ref: "autocomplete", - items: this.state.unitedStates, - getItemValue: (item) => item.name, - onSelect: (value, item) => { - // set the menu to only the selected item - this.setState({ unitedStates: [ item ] }) - // or you could reset it to a default list again - // this.setState({ unitedStates: getStates() }) - }, - onChange: (event, value) => { - this.setState({loading: true}) - fakeRequest(value, (items) => { - this.setState({unitedStates: items, loading: false}) - }) - }, - onSearch: (value) => { - this.setState({valueEntered: value}) - }, - renderItem: (item, isHighlighted) => ( -
{item.name}
- ) - }; } }) diff --git a/lib/Autocomplete.js b/lib/Autocomplete.js index 1fe6e4b7..ee83edd4 100644 --- a/lib/Autocomplete.js +++ b/lib/Autocomplete.js @@ -10,7 +10,7 @@ let Autocomplete = React.createClass({ initialValue: React.PropTypes.any, onChange: React.PropTypes.func, onSelect: React.PropTypes.func, - onSearch: React.PropTypes.func, + onSubmit: React.PropTypes.func, shouldItemRender: React.PropTypes.func, renderItem: React.PropTypes.func.isRequired, menuStyle: React.PropTypes.object, @@ -146,8 +146,8 @@ let Autocomplete = React.createClass({ this.setState({ isOpen: false }, () => { - if (this.props.onSearch) - this.props.onSearch(this.state.value); + if (this.props.onSubmit) + this.props.onSubmit(this.state.value, this.setValue); else this.refs.input.select(); }) @@ -178,6 +178,12 @@ let Autocomplete = React.createClass({ } }, + setValue (newValue) { + this.setState({ + value: newValue + }) + }, + getFilteredItems () { let items = this.props.items diff --git a/lib/__tests__/Autocomplete-test.js b/lib/__tests__/Autocomplete-test.js index 92c6938e..e1202e0f 100644 --- a/lib/__tests__/Autocomplete-test.js +++ b/lib/__tests__/Autocomplete-test.js @@ -13,7 +13,7 @@ import { getStates, matchStateToTerm, sortStates, styles } from '../utils' chai.use(chaiEnzyme()); -let onSearchStub = sinon.stub(); +let onSubmitStub = sinon.stub(); function AutocompleteComponentJSX (extraProps) { return ( @@ -166,7 +166,7 @@ describe('Autocomplete kewDown->ArrowUp event handlers', () => { describe('Autocomplete kewDown->Enter event handlers', () => { var autocompleteWrapper = mount(AutocompleteComponentJSX({ - onSearch: onSearchStub + onSubmit: onSubmitStub })); var autocompleteInputWrapper = autocompleteWrapper.find('input'); @@ -199,8 +199,8 @@ describe('Autocomplete kewDown->Enter event handlers', () => { autocompleteInputWrapper.simulate('change', { target: { value: 'TEST' } }); autocompleteInputWrapper.simulate('keyDown', { key : 'Enter', keyCode: 13, which: 13 }); - expect(onSearchStub.called).to.equal(true); - expect(onSearchStub.calledWith('TEST')).to.equal(true); + expect(onSubmitStub.called).to.equal(true); + expect(onSubmitStub.calledWith('TEST', autocompleteWrapper.component.getWrappedComponent().setValue)).to.equal(true); }); it('should update input value from selected menu item and close the menu', () => {