@schukai/monster 4.136.8 → 4.136.11
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/CHANGELOG.md
CHANGED
|
@@ -18,6 +18,8 @@
|
|
|
18
18
|
- **message-state-button:** distinguish overlay, prose, and wide message layouts for smart popper sizing and overflow ([#401](https://gitlab.schukai.com/oss/libraries/javascript/monster/issues/401))
|
|
19
19
|
- **popper:** support kebab-case camelCase option attributes such as `data-monster-option-popper-content-overflow` ([#401](https://gitlab.schukai.com/oss/libraries/javascript/monster/issues/401))
|
|
20
20
|
- **popper/select:** let nested `monster-select` poppers escape parent popper content wrappers without shrinking normal parent content sizing ([#416](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/work_items/416))
|
|
21
|
+
- **select:** skip hidden remote info requests when `showRemoteInfo` is disabled ([#418](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/work_items/418))
|
|
22
|
+
- **select:** defer remote info loading until the dropdown opens and reuse pagination totals from remote option fetches when available ([#418](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/work_items/418))
|
|
21
23
|
|
|
22
24
|
### Changes
|
|
23
25
|
|
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"author":"Volker Schukai","dependencies":{"@floating-ui/dom":"^1.7.6"},"description":"Monster is a simple library for creating fast, robust and lightweight websites.","homepage":"https://monsterjs.org/","keywords":["framework","web","dom","css","sass","mobile-first","app","front-end","templates","schukai","core","shopcloud","alvine","monster","buildmap","stack","observer","observable","uuid","node","nodelist","css-in-js","logger","log","theme"],"license":"AGPL 3.0","main":"source/monster.mjs","module":"source/monster.mjs","name":"@schukai/monster","repository":{"type":"git","url":"https://gitlab.schukai.com/oss/libraries/javascript/monster.git"},"type":"module","version":"4.136.
|
|
1
|
+
{"author":"Volker Schukai","dependencies":{"@floating-ui/dom":"^1.7.6"},"description":"Monster is a simple library for creating fast, robust and lightweight websites.","homepage":"https://monsterjs.org/","keywords":["framework","web","dom","css","sass","mobile-first","app","front-end","templates","schukai","core","shopcloud","alvine","monster","buildmap","stack","observer","observable","uuid","node","nodelist","css-in-js","logger","log","theme"],"license":"AGPL 3.0","main":"source/monster.mjs","module":"source/monster.mjs","name":"@schukai/monster","repository":{"type":"git","url":"https://gitlab.schukai.com/oss/libraries/javascript/monster.git"},"type":"module","version":"4.136.11"}
|
|
@@ -306,6 +306,7 @@ const lookupCacheSymbol = Symbol("lookupCache");
|
|
|
306
306
|
const lookupInProgressSymbol = Symbol("lookupInProgress");
|
|
307
307
|
const unresolvedSelectionValuesSymbol = Symbol("unresolvedSelectionValues");
|
|
308
308
|
const fetchRequestVersionSymbol = Symbol("fetchRequestVersion");
|
|
309
|
+
const remoteInfoRequestSymbol = Symbol("remoteInfoRequest");
|
|
309
310
|
|
|
310
311
|
/**
|
|
311
312
|
* @private
|
|
@@ -458,6 +459,28 @@ class Select extends CustomControl {
|
|
|
458
459
|
/**
|
|
459
460
|
* Defines the default configuration options for the monster-control.
|
|
460
461
|
* These options can be overridden via the HTML attribute `data-monster-options`.
|
|
462
|
+
* Use JSON in `data-monster-options` when option values must keep their JSON
|
|
463
|
+
* types, for example:
|
|
464
|
+
*
|
|
465
|
+
* ```html
|
|
466
|
+
* <monster-select
|
|
467
|
+
* data-monster-options='{"empty":{"equivalents":[null,"",0]}}'>
|
|
468
|
+
* </monster-select>
|
|
469
|
+
* ```
|
|
470
|
+
*
|
|
471
|
+
* When the value comes from an updater object, bind it through
|
|
472
|
+
* `data-monster-properties`. The bound object value keeps its JavaScript type,
|
|
473
|
+
* so an array such as `[null, "", 0]` keeps the numeric `0` as a number:
|
|
474
|
+
*
|
|
475
|
+
* ```html
|
|
476
|
+
* <monster-select
|
|
477
|
+
* data-monster-properties="option:empty.equivalents path:emptyEquivalents">
|
|
478
|
+
* </monster-select>
|
|
479
|
+
* ```
|
|
480
|
+
*
|
|
481
|
+
* Do not use `data-monster-option-empty-equivalents` for typed primitive
|
|
482
|
+
* values; array option attributes are split as strings and cannot express a
|
|
483
|
+
* numeric `0`.
|
|
461
484
|
* @see {@link https://monsterjs.org/en/doc/#configurate-a-monster-control}
|
|
462
485
|
*
|
|
463
486
|
* @property {string[]} toggleEventType - Array of DOM event names (e.g., ["click", "touch"]) that toggle the dropdown.
|
|
@@ -528,7 +551,7 @@ class Select extends CustomControl {
|
|
|
528
551
|
* @property {Object} empty - Handling of empty or undefined values.
|
|
529
552
|
* @property {string} empty.defaultValueRadio - Default value for `type="radio"` when no selection exists.
|
|
530
553
|
* @property {Array} empty.defaultValueCheckbox - Default value (empty array) for `type="checkbox"`.
|
|
531
|
-
* @property {Array} empty.equivalents - Values that are considered "empty" (e.g., `undefined`, `null`, `""`) and are normalized to the default value.
|
|
554
|
+
* @property {Array} empty.equivalents - Values that are considered "empty" (e.g., `undefined`, `null`, `""`, `NaN`) and are normalized to the default value. Matching is type-safe: a configured string `"0"` only matches the string `"0"`; configure numeric `0` explicitly, via `data-monster-options` JSON, `data-monster-properties`, or `setOption()`, when the number `0` should be treated as empty.
|
|
532
555
|
* @property {Object} formatter - Functions for formatting display values.
|
|
533
556
|
* @property {Function} formatter.selection - Callback `(value, option) => string` to format the display text of selected values.
|
|
534
557
|
* @property {Object} classes - CSS classes for various elements.
|
|
@@ -698,7 +721,6 @@ class Select extends CustomControl {
|
|
|
698
721
|
checkOptionState.call(this);
|
|
699
722
|
calcAndSetOptionsDimension.call(this);
|
|
700
723
|
updatePopper.call(this);
|
|
701
|
-
initTotal.call(this);
|
|
702
724
|
})
|
|
703
725
|
.catch((e) => {
|
|
704
726
|
addErrorAttribute(this, e);
|
|
@@ -721,8 +743,6 @@ class Select extends CustomControl {
|
|
|
721
743
|
let lazyLoadFlag = self.getOption("features.lazyLoad", false);
|
|
722
744
|
const remoteFilterFlag = getFilterMode.call(this) === FILTER_MODE_REMOTE;
|
|
723
745
|
|
|
724
|
-
initTotal.call(self);
|
|
725
|
-
|
|
726
746
|
if (getFilterMode.call(this) === FILTER_MODE_REMOTE) {
|
|
727
747
|
self.setOption("features.lazyLoad", false);
|
|
728
748
|
lazyLoadFlag = false;
|
|
@@ -994,7 +1014,7 @@ function processAndApplyPaginationData(data) {
|
|
|
994
1014
|
const mappingCurrentPage = this.getOption("mapping.currentPage");
|
|
995
1015
|
const mappingObjectsPerPage = this.getOption("mapping.objectsPerPage");
|
|
996
1016
|
|
|
997
|
-
if (!mappingTotal
|
|
1017
|
+
if (!mappingTotal) {
|
|
998
1018
|
this.setOption("total", null);
|
|
999
1019
|
resetPaginationState.call(this);
|
|
1000
1020
|
return;
|
|
@@ -1003,8 +1023,6 @@ function processAndApplyPaginationData(data) {
|
|
|
1003
1023
|
try {
|
|
1004
1024
|
const pathfinder = new Pathfinder(data);
|
|
1005
1025
|
const total = pathfinder.getVia(mappingTotal);
|
|
1006
|
-
const currentPage = pathfinder.getVia(mappingCurrentPage);
|
|
1007
|
-
const objectsPerPage = pathfinder.getVia(mappingObjectsPerPage);
|
|
1008
1026
|
|
|
1009
1027
|
if (!isInteger(total)) {
|
|
1010
1028
|
addErrorAttribute(this, "total is not an integer");
|
|
@@ -1015,8 +1033,16 @@ function processAndApplyPaginationData(data) {
|
|
|
1015
1033
|
|
|
1016
1034
|
this.setOption("total", total);
|
|
1017
1035
|
|
|
1036
|
+
if (!mappingCurrentPage || !mappingObjectsPerPage) {
|
|
1037
|
+
resetPaginationState.call(this, false);
|
|
1038
|
+
return;
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
const currentPage = pathfinder.getVia(mappingCurrentPage);
|
|
1042
|
+
const objectsPerPage = pathfinder.getVia(mappingObjectsPerPage);
|
|
1043
|
+
|
|
1018
1044
|
if (total === 0) {
|
|
1019
|
-
resetPaginationState.call(this);
|
|
1045
|
+
resetPaginationState.call(this, false);
|
|
1020
1046
|
return;
|
|
1021
1047
|
}
|
|
1022
1048
|
|
|
@@ -1887,6 +1913,14 @@ function fetchIt(url, controlOptions) {
|
|
|
1887
1913
|
this[fetchRequestVersionSymbol] += 1;
|
|
1888
1914
|
const requestVersion = this[fetchRequestVersionSymbol];
|
|
1889
1915
|
|
|
1916
|
+
if (getFilterMode.call(this) === FILTER_MODE_REMOTE) {
|
|
1917
|
+
let classes = new TokenList(this.getOption("classes.noOptions"));
|
|
1918
|
+
classes.add("d-none");
|
|
1919
|
+
this.setOption("classes.noOptions", classes.toString());
|
|
1920
|
+
this.setOption("messages.emptyOptions", "");
|
|
1921
|
+
this.setOption("messages.total", "");
|
|
1922
|
+
}
|
|
1923
|
+
|
|
1890
1924
|
new Processing(10, () => {
|
|
1891
1925
|
fetchData
|
|
1892
1926
|
.call(this, url)
|
|
@@ -2692,7 +2726,17 @@ function setTotalText() {
|
|
|
2692
2726
|
return;
|
|
2693
2727
|
}
|
|
2694
2728
|
|
|
2729
|
+
if (this[isLoadingSymbol] === true) {
|
|
2730
|
+
this.setOption("messages.total", "");
|
|
2731
|
+
return;
|
|
2732
|
+
}
|
|
2733
|
+
|
|
2695
2734
|
const count = this.getOption("options").length;
|
|
2735
|
+
if (count === 0) {
|
|
2736
|
+
this.setOption("messages.total", "");
|
|
2737
|
+
return;
|
|
2738
|
+
}
|
|
2739
|
+
|
|
2696
2740
|
const total = Number.parseInt(this.getOption("total"));
|
|
2697
2741
|
if (Number.isNaN(total)) {
|
|
2698
2742
|
this.setOption("messages.total", "");
|
|
@@ -3887,8 +3931,12 @@ function areOptionsAvailableAndInitInternal() {
|
|
|
3887
3931
|
setStatusOrRemoveBadges.call(this, "empty");
|
|
3888
3932
|
if (getFilterMode.call(this) === FILTER_MODE_REMOTE) {
|
|
3889
3933
|
if (this[isLoadingSymbol] !== true) {
|
|
3890
|
-
this.
|
|
3891
|
-
|
|
3934
|
+
if (isInteger(this.getOption("total"))) {
|
|
3935
|
+
resetPaginationState.call(this, false);
|
|
3936
|
+
} else {
|
|
3937
|
+
this.setOption("total", null);
|
|
3938
|
+
resetPaginationState.call(this);
|
|
3939
|
+
}
|
|
3892
3940
|
}
|
|
3893
3941
|
}
|
|
3894
3942
|
|
|
@@ -4113,6 +4161,9 @@ function convertSelectionToValue(selection) {
|
|
|
4113
4161
|
* @private
|
|
4114
4162
|
* @param value
|
|
4115
4163
|
* @returns {boolean}
|
|
4164
|
+
*
|
|
4165
|
+
* Empty-equivalent matching is intentionally type-safe. Consumers that want
|
|
4166
|
+
* numeric `0` to count as empty must configure `0`, not `"0"`.
|
|
4116
4167
|
*/
|
|
4117
4168
|
function isValueIsEmpty(value) {
|
|
4118
4169
|
let equivalents = this.getOption("empty.equivalents");
|
|
@@ -4123,7 +4174,7 @@ function isValueIsEmpty(value) {
|
|
|
4123
4174
|
equivalents = [equivalents];
|
|
4124
4175
|
}
|
|
4125
4176
|
|
|
4126
|
-
return equivalents.
|
|
4177
|
+
return equivalents.includes(value);
|
|
4127
4178
|
}
|
|
4128
4179
|
|
|
4129
4180
|
/**
|
|
@@ -4415,8 +4466,10 @@ function show() {
|
|
|
4415
4466
|
const shouldLoadRemoteOptions =
|
|
4416
4467
|
getFilterMode.call(self) === FILTER_MODE_REMOTE &&
|
|
4417
4468
|
getOptionElements.call(self).length === 0;
|
|
4469
|
+
const shouldLoadDefaultOptions =
|
|
4470
|
+
shouldUseDefaultOptionsUrl.call(self, getCurrentFilterValue.call(self));
|
|
4418
4471
|
|
|
4419
|
-
if (
|
|
4472
|
+
if (shouldLoadDefaultOptions) {
|
|
4420
4473
|
setTimeout(() => {
|
|
4421
4474
|
loadDefaultOptionsFromUrl.call(self).catch((e) => {
|
|
4422
4475
|
addErrorAttribute(self, e);
|
|
@@ -4429,6 +4482,8 @@ function show() {
|
|
|
4429
4482
|
addErrorAttribute(self, e);
|
|
4430
4483
|
});
|
|
4431
4484
|
}, 0);
|
|
4485
|
+
} else {
|
|
4486
|
+
initTotal.call(self);
|
|
4432
4487
|
}
|
|
4433
4488
|
calcAndSetOptionsDimension.call(this);
|
|
4434
4489
|
focusFilter.call(this);
|
|
@@ -4500,6 +4555,18 @@ function initTotal() {
|
|
|
4500
4555
|
return;
|
|
4501
4556
|
}
|
|
4502
4557
|
|
|
4558
|
+
if (this.getOption("features.showRemoteInfo") !== true) {
|
|
4559
|
+
return;
|
|
4560
|
+
}
|
|
4561
|
+
|
|
4562
|
+
if (isInteger(this.getOption("total"))) {
|
|
4563
|
+
return;
|
|
4564
|
+
}
|
|
4565
|
+
|
|
4566
|
+
if (this[remoteInfoRequestSymbol]) {
|
|
4567
|
+
return;
|
|
4568
|
+
}
|
|
4569
|
+
|
|
4503
4570
|
const url = this.getOption("remoteInfo.url");
|
|
4504
4571
|
const mappingTotal = this.getOption("mapping.total");
|
|
4505
4572
|
|
|
@@ -4508,8 +4575,9 @@ function initTotal() {
|
|
|
4508
4575
|
}
|
|
4509
4576
|
|
|
4510
4577
|
const fetchOptions = this.getOption("fetch", {});
|
|
4578
|
+
this.setOption("messages.total", "");
|
|
4511
4579
|
|
|
4512
|
-
getGlobal()
|
|
4580
|
+
const remoteInfoRequest = getGlobal()
|
|
4513
4581
|
.fetch(url, fetchOptions)
|
|
4514
4582
|
.then((response) => {
|
|
4515
4583
|
if (!response.ok) {
|
|
@@ -4533,7 +4601,14 @@ function initTotal() {
|
|
|
4533
4601
|
})
|
|
4534
4602
|
.catch((e) => {
|
|
4535
4603
|
addErrorAttribute(this, e);
|
|
4604
|
+
})
|
|
4605
|
+
.finally(() => {
|
|
4606
|
+
if (this[remoteInfoRequestSymbol] === remoteInfoRequest) {
|
|
4607
|
+
this[remoteInfoRequestSymbol] = null;
|
|
4608
|
+
}
|
|
4536
4609
|
});
|
|
4610
|
+
|
|
4611
|
+
this[remoteInfoRequestSymbol] = remoteInfoRequest;
|
|
4537
4612
|
}
|
|
4538
4613
|
|
|
4539
4614
|
function updatePagination(total, currentPage, objectsPerPage) {
|
|
@@ -4546,7 +4621,7 @@ function updatePagination(total, currentPage, objectsPerPage) {
|
|
|
4546
4621
|
});
|
|
4547
4622
|
}
|
|
4548
4623
|
|
|
4549
|
-
function resetPaginationState() {
|
|
4624
|
+
function resetPaginationState(clearTotalMessage = true) {
|
|
4550
4625
|
const paginationElement = this[paginationElementSymbol];
|
|
4551
4626
|
if (!paginationElement) {
|
|
4552
4627
|
return;
|
|
@@ -4556,7 +4631,9 @@ function resetPaginationState() {
|
|
|
4556
4631
|
paginationElement.setOption("pages", null);
|
|
4557
4632
|
paginationElement.setOption("currentPage", null);
|
|
4558
4633
|
paginationElement.setOption("objectsPerPage", null);
|
|
4559
|
-
|
|
4634
|
+
if (clearTotalMessage === true) {
|
|
4635
|
+
this.setOption("messages.total", "");
|
|
4636
|
+
}
|
|
4560
4637
|
}
|
|
4561
4638
|
|
|
4562
4639
|
function clearOptionsOnError() {
|
|
@@ -790,6 +790,233 @@ describe('Select', function () {
|
|
|
790
790
|
}, 150);
|
|
791
791
|
});
|
|
792
792
|
|
|
793
|
+
it('should not eagerly fetch remote info when showRemoteInfo is disabled', function (done) {
|
|
794
|
+
this.timeout(3000);
|
|
795
|
+
|
|
796
|
+
let mocks = document.getElementById('mocks');
|
|
797
|
+
const requests = [];
|
|
798
|
+
const previousFetch = global['fetch'];
|
|
799
|
+
|
|
800
|
+
global['fetch'] = function (url) {
|
|
801
|
+
requests.push(String(url));
|
|
802
|
+
|
|
803
|
+
return createJsonResponse({
|
|
804
|
+
items: [
|
|
805
|
+
{id: 'alpha', name: 'Alpha'}
|
|
806
|
+
],
|
|
807
|
+
pagination: {
|
|
808
|
+
total: 3,
|
|
809
|
+
page: 2,
|
|
810
|
+
perPage: 1
|
|
811
|
+
}
|
|
812
|
+
});
|
|
813
|
+
};
|
|
814
|
+
|
|
815
|
+
const select = document.createElement('monster-select');
|
|
816
|
+
select.setOption('url', 'https://example.com/items?filter={filter}&page={page}');
|
|
817
|
+
select.setOption('filter.mode', 'remote');
|
|
818
|
+
select.setOption('mapping.selector', 'items.*');
|
|
819
|
+
select.setOption('mapping.labelTemplate', '${name}');
|
|
820
|
+
select.setOption('mapping.valueTemplate', '${id}');
|
|
821
|
+
select.setOption('mapping.total', 'pagination.total');
|
|
822
|
+
select.setOption('mapping.currentPage', 'pagination.page');
|
|
823
|
+
select.setOption('mapping.objectsPerPage', 'pagination.perPage');
|
|
824
|
+
select.setOption('remoteInfo.url', 'https://example.com/remote-info');
|
|
825
|
+
select.setOption('features.showRemoteInfo', false);
|
|
826
|
+
mocks.appendChild(select);
|
|
827
|
+
|
|
828
|
+
setTimeout(() => {
|
|
829
|
+
try {
|
|
830
|
+
expect(requests).to.deep.equal([]);
|
|
831
|
+
} catch (e) {
|
|
832
|
+
global['fetch'] = previousFetch;
|
|
833
|
+
return done(e);
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
select.fetch('https://example.com/items?filter=*&page=2')
|
|
837
|
+
.then(() => {
|
|
838
|
+
try {
|
|
839
|
+
const pagination = select.shadowRoot.querySelector('[data-monster-role=pagination]');
|
|
840
|
+
|
|
841
|
+
expect(requests).to.deep.equal([
|
|
842
|
+
'https://example.com/items?filter=*&page=2'
|
|
843
|
+
]);
|
|
844
|
+
expect(select.getOption('total')).to.equal(3);
|
|
845
|
+
expect(pagination.getOption('currentPage')).to.equal(2);
|
|
846
|
+
expect(pagination.getOption('pages')).to.equal(3);
|
|
847
|
+
expect(pagination.getOption('objectsPerPage')).to.equal(1);
|
|
848
|
+
} catch (e) {
|
|
849
|
+
return done(e);
|
|
850
|
+
} finally {
|
|
851
|
+
global['fetch'] = previousFetch;
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
done();
|
|
855
|
+
})
|
|
856
|
+
.catch((e) => {
|
|
857
|
+
global['fetch'] = previousFetch;
|
|
858
|
+
done(e);
|
|
859
|
+
});
|
|
860
|
+
}, 150);
|
|
861
|
+
});
|
|
862
|
+
|
|
863
|
+
it('should defer remote info fetching until the dropdown is opened', async function () {
|
|
864
|
+
this.timeout(4000);
|
|
865
|
+
|
|
866
|
+
let mocks = document.getElementById('mocks');
|
|
867
|
+
const requests = [];
|
|
868
|
+
const remoteInfoUrl = 'https://example.com/remote-info';
|
|
869
|
+
|
|
870
|
+
global['fetch'] = function (url) {
|
|
871
|
+
requests.push(String(url));
|
|
872
|
+
|
|
873
|
+
return createJsonResponse({
|
|
874
|
+
pagination: {
|
|
875
|
+
total: 5
|
|
876
|
+
}
|
|
877
|
+
});
|
|
878
|
+
};
|
|
879
|
+
|
|
880
|
+
const select = document.createElement('monster-select');
|
|
881
|
+
select.setOption('filter.mode', 'remote');
|
|
882
|
+
select.setOption('filter.position', 'popper');
|
|
883
|
+
select.setOption('mapping.total', 'pagination.total');
|
|
884
|
+
select.setOption('remoteInfo.url', remoteInfoUrl);
|
|
885
|
+
select.setOption('options', [
|
|
886
|
+
{label: 'Alpha', value: 'alpha'}
|
|
887
|
+
]);
|
|
888
|
+
mocks.appendChild(select);
|
|
889
|
+
|
|
890
|
+
await waitForCondition(() => {
|
|
891
|
+
return select.shadowRoot.querySelector('[data-monster-role=container]') instanceof HTMLElement;
|
|
892
|
+
});
|
|
893
|
+
|
|
894
|
+
expect(requests).to.deep.equal([]);
|
|
895
|
+
|
|
896
|
+
const container = select.shadowRoot.querySelector('[data-monster-role=container]');
|
|
897
|
+
|
|
898
|
+
container.click();
|
|
899
|
+
await waitForCondition(() => requests.includes(remoteInfoUrl));
|
|
900
|
+
expect(requests.filter((url) => url === remoteInfoUrl)).to.have.length(1);
|
|
901
|
+
});
|
|
902
|
+
|
|
903
|
+
it('should keep total-only remote totals for loaded options', async function () {
|
|
904
|
+
this.timeout(3000);
|
|
905
|
+
|
|
906
|
+
let mocks = document.getElementById('mocks');
|
|
907
|
+
|
|
908
|
+
global['fetch'] = function () {
|
|
909
|
+
return createJsonResponse({
|
|
910
|
+
items: [
|
|
911
|
+
{id: 'alpha', name: 'Alpha'}
|
|
912
|
+
],
|
|
913
|
+
pagination: {
|
|
914
|
+
total: 1
|
|
915
|
+
}
|
|
916
|
+
});
|
|
917
|
+
};
|
|
918
|
+
|
|
919
|
+
const select = document.createElement('monster-select');
|
|
920
|
+
select.setOption('url', 'https://example.com/items?filter={filter}&page={page}');
|
|
921
|
+
select.setOption('filter.mode', 'remote');
|
|
922
|
+
select.setOption('mapping.selector', 'items.*');
|
|
923
|
+
select.setOption('mapping.labelTemplate', '${name}');
|
|
924
|
+
select.setOption('mapping.valueTemplate', '${id}');
|
|
925
|
+
select.setOption('mapping.total', 'pagination.total');
|
|
926
|
+
mocks.appendChild(select);
|
|
927
|
+
|
|
928
|
+
await waitForCondition(() => {
|
|
929
|
+
return select.shadowRoot.querySelector('[data-monster-role=container]') instanceof HTMLElement;
|
|
930
|
+
});
|
|
931
|
+
|
|
932
|
+
await select.fetch('https://example.com/items?filter=alpha&page=1');
|
|
933
|
+
|
|
934
|
+
expect(select.getOption('total')).to.equal(1);
|
|
935
|
+
expect(select.getOption('messages.total')).to.contain('No additional entries are available');
|
|
936
|
+
});
|
|
937
|
+
|
|
938
|
+
it('should avoid duplicate remote-info badges for empty remote filter results', async function () {
|
|
939
|
+
this.timeout(4000);
|
|
940
|
+
|
|
941
|
+
let mocks = document.getElementById('mocks');
|
|
942
|
+
|
|
943
|
+
global['fetch'] = function () {
|
|
944
|
+
return createJsonResponse({
|
|
945
|
+
items: [],
|
|
946
|
+
pagination: {
|
|
947
|
+
total: 0
|
|
948
|
+
}
|
|
949
|
+
});
|
|
950
|
+
};
|
|
951
|
+
|
|
952
|
+
const select = document.createElement('monster-select');
|
|
953
|
+
select.setOption('url', 'https://example.com/items?filter={filter}&page={page}');
|
|
954
|
+
select.setOption('filter.mode', 'remote');
|
|
955
|
+
select.setOption('filter.position', 'popper');
|
|
956
|
+
select.setOption('mapping.selector', 'items.*');
|
|
957
|
+
select.setOption('mapping.labelTemplate', '${name}');
|
|
958
|
+
select.setOption('mapping.valueTemplate', '${id}');
|
|
959
|
+
select.setOption('mapping.total', 'pagination.total');
|
|
960
|
+
mocks.appendChild(select);
|
|
961
|
+
|
|
962
|
+
await waitForCondition(() => {
|
|
963
|
+
return select.shadowRoot.querySelector('[data-monster-role=filter][name=\"popper-filter\"]') instanceof HTMLInputElement;
|
|
964
|
+
});
|
|
965
|
+
|
|
966
|
+
const container = select.shadowRoot.querySelector('[data-monster-role=container]');
|
|
967
|
+
const filterInput = select.shadowRoot.querySelector('[data-monster-role=filter][name="popper-filter"]');
|
|
968
|
+
filterInput.value = 'alpha';
|
|
969
|
+
|
|
970
|
+
container.click();
|
|
971
|
+
|
|
972
|
+
await waitForCondition(() => {
|
|
973
|
+
return select.getOption('total') === 0;
|
|
974
|
+
});
|
|
975
|
+
|
|
976
|
+
expect(select.getOption('total')).to.equal(0);
|
|
977
|
+
expect(select.getOption('messages.total')).to.equal('');
|
|
978
|
+
expect(select.getOption('messages.emptyOptions')).to.contain('Please consider modifying the filter');
|
|
979
|
+
});
|
|
980
|
+
|
|
981
|
+
it('should keep empty equivalent matching type-safe for numeric zero', function (done) {
|
|
982
|
+
this.timeout(2000);
|
|
983
|
+
|
|
984
|
+
let mocks = document.getElementById('mocks');
|
|
985
|
+
const stringEquivalentSelect = document.createElement('monster-select');
|
|
986
|
+
stringEquivalentSelect.setOption('empty.equivalents', ['0']);
|
|
987
|
+
mocks.appendChild(stringEquivalentSelect);
|
|
988
|
+
|
|
989
|
+
const numericEquivalentSelect = document.createElement('monster-select');
|
|
990
|
+
numericEquivalentSelect.setOption('empty.equivalents', [0]);
|
|
991
|
+
mocks.appendChild(numericEquivalentSelect);
|
|
992
|
+
|
|
993
|
+
setTimeout(() => {
|
|
994
|
+
stringEquivalentSelect.value = 0;
|
|
995
|
+
numericEquivalentSelect.value = 0;
|
|
996
|
+
|
|
997
|
+
setTimeout(() => {
|
|
998
|
+
try {
|
|
999
|
+
expect(stringEquivalentSelect.value).to.equal('0');
|
|
1000
|
+
expect(stringEquivalentSelect.getOption('selection')).to.deep.equal([
|
|
1001
|
+
{
|
|
1002
|
+
label: '0',
|
|
1003
|
+
value: 0,
|
|
1004
|
+
class: 'monster-badge-primary',
|
|
1005
|
+
unresolved: false
|
|
1006
|
+
}
|
|
1007
|
+
]);
|
|
1008
|
+
|
|
1009
|
+
expect(numericEquivalentSelect.value).to.equal('');
|
|
1010
|
+
expect(numericEquivalentSelect.getOption('selection')).to.deep.equal([]);
|
|
1011
|
+
} catch (e) {
|
|
1012
|
+
return done(e);
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
done();
|
|
1016
|
+
}, 50);
|
|
1017
|
+
}, 50);
|
|
1018
|
+
});
|
|
1019
|
+
|
|
793
1020
|
it('should not refetch after selecting an already loaded remote option', function (done) {
|
|
794
1021
|
this.timeout(3000);
|
|
795
1022
|
|