@schukai/monster 4.63.0 → 4.65.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.
@@ -43,6 +43,10 @@ const ATTRIBUTE_CALL = `${ATTRIBUTE_PREFIX}call`;
43
43
  /**
44
44
  * The call button component is used to call a method of another element.
45
45
  *
46
+ * @fragments /fragments/components/host/call-button/
47
+ *
48
+ * @example /examples/components/host/call-button-simple Call button
49
+ *
46
50
  * @copyright Volker Schukai
47
51
  * @summary A call button component that can call a method of another element.
48
52
  */
@@ -50,6 +50,10 @@ const MODE_READ_WRITE = "readwrite";
50
50
  /**
51
51
  * The Config Manager component is used to encapsulate the configuration of the application.
52
52
  *
53
+ * @fragments /fragments/components/host/config-manager/
54
+ *
55
+ * @example /examples/components/host/config-manager-simple Config manager
56
+ *
53
57
  * @copyright Volker Schukai
54
58
  * @summary A config manager component
55
59
  */
@@ -71,6 +71,10 @@ const resourceManagerSymbol = Symbol("resourceManager");
71
71
  /**
72
72
  * The Host component is used to encapsulate the content of a web app.
73
73
  *
74
+ * @fragments /fragments/components/host/host/
75
+ *
76
+ * @example /examples/components/host/host-simple Host container
77
+ *
74
78
  * @copyright Volker Schukai
75
79
  * @summary A simple host component
76
80
  * @fires monster-host-connected
@@ -21,6 +21,10 @@ export { ToggleButton };
21
21
  /**
22
22
  * The Toggle Button component is used toggle a other element wich has a method called toggle.
23
23
  *
24
+ * @fragments /fragments/components/host/toggle-button/
25
+ *
26
+ * @example /examples/components/host/toggle-button-simple Toggle button
27
+ *
24
28
  * @copyright Volker Schukai
25
29
  * @summary A toggle button
26
30
  */
