@schukai/monster 4.147.1 → 4.148.1
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/package.json +1 -1
- package/source/components/form/choice-cards.mjs +194 -29
- package/source/components/form/select.mjs +151 -34
- package/source/components/form/util/floating-layout-queue.mjs +38 -18
- package/source/components/form/util/floating-ui.mjs +68 -12
- package/test/cases/components/form/choice-cards.mjs +174 -0
- package/test/cases/components/form/floating-ui.mjs +48 -1
- package/test/cases/components/form/select.mjs +276 -23
|
@@ -132,6 +132,86 @@ describe("ChoiceCards", function () {
|
|
|
132
132
|
cards.shadowRoot.querySelector('[data-choice-value="api"]').click();
|
|
133
133
|
});
|
|
134
134
|
|
|
135
|
+
it("toggles multiple cards when the multiple option is enabled", function () {
|
|
136
|
+
const cards = document.createElement("monster-choice-cards");
|
|
137
|
+
cards.setOption("multiple", true);
|
|
138
|
+
cards.setItems([
|
|
139
|
+
{ value: "assets", label: "Project assets" },
|
|
140
|
+
{ value: "api", label: "API collection" },
|
|
141
|
+
{ value: "manual", label: "Manual" },
|
|
142
|
+
]);
|
|
143
|
+
|
|
144
|
+
const changes = [];
|
|
145
|
+
cards.addEventListener("monster-change", (event) => {
|
|
146
|
+
changes.push(event.detail);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
document.getElementById("test").appendChild(cards);
|
|
150
|
+
|
|
151
|
+
expect(
|
|
152
|
+
cards.shadowRoot
|
|
153
|
+
.querySelector('[data-monster-role="control"]')
|
|
154
|
+
.getAttribute("role"),
|
|
155
|
+
).is.equal("group");
|
|
156
|
+
expect(
|
|
157
|
+
cards.shadowRoot
|
|
158
|
+
.querySelector('[data-choice-value="assets"]')
|
|
159
|
+
.getAttribute("role"),
|
|
160
|
+
).is.equal("checkbox");
|
|
161
|
+
|
|
162
|
+
cards.shadowRoot.querySelector('[data-choice-value="assets"]').click();
|
|
163
|
+
cards.shadowRoot.querySelector('[data-choice-value="api"]').click();
|
|
164
|
+
cards.shadowRoot.querySelector('[data-choice-value="assets"]').click();
|
|
165
|
+
|
|
166
|
+
expect(cards.value).deep.equal(["api"]);
|
|
167
|
+
expect(
|
|
168
|
+
cards.shadowRoot
|
|
169
|
+
.querySelector('[data-choice-value="assets"]')
|
|
170
|
+
.getAttribute("aria-checked"),
|
|
171
|
+
).is.equal("false");
|
|
172
|
+
expect(
|
|
173
|
+
cards.shadowRoot
|
|
174
|
+
.querySelector('[data-choice-value="api"]')
|
|
175
|
+
.getAttribute("aria-checked"),
|
|
176
|
+
).is.equal("true");
|
|
177
|
+
expect(changes.length).is.equal(3);
|
|
178
|
+
expect(changes[0].value).deep.equal(["assets"]);
|
|
179
|
+
expect(changes[0].selected).is.equal(true);
|
|
180
|
+
expect(changes[2].value).deep.equal(["api"]);
|
|
181
|
+
expect(changes[2].selected).is.equal(false);
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
it("reads multiple values from the value attribute", function () {
|
|
185
|
+
document.getElementById("test").innerHTML = `
|
|
186
|
+
<monster-choice-cards
|
|
187
|
+
id="multi-attribute"
|
|
188
|
+
value="assets::api"
|
|
189
|
+
data-monster-option-multiple="true"
|
|
190
|
+
data-monster-options='{
|
|
191
|
+
"items": [
|
|
192
|
+
{ "value": "assets", "label": "Assets" },
|
|
193
|
+
{ "value": "api", "label": "API" },
|
|
194
|
+
{ "value": "manual", "label": "Manual" }
|
|
195
|
+
]
|
|
196
|
+
}'
|
|
197
|
+
></monster-choice-cards>
|
|
198
|
+
`;
|
|
199
|
+
|
|
200
|
+
const cards = document.getElementById("multi-attribute");
|
|
201
|
+
|
|
202
|
+
expect(cards.value).deep.equal(["assets", "api"]);
|
|
203
|
+
expect(
|
|
204
|
+
cards.shadowRoot
|
|
205
|
+
.querySelector('[data-choice-value="assets"]')
|
|
206
|
+
.getAttribute("aria-checked"),
|
|
207
|
+
).is.equal("true");
|
|
208
|
+
expect(
|
|
209
|
+
cards.shadowRoot
|
|
210
|
+
.querySelector('[data-choice-value="manual"]')
|
|
211
|
+
.getAttribute("aria-checked"),
|
|
212
|
+
).is.equal("false");
|
|
213
|
+
});
|
|
214
|
+
|
|
135
215
|
it("reads from and writes back to a form datasource", async function () {
|
|
136
216
|
this.timeout(6000);
|
|
137
217
|
|
|
@@ -177,6 +257,80 @@ describe("ChoiceCards", function () {
|
|
|
177
257
|
await waitForCondition(() => datasource.data?.[0]?.source === "api");
|
|
178
258
|
});
|
|
179
259
|
|
|
260
|
+
it("writes multiple values back to a form datasource", async function () {
|
|
261
|
+
this.timeout(6000);
|
|
262
|
+
|
|
263
|
+
document.getElementById("test").innerHTML = `
|
|
264
|
+
<monster-datasource-dom id="multi-choice-ds">
|
|
265
|
+
<script type="application/json">
|
|
266
|
+
[
|
|
267
|
+
{
|
|
268
|
+
"sources": []
|
|
269
|
+
}
|
|
270
|
+
]
|
|
271
|
+
</script>
|
|
272
|
+
</monster-datasource-dom>
|
|
273
|
+
<monster-form
|
|
274
|
+
id="multi-choice-form"
|
|
275
|
+
data-monster-option-datasource-selector="#multi-choice-ds"
|
|
276
|
+
data-monster-option-mapping-data=""
|
|
277
|
+
data-monster-option-mapping-index="0"
|
|
278
|
+
>
|
|
279
|
+
<monster-choice-cards
|
|
280
|
+
id="multi-choice-source"
|
|
281
|
+
data-monster-attributes="value path:data.sources"
|
|
282
|
+
data-monster-bind="path:data.sources"
|
|
283
|
+
data-monster-option-multiple="true"
|
|
284
|
+
data-monster-options='{
|
|
285
|
+
"items": [
|
|
286
|
+
{ "value": "assets", "label": "Assets" },
|
|
287
|
+
{ "value": "api", "label": "API" },
|
|
288
|
+
{ "value": "manual", "label": "Manual" }
|
|
289
|
+
]
|
|
290
|
+
}'
|
|
291
|
+
></monster-choice-cards>
|
|
292
|
+
</monster-form>
|
|
293
|
+
`;
|
|
294
|
+
|
|
295
|
+
const datasource = document.getElementById("multi-choice-ds");
|
|
296
|
+
const cards = document.getElementById("multi-choice-source");
|
|
297
|
+
|
|
298
|
+
await waitForCondition(() => Array.isArray(datasource.data?.[0]?.sources));
|
|
299
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
300
|
+
|
|
301
|
+
cards.select("assets");
|
|
302
|
+
await waitForCondition(
|
|
303
|
+
() => JSON.stringify(datasource.data?.[0]?.sources) === '["assets"]',
|
|
304
|
+
);
|
|
305
|
+
|
|
306
|
+
cards.select("api");
|
|
307
|
+
|
|
308
|
+
await waitForCondition(
|
|
309
|
+
() => JSON.stringify(datasource.data?.[0]?.sources) === '["assets","api"]',
|
|
310
|
+
);
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
it("submits multiple values through form data", function () {
|
|
314
|
+
const form = document.createElement("form");
|
|
315
|
+
const cards = document.createElement("monster-choice-cards");
|
|
316
|
+
cards.setAttribute("name", "sources");
|
|
317
|
+
cards.setOption("multiple", true);
|
|
318
|
+
cards.setItems([
|
|
319
|
+
{ value: "assets", label: "Project assets" },
|
|
320
|
+
{ value: "api", label: "API collection" },
|
|
321
|
+
{ value: "manual", label: "Manual" },
|
|
322
|
+
]);
|
|
323
|
+
|
|
324
|
+
form.appendChild(cards);
|
|
325
|
+
document.getElementById("test").appendChild(form);
|
|
326
|
+
|
|
327
|
+
cards.select("assets");
|
|
328
|
+
cards.select("api");
|
|
329
|
+
|
|
330
|
+
const formData = new window.FormData(form);
|
|
331
|
+
expect(formData.getAll("sources")).deep.equal(["assets::api"]);
|
|
332
|
+
});
|
|
333
|
+
|
|
180
334
|
it("does not select disabled items", function () {
|
|
181
335
|
const cards = document.createElement("monster-choice-cards");
|
|
182
336
|
cards.setItems([
|
|
@@ -189,4 +343,24 @@ describe("ChoiceCards", function () {
|
|
|
189
343
|
|
|
190
344
|
expect(cards.value).is.equal(null);
|
|
191
345
|
});
|
|
346
|
+
|
|
347
|
+
it("does not toggle disabled items in multiple mode", function () {
|
|
348
|
+
const cards = document.createElement("monster-choice-cards");
|
|
349
|
+
cards.setOption("multiple", true);
|
|
350
|
+
cards.setItems([
|
|
351
|
+
{ value: "assets", label: "Project assets" },
|
|
352
|
+
{ value: "api", label: "API collection", disabled: true },
|
|
353
|
+
]);
|
|
354
|
+
|
|
355
|
+
document.getElementById("test").appendChild(cards);
|
|
356
|
+
cards.select("assets");
|
|
357
|
+
cards.select("api");
|
|
358
|
+
|
|
359
|
+
expect(cards.value).deep.equal(["assets"]);
|
|
360
|
+
expect(
|
|
361
|
+
cards.shadowRoot
|
|
362
|
+
.querySelector('[data-choice-value="api"]')
|
|
363
|
+
.getAttribute("aria-checked"),
|
|
364
|
+
).is.equal("false");
|
|
365
|
+
});
|
|
192
366
|
});
|
|
@@ -5,6 +5,7 @@ let expect = chai.expect;
|
|
|
5
5
|
|
|
6
6
|
let resolveClippingBoundaryElement;
|
|
7
7
|
let applyAdaptiveFloatingElementSize;
|
|
8
|
+
let applyFloatingReferenceVisibility;
|
|
8
9
|
let createVisibilityRecoveryConfig;
|
|
9
10
|
let getFloatingVisibleRatio;
|
|
10
11
|
|
|
@@ -19,6 +20,7 @@ describe("form floating-ui boundary resolution", function () {
|
|
|
19
20
|
.then((m) => {
|
|
20
21
|
resolveClippingBoundaryElement = m.resolveClippingBoundaryElement;
|
|
21
22
|
applyAdaptiveFloatingElementSize = m.applyAdaptiveFloatingElementSize;
|
|
23
|
+
applyFloatingReferenceVisibility = m.applyFloatingReferenceVisibility;
|
|
22
24
|
createVisibilityRecoveryConfig = m.createVisibilityRecoveryConfig;
|
|
23
25
|
getFloatingVisibleRatio = m.getFloatingVisibleRatio;
|
|
24
26
|
done();
|
|
@@ -169,7 +171,8 @@ describe("form floating-ui boundary resolution", function () {
|
|
|
169
171
|
|
|
170
172
|
popper.style.maxHeight = "300px";
|
|
171
173
|
content.setAttribute("part", "content");
|
|
172
|
-
content.textContent =
|
|
174
|
+
content.textContent =
|
|
175
|
+
"A long help text that still needs one readable line.";
|
|
173
176
|
content.style.fontSize = "16px";
|
|
174
177
|
content.style.lineHeight = "24px";
|
|
175
178
|
popper.appendChild(content);
|
|
@@ -476,4 +479,48 @@ describe("form floating-ui boundary resolution", function () {
|
|
|
476
479
|
"arrow",
|
|
477
480
|
]);
|
|
478
481
|
});
|
|
482
|
+
|
|
483
|
+
it("should hide a floating element while its reference is clipped", function () {
|
|
484
|
+
const reference = document.createElement("div");
|
|
485
|
+
const popper = document.createElement("div");
|
|
486
|
+
reference.getBoundingClientRect = () => ({
|
|
487
|
+
top: -100,
|
|
488
|
+
left: 10,
|
|
489
|
+
right: 110,
|
|
490
|
+
bottom: -50,
|
|
491
|
+
width: 100,
|
|
492
|
+
height: 50,
|
|
493
|
+
x: 10,
|
|
494
|
+
y: -100,
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
const visible = applyFloatingReferenceVisibility(reference, popper);
|
|
498
|
+
|
|
499
|
+
expect(visible).to.equal(false);
|
|
500
|
+
expect(popper.style.visibility).to.equal("hidden");
|
|
501
|
+
expect(popper.dataset.monsterReferenceHidden).to.equal("true");
|
|
502
|
+
});
|
|
503
|
+
|
|
504
|
+
it("should restore a floating element when its reference is visible again", function () {
|
|
505
|
+
const reference = document.createElement("div");
|
|
506
|
+
const popper = document.createElement("div");
|
|
507
|
+
reference.getBoundingClientRect = () => ({
|
|
508
|
+
top: 10,
|
|
509
|
+
left: 10,
|
|
510
|
+
right: 110,
|
|
511
|
+
bottom: 60,
|
|
512
|
+
width: 100,
|
|
513
|
+
height: 50,
|
|
514
|
+
x: 10,
|
|
515
|
+
y: 10,
|
|
516
|
+
});
|
|
517
|
+
popper.dataset.monsterReferenceHidden = "true";
|
|
518
|
+
popper.style.visibility = "hidden";
|
|
519
|
+
|
|
520
|
+
const visible = applyFloatingReferenceVisibility(reference, popper);
|
|
521
|
+
|
|
522
|
+
expect(visible).to.equal(true);
|
|
523
|
+
expect(popper.style.visibility).to.equal("");
|
|
524
|
+
expect(popper.dataset.monsterReferenceHidden).to.equal(undefined);
|
|
525
|
+
});
|
|
479
526
|
});
|
|
@@ -86,6 +86,17 @@ function waitForCondition(check, {timeout = 4000, interval = 25} = {}) {
|
|
|
86
86
|
});
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
+
function configureRemotePaginatedSelect(select) {
|
|
90
|
+
select.setOption('url', 'https://example.com/items?filter={filter}&page={page}');
|
|
91
|
+
select.setOption('filter.mode', 'remote');
|
|
92
|
+
select.setOption('mapping.selector', 'items.*');
|
|
93
|
+
select.setOption('mapping.labelTemplate', '${name}');
|
|
94
|
+
select.setOption('mapping.valueTemplate', '${id}');
|
|
95
|
+
select.setOption('mapping.total', 'pagination.total');
|
|
96
|
+
select.setOption('mapping.currentPage', 'pagination.page');
|
|
97
|
+
select.setOption('mapping.objectsPerPage', 'pagination.perPage');
|
|
98
|
+
}
|
|
99
|
+
|
|
89
100
|
let Select,
|
|
90
101
|
SelectStyleSheet,
|
|
91
102
|
getDefaultSelectPopperPositionProfile,
|
|
@@ -397,6 +408,46 @@ describe('Select', function () {
|
|
|
397
408
|
}
|
|
398
409
|
});
|
|
399
410
|
|
|
411
|
+
it('should keep the floating layout queue usable after cancelling a running reentrant job', async function () {
|
|
412
|
+
const cancelledPopper = document.createElement('div');
|
|
413
|
+
const nextPopper = document.createElement('div');
|
|
414
|
+
const releasePosition = createDeferred();
|
|
415
|
+
let positionStarted = false;
|
|
416
|
+
let positionFinished = false;
|
|
417
|
+
let nextMutationCount = 0;
|
|
418
|
+
|
|
419
|
+
enqueueFloatingLayout({
|
|
420
|
+
popperElement: cancelledPopper,
|
|
421
|
+
reason: FLOATING_LAYOUT_REASON.POSITION,
|
|
422
|
+
position: async () => {
|
|
423
|
+
positionStarted = true;
|
|
424
|
+
await releasePosition.promise;
|
|
425
|
+
await enqueueFloatingLayout({
|
|
426
|
+
popperElement: cancelledPopper,
|
|
427
|
+
reason: FLOATING_LAYOUT_REASON.SETTLE
|
|
428
|
+
});
|
|
429
|
+
positionFinished = true;
|
|
430
|
+
}
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
await waitForCondition(() => positionStarted === true);
|
|
434
|
+
cancelFloatingLayout(cancelledPopper);
|
|
435
|
+
releasePosition.resolve();
|
|
436
|
+
await waitForCondition(() => positionFinished === true);
|
|
437
|
+
await new Promise(resolve => setTimeout(resolve, 0));
|
|
438
|
+
|
|
439
|
+
enqueueFloatingLayout({
|
|
440
|
+
popperElement: nextPopper,
|
|
441
|
+
reason: FLOATING_LAYOUT_REASON.POSITION,
|
|
442
|
+
mutate: () => {
|
|
443
|
+
nextMutationCount += 1;
|
|
444
|
+
}
|
|
445
|
+
});
|
|
446
|
+
await flushFloatingLayoutQueueForTests();
|
|
447
|
+
|
|
448
|
+
expect(nextMutationCount).to.equal(1);
|
|
449
|
+
});
|
|
450
|
+
|
|
400
451
|
it('should flush reentrant floating layout queue jobs through the watchdog', async function () {
|
|
401
452
|
const originalRequestAnimationFrame = global.requestAnimationFrame;
|
|
402
453
|
const originalCancelAnimationFrame = global.cancelAnimationFrame;
|
|
@@ -738,7 +789,7 @@ describe('Select', function () {
|
|
|
738
789
|
expect(popper.style.display).to.equal('block');
|
|
739
790
|
});
|
|
740
791
|
|
|
741
|
-
it('should allow the popper to become wider than a narrow control', function (
|
|
792
|
+
it('should allow the popper to become wider than a narrow control', async function () {
|
|
742
793
|
const mocks = document.getElementById('mocks');
|
|
743
794
|
const select = document.createElement('monster-select');
|
|
744
795
|
|
|
@@ -764,23 +815,12 @@ describe('Select', function () {
|
|
|
764
815
|
y: 100
|
|
765
816
|
});
|
|
766
817
|
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
expect(popper.dataset.monsterWidthBehavior).to.equal('preferred');
|
|
774
|
-
expect(popper.dataset.monsterPreferredWidth).to.equal('240');
|
|
775
|
-
done();
|
|
776
|
-
} catch (e) {
|
|
777
|
-
done(e);
|
|
778
|
-
}
|
|
779
|
-
}, 80);
|
|
780
|
-
} catch (e) {
|
|
781
|
-
done(e);
|
|
782
|
-
}
|
|
783
|
-
}, 20);
|
|
818
|
+
await new Promise(resolve => setTimeout(resolve, 20));
|
|
819
|
+
shadowRoot.querySelector('[data-monster-role=container]').click();
|
|
820
|
+
await waitForCondition(() => popper.style.minWidth === '240px');
|
|
821
|
+
|
|
822
|
+
expect(popper.dataset.monsterWidthBehavior).to.equal('preferred');
|
|
823
|
+
expect(popper.dataset.monsterPreferredWidth).to.equal('240');
|
|
784
824
|
});
|
|
785
825
|
|
|
786
826
|
it('should use fixed positioning inside a control bar', function (done) {
|
|
@@ -1136,12 +1176,48 @@ describe('Select', function () {
|
|
|
1136
1176
|
]);
|
|
1137
1177
|
});
|
|
1138
1178
|
|
|
1139
|
-
it('should
|
|
1179
|
+
it('should use the reduced visual viewport while a soft keyboard is open', function () {
|
|
1180
|
+
const result = resolveSelectViewportMetrics({
|
|
1181
|
+
layoutWidth: 390,
|
|
1182
|
+
layoutHeight: 844,
|
|
1183
|
+
visualWidth: 390,
|
|
1184
|
+
visualHeight: 480,
|
|
1185
|
+
offsetLeft: 0,
|
|
1186
|
+
offsetTop: 0,
|
|
1187
|
+
padding: 12
|
|
1188
|
+
});
|
|
1189
|
+
|
|
1190
|
+
expect(result.width).to.equal(390);
|
|
1191
|
+
expect(result.height).to.equal(480);
|
|
1192
|
+
expect(result.left).to.equal(0);
|
|
1193
|
+
expect(result.top).to.equal(0);
|
|
1194
|
+
expect(result.padding).to.equal(12);
|
|
1195
|
+
});
|
|
1196
|
+
|
|
1197
|
+
it('should preserve visual viewport offsets for zoomed mobile layouts', function () {
|
|
1198
|
+
const result = resolveSelectViewportMetrics({
|
|
1199
|
+
layoutWidth: 1200,
|
|
1200
|
+
layoutHeight: 900,
|
|
1201
|
+
visualWidth: 600,
|
|
1202
|
+
visualHeight: 450,
|
|
1203
|
+
offsetLeft: 20,
|
|
1204
|
+
offsetTop: 30,
|
|
1205
|
+
padding: 12
|
|
1206
|
+
});
|
|
1207
|
+
|
|
1208
|
+
expect(result.width).to.equal(600);
|
|
1209
|
+
expect(result.height).to.equal(450);
|
|
1210
|
+
expect(result.left).to.equal(20);
|
|
1211
|
+
expect(result.top).to.equal(30);
|
|
1212
|
+
expect(result.padding).to.equal(12);
|
|
1213
|
+
});
|
|
1214
|
+
|
|
1215
|
+
it('should fall back to layout viewport metrics without a visual viewport', function () {
|
|
1140
1216
|
const result = resolveSelectViewportMetrics({
|
|
1141
1217
|
layoutWidth: 1400,
|
|
1142
1218
|
layoutHeight: 900,
|
|
1143
|
-
visualWidth:
|
|
1144
|
-
visualHeight:
|
|
1219
|
+
visualWidth: 0,
|
|
1220
|
+
visualHeight: 0,
|
|
1145
1221
|
offsetLeft: 20,
|
|
1146
1222
|
offsetTop: 30,
|
|
1147
1223
|
padding: 12
|
|
@@ -1149,8 +1225,8 @@ describe('Select', function () {
|
|
|
1149
1225
|
|
|
1150
1226
|
expect(result.width).to.equal(1400);
|
|
1151
1227
|
expect(result.height).to.equal(900);
|
|
1152
|
-
expect(result.left).to.equal(
|
|
1153
|
-
expect(result.top).to.equal(
|
|
1228
|
+
expect(result.left).to.equal(0);
|
|
1229
|
+
expect(result.top).to.equal(0);
|
|
1154
1230
|
expect(result.padding).to.equal(12);
|
|
1155
1231
|
});
|
|
1156
1232
|
|
|
@@ -1487,6 +1563,142 @@ describe('Select', function () {
|
|
|
1487
1563
|
.catch((e) => done(e));
|
|
1488
1564
|
}, 50);
|
|
1489
1565
|
});
|
|
1566
|
+
|
|
1567
|
+
it('should ignore a remote page response that settles after reset', async function () {
|
|
1568
|
+
this.timeout(5000);
|
|
1569
|
+
|
|
1570
|
+
const deferredResponse = createDeferred();
|
|
1571
|
+
let requestStarted = false;
|
|
1572
|
+
global['fetch'] = function () {
|
|
1573
|
+
requestStarted = true;
|
|
1574
|
+
return deferredResponse.promise;
|
|
1575
|
+
};
|
|
1576
|
+
|
|
1577
|
+
const mocks = document.getElementById('mocks');
|
|
1578
|
+
const select = document.createElement('monster-select');
|
|
1579
|
+
configureRemotePaginatedSelect(select);
|
|
1580
|
+
mocks.appendChild(select);
|
|
1581
|
+
|
|
1582
|
+
const request = select.fetch('https://example.com/items?filter=old&page=3');
|
|
1583
|
+
await waitForCondition(() => requestStarted === true);
|
|
1584
|
+
|
|
1585
|
+
select.reset();
|
|
1586
|
+
await waitForCondition(() => {
|
|
1587
|
+
return select.getOption('options').length === 0 && select.getOption('total') === null;
|
|
1588
|
+
});
|
|
1589
|
+
|
|
1590
|
+
deferredResponse.resolve(
|
|
1591
|
+
await createJsonResponse({
|
|
1592
|
+
items: [{id: 'old-3', name: 'Old page 3'}],
|
|
1593
|
+
pagination: {
|
|
1594
|
+
total: 9,
|
|
1595
|
+
page: 3,
|
|
1596
|
+
perPage: 1
|
|
1597
|
+
}
|
|
1598
|
+
})
|
|
1599
|
+
);
|
|
1600
|
+
await request;
|
|
1601
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
1602
|
+
|
|
1603
|
+
const pagination = select.shadowRoot.querySelector('[data-monster-role=pagination]');
|
|
1604
|
+
expect(select.getOption('options')).to.deep.equal([]);
|
|
1605
|
+
expect(select.getOption('total')).to.equal(null);
|
|
1606
|
+
expect(pagination.style.display).to.equal('none');
|
|
1607
|
+
expect(pagination.getOption('currentPage')).to.equal(null);
|
|
1608
|
+
expect(pagination.getOption('pages')).to.equal(null);
|
|
1609
|
+
});
|
|
1610
|
+
|
|
1611
|
+
it('should ignore a remote page response after disconnect and reconnect', async function () {
|
|
1612
|
+
this.timeout(5000);
|
|
1613
|
+
|
|
1614
|
+
const deferredResponse = createDeferred();
|
|
1615
|
+
let requestStarted = false;
|
|
1616
|
+
global['fetch'] = function () {
|
|
1617
|
+
requestStarted = true;
|
|
1618
|
+
return deferredResponse.promise;
|
|
1619
|
+
};
|
|
1620
|
+
|
|
1621
|
+
const mocks = document.getElementById('mocks');
|
|
1622
|
+
const select = document.createElement('monster-select');
|
|
1623
|
+
configureRemotePaginatedSelect(select);
|
|
1624
|
+
mocks.appendChild(select);
|
|
1625
|
+
|
|
1626
|
+
const request = select.fetch('https://example.com/items?filter=old&page=2');
|
|
1627
|
+
await waitForCondition(() => requestStarted === true);
|
|
1628
|
+
|
|
1629
|
+
select.remove();
|
|
1630
|
+
deferredResponse.resolve(
|
|
1631
|
+
await createJsonResponse({
|
|
1632
|
+
items: [{id: 'old-2', name: 'Old page 2'}],
|
|
1633
|
+
pagination: {
|
|
1634
|
+
total: 4,
|
|
1635
|
+
page: 2,
|
|
1636
|
+
perPage: 1
|
|
1637
|
+
}
|
|
1638
|
+
})
|
|
1639
|
+
);
|
|
1640
|
+
await request;
|
|
1641
|
+
mocks.appendChild(select);
|
|
1642
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
1643
|
+
|
|
1644
|
+
const pagination = select.shadowRoot.querySelector('[data-monster-role=pagination]');
|
|
1645
|
+
expect(select.getOption('options')).to.deep.equal([]);
|
|
1646
|
+
expect(select.getOption('total')).to.equal(null);
|
|
1647
|
+
expect(pagination.style.display).to.equal('none');
|
|
1648
|
+
expect(pagination.getOption('currentPage')).to.equal(null);
|
|
1649
|
+
expect(pagination.getOption('pages')).to.equal(null);
|
|
1650
|
+
});
|
|
1651
|
+
|
|
1652
|
+
it('should keep the newest remote page when responses settle out of order', async function () {
|
|
1653
|
+
this.timeout(5000);
|
|
1654
|
+
|
|
1655
|
+
const page2Response = createDeferred();
|
|
1656
|
+
const page3Response = createDeferred();
|
|
1657
|
+
const requests = [];
|
|
1658
|
+
global['fetch'] = function (url) {
|
|
1659
|
+
const requestUrl = String(url);
|
|
1660
|
+
requests.push(requestUrl);
|
|
1661
|
+
return requestUrl.includes('page=2') ? page2Response.promise : page3Response.promise;
|
|
1662
|
+
};
|
|
1663
|
+
|
|
1664
|
+
const mocks = document.getElementById('mocks');
|
|
1665
|
+
const select = document.createElement('monster-select');
|
|
1666
|
+
configureRemotePaginatedSelect(select);
|
|
1667
|
+
mocks.appendChild(select);
|
|
1668
|
+
|
|
1669
|
+
const request2 = select.fetch('https://example.com/items?filter=all&page=2');
|
|
1670
|
+
const request3 = select.fetch('https://example.com/items?filter=all&page=3');
|
|
1671
|
+
await waitForCondition(() => requests.length === 2);
|
|
1672
|
+
|
|
1673
|
+
page3Response.resolve(
|
|
1674
|
+
await createJsonResponse({
|
|
1675
|
+
items: [{id: 'page-3', name: 'Page 3'}],
|
|
1676
|
+
pagination: {
|
|
1677
|
+
total: 3,
|
|
1678
|
+
page: 3,
|
|
1679
|
+
perPage: 1
|
|
1680
|
+
}
|
|
1681
|
+
})
|
|
1682
|
+
);
|
|
1683
|
+
await request3;
|
|
1684
|
+
|
|
1685
|
+
page2Response.resolve(
|
|
1686
|
+
await createJsonResponse({
|
|
1687
|
+
items: [{id: 'page-2', name: 'Page 2'}],
|
|
1688
|
+
pagination: {
|
|
1689
|
+
total: 3,
|
|
1690
|
+
page: 2,
|
|
1691
|
+
perPage: 1
|
|
1692
|
+
}
|
|
1693
|
+
})
|
|
1694
|
+
);
|
|
1695
|
+
await request2;
|
|
1696
|
+
|
|
1697
|
+
const pagination = select.shadowRoot.querySelector('[data-monster-role=pagination]');
|
|
1698
|
+
expect(select.getOption('options').map(option => option.value)).to.deep.equal(['page-3']);
|
|
1699
|
+
expect(pagination.getOption('currentPage')).to.equal(3);
|
|
1700
|
+
expect(pagination.getOption('pages')).to.equal(3);
|
|
1701
|
+
});
|
|
1490
1702
|
});
|
|
1491
1703
|
|
|
1492
1704
|
describe('document.createElement()', function () {
|
|
@@ -2488,6 +2700,47 @@ describe('Select', function () {
|
|
|
2488
2700
|
expect(requests).to.have.length(1);
|
|
2489
2701
|
});
|
|
2490
2702
|
|
|
2703
|
+
it('should cancel a pending remote filter request on reset', async function () {
|
|
2704
|
+
this.timeout(5000);
|
|
2705
|
+
|
|
2706
|
+
const mocks = document.getElementById('mocks');
|
|
2707
|
+
const requests = [];
|
|
2708
|
+
global['fetch'] = function (url) {
|
|
2709
|
+
requests.push(String(url));
|
|
2710
|
+
return createJsonResponse({
|
|
2711
|
+
items: [{id: 'alpha', name: 'Alpha'}],
|
|
2712
|
+
pagination: {
|
|
2713
|
+
total: 1,
|
|
2714
|
+
page: 1,
|
|
2715
|
+
perPage: 1
|
|
2716
|
+
}
|
|
2717
|
+
});
|
|
2718
|
+
};
|
|
2719
|
+
|
|
2720
|
+
const select = document.createElement('monster-select');
|
|
2721
|
+
configureRemotePaginatedSelect(select);
|
|
2722
|
+
select.setOption('filter.position', 'popper');
|
|
2723
|
+
mocks.appendChild(select);
|
|
2724
|
+
|
|
2725
|
+
await waitForCondition(() => {
|
|
2726
|
+
return select.shadowRoot.querySelector('[data-monster-role=filter][name="popper-filter"]') instanceof HTMLInputElement;
|
|
2727
|
+
});
|
|
2728
|
+
|
|
2729
|
+
const filterInput = select.shadowRoot.querySelector('[data-monster-role=filter][name="popper-filter"]');
|
|
2730
|
+
filterInput.value = 'alpha';
|
|
2731
|
+
filterInput.dispatchEvent(new Event('input', {
|
|
2732
|
+
bubbles: true,
|
|
2733
|
+
composed: true
|
|
2734
|
+
}));
|
|
2735
|
+
select.reset();
|
|
2736
|
+
|
|
2737
|
+
await new Promise(resolve => setTimeout(resolve, 260));
|
|
2738
|
+
|
|
2739
|
+
expect(requests).to.deep.equal([]);
|
|
2740
|
+
expect(select.getOption('options')).to.deep.equal([]);
|
|
2741
|
+
expect(select.getOption('total')).to.equal(null);
|
|
2742
|
+
});
|
|
2743
|
+
|
|
2491
2744
|
it('should keep unresolved lookup values visible and mark their badge', function (done) {
|
|
2492
2745
|
this.timeout(3000);
|
|
2493
2746
|
|