Skip to content
This repository was archived by the owner on Jul 19, 2019. It is now read-only.

Commit 6c7a7cf

Browse files
author
Stephen Rivas Jr
committed
[release] 1.0.0-rc1 vets moving to a prop-driven value over internal state.
1 parent 16cf53e commit 6c7a7cf

9 files changed

+118
-91
lines changed

CHANGELOG.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,16 @@
1-
v0.2.1 - Wed, 12 Aug 2015 20:22:13 GMT
1+
v1.0.0-rc1 - 31 Mar 2016
2+
--------------------------------------
3+
4+
- Updated this component to no longer use internal state
5+
for retaining value, this is prop driven now.
6+
- The version number `1.0.0-rc1` is an unforunate coincidence,
7+
We're only bumping this as it introduces breaking changes.
8+
- Other outstanding PRs may be merged in to the the final
9+
release so we can break several things at once (now that we
10+
have tests of course)
11+
12+
13+
v0.2.1 - 10 Mar 2016
214
--------------------------------------
315

416
- Added unit tests (thanks @ryanalane)

build/lib/Autocomplete.js

Lines changed: 31 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ var Autocomplete = React.createClass({
1212
displayName: 'Autocomplete',
1313

1414
propTypes: {
15-
initialValue: React.PropTypes.any,
15+
value: React.PropTypes.any,
1616
onChange: React.PropTypes.func,
1717
onSelect: React.PropTypes.func,
1818
shouldItemRender: React.PropTypes.func,
@@ -24,6 +24,7 @@ var Autocomplete = React.createClass({
2424

2525
getDefaultProps: function getDefaultProps() {
2626
return {
27+
value: '',
2728
inputProps: {},
2829
labelText: '',
2930
onChange: function onChange() {},
@@ -49,7 +50,6 @@ var Autocomplete = React.createClass({
4950
// TODO: don't cheat, let it flow to the bottom
5051
getInitialState: function getInitialState() {
5152
return {
52-
value: this.props.initialValue || '',
5353
isOpen: false,
5454
highlightedIndex: null
5555
};
@@ -95,14 +95,8 @@ var Autocomplete = React.createClass({
9595
},
9696

9797
handleChange: function handleChange(event) {
98-
var _this = this;
99-
10098
this._performAutoCompleteOnKeyUp = true;
101-
this.setState({
102-
value: event.target.value
103-
}, function () {
104-
_this.props.onChange(event, _this.state.value);
105-
});
99+
this.props.onChange(event, event.target.value);
106100
},
107101

108102
handleKeyUp: function handleKeyUp() {
@@ -138,7 +132,7 @@ var Autocomplete = React.createClass({
138132
},
139133

140134
Enter: function Enter(event) {
141-
var _this2 = this;
135+
var _this = this;
142136

143137
if (this.state.isOpen === false) {
144138
// menu is closed so there is no selection to accept -> do nothing
@@ -148,19 +142,19 @@ var Autocomplete = React.createClass({
148142
this.setState({
149143
isOpen: false
150144
}, function () {
151-
_this2.refs.input.select();
145+
_this.refs.input.select();
152146
});
153147
} else {
154148
// text entered + menu item has been highlighted + enter is hit -> update value to that of selected menu item, close the menu
155149
var item = this.getFilteredItems()[this.state.highlightedIndex];
150+
var value = this.props.getItemValue(item);
156151
this.setState({
157-
value: this.props.getItemValue(item),
158152
isOpen: false,
159153
highlightedIndex: null
160154
}, function () {
161155
//this.refs.input.focus() // TODO: file issue
162-
_this2.refs.input.setSelectionRange(_this2.state.value.length, _this2.state.value.length);
163-
_this2.props.onSelect(_this2.state.value, item);
156+
_this.refs.input.setSelectionRange(value.length, value.length);
157+
_this.props.onSelect(value, item);
164158
});
165159
}
166160
},
@@ -174,41 +168,41 @@ var Autocomplete = React.createClass({
174168
},
175169

176170
getFilteredItems: function getFilteredItems() {
177-
var _this3 = this;
171+
var _this2 = this;
178172

179173
var items = this.props.items;
180174

181175
if (this.props.shouldItemRender) {
182176
items = items.filter(function (item) {
183-
return _this3.props.shouldItemRender(item, _this3.state.value);
177+
return _this2.props.shouldItemRender(item, _this2.props.value);
184178
});
185179
}
186180

187181
if (this.props.sortItems) {
188182
items.sort(function (a, b) {
189-
return _this3.props.sortItems(a, b, _this3.state.value);
183+
return _this2.props.sortItems(a, b, _this2.props.value);
190184
});
191185
}
192186

193187
return items;
194188
},
195189

196190
maybeAutoCompleteText: function maybeAutoCompleteText() {
197-
var _this4 = this;
191+
var _this3 = this;
198192

199-
if (this.state.value === '') return;
193+
if (this.props.value === '') return;
200194
var highlightedIndex = this.state.highlightedIndex;
201195

202196
var items = this.getFilteredItems();
203197
if (items.length === 0) return;
204198
var matchedItem = highlightedIndex !== null ? items[highlightedIndex] : items[0];
205199
var itemValue = this.props.getItemValue(matchedItem);
206-
var itemValueDoesMatch = itemValue.toLowerCase().indexOf(this.state.value.toLowerCase()) === 0;
200+
var itemValueDoesMatch = itemValue.toLowerCase().indexOf(this.props.value.toLowerCase()) === 0;
207201
if (itemValueDoesMatch) {
208202
var node = this.refs.input;
209203
var setSelection = function setSelection() {
210204
node.value = itemValue;
211-
node.setSelectionRange(_this4.state.value.length, itemValue.length);
205+
node.setSelectionRange(_this3.props.value.length, itemValue.length);
212206
};
213207
if (highlightedIndex === null) this.setState({ highlightedIndex: 0 }, setSelection);else setSelection();
214208
}
@@ -233,16 +227,16 @@ var Autocomplete = React.createClass({
233227
},
234228

235229
selectItemFromMouse: function selectItemFromMouse(item) {
236-
var _this5 = this;
230+
var _this4 = this;
237231

232+
var value = this.props.getItemValue(item);
238233
this.setState({
239-
value: this.props.getItemValue(item),
240234
isOpen: false,
241235
highlightedIndex: null
242236
}, function () {
243-
_this5.props.onSelect(_this5.state.value, item);
244-
_this5.refs.input.focus();
245-
_this5.setIgnoreBlur(false);
237+
_this4.props.onSelect(value, item);
238+
_this4.refs.input.focus();
239+
_this4.setIgnoreBlur(false);
246240
});
247241
},
248242

@@ -251,19 +245,19 @@ var Autocomplete = React.createClass({
251245
},
252246

253247
renderMenu: function renderMenu() {
254-
var _this6 = this;
248+
var _this5 = this;
255249

256250
var items = this.getFilteredItems().map(function (item, index) {
257-
var element = _this6.props.renderItem(item, _this6.state.highlightedIndex === index, { cursor: 'default' });
251+
var element = _this5.props.renderItem(item, _this5.state.highlightedIndex === index, { cursor: 'default' });
258252
return React.cloneElement(element, {
259253
onMouseDown: function onMouseDown() {
260-
return _this6.setIgnoreBlur(true);
254+
return _this5.setIgnoreBlur(true);
261255
},
262256
onMouseEnter: function onMouseEnter() {
263-
return _this6.highlightItemFromMouse(index);
257+
return _this5.highlightItemFromMouse(index);
264258
},
265259
onClick: function onClick() {
266-
return _this6.selectItemFromMouse(item);
260+
return _this5.selectItemFromMouse(item);
267261
},
268262
ref: 'item-' + index
269263
});
@@ -273,7 +267,7 @@ var Autocomplete = React.createClass({
273267
top: this.state.menuTop,
274268
minWidth: this.state.menuWidth
275269
};
276-
var menu = this.props.renderMenu(items, this.state.value, style);
270+
var menu = this.props.renderMenu(items, this.props.value, style);
277271
return React.cloneElement(menu, { ref: 'menu' });
278272
},
279273

@@ -295,7 +289,7 @@ var Autocomplete = React.createClass({
295289
},
296290

297291
render: function render() {
298-
var _this7 = this;
292+
var _this6 = this;
299293

300294
if (this.props.debug) {
301295
// you don't like it, you love it
@@ -319,16 +313,16 @@ var Autocomplete = React.createClass({
319313
onFocus: this.handleInputFocus,
320314
onBlur: this.handleInputBlur,
321315
onChange: function (event) {
322-
return _this7.handleChange(event);
316+
return _this6.handleChange(event);
323317
},
324318
onKeyDown: function (event) {
325-
return _this7.handleKeyDown(event);
319+
return _this6.handleKeyDown(event);
326320
},
327321
onKeyUp: function (event) {
328-
return _this7.handleKeyUp(event);
322+
return _this6.handleKeyUp(event);
329323
},
330324
onClick: this.handleInputClick,
331-
value: this.state.value,
325+
value: this.props.value,
332326
id: this.id
333327
})),
334328
this.state.isOpen && this.renderMenu(),

build/lib/__tests__/Autocomplete-test.js

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ _chai2['default'].use((0, _chaiEnzyme2['default'])());
4444

4545
function AutocompleteComponentJSX(extraProps) {
4646
return _react2['default'].createElement(_Autocomplete2['default'], _extends({
47-
initialValue: '',
4847
labelText: 'Choose a state from the US',
4948
inputProps: { name: "US state" },
5049
getItemValue: function (item) {
@@ -82,11 +81,11 @@ describe('Autocomplete acceptance tests', function () {
8281
expect(autocompleteWrapper.instance().refs.menu).to.exist;
8382
});
8483

85-
it('should show results when partial match is typed in', function () {
84+
it('should show results when value is a partial match', function () {
8685

8786
// Render autocomplete results upon partial input match
8887
expect(autocompleteWrapper.ref('menu').children()).to.have.length(50);
89-
autocompleteInputWrapper.simulate('change', { target: { value: 'Ar' } });
88+
autocompleteWrapper.setProps({ value: 'Ar' });
9089
expect(autocompleteWrapper.ref('menu').children()).to.have.length(6);
9190
});
9291

@@ -101,6 +100,28 @@ describe('Autocomplete acceptance tests', function () {
101100

102101
// Event handler unit tests
103102

103+
describe('Autocomplete keyPress-><character> event handlers', function () {
104+
105+
var autocompleteWrapper = (0, _enzyme.mount)(AutocompleteComponentJSX({}));
106+
var autocompleteInputWrapper = autocompleteWrapper.find('input');
107+
108+
it('should pass updated `input.value` to `onChange` and replace with `props.value`', function (done) {
109+
110+
var value = '';
111+
autocompleteWrapper.setProps({ value: value, onChange: function onChange(_, v) {
112+
value = v;
113+
} });
114+
115+
autocompleteInputWrapper.get(0).value = 'a';
116+
autocompleteInputWrapper.simulate('keyPress', { key: 'a', keyCode: 97, which: 97 });
117+
autocompleteInputWrapper.simulate('change');
118+
119+
expect(autocompleteInputWrapper.get(0).value).to.equal('');
120+
expect(value).to.equal('a');
121+
done();
122+
});
123+
});
124+
104125
describe('Autocomplete kewDown->ArrowDown event handlers', function () {
105126

106127
var autocompleteWrapper = (0, _enzyme.mount)(AutocompleteComponentJSX({}));
@@ -201,31 +222,37 @@ describe('Autocomplete kewDown->Enter event handlers', function () {
201222
});
202223

203224
it('should close menu if input has focus but no item has been selected and then the Enter key is hit', function () {
225+
var value = '';
204226
autocompleteWrapper.setState({ 'isOpen': true });
205227
autocompleteInputWrapper.simulate('focus');
206-
autocompleteInputWrapper.simulate('change', { target: { value: '' } });
228+
autocompleteWrapper.setProps({ value: value, onSelect: function onSelect(v) {
229+
value = v;
230+
} });
207231

208232
// simulate keyUp of backspace, triggering autocomplete suggestion on an empty string, which should result in nothing highlighted
209233
autocompleteInputWrapper.simulate('keyUp', { key: 'Backspace', keyCode: 8, which: 8 });
210234
expect(autocompleteWrapper.state('highlightedIndex')).to.be['null'];
211235

212236
autocompleteInputWrapper.simulate('keyDown', { key: 'Enter', keyCode: 13, which: 13 });
213237

214-
expect(autocompleteWrapper.state('value')).to.equal('');
238+
expect(value).to.equal('');
215239
expect(autocompleteWrapper.state('isOpen')).to.be['false'];
216240
});
217241

218-
it('should update input value from selected menu item and close the menu', function () {
242+
it('should invoke `onSelect` with the selected menu item and close the menu', function () {
243+
var value = 'Ar';
219244
autocompleteWrapper.setState({ 'isOpen': true });
220245
autocompleteInputWrapper.simulate('focus');
221-
autocompleteInputWrapper.simulate('change', { target: { value: 'Ar' } });
246+
autocompleteWrapper.setProps({ value: value, onSelect: function onSelect(v) {
247+
value = v;
248+
} });
222249

223250
// simulate keyUp of last key, triggering autocomplete suggestion + selection of the suggestion in the menu
224251
autocompleteInputWrapper.simulate('keyUp', { key: 'r', keyCode: 82, which: 82 });
225252

226253
// Hit enter, updating state.value with the selected Autocomplete suggestion
227254
autocompleteInputWrapper.simulate('keyDown', { key: 'Enter', keyCode: 13, which: 13 });
228-
expect(autocompleteWrapper.state('value')).to.equal('Arizona');
255+
expect(value).to.equal('Arizona');
229256
expect(autocompleteWrapper.state('isOpen')).to.be['false'];
230257
});
231258
});
@@ -262,7 +289,7 @@ describe('Autocomplete#renderMenu', function () {
262289

263290
it('should return a menu ReactComponent with a subset of children when partial match text has been entered', function () {
264291
// Input 'Ar' should result in 6 items in the menu, populated from autocomplete.
265-
autocompleteInputWrapper.simulate('change', { target: { value: 'Ar' } });
292+
autocompleteWrapper.setProps({ value: 'Ar' });
266293

267294
var autocompleteMenu = autocompleteWrapper.instance().renderMenu();
268295
expect(autocompleteMenu.props.children.length).to.be.equal(6);

build/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "react-autocomplete",
3-
"version": "0.2.1",
3+
"version": "1.0.0-rc1",
44
"description": "Accessible, extensible, Autocomplete for React.js",
55
"main": "./build/lib/index.js",
66
"repository": {

0 commit comments

Comments
 (0)