@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
|
@@ -117,6 +117,7 @@ const updateCloneDataSymbol = Symbol("@schukai/monster/dom/@@updateCloneData");
|
|
|
117
117
|
* @type {symbol}
|
|
118
118
|
*/
|
|
119
119
|
const scriptHostElementSymbol = Symbol("scriptHostElement");
|
|
120
|
+
const managedShadowRootSymbol = Symbol("managedShadowRoot");
|
|
120
121
|
|
|
121
122
|
/**
|
|
122
123
|
* The `CustomElement` class provides a way to define a new HTML element using the power of Custom Elements.
|
|
@@ -623,7 +624,7 @@ class CustomElement extends HTMLElement {
|
|
|
623
624
|
if (this.getOption("shadowMode", false) !== false) {
|
|
624
625
|
try {
|
|
625
626
|
initShadowRoot.call(this);
|
|
626
|
-
elements = this
|
|
627
|
+
elements = getManagedShadowRoot.call(this)?.childNodes;
|
|
627
628
|
} catch (e) {
|
|
628
629
|
addErrorAttribute(this, e);
|
|
629
630
|
}
|
|
@@ -770,11 +771,12 @@ class CustomElement extends HTMLElement {
|
|
|
770
771
|
return true;
|
|
771
772
|
}
|
|
772
773
|
|
|
773
|
-
|
|
774
|
+
const shadowRoot = getManagedShadowRoot.call(this);
|
|
775
|
+
if (!(shadowRoot instanceof ShadowRoot)) {
|
|
774
776
|
return false;
|
|
775
777
|
}
|
|
776
778
|
|
|
777
|
-
return containChildNode.call(
|
|
779
|
+
return containChildNode.call(shadowRoot, node);
|
|
778
780
|
}
|
|
779
781
|
|
|
780
782
|
/**
|
|
@@ -815,7 +817,7 @@ function callControlCallback(callBackFunctionName, ...args) {
|
|
|
815
817
|
|
|
816
818
|
const list = targetId.split(",");
|
|
817
819
|
for (const id of list) {
|
|
818
|
-
const host = findElementWithIdUpwards(this,
|
|
820
|
+
const host = findElementWithIdUpwards(this, id.trim());
|
|
819
821
|
if (!(host instanceof HTMLElement)) {
|
|
820
822
|
continue;
|
|
821
823
|
}
|
|
@@ -878,8 +880,14 @@ function attachAttributeChangeMutationObserver() {
|
|
|
878
880
|
|
|
879
881
|
self[attributeMutationObserverSymbol] = new MutationObserver(
|
|
880
882
|
function (mutations, observer) {
|
|
883
|
+
const observedAttributes = new Set(
|
|
884
|
+
self.constructor.observedAttributes || [],
|
|
885
|
+
);
|
|
881
886
|
for (const mutation of mutations) {
|
|
882
887
|
if (mutation.type === "attributes") {
|
|
888
|
+
if (observedAttributes.has(mutation.attributeName)) {
|
|
889
|
+
continue;
|
|
890
|
+
}
|
|
883
891
|
self.attributeChangedCallback(
|
|
884
892
|
mutation.attributeName,
|
|
885
893
|
mutation.oldValue,
|
|
@@ -1014,7 +1022,8 @@ function initOptionObserver() {
|
|
|
1014
1022
|
return;
|
|
1015
1023
|
}
|
|
1016
1024
|
|
|
1017
|
-
|
|
1025
|
+
const shadowRoot = getManagedShadowRoot.call(self);
|
|
1026
|
+
if (!(shadowRoot instanceof ShadowRoot) && !self.childNodes.length) {
|
|
1018
1027
|
return;
|
|
1019
1028
|
}
|
|
1020
1029
|
|
|
@@ -1024,8 +1033,8 @@ function initOptionObserver() {
|
|
|
1024
1033
|
"button, command, fieldset, keygen, optgroup, option, select, textarea, input, [data-monster-objectlink]";
|
|
1025
1034
|
|
|
1026
1035
|
let elements = [];
|
|
1027
|
-
if (
|
|
1028
|
-
elements =
|
|
1036
|
+
if (shadowRoot instanceof ShadowRoot) {
|
|
1037
|
+
elements = shadowRoot.querySelectorAll(query);
|
|
1029
1038
|
}
|
|
1030
1039
|
|
|
1031
1040
|
let nodeList;
|
|
@@ -1235,6 +1244,16 @@ function parseOptionsJSON(data) {
|
|
|
1235
1244
|
return validateObject(obj);
|
|
1236
1245
|
}
|
|
1237
1246
|
|
|
1247
|
+
function getManagedShadowRoot() {
|
|
1248
|
+
if (this[managedShadowRootSymbol] instanceof ShadowRoot) {
|
|
1249
|
+
return this[managedShadowRootSymbol];
|
|
1250
|
+
}
|
|
1251
|
+
if (this.shadowRoot instanceof ShadowRoot) {
|
|
1252
|
+
return this.shadowRoot;
|
|
1253
|
+
}
|
|
1254
|
+
return null;
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1238
1257
|
/**
|
|
1239
1258
|
* @private
|
|
1240
1259
|
* @param html
|
|
@@ -1286,7 +1305,8 @@ function initHtmlContent() {
|
|
|
1286
1305
|
* @throws {TypeError} value is not an instance of
|
|
1287
1306
|
*/
|
|
1288
1307
|
function initCSSStylesheet() {
|
|
1289
|
-
|
|
1308
|
+
const shadowRoot = getManagedShadowRoot.call(this);
|
|
1309
|
+
if (!(shadowRoot instanceof ShadowRoot)) {
|
|
1290
1310
|
return this;
|
|
1291
1311
|
}
|
|
1292
1312
|
|
|
@@ -1294,7 +1314,7 @@ function initCSSStylesheet() {
|
|
|
1294
1314
|
|
|
1295
1315
|
if (styleSheet instanceof CSSStyleSheet) {
|
|
1296
1316
|
if (styleSheet.cssRules.length > 0) {
|
|
1297
|
-
|
|
1317
|
+
shadowRoot.adoptedStyleSheets = [styleSheet];
|
|
1298
1318
|
}
|
|
1299
1319
|
} else if (isArray(styleSheet)) {
|
|
1300
1320
|
const assign = [];
|
|
@@ -1304,7 +1324,7 @@ function initCSSStylesheet() {
|
|
|
1304
1324
|
if (trimedStyleSheet !== "") {
|
|
1305
1325
|
const style = document.createElement("style");
|
|
1306
1326
|
style.innerHTML = trimedStyleSheet;
|
|
1307
|
-
|
|
1327
|
+
shadowRoot.prepend(style);
|
|
1308
1328
|
}
|
|
1309
1329
|
continue;
|
|
1310
1330
|
}
|
|
@@ -1317,14 +1337,14 @@ function initCSSStylesheet() {
|
|
|
1317
1337
|
}
|
|
1318
1338
|
|
|
1319
1339
|
if (assign.length > 0) {
|
|
1320
|
-
|
|
1340
|
+
shadowRoot.adoptedStyleSheets = assign;
|
|
1321
1341
|
}
|
|
1322
1342
|
} else if (isString(styleSheet)) {
|
|
1323
1343
|
const trimedStyleSheet = styleSheet.trim();
|
|
1324
1344
|
if (trimedStyleSheet !== "") {
|
|
1325
1345
|
const style = document.createElement("style");
|
|
1326
1346
|
style.innerHTML = styleSheet;
|
|
1327
|
-
|
|
1347
|
+
shadowRoot.prepend(style);
|
|
1328
1348
|
}
|
|
1329
1349
|
}
|
|
1330
1350
|
|
|
@@ -1352,13 +1372,15 @@ function initShadowRoot() {
|
|
|
1352
1372
|
}
|
|
1353
1373
|
}
|
|
1354
1374
|
|
|
1355
|
-
this.attachShadow({
|
|
1375
|
+
this[managedShadowRootSymbol] = this.attachShadow({
|
|
1356
1376
|
mode: this.getOption("shadowMode", "open"),
|
|
1357
1377
|
delegatesFocus: this.getOption("delegatesFocus", true),
|
|
1358
1378
|
});
|
|
1359
1379
|
|
|
1360
1380
|
if (template instanceof Template) {
|
|
1361
|
-
this.
|
|
1381
|
+
this[managedShadowRootSymbol].appendChild(
|
|
1382
|
+
template.createDocumentFragment(),
|
|
1383
|
+
);
|
|
1362
1384
|
return this;
|
|
1363
1385
|
}
|
|
1364
1386
|
|
|
@@ -1374,7 +1396,7 @@ function initShadowRoot() {
|
|
|
1374
1396
|
html = formatter.format(html);
|
|
1375
1397
|
}
|
|
1376
1398
|
|
|
1377
|
-
this.
|
|
1399
|
+
this[managedShadowRootSymbol].innerHTML = substituteI18n.call(this, html);
|
|
1378
1400
|
return this;
|
|
1379
1401
|
}
|
|
1380
1402
|
|
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
|
+
});
|