@schukai/monster 3.44.0 → 3.46.0
Sign up to get free protection for your applications and to get access to all the features.
- package/package.json +1 -1
- package/source/dom/customcontrol.mjs +55 -14
- package/source/dom/customelement.mjs +44 -14
- package/source/dom/util/extract-keys.mjs +39 -0
- package/source/dom/util/init-options-from-attributes.mjs +1 -53
- package/source/dom/util/set-option-from-attribute.mjs +83 -0
- package/source/types/version.mjs +1 -1
- package/test/cases/dom/customcontrol.mjs +48 -41
- package/test/cases/dom/customelement.mjs +6 -6
- package/test/cases/dom/focusmanager.mjs +0 -1
- package/test/cases/monster.mjs +1 -1
- package/test/util/cleanupdom.mjs +0 -4
package/package.json
CHANGED
@@ -5,11 +5,12 @@
|
|
5
5
|
* License text available at https://www.gnu.org/licenses/agpl-3.0.en.html
|
6
6
|
*/
|
7
7
|
|
8
|
-
import {
|
9
|
-
import {
|
10
|
-
import {
|
11
|
-
import {
|
12
|
-
|
8
|
+
import {extend} from "../data/extend.mjs";
|
9
|
+
import {ATTRIBUTE_VALUE} from "./constants.mjs";
|
10
|
+
import {CustomElement, attributeObserverSymbol} from "./customelement.mjs";
|
11
|
+
import {instanceSymbol} from "../constants.mjs";
|
12
|
+
|
13
|
+
export {CustomControl};
|
13
14
|
|
14
15
|
/**
|
15
16
|
* @private
|
@@ -41,6 +42,7 @@ const attachedInternalSymbol = Symbol("attachedInternal");
|
|
41
42
|
* @see {@link https://www.npmjs.com/package/element-internals-polyfill}
|
42
43
|
* @see {@link https://github.com/WICG/webcomponents}
|
43
44
|
* @see {@link https://html.spec.whatwg.org/multipage/custom-elements.html#custom-elements}
|
45
|
+
* @see {@link https://html.spec.whatwg.org/dev/custom-elements.html#custom-element-reactions}
|
44
46
|
* @license AGPLv3
|
45
47
|
* @since 1.14.0
|
46
48
|
* @copyright schukai GmbH
|
@@ -74,7 +76,7 @@ class CustomControl extends CustomElement {
|
|
74
76
|
* @since 2.1.0
|
75
77
|
*/
|
76
78
|
static get [instanceSymbol]() {
|
77
|
-
return Symbol.for("@schukai/monster/dom/custom-control");
|
79
|
+
return Symbol.for("@schukai/monster/dom/custom-control@@instance");
|
78
80
|
}
|
79
81
|
|
80
82
|
/**
|
@@ -84,20 +86,18 @@ class CustomControl extends CustomElement {
|
|
84
86
|
* @since 1.15.0
|
85
87
|
*/
|
86
88
|
static get observedAttributes() {
|
87
|
-
|
88
|
-
list.push(ATTRIBUTE_VALUE);
|
89
|
-
return list;
|
89
|
+
return super.observedAttributes;
|
90
90
|
}
|
91
91
|
|
92
92
|
/**
|
93
|
+
* Adding a static formAssociated property, with a true value, makes an autonomous custom element a form-associated custom element.
|
93
94
|
*
|
94
95
|
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/attachInternals}
|
96
|
+
* @see {@link https://html.spec.whatwg.org/multipage/custom-elements.html#custom-elements-face-example}
|
95
97
|
* @since 1.14.0
|
96
98
|
* @return {boolean}
|
97
99
|
*/
|
98
|
-
static
|
99
|
-
return true;
|
100
|
-
}
|
100
|
+
static formAssociated = true
|
101
101
|
|
102
102
|
/**
|
103
103
|
* Derived classes can override and extend this method as follows.
|
@@ -116,7 +116,8 @@ class CustomControl extends CustomElement {
|
|
116
116
|
* @since 1.14.0
|
117
117
|
*/
|
118
118
|
get defaults() {
|
119
|
-
return extend({
|
119
|
+
return extend({
|
120
|
+
}, super.defaults);
|
120
121
|
}
|
121
122
|
|
122
123
|
/**
|
@@ -298,6 +299,46 @@ class CustomControl extends CustomElement {
|
|
298
299
|
reportValidity() {
|
299
300
|
return getInternal.call(this)?.reportValidity();
|
300
301
|
}
|
302
|
+
|
303
|
+
/**
|
304
|
+
* @param {string} form
|
305
|
+
*/
|
306
|
+
formAssociatedCallback(form) {
|
307
|
+
if (form) {
|
308
|
+
if(form.id) {
|
309
|
+
this.setAttribute("form", form.id);
|
310
|
+
}
|
311
|
+
} else {
|
312
|
+
this.removeAttribute("form");
|
313
|
+
}
|
314
|
+
}
|
315
|
+
|
316
|
+
/**
|
317
|
+
* @param {string} disabled
|
318
|
+
*/
|
319
|
+
formDisabledCallback(disabled) {
|
320
|
+
if (disabled) {
|
321
|
+
this.setAttribute("disabled", "");
|
322
|
+
} else {
|
323
|
+
this.removeAttribute("disabled");
|
324
|
+
}
|
325
|
+
}
|
326
|
+
|
327
|
+
/**
|
328
|
+
* @param {string} state
|
329
|
+
* @param {string} mode
|
330
|
+
*/
|
331
|
+
formStateRestoreCallback(state, mode) {
|
332
|
+
|
333
|
+
}
|
334
|
+
|
335
|
+
/**
|
336
|
+
*
|
337
|
+
*/
|
338
|
+
formResetCallback() {
|
339
|
+
this.value = "";
|
340
|
+
}
|
341
|
+
|
301
342
|
}
|
302
343
|
|
303
344
|
/**
|
@@ -313,7 +354,7 @@ function getInternal() {
|
|
313
354
|
throw new Error("ElementInternals is not supported and a polyfill is necessary");
|
314
355
|
}
|
315
356
|
|
316
|
-
return
|
357
|
+
return self[attachedInternalSymbol];
|
317
358
|
}
|
318
359
|
|
319
360
|
/**
|
@@ -31,6 +31,7 @@ import {instanceSymbol} from "../constants.mjs";
|
|
31
31
|
import {getDocumentTranslations, Translations} from "../i18n/translations.mjs";
|
32
32
|
import {getSlottedElements} from "./slotted.mjs";
|
33
33
|
import {initOptionsFromAttributes} from "./util/init-options-from-attributes.mjs";
|
34
|
+
import {setOptionFromAttribute} from "./util/set-option-from-attribute.mjs";
|
34
35
|
|
35
36
|
export {
|
36
37
|
CustomElement,
|
@@ -60,6 +61,12 @@ const assembleMethodSymbol = Symbol.for("@schukai/monster/dom/@@assembleMethodSy
|
|
60
61
|
*/
|
61
62
|
const attributeObserverSymbol = Symbol.for("@schukai/monster/dom/@@attributeObserver");
|
62
63
|
|
64
|
+
/**
|
65
|
+
* @private
|
66
|
+
* @type {symbol}
|
67
|
+
*/
|
68
|
+
const attributeMutationObserverSymbol = Symbol("@schukai/monster/dom/@@mutationObserver");
|
69
|
+
|
63
70
|
/**
|
64
71
|
* HTMLElement
|
65
72
|
* @external HTMLElement
|
@@ -205,7 +212,7 @@ class CustomElement extends HTMLElement {
|
|
205
212
|
});
|
206
213
|
this[initMethodSymbol]();
|
207
214
|
initOptionObserver.call(this);
|
208
|
-
|
215
|
+
|
209
216
|
}
|
210
217
|
|
211
218
|
/**
|
@@ -214,7 +221,7 @@ class CustomElement extends HTMLElement {
|
|
214
221
|
* @since 2.1.0
|
215
222
|
*/
|
216
223
|
static get [instanceSymbol]() {
|
217
|
-
return Symbol.for("@schukai/monster/dom/custom-element");
|
224
|
+
return Symbol.for("@schukai/monster/dom/custom-element@@instance");
|
218
225
|
}
|
219
226
|
|
220
227
|
/**
|
@@ -226,11 +233,11 @@ class CustomElement extends HTMLElement {
|
|
226
233
|
* @since 1.15.0
|
227
234
|
*/
|
228
235
|
static get observedAttributes() {
|
229
|
-
return [
|
236
|
+
return [];
|
230
237
|
}
|
231
238
|
|
232
239
|
/**
|
233
|
-
*
|
240
|
+
*
|
234
241
|
* @param attribute
|
235
242
|
* @param callback
|
236
243
|
* @returns {Monster.DOM.CustomElement}
|
@@ -242,7 +249,7 @@ class CustomElement extends HTMLElement {
|
|
242
249
|
}
|
243
250
|
|
244
251
|
/**
|
245
|
-
*
|
252
|
+
*
|
246
253
|
* @param attribute
|
247
254
|
* @returns {Monster.DOM.CustomElement}
|
248
255
|
*/
|
@@ -303,7 +310,7 @@ class CustomElement extends HTMLElement {
|
|
303
310
|
*/
|
304
311
|
get defaults() {
|
305
312
|
return {
|
306
|
-
|
313
|
+
disabled: false,
|
307
314
|
shadowMode: "open",
|
308
315
|
delegatesFocus: true,
|
309
316
|
templates: {
|
@@ -546,6 +553,9 @@ class CustomElement extends HTMLElement {
|
|
546
553
|
customElementUpdaterLinkSymbol,
|
547
554
|
clone(self[internalSymbol].getRealSubject()["options"]),
|
548
555
|
);
|
556
|
+
|
557
|
+
attachAttributeChangeMutationObserver.call(this);
|
558
|
+
|
549
559
|
return self;
|
550
560
|
}
|
551
561
|
|
@@ -561,6 +571,7 @@ class CustomElement extends HTMLElement {
|
|
561
571
|
if (!hasObjectLink(self, customElementUpdaterLinkSymbol)) {
|
562
572
|
self[assembleMethodSymbol]();
|
563
573
|
}
|
574
|
+
|
564
575
|
}
|
565
576
|
|
566
577
|
/**
|
@@ -595,10 +606,18 @@ class CustomElement extends HTMLElement {
|
|
595
606
|
attributeChangedCallback(attrName, oldVal, newVal) {
|
596
607
|
const self = this;
|
597
608
|
|
598
|
-
|
609
|
+
if (attrName.startsWith("data-monster-option-")) {
|
610
|
+
setOptionFromAttribute(self, attrName, this[internalSymbol].getSubject()["options"])
|
611
|
+
}
|
599
612
|
|
613
|
+
const callback = self[attributeObserverSymbol]?.[attrName];
|
600
614
|
if (isFunction(callback)) {
|
601
|
-
|
615
|
+
try {
|
616
|
+
callback.call(self, newVal, oldVal);
|
617
|
+
} catch (e) {
|
618
|
+
addAttributeToken(self, ATTRIBUTE_ERRORMESSAGE, e.toString());
|
619
|
+
}
|
620
|
+
|
602
621
|
}
|
603
622
|
}
|
604
623
|
|
@@ -626,23 +645,34 @@ class CustomElement extends HTMLElement {
|
|
626
645
|
|
627
646
|
/**
|
628
647
|
* This method is called when the element is first created.
|
629
|
-
*
|
648
|
+
*
|
630
649
|
* @private
|
631
650
|
* @this CustomElement
|
632
651
|
*/
|
633
|
-
function
|
652
|
+
function attachAttributeChangeMutationObserver() {
|
634
653
|
const self = this;
|
635
654
|
|
636
|
-
|
655
|
+
if (typeof self[attributeMutationObserverSymbol] !== "undefined") {
|
656
|
+
return;
|
657
|
+
}
|
658
|
+
|
659
|
+
self[attributeMutationObserverSymbol] = new MutationObserver(function (mutations, observer) {
|
637
660
|
for (const mutation of mutations) {
|
638
661
|
if (mutation.type === "attributes") {
|
639
662
|
self.attributeChangedCallback(mutation.attributeName, mutation.oldValue, mutation.target.getAttribute(mutation.attributeName));
|
640
663
|
}
|
641
664
|
}
|
642
|
-
}).observe(self, {
|
643
|
-
attributes: true,
|
644
|
-
attributeOldValue: true,
|
645
665
|
});
|
666
|
+
|
667
|
+
try {
|
668
|
+
self[attributeMutationObserverSymbol].observe(self, {
|
669
|
+
attributes: true,
|
670
|
+
attributeOldValue: true,
|
671
|
+
});
|
672
|
+
|
673
|
+
} catch (e) {
|
674
|
+
addAttributeToken(self, ATTRIBUTE_ERRORMESSAGE, e.toString());
|
675
|
+
}
|
646
676
|
}
|
647
677
|
|
648
678
|
/**
|
@@ -0,0 +1,39 @@
|
|
1
|
+
/**
|
2
|
+
* Copyright schukai GmbH and contributors 2023. All Rights Reserved.
|
3
|
+
* Node module: @schukai/monster
|
4
|
+
* This file is licensed under the AGPLv3 License.
|
5
|
+
* License text available at https://www.gnu.org/licenses/agpl-3.0.en.html
|
6
|
+
*/
|
7
|
+
|
8
|
+
export {extractKeys}
|
9
|
+
|
10
|
+
/**
|
11
|
+
* Extracts the keys from the given object and returns a map with the keys and values.
|
12
|
+
*
|
13
|
+
* @private
|
14
|
+
* @param {object} obj
|
15
|
+
* @param {string} keyPrefix
|
16
|
+
* @param {string} keySeparator
|
17
|
+
* @param {string} valueSeparator
|
18
|
+
* @returns {Map<any, any>}
|
19
|
+
*/
|
20
|
+
function extractKeys(obj, keyPrefix = '', keySeparator = '-', valueSeparator = '.') {
|
21
|
+
const resultMap = new Map();
|
22
|
+
|
23
|
+
function helper(currentObj, currentKeyPrefix, currentValuePrefix) {
|
24
|
+
for (const key in currentObj) {
|
25
|
+
if (typeof currentObj[key] === 'object' && !Array.isArray(currentObj[key])) {
|
26
|
+
const newKeyPrefix = currentKeyPrefix ? currentKeyPrefix + keySeparator + key.toLowerCase() : key.toLowerCase();
|
27
|
+
const newValuePrefix = currentValuePrefix ? currentValuePrefix + valueSeparator + key : key;
|
28
|
+
helper(currentObj[key], newKeyPrefix, newValuePrefix);
|
29
|
+
} else {
|
30
|
+
const finalKey = currentKeyPrefix ? currentKeyPrefix + keySeparator + key.toLowerCase() : key.toLowerCase();
|
31
|
+
const finalValue = currentValuePrefix ? currentValuePrefix + valueSeparator + key : key;
|
32
|
+
resultMap.set(finalKey, finalValue);
|
33
|
+
}
|
34
|
+
}
|
35
|
+
}
|
36
|
+
|
37
|
+
helper(obj, keyPrefix, keyPrefix);
|
38
|
+
return resultMap;
|
39
|
+
}
|
@@ -8,6 +8,7 @@
|
|
8
8
|
import {Pathfinder} from '../../data/pathfinder.mjs';
|
9
9
|
import {isFunction} from '../../types/is.mjs';
|
10
10
|
import {attributeObserverSymbol} from "../customelement.mjs";
|
11
|
+
import {extractKeys} from "./extract-keys.mjs";
|
11
12
|
|
12
13
|
export {initOptionsFromAttributes};
|
13
14
|
|
@@ -75,63 +76,10 @@ function initOptionsFromAttributes(element, options, mapping = {}, prefix = 'dat
|
|
75
76
|
}
|
76
77
|
|
77
78
|
finder.setVia(optionName, value);
|
78
|
-
|
79
|
-
// if element has an attribute observer, then register the attribute observer
|
80
|
-
if (element?.[attributeObserverSymbol]) {
|
81
|
-
element[attributeObserverSymbol][name] = (newValue, oldValue) => {
|
82
|
-
|
83
|
-
if (newValue === oldValue) return;
|
84
|
-
|
85
|
-
let changedValue = newValue;
|
86
|
-
|
87
|
-
if (typeOfOptionValue === 'boolean') {
|
88
|
-
changedValue = changedValue === 'true';
|
89
|
-
} else if (typeOfOptionValue === 'number') {
|
90
|
-
changedValue = Number(changedValue);
|
91
|
-
} else if (typeOfOptionValue === 'string') {
|
92
|
-
changedValue = String(changedValue);
|
93
|
-
} else if (typeOfOptionValue === 'object') {
|
94
|
-
changedValue = JSON.parse(changedValue);
|
95
|
-
}
|
96
|
-
|
97
|
-
finder.setVia(optionName, changedValue);
|
98
|
-
}
|
99
|
-
}
|
100
|
-
|
101
|
-
|
102
79
|
}
|
103
80
|
})
|
104
81
|
|
105
82
|
return options;
|
106
83
|
}
|
107
84
|
|
108
|
-
/**
|
109
|
-
* Extracts the keys from the given object and returns a map with the keys and values.
|
110
|
-
*
|
111
|
-
* @private
|
112
|
-
* @param {object} obj
|
113
|
-
* @param {string} keyPrefix
|
114
|
-
* @param {string} keySeparator
|
115
|
-
* @param {string} valueSeparator
|
116
|
-
* @returns {Map<any, any>}
|
117
|
-
*/
|
118
|
-
function extractKeys(obj, keyPrefix = '', keySeparator = '-', valueSeparator = '.') {
|
119
|
-
const resultMap = new Map();
|
120
85
|
|
121
|
-
function helper(currentObj, currentKeyPrefix, currentValuePrefix) {
|
122
|
-
for (const key in currentObj) {
|
123
|
-
if (typeof currentObj[key] === 'object' && !Array.isArray(currentObj[key])) {
|
124
|
-
const newKeyPrefix = currentKeyPrefix ? currentKeyPrefix + keySeparator + key.toLowerCase() : key.toLowerCase();
|
125
|
-
const newValuePrefix = currentValuePrefix ? currentValuePrefix + valueSeparator + key : key;
|
126
|
-
helper(currentObj[key], newKeyPrefix, newValuePrefix);
|
127
|
-
} else {
|
128
|
-
const finalKey = currentKeyPrefix ? currentKeyPrefix + keySeparator + key.toLowerCase() : key.toLowerCase();
|
129
|
-
const finalValue = currentValuePrefix ? currentValuePrefix + valueSeparator + key : key;
|
130
|
-
resultMap.set(finalKey, finalValue);
|
131
|
-
}
|
132
|
-
}
|
133
|
-
}
|
134
|
-
|
135
|
-
helper(obj, keyPrefix, keyPrefix);
|
136
|
-
return resultMap;
|
137
|
-
}
|
@@ -0,0 +1,83 @@
|
|
1
|
+
/**
|
2
|
+
* Copyright schukai GmbH and contributors 2023. All Rights Reserved.
|
3
|
+
* Node module: @schukai/monster
|
4
|
+
* This file is licensed under the AGPLv3 License.
|
5
|
+
* License text available at https://www.gnu.org/licenses/agpl-3.0.en.html
|
6
|
+
*/
|
7
|
+
|
8
|
+
import {Pathfinder} from '../../data/pathfinder.mjs';
|
9
|
+
import {isFunction} from '../../types/is.mjs';
|
10
|
+
import {attributeObserverSymbol} from "../customelement.mjs";
|
11
|
+
import {extractKeys} from "./extract-keys.mjs";
|
12
|
+
|
13
|
+
export {setOptionFromAttribute};
|
14
|
+
|
15
|
+
/**
|
16
|
+
* Set the given options object based on the attributes of the current DOM element.
|
17
|
+
* The function looks for attributes with the prefix 'data-monster-option-', and maps them to
|
18
|
+
* properties in the options object. It replaces the dashes with dots to form the property path.
|
19
|
+
* For example, the attribute 'data-monster-option-url' maps to the 'url' property in the options object.
|
20
|
+
*
|
21
|
+
* With the mapping parameter, the attribute value can be mapped to a different value.
|
22
|
+
* For example, the attribute 'data-monster-option-foo' maps to the 'bar' property in the options object.
|
23
|
+
*
|
24
|
+
* The mapping object would look like this:
|
25
|
+
* {
|
26
|
+
* 'foo': (value) => value + 'bar'
|
27
|
+
* // the value of the attribute 'data-monster-option-foo' is appended with 'bar'
|
28
|
+
* // and assigned to the 'bar' property in the options object.
|
29
|
+
* // e.g. <div data-monster-option-foo="foo"></div>
|
30
|
+
* 'bar.baz': (value) => value + 'bar'
|
31
|
+
* // the value of the attribute 'data-monster-option-bar-baz' is appended with 'bar'
|
32
|
+
* // and assigned to the 'bar.baz' property in the options object.
|
33
|
+
* // e.g. <div data-monster-option-bar-baz="foo"></div>
|
34
|
+
* }
|
35
|
+
*
|
36
|
+
* @since 3.45.0
|
37
|
+
* @param {HTMLElement} element - The DOM element to be used as the source of the attributes.
|
38
|
+
* @param {Object} name - The attribute object to be used as the source of the attributes.
|
39
|
+
* @param {Object} options - The options object to be initialized.
|
40
|
+
* @param {Object} mapping - A mapping between the attribute value and the property value.
|
41
|
+
* @param {string} prefix - The prefix of the attributes to be considered.
|
42
|
+
* @returns {Object} - The initialized options object.
|
43
|
+
* @this HTMLElement - The context of the DOM element.
|
44
|
+
*/
|
45
|
+
function setOptionFromAttribute(element, name, options, mapping = {}, prefix = 'data-monster-option-') {
|
46
|
+
if (!(element instanceof HTMLElement)) return options;
|
47
|
+
if (!element.hasAttributes()) return options;
|
48
|
+
|
49
|
+
const keyMap = extractKeys(options);
|
50
|
+
const finder = new Pathfinder(options);
|
51
|
+
|
52
|
+
// check if the attribute name is a valid option.
|
53
|
+
// the mapping between the attribute is simple. The dash is replaced by a dot.
|
54
|
+
// e.g. data-monster-url => url
|
55
|
+
const optionName = keyMap.get(name.substring(prefix.length).toLowerCase());
|
56
|
+
if (!finder.exists(optionName)) return;
|
57
|
+
|
58
|
+
if (!element.hasAttribute(name)) {
|
59
|
+
return options;
|
60
|
+
}
|
61
|
+
|
62
|
+
let value = element.getAttribute(name);
|
63
|
+
if (mapping.hasOwnProperty(optionName) && isFunction(mapping[optionName])) {
|
64
|
+
value = mapping[optionName](value);
|
65
|
+
}
|
66
|
+
|
67
|
+
const typeOfOptionValue = typeof finder.getVia(optionName);
|
68
|
+
if (typeOfOptionValue === 'boolean') {
|
69
|
+
value = value === 'true';
|
70
|
+
} else if (typeOfOptionValue === 'number') {
|
71
|
+
value = Number(value);
|
72
|
+
} else if (typeOfOptionValue === 'string') {
|
73
|
+
value = String(value);
|
74
|
+
} else if (typeOfOptionValue === 'object') {
|
75
|
+
value = JSON.parse(value);
|
76
|
+
}
|
77
|
+
|
78
|
+
finder.setVia(optionName, value);
|
79
|
+
|
80
|
+
return options;
|
81
|
+
}
|
82
|
+
|
83
|
+
|
package/source/types/version.mjs
CHANGED
@@ -4,9 +4,9 @@ import chai from "chai"
|
|
4
4
|
import {ATTRIBUTE_OPTIONS} from "../../../../application/source/dom/constants.mjs";
|
5
5
|
import {getDocument} from "../../../../application/source/dom/util.mjs";
|
6
6
|
import {chaiDom} from "../../util/chai-dom.mjs";
|
7
|
+
import {cleanupDOMFromTesting, initMutationObserverForTesting} from "../../util/cleanupdom.mjs";
|
7
8
|
import {initJSDOM} from "../../util/jsdom.mjs";
|
8
9
|
|
9
|
-
|
10
10
|
let expect = chai.expect;
|
11
11
|
chai.use(chaiDom);
|
12
12
|
|
@@ -19,45 +19,46 @@ describe('DOM', function () {
|
|
19
19
|
|
20
20
|
let CustomControl, registerCustomElement, TestComponent, document, jsdomFlag;
|
21
21
|
|
22
|
-
|
23
|
-
|
24
|
-
before(function (done) {
|
25
|
-
initJSDOM().then(() => {
|
22
|
+
before(function (done) {
|
23
|
+
initJSDOM().then(() => {
|
26
24
|
|
27
|
-
|
28
|
-
|
25
|
+
// jsdom does not support ElementInternals
|
26
|
+
jsdomFlag = navigator.userAgent.includes("jsdom");
|
29
27
|
|
30
|
-
|
31
|
-
|
28
|
+
import("../../../../application/source/dom/customelement.mjs").then((m) => {
|
29
|
+
registerCustomElement = m['registerCustomElement'];
|
32
30
|
|
33
31
|
|
34
|
-
|
32
|
+
import("../../../../application/source/dom/customcontrol.mjs").then((m) => {
|
35
33
|
|
36
|
-
|
34
|
+
document = getDocument();
|
37
35
|
|
38
|
-
|
39
|
-
|
36
|
+
try {
|
37
|
+
CustomControl = m['CustomControl'];
|
40
38
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
}
|
39
|
+
TestComponent = class extends CustomControl {
|
40
|
+
static getTag() {
|
41
|
+
return "monster-customcontrol"
|
45
42
|
}
|
46
|
-
|
43
|
+
}
|
44
|
+
registerCustomElement(TestComponent)
|
47
45
|
|
48
46
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
47
|
+
done()
|
48
|
+
} catch (e) {
|
49
|
+
done(e);
|
50
|
+
}
|
53
51
|
|
54
52
|
|
55
|
-
});
|
56
53
|
});
|
57
54
|
});
|
58
|
-
})
|
55
|
+
});
|
56
|
+
})
|
57
|
+
|
58
|
+
describe('CustomControl()', function () {
|
59
59
|
|
60
60
|
beforeEach(() => {
|
61
|
+
initMutationObserverForTesting()
|
61
62
|
let mocks = document.getElementById('mocks');
|
62
63
|
mocks.innerHTML = html1;
|
63
64
|
})
|
@@ -65,6 +66,8 @@ describe('DOM', function () {
|
|
65
66
|
afterEach(() => {
|
66
67
|
let mocks = document.getElementById('mocks');
|
67
68
|
mocks.innerHTML = "";
|
69
|
+
cleanupDOMFromTesting();
|
70
|
+
|
68
71
|
})
|
69
72
|
|
70
73
|
describe('create', function () {
|
@@ -76,28 +79,33 @@ describe('DOM', function () {
|
|
76
79
|
|
77
80
|
describe('connect empty element', function () {
|
78
81
|
it('document should contain monster-customcontrol', function () {
|
79
|
-
|
82
|
+
|
80
83
|
let d = document.createElement('monster-customcontrol');
|
81
84
|
document.getElementById('test1').appendChild(d);
|
82
85
|
expect(document.getElementsByTagName('monster-customcontrol').length).is.equal(1);
|
83
|
-
// no data-monster-objectlink="Symbol(monsterUpdater)" because it has nothing to update
|
86
|
+
// no data-monster-objectlink="Symbol(monsterUpdater)" because it has nothing to update
|
84
87
|
expect(document.getElementById('test1')).contain.html('<monster-customcontrol></monster-customcontrol>');
|
85
88
|
});
|
86
89
|
});
|
87
90
|
|
88
|
-
describe('Options change', function () {
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
})
|
91
|
+
// describe('Options change', function () {
|
92
|
+
//
|
93
|
+
// it('delegatesFocus should change from true to false', function (done) {
|
94
|
+
// let element = document.createElement('monster-customcontrol')
|
95
|
+
//
|
96
|
+
// expect(element.getOption('delegatesFocus')).to.be.true;
|
97
|
+
// setTimeout(() => {
|
98
|
+
// element.setAttribute(ATTRIBUTE_OPTIONS, JSON.stringify({delegatesFocus: false}));
|
99
|
+
// setTimeout(() => {
|
100
|
+
// expect(element.getOption('delegatesFocus')).to.be.false;
|
101
|
+
// done();
|
102
|
+
// }, 10);
|
103
|
+
// }, 10);
|
104
|
+
//
|
105
|
+
//
|
106
|
+
// }).timeout(100);
|
107
|
+
//
|
108
|
+
// })
|
101
109
|
|
102
110
|
describe('Test ElementInternals', function () {
|
103
111
|
|
@@ -115,6 +123,7 @@ describe('DOM', function () {
|
|
115
123
|
expect(d.constructor.formAssociated).to.be.true;
|
116
124
|
|
117
125
|
});
|
126
|
+
|
118
127
|
it('form', function () {
|
119
128
|
|
120
129
|
let d = document.createElement('monster-customcontrol');
|
@@ -147,7 +156,6 @@ describe('DOM', function () {
|
|
147
156
|
|
148
157
|
});
|
149
158
|
|
150
|
-
|
151
159
|
it('setFormValue', function () {
|
152
160
|
|
153
161
|
let d = document.createElement('monster-customcontrol');
|
@@ -161,7 +169,6 @@ describe('DOM', function () {
|
|
161
169
|
|
162
170
|
});
|
163
171
|
|
164
|
-
|
165
172
|
it('name getter', function () {
|
166
173
|
|
167
174
|
let d = document.createElement('monster-customcontrol');
|
@@ -277,12 +277,12 @@ describe('DOM', function () {
|
|
277
277
|
expect(element.getOption('delegatesFocus')).to.be.true;
|
278
278
|
expect(Object.is(element[internalSymbol].realSubject, o)).to.be.true;
|
279
279
|
|
280
|
-
element.setAttribute(ATTRIBUTE_OPTIONS, JSON.stringify({delegatesFocus: false}));
|
281
|
-
expect(Object.is(element[internalSymbol].realSubject, o)).to.be.true;
|
282
|
-
|
283
|
-
expect(element.getOption('delegatesFocus')).to.be.false;
|
284
|
-
expect(element[internalSymbol].realSubject.options.delegatesFocus).to.be.false;
|
285
|
-
expect(Object.is(element[internalSymbol].realSubject, o)).to.be.true;
|
280
|
+
// element.setAttribute(ATTRIBUTE_OPTIONS, JSON.stringify({delegatesFocus: false}));
|
281
|
+
// expect(Object.is(element[internalSymbol].realSubject, o)).to.be.true;
|
282
|
+
//
|
283
|
+
// expect(element.getOption('delegatesFocus')).to.be.false;
|
284
|
+
// expect(element[internalSymbol].realSubject.options.delegatesFocus).to.be.false;
|
285
|
+
// expect(Object.is(element[internalSymbol].realSubject, o)).to.be.true;
|
286
286
|
|
287
287
|
})
|
288
288
|
|
package/test/cases/monster.mjs
CHANGED