@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.
- package/CHANGELOG.md +25 -0
- package/package.json +1 -1
- package/source/components/form/buy-box.mjs +2516 -0
- package/source/components/form/cart-control.mjs +710 -0
- package/source/components/form/style/buy-box.pcss +124 -0
- package/source/components/form/style/cart-control.pcss +35 -0
- package/source/components/form/style/variant-select.pcss +79 -0
- package/source/components/form/stylesheet/buy-box.mjs +38 -0
- package/source/components/form/stylesheet/cart-control.mjs +38 -0
- package/source/components/form/stylesheet/variant-select.mjs +38 -0
- package/source/components/form/variant-select.mjs +1483 -0
- package/source/components/host/call-button.mjs +4 -0
- package/source/components/host/config-manager.mjs +4 -0
- package/source/components/host/host.mjs +4 -0
- package/source/components/host/toggle-button.mjs +4 -0
- package/source/components/navigation/table-of-content.mjs +2 -2
- package/source/components/tree-menu/html-tree-menu.mjs +30 -28
- package/source/components/tree-menu/tree-menu.mjs +4 -0
- package/source/monster.mjs +1 -0
- package/test/cases/components/form/buy-box.mjs +181 -0
|
@@ -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/
|
|
74
|
+
* @fragments /fragments/components/navigation/table-of-content/
|
|
75
75
|
*
|
|
76
|
-
* @example /examples/components/
|
|
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
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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
|
package/source/monster.mjs
CHANGED
|
@@ -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
|
+
});
|