selectic 3.0.19 → 3.0.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.tool-versions +5 -0
- package/README.md +42 -27
- package/dist/selectic.common.js +120 -15
- package/dist/selectic.esm.js +120 -15
- package/doc/changeText.md +10 -8
- package/doc/css.md +1 -1
- package/doc/domProperties.md +7 -7
- package/doc/dynamic.md +11 -11
- package/doc/extendedProperties.md +12 -12
- package/doc/images/example1.png +0 -0
- package/doc/images/example2.png +0 -0
- package/doc/images/selectic_example.png +0 -0
- package/doc/list.md +28 -15
- package/doc/methods.md +2 -2
- package/doc/params.md +56 -56
- package/package.json +1 -1
- package/src/MainInput.tsx +66 -7
- package/src/Store.tsx +42 -12
- package/src/tools.ts +31 -0
- package/test/Store/Store_creation.spec.js +1 -1
- package/test/Store/commit.spec.js +180 -0
- package/test/helper.js +10 -2
- package/types/MainInput.d.ts +7 -1
- package/types/Store.d.ts +1 -0
- package/types/tools.d.ts +5 -0
package/src/MainInput.tsx
CHANGED
|
@@ -32,6 +32,9 @@ export default class MainInput extends Vue<Props> {
|
|
|
32
32
|
|
|
33
33
|
private nbHiddenItems = 0;
|
|
34
34
|
|
|
35
|
+
/* reactivity non needed */
|
|
36
|
+
private domObserver: MutationObserver | null = null;
|
|
37
|
+
|
|
35
38
|
/* }}} */
|
|
36
39
|
/* {{{ computed */
|
|
37
40
|
|
|
@@ -79,19 +82,34 @@ export default class MainInput extends Vue<Props> {
|
|
|
79
82
|
return !isMultiple || !hasOnlyOneValue;
|
|
80
83
|
}
|
|
81
84
|
|
|
82
|
-
get clearedLabel() {
|
|
85
|
+
get clearedLabel(): string {
|
|
83
86
|
const isMultiple = this.store.state.multiple;
|
|
84
87
|
const labelKey = isMultiple ? 'clearSelections' : 'clearSelection';
|
|
85
88
|
|
|
86
89
|
return this.store.data.labels[labelKey];
|
|
87
90
|
}
|
|
88
91
|
|
|
89
|
-
get singleSelectedItem() {
|
|
92
|
+
get singleSelectedItem(): undefined | OptionItem {
|
|
90
93
|
const state = this.store.state;
|
|
91
94
|
const isMultiple = state.multiple;
|
|
92
|
-
const selected = this.selectedOptions;
|
|
93
95
|
|
|
94
|
-
|
|
96
|
+
if (isMultiple) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return this.selectedOptions as OptionItem;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
get singleSelectedItemText(): string {
|
|
104
|
+
const item = this.singleSelectedItem;
|
|
105
|
+
|
|
106
|
+
return item?.text || '';
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
get singleSelectedItemTitle(): string {
|
|
110
|
+
const item = this.singleSelectedItem;
|
|
111
|
+
|
|
112
|
+
return item?.title || item?.text || '';
|
|
95
113
|
}
|
|
96
114
|
|
|
97
115
|
get singleStyle() {
|
|
@@ -221,6 +239,13 @@ export default class MainInput extends Vue<Props> {
|
|
|
221
239
|
* currently shown */
|
|
222
240
|
const el = this.$refs.selectedItems;
|
|
223
241
|
const parentEl = el.parentElement as HTMLDivElement;
|
|
242
|
+
|
|
243
|
+
if (!document.contains(parentEl)) {
|
|
244
|
+
/* The element is currently not in DOM */
|
|
245
|
+
this.createObserver(parentEl);
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
|
|
224
249
|
const parentPadding = parseInt(getComputedStyle(parentEl).getPropertyValue('padding-right'), 10);
|
|
225
250
|
const clearEl = parentEl.querySelector('.selectic-input__clear-icon') as HTMLSpanElement;
|
|
226
251
|
const clearWidth = clearEl ? clearEl.offsetWidth : 0;
|
|
@@ -261,6 +286,36 @@ export default class MainInput extends Vue<Props> {
|
|
|
261
286
|
this.nbHiddenItems = selectedOptions.length - idx;
|
|
262
287
|
}
|
|
263
288
|
|
|
289
|
+
private closeObserver() {
|
|
290
|
+
const observer = this.domObserver;
|
|
291
|
+
if (observer) {
|
|
292
|
+
observer.disconnect();
|
|
293
|
+
}
|
|
294
|
+
this.domObserver = null;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
private createObserver(el: HTMLElement) {
|
|
298
|
+
this.closeObserver();
|
|
299
|
+
const observer = new MutationObserver((mutationsList) => {
|
|
300
|
+
for (const mutation of mutationsList) {
|
|
301
|
+
if (mutation.type === 'childList') {
|
|
302
|
+
for (const elMutated of Array.from(mutation.addedNodes)) {
|
|
303
|
+
/* Check that element has been added to DOM */
|
|
304
|
+
if (elMutated.contains(el)) {
|
|
305
|
+
this.closeObserver();
|
|
306
|
+
this.computeSize();
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
});
|
|
313
|
+
const config = { childList: true, subtree: true };
|
|
314
|
+
|
|
315
|
+
observer.observe(document, config);
|
|
316
|
+
this.domObserver = observer;
|
|
317
|
+
}
|
|
318
|
+
|
|
264
319
|
/* }}} */
|
|
265
320
|
/* {{{ watch */
|
|
266
321
|
|
|
@@ -276,6 +331,10 @@ export default class MainInput extends Vue<Props> {
|
|
|
276
331
|
this.computeSize();
|
|
277
332
|
}
|
|
278
333
|
|
|
334
|
+
public beforeUnmount() {
|
|
335
|
+
this.closeObserver();
|
|
336
|
+
}
|
|
337
|
+
|
|
279
338
|
/* }}} */
|
|
280
339
|
|
|
281
340
|
public render() {
|
|
@@ -298,9 +357,9 @@ export default class MainInput extends Vue<Props> {
|
|
|
298
357
|
<span
|
|
299
358
|
class="selectic-item_text"
|
|
300
359
|
style={this.singleStyle}
|
|
301
|
-
title={this.
|
|
360
|
+
title={this.singleSelectedItemTitle}
|
|
302
361
|
>
|
|
303
|
-
{this.
|
|
362
|
+
{this.singleSelectedItemText}
|
|
304
363
|
</span>
|
|
305
364
|
)}
|
|
306
365
|
{this.displayPlaceholder && (
|
|
@@ -330,7 +389,7 @@ export default class MainInput extends Vue<Props> {
|
|
|
330
389
|
<div
|
|
331
390
|
class="single-value"
|
|
332
391
|
style={item.style}
|
|
333
|
-
title={item.text}
|
|
392
|
+
title={item.title || item.text}
|
|
334
393
|
on={{
|
|
335
394
|
click: () => this.$emit('item:click', item.id),
|
|
336
395
|
}}
|
package/src/Store.tsx
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { reactive, watch, unref, computed, ComputedRef } from 'vue';
|
|
8
|
-
import { convertToRegExp, assignObject, deepClone } from './tools';
|
|
8
|
+
import { convertToRegExp, assignObject, deepClone, compareOptions } from './tools';
|
|
9
9
|
|
|
10
10
|
/* {{{ Types definitions */
|
|
11
11
|
|
|
@@ -346,6 +346,7 @@ interface Messages {
|
|
|
346
346
|
moreSelectedItem: string;
|
|
347
347
|
moreSelectedItems: string;
|
|
348
348
|
unknownPropertyValue: string;
|
|
349
|
+
wrongQueryResult: string;
|
|
349
350
|
}
|
|
350
351
|
|
|
351
352
|
export type PartialMessages = { [K in keyof Messages]?: Messages[K] };
|
|
@@ -376,6 +377,7 @@ let messages: Messages = {
|
|
|
376
377
|
moreSelectedItem: '+1 other',
|
|
377
378
|
moreSelectedItems: '+%d others',
|
|
378
379
|
unknownPropertyValue: 'property "%s" has incorrect values.',
|
|
380
|
+
wrongQueryResult: 'Query did not return all results.',
|
|
379
381
|
};
|
|
380
382
|
|
|
381
383
|
let closePreviousSelectic: undefined | voidCaller;
|
|
@@ -1165,7 +1167,7 @@ export default class SelecticStore {
|
|
|
1165
1167
|
return childOptions;
|
|
1166
1168
|
}
|
|
1167
1169
|
|
|
1168
|
-
private buildAllOptions(keepFetched = false) {
|
|
1170
|
+
private buildAllOptions(keepFetched = false, stopFetch = false) {
|
|
1169
1171
|
const allOptions: OptionValue[] = [];
|
|
1170
1172
|
let listOptions: OptionValue[] = [];
|
|
1171
1173
|
let elementOptions: OptionValue[] = [];
|
|
@@ -1242,13 +1244,26 @@ export default class SelecticStore {
|
|
|
1242
1244
|
}
|
|
1243
1245
|
}
|
|
1244
1246
|
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
+
if (!stopFetch) {
|
|
1248
|
+
this.state.filteredOptions = [];
|
|
1249
|
+
this.state.totalFilteredOptions = Infinity;
|
|
1247
1250
|
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1251
|
+
this.buildFilteredOptions().then(() => {
|
|
1252
|
+
/* XXX: To recompute for strict mode and auto-select */
|
|
1253
|
+
this.assertCorrectValue();
|
|
1254
|
+
});
|
|
1255
|
+
} else {
|
|
1256
|
+
/* Do not fetch again just build filteredOptions */
|
|
1257
|
+
const search = this.state.searchText;
|
|
1258
|
+
if (!search) {
|
|
1259
|
+
this.state.filteredOptions = this.buildGroupItems(allOptions);
|
|
1260
|
+
this.state.totalFilteredOptions = this.state.filteredOptions.length;
|
|
1261
|
+
return;
|
|
1262
|
+
}
|
|
1263
|
+
const options = this.filterOptions(allOptions, search);
|
|
1264
|
+
this.state.filteredOptions = options;
|
|
1265
|
+
this.state.totalFilteredOptions = this.state.filteredOptions.length;
|
|
1266
|
+
}
|
|
1252
1267
|
}
|
|
1253
1268
|
|
|
1254
1269
|
private async buildFilteredOptions() {
|
|
@@ -1407,6 +1422,7 @@ export default class SelecticStore {
|
|
|
1407
1422
|
const requestId = ++this.requestId;
|
|
1408
1423
|
const {total: rTotal, result} = await fetchCallback(search, offset, limit);
|
|
1409
1424
|
let total = rTotal;
|
|
1425
|
+
let errorMessage = '';
|
|
1410
1426
|
|
|
1411
1427
|
/* Assert result is correctly formatted */
|
|
1412
1428
|
if (!Array.isArray(result)) {
|
|
@@ -1426,8 +1442,22 @@ export default class SelecticStore {
|
|
|
1426
1442
|
if (!search) {
|
|
1427
1443
|
/* update cache */
|
|
1428
1444
|
state.totalDynOptions = total;
|
|
1429
|
-
state.dynOptions.splice(offset, limit, ...result);
|
|
1430
|
-
|
|
1445
|
+
const old = state.dynOptions.splice(offset, limit, ...result);
|
|
1446
|
+
if (compareOptions(old, result)) {
|
|
1447
|
+
/* Added options are the same as previous ones.
|
|
1448
|
+
* Stop fetching to avoid infinite loop
|
|
1449
|
+
*/
|
|
1450
|
+
if (!this.hasFetchedAllItems) {
|
|
1451
|
+
/* Display error if all items are not fetch
|
|
1452
|
+
* We can have the case where old value and result
|
|
1453
|
+
* are the same but total is correct when the
|
|
1454
|
+
* total is 0 */
|
|
1455
|
+
errorMessage = labels.wrongQueryResult;
|
|
1456
|
+
}
|
|
1457
|
+
setTimeout(() => this.buildAllOptions(true, true), 0);
|
|
1458
|
+
} else {
|
|
1459
|
+
setTimeout(() => this.buildAllOptions(true), 0);
|
|
1460
|
+
}
|
|
1431
1461
|
}
|
|
1432
1462
|
|
|
1433
1463
|
/* Check request is not obsolete */
|
|
@@ -1456,12 +1486,12 @@ export default class SelecticStore {
|
|
|
1456
1486
|
this.addStaticFilteredOptions(true);
|
|
1457
1487
|
}
|
|
1458
1488
|
|
|
1459
|
-
state.status.errorMessage =
|
|
1489
|
+
state.status.errorMessage = errorMessage;
|
|
1460
1490
|
} catch (e) {
|
|
1461
1491
|
state.status.errorMessage = (e as Error).message;
|
|
1462
1492
|
if (!search) {
|
|
1463
1493
|
state.totalDynOptions = 0;
|
|
1464
|
-
this.buildAllOptions(true);
|
|
1494
|
+
this.buildAllOptions(true, true);
|
|
1465
1495
|
}
|
|
1466
1496
|
}
|
|
1467
1497
|
|
package/src/tools.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { unref } from 'vue';
|
|
2
|
+
import { OptionValue } from './Store';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Clone the object and its inner properties.
|
|
@@ -86,3 +87,33 @@ export function assignObject<T>(obj: Partial<T>, ...sourceObjects: Array<Partial
|
|
|
86
87
|
}
|
|
87
88
|
return result as T;
|
|
88
89
|
}
|
|
90
|
+
|
|
91
|
+
/** Compare 2 list of options.
|
|
92
|
+
* @returns true if there are no difference
|
|
93
|
+
*/
|
|
94
|
+
export function compareOptions(oldOptions: OptionValue[], newOptions: OptionValue[]): boolean {
|
|
95
|
+
if (oldOptions.length !== newOptions.length) {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return oldOptions.every((oldOption, idx) => {
|
|
100
|
+
const newOption = newOptions[idx];
|
|
101
|
+
const keys = Object.keys(oldOption);
|
|
102
|
+
if (keys.length !== Object.keys(newOption).length) {
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
return keys.every((optionKey) => {
|
|
106
|
+
const key = optionKey as keyof OptionValue;
|
|
107
|
+
const oldValue = oldOption[key];
|
|
108
|
+
const newValue = newOption[key];
|
|
109
|
+
|
|
110
|
+
if (key === 'options') {
|
|
111
|
+
return compareOptions(oldValue, newValue);
|
|
112
|
+
}
|
|
113
|
+
if (key === 'data') {
|
|
114
|
+
return JSON.stringify(oldValue) === JSON.stringify(newValue);
|
|
115
|
+
}
|
|
116
|
+
return oldValue === newValue;
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
}
|
|
@@ -1387,7 +1387,7 @@ tape.test('Store creation', (subT) => {
|
|
|
1387
1387
|
command1.fetch();
|
|
1388
1388
|
await _.nextVueTick(store1, spy1.promise);
|
|
1389
1389
|
|
|
1390
|
-
t.is(store1.state.filteredOptions.length, 3);
|
|
1390
|
+
t.is(store1.state.filteredOptions.length, 3, 'should contain the groups');
|
|
1391
1391
|
|
|
1392
1392
|
const command2 = {};
|
|
1393
1393
|
const spy2 = {};
|
|
@@ -748,6 +748,186 @@ tape.test('commit()', (st) => {
|
|
|
748
748
|
t.true(toHaveBeenCalledWith(spy, ['', 100, 200]));
|
|
749
749
|
t.deepEqual(store.state.allOptions.length, 300);
|
|
750
750
|
t.deepEqual(store.state.offsetItem, 180);
|
|
751
|
+
t.is(store.state.status.errorMessage, '');
|
|
752
|
+
|
|
753
|
+
t.end();
|
|
754
|
+
});
|
|
755
|
+
|
|
756
|
+
sTest.test('should fetch only once', async (t) => {
|
|
757
|
+
const command = {};
|
|
758
|
+
let nbTry = 0;
|
|
759
|
+
|
|
760
|
+
const store = new Store({
|
|
761
|
+
fetchCallback: buildFetchCb({ total: 500, command }),
|
|
762
|
+
});
|
|
763
|
+
command.usage = 0;
|
|
764
|
+
store.commit('isOpen', true);
|
|
765
|
+
|
|
766
|
+
nbTry = 0;
|
|
767
|
+
while (nbTry !== command.usage && nbTry < 10) {
|
|
768
|
+
nbTry = command.usage;
|
|
769
|
+
command.fetch();
|
|
770
|
+
await _.nextVueTick(store, command.promise);
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
t.is(command.usage, 1, 'the fetch is done only once to get first page');
|
|
774
|
+
t.is(store.state.status.errorMessage, '');
|
|
775
|
+
|
|
776
|
+
command.usage = 0;
|
|
777
|
+
store.commit('offsetItem', 180);
|
|
778
|
+
nbTry = 0;
|
|
779
|
+
while (nbTry !== command.usage && nbTry < 10) {
|
|
780
|
+
nbTry = command.usage;
|
|
781
|
+
command.fetch();
|
|
782
|
+
await _.nextVueTick(store, command.promise);
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
t.is(command.usage, 1, 'fetch missing options only once');
|
|
786
|
+
t.is(store.state.status.errorMessage, '');
|
|
787
|
+
|
|
788
|
+
t.deepEqual(store.state.allOptions.length, 300);
|
|
789
|
+
|
|
790
|
+
t.end();
|
|
791
|
+
});
|
|
792
|
+
|
|
793
|
+
sTest.test('should fetch several time if needed', async (t) => {
|
|
794
|
+
const command = {};
|
|
795
|
+
let nbTry = 0;
|
|
796
|
+
|
|
797
|
+
const store = new Store({
|
|
798
|
+
fetchCallback: buildFetchCb({ total: 500, command }),
|
|
799
|
+
});
|
|
800
|
+
command.usage = 0;
|
|
801
|
+
store.commit('isOpen', true);
|
|
802
|
+
|
|
803
|
+
command.interceptResult = (result) => {
|
|
804
|
+
switch (command.usage) {
|
|
805
|
+
case 1:
|
|
806
|
+
/* returns only some of the first options */
|
|
807
|
+
result.result = result.result.slice(0, 15);
|
|
808
|
+
break;
|
|
809
|
+
case 2:
|
|
810
|
+
/* returns only some options */
|
|
811
|
+
result.result = result.result.slice(0, 25);
|
|
812
|
+
default:
|
|
813
|
+
/* do not change the result */
|
|
814
|
+
}
|
|
815
|
+
return result;
|
|
816
|
+
};
|
|
817
|
+
|
|
818
|
+
nbTry = 0;
|
|
819
|
+
while (nbTry !== command.usage && nbTry < 10) {
|
|
820
|
+
nbTry = command.usage;
|
|
821
|
+
command.fetch();
|
|
822
|
+
await _.nextVueTick(store, command.promise);
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
t.is(command.usage > 1, true, 'should fetch first options with several fetch');
|
|
826
|
+
t.is(command.usage < 10, true, 'should have stop fetching by itself');
|
|
827
|
+
t.is(store.state.allOptions.length >= 50, true, 'should have retrieve the first options');
|
|
828
|
+
t.is(store.state.status.errorMessage, '');
|
|
829
|
+
|
|
830
|
+
command.usage = 0;
|
|
831
|
+
store.commit('offsetItem', 180);
|
|
832
|
+
nbTry = 0;
|
|
833
|
+
while (nbTry !== command.usage && nbTry < 10) {
|
|
834
|
+
nbTry = command.usage;
|
|
835
|
+
command.fetch();
|
|
836
|
+
await _.nextVueTick(store, command.promise);
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
t.is(command.usage > 1, true, 'should fetch missing options with several fetch');
|
|
840
|
+
t.is(command.usage < 10, true, 'should have stop fetching by itself');
|
|
841
|
+
|
|
842
|
+
t.deepEqual(store.state.allOptions.length, 300);
|
|
843
|
+
t.is(store.state.status.errorMessage, '');
|
|
844
|
+
|
|
845
|
+
t.end();
|
|
846
|
+
});
|
|
847
|
+
|
|
848
|
+
sTest.test('should stop fetching with wrong result: similar results', async (t) => {
|
|
849
|
+
const command = {};
|
|
850
|
+
let nbTry = 0;
|
|
851
|
+
|
|
852
|
+
const store = new Store({
|
|
853
|
+
fetchCallback: buildFetchCb({ total: 500, command }),
|
|
854
|
+
});
|
|
855
|
+
command.usage = 0;
|
|
856
|
+
store.commit('isOpen', true);
|
|
857
|
+
|
|
858
|
+
command.interceptResult = (result) => {
|
|
859
|
+
/* Returns only the 2 first options */
|
|
860
|
+
result.result = result.result.slice(0, 2);
|
|
861
|
+
|
|
862
|
+
return result;
|
|
863
|
+
};
|
|
864
|
+
|
|
865
|
+
nbTry = 0;
|
|
866
|
+
while (nbTry !== command.usage && nbTry < 10) {
|
|
867
|
+
nbTry = command.usage;
|
|
868
|
+
command.fetch();
|
|
869
|
+
await _.nextVueTick(store, command.promise);
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
t.is(command.usage > 1, true, 'should have try to fetch several times');
|
|
873
|
+
t.is(command.usage < 10, true, 'should have stop fetching');
|
|
874
|
+
t.is(store.state.status.errorMessage, store.data.labels.wrongQueryResult);
|
|
875
|
+
|
|
876
|
+
command.usage = 0;
|
|
877
|
+
store.commit('offsetItem', 180);
|
|
878
|
+
nbTry = 0;
|
|
879
|
+
while (nbTry !== command.usage && nbTry < 10) {
|
|
880
|
+
nbTry = command.usage;
|
|
881
|
+
command.fetch();
|
|
882
|
+
await _.nextVueTick(store, command.promise);
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
t.is(command.usage < 10, true, 'should have stop fetching');
|
|
886
|
+
t.is(store.state.status.errorMessage, store.data.labels.wrongQueryResult);
|
|
887
|
+
|
|
888
|
+
t.end();
|
|
889
|
+
});
|
|
890
|
+
|
|
891
|
+
sTest.test('should stop fetching with wrong result: wrong total', async (t) => {
|
|
892
|
+
const command = {};
|
|
893
|
+
let nbTry = 0;
|
|
894
|
+
|
|
895
|
+
const store = new Store({
|
|
896
|
+
fetchCallback: buildFetchCb({ total: 20, command }),
|
|
897
|
+
});
|
|
898
|
+
command.usage = 0;
|
|
899
|
+
store.commit('isOpen', true);
|
|
900
|
+
|
|
901
|
+
command.interceptResult = (result) => {
|
|
902
|
+
result.total = 50;
|
|
903
|
+
|
|
904
|
+
return result;
|
|
905
|
+
};
|
|
906
|
+
|
|
907
|
+
nbTry = 0;
|
|
908
|
+
while (nbTry !== command.usage && nbTry < 10) {
|
|
909
|
+
nbTry = command.usage;
|
|
910
|
+
command.fetch();
|
|
911
|
+
await _.nextVueTick(store, command.promise);
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
t.is(command.usage > 1, true, 'should have try to fetch several times');
|
|
915
|
+
t.is(command.usage < 10, true, 'should have stop fetching');
|
|
916
|
+
t.is(store.state.status.errorMessage, store.data.labels.wrongQueryResult);
|
|
917
|
+
|
|
918
|
+
command.usage = 0;
|
|
919
|
+
store.commit('offsetItem', 30);
|
|
920
|
+
nbTry = 0;
|
|
921
|
+
while (nbTry !== command.usage && nbTry < 10) {
|
|
922
|
+
nbTry = command.usage;
|
|
923
|
+
command.fetch();
|
|
924
|
+
await _.nextVueTick(store, command.promise);
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
t.is(command.usage < 10, true, 'should have stop fetching');
|
|
928
|
+
t.is(store.state.status.errorMessage, store.data.labels.wrongQueryResult);
|
|
929
|
+
|
|
930
|
+
t.deepEqual(store.state.allOptions.length, 20);
|
|
751
931
|
|
|
752
932
|
t.end();
|
|
753
933
|
});
|
package/test/helper.js
CHANGED
|
@@ -101,6 +101,7 @@ function buildFetchCb({
|
|
|
101
101
|
command.reject = () => {};
|
|
102
102
|
command.promise = () => {};
|
|
103
103
|
command.fetch = () => {};
|
|
104
|
+
command.usage = 0;
|
|
104
105
|
}
|
|
105
106
|
|
|
106
107
|
return (search, offset, limit) => {
|
|
@@ -128,10 +129,16 @@ function buildFetchCb({
|
|
|
128
129
|
});
|
|
129
130
|
|
|
130
131
|
function resolveFetch() {
|
|
131
|
-
|
|
132
|
+
let result = {
|
|
132
133
|
total,
|
|
133
134
|
result: getOptions(nb, prefix, offset, groupName),
|
|
134
|
-
}
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
if (command && command.interceptResult) {
|
|
138
|
+
result = command.interceptResult(result);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
resolve(result);
|
|
135
142
|
}
|
|
136
143
|
|
|
137
144
|
if (command) {
|
|
@@ -145,6 +152,7 @@ function buildFetchCb({
|
|
|
145
152
|
|
|
146
153
|
if (command) {
|
|
147
154
|
command.promise = fetchPromise;
|
|
155
|
+
command.usage++;
|
|
148
156
|
}
|
|
149
157
|
spy.promise = fetchPromise;
|
|
150
158
|
|
package/types/MainInput.d.ts
CHANGED
|
@@ -11,13 +11,16 @@ export default class MainInput extends Vue<Props> {
|
|
|
11
11
|
private store;
|
|
12
12
|
private id;
|
|
13
13
|
private nbHiddenItems;
|
|
14
|
+
private domObserver;
|
|
14
15
|
get isDisabled(): boolean;
|
|
15
16
|
get hasValue(): boolean;
|
|
16
17
|
get displayPlaceholder(): boolean;
|
|
17
18
|
get canBeCleared(): boolean;
|
|
18
19
|
get showClearAll(): boolean;
|
|
19
20
|
get clearedLabel(): string;
|
|
20
|
-
get singleSelectedItem():
|
|
21
|
+
get singleSelectedItem(): undefined | OptionItem;
|
|
22
|
+
get singleSelectedItemText(): string;
|
|
23
|
+
get singleSelectedItemTitle(): string;
|
|
21
24
|
get singleStyle(): string | undefined;
|
|
22
25
|
get selecticId(): string | undefined;
|
|
23
26
|
get isSelectionReversed(): boolean;
|
|
@@ -31,7 +34,10 @@ export default class MainInput extends Vue<Props> {
|
|
|
31
34
|
private selectItem;
|
|
32
35
|
private clearSelection;
|
|
33
36
|
private computeSize;
|
|
37
|
+
private closeObserver;
|
|
38
|
+
private createObserver;
|
|
34
39
|
onInternalChange(): void;
|
|
35
40
|
updated(): void;
|
|
41
|
+
beforeUnmount(): void;
|
|
36
42
|
render(): h.JSX.Element;
|
|
37
43
|
}
|
package/types/Store.d.ts
CHANGED
package/types/tools.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { OptionValue } from './Store';
|
|
1
2
|
/**
|
|
2
3
|
* Clone the object and its inner properties.
|
|
3
4
|
* @param obj The object to be clone.
|
|
@@ -20,3 +21,7 @@ export declare function deepClone<T = any>(origObject: T, ignoreAttributes?: str
|
|
|
20
21
|
export declare function convertToRegExp(name: string, flag?: string): RegExp;
|
|
21
22
|
/** Does the same as Object.assign but does not replace if value is undefined */
|
|
22
23
|
export declare function assignObject<T>(obj: Partial<T>, ...sourceObjects: Array<Partial<T>>): T;
|
|
24
|
+
/** Compare 2 list of options.
|
|
25
|
+
* @returns true if there are no difference
|
|
26
|
+
*/
|
|
27
|
+
export declare function compareOptions(oldOptions: OptionValue[], newOptions: OptionValue[]): boolean;
|