Skip to content

Commit 8248c9a

Browse files
committed
Feature #368 add cancelOnBackspace for multiselect option
1 parent ba89c90 commit 8248c9a

File tree

5 files changed

+239
-33
lines changed

5 files changed

+239
-33
lines changed

dist/jquery.typeahead.min.js

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/jquery.typeahead.js

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* Licensed under the MIT license
55
*
66
* @author Tom Bertrand
7-
* @version 2.10.0 (2017-9-9)
7+
* @version 2.10.0 (2017-9-15)
88
* @link http://www.runningcoder.org/jquerytypeahead/
99
*/
1010
(function (factory) {
@@ -769,8 +769,7 @@
769769
scope.searchResult();
770770
scope.buildLayout();
771771

772-
if (
773-
scope.result.length ||
772+
if (scope.result.length ||
774773
(scope.searchGroups.length &&
775774
scope.options.emptyTemplate &&
776775
scope.query.length)
@@ -796,6 +795,8 @@
796795
this.searchGroups = [];
797796
this.generateGroups = [];
798797

798+
if (this.focusOnly && !this.options.multiselect) return;
799+
799800
for (var group in this.options.source) {
800801
if (!this.options.source.hasOwnProperty(group)) continue;
801802
if (
@@ -1741,20 +1742,15 @@
17411742
item.addClass("active");
17421743
},
17431744

1744-
searchResult: function (preserveItem) {
1745-
// #54 In case the item is being clicked, preserve it for onSubmit callback
1746-
if (!preserveItem) {
1747-
this.item = null;
1748-
}
1745+
searchResult: function () {
17491746
this.resetLayout();
17501747

17511748
if (
17521749
this.helper.executeCallback.call(this, this.options.callback.onSearch, [
17531750
this.node,
17541751
this.query
17551752
]) === false
1756-
)
1757-
return;
1753+
) return;
17581754

17591755
if (
17601756
this.searchGroups.length && !(
@@ -2419,14 +2415,12 @@
24192415
scope.options.callback.onClickBefore,
24202416
[scope.node, $(this), item, e]
24212417
) === false
2422-
)
2423-
return;
2418+
) return;
2419+
24242420
if (
24252421
(e.originalEvent && e.originalEvent.defaultPrevented) ||
24262422
e.isDefaultPrevented()
2427-
) {
2428-
return;
2429-
}
2423+
) return;
24302424

24312425
var templateValue = scope.getTemplateValue.call(scope, item);
24322426

@@ -2442,11 +2436,11 @@
24422436
}
24432437

24442438
scope.focusOnly = true;
2445-
scope.node.val(scope.query).focus();
24462439

2447-
scope.searchResult(true);
2448-
scope.buildLayout();
2449-
scope.hideLayout();
2440+
scope.node
2441+
.val(scope.query)
2442+
.trigger('input' + scope.namespace)
2443+
.focus();
24502444

24512445
scope.helper.executeCallback.call(
24522446
scope,
@@ -3047,7 +3041,7 @@
30473041
this.getTemplateValue(data[i])
30483042
);
30493043
}
3050-
this.node.trigger("search" + this.namespace, {origin: 'populateMultiselectData'});
3044+
this.node.trigger("search" + this.namespace, { origin: 'populateMultiselectData' });
30513045
},
30523046

30533047
addMultiselectItemLayout: function (templateValue) {
@@ -3125,7 +3119,9 @@
31253119
);
31263120

31273121
this.adjustInputSize();
3128-
this.node.focus();
3122+
3123+
this.focusOnly = true;
3124+
this.node.focus().trigger('input' + this.namespace, { origin: 'cancelMultiselectItem' });
31293125
},
31303126

31313127
adjustInputSize: function () {
@@ -3239,11 +3235,11 @@
32393235
.off("click" + this.namespace + " touchend" + this.namespace)
32403236
.on("click" + this.namespace + " touchend" + this.namespace, function (e) {
32413237
if ($(e.target).closest(scope.container)[0] ||
3238+
$(e.target).closest('.' + scope.options.selector.item)[0] ||
32423239
e.target.className === scope.options.selector.cancelButton ||
32433240
scope.hasDragged
3244-
) {
3245-
return;
3246-
}
3241+
) return;
3242+
32473243
scope.hideLayout();
32483244
});
32493245
}
@@ -3281,6 +3277,10 @@
32813277
this.resultItemCount = 0;
32823278
this.resultHtml = null;
32833279