@@ -71,9 +71,9 @@ const scrollableEventHandlerSymbol = Symbol("scrollableEventHandler");
71
71
  /**
72
72
  * A TableOfContent
73
73
  *
74
- * @fragments /fragments/components/form/table-of-content
74
+ * @fragments /fragments/components/navigation/table-of-content/
75
75
  *
76
- * @example /examples/components/form/table-of-content-simple Table of content
76
+ * @example /examples/components/navigation/table-of-content-simple Table of content
77
77
  *
78
78
  * @since 3.65.0
79
79
  * @copyright Volker Schukai
@@ -49,6 +49,11 @@ const openEntryEventHandlerSymbol = Symbol("openEntryEventHandler");
49
49
  /**
50
50
  * HtmlTreeMenu
51
51
  *
52
+ * @fragments /fragments/components/tree-menu/html-tree-menu/
53
+ *
54
+ * @example /examples/components/tree-menu/html-tree-menu-simple Basic HTML tree menu
55
+ * @example /examples/components/tree-menu/html-tree-menu-lazy Lazy loading
56
+ *
52
57
  * @since 4.62.0
53
58
  * @summary A TreeMenu control that builds its entries from nested HTML lists.
54
59
  * @fires entries-imported
@@ -63,29 +68,29 @@ class HtmlTreeMenu extends CustomElement {
63
68
  }
64
69
 
65
70
  /**
66
- * @property {Object} templates Template definitions
67
- * @property {string} templates.main Main template
68
- * @property {Object} classes
69
- * @property {String} classes.control the class for the control element
70
- * @property {String} classes.label the class for the label element
71
- * @property {Object} lazy
72
- * @property {boolean} lazy.enabled enables lazy loading by endpoint
73
- * @property {string} lazy.attribute="data-monster-endpoint" attribute for the endpoint
74
- * @property {Object} lazy.fetchOptions fetch options for lazy requests
75
- * @property {Object} features
76
- * @property {boolean} features.selectParents=false allow selecting entries with children
77
- * @property {Object} actions
78
- * @property {Function} actions.open the action to open an entry (entry, index, event)
79
- * @property {Function} actions.close the action to close an entry (entry, index, event)
80
- * @property {Function} actions.select the action to select an entry (entry, index, event)
81
- * @property {Function} actions.onexpand the action to expand an entry (entry, index, event)
82
- * @property {Function} actions.oncollapse the action to collapse an entry (entry, index, event)
83
- * @property {Function} actions.onselect the action to select an entry (entry, index, event)
84
- * @property {Function} actions.onnavigate the action to navigate (entry, index, event)
85
- * @property {Function} actions.onlazyload the action before lazy load (entry, index, event)
86
- * @property {Function} actions.onlazyloaded the action after lazy load (entry, index, event)
87
- * @property {Function} actions.onlazyerror the action on lazy error (entry, index, event)
88
- */
71
+ * @property {Object} templates Template definitions
72
+ * @property {string} templates.main Main template
73
+ * @property {Object} classes
74
+ * @property {String} classes.control the class for the control element
75
+ * @property {String} classes.label the class for the label element
76
+ * @property {Object} lazy
77
+ * @property {boolean} lazy.enabled enables lazy loading by endpoint
78
+ * @property {string} lazy.attribute="data-monster-endpoint" attribute for the endpoint
79
+ * @property {Object} lazy.fetchOptions fetch options for lazy requests
80
+ * @property {Object} features
81
+ * @property {boolean} features.selectParents=false allow selecting entries with children
82
+ * @property {Object} actions
83
+ * @property {Function} actions.open the action to open an entry (entry, index, event)
84
+ * @property {Function} actions.close the action to close an entry (entry, index, event)
85
+ * @property {Function} actions.select the action to select an entry (entry, index, event)
86
+ * @property {Function} actions.onexpand the action to expand an entry (entry, index, event)
87
+ * @property {Function} actions.oncollapse the action to collapse an entry (entry, index, event)
88
+ * @property {Function} actions.onselect the action to select an entry (entry, index, event)
89
+ * @property {Function} actions.onnavigate the action to navigate (entry, index, event)
90
+ * @property {Function} actions.onlazyload the action before lazy load (entry, index, event)
91
+ * @property {Function} actions.onlazyloaded the action after lazy load (entry, index, event)
92
+ * @property {Function} actions.onlazyerror the action on lazy error (entry, index, event)
93
+ */
89
94
  get defaults() {
90
95
  return Object.assign({}, super.defaults, {
91
96
  classes: {
@@ -431,9 +436,7 @@ function buildEntriesFromList(list, level, ancestorHidden, entries) {
431
436
  const lazyConfig = this.getOption("lazy", {});
432
437
  const lazyEnabled = lazyConfig?.enabled !== false;
433
438
  const lazyAttribute = lazyConfig?.attribute || "";
434
- const items = Array.from(list.children).filter((node) =>
435
- node.matches("li"),
436
- );
439
+ const items = Array.from(list.children).filter((node) => node.matches("li"));
437
440
 
438
441
  for (const li of items) {
439
442
  const childList = li.querySelector(":scope > ul,:scope > ol");
@@ -861,8 +864,7 @@ function removeEntry(value) {
861
864
  if (parentEntryIndex !== -1) {
862
865
  const parentIntend = newEntries[parentEntryIndex].intend;
863
866
  const hasChildren = newEntries.some(
864
- (entry, idx) =>
865
- idx > parentEntryIndex && entry.intend > parentIntend,
867
+ (entry, idx) => idx > parentEntryIndex && entry.intend > parentIntend,
866
868
  );
867
869
  if (!hasChildren) {
868
870
  newEntries[parentEntryIndex] = Object.assign(
@@ -78,6 +78,10 @@ const firstRunDoneSymbol = Symbol("firstRunDone");
78
78
  /**
79
79
  * TreeMenu
80
80
  *
81
+ * @fragments /fragments/components/tree-menu/tree-menu/
82
+ *
83
+ * @example /examples/components/tree-menu/tree-menu-simple Basic tree menu
84
+ *
81
85
  * @since 1.0.0
82
86
  * @summary A TreeMenu control
83
87
  * @fires entries-imported
@@ -76,6 +76,7 @@ export * from "./components/notify/monitor-attribute-errors.mjs";
76
76
  export * from "./components/notify/message.mjs";
77
77
  export * from "./components/notify/notify.mjs";
78
78
  export * from "./components/notify/constants.mjs";
79
+ export * from "./components/tree-menu/html-tree-menu.mjs";
79
80
  export * from "./components/tree-menu/tree-menu.mjs";
80
81
  export * from "./components/host/collapse.mjs";
81
82
  export * from "./components/host/config-manager.mjs";
@@ -0,0 +1,181 @@
1
+ import {getGlobal} from "../../../../source/types/global.mjs";
2
+ import * as chai from 'chai';
3
+ import {chaiDom} from "../../../util/chai-dom.mjs";
4
+ import {initJSDOM} from "../../../util/jsdom.mjs";
5
+ import {ResizeObserverMock} from "../../../util/resize-observer.mjs";
6
+
7
+ let expect = chai.expect;
8
+ chai.use(chaiDom);
9
+
10
+ const global = getGlobal();
11
+
12
+ let BuyBox;
13
+
14
+ const html = `
15
+ <monster-buy-box id="buy-box"></monster-buy-box>
16
+ `;
17
+
18
+ describe('BuyBox', function () {
19
+
20
+ before(function (done) {
21
+ initJSDOM().then(() => {
22
+ import("element-internals-polyfill").catch(e => done(e));
23
+
24
+ if (!global.ResizeObserver) {
25
+ global.ResizeObserver = ResizeObserverMock;
26
+ }
27
+
28
+ import("../../../../source/components/form/buy-box.mjs").then((m) => {
29
+ BuyBox = m['BuyBox'];
30
+ done();
31
+ }).catch(e => done(e));
32
+ });
33
+ });
34
+
35
+ afterEach(() => {
36
+ let mocks = document.getElementById('mocks');
37
+ mocks.innerHTML = "";
38
+ });
39
+
40
+ it('requires variant price when any variant has pricing', function (done) {
41
+ const mocks = document.getElementById('mocks');
42
+ mocks.innerHTML = html;
43
+
44
+ setTimeout(() => {
45
+ try {
46
+ const box = document.getElementById('buy-box');
47
+ expect(box).is.instanceof(BuyBox);
48
+
49
+ box.setOption("product", { sku: "BASE", currency: "EUR" });
50
+ box.setOption("pricing", { price: 10 });
51
+ box.setOption("quantity", { min: 1, max: 10, value: 1 });
52
+ box.setOption("variants", {
53
+ dimensions: [
54
+ { key: "color", label: "Color" },
55
+ { key: "size", label: "Size" }
56
+ ],
57
+ data: [
58
+ { id: "SKU-RED-S", color: "Red", size: "S", price: 12 },
59
+ { id: "SKU-BLU-M", color: "Blue", size: "M" }
60
+ ]
61
+ });
62
+ box.refresh();
63
+
64
+ const onChange = (event) => {
65
+ const msg = box.shadowRoot
66
+ .querySelector("[data-monster-role=message]")
67
+ .textContent
68
+ .trim();
69
+ expect(msg).to.equal("Price unavailable.");
70
+ expect(event.detail.priceAvailable).to.equal(false);
71
+ box.removeEventListener("monster-buy-box-change", onChange);
72
+ done();
73
+ };
74
+ box.addEventListener("monster-buy-box-change", onChange);
75
+
76
+ setTimeout(() => {
77
+ const variants = box.shadowRoot.querySelector("monster-variant-select");
78
+ variants.setOption("selection", { color: "Blue", size: "M" });
79
+ variants.refresh();
80
+ variants.dispatchEvent(new CustomEvent("monster-variant-select-change"));
81
+ }, 0);
82
+
83
+ } catch (e) {
84
+ return done(e);
85
+ }
86
+ }, 0);
87
+ });
88
+
89
+ it('uses base pricing when no variant pricing is provided', function (done) {
90
+ const mocks = document.getElementById('mocks');
91
+ mocks.innerHTML = html;
92
+
93
+ setTimeout(() => {
94
+ try {
95
+ const box = document.getElementById('buy-box');
96
+ expect(box).is.instanceof(BuyBox);
97
+
98
+ box.setOption("product", { sku: "BASE", currency: "EUR" });
99
+ box.setOption("pricing", { price: 10 });
100
+ box.setOption("quantity", { min: 1, max: 10, value: 1 });
101
+ box.setOption("variants", {
102
+ dimensions: [
103
+ { key: "color", label: "Color" },
104
+ { key: "size", label: "Size" }
105
+ ],
106
+ data: [
107
+ { id: "SKU-RED-S", color: "Red", size: "S" },
108
+ { id: "SKU-BLU-M", color: "Blue", size: "M" }
109
+ ]
110
+ });
111
+ box.refresh();
112
+
113
+ const onChange = (event) => {
114
+ const msg = box.shadowRoot
115
+ .querySelector("[data-monster-role=message]")
116
+ .textContent
117
+ .trim();
118
+ expect(msg).to.equal("");
119
+ expect(event.detail.priceAvailable).to.equal(true);
120
+ expect(event.detail.pricing.price).to.equal(10);
121
+ box.removeEventListener("monster-buy-box-change", onChange);
122
+ done();
123
+ };
124
+ box.addEventListener("monster-buy-box-change", onChange);
125
+
126
+ setTimeout(() => {
127
+ const variants = box.shadowRoot.querySelector("monster-variant-select");
128
+ variants.setOption("selection", { color: "Blue", size: "M" });
129
+ variants.refresh();
130
+ variants.dispatchEvent(new CustomEvent("monster-variant-select-change"));
131
+ }, 0);
132
+
133
+ } catch (e) {
134
+ return done(e);
135
+ }
136
+ }, 0);
137
+ });
138
+
139
+ it('applies tier pricing based on quantity', function (done) {
140
+ const mocks = document.getElementById('mocks');
141
+ mocks.innerHTML = html;
142
+
143
+ setTimeout(() => {
144
+ try {
145
+ const box = document.getElementById('buy-box');
146
+ expect(box).is.instanceof(BuyBox);
147
+
148
+ box.setOption("product", { sku: "BASE", currency: "EUR" });
149
+ box.setOption("pricing", {
150
+ price: 6,
151
+ tiers: [
152
+ { min: 1, price: 6 },
153
+ { min: 5, price: 5.5 },
154
+ { min: 10, price: 5 }
155
+ ]
156
+ });
157
+ box.setOption("quantity", { min: 1, max: 20, value: 4 });
158
+ box.refresh();
159
+
160
+ let lastDetail = null;
161
+ box.addEventListener("monster-buy-box-change", (event) => {
162
+ lastDetail = event.detail;
163
+ });
164
+
165
+ setTimeout(() => {
166
+ const quantity = box.shadowRoot.querySelector("monster-quantity");
167
+ quantity.value = 10;
168
+ quantity.dispatchEvent(new CustomEvent("monster-quantity-change"));
169
+
170
+ setTimeout(() => {
171
+ expect(lastDetail.pricing.price).to.equal(5);
172
+ done();
173
+ }, 0);
174
+ }, 0);
175
+
176
+ } catch (e) {
177
+ return done(e);
178
+ }
179
+ }, 0);
180
+ });
181
+ });