@schukai/monster 4.141.2 → 4.141.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
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"author":"Volker Schukai","dependencies":{"@floating-ui/dom":"^1.7.6"},"description":"Monster is a simple library for creating fast, robust and lightweight websites.","homepage":"https://monsterjs.org/","keywords":["framework","web","dom","css","sass","mobile-first","app","front-end","templates","schukai","core","shopcloud","alvine","monster","buildmap","stack","observer","observable","uuid","node","nodelist","css-in-js","logger","log","theme"],"license":"AGPL 3.0","main":"source/monster.mjs","module":"source/monster.mjs","name":"@schukai/monster","repository":{"type":"git","url":"https://gitlab.schukai.com/oss/libraries/javascript/monster.git"},"type":"module","version":"4.141.
|
|
1
|
+
{"author":"Volker Schukai","dependencies":{"@floating-ui/dom":"^1.7.6"},"description":"Monster is a simple library for creating fast, robust and lightweight websites.","homepage":"https://monsterjs.org/","keywords":["framework","web","dom","css","sass","mobile-first","app","front-end","templates","schukai","core","shopcloud","alvine","monster","buildmap","stack","observer","observable","uuid","node","nodelist","css-in-js","logger","log","theme"],"license":"AGPL 3.0","main":"source/monster.mjs","module":"source/monster.mjs","name":"@schukai/monster","repository":{"type":"git","url":"https://gitlab.schukai.com/oss/libraries/javascript/monster.git"},"type":"module","version":"4.141.3"}
|
|
@@ -186,6 +186,13 @@ const ATTRIBUTE_OPTION_LAYOUT_STACKED_ALIGNMENT =
|
|
|
186
186
|
const ATTRIBUTE_OPTION_LAYOUT_STACKED_BREAKPOINT =
|
|
187
187
|
"data-monster-option-layout-stacked-breakpoint";
|
|
188
188
|
|
|
189
|
+
/**
|
|
190
|
+
* @private
|
|
191
|
+
* @type {string}
|
|
192
|
+
*/
|
|
193
|
+
const ATTRIBUTE_OPTION_LAYOUT_STACKED_BREAKPOINT_CONTAINER =
|
|
194
|
+
"data-monster-option-layout-stacked-breakpoint-container";
|
|
195
|
+
|
|
189
196
|
/**
|
|
190
197
|
* @private
|
|
191
198
|
* @type {string}
|
|
@@ -241,6 +248,7 @@ class ControlBar extends CustomElement {
|
|
|
241
248
|
alignment: "left",
|
|
242
249
|
stackedAlignment: undefined,
|
|
243
250
|
stackedBreakpoint: undefined,
|
|
251
|
+
stackedBreakpointContainer: undefined,
|
|
244
252
|
},
|
|
245
253
|
popper: {
|
|
246
254
|
placement: "left",
|
|
@@ -301,9 +309,12 @@ class ControlBar extends CustomElement {
|
|
|
301
309
|
if (
|
|
302
310
|
path === "layout.alignment" ||
|
|
303
311
|
path === "layout.stackedAlignment" ||
|
|
304
|
-
path === "layout.stackedBreakpoint"
|
|
312
|
+
path === "layout.stackedBreakpoint" ||
|
|
313
|
+
path === "layout.stackedBreakpointContainer"
|
|
305
314
|
) {
|
|
306
|
-
syncLayoutState.call(this
|
|
315
|
+
syncLayoutState.call(this, {
|
|
316
|
+
observe: path === "layout.stackedBreakpointContainer",
|
|
317
|
+
});
|
|
307
318
|
}
|
|
308
319
|
return this;
|
|
309
320
|
}
|
|
@@ -355,6 +366,7 @@ class ControlBar extends CustomElement {
|
|
|
355
366
|
attributes.push(ATTRIBUTE_OPTION_LAYOUT_ALIGNMENT);
|
|
356
367
|
attributes.push(ATTRIBUTE_OPTION_LAYOUT_STACKED_ALIGNMENT);
|
|
357
368
|
attributes.push(ATTRIBUTE_OPTION_LAYOUT_STACKED_BREAKPOINT);
|
|
369
|
+
attributes.push(ATTRIBUTE_OPTION_LAYOUT_STACKED_BREAKPOINT_CONTAINER);
|
|
358
370
|
return attributes;
|
|
359
371
|
}
|
|
360
372
|
|
|
@@ -590,6 +602,12 @@ function initEventHandler() {
|
|
|
590
602
|
syncLayoutState.call(self);
|
|
591
603
|
};
|
|
592
604
|
|
|
605
|
+
self[attributeObserverSymbol][
|
|
606
|
+
ATTRIBUTE_OPTION_LAYOUT_STACKED_BREAKPOINT_CONTAINER
|
|
607
|
+
] = () => {
|
|
608
|
+
syncLayoutState.call(self, { observe: true });
|
|
609
|
+
};
|
|
610
|
+
|
|
593
611
|
self[resizeObserverSymbol] = new ResizeObserver(() => {
|
|
594
612
|
scheduleLayout.call(self, { measure: true, layout: true });
|
|
595
613
|
});
|
|
@@ -1272,6 +1290,10 @@ function calculateControlBarDimensions() {
|
|
|
1272
1290
|
|
|
1273
1291
|
this[dimensionsSymbol].setVia("data.visible", !(containerWidth === 0));
|
|
1274
1292
|
this[dimensionsSymbol].setVia("data.containerWidth", containerWidth);
|
|
1293
|
+
this[dimensionsSymbol].setVia(
|
|
1294
|
+
"data.stackedBreakpointContainerWidth",
|
|
1295
|
+
getStackedBreakpointContainerWidth.call(this),
|
|
1296
|
+
);
|
|
1275
1297
|
|
|
1276
1298
|
const itemReferences = [];
|
|
1277
1299
|
|
|
@@ -1336,6 +1358,65 @@ function calculateControlBarDimensions() {
|
|
|
1336
1358
|
this[dimensionsSymbol].setVia("data.itemReferences", itemReferences);
|
|
1337
1359
|
}
|
|
1338
1360
|
|
|
1361
|
+
/**
|
|
1362
|
+
* @private
|
|
1363
|
+
* @return {number}
|
|
1364
|
+
*/
|
|
1365
|
+
function getStackedBreakpointContainerWidth() {
|
|
1366
|
+
const container = resolveStackedBreakpointContainer.call(this);
|
|
1367
|
+
if (!(container instanceof HTMLElement)) {
|
|
1368
|
+
return 0;
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1371
|
+
return getElementLayoutWidth(container);
|
|
1372
|
+
}
|
|
1373
|
+
|
|
1374
|
+
/**
|
|
1375
|
+
* @private
|
|
1376
|
+
* @return {HTMLElement|undefined}
|
|
1377
|
+
*/
|
|
1378
|
+
function resolveStackedBreakpointContainer() {
|
|
1379
|
+
const selector = this.getOption("layout.stackedBreakpointContainer");
|
|
1380
|
+
if (typeof selector !== "string" || selector.trim() === "") {
|
|
1381
|
+
return undefined;
|
|
1382
|
+
}
|
|
1383
|
+
|
|
1384
|
+
try {
|
|
1385
|
+
const closest = this.closest(selector);
|
|
1386
|
+
if (closest instanceof HTMLElement) {
|
|
1387
|
+
return closest;
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1390
|
+
const root = this.getRootNode();
|
|
1391
|
+
if (root && typeof root.querySelector === "function") {
|
|
1392
|
+
const found = root.querySelector(selector);
|
|
1393
|
+
if (found instanceof HTMLElement) {
|
|
1394
|
+
return found;
|
|
1395
|
+
}
|
|
1396
|
+
}
|
|
1397
|
+
} catch {}
|
|
1398
|
+
|
|
1399
|
+
return undefined;
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1402
|
+
/**
|
|
1403
|
+
* @private
|
|
1404
|
+
* @param {HTMLElement} element
|
|
1405
|
+
* @return {number}
|
|
1406
|
+
*/
|
|
1407
|
+
function getElementLayoutWidth(element) {
|
|
1408
|
+
const computedStyle = getComputedStyle(element);
|
|
1409
|
+
if (computedStyle === null) {
|
|
1410
|
+
return 0;
|
|
1411
|
+
}
|
|
1412
|
+
|
|
1413
|
+
if (computedStyle.getPropertyValue("box-sizing") !== "border-box") {
|
|
1414
|
+
return getComputedCssPixels(computedStyle.getPropertyValue("width"));
|
|
1415
|
+
}
|
|
1416
|
+
|
|
1417
|
+
return element.clientWidth;
|
|
1418
|
+
}
|
|
1419
|
+
|
|
1339
1420
|
/**
|
|
1340
1421
|
* @private
|
|
1341
1422
|
*/
|
|
@@ -1369,20 +1450,21 @@ function updateResizeObserverObservation() {
|
|
|
1369
1450
|
*/
|
|
1370
1451
|
function getLayoutObservedNodes() {
|
|
1371
1452
|
const observedNodes = [];
|
|
1372
|
-
|
|
1373
|
-
if (node instanceof HTMLElement) {
|
|
1453
|
+
const addObservedNode = (node) => {
|
|
1454
|
+
if (node instanceof HTMLElement && !observedNodes.includes(node)) {
|
|
1374
1455
|
observedNodes.push(node);
|
|
1375
1456
|
}
|
|
1376
|
-
}
|
|
1457
|
+
};
|
|
1458
|
+
|
|
1459
|
+
Array.from(this.children).forEach(addObservedNode);
|
|
1377
1460
|
|
|
1378
1461
|
let parent = this.parentNode;
|
|
1379
1462
|
while (!(parent instanceof HTMLElement) && parent !== null) {
|
|
1380
1463
|
parent = parent.parentNode;
|
|
1381
1464
|
}
|
|
1382
1465
|
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
}
|
|
1466
|
+
addObservedNode(parent);
|
|
1467
|
+
addObservedNode(resolveStackedBreakpointContainer.call(this));
|
|
1386
1468
|
|
|
1387
1469
|
return observedNodes;
|
|
1388
1470
|
}
|
|
@@ -1567,11 +1649,16 @@ function applyLayoutAlignment() {
|
|
|
1567
1649
|
|
|
1568
1650
|
/**
|
|
1569
1651
|
* @private
|
|
1652
|
+
* @param {{observe?: boolean}} options
|
|
1570
1653
|
* @return {void}
|
|
1571
1654
|
*/
|
|
1572
|
-
function syncLayoutState() {
|
|
1655
|
+
function syncLayoutState(options = {}) {
|
|
1573
1656
|
applyLayoutAlignment.call(this);
|
|
1574
|
-
scheduleLayout.call(this, {
|
|
1657
|
+
scheduleLayout.call(this, {
|
|
1658
|
+
measure: true,
|
|
1659
|
+
layout: true,
|
|
1660
|
+
observe: options.observe === true,
|
|
1661
|
+
});
|
|
1575
1662
|
}
|
|
1576
1663
|
|
|
1577
1664
|
/**
|
|
@@ -1586,11 +1673,19 @@ function isStackedBreakpointMatched() {
|
|
|
1586
1673
|
|
|
1587
1674
|
let width = 0;
|
|
1588
1675
|
try {
|
|
1589
|
-
width = this[dimensionsSymbol].getVia(
|
|
1590
|
-
|
|
1676
|
+
width = this[dimensionsSymbol].getVia(
|
|
1677
|
+
"data.stackedBreakpointContainerWidth",
|
|
1678
|
+
);
|
|
1679
|
+
} catch {}
|
|
1680
|
+
|
|
1681
|
+
if (!(width > 0)) {
|
|
1591
1682
|
try {
|
|
1592
|
-
width = this[dimensionsSymbol].getVia("data.
|
|
1593
|
-
} catch {
|
|
1683
|
+
width = this[dimensionsSymbol].getVia("data.containerWidth");
|
|
1684
|
+
} catch {
|
|
1685
|
+
try {
|
|
1686
|
+
width = this[dimensionsSymbol].getVia("data.space");
|
|
1687
|
+
} catch {}
|
|
1688
|
+
}
|
|
1594
1689
|
}
|
|
1595
1690
|
|
|
1596
1691
|
if (!(width > 0)) {
|
|
@@ -1720,6 +1815,9 @@ function getLayoutChangedEventDetail() {
|
|
|
1720
1815
|
stacked,
|
|
1721
1816
|
stackedAlignment: this.getOption("layout.stackedAlignment"),
|
|
1722
1817
|
stackedBreakpoint: this.getOption("layout.stackedBreakpoint"),
|
|
1818
|
+
stackedBreakpointContainer: this.getOption(
|
|
1819
|
+
"layout.stackedBreakpointContainer",
|
|
1820
|
+
),
|
|
1723
1821
|
};
|
|
1724
1822
|
}
|
|
1725
1823
|
|
|
@@ -162,6 +162,7 @@ describe("ControlBar", function () {
|
|
|
162
162
|
stacked: false,
|
|
163
163
|
stackedAlignment: undefined,
|
|
164
164
|
stackedBreakpoint: undefined,
|
|
165
|
+
stackedBreakpointContainer: undefined,
|
|
165
166
|
});
|
|
166
167
|
});
|
|
167
168
|
|
|
@@ -843,4 +844,458 @@ describe("ControlBar", function () {
|
|
|
843
844
|
globalThis.requestAnimationFrame = originalGlobalRequestAnimationFrame;
|
|
844
845
|
}
|
|
845
846
|
});
|
|
847
|
+
|
|
848
|
+
it("should keep the configured alignment when the reference container is above the stacked breakpoint", async function () {
|
|
849
|
+
const originalRequestAnimationFrame = window.requestAnimationFrame;
|
|
850
|
+
const originalGlobalRequestAnimationFrame = globalThis.requestAnimationFrame;
|
|
851
|
+
|
|
852
|
+
const scheduledCallbacks = [];
|
|
853
|
+
const flushFrames = async () => {
|
|
854
|
+
while (scheduledCallbacks.length > 0) {
|
|
855
|
+
scheduledCallbacks.shift()();
|
|
856
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
857
|
+
}
|
|
858
|
+
};
|
|
859
|
+
|
|
860
|
+
try {
|
|
861
|
+
window.requestAnimationFrame = (callback) => {
|
|
862
|
+
scheduledCallbacks.push(callback);
|
|
863
|
+
return scheduledCallbacks.length;
|
|
864
|
+
};
|
|
865
|
+
globalThis.requestAnimationFrame = window.requestAnimationFrame;
|
|
866
|
+
|
|
867
|
+
const mocks = document.getElementById("mocks");
|
|
868
|
+
mocks.innerHTML = `
|
|
869
|
+
<div id="headline-wide" class="page-headline-bar">
|
|
870
|
+
<div id="headline-wide-control-column">
|
|
871
|
+
<monster-control-bar
|
|
872
|
+
id="headline-wide-bar"
|
|
873
|
+
data-monster-option-layout-alignment="right"
|
|
874
|
+
data-monster-option-layout-stacked-alignment="left"
|
|
875
|
+
data-monster-option-layout-stacked-breakpoint="480px"
|
|
876
|
+
data-monster-option-layout-stacked-breakpoint-container=".page-headline-bar"
|
|
877
|
+
>
|
|
878
|
+
<button id="headline-wide-button">Run</button>
|
|
879
|
+
</monster-control-bar>
|
|
880
|
+
</div>
|
|
881
|
+
</div>
|
|
882
|
+
`;
|
|
883
|
+
|
|
884
|
+
const headline = document.getElementById("headline-wide");
|
|
885
|
+
const column = document.getElementById("headline-wide-control-column");
|
|
886
|
+
const button = document.getElementById("headline-wide-button");
|
|
887
|
+
const bar = document.getElementById("headline-wide-bar");
|
|
888
|
+
const controlBar = bar.shadowRoot.querySelector(
|
|
889
|
+
'[data-monster-role="control-bar"]',
|
|
890
|
+
);
|
|
891
|
+
|
|
892
|
+
headline.style.boxSizing = "border-box";
|
|
893
|
+
Object.defineProperty(headline, "clientWidth", {
|
|
894
|
+
configurable: true,
|
|
895
|
+
value: 640,
|
|
896
|
+
});
|
|
897
|
+
column.style.boxSizing = "border-box";
|
|
898
|
+
Object.defineProperty(column, "clientWidth", {
|
|
899
|
+
configurable: true,
|
|
900
|
+
value: 459.469,
|
|
901
|
+
});
|
|
902
|
+
Object.defineProperty(button, "offsetWidth", {
|
|
903
|
+
configurable: true,
|
|
904
|
+
value: 40,
|
|
905
|
+
});
|
|
906
|
+
Object.defineProperty(button, "offsetHeight", {
|
|
907
|
+
configurable: true,
|
|
908
|
+
value: 30,
|
|
909
|
+
});
|
|
910
|
+
button.getBoundingClientRect = () => ({
|
|
911
|
+
width: 40,
|
|
912
|
+
height: 30,
|
|
913
|
+
top: 0,
|
|
914
|
+
right: 40,
|
|
915
|
+
bottom: 30,
|
|
916
|
+
left: 0,
|
|
917
|
+
x: 0,
|
|
918
|
+
y: 0,
|
|
919
|
+
toJSON: () => {},
|
|
920
|
+
});
|
|
921
|
+
|
|
922
|
+
await flushFrames();
|
|
923
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
924
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
925
|
+
|
|
926
|
+
expect(controlBar.getAttribute("data-monster-layout-stacked")).to.equal(
|
|
927
|
+
"false",
|
|
928
|
+
);
|
|
929
|
+
expect(controlBar.getAttribute("data-monster-layout-alignment")).to.equal(
|
|
930
|
+
"right",
|
|
931
|
+
);
|
|
932
|
+
} finally {
|
|
933
|
+
window.requestAnimationFrame = originalRequestAnimationFrame;
|
|
934
|
+
globalThis.requestAnimationFrame = originalGlobalRequestAnimationFrame;
|
|
935
|
+
}
|
|
936
|
+
});
|
|
937
|
+
|
|
938
|
+
it("should apply stacked alignment when the reference container is below the stacked breakpoint", async function () {
|
|
939
|
+
const originalRequestAnimationFrame = window.requestAnimationFrame;
|
|
940
|
+
const originalGlobalRequestAnimationFrame = globalThis.requestAnimationFrame;
|
|
941
|
+
|
|
942
|
+
const scheduledCallbacks = [];
|
|
943
|
+
const flushFrames = async () => {
|
|
944
|
+
while (scheduledCallbacks.length > 0) {
|
|
945
|
+
scheduledCallbacks.shift()();
|
|
946
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
947
|
+
}
|
|
948
|
+
};
|
|
949
|
+
|
|
950
|
+
try {
|
|
951
|
+
window.requestAnimationFrame = (callback) => {
|
|
952
|
+
scheduledCallbacks.push(callback);
|
|
953
|
+
return scheduledCallbacks.length;
|
|
954
|
+
};
|
|
955
|
+
globalThis.requestAnimationFrame = window.requestAnimationFrame;
|
|
956
|
+
|
|
957
|
+
const mocks = document.getElementById("mocks");
|
|
958
|
+
mocks.innerHTML = `
|
|
959
|
+
<div id="headline-narrow" class="page-headline-bar">
|
|
960
|
+
<div id="headline-narrow-control-column">
|
|
961
|
+
<monster-control-bar
|
|
962
|
+
id="headline-narrow-bar"
|
|
963
|
+
data-monster-option-layout-alignment="right"
|
|
964
|
+
data-monster-option-layout-stacked-alignment="left"
|
|
965
|
+
data-monster-option-layout-stacked-breakpoint="480px"
|
|
966
|
+
data-monster-option-layout-stacked-breakpoint-container=".page-headline-bar"
|
|
967
|
+
>
|
|
968
|
+
<button id="headline-narrow-button">Run</button>
|
|
969
|
+
</monster-control-bar>
|
|
970
|
+
</div>
|
|
971
|
+
</div>
|
|
972
|
+
`;
|
|
973
|
+
|
|
974
|
+
const headline = document.getElementById("headline-narrow");
|
|
975
|
+
const column = document.getElementById("headline-narrow-control-column");
|
|
976
|
+
const button = document.getElementById("headline-narrow-button");
|
|
977
|
+
const bar = document.getElementById("headline-narrow-bar");
|
|
978
|
+
const controlBar = bar.shadowRoot.querySelector(
|
|
979
|
+
'[data-monster-role="control-bar"]',
|
|
980
|
+
);
|
|
981
|
+
|
|
982
|
+
headline.style.boxSizing = "border-box";
|
|
983
|
+
Object.defineProperty(headline, "clientWidth", {
|
|
984
|
+
configurable: true,
|
|
985
|
+
value: 350,
|
|
986
|
+
});
|
|
987
|
+
column.style.boxSizing = "border-box";
|
|
988
|
+
Object.defineProperty(column, "clientWidth", {
|
|
989
|
+
configurable: true,
|
|
990
|
+
value: 459.469,
|
|
991
|
+
});
|
|
992
|
+
Object.defineProperty(button, "offsetWidth", {
|
|
993
|
+
configurable: true,
|
|
994
|
+
value: 40,
|
|
995
|
+
});
|
|
996
|
+
Object.defineProperty(button, "offsetHeight", {
|
|
997
|
+
configurable: true,
|
|
998
|
+
value: 30,
|
|
999
|
+
});
|
|
1000
|
+
button.getBoundingClientRect = () => ({
|
|
1001
|
+
width: 40,
|
|
1002
|
+
height: 30,
|
|
1003
|
+
top: 0,
|
|
1004
|
+
right: 40,
|
|
1005
|
+
bottom: 30,
|
|
1006
|
+
left: 0,
|
|
1007
|
+
x: 0,
|
|
1008
|
+
y: 0,
|
|
1009
|
+
toJSON: () => {},
|
|
1010
|
+
});
|
|
1011
|
+
|
|
1012
|
+
await flushFrames();
|
|
1013
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
1014
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
1015
|
+
|
|
1016
|
+
expect(controlBar.getAttribute("data-monster-layout-stacked")).to.equal(
|
|
1017
|
+
"true",
|
|
1018
|
+
);
|
|
1019
|
+
expect(controlBar.getAttribute("data-monster-layout-alignment")).to.equal(
|
|
1020
|
+
"left",
|
|
1021
|
+
);
|
|
1022
|
+
} finally {
|
|
1023
|
+
window.requestAnimationFrame = originalRequestAnimationFrame;
|
|
1024
|
+
globalThis.requestAnimationFrame = originalGlobalRequestAnimationFrame;
|
|
1025
|
+
}
|
|
1026
|
+
});
|
|
1027
|
+
|
|
1028
|
+
it("should fall back to the control bar width when the breakpoint container selector cannot resolve", async function () {
|
|
1029
|
+
const originalRequestAnimationFrame = window.requestAnimationFrame;
|
|
1030
|
+
const originalGlobalRequestAnimationFrame = globalThis.requestAnimationFrame;
|
|
1031
|
+
|
|
1032
|
+
const scheduledCallbacks = [];
|
|
1033
|
+
const flushFrames = async () => {
|
|
1034
|
+
while (scheduledCallbacks.length > 0) {
|
|
1035
|
+
scheduledCallbacks.shift()();
|
|
1036
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
1037
|
+
}
|
|
1038
|
+
};
|
|
1039
|
+
|
|
1040
|
+
try {
|
|
1041
|
+
window.requestAnimationFrame = (callback) => {
|
|
1042
|
+
scheduledCallbacks.push(callback);
|
|
1043
|
+
return scheduledCallbacks.length;
|
|
1044
|
+
};
|
|
1045
|
+
globalThis.requestAnimationFrame = window.requestAnimationFrame;
|
|
1046
|
+
|
|
1047
|
+
const mocks = document.getElementById("mocks");
|
|
1048
|
+
mocks.innerHTML = `
|
|
1049
|
+
<div id="missing-reference-wrapper">
|
|
1050
|
+
<monster-control-bar
|
|
1051
|
+
id="missing-reference-bar"
|
|
1052
|
+
data-monster-option-layout-alignment="right"
|
|
1053
|
+
data-monster-option-layout-stacked-alignment="left"
|
|
1054
|
+
data-monster-option-layout-stacked-breakpoint="480px"
|
|
1055
|
+
data-monster-option-layout-stacked-breakpoint-container=".missing-reference"
|
|
1056
|
+
>
|
|
1057
|
+
<button id="missing-reference-button">Run</button>
|
|
1058
|
+
</monster-control-bar>
|
|
1059
|
+
</div>
|
|
1060
|
+
`;
|
|
1061
|
+
|
|
1062
|
+
const wrapper = document.getElementById("missing-reference-wrapper");
|
|
1063
|
+
const button = document.getElementById("missing-reference-button");
|
|
1064
|
+
const bar = document.getElementById("missing-reference-bar");
|
|
1065
|
+
const controlBar = bar.shadowRoot.querySelector(
|
|
1066
|
+
'[data-monster-role="control-bar"]',
|
|
1067
|
+
);
|
|
1068
|
+
|
|
1069
|
+
wrapper.style.boxSizing = "border-box";
|
|
1070
|
+
Object.defineProperty(wrapper, "clientWidth", {
|
|
1071
|
+
configurable: true,
|
|
1072
|
+
value: 350,
|
|
1073
|
+
});
|
|
1074
|
+
Object.defineProperty(button, "offsetWidth", {
|
|
1075
|
+
configurable: true,
|
|
1076
|
+
value: 40,
|
|
1077
|
+
});
|
|
1078
|
+
Object.defineProperty(button, "offsetHeight", {
|
|
1079
|
+
configurable: true,
|
|
1080
|
+
value: 30,
|
|
1081
|
+
});
|
|
1082
|
+
button.getBoundingClientRect = () => ({
|
|
1083
|
+
width: 40,
|
|
1084
|
+
height: 30,
|
|
1085
|
+
top: 0,
|
|
1086
|
+
right: 40,
|
|
1087
|
+
bottom: 30,
|
|
1088
|
+
left: 0,
|
|
1089
|
+
x: 0,
|
|
1090
|
+
y: 0,
|
|
1091
|
+
toJSON: () => {},
|
|
1092
|
+
});
|
|
1093
|
+
|
|
1094
|
+
await flushFrames();
|
|
1095
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
1096
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
1097
|
+
|
|
1098
|
+
expect(controlBar.getAttribute("data-monster-layout-stacked")).to.equal(
|
|
1099
|
+
"true",
|
|
1100
|
+
);
|
|
1101
|
+
expect(controlBar.getAttribute("data-monster-layout-alignment")).to.equal(
|
|
1102
|
+
"left",
|
|
1103
|
+
);
|
|
1104
|
+
} finally {
|
|
1105
|
+
window.requestAnimationFrame = originalRequestAnimationFrame;
|
|
1106
|
+
globalThis.requestAnimationFrame = originalGlobalRequestAnimationFrame;
|
|
1107
|
+
}
|
|
1108
|
+
});
|
|
1109
|
+
|
|
1110
|
+
it("should fall back without throwing when the breakpoint container selector is invalid", async function () {
|
|
1111
|
+
const originalRequestAnimationFrame = window.requestAnimationFrame;
|
|
1112
|
+
const originalGlobalRequestAnimationFrame = globalThis.requestAnimationFrame;
|
|
1113
|
+
|
|
1114
|
+
const scheduledCallbacks = [];
|
|
1115
|
+
const flushFrames = async () => {
|
|
1116
|
+
while (scheduledCallbacks.length > 0) {
|
|
1117
|
+
scheduledCallbacks.shift()();
|
|
1118
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
1119
|
+
}
|
|
1120
|
+
};
|
|
1121
|
+
|
|
1122
|
+
try {
|
|
1123
|
+
window.requestAnimationFrame = (callback) => {
|
|
1124
|
+
scheduledCallbacks.push(callback);
|
|
1125
|
+
return scheduledCallbacks.length;
|
|
1126
|
+
};
|
|
1127
|
+
globalThis.requestAnimationFrame = window.requestAnimationFrame;
|
|
1128
|
+
|
|
1129
|
+
const mocks = document.getElementById("mocks");
|
|
1130
|
+
mocks.innerHTML = `
|
|
1131
|
+
<div id="invalid-reference-wrapper">
|
|
1132
|
+
<monster-control-bar
|
|
1133
|
+
id="invalid-reference-bar"
|
|
1134
|
+
data-monster-option-layout-alignment="right"
|
|
1135
|
+
data-monster-option-layout-stacked-alignment="left"
|
|
1136
|
+
data-monster-option-layout-stacked-breakpoint="480px"
|
|
1137
|
+
>
|
|
1138
|
+
<button id="invalid-reference-button">Run</button>
|
|
1139
|
+
</monster-control-bar>
|
|
1140
|
+
</div>
|
|
1141
|
+
`;
|
|
1142
|
+
|
|
1143
|
+
const wrapper = document.getElementById("invalid-reference-wrapper");
|
|
1144
|
+
const button = document.getElementById("invalid-reference-button");
|
|
1145
|
+
const bar = document.getElementById("invalid-reference-bar");
|
|
1146
|
+
const controlBar = bar.shadowRoot.querySelector(
|
|
1147
|
+
'[data-monster-role="control-bar"]',
|
|
1148
|
+
);
|
|
1149
|
+
|
|
1150
|
+
bar.setOption("layout.stackedBreakpointContainer", "[");
|
|
1151
|
+
wrapper.style.boxSizing = "border-box";
|
|
1152
|
+
Object.defineProperty(wrapper, "clientWidth", {
|
|
1153
|
+
configurable: true,
|
|
1154
|
+
value: 350,
|
|
1155
|
+
});
|
|
1156
|
+
Object.defineProperty(button, "offsetWidth", {
|
|
1157
|
+
configurable: true,
|
|
1158
|
+
value: 40,
|
|
1159
|
+
});
|
|
1160
|
+
Object.defineProperty(button, "offsetHeight", {
|
|
1161
|
+
configurable: true,
|
|
1162
|
+
value: 30,
|
|
1163
|
+
});
|
|
1164
|
+
button.getBoundingClientRect = () => ({
|
|
1165
|
+
width: 40,
|
|
1166
|
+
height: 30,
|
|
1167
|
+
top: 0,
|
|
1168
|
+
right: 40,
|
|
1169
|
+
bottom: 30,
|
|
1170
|
+
left: 0,
|
|
1171
|
+
x: 0,
|
|
1172
|
+
y: 0,
|
|
1173
|
+
toJSON: () => {},
|
|
1174
|
+
});
|
|
1175
|
+
|
|
1176
|
+
await flushFrames();
|
|
1177
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
1178
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
1179
|
+
|
|
1180
|
+
expect(controlBar.getAttribute("data-monster-layout-stacked")).to.equal(
|
|
1181
|
+
"true",
|
|
1182
|
+
);
|
|
1183
|
+
expect(controlBar.getAttribute("data-monster-layout-alignment")).to.equal(
|
|
1184
|
+
"left",
|
|
1185
|
+
);
|
|
1186
|
+
} finally {
|
|
1187
|
+
window.requestAnimationFrame = originalRequestAnimationFrame;
|
|
1188
|
+
globalThis.requestAnimationFrame = originalGlobalRequestAnimationFrame;
|
|
1189
|
+
}
|
|
1190
|
+
});
|
|
1191
|
+
|
|
1192
|
+
it("should observe the resolved breakpoint container and update observation when it changes", async function () {
|
|
1193
|
+
const OriginalResizeObserver = window.ResizeObserver;
|
|
1194
|
+
const originalGlobalResizeObserver = globalThis.ResizeObserver;
|
|
1195
|
+
const originalRequestAnimationFrame = window.requestAnimationFrame;
|
|
1196
|
+
const originalGlobalRequestAnimationFrame = globalThis.requestAnimationFrame;
|
|
1197
|
+
|
|
1198
|
+
class TrackingResizeObserver extends ResizeObserverMock {
|
|
1199
|
+
static instances = [];
|
|
1200
|
+
|
|
1201
|
+
constructor(callback) {
|
|
1202
|
+
super(callback);
|
|
1203
|
+
TrackingResizeObserver.instances.push(this);
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1207
|
+
const scheduledCallbacks = [];
|
|
1208
|
+
const flushFrames = async () => {
|
|
1209
|
+
while (scheduledCallbacks.length > 0) {
|
|
1210
|
+
scheduledCallbacks.shift()();
|
|
1211
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
1212
|
+
}
|
|
1213
|
+
};
|
|
1214
|
+
|
|
1215
|
+
try {
|
|
1216
|
+
window.ResizeObserver = TrackingResizeObserver;
|
|
1217
|
+
globalThis.ResizeObserver = TrackingResizeObserver;
|
|
1218
|
+
window.requestAnimationFrame = (callback) => {
|
|
1219
|
+
scheduledCallbacks.push(callback);
|
|
1220
|
+
return scheduledCallbacks.length;
|
|
1221
|
+
};
|
|
1222
|
+
globalThis.requestAnimationFrame = window.requestAnimationFrame;
|
|
1223
|
+
|
|
1224
|
+
const mocks = document.getElementById("mocks");
|
|
1225
|
+
mocks.innerHTML = `
|
|
1226
|
+
<div id="observed-reference-wrapper">
|
|
1227
|
+
<monster-control-bar
|
|
1228
|
+
id="observed-reference-bar"
|
|
1229
|
+
data-monster-option-layout-stacked-breakpoint="480px"
|
|
1230
|
+
data-monster-option-layout-stacked-breakpoint-container="#reference-a"
|
|
1231
|
+
>
|
|
1232
|
+
<button id="observed-reference-button">Run</button>
|
|
1233
|
+
</monster-control-bar>
|
|
1234
|
+
</div>
|
|
1235
|
+
<div id="reference-a"></div>
|
|
1236
|
+
<div id="reference-b"></div>
|
|
1237
|
+
`;
|
|
1238
|
+
|
|
1239
|
+
const wrapper = document.getElementById("observed-reference-wrapper");
|
|
1240
|
+
const referenceA = document.getElementById("reference-a");
|
|
1241
|
+
const referenceB = document.getElementById("reference-b");
|
|
1242
|
+
const button = document.getElementById("observed-reference-button");
|
|
1243
|
+
const bar = document.getElementById("observed-reference-bar");
|
|
1244
|
+
|
|
1245
|
+
wrapper.style.boxSizing = "border-box";
|
|
1246
|
+
referenceA.style.boxSizing = "border-box";
|
|
1247
|
+
referenceB.style.boxSizing = "border-box";
|
|
1248
|
+
Object.defineProperty(wrapper, "clientWidth", {
|
|
1249
|
+
configurable: true,
|
|
1250
|
+
value: 459,
|
|
1251
|
+
});
|
|
1252
|
+
Object.defineProperty(referenceA, "clientWidth", {
|
|
1253
|
+
configurable: true,
|
|
1254
|
+
value: 640,
|
|
1255
|
+
});
|
|
1256
|
+
Object.defineProperty(referenceB, "clientWidth", {
|
|
1257
|
+
configurable: true,
|
|
1258
|
+
value: 350,
|
|
1259
|
+
});
|
|
1260
|
+
Object.defineProperty(button, "offsetWidth", {
|
|
1261
|
+
configurable: true,
|
|
1262
|
+
value: 40,
|
|
1263
|
+
});
|
|
1264
|
+
Object.defineProperty(button, "offsetHeight", {
|
|
1265
|
+
configurable: true,
|
|
1266
|
+
value: 30,
|
|
1267
|
+
});
|
|
1268
|
+
button.getBoundingClientRect = () => ({
|
|
1269
|
+
width: 40,
|
|
1270
|
+
height: 30,
|
|
1271
|
+
top: 0,
|
|
1272
|
+
right: 40,
|
|
1273
|
+
bottom: 30,
|
|
1274
|
+
left: 0,
|
|
1275
|
+
x: 0,
|
|
1276
|
+
y: 0,
|
|
1277
|
+
toJSON: () => {},
|
|
1278
|
+
});
|
|
1279
|
+
|
|
1280
|
+
await flushFrames();
|
|
1281
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
1282
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
1283
|
+
|
|
1284
|
+
const observer = TrackingResizeObserver.instances[0];
|
|
1285
|
+
expect(observer.elements).to.include(referenceA);
|
|
1286
|
+
|
|
1287
|
+
bar.setOption("layout.stackedBreakpointContainer", "#reference-b");
|
|
1288
|
+
await flushFrames();
|
|
1289
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
1290
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
1291
|
+
|
|
1292
|
+
expect(observer.elements).to.not.include(referenceA);
|
|
1293
|
+
expect(observer.elements).to.include(referenceB);
|
|
1294
|
+
} finally {
|
|
1295
|
+
window.ResizeObserver = OriginalResizeObserver;
|
|
1296
|
+
globalThis.ResizeObserver = originalGlobalResizeObserver;
|
|
1297
|
+
window.requestAnimationFrame = originalRequestAnimationFrame;
|
|
1298
|
+
globalThis.requestAnimationFrame = originalGlobalRequestAnimationFrame;
|
|
1299
|
+
}
|
|
1300
|
+
});
|
|
846
1301
|
});
|
|
@@ -1613,9 +1613,7 @@ describe('Select', function () {
|
|
|
1613
1613
|
}
|
|
1614
1614
|
});
|
|
1615
1615
|
|
|
1616
|
-
it('should keep empty equivalent matching type-safe for numeric zero', function (
|
|
1617
|
-
this.timeout(2000);
|
|
1618
|
-
|
|
1616
|
+
it('should keep empty equivalent matching type-safe for numeric zero', async function () {
|
|
1619
1617
|
let mocks = document.getElementById('mocks');
|
|
1620
1618
|
const stringEquivalentSelect = document.createElement('monster-select');
|
|
1621
1619
|
stringEquivalentSelect.setOption('empty.equivalents', ['0']);
|
|
@@ -1625,31 +1623,27 @@ describe('Select', function () {
|
|
|
1625
1623
|
numericEquivalentSelect.setOption('empty.equivalents', [0]);
|
|
1626
1624
|
mocks.appendChild(numericEquivalentSelect);
|
|
1627
1625
|
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
numericEquivalentSelect.value = 0;
|
|
1626
|
+
stringEquivalentSelect.value = 0;
|
|
1627
|
+
numericEquivalentSelect.value = 0;
|
|
1631
1628
|
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
unresolved: false
|
|
1641
|
-
}
|
|
1642
|
-
]);
|
|
1643
|
-
|
|
1644
|
-
expect(numericEquivalentSelect.value).to.equal('');
|
|
1645
|
-
expect(numericEquivalentSelect.getOption('selection')).to.deep.equal([]);
|
|
1646
|
-
} catch (e) {
|
|
1647
|
-
return done(e);
|
|
1648
|
-
}
|
|
1629
|
+
await waitForCondition(() => {
|
|
1630
|
+
return stringEquivalentSelect.value === '0' &&
|
|
1631
|
+
Array.isArray(stringEquivalentSelect.getOption('selection')) &&
|
|
1632
|
+
stringEquivalentSelect.getOption('selection').length === 1 &&
|
|
1633
|
+
numericEquivalentSelect.value === '' &&
|
|
1634
|
+
Array.isArray(numericEquivalentSelect.getOption('selection')) &&
|
|
1635
|
+
numericEquivalentSelect.getOption('selection').length === 0;
|
|
1636
|
+
});
|
|
1649
1637
|
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1638
|
+
expect(stringEquivalentSelect.getOption('selection')).to.deep.equal([
|
|
1639
|
+
{
|
|
1640
|
+
label: '0',
|
|
1641
|
+
value: 0,
|
|
1642
|
+
class: 'monster-badge-primary',
|
|
1643
|
+
unresolved: false
|
|
1644
|
+
}
|
|
1645
|
+
]);
|
|
1646
|
+
expect(numericEquivalentSelect.getOption('selection')).to.deep.equal([]);
|
|
1653
1647
|
});
|
|
1654
1648
|
|
|
1655
1649
|
it('should not refetch after selecting an already loaded remote option', function (done) {
|