@schukai/monster 4.112.0 → 4.113.0
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
|
@@ -2,6 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
|
|
5
|
+
## [4.113.0] - 2026-01-30
|
|
6
|
+
|
|
7
|
+
### Add Features
|
|
8
|
+
|
|
9
|
+
- Improve selection handling in select.mjs
|
|
10
|
+
- Implement two linked selection components and associated functionality [#381](https://gitlab.schukai.com/oss/libraries/javascript/monster/issues/381)
|
|
11
|
+
- Add two selection dependency with UI and mock data
|
|
12
|
+
### Changes
|
|
13
|
+
|
|
14
|
+
- move mock to development mock
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
5
18
|
## [4.112.0] - 2026-01-29
|
|
6
19
|
|
|
7
20
|
### Add Features
|
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"author":"Volker Schukai","dependencies":{"@floating-ui/dom":"^1.7.4","@popperjs/core":"^2.11.8"},"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.
|
|
1
|
+
{"author":"Volker Schukai","dependencies":{"@floating-ui/dom":"^1.7.4","@popperjs/core":"^2.11.8"},"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.113.0"}
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright © Volker Schukai and all contributing authors, {{copyRightYear}}. All rights reserved.
|
|
3
|
+
* Node module: @schukai/monster
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the GNU Affero General Public License version 3 (AGPLv3).
|
|
6
|
+
* The full text of the license can be found at: https://www.gnu.org/licenses/agpl-3.0.en.html
|
|
7
|
+
*
|
|
8
|
+
* For those who do not wish to adhere to the AGPLv3, a commercial license is available.
|
|
9
|
+
* Acquiring a commercial license allows you to use this software without complying with the AGPLv3 terms.
|
|
10
|
+
* For more information about purchasing a commercial license, please contact Volker Schukai.
|
|
11
|
+
*
|
|
12
|
+
* SPDX-License-Identifier: AGPL-3.0
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { instanceSymbol } from "../../constants.mjs";
|
|
16
|
+
import {
|
|
17
|
+
assembleMethodSymbol,
|
|
18
|
+
registerCustomElement,
|
|
19
|
+
} from "../../dom/customelement.mjs";
|
|
20
|
+
import { addErrorAttribute } from "../../dom/error.mjs";
|
|
21
|
+
import { getDocument } from "../../dom/util.mjs";
|
|
22
|
+
import { isArray, isObject, isString } from "../../types/is.mjs";
|
|
23
|
+
import { CustomElement } from "../../dom/customelement.mjs";
|
|
24
|
+
|
|
25
|
+
export { SelectLink };
|
|
26
|
+
|
|
27
|
+
const sourceElementSymbol = Symbol("sourceElement");
|
|
28
|
+
const targetElementSymbol = Symbol("targetElement");
|
|
29
|
+
const observerSymbol = Symbol("observer");
|
|
30
|
+
const handlerSymbol = Symbol("handler");
|
|
31
|
+
const boundSymbol = Symbol("bound");
|
|
32
|
+
|
|
33
|
+
class SelectLink extends CustomElement {
|
|
34
|
+
static get [instanceSymbol]() {
|
|
35
|
+
return Symbol.for("@schukai/monster/components/form/select-link@@instance");
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
static getTag() {
|
|
39
|
+
return "monster-select-link";
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
get defaults() {
|
|
43
|
+
return Object.assign({}, super.defaults, {
|
|
44
|
+
shadowMode: false,
|
|
45
|
+
source: "",
|
|
46
|
+
target: "",
|
|
47
|
+
param: "",
|
|
48
|
+
emptyValue: "",
|
|
49
|
+
disableTarget: true,
|
|
50
|
+
clearSelection: true,
|
|
51
|
+
clearOptions: true,
|
|
52
|
+
clearTotalMessage: true,
|
|
53
|
+
clearMessageOnValue: true,
|
|
54
|
+
emptyMessage: "",
|
|
55
|
+
autoFetch: true,
|
|
56
|
+
autoFetchOnEmpty: false,
|
|
57
|
+
syncOnInit: true,
|
|
58
|
+
events: [
|
|
59
|
+
"monster-selected",
|
|
60
|
+
"monster-changed",
|
|
61
|
+
"monster-selection-removed",
|
|
62
|
+
"monster-selection-cleared",
|
|
63
|
+
],
|
|
64
|
+
debug: false,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
[assembleMethodSymbol]() {
|
|
69
|
+
super[assembleMethodSymbol]();
|
|
70
|
+
bindSelects.call(this);
|
|
71
|
+
return this;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
disconnectedCallback() {
|
|
75
|
+
unbindSelects.call(this);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function resolveElement(value) {
|
|
80
|
+
if (value instanceof HTMLElement) {
|
|
81
|
+
return value;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (!isString(value) || value.trim() === "") {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const selector = value.trim();
|
|
89
|
+
const doc = getDocument();
|
|
90
|
+
let element = null;
|
|
91
|
+
|
|
92
|
+
try {
|
|
93
|
+
element = doc.querySelector(selector);
|
|
94
|
+
} catch (e) {
|
|
95
|
+
element = null;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (!element) {
|
|
99
|
+
element = doc.getElementById(selector.replace(/^#/, ""));
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return element instanceof HTMLElement ? element : null;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function bindSelects() {
|
|
106
|
+
const source = resolveElement(this.getOption("source"));
|
|
107
|
+
const target = resolveElement(this.getOption("target"));
|
|
108
|
+
|
|
109
|
+
if (!(source && target)) {
|
|
110
|
+
if (!this[observerSymbol] && getDocument().body) {
|
|
111
|
+
const observer = new MutationObserver(() => {
|
|
112
|
+
bindSelects.call(this);
|
|
113
|
+
});
|
|
114
|
+
observer.observe(getDocument().body, {
|
|
115
|
+
childList: true,
|
|
116
|
+
subtree: true,
|
|
117
|
+
});
|
|
118
|
+
this[observerSymbol] = observer;
|
|
119
|
+
}
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (this[observerSymbol]) {
|
|
124
|
+
this[observerSymbol].disconnect();
|
|
125
|
+
delete this[observerSymbol];
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
this[sourceElementSymbol] = source;
|
|
129
|
+
this[targetElementSymbol] = target;
|
|
130
|
+
|
|
131
|
+
if (this[boundSymbol]) {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const events = this.getOption("events");
|
|
136
|
+
const handler = () => syncTarget.call(this);
|
|
137
|
+
this[handlerSymbol] = handler;
|
|
138
|
+
|
|
139
|
+
if (isArray(events)) {
|
|
140
|
+
for (const eventName of events) {
|
|
141
|
+
if (isString(eventName) && eventName !== "") {
|
|
142
|
+
source.addEventListener(eventName, handler);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
this[boundSymbol] = true;
|
|
148
|
+
|
|
149
|
+
if (this.getOption("syncOnInit") === true) {
|
|
150
|
+
queueMicrotask(() => {
|
|
151
|
+
syncTarget.call(this);
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function unbindSelects() {
|
|
157
|
+
if (this[observerSymbol]) {
|
|
158
|
+
this[observerSymbol].disconnect();
|
|
159
|
+
delete this[observerSymbol];
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const source = this[sourceElementSymbol];
|
|
163
|
+
const handler = this[handlerSymbol];
|
|
164
|
+
const events = this.getOption("events");
|
|
165
|
+
|
|
166
|
+
if (source && handler && isArray(events)) {
|
|
167
|
+
for (const eventName of events) {
|
|
168
|
+
if (isString(eventName) && eventName !== "") {
|
|
169
|
+
source.removeEventListener(eventName, handler);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
delete this[sourceElementSymbol];
|
|
175
|
+
delete this[targetElementSymbol];
|
|
176
|
+
delete this[handlerSymbol];
|
|
177
|
+
delete this[boundSymbol];
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function syncTarget() {
|
|
181
|
+
const source = this[sourceElementSymbol];
|
|
182
|
+
const target = this[targetElementSymbol];
|
|
183
|
+
const param = this.getOption("param");
|
|
184
|
+
|
|
185
|
+
if (!(source && target)) {
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (!isString(param) || param.trim() === "") {
|
|
190
|
+
addErrorAttribute(this, "Missing param option.");
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
let value = source.value;
|
|
195
|
+
if (isArray(value)) {
|
|
196
|
+
value = value.join(",");
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const isEmpty = value === null || value === undefined || value === "";
|
|
200
|
+
const emptyValue = this.getOption("emptyValue");
|
|
201
|
+
const autoFetch = this.getOption("autoFetch") === true;
|
|
202
|
+
const autoFetchOnEmpty = this.getOption("autoFetchOnEmpty") === true;
|
|
203
|
+
const disableTarget = this.getOption("disableTarget") === true;
|
|
204
|
+
const debug = this.getOption("debug") === true;
|
|
205
|
+
|
|
206
|
+
if (debug) {
|
|
207
|
+
console.log("[select-link]", {
|
|
208
|
+
source: source.id || source.tagName,
|
|
209
|
+
target: target.id || target.tagName,
|
|
210
|
+
param,
|
|
211
|
+
value,
|
|
212
|
+
isEmpty,
|
|
213
|
+
disableTarget,
|
|
214
|
+
autoFetch,
|
|
215
|
+
autoFetchOnEmpty,
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (isEmpty) {
|
|
220
|
+
if (disableTarget === true) {
|
|
221
|
+
target.setAttribute("disabled", "");
|
|
222
|
+
if (debug) {
|
|
223
|
+
console.log("[select-link] target disabled (empty)");
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (this.getOption("clearSelection") === true) {
|
|
228
|
+
target.setOption("selection", []);
|
|
229
|
+
}
|
|
230
|
+
if (this.getOption("clearOptions") === true) {
|
|
231
|
+
target.setOption("options", []);
|
|
232
|
+
}
|
|
233
|
+
if (this.getOption("clearTotalMessage") === true) {
|
|
234
|
+
target.setOption("messages.total", "");
|
|
235
|
+
}
|
|
236
|
+
const emptyMessage = this.getOption("emptyMessage");
|
|
237
|
+
if (isString(emptyMessage) && emptyMessage !== "") {
|
|
238
|
+
target.setOption("messages.control", emptyMessage);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const params = Object.assign({}, target.getOption("filter.params", {}));
|
|
242
|
+
params[param] = emptyValue;
|
|
243
|
+
target.setOption("filter.params", params);
|
|
244
|
+
|
|
245
|
+
if (autoFetchOnEmpty && typeof target.fetch === "function") {
|
|
246
|
+
target.fetch().catch((e) => {
|
|
247
|
+
addErrorAttribute(target, e);
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if (disableTarget === true) {
|
|
254
|
+
target.removeAttribute("disabled");
|
|
255
|
+
if (debug) {
|
|
256
|
+
console.log("[select-link] target enabled (value)");
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
if (this.getOption("clearMessageOnValue") === true) {
|
|
261
|
+
target.setOption("messages.control", "");
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const params = Object.assign({}, target.getOption("filter.params", {}));
|
|
265
|
+
params[param] = value;
|
|
266
|
+
target.setOption("filter.params", params);
|
|
267
|
+
|
|
268
|
+
if (autoFetch && typeof target.fetch === "function") {
|
|
269
|
+
target.fetch().catch((e) => {
|
|
270
|
+
addErrorAttribute(target, e);
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
registerCustomElement(SelectLink);
|
|
@@ -256,7 +256,11 @@ const optionsVersionSymbol = Symbol("optionsVersion");
|
|
|
256
256
|
const pendingSelectionSymbol = Symbol("pendingSelection");
|
|
257
257
|
const selectionSyncScheduledSymbol = Symbol("selectionSyncScheduled");
|
|
258
258
|
const optionsSnapshotSymbol = Symbol("optionsSnapshot");
|
|
259
|
+
const strictModeSnapshotSymbol = Symbol("strictModeSnapshot");
|
|
260
|
+
const optionsMapSymbol = Symbol("optionsMap");
|
|
261
|
+
const optionsMapVersionSnapshotSymbol = Symbol("optionsMapVersionSnapshot");
|
|
259
262
|
const selectionVersionSymbol = Symbol("selectionVersion");
|
|
263
|
+
const closeOnSelectAutoSymbol = Symbol("closeOnSelectAuto");
|
|
260
264
|
|
|
261
265
|
/**
|
|
262
266
|
* @private
|
|
@@ -288,6 +292,7 @@ const lookupCacheSymbol = Symbol("lookupCache");
|
|
|
288
292
|
* @type {symbol}
|
|
289
293
|
*/
|
|
290
294
|
const lookupInProgressSymbol = Symbol("lookupInProgress");
|
|
295
|
+
const fetchRequestVersionSymbol = Symbol("fetchRequestVersion");
|
|
291
296
|
|
|
292
297
|
/**
|
|
293
298
|
* @private
|
|
@@ -365,6 +370,8 @@ class Select extends CustomControl {
|
|
|
365
370
|
this[currentPageSymbol] = 1;
|
|
366
371
|
this[lookupCacheSymbol] = new Map();
|
|
367
372
|
this[lookupInProgressSymbol] = new Map();
|
|
373
|
+
this[optionsMapSymbol] = new Map();
|
|
374
|
+
this[closeOnSelectAutoSymbol] = true;
|
|
368
375
|
initOptionObserver.call(this);
|
|
369
376
|
}
|
|
370
377
|
|
|
@@ -574,6 +581,8 @@ class Select extends CustomControl {
|
|
|
574
581
|
open: "{",
|
|
575
582
|
close: "}",
|
|
576
583
|
},
|
|
584
|
+
params: {},
|
|
585
|
+
paramsDefaults: {},
|
|
577
586
|
defaultOptionsUrl: null,
|
|
578
587
|
},
|
|
579
588
|
|
|
@@ -650,6 +659,8 @@ class Select extends CustomControl {
|
|
|
650
659
|
this.setOption("messages.selected", "");
|
|
651
660
|
this.setOption("messages.total", "");
|
|
652
661
|
this.setOption("messages.summary", "");
|
|
662
|
+
this.setOption("total", null);
|
|
663
|
+
resetPaginationState.call(this);
|
|
653
664
|
|
|
654
665
|
resetErrorAttribute(this);
|
|
655
666
|
|
|
@@ -921,11 +932,29 @@ function processAndApplyPaginationData(data) {
|
|
|
921
932
|
return;
|
|
922
933
|
}
|
|
923
934
|
|
|
935
|
+
let dataCount;
|
|
936
|
+
const mappingSelector = this.getOption("mapping.selector");
|
|
937
|
+
if (isString(mappingSelector)) {
|
|
938
|
+
try {
|
|
939
|
+
const pathfinder = new Pathfinder(data);
|
|
940
|
+
const mapped = pathfinder.getVia(mappingSelector);
|
|
941
|
+
if (isArray(mapped)) {
|
|
942
|
+
dataCount = mapped.length;
|
|
943
|
+
} else if (mapped instanceof Map) {
|
|
944
|
+
dataCount = mapped.size;
|
|
945
|
+
} else if (isObject(mapped)) {
|
|
946
|
+
dataCount = Object.keys(mapped).length;
|
|
947
|
+
}
|
|
948
|
+
} catch (e) {}
|
|
949
|
+
}
|
|
950
|
+
|
|
924
951
|
const mappingTotal = this.getOption("mapping.total");
|
|
925
952
|
const mappingCurrentPage = this.getOption("mapping.currentPage");
|
|
926
953
|
const mappingObjectsPerPage = this.getOption("mapping.objectsPerPage");
|
|
927
954
|
|
|
928
955
|
if (!mappingTotal || !mappingCurrentPage || !mappingObjectsPerPage) {
|
|
956
|
+
this.setOption("total", null);
|
|
957
|
+
resetPaginationState.call(this);
|
|
929
958
|
return;
|
|
930
959
|
}
|
|
931
960
|
|
|
@@ -937,15 +966,15 @@ function processAndApplyPaginationData(data) {
|
|
|
937
966
|
|
|
938
967
|
if (!isInteger(total)) {
|
|
939
968
|
addErrorAttribute(this, "total is not an integer");
|
|
969
|
+
this.setOption("total", null);
|
|
970
|
+
resetPaginationState.call(this);
|
|
940
971
|
return;
|
|
941
972
|
}
|
|
942
973
|
|
|
943
974
|
this.setOption("total", total);
|
|
944
975
|
|
|
945
976
|
if (total === 0) {
|
|
946
|
-
this
|
|
947
|
-
this[paginationElementSymbol].setOption("pages", null);
|
|
948
|
-
this[paginationElementSymbol].setOption("currentPage", null);
|
|
977
|
+
resetPaginationState.call(this);
|
|
949
978
|
return;
|
|
950
979
|
}
|
|
951
980
|
|
|
@@ -955,10 +984,53 @@ function processAndApplyPaginationData(data) {
|
|
|
955
984
|
isInteger(objectsPerPage) &&
|
|
956
985
|
objectsPerPage > 0
|
|
957
986
|
) {
|
|
987
|
+
if (
|
|
988
|
+
isInteger(dataCount) &&
|
|
989
|
+
(dataCount === 0 ||
|
|
990
|
+
dataCount > objectsPerPage ||
|
|
991
|
+
total < dataCount ||
|
|
992
|
+
currentPage > Math.ceil(total / objectsPerPage))
|
|
993
|
+
) {
|
|
994
|
+
addErrorAttribute(this, "Invalid pagination data.");
|
|
995
|
+
this.setOption("total", null);
|
|
996
|
+
resetPaginationState.call(this);
|
|
997
|
+
return;
|
|
998
|
+
}
|
|
958
999
|
updatePagination.call(this, total, currentPage, objectsPerPage);
|
|
959
1000
|
}
|
|
960
1001
|
} catch (e) {
|
|
961
1002
|
addErrorAttribute(this, e);
|
|
1003
|
+
this.setOption("total", null);
|
|
1004
|
+
resetPaginationState.call(this);
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
/**
|
|
1009
|
+
* @private
|
|
1010
|
+
* @param {object} data Die rohen Daten aus der API-Antwort.
|
|
1011
|
+
*/
|
|
1012
|
+
function processAndApplyRemoteInfoTotal(data) {
|
|
1013
|
+
const mappingTotal = this.getOption("mapping.total");
|
|
1014
|
+
if (!isString(mappingTotal)) {
|
|
1015
|
+
return;
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
try {
|
|
1019
|
+
const pathfinder = new Pathfinder(data);
|
|
1020
|
+
const total = pathfinder.getVia(mappingTotal);
|
|
1021
|
+
|
|
1022
|
+
if (!isInteger(total)) {
|
|
1023
|
+
addErrorAttribute(this, "total is not an integer");
|
|
1024
|
+
this.setOption("total", null);
|
|
1025
|
+
return;
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
this.setOption("total", total);
|
|
1029
|
+
// Note: remoteInfo is a lightweight request (count=1). Only update total/message here.
|
|
1030
|
+
setTotalText.call(this);
|
|
1031
|
+
} catch (e) {
|
|
1032
|
+
addErrorAttribute(this, e);
|
|
1033
|
+
this.setOption("total", null);
|
|
962
1034
|
}
|
|
963
1035
|
}
|
|
964
1036
|
|
|
@@ -999,10 +1071,14 @@ function scheduleSelectionSync(version) {
|
|
|
999
1071
|
return;
|
|
1000
1072
|
}
|
|
1001
1073
|
|
|
1074
|
+
const shouldUseAttrValue =
|
|
1075
|
+
state.attrValue !== null &&
|
|
1076
|
+
!(state.attrValue === "" && selectionIsEmpty === false);
|
|
1077
|
+
|
|
1002
1078
|
const pending = {
|
|
1003
1079
|
version,
|
|
1004
1080
|
selectionVersion: this[selectionVersionSymbol] || 0,
|
|
1005
|
-
value:
|
|
1081
|
+
value: shouldUseAttrValue ? state.attrValue : state.selection,
|
|
1006
1082
|
};
|
|
1007
1083
|
this[pendingSelectionSymbol] = pending;
|
|
1008
1084
|
|
|
@@ -1731,10 +1807,20 @@ function fetchIt(url, controlOptions) {
|
|
|
1731
1807
|
return new Promise((resolve, reject) => {
|
|
1732
1808
|
setStatusOrRemoveBadges.call(this, "loading");
|
|
1733
1809
|
|
|
1810
|
+
if (!isInteger(this[fetchRequestVersionSymbol])) {
|
|
1811
|
+
this[fetchRequestVersionSymbol] = 0;
|
|
1812
|
+
}
|
|
1813
|
+
this[fetchRequestVersionSymbol] += 1;
|
|
1814
|
+
const requestVersion = this[fetchRequestVersionSymbol];
|
|
1815
|
+
|
|
1734
1816
|
new Processing(10, () => {
|
|
1735
1817
|
fetchData
|
|
1736
1818
|
.call(this, url)
|
|
1737
1819
|
.then((map) => {
|
|
1820
|
+
if (requestVersion !== this[fetchRequestVersionSymbol]) {
|
|
1821
|
+
resolve();
|
|
1822
|
+
return;
|
|
1823
|
+
}
|
|
1738
1824
|
if (
|
|
1739
1825
|
isObject(map) ||
|
|
1740
1826
|
isArray(map) ||
|
|
@@ -1765,10 +1851,20 @@ function fetchIt(url, controlOptions) {
|
|
|
1765
1851
|
}
|
|
1766
1852
|
|
|
1767
1853
|
setStatusOrRemoveBadges.call(this, "error");
|
|
1854
|
+
clearOptionsOnError.call(this);
|
|
1855
|
+
this.setOption("total", null);
|
|
1856
|
+
resetPaginationState.call(this);
|
|
1768
1857
|
reject(new Error("invalid response"));
|
|
1769
1858
|
})
|
|
1770
1859
|
.catch((e) => {
|
|
1860
|
+
if (requestVersion !== this[fetchRequestVersionSymbol]) {
|
|
1861
|
+
resolve();
|
|
1862
|
+
return;
|
|
1863
|
+
}
|
|
1771
1864
|
setStatusOrRemoveBadges.call(this, "error");
|
|
1865
|
+
clearOptionsOnError.call(this);
|
|
1866
|
+
this.setOption("total", null);
|
|
1867
|
+
resetPaginationState.call(this);
|
|
1772
1868
|
reject(e);
|
|
1773
1869
|
});
|
|
1774
1870
|
})
|
|
@@ -1776,6 +1872,9 @@ function fetchIt(url, controlOptions) {
|
|
|
1776
1872
|
.catch((e) => {
|
|
1777
1873
|
setStatusOrRemoveBadges.call(this, "error");
|
|
1778
1874
|
addErrorAttribute(this, e);
|
|
1875
|
+
clearOptionsOnError.call(this);
|
|
1876
|
+
this.setOption("total", null);
|
|
1877
|
+
resetPaginationState.call(this);
|
|
1779
1878
|
reject(e);
|
|
1780
1879
|
});
|
|
1781
1880
|
});
|
|
@@ -1940,35 +2039,12 @@ function buildSelectionLabel(value) {
|
|
|
1940
2039
|
return this[lookupCacheSymbol].get(value);
|
|
1941
2040
|
}
|
|
1942
2041
|
|
|
1943
|
-
const
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
let o = options?.[i];
|
|
1947
|
-
let l, v, v2;
|
|
1948
|
-
|
|
1949
|
-
if (this.getOption("features.useStrictValueComparison") === true) {
|
|
1950
|
-
v = value;
|
|
1951
|
-
} else {
|
|
1952
|
-
v = `${value}`;
|
|
1953
|
-
}
|
|
1954
|
-
|
|
1955
|
-
if (isPrimitive(o) && o === value) {
|
|
1956
|
-
return o;
|
|
1957
|
-
} else if (!isObject(o)) {
|
|
1958
|
-
continue;
|
|
1959
|
-
}
|
|
1960
|
-
|
|
1961
|
-
if (this.getOption("features.useStrictValueComparison") === true) {
|
|
1962
|
-
l = o?.["label"];
|
|
1963
|
-
v2 = o?.["value"];
|
|
1964
|
-
} else {
|
|
1965
|
-
l = `${o?.["label"]}`;
|
|
1966
|
-
v2 = `${o?.["value"]}`;
|
|
1967
|
-
}
|
|
2042
|
+
const strict = this.getOption("features.useStrictValueComparison") === true;
|
|
2043
|
+
const map = this[optionsMapSymbol];
|
|
2044
|
+
const key = strict ? value : String(value);
|
|
1968
2045
|
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
}
|
|
2046
|
+
if (map && map.has(key)) {
|
|
2047
|
+
return map.get(key);
|
|
1972
2048
|
}
|
|
1973
2049
|
|
|
1974
2050
|
return undefined;
|
|
@@ -2113,9 +2189,55 @@ function initOptionObserver() {
|
|
|
2113
2189
|
|
|
2114
2190
|
self.attachObserver(
|
|
2115
2191
|
new Observer(function () {
|
|
2192
|
+
if (self[closeOnSelectAutoSymbol] === true) {
|
|
2193
|
+
self[closeOnSelectAutoSymbol] = false;
|
|
2194
|
+
if (
|
|
2195
|
+
self.hasAttribute("data-monster-option-features-closeonselect") ===
|
|
2196
|
+
false
|
|
2197
|
+
) {
|
|
2198
|
+
const type = self.getOption("type");
|
|
2199
|
+
const shouldClose = type !== "checkbox";
|
|
2200
|
+
self.setOption("features.closeOnSelect", shouldClose);
|
|
2201
|
+
}
|
|
2202
|
+
}
|
|
2116
2203
|
const options = self.getOption("options");
|
|
2117
|
-
|
|
2204
|
+
const strict =
|
|
2205
|
+
self.getOption("features.useStrictValueComparison") === true;
|
|
2206
|
+
const optionsVersion = self[optionsVersionSymbol] || 0;
|
|
2207
|
+
|
|
2208
|
+
if (
|
|
2209
|
+
options !== self[optionsSnapshotSymbol] ||
|
|
2210
|
+
strict !== self[strictModeSnapshotSymbol] ||
|
|
2211
|
+
optionsVersion !== self[optionsMapVersionSnapshotSymbol]
|
|
2212
|
+
) {
|
|
2118
2213
|
self[optionsSnapshotSymbol] = options;
|
|
2214
|
+
self[strictModeSnapshotSymbol] = strict;
|
|
2215
|
+
self[optionsMapVersionSnapshotSymbol] = optionsVersion;
|
|
2216
|
+
|
|
2217
|
+
const map = new Map();
|
|
2218
|
+
if (isArray(options)) {
|
|
2219
|
+
for (const o of options) {
|
|
2220
|
+
if (isPrimitive(o)) {
|
|
2221
|
+
const key = strict ? o : String(o);
|
|
2222
|
+
if (!map.has(key)) {
|
|
2223
|
+
map.set(key, o);
|
|
2224
|
+
}
|
|
2225
|
+
} else if (isObject(o)) {
|
|
2226
|
+
const v = o?.value;
|
|
2227
|
+
const l = o?.label;
|
|
2228
|
+
// If strict is true, use value as is (could be number, symbol, etc.)
|
|
2229
|
+
// If strict is false, use String(value) to allow loose matching
|
|
2230
|
+
if (v !== undefined) {
|
|
2231
|
+
const key = strict ? v : String(v);
|
|
2232
|
+
if (!map.has(key)) {
|
|
2233
|
+
map.set(key, l);
|
|
2234
|
+
}
|
|
2235
|
+
}
|
|
2236
|
+
}
|
|
2237
|
+
}
|
|
2238
|
+
}
|
|
2239
|
+
self[optionsMapSymbol] = map;
|
|
2240
|
+
|
|
2119
2241
|
const version = bumpOptionsVersion.call(self);
|
|
2120
2242
|
scheduleSelectionSync.call(self, version);
|
|
2121
2243
|
}
|
|
@@ -2291,7 +2413,20 @@ function calcAndSetOptionsDimension() {
|
|
|
2291
2413
|
}
|
|
2292
2414
|
|
|
2293
2415
|
if (visible === 0) {
|
|
2294
|
-
if (
|
|
2416
|
+
if (this.getOption("classes.statusOrRemoveBadge") === "error") {
|
|
2417
|
+
this.setOption(
|
|
2418
|
+
"messages.emptyOptions",
|
|
2419
|
+
this.getOption("labels.cannot-be-loaded"),
|
|
2420
|
+
);
|
|
2421
|
+
} else if (getFilterMode.call(this) === FILTER_MODE_DISABLED) {
|
|
2422
|
+
this.setOption(
|
|
2423
|
+
"messages.emptyOptions",
|
|
2424
|
+
this.getOption("labels.no-options-available"),
|
|
2425
|
+
);
|
|
2426
|
+
} else if (
|
|
2427
|
+
getFilterMode.call(this) === FILTER_MODE_REMOTE &&
|
|
2428
|
+
getCurrentFilterValue.call(this) === ""
|
|
2429
|
+
) {
|
|
2295
2430
|
this.setOption(
|
|
2296
2431
|
"messages.emptyOptions",
|
|
2297
2432
|
this.getOption("labels.no-options-available"),
|
|
@@ -2619,6 +2754,29 @@ function filterFromRemote() {
|
|
|
2619
2754
|
* @returns {string}
|
|
2620
2755
|
*/
|
|
2621
2756
|
function formatURL(url, params = {}) {
|
|
2757
|
+
const paramsDefaults = this.getOption("filter.paramsDefaults");
|
|
2758
|
+
const externalParams = this.getOption("filter.params");
|
|
2759
|
+
if (isObject(paramsDefaults) || isObject(externalParams)) {
|
|
2760
|
+
params = Object.assign(
|
|
2761
|
+
{},
|
|
2762
|
+
paramsDefaults || {},
|
|
2763
|
+
externalParams || {},
|
|
2764
|
+
params,
|
|
2765
|
+
);
|
|
2766
|
+
if (isObject(paramsDefaults)) {
|
|
2767
|
+
for (const key in paramsDefaults) {
|
|
2768
|
+
if (!Object.hasOwn(paramsDefaults, key)) continue;
|
|
2769
|
+
if (
|
|
2770
|
+
params[key] === "" ||
|
|
2771
|
+
params[key] === null ||
|
|
2772
|
+
params[key] === undefined
|
|
2773
|
+
) {
|
|
2774
|
+
params[key] = paramsDefaults[key];
|
|
2775
|
+
}
|
|
2776
|
+
}
|
|
2777
|
+
}
|
|
2778
|
+
}
|
|
2779
|
+
|
|
2622
2780
|
// Die Logik für den Default-Filterwert bleibt erhalten
|
|
2623
2781
|
if (
|
|
2624
2782
|
params.filter === undefined ||
|
|
@@ -2668,6 +2826,8 @@ function filterFromRemoteByValue(optionUrl, params, openPopper) {
|
|
|
2668
2826
|
let url = formatURL.call(this, optionUrl, params);
|
|
2669
2827
|
|
|
2670
2828
|
if (url.indexOf(disabledRequestMarker.toString()) !== -1) {
|
|
2829
|
+
this.setOption("total", null);
|
|
2830
|
+
resetPaginationState.call(this);
|
|
2671
2831
|
return Promise.resolve();
|
|
2672
2832
|
}
|
|
2673
2833
|
|
|
@@ -2682,6 +2842,10 @@ function filterFromRemoteByValue(optionUrl, params, openPopper) {
|
|
|
2682
2842
|
}
|
|
2683
2843
|
})
|
|
2684
2844
|
.catch((e) => {
|
|
2845
|
+
if (getFilterMode.call(this) === FILTER_MODE_REMOTE) {
|
|
2846
|
+
this.setOption("total", null);
|
|
2847
|
+
resetPaginationState.call(this);
|
|
2848
|
+
}
|
|
2685
2849
|
addErrorAttribute(this, e);
|
|
2686
2850
|
setStatusOrRemoveBadges.call(this, "error");
|
|
2687
2851
|
throw e;
|
|
@@ -2755,6 +2919,24 @@ function getFilterMode() {
|
|
|
2755
2919
|
}
|
|
2756
2920
|
}
|
|
2757
2921
|
|
|
2922
|
+
function getCurrentFilterValue() {
|
|
2923
|
+
if (this.getOption("filter.position") === FILTER_POSITION_INLINE) {
|
|
2924
|
+
if (this[inlineFilterElementSymbol] instanceof HTMLInputElement) {
|
|
2925
|
+
return this[inlineFilterElementSymbol].value?.trim() ?? "";
|
|
2926
|
+
}
|
|
2927
|
+
}
|
|
2928
|
+
|
|
2929
|
+
if (this[popperFilterElementSymbol] instanceof HTMLInputElement) {
|
|
2930
|
+
return this[popperFilterElementSymbol].value?.trim() ?? "";
|
|
2931
|
+
}
|
|
2932
|
+
|
|
2933
|
+
if (this[inlineFilterElementSymbol] instanceof HTMLInputElement) {
|
|
2934
|
+
return this[inlineFilterElementSymbol].value?.trim() ?? "";
|
|
2935
|
+
}
|
|
2936
|
+
|
|
2937
|
+
return "";
|
|
2938
|
+
}
|
|
2939
|
+
|
|
2758
2940
|
/**
|
|
2759
2941
|
* @private
|
|
2760
2942
|
*/
|
|
@@ -2930,6 +3112,10 @@ function clearSelection() {
|
|
|
2930
3112
|
throw new Error("no shadow-root is defined");
|
|
2931
3113
|
}
|
|
2932
3114
|
|
|
3115
|
+
if (this.hasAttribute("value")) {
|
|
3116
|
+
this.removeAttribute("value");
|
|
3117
|
+
}
|
|
3118
|
+
|
|
2933
3119
|
setSelection
|
|
2934
3120
|
.call(this, [])
|
|
2935
3121
|
.then(() => {})
|
|
@@ -2996,6 +3182,12 @@ function areOptionsAvailableAndInitInternal() {
|
|
|
2996
3182
|
(isArray(options) && options.length === 0)
|
|
2997
3183
|
) {
|
|
2998
3184
|
setStatusOrRemoveBadges.call(this, "empty");
|
|
3185
|
+
if (getFilterMode.call(this) === FILTER_MODE_REMOTE) {
|
|
3186
|
+
if (this[isLoadingSymbol] !== true) {
|
|
3187
|
+
this.setOption("total", null);
|
|
3188
|
+
resetPaginationState.call(this);
|
|
3189
|
+
}
|
|
3190
|
+
}
|
|
2999
3191
|
|
|
3000
3192
|
let msg = this.getOption("labels.no-options-available");
|
|
3001
3193
|
|
|
@@ -3035,7 +3227,9 @@ function areOptionsAvailableAndInitInternal() {
|
|
|
3035
3227
|
if (this.getOption("features.emptyValueIfNoOptions") === true) {
|
|
3036
3228
|
this.value = "";
|
|
3037
3229
|
}
|
|
3038
|
-
|
|
3230
|
+
if (this[isLoadingSymbol] !== true) {
|
|
3231
|
+
addErrorAttribute(this, "No options available.");
|
|
3232
|
+
}
|
|
3039
3233
|
return false;
|
|
3040
3234
|
}
|
|
3041
3235
|
|
|
@@ -3342,6 +3536,7 @@ function setSelection(selection) {
|
|
|
3342
3536
|
|
|
3343
3537
|
checkOptionState.call(this);
|
|
3344
3538
|
setSummaryAndControlText.call(this);
|
|
3539
|
+
setStatusOrRemoveBadges.call(this);
|
|
3345
3540
|
|
|
3346
3541
|
if (valuesChanged) {
|
|
3347
3542
|
try {
|
|
@@ -3622,7 +3817,7 @@ function initTotal() {
|
|
|
3622
3817
|
try {
|
|
3623
3818
|
const data = JSON.parse(String(text));
|
|
3624
3819
|
|
|
3625
|
-
|
|
3820
|
+
processAndApplyRemoteInfoTotal.call(this, data);
|
|
3626
3821
|
} catch (e) {
|
|
3627
3822
|
addErrorAttribute(this, e);
|
|
3628
3823
|
}
|
|
@@ -3642,6 +3837,24 @@ function updatePagination(total, currentPage, objectsPerPage) {
|
|
|
3642
3837
|
});
|
|
3643
3838
|
}
|
|
3644
3839
|
|
|
3840
|
+
function resetPaginationState() {
|
|
3841
|
+
const paginationElement = this[paginationElementSymbol];
|
|
3842
|
+
if (!paginationElement) {
|
|
3843
|
+
return;
|
|
3844
|
+
}
|
|
3845
|
+
|
|
3846
|
+
paginationElement.style.display = "none";
|
|
3847
|
+
paginationElement.setOption("pages", null);
|
|
3848
|
+
paginationElement.setOption("currentPage", null);
|
|
3849
|
+
paginationElement.setOption("objectsPerPage", null);
|
|
3850
|
+
this.setOption("messages.total", "");
|
|
3851
|
+
}
|
|
3852
|
+
|
|
3853
|
+
function clearOptionsOnError() {
|
|
3854
|
+
this[cleanupOptionsListSymbol] = true;
|
|
3855
|
+
this.setOption("options", []);
|
|
3856
|
+
}
|
|
3857
|
+
|
|
3645
3858
|
function refreshSelectPaginationLayout() {
|
|
3646
3859
|
const paginationElement = this[paginationElementSymbol];
|
|
3647
3860
|
if (!paginationElement || typeof paginationElement.getOption !== "function") {
|
|
@@ -3709,8 +3922,12 @@ function initEventHandler() {
|
|
|
3709
3922
|
return value !== b.value;
|
|
3710
3923
|
});
|
|
3711
3924
|
|
|
3712
|
-
|
|
3713
|
-
.
|
|
3925
|
+
const applyRemoval =
|
|
3926
|
+
Array.isArray(selection) && selection.length === 0
|
|
3927
|
+
? clearSelection.call(self)
|
|
3928
|
+
: setSelection.call(self, selection);
|
|
3929
|
+
|
|
3930
|
+
applyRemoval
|
|
3714
3931
|
.then(() => {
|
|
3715
3932
|
fireCustomEvent(self, "monster-selection-removed", {
|
|
3716
3933
|
value,
|
|
@@ -3771,6 +3988,10 @@ function initEventHandler() {
|
|
|
3771
3988
|
};
|
|
3772
3989
|
|
|
3773
3990
|
self[keyEventHandler] = (event) => {
|
|
3991
|
+
if (event?.monsterFilterHandled === true) {
|
|
3992
|
+
return;
|
|
3993
|
+
}
|
|
3994
|
+
|
|
3774
3995
|
const path = event.composedPath();
|
|
3775
3996
|
const element = path.shift();
|
|
3776
3997
|
|
|
@@ -3808,6 +4029,23 @@ function initEventHandler() {
|
|
|
3808
4029
|
}
|
|
3809
4030
|
};
|
|
3810
4031
|
|
|
4032
|
+
const attachFilterKeyListener = (element) => {
|
|
4033
|
+
if (!(element instanceof HTMLElement)) {
|
|
4034
|
+
return;
|
|
4035
|
+
}
|
|
4036
|
+
|
|
4037
|
+
element.addEventListener("keydown", (event) => {
|
|
4038
|
+
if (event?.monsterFilterHandled === true) {
|
|
4039
|
+
return;
|
|
4040
|
+
}
|
|
4041
|
+
event.monsterFilterHandled = true;
|
|
4042
|
+
handleFilterKeyboardEvents.call(self, event);
|
|
4043
|
+
});
|
|
4044
|
+
};
|
|
4045
|
+
|
|
4046
|
+
attachFilterKeyListener(this[inlineFilterElementSymbol]);
|
|
4047
|
+
attachFilterKeyListener(this[popperFilterElementSymbol]);
|
|
4048
|
+
|
|
3811
4049
|
const types = self.getOption("toggleEventType", ["click"]);
|
|
3812
4050
|
|
|
3813
4051
|
for (const [, type] of Object.entries(types)) {
|
|
@@ -3960,6 +4198,20 @@ function setStatusOrRemoveBadges(suggestion) {
|
|
|
3960
4198
|
return;
|
|
3961
4199
|
}
|
|
3962
4200
|
|
|
4201
|
+
if (current === "clear") {
|
|
4202
|
+
const options = this.getOption("options");
|
|
4203
|
+
if (
|
|
4204
|
+
options === undefined ||
|
|
4205
|
+
options === null ||
|
|
4206
|
+
(isArray(options) && options.length === 0)
|
|
4207
|
+
) {
|
|
4208
|
+
this.setOption("classes.statusOrRemoveBadge", "empty");
|
|
4209
|
+
} else {
|
|
4210
|
+
this.setOption("classes.statusOrRemoveBadge", "closed");
|
|
4211
|
+
}
|
|
4212
|
+
return;
|
|
4213
|
+
}
|
|
4214
|
+
|
|
3963
4215
|
const options = this.getOption("options");
|
|
3964
4216
|
if (
|
|
3965
4217
|
options === undefined ||
|
package/source/monster.mjs
CHANGED
|
@@ -29,6 +29,7 @@ export * from "./components/layout/width-toggle.mjs";
|
|
|
29
29
|
export * from "./components/layout/board.mjs";
|
|
30
30
|
export * from "./components/layout/panel.mjs";
|
|
31
31
|
export * from "./components/layout/details.mjs";
|
|
32
|
+
export * from "./components/layout/vertical-tabs.mjs";
|
|
32
33
|
export * from "./components/layout/slider.mjs";
|
|
33
34
|
export * from "./components/content/fetch-box.mjs";
|
|
34
35
|
export * from "./components/content/viewer.mjs";
|
|
@@ -73,6 +74,7 @@ export * from "./components/form/action-button.mjs";
|
|
|
73
74
|
export * from "./components/form/form.mjs";
|
|
74
75
|
export * from "./components/form/repeat-field-set.mjs";
|
|
75
76
|
export * from "./components/form/api-button.mjs";
|
|
77
|
+
export * from "./components/form/select-link.mjs";
|
|
76
78
|
export * from "./components/form/digits.mjs";
|
|
77
79
|
export * from "./components/form/tree-select.mjs";
|
|
78
80
|
export * from "./components/form/popper-button.mjs";
|
|
@@ -184,6 +184,122 @@ describe('Select', function () {
|
|
|
184
184
|
|
|
185
185
|
});
|
|
186
186
|
|
|
187
|
+
describe('Remote filter pagination', function () {
|
|
188
|
+
let requestCount = 0;
|
|
189
|
+
|
|
190
|
+
beforeEach((done) => {
|
|
191
|
+
let mocks = document.getElementById('mocks');
|
|
192
|
+
mocks.innerHTML = html1;
|
|
193
|
+
|
|
194
|
+
requestCount = 0;
|
|
195
|
+
global['fetch'] = function () {
|
|
196
|
+
requestCount += 1;
|
|
197
|
+
let headers = new Map;
|
|
198
|
+
headers.set('content-type', 'application/json');
|
|
199
|
+
|
|
200
|
+
if (requestCount === 1) {
|
|
201
|
+
return Promise.resolve({
|
|
202
|
+
ok: true,
|
|
203
|
+
status: 200,
|
|
204
|
+
headers: headers,
|
|
205
|
+
text: function () {
|
|
206
|
+
return Promise.resolve(JSON.stringify({
|
|
207
|
+
items: [
|
|
208
|
+
{id: 1, name: "Alpha"}
|
|
209
|
+
],
|
|
210
|
+
pagination: {
|
|
211
|
+
total: 2,
|
|
212
|
+
page: 1,
|
|
213
|
+
perPage: 1
|
|
214
|
+
}
|
|
215
|
+
}));
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return Promise.resolve({
|
|
221
|
+
ok: false,
|
|
222
|
+
status: 500,
|
|
223
|
+
headers: headers,
|
|
224
|
+
text: function () {
|
|
225
|
+
return Promise.resolve(JSON.stringify({}));
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
done();
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
afterEach(() => {
|
|
234
|
+
let mocks = document.getElementById('mocks');
|
|
235
|
+
mocks.innerHTML = "";
|
|
236
|
+
global['fetch'] = fetchReference;
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
it('should reset pagination and clear options on fetch error', function (done) {
|
|
240
|
+
this.timeout(4000);
|
|
241
|
+
|
|
242
|
+
let mocks = document.getElementById('mocks');
|
|
243
|
+
const select = document.createElement('monster-select');
|
|
244
|
+
select.setOption('url', 'https://example.com/items?filter={filter}&page={page}');
|
|
245
|
+
select.setOption('filter.mode', 'remote');
|
|
246
|
+
select.setOption('mapping.selector', 'items.*');
|
|
247
|
+
select.setOption('mapping.labelTemplate', '${name}');
|
|
248
|
+
select.setOption('mapping.valueTemplate', '${id}');
|
|
249
|
+
select.setOption('mapping.total', 'pagination.total');
|
|
250
|
+
select.setOption('mapping.currentPage', 'pagination.page');
|
|
251
|
+
select.setOption('mapping.objectsPerPage', 'pagination.perPage');
|
|
252
|
+
mocks.appendChild(select);
|
|
253
|
+
|
|
254
|
+
const pagination = () => select.shadowRoot.querySelector('[data-monster-role=pagination]');
|
|
255
|
+
|
|
256
|
+
const triggerFilter = (value) => {
|
|
257
|
+
const filterInput = select.shadowRoot.querySelector('[data-monster-role=filter]');
|
|
258
|
+
filterInput.value = value;
|
|
259
|
+
filterInput.dispatchEvent(new KeyboardEvent('keydown', {
|
|
260
|
+
code: 'KeyA',
|
|
261
|
+
bubbles: true
|
|
262
|
+
}));
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
select.addEventListener('monster-options-set', () => {
|
|
266
|
+
setTimeout(() => {
|
|
267
|
+
try {
|
|
268
|
+
const options = select.getOption('options');
|
|
269
|
+
expect(options.length).to.equal(1);
|
|
270
|
+
expect(pagination().getOption('currentPage')).to.equal(1);
|
|
271
|
+
expect(pagination().getOption('pages')).to.equal(2);
|
|
272
|
+
expect(pagination().getOption('objectsPerPage')).to.equal(1);
|
|
273
|
+
|
|
274
|
+
triggerFilter('b');
|
|
275
|
+
|
|
276
|
+
setTimeout(() => {
|
|
277
|
+
try {
|
|
278
|
+
const optionsAfterError = select.getOption('options');
|
|
279
|
+
expect(optionsAfterError.length).to.equal(0);
|
|
280
|
+
expect(pagination().getOption('currentPage')).to.equal(null);
|
|
281
|
+
expect(pagination().getOption('pages')).to.equal(null);
|
|
282
|
+
expect(pagination().getOption('objectsPerPage')).to.equal(null);
|
|
283
|
+
expect(select.getOption('total')).to.equal(null);
|
|
284
|
+
expect(select.getOption('messages.total')).to.equal("");
|
|
285
|
+
} catch (e) {
|
|
286
|
+
return done(e);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
done();
|
|
290
|
+
}, 500);
|
|
291
|
+
} catch (e) {
|
|
292
|
+
done(e);
|
|
293
|
+
}
|
|
294
|
+
}, 50);
|
|
295
|
+
}, {once: true});
|
|
296
|
+
|
|
297
|
+
setTimeout(() => {
|
|
298
|
+
triggerFilter('a');
|
|
299
|
+
}, 0);
|
|
300
|
+
});
|
|
301
|
+
});
|
|
302
|
+
|
|
187
303
|
describe('document.createElement()', function () {
|
|
188
304
|
|
|
189
305
|
afterEach(() => {
|