@schukai/monster 4.141.1 → 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",
|
|
@@ -266,11 +274,14 @@ class ControlBar extends CustomElement {
|
|
|
266
274
|
needsMeasure: true,
|
|
267
275
|
needsLayout: true,
|
|
268
276
|
needsObserve: true,
|
|
277
|
+
initialLayoutPending: true,
|
|
278
|
+
initialLayoutOpacity: undefined,
|
|
269
279
|
suppressSlotChange: false,
|
|
270
280
|
suppressMutation: false,
|
|
271
281
|
};
|
|
272
282
|
|
|
273
283
|
initControlReferences.call(this);
|
|
284
|
+
hideControlBarUntilInitialLayout.call(this);
|
|
274
285
|
initEventHandler.call(this);
|
|
275
286
|
|
|
276
287
|
// setup structure
|
|
@@ -298,9 +309,12 @@ class ControlBar extends CustomElement {
|
|
|
298
309
|
if (
|
|
299
310
|
path === "layout.alignment" ||
|
|
300
311
|
path === "layout.stackedAlignment" ||
|
|
301
|
-
path === "layout.stackedBreakpoint"
|
|
312
|
+
path === "layout.stackedBreakpoint" ||
|
|
313
|
+
path === "layout.stackedBreakpointContainer"
|
|
302
314
|
) {
|
|
303
|
-
syncLayoutState.call(this
|
|
315
|
+
syncLayoutState.call(this, {
|
|
316
|
+
observe: path === "layout.stackedBreakpointContainer",
|
|
317
|
+
});
|
|
304
318
|
}
|
|
305
319
|
return this;
|
|
306
320
|
}
|
|
@@ -352,6 +366,7 @@ class ControlBar extends CustomElement {
|
|
|
352
366
|
attributes.push(ATTRIBUTE_OPTION_LAYOUT_ALIGNMENT);
|
|
353
367
|
attributes.push(ATTRIBUTE_OPTION_LAYOUT_STACKED_ALIGNMENT);
|
|
354
368
|
attributes.push(ATTRIBUTE_OPTION_LAYOUT_STACKED_BREAKPOINT);
|
|
369
|
+
attributes.push(ATTRIBUTE_OPTION_LAYOUT_STACKED_BREAKPOINT_CONTAINER);
|
|
355
370
|
return attributes;
|
|
356
371
|
}
|
|
357
372
|
|
|
@@ -462,6 +477,44 @@ function isElementSelfHidden(element) {
|
|
|
462
477
|
);
|
|
463
478
|
}
|
|
464
479
|
|
|
480
|
+
/**
|
|
481
|
+
* @private
|
|
482
|
+
* @return {void}
|
|
483
|
+
*/
|
|
484
|
+
function hideControlBarUntilInitialLayout() {
|
|
485
|
+
const state = this[layoutStateSymbol];
|
|
486
|
+
if (!state || !(this[controlBarElementSymbol] instanceof HTMLElement)) {
|
|
487
|
+
return;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
state.initialLayoutOpacity = this[controlBarElementSymbol].style.opacity;
|
|
491
|
+
this[controlBarElementSymbol].style.opacity = "0";
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
/**
|
|
495
|
+
* @private
|
|
496
|
+
* @return {void}
|
|
497
|
+
*/
|
|
498
|
+
function revealControlBarAfterInitialLayout() {
|
|
499
|
+
const state = this[layoutStateSymbol];
|
|
500
|
+
if (
|
|
501
|
+
!state ||
|
|
502
|
+
state.initialLayoutPending !== true ||
|
|
503
|
+
!(this[controlBarElementSymbol] instanceof HTMLElement)
|
|
504
|
+
) {
|
|
505
|
+
return;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
state.initialLayoutPending = false;
|
|
509
|
+
const initialOpacity = state.initialLayoutOpacity;
|
|
510
|
+
if (typeof initialOpacity === "string" && initialOpacity !== "") {
|
|
511
|
+
this[controlBarElementSymbol].style.opacity = initialOpacity;
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
this[controlBarElementSymbol].style.removeProperty("opacity");
|
|
516
|
+
}
|
|
517
|
+
|
|
465
518
|
/**
|
|
466
519
|
* @private
|
|
467
520
|
*/
|
|
@@ -549,6 +602,12 @@ function initEventHandler() {
|
|
|
549
602
|
syncLayoutState.call(self);
|
|
550
603
|
};
|
|
551
604
|
|
|
605
|
+
self[attributeObserverSymbol][
|
|
606
|
+
ATTRIBUTE_OPTION_LAYOUT_STACKED_BREAKPOINT_CONTAINER
|
|
607
|
+
] = () => {
|
|
608
|
+
syncLayoutState.call(self, { observe: true });
|
|
609
|
+
};
|
|
610
|
+
|
|
552
611
|
self[resizeObserverSymbol] = new ResizeObserver(() => {
|
|
553
612
|
scheduleLayout.call(self, { measure: true, layout: true });
|
|
554
613
|
});
|
|
@@ -664,6 +723,7 @@ function runLayout() {
|
|
|
664
723
|
})
|
|
665
724
|
.finally(() => {
|
|
666
725
|
state.running = false;
|
|
726
|
+
revealControlBarAfterInitialLayout.call(this);
|
|
667
727
|
if (state.needsObserve || state.needsMeasure || state.needsLayout) {
|
|
668
728
|
scheduleLayoutFrame.call(this);
|
|
669
729
|
}
|
|
@@ -1230,6 +1290,10 @@ function calculateControlBarDimensions() {
|
|
|
1230
1290
|
|
|
1231
1291
|
this[dimensionsSymbol].setVia("data.visible", !(containerWidth === 0));
|
|
1232
1292
|
this[dimensionsSymbol].setVia("data.containerWidth", containerWidth);
|
|
1293
|
+
this[dimensionsSymbol].setVia(
|
|
1294
|
+
"data.stackedBreakpointContainerWidth",
|
|
1295
|
+
getStackedBreakpointContainerWidth.call(this),
|
|
1296
|
+
);
|
|
1233
1297
|
|
|
1234
1298
|
const itemReferences = [];
|
|
1235
1299
|
|
|
@@ -1294,6 +1358,65 @@ function calculateControlBarDimensions() {
|
|
|
1294
1358
|
this[dimensionsSymbol].setVia("data.itemReferences", itemReferences);
|
|
1295
1359
|
}
|
|
1296
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
|
+
|
|
1297
1420
|
/**
|
|
1298
1421
|
* @private
|
|
1299
1422
|
*/
|
|
@@ -1327,20 +1450,21 @@ function updateResizeObserverObservation() {
|
|
|
1327
1450
|
*/
|
|
1328
1451
|
function getLayoutObservedNodes() {
|
|
1329
1452
|
const observedNodes = [];
|
|
1330
|
-
|
|
1331
|
-
if (node instanceof HTMLElement) {
|
|
1453
|
+
const addObservedNode = (node) => {
|
|
1454
|
+
if (node instanceof HTMLElement && !observedNodes.includes(node)) {
|
|
1332
1455
|
observedNodes.push(node);
|
|
1333
1456
|
}
|
|
1334
|
-
}
|
|
1457
|
+
};
|
|
1458
|
+
|
|
1459
|
+
Array.from(this.children).forEach(addObservedNode);
|
|
1335
1460
|
|
|
1336
1461
|
let parent = this.parentNode;
|
|
1337
1462
|
while (!(parent instanceof HTMLElement) && parent !== null) {
|
|
1338
1463
|
parent = parent.parentNode;
|
|
1339
1464
|
}
|
|
1340
1465
|
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
}
|
|
1466
|
+
addObservedNode(parent);
|
|
1467
|
+
addObservedNode(resolveStackedBreakpointContainer.call(this));
|
|
1344
1468
|
|
|
1345
1469
|
return observedNodes;
|
|
1346
1470
|
}
|
|
@@ -1525,11 +1649,16 @@ function applyLayoutAlignment() {
|
|
|
1525
1649
|
|
|
1526
1650
|
/**
|
|
1527
1651
|
* @private
|
|
1652
|
+
* @param {{observe?: boolean}} options
|
|
1528
1653
|
* @return {void}
|
|
1529
1654
|
*/
|
|
1530
|
-
function syncLayoutState() {
|
|
1655
|
+
function syncLayoutState(options = {}) {
|
|
1531
1656
|
applyLayoutAlignment.call(this);
|
|
1532
|
-
scheduleLayout.call(this, {
|
|
1657
|
+
scheduleLayout.call(this, {
|
|
1658
|
+
measure: true,
|
|
1659
|
+
layout: true,
|
|
1660
|
+
observe: options.observe === true,
|
|
1661
|
+
});
|
|
1533
1662
|
}
|
|
1534
1663
|
|
|
1535
1664
|
/**
|
|
@@ -1544,11 +1673,19 @@ function isStackedBreakpointMatched() {
|
|
|
1544
1673
|
|
|
1545
1674
|
let width = 0;
|
|
1546
1675
|
try {
|
|
1547
|
-
width = this[dimensionsSymbol].getVia(
|
|
1548
|
-
|
|
1676
|
+
width = this[dimensionsSymbol].getVia(
|
|
1677
|
+
"data.stackedBreakpointContainerWidth",
|
|
1678
|
+
);
|
|
1679
|
+
} catch {}
|
|
1680
|
+
|
|
1681
|
+
if (!(width > 0)) {
|
|
1549
1682
|
try {
|
|
1550
|
-
width = this[dimensionsSymbol].getVia("data.
|
|
1551
|
-
} catch {
|
|
1683
|
+
width = this[dimensionsSymbol].getVia("data.containerWidth");
|
|
1684
|
+
} catch {
|
|
1685
|
+
try {
|
|
1686
|
+
width = this[dimensionsSymbol].getVia("data.space");
|
|
1687
|
+
} catch {}
|
|
1688
|
+
}
|
|
1552
1689
|
}
|
|
1553
1690
|
|
|
1554
1691
|
if (!(width > 0)) {
|
|
@@ -1678,6 +1815,9 @@ function getLayoutChangedEventDetail() {
|
|
|
1678
1815
|
stacked,
|
|
1679
1816
|
stackedAlignment: this.getOption("layout.stackedAlignment"),
|
|
1680
1817
|
stackedBreakpoint: this.getOption("layout.stackedBreakpoint"),
|
|
1818
|
+
stackedBreakpointContainer: this.getOption(
|
|
1819
|
+
"layout.stackedBreakpointContainer",
|
|
1820
|
+
),
|
|
1681
1821
|
};
|
|
1682
1822
|
}
|
|
1683
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
|
|
|
@@ -734,12 +735,15 @@ describe("ControlBar", function () {
|
|
|
734
735
|
value: 20,
|
|
735
736
|
});
|
|
736
737
|
|
|
738
|
+
expect(controlBar.style.opacity).to.equal("0");
|
|
739
|
+
|
|
737
740
|
await flushFrames();
|
|
738
741
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
739
742
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
740
743
|
|
|
741
744
|
expect(switchButton.hasAttribute("hidden")).to.be.true;
|
|
742
745
|
expect(button.hasAttribute("slot")).to.be.false;
|
|
746
|
+
expect(controlBar.style.opacity).to.equal("");
|
|
743
747
|
expect(controlBar.getAttribute("data-monster-layout-stacked")).to.equal(
|
|
744
748
|
"true",
|
|
745
749
|
);
|
|
@@ -840,4 +844,458 @@ describe("ControlBar", function () {
|
|
|
840
844
|
globalThis.requestAnimationFrame = originalGlobalRequestAnimationFrame;
|
|
841
845
|
}
|
|
842
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
|
+
});
|
|
843
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) {
|