3280+
if (!this.focusOnly) {
3281+
this.item = null;
3282+
}
3283+
32843284
if (this.options.hint && this.hint.container) {
32853285
this.hint.container.val("");
32863286
if (this.isContentEditable) {
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
const $ = require("jquery");
2+
const Typeahead = require('../../src/jquery.typeahead');
3+
4+
describe('Typeahead onSubmit Callback Tests', () => {
5+
'use strict';
6+
7+
let myTypeahead,
8+
onSubmitCalled,
9+
onSubmitItem;
10+
11+
let enterEvent = $.Event("keydown");
12+
enterEvent.keyCode = 13;
13+
14+
beforeAll(() => {
15+
16+
document.body.innerHTML = `<form>
17+
<div class="typeahead__container">
18+
<div class="typeahead__field">
19+
20+
<span class="typeahead__query">
21+
<input class="js-typeahead"
22+
name="q"
23+
type="search"
24+
autofocus
25+
autocomplete="off">
26+
</span>
27+
<span class="typeahead__button">
28+
<button type="submit">
29+
<span class="typeahead__search-icon"></span>
30+
</button>
31+
</span>
32+
33+
</div>
34+
</div>
35+
</form>`;
36+
37+
myTypeahead = $.typeahead({
38+
input: '.js-typeahead',
39+
minLength: 0,
40+
source: [
41+
{
42+
id: 1,
43+
display: "Test"
44+
},
45+
{
46+
id: 2,
47+
display: "callback"
48+
}
49+
],
50+
callback: {
51+
onSubmit: function (node, form, item, event) {
52+
event.preventDefault();
53+
54+
onSubmitCalled = true;
55+
onSubmitItem = item;
56+
}
57+
}
58+
});
59+
});
60+
61+
it('Should call onSubmit callback with with the selected item', () => {
62+
myTypeahead.node.val('test');
63+
myTypeahead.node.trigger('input');
64+
65+
myTypeahead.resultContainer.find('li:eq(0) a').trigger('click');
66+
67+
68+
myTypeahead.node.trigger(enterEvent);
69+
myTypeahead.node.closest('form').submit();
70+
71+
72+
expect(onSubmitCalled).toBeTruthy();
73+
expect(onSubmitItem).toEqual({
74+
"display": "Test",
75+
"group": "group",
76+
"id": 1,
77+
"matchedKey": "display"
78+
});
79+
80+
});
81+
});
82+
83+
84+
describe('Typeahead onSubmit Callback Tests', () => {
85+
'use strict';
86+
87+
let myTypeahead,
88+
onSubmitCalled,
89+
onSubmitItem;
90+
91+
let enterEvent = $.Event("keydown");
92+
enterEvent.keyCode = 13;
93+
94+
beforeAll(() => {
95+
96+
document.body.innerHTML = `<form>
97+
<div class="typeahead__container">
98+
<div class="typeahead__field">
99+
100+
<span class="typeahead__query">
101+
<input class="js-typeahead"
102+
name="q"
103+
type="search"
104+
autofocus
105+
autocomplete="off">
106+
</span>
107+
<span class="typeahead__button">
108+
<button type="submit">
109+
<span class="typeahead__search-icon"></span>
110+
</button>
111+
</span>
112+
113+
</div>
114+
</div>
115+
</form>`;
116+
117+
myTypeahead = $.typeahead({
118+
input: '.js-typeahead',
119+
minLength: 0,
120+
multiselect: {
121+
data: [
122+
{
123+
id: 1,
124+
display: "Test"
125+
},
126+
{
127+
id: 2,
128+
display: "callback"
129+
}
130+
]
131+
},
132+
source: [
133+
{
134+
id: 1,
135+
display: "Test"
136+
},
137+
{
138+
id: 2,
139+
display: "callback"
140+
}
141+
],
142+
callback: {
143+
onSubmit: function (node, form, item, event) {
144+
event.preventDefault();
145+
146+
onSubmitCalled = true;
147+
onSubmitItem = item;
148+
}
149+
}
150+
});
151+
});
152+
153+
it('Should call onSubmit callback with with the selected item', () => {
154+
155+
myTypeahead.node.closest('form').submit();
156+
157+
expect(onSubmitCalled).toBeTruthy();
158+
expect(onSubmitItem).toEqual([
159+
{"display": "Test", "id": 1},
160+
{"display": "callback", "id": 2}
161+
]);
162+
163+
});
164+
});

test/integration/multiselect.test.js

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ const Typeahead = require('../../src/jquery.typeahead');
44
describe('Typeahead multiselect option Tests', () => {
55

66
let myTypeahead;
7+
let backspaceEvent = $.Event("keydown");
8+
backspaceEvent.keyCode = 8;
79

810
describe('Typeahead multiselect option Tests - basic setup', () => {
911

@@ -96,11 +98,12 @@ describe('Typeahead multiselect option Tests', () => {
9698
limit: 2,
9799
matchOn: ['id', 'key3'],
98100
limitTemplate: function () {
99-
return 'Only 1 item is allowed.'
101+
return 'Only 2 item is allowed.'
100102
},
101103
href: function (item) {
102104
return '/item/' + item.id;
103105
},
106+
cancelOnBackspace: true,
104107
data: [
105108
{
106109
id: 1,
@@ -150,7 +153,7 @@ describe('Typeahead multiselect option Tests', () => {
150153
});
151154
});
152155

153-
it('Should populate Typeahead input with the templateValue when an item is clicked', () => {
156+
it('Should populate Typeahead input with the templateValue when an item is clicked', (done) => {
154157

155158
expect(myTypeahead.result.length).toEqual(2);
156159
myTypeahead.resultContainer.find('li:eq(0) a').trigger('click');
@@ -160,7 +163,7 @@ describe('Typeahead multiselect option Tests', () => {
160163

161164
myTypeahead.node.val('group').trigger('input');
162165
expect(myTypeahead.result).toEqual({});
163-
expect(myTypeahead.resultContainer.find('.typeahead__empty').text()).toBe('Only 1 item is allowed.');
166+
expect(myTypeahead.resultContainer.find('.typeahead__empty').text()).toBe('Only 2 item is allowed.');
164167

165168
myTypeahead.label.container.find('.typeahead__label:first > a').trigger('click');
166169
expect(isClicked).toBeTruthy();
@@ -176,6 +179,17 @@ describe('Typeahead multiselect option Tests', () => {
176179

177180
myTypeahead.node.val('group').trigger('input');
178181
expect(myTypeahead.result.length).toEqual(2);
182+
183+
myTypeahead.node.val('').trigger('input');
184+
myTypeahead.node.on('input' + myTypeahead.namespace, () => {
185+
186+
expect(myTypeahead.items.length).toEqual(0);
187+
expect(myTypeahead.result.length).toEqual(3);
188+
done();
189+
190+
});
191+
192+
myTypeahead.node.trigger(backspaceEvent);
179193
});
180194

181195
});
@@ -195,6 +209,7 @@ describe('Typeahead multiselect option Tests', () => {
195209
generateOnLoad: true,
196210
display: ['id', 'key1', 'key2', 'key3'],
197211
templateValue: '{{key2}}',
212+
cancelOnBackspace: false,
198213
multiselect: {
199214
matchOn: ['id'],
200215
data: function () {
@@ -248,11 +263,16 @@ describe('Typeahead multiselect option Tests', () => {
248263
expect(myTypeahead.result.length).toEqual(2);
249264

250265
myTypeahead.node.on('search' + myTypeahead.namespace, (event, data) => {
251-
if (data && data.origin !== 'populateMultiselectData') return;
266+
if (!data || data.origin !== 'populateMultiselectData') return;
252267

253268
expect(myTypeahead.result.length).toEqual(1);
254-
269+
myTypeahead.node.trigger(backspaceEvent);
270+
expect(myTypeahead.items.length).toEqual(1);
255271
myTypeahead.label.container.find('.typeahead__label:first .typeahead__cancel-button').trigger('click');
272+
});
273+
274+
myTypeahead.node.on('input' + myTypeahead.namespace, (event, data) => {
275+
if (!data || data.origin !== 'cancelMultiselectItem') return;
256276

257277
expect(myTypeahead.items.length).toEqual(0);
258278
expect(isCanceled).toBeTruthy();
@@ -264,8 +284,11 @@ describe('Typeahead multiselect option Tests', () => {
264284
key3: 'group1-data1-key3'
265285
}
266286
);
287+
267288
done();
268289
});
290+
291+
269292
});
270293
});
271294
});

0 commit comments

Comments
 (0)