@schukai/monster 3.44.1 → 3.47.0

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@schukai/monster",
3
- "version": "3.44.1",
3
+ "version": "3.47.0",
4
4
  "description": "Monster is a simple library for creating fast, robust and lightweight websites.",
5
5
  "keywords": [
6
6
  "framework",
@@ -59,6 +59,9 @@ export {
59
59
  ATTRIBUTE_HIDDEN,
60
60
  objectUpdaterLinkSymbol,
61
61
  customElementUpdaterLinkSymbol,
62
+ optionCallbackName,
63
+ ATTRIBUTE_SCRIPT_HOST,
64
+ ATTRIBUTE_OPTION_CALLBACK
62
65
  };
63
66
 
64
67
  /**
@@ -86,6 +89,16 @@ const ATTRIBUTE_PREFIX = "data-monster-";
86
89
  */
87
90
  const ATTRIBUTE_OPTIONS = `${ATTRIBUTE_PREFIX}options`;
88
91
 
92
+ /**
93
+ * This is name of the attribute to pass the script host to a control
94
+ *
95
+ * @memberOf Monster.DOM
96
+ * @license AGPLv3
97
+ * @since 3.48.0
98
+ * @type {string}
99
+ */
100
+ const ATTRIBUTE_SCRIPT_HOST = `${ATTRIBUTE_PREFIX}script-host`;
101
+
89
102
  /**
90
103
  * This is the name of the attribute to pass options to a control
91
104
  *
@@ -96,6 +109,26 @@ const ATTRIBUTE_OPTIONS = `${ATTRIBUTE_PREFIX}options`;
96
109
  */
97
110
  const ATTRIBUTE_OPTIONS_SELECTOR = `${ATTRIBUTE_PREFIX}options-selector`;
98
111
 
112
+ /**
113
+ * This is the name of the attribute to pass the callback to a control
114
+ *
115
+ * @memberOf Monster.DOM
116
+ * @license AGPLv3
117
+ * @since 3.48.0
118
+ * @type {string}
119
+ */
120
+ const ATTRIBUTE_OPTION_CALLBACK = `${ATTRIBUTE_PREFIX}option-callback`;
121
+
122
+ /**
123
+ * This is the name of the callback to pass the callback to a control
124
+ *
125
+ * @memberOf Monster.DOM
126
+ * @license AGPLv3
127
+ * @since 3.48.0
128
+ * @type {string}
129
+ */
130
+ const optionCallbackName = `initCustomControlOptionsCallback`;
131
+
99
132
  /**
100
133
  * @memberOf Monster.DOM
101
134
  * @type {string}
@@ -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 { 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
- export { CustomControl };
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
- const list = super.observedAttributes;
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 get formAssociated() {
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({}, super.defaults);
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 this[attachedInternalSymbol];
357
+ return self[attachedInternalSymbol];
317
358
  }
318
359
 
319
360
  /**
@@ -5,6 +5,7 @@
5
5
  * License text available at https://www.gnu.org/licenses/agpl-3.0.en.html
6
6
  */
7
7
 
8
+ import {findElementWithIdUpwards} from "./util.mjs";
8
9
  import {internalSymbol} from "../constants.mjs";
9
10
  import {extend} from "../data/extend.mjs";
10
11
  import {Pathfinder} from "../data/pathfinder.mjs";
@@ -22,8 +23,11 @@ import {
22
23
  ATTRIBUTE_DISABLED,
23
24
  ATTRIBUTE_ERRORMESSAGE,
24
25
  ATTRIBUTE_OPTIONS,
26
+ ATTRIBUTE_OPTION_CALLBACK,
25
27
  ATTRIBUTE_OPTIONS_SELECTOR,
28
+ ATTRIBUTE_SCRIPT_HOST,
26
29
  customElementUpdaterLinkSymbol,
30
+ optionCallbackName
27
31
  } from "./constants.mjs";
28
32
  import {findDocumentTemplate, Template} from "./template.mjs";
29
33
  import {addObjectWithUpdaterToElement} from "./updater.mjs";
@@ -31,6 +35,7 @@ import {instanceSymbol} from "../constants.mjs";
31
35
  import {getDocumentTranslations, Translations} from "../i18n/translations.mjs";
32
36
  import {getSlottedElements} from "./slotted.mjs";
33
37
  import {initOptionsFromAttributes} from "./util/init-options-from-attributes.mjs";
38
+ import {setOptionFromAttribute} from "./util/set-option-from-attribute.mjs";
34
39
 
35
40
  export {
36
41
  CustomElement,
@@ -66,6 +71,12 @@ const attributeObserverSymbol = Symbol.for("@schukai/monster/dom/@@attributeObse
66
71
  */
67
72
  const attributeMutationObserverSymbol = Symbol("@schukai/monster/dom/@@mutationObserver");
68
73
 
74
+ /**
75
+ * @private
76
+ * @type {symbol}
77
+ */
78
+ const scriptHostElementSymbol = Symbol("scriptHostElement");
79
+
69
80
  /**
70
81
  * HTMLElement
71
82
  * @external HTMLElement
@@ -211,6 +222,7 @@ class CustomElement extends HTMLElement {
211
222
  });
212
223
  this[initMethodSymbol]();
213
224
  initOptionObserver.call(this);
225
+ this[scriptHostElementSymbol] = [];
214
226
 
215
227
  }
216
228
 
@@ -220,7 +232,7 @@ class CustomElement extends HTMLElement {
220
232
  * @since 2.1.0
221
233
  */
222
234
  static get [instanceSymbol]() {
223
- return Symbol.for("@schukai/monster/dom/custom-element");
235
+ return Symbol.for("@schukai/monster/dom/custom-element@@instance");
224
236
  }
225
237
 
226
238
  /**
@@ -232,7 +244,7 @@ class CustomElement extends HTMLElement {
232
244
  * @since 1.15.0
233
245
  */
234
246
  static get observedAttributes() {
235
- return [ATTRIBUTE_OPTIONS, ATTRIBUTE_DISABLED];
247
+ return [];
236
248
  }
237
249
 
238
250
  /**
@@ -309,7 +321,7 @@ class CustomElement extends HTMLElement {
309
321
  */
310
322
  get defaults() {
311
323
  return {
312
- ATTRIBUTE_DISABLED: this.getAttribute(ATTRIBUTE_DISABLED),
324
+ disabled: false,
313
325
  shadowMode: "open",
314
326
  delegatesFocus: true,
315
327
  templates: {
@@ -519,6 +531,7 @@ class CustomElement extends HTMLElement {
519
531
  self.setOptions(ScriptOptions);
520
532
  }
521
533
 
534
+
522
535
  if (self.getOption("shadowMode", false) !== false) {
523
536
  try {
524
537
  initShadowRoot.call(self);
@@ -540,6 +553,8 @@ class CustomElement extends HTMLElement {
540
553
  }
541
554
  }
542
555
 
556
+ initFromCallbackHost.call(this);
557
+
543
558
  try {
544
559
  nodeList = new Set([...elements, ...getSlottedElements.call(self)]);
545
560
  } catch (e) {
@@ -605,8 +620,11 @@ class CustomElement extends HTMLElement {
605
620
  attributeChangedCallback(attrName, oldVal, newVal) {
606
621
  const self = this;
607
622
 
608
- const callback = self[attributeObserverSymbol]?.[attrName];
623
+ if (attrName.startsWith("data-monster-option-")) {
624
+ setOptionFromAttribute(self, attrName, this[internalSymbol].getSubject()["options"])
625
+ }
609
626
 
627
+ const callback = self[attributeObserverSymbol]?.[attrName];
610
628
  if (isFunction(callback)) {
611
629
  try {
612
630
  callback.call(self, newVal, oldVal);
@@ -637,6 +655,99 @@ class CustomElement extends HTMLElement {
637
655
 
638
656
  return containChildNode.call(self.shadowRoot, node);
639
657
  }
658
+
659
+ /**
660
+ * Calls a callback function if it exists.
661
+ *
662
+ * @param {string} name
663
+ * @param {*} args
664
+ * @returns {*}
665
+ */
666
+ callCallback(name, args) {
667
+ const self = this;
668
+ return callControlCallback.call(self, name, ...args);
669
+ }
670
+
671
+
672
+ }
673
+
674
+ /**
675
+ * @param {string} callBackFunctionName
676
+ * @param {*} args
677
+ * @return {any}
678
+ */
679
+ function callControlCallback(callBackFunctionName, ...args) {
680
+ const self = this;
681
+
682
+ if (!isString(callBackFunctionName) || callBackFunctionName === "") {
683
+ return;
684
+ }
685
+
686
+ if (callBackFunctionName in self) {
687
+ return self[callBackFunctionName](self, ...args);
688
+
689
+ }
690
+
691
+ if (!self.hasAttribute(ATTRIBUTE_SCRIPT_HOST)) {
692
+ return;
693
+ }
694
+
695
+ if (self[scriptHostElementSymbol].length === 0) {
696
+
697
+ const targetId = self.getAttribute(ATTRIBUTE_SCRIPT_HOST);
698
+ if (!targetId) {
699
+ return;
700
+ }
701
+
702
+ const list = targetId.split(",")
703
+ for (const id of list) {
704
+ const host = findElementWithIdUpwards(self, targetId);
705
+ if (!(host instanceof HTMLElement)) {
706
+ continue;
707
+ }
708
+
709
+ self[scriptHostElementSymbol].push(host);
710
+ }
711
+ }
712
+
713
+ for (const host of self[scriptHostElementSymbol]) {
714
+ if (callBackFunctionName in host) {
715
+ try {
716
+ return host[callBackFunctionName](self, ...args);
717
+ } catch (e) {
718
+ addAttributeToken(self, ATTRIBUTE_ERRORMESSAGE, e.toString());
719
+ }
720
+ }
721
+ }
722
+
723
+ addAttributeToken(self, ATTRIBUTE_ERRORMESSAGE, `callback ${callBackFunctionName} not found`);
724
+
725
+ }
726
+
727
+ /**
728
+ * This Function is called when the element is attached to the DOM.
729
+ *
730
+ * It looks for the attribute `data-monster-option-callback`. Is this attribute is not set, the default callback
731
+ * `initCustomControlOptionsCallback` is called.
732
+ *
733
+ * The callback is searched in this element and in the host element. If the callback is found, it is called with the
734
+ * element as parameter.
735
+ *
736
+ * The `monster
737
+ *
738
+ * @this CustomElement
739
+ */
740
+ function initFromCallbackHost() {
741
+ const self = this;
742
+
743
+ let callBackFunctionName = optionCallbackName // default callback
744
+ if (self.hasAttribute(ATTRIBUTE_OPTION_CALLBACK)) {
745
+ callBackFunctionName = self.getAttribute(ATTRIBUTE_OPTION_CALLBACK);
746
+ }
747
+
748
+ callControlCallback.call(self, callBackFunctionName);
749
+
750
+
640
751
  }
641
752
 
642
753
  /**
@@ -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
+
@@ -142,7 +142,7 @@ function getMonsterVersion() {
142
142
  }
143
143
 
144
144
  /** don't touch, replaced by make with package.json version */
145
- monsterVersion = new Version("3.44.1");
145
+ monsterVersion = new Version("3.47.0");
146
146
 
147
147
  return monsterVersion;
148
148
  }
@@ -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
- describe('CustomControl()', function () {
23
-
24
- before(function (done) {
25
- initJSDOM().then(() => {
22
+ before(function (done) {
23
+ initJSDOM().then(() => {
26
24
 
27
- // jsdom does not support ElementInternals
28
- jsdomFlag = navigator.userAgent.includes("jsdom");
25
+ // jsdom does not support ElementInternals
26
+ jsdomFlag = navigator.userAgent.includes("jsdom");
29
27
 
30
- import("../../../../application/source/dom/customelement.mjs").then((m) => {
31
- registerCustomElement = m['registerCustomElement'];
28
+ import("../../../../application/source/dom/customelement.mjs").then((m) => {
29
+ registerCustomElement = m['registerCustomElement'];
32
30
 
33
31
 
34
- import("../../../../application/source/dom/customcontrol.mjs").then((m) => {
32
+ import("../../../../application/source/dom/customcontrol.mjs").then((m) => {
35
33
 
36
- document = getDocument();
34
+ document = getDocument();
37
35
 
38
- try {
39
- CustomControl = m['CustomControl'];
36
+ try {
37
+ CustomControl = m['CustomControl'];
40
38
 
41
- TestComponent = class extends CustomControl {
42
- static getTag() {
43
- return "monster-customcontrol"
44
- }
39
+ TestComponent = class extends CustomControl {
40
+ static getTag() {
41
+ return "monster-customcontrol"
45
42
  }
46
- registerCustomElement(TestComponent)
43
+ }
44
+ registerCustomElement(TestComponent)
47
45
 
48
46
 
49
- done()
50
- } catch (e) {
51
- done(e);
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
- it('delegatesFocus should change from true to false', function () {
91
- let element = document.createElement('monster-customcontrol')
92
-
93
- expect(element.getOption('delegatesFocus')).to.be.true;
94
- element.setAttribute(ATTRIBUTE_OPTIONS, JSON.stringify({delegatesFocus: false}));
95
- expect(element.getOption('delegatesFocus')).to.be.false;
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');
@@ -0,0 +1,143 @@
1
+ 'use strict';
2
+
3
+ import chai from "chai"
4
+ // import {internalSymbol} from "../../../../application/source/constants.mjs";
5
+ // import {ATTRIBUTE_OPTIONS} from "../../../../application/source/dom/constants.mjs";
6
+ import {getDocument} from "../../../../application/source/dom/util.mjs";
7
+ // import {ProxyObserver} from "../../../../application/source/types/proxyobserver.mjs";
8
+ // import {addObjectWithUpdaterToElement} from "../../../../application/source/dom/updater.mjs";
9
+ import {chaiDom} from "../../util/chai-dom.mjs";
10
+ import {initJSDOM} from "../../util/jsdom.mjs";
11
+
12
+
13
+ let expect = chai.expect;
14
+ chai.use(chaiDom);
15
+
16
+ // let html1 = `
17
+ // <div id="scripthost">
18
+ // </div>
19
+ //
20
+ // <div>
21
+ // <
22
+ // </div>
23
+ // `;
24
+
25
+
26
+ // defined in constants.mjs
27
+ // const updaterSymbolKey = "@schukai/monster/dom/custom-element@@options-updater-link"
28
+ // const updaterSymbolSymbol = Symbol.for(updaterSymbolKey);
29
+
30
+
31
+
32
+ describe('DOM', function () {
33
+
34
+ let CustomElement, registerCustomElement, TestComponent, document, TestComponent2,assignUpdaterToElement;
35
+
36
+ describe('initFromScriptHost()', function () {
37
+
38
+ const randomTagNumber = "monster-test"+Math.floor(Math.random() * 1000000);
39
+
40
+ before(function (done) {
41
+ initJSDOM().then(() => {
42
+
43
+ import("../../../../application/source/dom/customelement.mjs").then((m) => {
44
+
45
+ try {
46
+ CustomElement = m['CustomElement'];
47
+ registerCustomElement = m['registerCustomElement'];
48
+
49
+ TestComponent2 = class extends CustomElement {
50
+ static getTag() {
51
+ return randomTagNumber;
52
+ }
53
+
54
+ /**
55
+ *
56
+ * @return {Object}
57
+ */
58
+ get defaults() {
59
+
60
+ return Object.assign({}, super.defaults, {
61
+ test: 0,
62
+ templates: {
63
+ main: '<h1></h1><article><p>test</p><div id="container"></div></article>'
64
+ },
65
+ })
66
+ }
67
+
68
+ }
69
+
70
+ registerCustomElement(TestComponent2)
71
+
72
+ document = getDocument();
73
+ done()
74
+ } catch (e) {
75
+ done(e);
76
+ }
77
+
78
+
79
+ }).catch((e) => {
80
+ done(e);
81
+ });
82
+
83
+ });
84
+ })
85
+
86
+ afterEach(() => {
87
+ let mocks = document.getElementById('mocks');
88
+ mocks.innerHTML = "";
89
+ })
90
+
91
+ describe('call callback', function () {
92
+ it('should not found callback and add error attribute', function () {
93
+
94
+ let mocks = document.getElementById('mocks');
95
+ mocks.innerHTML = `<div id="call-back-host"></div><div id="container"></div>`;
96
+
97
+ let control = document.createElement(randomTagNumber);
98
+ control.setAttribute('data-monster-script-host', "call-back-host");
99
+ document.getElementById('container').appendChild(control);
100
+ expect(control.getOption('test')).is.eql(0);
101
+ expect(control.hasAttribute('data-monster-error')).is.true;
102
+
103
+ });
104
+
105
+ it('should found callback initCustomControlOptionsCallback', function () {
106
+
107
+ let mocks = document.getElementById('mocks');
108
+ mocks.innerHTML = `<div id="call-back-host"></div><div id="container"></div>`;
109
+
110
+ const container = document.getElementById('call-back-host');
111
+ container.initCustomControlOptionsCallback = function (control) {
112
+ control.setOption('test', 1);
113
+ }
114
+
115
+ let control = document.createElement(randomTagNumber);
116
+ control.setAttribute('data-monster-script-host', "call-back-host");
117
+ document.getElementById('container').appendChild(control);
118
+ expect(control.getOption('test')).is.eql(1);
119
+ expect(control.hasAttribute('data-monster-error')).is.false;
120
+
121
+ });
122
+
123
+ it('should found callback initCustomControlOptionsCallback from self', function () {
124
+
125
+ let mocks = document.getElementById('mocks');
126
+ mocks.innerHTML = `<div id="call-back-host"></div><div id="container"></div>`;
127
+
128
+ let control = document.createElement(randomTagNumber);
129
+ expect(control.getOption('test')).is.eql(0);
130
+ control.initCustomControlOptionsCallback = function (control) {
131
+ control.setOption('test', 2);
132
+ }
133
+
134
+ control.setAttribute('data-monster-script-host', "call-back-host");
135
+ document.getElementById('container').appendChild(control);
136
+ expect(control.getOption('test')).is.eql(2);
137
+ expect(control.hasAttribute('data-monster-error')).is.false;
138
+
139
+ });
140
+ })
141
+
142
+ });
143
+ })
@@ -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
 
@@ -55,7 +55,6 @@ describe('FocusManager', function () {
55
55
  });
56
56
  }
57
57
 
58
-
59
58
  })
60
59
 
61
60
  it('run ist', function () {
@@ -7,7 +7,7 @@ describe('Monster', function () {
7
7
  let monsterVersion
8
8
 
9
9
  /** don´t touch, replaced by make with package.json version */
10
- monsterVersion = new Version("3.44.1")
10
+ monsterVersion = new Version("3.47.0")
11
11
 
12
12
  let m = getMonsterVersion();
13
13
 
@@ -16,14 +16,10 @@ function init() {
16
16
  }
17
17
  })
18
18
  }
19
-
20
19
  });
21
20
  });
22
-
23
-
24
21
  }
25
22
 
26
-
27
23
  /**
28
24
  *
29
25
  */