@schukai/monster 4.128.2 → 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/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
package/source/dom/updater.mjs
CHANGED
|
@@ -88,6 +88,7 @@ const updaterRootSymbol = Symbol.for("@schukai/monster/dom/@@updater-root");
|
|
|
88
88
|
const disposedSymbol = Symbol("disposed");
|
|
89
89
|
const subjectObserverSymbol = Symbol("subjectObserver");
|
|
90
90
|
const patchNodeKeySymbol = Symbol("patchNodeKey");
|
|
91
|
+
const queuedSnapshotSymbol = Symbol("queuedSnapshot");
|
|
91
92
|
|
|
92
93
|
/**
|
|
93
94
|
* The updater class connects an object with the DOM. In this way, structures and contents in the DOM can be
|
|
@@ -155,6 +156,7 @@ class Updater extends Base {
|
|
|
155
156
|
this[pendingDiffsSymbol] = [];
|
|
156
157
|
this[processingSymbol] = false;
|
|
157
158
|
this[disposedSymbol] = false;
|
|
159
|
+
this[queuedSnapshotSymbol] = clone(this[internalSymbol].last);
|
|
158
160
|
|
|
159
161
|
this[subjectObserverSymbol] = new Observer(() => {
|
|
160
162
|
if (this[disposedSymbol] === true) {
|
|
@@ -162,12 +164,13 @@ class Updater extends Base {
|
|
|
162
164
|
}
|
|
163
165
|
|
|
164
166
|
const real = this[internalSymbol].subject.getRealSubject();
|
|
165
|
-
const diffResult = diff(this[
|
|
166
|
-
this[internalSymbol].last = clone(real);
|
|
167
|
+
const diffResult = diff(this[queuedSnapshotSymbol], real);
|
|
167
168
|
if (diffResult.length === 0) {
|
|
168
169
|
return Promise.resolve();
|
|
169
170
|
}
|
|
170
|
-
|
|
171
|
+
const snapshot = clone(real);
|
|
172
|
+
this[queuedSnapshotSymbol] = snapshot;
|
|
173
|
+
this[pendingDiffsSymbol].push({ diffResult, snapshot });
|
|
171
174
|
return this[processQueueSymbol]();
|
|
172
175
|
});
|
|
173
176
|
this[internalSymbol].subject.attachObserver(this[subjectObserverSymbol]);
|
|
@@ -194,7 +197,7 @@ class Updater extends Base {
|
|
|
194
197
|
return Promise.resolve();
|
|
195
198
|
}
|
|
196
199
|
|
|
197
|
-
const diffResult = this[pendingDiffsSymbol].shift();
|
|
200
|
+
const { diffResult, snapshot } = this[pendingDiffsSymbol].shift();
|
|
198
201
|
if (this[internalSymbol].features.batchUpdates === true) {
|
|
199
202
|
const updatePaths = new Map();
|
|
200
203
|
for (const change of Object.values(diffResult)) {
|
|
@@ -225,6 +228,7 @@ class Updater extends Base {
|
|
|
225
228
|
await this[applyChangeSymbol](change);
|
|
226
229
|
}
|
|
227
230
|
}
|
|
231
|
+
this[internalSymbol].last = clone(snapshot);
|
|
228
232
|
}
|
|
229
233
|
} catch (err) {
|
|
230
234
|
addErrorAttribute(this[internalSymbol]?.element, err);
|
|
@@ -371,6 +375,7 @@ class Updater extends Base {
|
|
|
371
375
|
// the key __init__has no further meaning and is only
|
|
372
376
|
// used to create the diff for empty objects.
|
|
373
377
|
this[internalSymbol].last = { __init__: true };
|
|
378
|
+
this[queuedSnapshotSymbol] = clone(this[internalSymbol].last);
|
|
374
379
|
return this[internalSymbol].subject.notifyObservers();
|
|
375
380
|
}
|
|
376
381
|
|
|
@@ -597,12 +602,17 @@ function retrieveAndSetValue(element) {
|
|
|
597
602
|
switch (type) {
|
|
598
603
|
case "integer?":
|
|
599
604
|
case "int?":
|
|
600
|
-
case "number?":
|
|
605
|
+
case "number?": {
|
|
606
|
+
const empty =
|
|
607
|
+
value === undefined ||
|
|
608
|
+
value === null ||
|
|
609
|
+
(isString(value) && value.trim() === "");
|
|
601
610
|
value = Number(value);
|
|
602
|
-
if (
|
|
611
|
+
if (empty || isNaN(value)) {
|
|
603
612
|
value = undefined;
|
|
604
613
|
}
|
|
605
614
|
break;
|
|
615
|
+
}
|
|
606
616
|
|
|
607
617
|
case "number":
|
|
608
618
|
case "int":
|
|
@@ -1055,7 +1065,7 @@ function runUpdateContent(container, parts, subject) {
|
|
|
1055
1065
|
* @type {HTMLElement}
|
|
1056
1066
|
*/
|
|
1057
1067
|
for (const [element] of iterator.entries()) {
|
|
1058
|
-
if (mem.has(element))
|
|
1068
|
+
if (mem.has(element)) continue;
|
|
1059
1069
|
mem.add(element);
|
|
1060
1070
|
|
|
1061
1071
|
const attributes = element.getAttribute(ATTRIBUTE_UPDATER_REPLACE);
|
|
@@ -1113,7 +1123,7 @@ function runUpdatePatch(container, parts, subject) {
|
|
|
1113
1123
|
}
|
|
1114
1124
|
|
|
1115
1125
|
for (const [element] of iterator.entries()) {
|
|
1116
|
-
if (mem.has(element))
|
|
1126
|
+
if (mem.has(element)) continue;
|
|
1117
1127
|
mem.add(element);
|
|
1118
1128
|
|
|
1119
1129
|
const attributes = element.getAttribute(ATTRIBUTE_UPDATER_PATCH);
|
|
@@ -1501,7 +1511,7 @@ function runUpdateAttributes(container, parts, subject) {
|
|
|
1501
1511
|
}
|
|
1502
1512
|
|
|
1503
1513
|
for (const [element] of iterator.entries()) {
|
|
1504
|
-
if (mem.has(element))
|
|
1514
|
+
if (mem.has(element)) continue;
|
|
1505
1515
|
mem.add(element);
|
|
1506
1516
|
|
|
1507
1517
|
// this case occurs when the ATTRIBUTE_UPDATER_SELECT_THIS attribute is set
|
|
@@ -1576,7 +1586,7 @@ function runUpdateProperties(container, parts, subject) {
|
|
|
1576
1586
|
}
|
|
1577
1587
|
|
|
1578
1588
|
for (const [element] of iterator.entries()) {
|
|
1579
|
-
if (mem.has(element))
|
|
1589
|
+
if (mem.has(element)) continue;
|
|
1580
1590
|
mem.add(element);
|
|
1581
1591
|
|
|
1582
1592
|
// this case occurs when the ATTRIBUTE_UPDATER_SELECT_THIS attribute is set
|
|
@@ -1625,14 +1635,24 @@ function handleInputControlAttributeUpdate(element, name, value) {
|
|
|
1625
1635
|
if (element instanceof HTMLSelectElement) {
|
|
1626
1636
|
switch (element.type) {
|
|
1627
1637
|
case "select-multiple":
|
|
1628
|
-
|
|
1629
|
-
|
|
1638
|
+
const selectedValues = isArray(value)
|
|
1639
|
+
? value
|
|
1640
|
+
: value === undefined || value === null
|
|
1641
|
+
? []
|
|
1642
|
+
: [`${value}`];
|
|
1643
|
+
for (const [, opt] of Object.entries(element.options)) {
|
|
1644
|
+
opt.selected = selectedValues.indexOf(opt.value) !== -1;
|
|
1630
1645
|
}
|
|
1631
1646
|
|
|
1632
1647
|
break;
|
|
1633
1648
|
case "select-one":
|
|
1634
1649
|
// Only one value may be selected
|
|
1650
|
+
if (value === undefined || value === null) {
|
|
1651
|
+
element.selectedIndex = -1;
|
|
1652
|
+
break;
|
|
1653
|
+
}
|
|
1635
1654
|
|
|
1655
|
+
element.selectedIndex = -1;
|
|
1636
1656
|
for (const [index, opt] of Object.entries(element.options)) {
|
|
1637
1657
|
if (opt.value === value) {
|
|
1638
1658
|
element.selectedIndex = index;
|
package/source/types/is.mjs
CHANGED
|
@@ -209,22 +209,19 @@ function getHandler() {
|
|
|
209
209
|
return true;
|
|
210
210
|
}
|
|
211
211
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
if (descriptor === undefined) {
|
|
216
|
-
descriptor = {
|
|
217
|
-
writable: true,
|
|
218
|
-
enumerable: true,
|
|
219
|
-
configurable: true,
|
|
220
|
-
};
|
|
212
|
+
const result = Reflect.set(target, key, value, receiver);
|
|
213
|
+
if (result !== true) {
|
|
214
|
+
return result;
|
|
221
215
|
}
|
|
222
216
|
|
|
223
|
-
descriptor["value"] = value;
|
|
224
|
-
result = Reflect.defineProperty(target, key, descriptor);
|
|
225
|
-
|
|
226
217
|
if (typeof key !== "symbol") {
|
|
227
|
-
|
|
218
|
+
let next = Reflect.get(target, key, receiver);
|
|
219
|
+
if (proxy.proxyMap.has(next)) {
|
|
220
|
+
next = proxy.proxyMap.get(next);
|
|
221
|
+
}
|
|
222
|
+
if (next !== current) {
|
|
223
|
+
proxy.observers.notify(proxy);
|
|
224
|
+
}
|
|
228
225
|
}
|
|
229
226
|
|
|
230
227
|
return result;
|
package/source/types/version.mjs
CHANGED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
import * as chai from 'chai';
|
|
4
|
+
import {getDocument} from "../../../../source/dom/util.mjs";
|
|
5
|
+
import {chaiDom} from "../../../util/chai-dom.mjs";
|
|
6
|
+
import {initJSDOM} from "../../../util/jsdom.mjs";
|
|
7
|
+
|
|
8
|
+
let expect = chai.expect;
|
|
9
|
+
chai.use(chaiDom);
|
|
10
|
+
|
|
11
|
+
describe('ImageEditor', function () {
|
|
12
|
+
|
|
13
|
+
let ImageEditor, document;
|
|
14
|
+
let registerCustomElement;
|
|
15
|
+
let CustomElement;
|
|
16
|
+
let assembleMethodSymbol;
|
|
17
|
+
|
|
18
|
+
before(function (done) {
|
|
19
|
+
initJSDOM({}).then(() => {
|
|
20
|
+
import("../../../../source/dom/customelement.mjs").then((domModule) => {
|
|
21
|
+
registerCustomElement = domModule['registerCustomElement'];
|
|
22
|
+
CustomElement = domModule['CustomElement'];
|
|
23
|
+
assembleMethodSymbol = domModule['assembleMethodSymbol'];
|
|
24
|
+
return import("../../../../source/components/content/image-editor.mjs");
|
|
25
|
+
}).then((m) => {
|
|
26
|
+
try {
|
|
27
|
+
ImageEditor = m['ImageEditor'];
|
|
28
|
+
document = getDocument();
|
|
29
|
+
done();
|
|
30
|
+
} catch (e) {
|
|
31
|
+
done(e);
|
|
32
|
+
}
|
|
33
|
+
}).catch((e) => done(e));
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
beforeEach(() => {
|
|
38
|
+
let mocks = document.getElementById('mocks');
|
|
39
|
+
mocks.innerHTML = '<div id="target"></div>';
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
afterEach(() => {
|
|
43
|
+
let mocks = document.getElementById('mocks');
|
|
44
|
+
mocks.innerHTML = '';
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('should not double-call attributeChangedCallback for readonly', function (done) {
|
|
48
|
+
const htmlTAG = 'monster-image-editor-observed-test';
|
|
49
|
+
|
|
50
|
+
class ObservedImageEditor extends ImageEditor {
|
|
51
|
+
static getTag() {
|
|
52
|
+
return htmlTAG;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
get defaults() {
|
|
56
|
+
return Object.assign({}, super.defaults, {
|
|
57
|
+
shadowMode: false,
|
|
58
|
+
templates: {
|
|
59
|
+
main: '<div id="image-editor-test"></div>'
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
[assembleMethodSymbol]() {
|
|
65
|
+
return CustomElement.prototype[assembleMethodSymbol].call(this);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
constructor() {
|
|
69
|
+
super();
|
|
70
|
+
this.attributeChangedCalls = [];
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
attributeChangedCallback(name, oldValue, newValue) {
|
|
74
|
+
this.attributeChangedCalls.push([name, oldValue, newValue]);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
registerCustomElement(ObservedImageEditor);
|
|
79
|
+
|
|
80
|
+
let element = document.createElement(htmlTAG);
|
|
81
|
+
document.getElementById('target').appendChild(element);
|
|
82
|
+
|
|
83
|
+
element.attributeChangedCalls = [];
|
|
84
|
+
element.setAttribute('data-monster-readonly', '');
|
|
85
|
+
|
|
86
|
+
setTimeout(function () {
|
|
87
|
+
try {
|
|
88
|
+
const readonlyCalls = element.attributeChangedCalls.filter(
|
|
89
|
+
([name]) => name === 'data-monster-readonly',
|
|
90
|
+
);
|
|
91
|
+
expect(readonlyCalls).to.have.length(1);
|
|
92
|
+
done();
|
|
93
|
+
} catch (e) {
|
|
94
|
+
done(e);
|
|
95
|
+
}
|
|
96
|
+
}, 20);
|
|
97
|
+
});
|
|
98
|
+
});
|
|
@@ -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
|
|