Skip to content

Commit 997c0c2

Browse files
authored
fix(searchBox): update helper query on every keystroke (#1127)
fixes #1015
1 parent 12ebde7 commit 997c0c2

File tree

2 files changed

+73
-65
lines changed

2 files changed

+73
-65
lines changed

src/widgets/search-box/__tests__/search-box-test.js

Lines changed: 52 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ function createHTMLNodeFromString(string) {
1616
return parent.firstChild;
1717
}
1818

19+
const onHistoryChange = () => {};
20+
1921
describe('searchBox()', () => {
2022
beforeEach(function() {this.jsdom = jsdom();});
2123
afterEach(function() {this.jsdom();});
@@ -24,7 +26,6 @@ describe('searchBox()', () => {
2426
let state;
2527
let helper;
2628
let widget;
27-
let onHistoryChange;
2829

2930
beforeEach(() => {
3031
state = {
@@ -38,7 +39,6 @@ describe('searchBox()', () => {
3839
},
3940
...EventEmitter.prototype
4041
};
41-
onHistoryChange = function() {};
4242
});
4343

4444
context('bad usage', () => {
@@ -389,56 +389,39 @@ describe('searchBox()', () => {
389389
container = document.body.appendChild(document.createElement('input'));
390390
});
391391

392-
function simulateInputEvent(query, stateQuery) {
393-
if (query === undefined) {
394-
query = 'test';
395-
}
396-
397-
// Given
398-
if (stateQuery !== undefined) {
399-
helper.state.query = stateQuery;
400-
} else {
401-
helper.state.query = 'tes';
402-
}
403-
404-
// When
405-
widget.init({state, helper, onHistoryChange});
406-
// Then
407-
container.value = query;
408-
let event = new window.Event('input');
409-
container.dispatchEvent(event);
410-
}
411-
412392
context('instant search', () => {
413393
beforeEach(() => {
414394
widget = searchBox({container});
415395
});
416396

417397
it('performs a search on any change', () => {
418-
simulateInputEvent();
398+
simulateInputEvent('test', 'tes', widget, helper, state, container);
419399
expect(helper.search.called).toBe(true);
420400
});
421401

422402
it('sets the query on any change', () => {
423-
simulateInputEvent();
403+
simulateInputEvent('test', 'tes', widget, helper, state, container);
424404
expect(helper.setQuery.calledOnce).toBe(true);
425405
});
426406

427407
it('does nothing when query is the same as state', () => {
428-
simulateInputEvent('test', 'test');
408+
simulateInputEvent('test', 'test', widget, helper, state, container);
429409
expect(helper.setQuery.calledOnce).toBe(false);
430410
expect(helper.search.called).toBe(false);
431411
});
432412
});
433413

434-
context('non-instant search', () => {
414+
context('non-instant search and input event', () => {
435415
beforeEach(() => {
436416
widget = searchBox({container, searchOnEnterKeyPressOnly: true});
417+
simulateInputEvent('test', 'tes', widget, helper, state, container);
437418
});
438419

439-
it('does not performs (will be handle by keyup event)', () => {
440-
simulateInputEvent();
441-
expect(helper.setQuery.calledOnce).toBe(false);
420+
it('updates the query', () => {
421+
expect(helper.setQuery.calledOnce).toBe(true);
422+
});
423+
424+
it('does not search', () => {
442425
expect(helper.search.called).toBe(false);
443426
});
444427
});
@@ -447,7 +430,7 @@ describe('searchBox()', () => {
447430
it('calls the queryHook', () => {
448431
let queryHook = sinon.spy();
449432
widget = searchBox({container, queryHook});
450-
simulateInputEvent('queryhook input');
433+
simulateInputEvent('queryhook input', 'tes', widget, helper, state, container);
451434
expect(queryHook.calledOnce).toBe(true);
452435
expect(queryHook.firstCall.args[0]).toBe('queryhook input');
453436
expect(queryHook.firstCall.args[1]).toBeA(Function);
@@ -456,15 +439,15 @@ describe('searchBox()', () => {
456439
it('does not perform a search by default', () => {
457440
let queryHook = sinon.spy();
458441
widget = searchBox({container, queryHook});
459-
simulateInputEvent();
442+
simulateInputEvent('test', 'tes', widget, helper, state, container);
460443
expect(helper.setQuery.calledOnce).toBe(false);
461444
expect(helper.search.called).toBe(false);
462445
});
463446

464447
it('when calling the provided search function', () => {
465448
let queryHook = sinon.spy((query, search) => search(query));
466449
widget = searchBox({container, queryHook});
467-
simulateInputEvent('oh rly?');
450+
simulateInputEvent('oh rly?', 'tes', widget, helper, state, container);
468451
expect(helper.setQuery.calledOnce).toBe(true);
469452
expect(helper.setQuery.firstCall.args[0]).toBe('oh rly?');
470453
expect(helper.search.called).toBe(true);
@@ -473,7 +456,7 @@ describe('searchBox()', () => {
473456
it('can override the query', () => {
474457
let queryHook = sinon.spy((originalQuery, search) => search('hi mom!'));
475458
widget = searchBox({container, queryHook});
476-
simulateInputEvent('come.on.');
459+
simulateInputEvent('come.on.', 'tes', widget, helper, state, container);
477460
expect(helper.setQuery.firstCall.args[0]).toBe('hi mom!');
478461
});
479462
});
@@ -484,24 +467,13 @@ describe('searchBox()', () => {
484467
container = document.body.appendChild(document.createElement('input'));
485468
});
486469

487-
function simulateKeyUpEvent(args) {
488-
// Given
489-
helper.state.query = 'foo';
490-
// When
491-
widget.init({state, helper, onHistoryChange});
492-
// Then
493-
let event = new window.Event('keyup', args);
494-
Object.defineProperty(event, 'keyCode', {get: () => args.keyCode});
495-
container.dispatchEvent(event);
496-
}
497-
498470
context('instant search', () => {
499471
beforeEach(() => {
500472
widget = searchBox({container});
501473
});
502474

503475
it('do not perform the search on keyup event (should be done by input event)', () => {
504-
simulateKeyUpEvent({});
476+
simulateKeyUpEvent({}, widget, helper, state, container);
505477
expect(helper.search.called).toBe(false);
506478
});
507479
});
@@ -511,18 +483,14 @@ describe('searchBox()', () => {
511483
widget = searchBox({container, searchOnEnterKeyPressOnly: true});
512484
});
513485

514-
it('sets the query on keyup if <ENTER>', () => {
515-
simulateKeyUpEvent({keyCode: 13});
516-
expect(helper.setQuery.calledOnce).toBe(true);
517-
});
518-
519486
it('performs the search on keyup if <ENTER>', () => {
520-
simulateKeyUpEvent({keyCode: 13});
487+
simulateInputEvent('test', 'tes', widget, helper, state, container);
488+
simulateKeyUpEvent({keyCode: 13}, widget, helper, state, container);
521489
expect(helper.search.calledOnce).toBe(true);
522490
});
523491

524492
it('doesn\'t perform the search on keyup if not <ENTER>', () => {
525-
simulateKeyUpEvent({});
493+
simulateKeyUpEvent({}, widget, helper, state, container);
526494
expect(helper.setQuery.called).toBe(false);
527495
expect(helper.search.called).toBe(false);
528496
});
@@ -531,12 +499,9 @@ describe('searchBox()', () => {
531499

532500
it('updates the input on history update', () => {
533501
let cb;
534-
onHistoryChange = function(fn) {
535-
cb = fn;
536-
};
537502
container = document.body.appendChild(document.createElement('input'));
538503
widget = searchBox({container});
539-
widget.init({state, helper, onHistoryChange});
504+
widget.init({state, helper, onHistoryChange: fn => cb = fn});
540505
expect(container.value).toBe('');
541506
container.blur();
542507
cb({query: 'iphone'});
@@ -652,3 +617,34 @@ describe('searchBox()', () => {
652617
});
653618
});
654619
});
620+
621+
function simulateKeyUpEvent(args, widget, helper, state, container) {
622+
// Given
623+
helper.state.query = 'foo';
624+
// When
625+
widget.init({state, helper, onHistoryChange});
626+
// Then
627+
let event = new window.Event('keyup', args);
628+
Object.defineProperty(event, 'keyCode', {get: () => args.keyCode});
629+
container.dispatchEvent(event);
630+
}
631+
632+
function simulateInputEvent(query, stateQuery, widget, helper, state, container) {
633+
if (query === undefined) {
634+
query = 'test';
635+
}
636+
637+
// Given
638+
if (stateQuery !== undefined) {
639+
helper.state.query = stateQuery;
640+
} else {
641+
helper.state.query = 'tes';
642+
}
643+
644+
// When
645+
widget.init({state, helper, onHistoryChange});
646+
// Then
647+
container.value = query;
648+
let event = new window.Event('input');
649+
container.dispatchEvent(event);
650+
}

src/widgets/search-box/search-box.js

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -167,15 +167,20 @@ function searchBox({
167167
init: function({state, helper, onHistoryChange}) {
168168
let isInputTargeted = container.tagName === 'INPUT';
169169
let input = this._input = this.getInput();
170+
let previousQuery;
170171

171172
// Add all the needed attributes and listeners to the input
172173
this.addDefaultAttributesToInput(input, state.query);
173174

174-
// only update query and search on enter
175+
// always set the query every keystrokes when there's no queryHook
176+
if (!queryHook) {
177+
addListener(input, INPUT_EVENT, getInputValueAndCall(setQuery));
178+
}
179+
180+
// search on enter
175181
if (searchOnEnterKeyPressOnly) {
176182
addListener(input, 'keyup', ifKey(KEY_ENTER, getInputValueAndCall(maybeSearch)));
177183
} else {
178-
// always set the query and search on every keystrokes
179184
addListener(input, INPUT_EVENT, getInputValueAndCall(maybeSearch));
180185

181186
// handle IE8 weirdness where BACKSPACE key will not trigger an input change..
@@ -186,21 +191,28 @@ function searchBox({
186191
}
187192

188193
function maybeSearch(query) {
189-
if (query === helper.state.query) {
190-
return;
191-
}
192-
193194
if (queryHook) {
194-
queryHook(query, search);
195+
queryHook(query, setQueryAndSearch);
195196
return;
196197
}
197198

198199
search(query);
199200
}
200201

202+
function setQuery(query) {
203+
if (query !== helper.state.query) {
204+
previousQuery = helper.state.query;
205+
helper.setQuery(query);
206+
}
207+
}
208+
201209
function search(query) {
202-
helper.setQuery(query);
203-
helper.search();
210+
if (previousQuery !== undefined && previousQuery !== query) helper.search();
211+
}
212+
213+
function setQueryAndSearch(query) {
214+
setQuery(query);
215+
search(query);
204216
}
205217

206218
if (isInputTargeted) {

0 commit comments

Comments
 (0)