@schukai/monster 4.128.1 → 4.128.3
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/content/stylesheet/camera-capture.mjs +1 -1
- package/source/components/content/stylesheet/copy.mjs +1 -1
- package/source/components/content/viewer/stylesheet/message.mjs +1 -1
- package/source/components/datatable/columnbar.mjs +30 -3
- package/source/components/datatable/datatable.mjs +26 -1
- package/source/components/datatable/stylesheet/filter-controls-defaults.mjs +1 -1
- package/source/components/form/stylesheet/button-bar.mjs +1 -1
- package/source/components/form/stylesheet/confirm-button.mjs +1 -1
- package/source/components/form/stylesheet/context-error.mjs +1 -1
- package/source/components/form/stylesheet/context-help.mjs +1 -1
- package/source/components/form/stylesheet/digits.mjs +1 -1
- package/source/components/form/stylesheet/field-set.mjs +1 -1
- package/source/components/form/stylesheet/login.mjs +1 -1
- package/source/components/form/stylesheet/popper-button.mjs +1 -1
- package/source/components/form/stylesheet/select.mjs +1 -1
- package/source/components/layout/stylesheet/popper.mjs +1 -1
- package/source/components/style/floating-ui.css +7 -0
- package/source/components/style/floating-ui.pcss +8 -0
- package/source/components/stylesheet/floating-ui.mjs +1 -1
- package/source/dom/customcontrol.mjs +1 -1
- package/source/dom/customelement.mjs +37 -15
- package/source/dom/updater.mjs +32 -12
- package/source/types/is.mjs +3 -0
- package/source/types/proxyobserver.mjs +10 -13
- package/source/types/version.mjs +1 -1
- package/test/cases/components/content/image-editor.mjs +98 -0
- package/test/cases/components/datatable/drag-scroll.mjs +218 -14
- package/test/cases/components/form/select.mjs +70 -32
- package/test/cases/dom/customcontrol.mjs +44 -10
- package/test/cases/dom/customelement-initfromscripthost.mjs +22 -1
- package/test/cases/dom/customelement.mjs +83 -0
- package/test/cases/dom/updater.mjs +98 -0
- package/test/cases/monster.mjs +1 -1
- package/test/cases/types/proxyobserver.mjs +31 -0
- package/test/web/import.js +3 -0
- package/test/web/puppeteer.mjs +94 -71
- package/test/web/test.html +29 -2
- package/test/web/tests.js +25120 -15937
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as chai from "chai";
|
|
2
2
|
import { chaiDom } from "../../../util/chai-dom.mjs";
|
|
3
3
|
import { initJSDOM } from "../../../util/jsdom.mjs";
|
|
4
|
+
import { ResizeObserverMock } from "../../../util/resize-observer.mjs";
|
|
4
5
|
|
|
5
6
|
const expect = chai.expect;
|
|
6
7
|
chai.use(chaiDom);
|
|
@@ -32,6 +33,26 @@ function dispatchPointerEvent(target, type, options = {}) {
|
|
|
32
33
|
return event;
|
|
33
34
|
}
|
|
34
35
|
|
|
36
|
+
function mockScrollableElement(element, { clientWidth, scrollWidth, scrollLeft = 0 }) {
|
|
37
|
+
let currentScrollLeft = scrollLeft;
|
|
38
|
+
|
|
39
|
+
Object.defineProperty(element, "clientWidth", {
|
|
40
|
+
configurable: true,
|
|
41
|
+
value: clientWidth,
|
|
42
|
+
});
|
|
43
|
+
Object.defineProperty(element, "scrollWidth", {
|
|
44
|
+
configurable: true,
|
|
45
|
+
value: scrollWidth,
|
|
46
|
+
});
|
|
47
|
+
Object.defineProperty(element, "scrollLeft", {
|
|
48
|
+
configurable: true,
|
|
49
|
+
get: () => currentScrollLeft,
|
|
50
|
+
set: (value) => {
|
|
51
|
+
currentScrollLeft = value;
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
35
56
|
describe("Datatable drag scroll", function () {
|
|
36
57
|
before(async function () {
|
|
37
58
|
await initJSDOM();
|
|
@@ -68,13 +89,9 @@ describe("Datatable drag scroll", function () {
|
|
|
68
89
|
expect(scroll).to.exist;
|
|
69
90
|
expect(cell).to.exist;
|
|
70
91
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
});
|
|
75
|
-
Object.defineProperty(scroll, "scrollWidth", {
|
|
76
|
-
configurable: true,
|
|
77
|
-
value: 600,
|
|
92
|
+
mockScrollableElement(scroll, {
|
|
93
|
+
clientWidth: 200,
|
|
94
|
+
scrollWidth: 600,
|
|
78
95
|
});
|
|
79
96
|
|
|
80
97
|
scroll.scrollLeft = 120;
|
|
@@ -134,13 +151,9 @@ describe("Datatable drag scroll", function () {
|
|
|
134
151
|
expect(scroll).to.exist;
|
|
135
152
|
expect(button).to.exist;
|
|
136
153
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
});
|
|
141
|
-
Object.defineProperty(scroll, "scrollWidth", {
|
|
142
|
-
configurable: true,
|
|
143
|
-
value: 600,
|
|
154
|
+
mockScrollableElement(scroll, {
|
|
155
|
+
clientWidth: 200,
|
|
156
|
+
scrollWidth: 600,
|
|
144
157
|
});
|
|
145
158
|
|
|
146
159
|
scroll.scrollLeft = 120;
|
|
@@ -255,4 +268,195 @@ describe("Datatable drag scroll", function () {
|
|
|
255
268
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
256
269
|
expect(copied).deep.equal(['"Alpha";"Beta"']);
|
|
257
270
|
});
|
|
271
|
+
|
|
272
|
+
it("defers resize observer grid updates to the next animation frame", async function () {
|
|
273
|
+
const OriginalResizeObserver = window.ResizeObserver;
|
|
274
|
+
const originalGlobalResizeObserver = globalThis.ResizeObserver;
|
|
275
|
+
const originalRequestAnimationFrame = window.requestAnimationFrame;
|
|
276
|
+
const originalGlobalRequestAnimationFrame = globalThis.requestAnimationFrame;
|
|
277
|
+
|
|
278
|
+
class TrackingResizeObserver extends ResizeObserverMock {
|
|
279
|
+
static instances = [];
|
|
280
|
+
|
|
281
|
+
constructor(callback) {
|
|
282
|
+
super(callback);
|
|
283
|
+
TrackingResizeObserver.instances.push(this);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
try {
|
|
288
|
+
window.ResizeObserver = TrackingResizeObserver;
|
|
289
|
+
globalThis.ResizeObserver = TrackingResizeObserver;
|
|
290
|
+
|
|
291
|
+
const mocks = document.getElementById("mocks");
|
|
292
|
+
const wrapper = document.createElement("div");
|
|
293
|
+
let wrapperWidth = 500;
|
|
294
|
+
|
|
295
|
+
Object.defineProperty(wrapper, "clientWidth", {
|
|
296
|
+
configurable: true,
|
|
297
|
+
get: () => wrapperWidth,
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
mocks.appendChild(wrapper);
|
|
301
|
+
|
|
302
|
+
const datatable = document.createElement("monster-datatable");
|
|
303
|
+
datatable.id = "resize-scheduled-table";
|
|
304
|
+
datatable.innerHTML = `
|
|
305
|
+
<template id="resize-scheduled-table-row">
|
|
306
|
+
<div data-monster-head="Name">Alpha</div>
|
|
307
|
+
<div data-monster-head="Value">Beta</div>
|
|
308
|
+
</template>
|
|
309
|
+
`;
|
|
310
|
+
datatable.setOption("responsive.breakpoint", 300);
|
|
311
|
+
datatable.setOption("data", [{}]);
|
|
312
|
+
wrapper.appendChild(datatable);
|
|
313
|
+
|
|
314
|
+
await new Promise((resolve) => setTimeout(resolve, 30));
|
|
315
|
+
|
|
316
|
+
const control = datatable.shadowRoot.querySelector(
|
|
317
|
+
"[data-monster-role=control]",
|
|
318
|
+
);
|
|
319
|
+
expect(control).to.exist;
|
|
320
|
+
expect(control.classList.contains("small")).to.equal(false);
|
|
321
|
+
expect(TrackingResizeObserver.instances).to.have.length.greaterThan(0);
|
|
322
|
+
const tableResizeObserver = TrackingResizeObserver.instances.find(
|
|
323
|
+
(observer) => observer.elements.includes(wrapper),
|
|
324
|
+
);
|
|
325
|
+
expect(tableResizeObserver).to.exist;
|
|
326
|
+
|
|
327
|
+
let scheduledCallback = null;
|
|
328
|
+
window.requestAnimationFrame = (callback) => {
|
|
329
|
+
scheduledCallback = callback;
|
|
330
|
+
return 1;
|
|
331
|
+
};
|
|
332
|
+
globalThis.requestAnimationFrame = window.requestAnimationFrame;
|
|
333
|
+
|
|
334
|
+
wrapperWidth = 200;
|
|
335
|
+
tableResizeObserver.triggerResize([]);
|
|
336
|
+
|
|
337
|
+
expect(scheduledCallback).to.be.a("function");
|
|
338
|
+
expect(control.classList.contains("small")).to.equal(false);
|
|
339
|
+
|
|
340
|
+
scheduledCallback();
|
|
341
|
+
|
|
342
|
+
expect(control.classList.contains("small")).to.equal(true);
|
|
343
|
+
} finally {
|
|
344
|
+
window.ResizeObserver = OriginalResizeObserver;
|
|
345
|
+
globalThis.ResizeObserver = originalGlobalResizeObserver;
|
|
346
|
+
window.requestAnimationFrame = originalRequestAnimationFrame;
|
|
347
|
+
globalThis.requestAnimationFrame = originalGlobalRequestAnimationFrame;
|
|
348
|
+
}
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
it("defers column bar resize updates to the next animation frame", async function () {
|
|
352
|
+
const OriginalResizeObserver = window.ResizeObserver;
|
|
353
|
+
const originalGlobalResizeObserver = globalThis.ResizeObserver;
|
|
354
|
+
const originalRequestAnimationFrame = window.requestAnimationFrame;
|
|
355
|
+
const originalGlobalRequestAnimationFrame = globalThis.requestAnimationFrame;
|
|
356
|
+
|
|
357
|
+
class TrackingResizeObserver extends ResizeObserverMock {
|
|
358
|
+
static instances = [];
|
|
359
|
+
|
|
360
|
+
constructor(callback) {
|
|
361
|
+
super(callback);
|
|
362
|
+
TrackingResizeObserver.instances.push(this);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
try {
|
|
367
|
+
window.ResizeObserver = TrackingResizeObserver;
|
|
368
|
+
globalThis.ResizeObserver = TrackingResizeObserver;
|
|
369
|
+
|
|
370
|
+
const mocks = document.getElementById("mocks");
|
|
371
|
+
const wrapper = document.createElement("div");
|
|
372
|
+
let parentWidth = 400;
|
|
373
|
+
|
|
374
|
+
Object.defineProperty(wrapper, "getBoundingClientRect", {
|
|
375
|
+
configurable: true,
|
|
376
|
+
value: () => ({ width: parentWidth }),
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
mocks.appendChild(wrapper);
|
|
380
|
+
|
|
381
|
+
const columnBar = document.createElement("monster-column-bar");
|
|
382
|
+
columnBar.setOption("columns", [
|
|
383
|
+
{ index: 0, name: "One", visible: true },
|
|
384
|
+
{ index: 1, name: "Two", visible: true },
|
|
385
|
+
{ index: 2, name: "Three", visible: true },
|
|
386
|
+
{ index: 3, name: "Four", visible: true },
|
|
387
|
+
{ index: 4, name: "Five", visible: true },
|
|
388
|
+
{ index: 5, name: "Six", visible: true },
|
|
389
|
+
]);
|
|
390
|
+
wrapper.appendChild(columnBar);
|
|
391
|
+
|
|
392
|
+
await new Promise((resolve) => setTimeout(resolve, 30));
|
|
393
|
+
|
|
394
|
+
const control = columnBar.shadowRoot.querySelector(
|
|
395
|
+
"[data-monster-role=control]",
|
|
396
|
+
);
|
|
397
|
+
const settingsButton = columnBar.shadowRoot.querySelector(
|
|
398
|
+
"[data-monster-role=settings-button]",
|
|
399
|
+
);
|
|
400
|
+
const dotsContainer = columnBar.shadowRoot.querySelector(
|
|
401
|
+
"[data-monster-role=dots]",
|
|
402
|
+
);
|
|
403
|
+
const dots = Array.from(dotsContainer.querySelectorAll("li"));
|
|
404
|
+
|
|
405
|
+
expect(control).to.exist;
|
|
406
|
+
expect(settingsButton).to.exist;
|
|
407
|
+
expect(dots.length).to.equal(6);
|
|
408
|
+
|
|
409
|
+
Object.defineProperty(control, "getBoundingClientRect", {
|
|
410
|
+
configurable: true,
|
|
411
|
+
value: () => ({ width: parentWidth }),
|
|
412
|
+
});
|
|
413
|
+
Object.defineProperty(settingsButton, "getBoundingClientRect", {
|
|
414
|
+
configurable: true,
|
|
415
|
+
value: () => ({ width: 20 }),
|
|
416
|
+
});
|
|
417
|
+
dots.forEach((dot) => {
|
|
418
|
+
Object.defineProperty(dot, "getBoundingClientRect", {
|
|
419
|
+
configurable: true,
|
|
420
|
+
value: () => ({ width: 20 }),
|
|
421
|
+
});
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
425
|
+
|
|
426
|
+
expect(
|
|
427
|
+
dotsContainer.querySelector(".dots-overflow-indicator"),
|
|
428
|
+
).to.equal(null);
|
|
429
|
+
|
|
430
|
+
const columnBarResizeObserver = TrackingResizeObserver.instances.find(
|
|
431
|
+
(observer) => observer.elements.includes(control),
|
|
432
|
+
);
|
|
433
|
+
expect(columnBarResizeObserver).to.exist;
|
|
434
|
+
|
|
435
|
+
let scheduledCallback = null;
|
|
436
|
+
window.requestAnimationFrame = (callback) => {
|
|
437
|
+
scheduledCallback = callback;
|
|
438
|
+
return 1;
|
|
439
|
+
};
|
|
440
|
+
globalThis.requestAnimationFrame = window.requestAnimationFrame;
|
|
441
|
+
|
|
442
|
+
parentWidth = 80;
|
|
443
|
+
columnBarResizeObserver.triggerResize([]);
|
|
444
|
+
|
|
445
|
+
expect(scheduledCallback).to.be.a("function");
|
|
446
|
+
expect(
|
|
447
|
+
dotsContainer.querySelector(".dots-overflow-indicator"),
|
|
448
|
+
).to.equal(null);
|
|
449
|
+
|
|
450
|
+
scheduledCallback();
|
|
451
|
+
|
|
452
|
+
expect(
|
|
453
|
+
dotsContainer.querySelector(".dots-overflow-indicator"),
|
|
454
|
+
).to.exist;
|
|
455
|
+
} finally {
|
|
456
|
+
window.ResizeObserver = OriginalResizeObserver;
|
|
457
|
+
globalThis.ResizeObserver = originalGlobalResizeObserver;
|
|
458
|
+
window.requestAnimationFrame = originalRequestAnimationFrame;
|
|
459
|
+
globalThis.requestAnimationFrame = originalGlobalRequestAnimationFrame;
|
|
460
|
+
}
|
|
461
|
+
});
|
|
258
462
|
});
|
|
@@ -238,7 +238,7 @@ describe('Select', function () {
|
|
|
238
238
|
});
|
|
239
239
|
|
|
240
240
|
it('should reset pagination and clear options on fetch error', function (done) {
|
|
241
|
-
this.timeout(
|
|
241
|
+
this.timeout(5000);
|
|
242
242
|
|
|
243
243
|
let mocks = document.getElementById('mocks');
|
|
244
244
|
const select = document.createElement('monster-select');
|
|
@@ -257,47 +257,85 @@ describe('Select', function () {
|
|
|
257
257
|
const triggerFilter = (value) => {
|
|
258
258
|
const filterInput = select.shadowRoot.querySelector('[data-monster-role=filter]');
|
|
259
259
|
filterInput.value = value;
|
|
260
|
+
filterInput.dispatchEvent(new InputEvent('input', {
|
|
261
|
+
bubbles: true,
|
|
262
|
+
composed: true,
|
|
263
|
+
data: value
|
|
264
|
+
}));
|
|
260
265
|
filterInput.dispatchEvent(new KeyboardEvent('keydown', {
|
|
261
266
|
code: 'KeyA',
|
|
262
|
-
|
|
267
|
+
key: 'a',
|
|
268
|
+
bubbles: true,
|
|
269
|
+
composed: true
|
|
263
270
|
}));
|
|
264
271
|
};
|
|
265
272
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
expect(pagination().getOption('objectsPerPage')).to.equal(null);
|
|
284
|
-
expect(select.getOption('total')).to.equal(null);
|
|
285
|
-
expect(select.getOption('messages.total')).to.equal("");
|
|
286
|
-
} catch (e) {
|
|
287
|
-
return done(e);
|
|
288
|
-
}
|
|
273
|
+
const startedAt = Date.now();
|
|
274
|
+
const pollLoadedState = () => {
|
|
275
|
+
try {
|
|
276
|
+
const options = select.getOption('options');
|
|
277
|
+
const pager = pagination();
|
|
278
|
+
|
|
279
|
+
if (
|
|
280
|
+
options?.length !== 1 ||
|
|
281
|
+
!pager ||
|
|
282
|
+
pager.getOption('currentPage') !== 1 ||
|
|
283
|
+
pager.getOption('pages') !== 2 ||
|
|
284
|
+
pager.getOption('objectsPerPage') !== 1
|
|
285
|
+
) {
|
|
286
|
+
if (Date.now() - startedAt < 3000) {
|
|
287
|
+
return setTimeout(pollLoadedState, 50);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
289
290
|
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
291
|
+
expect(options.length).to.equal(1);
|
|
292
|
+
expect(pager.getOption('currentPage')).to.equal(1);
|
|
293
|
+
expect(pager.getOption('pages')).to.equal(2);
|
|
294
|
+
expect(pager.getOption('objectsPerPage')).to.equal(1);
|
|
295
|
+
|
|
296
|
+
triggerFilter('b');
|
|
297
|
+
setTimeout(pollErrorState, 50);
|
|
298
|
+
} catch (e) {
|
|
299
|
+
done(e);
|
|
300
|
+
}
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
const pollErrorState = () => {
|
|
304
|
+
try {
|
|
305
|
+
const optionsAfterError = select.getOption('options');
|
|
306
|
+
const pager = pagination();
|
|
307
|
+
|
|
308
|
+
if (
|
|
309
|
+
optionsAfterError?.length !== 0 ||
|
|
310
|
+
!pager ||
|
|
311
|
+
pager.getOption('currentPage') !== null ||
|
|
312
|
+
pager.getOption('pages') !== null ||
|
|
313
|
+
pager.getOption('objectsPerPage') !== null ||
|
|
314
|
+
select.getOption('total') !== null ||
|
|
315
|
+
select.getOption('messages.total') !== ""
|
|
316
|
+
) {
|
|
317
|
+
if (Date.now() - startedAt < 4500) {
|
|
318
|
+
return setTimeout(pollErrorState, 50);
|
|
319
|
+
}
|
|
294
320
|
}
|
|
295
|
-
|
|
296
|
-
|
|
321
|
+
|
|
322
|
+
expect(optionsAfterError.length).to.equal(0);
|
|
323
|
+
expect(pager.getOption('currentPage')).to.equal(null);
|
|
324
|
+
expect(pager.getOption('pages')).to.equal(null);
|
|
325
|
+
expect(pager.getOption('objectsPerPage')).to.equal(null);
|
|
326
|
+
expect(select.getOption('total')).to.equal(null);
|
|
327
|
+
expect(select.getOption('messages.total')).to.equal("");
|
|
328
|
+
} catch (e) {
|
|
329
|
+
return done(e);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
done();
|
|
333
|
+
};
|
|
297
334
|
|
|
298
335
|
setTimeout(() => {
|
|
299
336
|
triggerFilter('a');
|
|
300
|
-
|
|
337
|
+
setTimeout(pollLoadedState, 50);
|
|
338
|
+
}, 50);
|
|
301
339
|
});
|
|
302
340
|
});
|
|
303
341
|
|
|
@@ -161,9 +161,9 @@ describe('DOM', function () {
|
|
|
161
161
|
//
|
|
162
162
|
// })
|
|
163
163
|
|
|
164
|
-
|
|
164
|
+
describe('Test ElementInternals', function () {
|
|
165
165
|
|
|
166
|
-
|
|
166
|
+
let form;
|
|
167
167
|
|
|
168
168
|
beforeEach(() => {
|
|
169
169
|
form = document.createElement('form');
|
|
@@ -276,18 +276,52 @@ describe('DOM', function () {
|
|
|
276
276
|
});
|
|
277
277
|
|
|
278
278
|
|
|
279
|
-
|
|
279
|
+
it('setValidity()', function () {
|
|
280
280
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
281
|
+
let d = document.createElement('monster-customcontrol');
|
|
282
|
+
form.appendChild(d);
|
|
283
|
+
expect(d.setValidity({'valueMissing': true}, "my message")).to.be.undefined;
|
|
284
284
|
|
|
285
|
-
|
|
285
|
+
});
|
|
286
286
|
|
|
287
|
+
it('formResetCallback() should restore the value attribute', function () {
|
|
288
|
+
const htmlTAG = 'monster-customcontrol-reset';
|
|
287
289
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
+
class ResetComponent extends CustomControl {
|
|
291
|
+
static getTag() {
|
|
292
|
+
return htmlTAG;
|
|
293
|
+
}
|
|
290
294
|
|
|
291
|
-
|
|
295
|
+
get defaults() {
|
|
296
|
+
return Object.assign({}, super.defaults, {
|
|
297
|
+
shadowMode: false,
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
get value() {
|
|
302
|
+
return this._value ?? '';
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
set value(value) {
|
|
306
|
+
this._value = value;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
292
309
|
|
|
310
|
+
registerCustomElement(ResetComponent);
|
|
311
|
+
|
|
312
|
+
let d = document.createElement(htmlTAG);
|
|
313
|
+
d.setAttribute('value', 'seed');
|
|
314
|
+
d.value = 'changed';
|
|
315
|
+
form.appendChild(d);
|
|
316
|
+
|
|
317
|
+
d.formResetCallback();
|
|
318
|
+
|
|
319
|
+
expect(d.value).to.equal('seed');
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
});
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
});
|
|
293
327
|
|
|
@@ -133,7 +133,28 @@ describe('DOM', function () {
|
|
|
133
133
|
expect(control.hasAttribute('data-monster-error')).is.false;
|
|
134
134
|
|
|
135
135
|
});
|
|
136
|
+
|
|
137
|
+
it('should resolve callback hosts from a comma separated list', function () {
|
|
138
|
+
let mocks = document.getElementById('mocks');
|
|
139
|
+
mocks.innerHTML = `
|
|
140
|
+
<div id="call-back-host-1"></div>
|
|
141
|
+
<div id="call-back-host-2"></div>
|
|
142
|
+
<div id="container"></div>
|
|
143
|
+
`;
|
|
144
|
+
|
|
145
|
+
const container = document.getElementById('call-back-host-2');
|
|
146
|
+
container.initCustomControlCallback = function (control) {
|
|
147
|
+
control.setOption('test', 3);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
let control = document.createElement(randomTagNumber);
|
|
151
|
+
control.setAttribute('data-monster-script-host', "call-back-host-1,call-back-host-2");
|
|
152
|
+
document.getElementById('container').appendChild(control);
|
|
153
|
+
|
|
154
|
+
expect(control.getOption('test')).is.eql(3);
|
|
155
|
+
expect(control.hasAttribute('data-monster-error')).is.false;
|
|
156
|
+
});
|
|
136
157
|
})
|
|
137
158
|
|
|
138
159
|
});
|
|
139
|
-
})
|
|
160
|
+
})
|
|
@@ -321,6 +321,89 @@ describe('DOM', function () {
|
|
|
321
321
|
});
|
|
322
322
|
});
|
|
323
323
|
|
|
324
|
+
describe('shadow root lifecycle', function () {
|
|
325
|
+
it('should support closed shadow roots without adding an error', function (done) {
|
|
326
|
+
const htmlTAG = 'monster-testclass-closed';
|
|
327
|
+
|
|
328
|
+
class ClosedShadowComponent extends CustomElement {
|
|
329
|
+
static getTag() {
|
|
330
|
+
return htmlTAG;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
get defaults() {
|
|
334
|
+
return Object.assign({}, super.defaults, {
|
|
335
|
+
shadowMode: 'closed',
|
|
336
|
+
templates: {
|
|
337
|
+
main: '<span id="inside">ok</span>'
|
|
338
|
+
},
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
registerCustomElement(ClosedShadowComponent);
|
|
344
|
+
|
|
345
|
+
let d = document.createElement(htmlTAG);
|
|
346
|
+
document.getElementById('test1').appendChild(d);
|
|
347
|
+
|
|
348
|
+
setTimeout(function () {
|
|
349
|
+
try {
|
|
350
|
+
expect(d.hasAttribute('data-monster-error')).to.be.false;
|
|
351
|
+
expect(d.shadowRoot).to.equal(null);
|
|
352
|
+
done();
|
|
353
|
+
} catch (e) {
|
|
354
|
+
done(e);
|
|
355
|
+
}
|
|
356
|
+
}, 10);
|
|
357
|
+
});
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
describe('attribute lifecycle', function () {
|
|
361
|
+
it('should not double-call attributeChangedCallback for observed attributes', function (done) {
|
|
362
|
+
const htmlTAG = 'monster-testclass-observed';
|
|
363
|
+
|
|
364
|
+
class ObservedAttributeComponent extends CustomElement {
|
|
365
|
+
static getTag() {
|
|
366
|
+
return htmlTAG;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
static get observedAttributes() {
|
|
370
|
+
return ['data-demo'];
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
get defaults() {
|
|
374
|
+
return Object.assign({}, super.defaults, {
|
|
375
|
+
shadowMode: false,
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
constructor() {
|
|
380
|
+
super();
|
|
381
|
+
this.attributeChangedCalls = [];
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
attributeChangedCallback(attrName, oldVal, newVal) {
|
|
385
|
+
this.attributeChangedCalls.push([attrName, oldVal, newVal]);
|
|
386
|
+
super.attributeChangedCallback(attrName, oldVal, newVal);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
registerCustomElement(ObservedAttributeComponent);
|
|
391
|
+
|
|
392
|
+
let d = document.createElement(htmlTAG);
|
|
393
|
+
document.getElementById('test1').appendChild(d);
|
|
394
|
+
d.setAttribute('data-demo', '1');
|
|
395
|
+
|
|
396
|
+
setTimeout(function () {
|
|
397
|
+
try {
|
|
398
|
+
expect(d.attributeChangedCalls).to.have.length(1);
|
|
399
|
+
done();
|
|
400
|
+
} catch (e) {
|
|
401
|
+
done(e);
|
|
402
|
+
}
|
|
403
|
+
}, 10);
|
|
404
|
+
});
|
|
405
|
+
});
|
|
406
|
+
|
|
324
407
|
describe('Options change', function () {
|
|
325
408
|
|
|
326
409
|
it('delegatesFocus should change from true to false', function () {
|
|
@@ -1907,5 +1907,103 @@ describe("DOM", function () {
|
|
|
1907
1907
|
})
|
|
1908
1908
|
.catch((e) => done(e));
|
|
1909
1909
|
});
|
|
1910
|
+
|
|
1911
|
+
it("should update every parent-path binding when a nested path changes", function (done) {
|
|
1912
|
+
let mocks = document.getElementById("mocks");
|
|
1913
|
+
mocks.innerHTML = `
|
|
1914
|
+
<div id="nested-parent-1" data-monster-replace="path:a | tojson"></div>
|
|
1915
|
+
<div id="nested-leaf" data-monster-replace="path:a.b"></div>
|
|
1916
|
+
<div id="nested-parent-2" data-monster-replace="path:a | tojson"></div>
|
|
1917
|
+
`;
|
|
1918
|
+
|
|
1919
|
+
const updater = new Updater(mocks, { a: { b: "first" } });
|
|
1920
|
+
|
|
1921
|
+
updater
|
|
1922
|
+
.run()
|
|
1923
|
+
.then(() => {
|
|
1924
|
+
updater.getSubject().a.b = "second";
|
|
1925
|
+
|
|
1926
|
+
setTimeout(() => {
|
|
1927
|
+
try {
|
|
1928
|
+
expect(document.getElementById("nested-parent-1").textContent).to.equal(
|
|
1929
|
+
'{"b":"second"}',
|
|
1930
|
+
);
|
|
1931
|
+
expect(document.getElementById("nested-leaf").textContent).to.equal(
|
|
1932
|
+
"second",
|
|
1933
|
+
);
|
|
1934
|
+
expect(document.getElementById("nested-parent-2").textContent).to.equal(
|
|
1935
|
+
'{"b":"second"}',
|
|
1936
|
+
);
|
|
1937
|
+
done();
|
|
1938
|
+
} catch (e) {
|
|
1939
|
+
done(e);
|
|
1940
|
+
}
|
|
1941
|
+
}, 50);
|
|
1942
|
+
})
|
|
1943
|
+
.catch((e) => done(e));
|
|
1944
|
+
});
|
|
1945
|
+
|
|
1946
|
+
it('should keep "0" for optional number bindings', function (done) {
|
|
1947
|
+
let mocks = document.getElementById("mocks");
|
|
1948
|
+
mocks.innerHTML = `
|
|
1949
|
+
<input id="optional-number" data-monster-bind="path:value" data-monster-bind-type="number?">
|
|
1950
|
+
`;
|
|
1951
|
+
|
|
1952
|
+
const updater = new Updater(mocks, {});
|
|
1953
|
+
updater.enableEventProcessing();
|
|
1954
|
+
|
|
1955
|
+
const input = document.getElementById("optional-number");
|
|
1956
|
+
input.value = "0";
|
|
1957
|
+
input.dispatchEvent(new Event("input", { bubbles: true, composed: true }));
|
|
1958
|
+
|
|
1959
|
+
setTimeout(() => {
|
|
1960
|
+
try {
|
|
1961
|
+
expect(updater.getSubject().value).to.equal(0);
|
|
1962
|
+
done();
|
|
1963
|
+
} catch (e) {
|
|
1964
|
+
done(e);
|
|
1965
|
+
}
|
|
1966
|
+
}, 80);
|
|
1967
|
+
});
|
|
1968
|
+
|
|
1969
|
+
it("should clear select attributes when bound values become undefined", function (done) {
|
|
1970
|
+
let mocks = document.getElementById("mocks");
|
|
1971
|
+
mocks.innerHTML = `
|
|
1972
|
+
<select id="select-one" data-monster-attributes="value path:single">
|
|
1973
|
+
<option value="a">a</option>
|
|
1974
|
+
<option value="b">b</option>
|
|
1975
|
+
</select>
|
|
1976
|
+
<select id="select-multiple" multiple data-monster-attributes="value path:multiple">
|
|
1977
|
+
<option value="a">a</option>
|
|
1978
|
+
<option value="b">b</option>
|
|
1979
|
+
</select>
|
|
1980
|
+
`;
|
|
1981
|
+
|
|
1982
|
+
const updater = new Updater(mocks, {
|
|
1983
|
+
single: "b",
|
|
1984
|
+
multiple: ["a"],
|
|
1985
|
+
});
|
|
1986
|
+
|
|
1987
|
+
updater
|
|
1988
|
+
.run()
|
|
1989
|
+
.then(() => {
|
|
1990
|
+
updater.getSubject().single = undefined;
|
|
1991
|
+
updater.getSubject().multiple = undefined;
|
|
1992
|
+
|
|
1993
|
+
setTimeout(() => {
|
|
1994
|
+
try {
|
|
1995
|
+
const selectOne = document.getElementById("select-one");
|
|
1996
|
+
const selectMultiple = document.getElementById("select-multiple");
|
|
1997
|
+
expect(selectOne.selectedIndex).to.equal(-1);
|
|
1998
|
+
expect(Array.from(selectMultiple.selectedOptions)).to.have.length(0);
|
|
1999
|
+
expect(mocks.hasAttribute("data-monster-error")).to.be.false;
|
|
2000
|
+
done();
|
|
2001
|
+
} catch (e) {
|
|
2002
|
+
done(e);
|
|
2003
|
+
}
|
|
2004
|
+
}, 50);
|
|
2005
|
+
})
|
|
2006
|
+
.catch((e) => done(e));
|
|
2007
|
+
});
|
|
1910
2008
|
});
|
|
1911
2009
|
});
|