@schukai/monster 4.152.0 → 4.153.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/README.md +24 -1
- package/package.json +1 -1
- package/source/components/content/viewer/message.mjs +1 -0
- package/source/components/form/select.mjs +2 -0
- package/source/components/form/util/fetch.mjs +6 -7
- package/source/components/state/log.mjs +7 -0
- package/source/components/state/thread.mjs +11 -0
- package/source/components/time/month-calendar.mjs +80 -30
- package/source/dom/resource.mjs +19 -5
- package/source/dom/sanitize-html.mjs +34 -29
- package/test/cases/components/datatable/drag-scroll.mjs +28 -1
- package/test/cases/components/form/button-bar.mjs +9 -6
- package/test/cases/components/form/confirm-button.mjs +4 -4
- package/test/cases/components/form/control-bar.mjs +73 -72
- package/test/cases/components/form/input-group.mjs +3 -1
- package/test/cases/components/form/password.mjs +2 -2
- package/test/cases/components/form/popper-button.mjs +2 -2
- package/test/cases/components/form/select-sort-callback.mjs +57 -0
- package/test/cases/components/form/select.mjs +2 -2
- package/test/cases/components/form/sheet.mjs +13 -1
- package/test/cases/components/layout/popper.mjs +3 -1
- package/test/cases/components/layout/tabs.mjs +4 -1
- package/test/cases/components/navigation/site-navigation.mjs +12 -26
- package/test/cases/components/navigation/table-of-content.mjs +3 -0
- package/test/cases/components/state/ticker-lifecycle.mjs +47 -0
- package/test/cases/components/time/month-calendar.mjs +100 -0
- package/test/cases/data/transformer.mjs +7 -0
- package/test/cases/dom/customcontrol.mjs +2 -6
- package/test/cases/dom/ready.mjs +3 -3
- package/test/cases/dom/resource-availability.mjs +46 -0
- package/test/cases/dom/resourcemanager.mjs +12 -6
- package/test/cases/dom/sanitize-html.mjs +71 -0
- package/test/cases/dom/updater.mjs +1 -1
- package/test/cases/util/processing.mjs +3 -3
|
@@ -392,74 +392,77 @@ describe("ControlBar", function () {
|
|
|
392
392
|
const cssText = ControlBar.getCSSStyleSheet()
|
|
393
393
|
.flatMap((styleSheet) => Array.from(styleSheet.cssRules))
|
|
394
394
|
.map((rule) => rule.cssText)
|
|
395
|
-
.join("\n")
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
expect(cssText).to.contain("
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
395
|
+
.join("\n")
|
|
396
|
+
.replace(/="([\w-]+)"/g, "=$1");
|
|
397
|
+
|
|
398
|
+
const expectCSS = (fragment) => expect(cssText.replace(/\s/g, "")).to.contain(fragment.replace(/\s/g, ""));
|
|
399
|
+
|
|
400
|
+
expectCSS("--monster-control-bar-height");
|
|
401
|
+
expectCSS("slot[name=popper]");
|
|
402
|
+
expectCSS("align-items: stretch");
|
|
403
|
+
expectCSS("[data-monster-layout-alignment=center]");
|
|
404
|
+
expectCSS("justify-content: center");
|
|
405
|
+
expectCSS("::slotted(*)");
|
|
406
|
+
expectCSS("display: flex");
|
|
407
|
+
expectCSS(
|
|
405
408
|
"--monster-control-min-block-size: var(--monster-control-bar-height)",
|
|
406
409
|
);
|
|
407
|
-
|
|
410
|
+
expectCSS(
|
|
408
411
|
"::slotted(monster-control-bar)",
|
|
409
412
|
);
|
|
410
|
-
|
|
413
|
+
expectCSS(
|
|
411
414
|
"--monster-control-min-block-size: inherit",
|
|
412
415
|
);
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
+
expectCSS("::slotted(form)");
|
|
417
|
+
expectCSS("height: var(--monster-control-bar-height)");
|
|
418
|
+
expectCSS(
|
|
416
419
|
"border-width: var(--monster-theme-control-border-width",
|
|
417
420
|
);
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
+
expectCSS("appearance: none");
|
|
422
|
+
expectCSS("--monster-select-container-overflow: hidden");
|
|
423
|
+
expectCSS(
|
|
421
424
|
"--monster-select-control-min-block-size: var(--monster-control-bar-height)",
|
|
422
425
|
);
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
426
|
+
expectCSS("::slotted(monster-button)");
|
|
427
|
+
expectCSS("::slotted(monster-popper-button)");
|
|
428
|
+
expectCSS("--monster-control-bar-item-flex");
|
|
429
|
+
expectCSS("--monster-control-bar-slot-flex");
|
|
430
|
+
expectCSS(
|
|
428
431
|
"--monster-control-bar-button-inline-size",
|
|
429
432
|
);
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
+
expectCSS("::slotted(monster-input-group)");
|
|
434
|
+
expectCSS("::slotted(monster-control-bar-spacer)");
|
|
435
|
+
expectCSS(
|
|
433
436
|
"--monster-control-bar-spacer-line-block-size: 60%",
|
|
434
437
|
);
|
|
435
|
-
|
|
438
|
+
expectCSS(
|
|
436
439
|
"--monster-control-bar-spacer-line-inline-size: calc(100% - 1rem)",
|
|
437
440
|
);
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
441
|
+
expectCSS("--monster-action-menu-padding");
|
|
442
|
+
expectCSS("--monster-action-menu-min-inline-size");
|
|
443
|
+
expectCSS("--monster-action-menu-item-min-block-size");
|
|
444
|
+
expectCSS(
|
|
442
445
|
"--monster-button-border-width: var(--monster-action-menu-item-border-width",
|
|
443
446
|
);
|
|
444
|
-
|
|
447
|
+
expectCSS(
|
|
445
448
|
"--monster-button-border-radius: var(--monster-action-menu-item-border-radius",
|
|
446
449
|
);
|
|
447
|
-
|
|
450
|
+
expectCSS(
|
|
448
451
|
"--monster-button-justify-content: var(--monster-action-menu-item-justify-content",
|
|
449
452
|
);
|
|
450
|
-
|
|
453
|
+
expectCSS(
|
|
451
454
|
"--monster-button-background-color: var(--monster-action-menu-item-background-color",
|
|
452
455
|
);
|
|
453
|
-
|
|
456
|
+
expectCSS(
|
|
454
457
|
"--monster-action-menu-item-hover-background-color",
|
|
455
458
|
);
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
459
|
+
expectCSS("--monster-action-menu-item-focus-outline-offset");
|
|
460
|
+
expectCSS("--monster-button-bar-flex-direction: column");
|
|
461
|
+
expectCSS("--monster-button-bar-height: auto");
|
|
462
|
+
expectCSS(
|
|
460
463
|
":has(>[data-monster-role=switch][hidden])",
|
|
461
464
|
);
|
|
462
|
-
|
|
465
|
+
expectCSS("height: auto !important");
|
|
463
466
|
});
|
|
464
467
|
|
|
465
468
|
it("should join adjacent borders using the smaller computed border width", async function () {
|
|
@@ -530,12 +533,12 @@ describe("ControlBar", function () {
|
|
|
530
533
|
});
|
|
531
534
|
}
|
|
532
535
|
|
|
533
|
-
controls[0].style.
|
|
534
|
-
controls[1].style.
|
|
535
|
-
controls[1].style.
|
|
536
|
-
controls[2].style.
|
|
537
|
-
controls[2].style.
|
|
538
|
-
controls[3].style.
|
|
536
|
+
controls[0].style.borderRight = "3px solid";
|
|
537
|
+
controls[1].style.borderLeft = "2px solid";
|
|
538
|
+
controls[1].style.borderRight = "2px solid";
|
|
539
|
+
controls[2].style.borderLeft = "2px solid";
|
|
540
|
+
controls[2].style.borderRight = "3px solid";
|
|
541
|
+
controls[3].style.borderLeft = "3px solid";
|
|
539
542
|
|
|
540
543
|
await flushFrames();
|
|
541
544
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
@@ -617,8 +620,8 @@ describe("ControlBar", function () {
|
|
|
617
620
|
});
|
|
618
621
|
}
|
|
619
622
|
|
|
620
|
-
button.style.
|
|
621
|
-
innerControl.style.
|
|
623
|
+
button.style.borderRight = "3px solid";
|
|
624
|
+
innerControl.style.borderLeft = "2px solid";
|
|
622
625
|
|
|
623
626
|
await flushFrames();
|
|
624
627
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
@@ -705,8 +708,8 @@ describe("ControlBar", function () {
|
|
|
705
708
|
});
|
|
706
709
|
|
|
707
710
|
const innerButton = control.shadowRoot?.querySelector("button");
|
|
708
|
-
innerButton.style.
|
|
709
|
-
innerButton.style.
|
|
711
|
+
innerButton.style.borderLeft = "3px solid";
|
|
712
|
+
innerButton.style.borderRight = "3px solid";
|
|
710
713
|
innerButton.style.borderRadius = "8px";
|
|
711
714
|
}
|
|
712
715
|
|
|
@@ -720,19 +723,17 @@ describe("ControlBar", function () {
|
|
|
720
723
|
const innerButtons = controls.map((control) =>
|
|
721
724
|
control.shadowRoot?.querySelector("button"),
|
|
722
725
|
);
|
|
723
|
-
expect(innerButtons[0].style.borderRadius).to.equal("8px");
|
|
724
|
-
expect(innerButtons[0].style.borderTopLeftRadius).to.equal("");
|
|
726
|
+
expect(innerButtons[0].style.borderTopLeftRadius || innerButtons[0].style.borderRadius).to.equal("8px");
|
|
725
727
|
expect(innerButtons[0].style.borderTopRightRadius).to.equal("0px");
|
|
726
728
|
expect(innerButtons[0].style.borderBottomRightRadius).to.equal("0px");
|
|
727
|
-
expect(innerButtons[0].style.borderBottomLeftRadius).to.equal("");
|
|
729
|
+
expect(innerButtons[0].style.borderBottomLeftRadius || innerButtons[0].style.borderRadius).to.equal("8px");
|
|
728
730
|
expect(innerButtons[1].style.borderTopLeftRadius).to.equal("0px");
|
|
729
731
|
expect(innerButtons[1].style.borderTopRightRadius).to.equal("0px");
|
|
730
732
|
expect(innerButtons[1].style.borderBottomRightRadius).to.equal("0px");
|
|
731
733
|
expect(innerButtons[1].style.borderBottomLeftRadius).to.equal("0px");
|
|
732
734
|
expect(innerButtons[2].style.borderTopLeftRadius).to.equal("0px");
|
|
733
|
-
expect(innerButtons[2].style.borderRadius).to.equal("8px");
|
|
734
|
-
expect(innerButtons[2].style.
|
|
735
|
-
expect(innerButtons[2].style.borderBottomRightRadius).to.equal("");
|
|
735
|
+
expect(innerButtons[2].style.borderTopRightRadius || innerButtons[2].style.borderRadius).to.equal("8px");
|
|
736
|
+
expect(innerButtons[2].style.borderBottomRightRadius || innerButtons[2].style.borderRadius).to.equal("8px");
|
|
736
737
|
expect(innerButtons[2].style.borderBottomLeftRadius).to.equal("0px");
|
|
737
738
|
} finally {
|
|
738
739
|
window.requestAnimationFrame = originalRequestAnimationFrame;
|
|
@@ -836,10 +837,10 @@ describe("ControlBar", function () {
|
|
|
836
837
|
'[data-monster-role="button"]',
|
|
837
838
|
);
|
|
838
839
|
|
|
839
|
-
releaseInnerButton.style.
|
|
840
|
-
cancelInnerButton.style.
|
|
841
|
-
cancelInnerButton.style.
|
|
842
|
-
followUpInnerButton.style.
|
|
840
|
+
releaseInnerButton.style.borderRight = "3px solid";
|
|
841
|
+
cancelInnerButton.style.borderLeft = "3px solid";
|
|
842
|
+
cancelInnerButton.style.borderRight = "2px solid";
|
|
843
|
+
followUpInnerButton.style.borderLeft = "2px solid";
|
|
843
844
|
|
|
844
845
|
await flushFrames();
|
|
845
846
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
@@ -942,8 +943,8 @@ describe("ControlBar", function () {
|
|
|
942
943
|
'[data-monster-role="button"]',
|
|
943
944
|
);
|
|
944
945
|
|
|
945
|
-
copyInnerButton.style.
|
|
946
|
-
repairInnerButton.style.
|
|
946
|
+
copyInnerButton.style.borderRight = "3px solid";
|
|
947
|
+
repairInnerButton.style.borderLeft = "3px solid";
|
|
947
948
|
|
|
948
949
|
await flushFrames();
|
|
949
950
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
@@ -1020,8 +1021,8 @@ describe("ControlBar", function () {
|
|
|
1020
1021
|
});
|
|
1021
1022
|
}
|
|
1022
1023
|
|
|
1023
|
-
left.style.
|
|
1024
|
-
right.style.
|
|
1024
|
+
left.style.borderRight = "4px solid";
|
|
1025
|
+
right.style.borderLeft = "2px solid";
|
|
1025
1026
|
|
|
1026
1027
|
await flushFrames();
|
|
1027
1028
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
@@ -1104,8 +1105,8 @@ describe("ControlBar", function () {
|
|
|
1104
1105
|
});
|
|
1105
1106
|
}
|
|
1106
1107
|
|
|
1107
|
-
top.style.
|
|
1108
|
-
bottom.style.
|
|
1108
|
+
top.style.borderBottom = "2px solid";
|
|
1109
|
+
bottom.style.borderTop = "5px solid";
|
|
1109
1110
|
|
|
1110
1111
|
await flushFrames();
|
|
1111
1112
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
@@ -1218,7 +1219,7 @@ describe("ControlBar", function () {
|
|
|
1218
1219
|
configurable: true,
|
|
1219
1220
|
value: 20,
|
|
1220
1221
|
});
|
|
1221
|
-
switchButton.style.
|
|
1222
|
+
switchButton.style.borderLeft = "1px solid";
|
|
1222
1223
|
const controlBar = bar.shadowRoot.querySelector(
|
|
1223
1224
|
'[data-monster-role="control-bar"]',
|
|
1224
1225
|
);
|
|
@@ -1241,11 +1242,11 @@ describe("ControlBar", function () {
|
|
|
1241
1242
|
expect(document.getElementById("overflow-button").getAttribute("slot")).to
|
|
1242
1243
|
.equal("popper");
|
|
1243
1244
|
expect(controls[1].style.borderRadius).to.equal("8px");
|
|
1244
|
-
expect(controls[1].style.borderTopLeftRadius).to.equal("");
|
|
1245
|
-
expect(controls[1].style.borderTopRightRadius).to.equal("");
|
|
1245
|
+
expect(controls[1].style.borderTopLeftRadius || controls[1].style.borderRadius).to.equal("8px");
|
|
1246
|
+
expect(controls[1].style.borderTopRightRadius || controls[1].style.borderRadius).to.equal("8px");
|
|
1246
1247
|
expect(controls[2].style.borderRadius).to.equal("8px");
|
|
1247
|
-
expect(controls[2].style.borderTopLeftRadius).to.equal("");
|
|
1248
|
-
expect(controls[2].style.borderTopRightRadius).to.equal("");
|
|
1248
|
+
expect(controls[2].style.borderTopLeftRadius || controls[2].style.borderRadius).to.equal("8px");
|
|
1249
|
+
expect(controls[2].style.borderTopRightRadius || controls[2].style.borderRadius).to.equal("8px");
|
|
1249
1250
|
} finally {
|
|
1250
1251
|
window.ResizeObserver = OriginalResizeObserver;
|
|
1251
1252
|
globalThis.ResizeObserver = originalGlobalResizeObserver;
|
|
@@ -1455,7 +1456,7 @@ describe("ControlBar", function () {
|
|
|
1455
1456
|
class="nucleus-list-main-actions"
|
|
1456
1457
|
data-monster-option-layout-alignment="right"
|
|
1457
1458
|
data-monster-option-layout-stacked-alignment="left"
|
|
1458
|
-
data-monster-option-layout-stacked-breakpoint="
|
|
1459
|
+
data-monster-option-layout-stacked-breakpoint="400px"
|
|
1459
1460
|
>
|
|
1460
1461
|
<button id="reconnect-create">Create</button>
|
|
1461
1462
|
<button id="reconnect-tags">Manage tags</button>
|
|
@@ -49,7 +49,9 @@ describe("InputGroup", () => {
|
|
|
49
49
|
expect(cssText).to.contain("var(--monster-focus-ring-width");
|
|
50
50
|
expect(cssText).to.contain("var(--monster-focus-ring-offset");
|
|
51
51
|
expect(cssText).to.contain("::slotted(input:focus-visible)");
|
|
52
|
-
|
|
52
|
+
const outline = new CSSStyleSheet();
|
|
53
|
+
outline.replaceSync("input { outline: none !important; }");
|
|
54
|
+
expect(cssText).to.contain(outline.cssRules[0].style.cssText.trim());
|
|
53
55
|
expect(cssText).to.contain("box-sizing: content-box !important");
|
|
54
56
|
expect(cssText).to.contain("height: 1.1rem");
|
|
55
57
|
});
|
|
@@ -40,7 +40,7 @@ describe("Password", function () {
|
|
|
40
40
|
it("should preserve the input-group radius around the reveal control", function () {
|
|
41
41
|
const cssText = Password.getCSSStyleSheet()
|
|
42
42
|
.flatMap((styleSheet) => Array.from(styleSheet.cssRules))
|
|
43
|
-
.map((rule) => rule.cssText)
|
|
43
|
+
.map((rule) => rule.cssText.replace(/="([\w-]+)"/g, "=$1"))
|
|
44
44
|
.join("\n");
|
|
45
45
|
|
|
46
46
|
expect(cssText).to.contain("monster-input-group");
|
|
@@ -51,7 +51,7 @@ describe("Password", function () {
|
|
|
51
51
|
it("should vertically center the input and reveal icon in the shared control height", function () {
|
|
52
52
|
const cssText = Password.getCSSStyleSheet()
|
|
53
53
|
.flatMap((styleSheet) => Array.from(styleSheet.cssRules))
|
|
54
|
-
.map((rule) => rule.cssText)
|
|
54
|
+
.map((rule) => rule.cssText.replace(/="([\w-]+)"/g, "=$1"))
|
|
55
55
|
.join("\n");
|
|
56
56
|
|
|
57
57
|
expect(cssText).to.contain("--monster-password-control-block-size");
|
|
@@ -34,7 +34,7 @@ describe("PopperButton", function () {
|
|
|
34
34
|
it("should style native form controls inside popper content", function () {
|
|
35
35
|
const cssText = PopperButton.getCSSStyleSheet()
|
|
36
36
|
.flatMap((styleSheet) => Array.from(styleSheet.cssRules))
|
|
37
|
-
.map((rule) => rule.cssText)
|
|
37
|
+
.map((rule) => rule.cssText.replace(/="([\w-]+)"/g, "=$1"))
|
|
38
38
|
.join("\n")
|
|
39
39
|
.replaceAll(/\s+/g, "");
|
|
40
40
|
|
|
@@ -51,7 +51,7 @@ describe("PopperButton", function () {
|
|
|
51
51
|
it("should preserve focus indicator space inside clipped popper content", function () {
|
|
52
52
|
const cssText = PopperButton.getCSSStyleSheet()
|
|
53
53
|
.flatMap((styleSheet) => Array.from(styleSheet.cssRules))
|
|
54
|
-
.map((rule) => rule.cssText)
|
|
54
|
+
.map((rule) => rule.cssText.replace(/="([\w-]+)"/g, "=$1"))
|
|
55
55
|
.join("\n")
|
|
56
56
|
.replaceAll(/\s+/g, "");
|
|
57
57
|
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { expect } from "chai";
|
|
2
|
+
import { initJSDOM } from "../../../util/jsdom.mjs";
|
|
3
|
+
|
|
4
|
+
describe("Select mapping sorting compatibility", () => {
|
|
5
|
+
before(async () => {
|
|
6
|
+
await initJSDOM();
|
|
7
|
+
await import("../../../../source/components/form/select.mjs");
|
|
8
|
+
});
|
|
9
|
+
afterEach(() => document.getElementById("mocks").replaceChildren());
|
|
10
|
+
for (const [name, sort, expected] of [
|
|
11
|
+
["callback", (a, b) => b[1].localeCompare(a[1]), ["b", "a"]],
|
|
12
|
+
["run", "run:return b[1].localeCompare(a[1]);", ["b", "a"]],
|
|
13
|
+
["ascending", "call:asc", ["a", "b"]],
|
|
14
|
+
["descending", "call:desc", ["b", "a"]],
|
|
15
|
+
["disabled", null, ["b", "a"]],
|
|
16
|
+
]) {
|
|
17
|
+
it(`preserves ${name} mapping`, () => {
|
|
18
|
+
const element = document.createElement("monster-select");
|
|
19
|
+
document.getElementById("mocks").append(element);
|
|
20
|
+
element.setOptions({
|
|
21
|
+
mapping: {
|
|
22
|
+
selector: "*",
|
|
23
|
+
labelTemplate: "label",
|
|
24
|
+
valueTemplate: "value",
|
|
25
|
+
sort,
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
element.importOptions([
|
|
29
|
+
{ value: "b", label: "Beta" },
|
|
30
|
+
{ value: "a", label: "Alpha" },
|
|
31
|
+
]);
|
|
32
|
+
expect(
|
|
33
|
+
element.getOption("options").map((option) => option.value),
|
|
34
|
+
).to.deep.equal(expected);
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
it("passes the owning control to callback sorters", () => {
|
|
38
|
+
const element = document.createElement("monster-select");
|
|
39
|
+
document.getElementById("mocks").append(element);
|
|
40
|
+
let owner;
|
|
41
|
+
element.setOptions({
|
|
42
|
+
mapping: {
|
|
43
|
+
labelTemplate: "label",
|
|
44
|
+
valueTemplate: "value",
|
|
45
|
+
sort: (_a, _b, control) => {
|
|
46
|
+
owner = control;
|
|
47
|
+
return 0;
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
element.importOptions([
|
|
52
|
+
{ value: "a", label: "Alpha" },
|
|
53
|
+
{ value: "b", label: "Beta" },
|
|
54
|
+
]);
|
|
55
|
+
expect(owner).to.equal(element);
|
|
56
|
+
});
|
|
57
|
+
});
|
|
@@ -180,7 +180,7 @@ describe('Select', function () {
|
|
|
180
180
|
it('should expose compact layout variables for constrained control contexts', function () {
|
|
181
181
|
const cssText = SelectStyleSheet
|
|
182
182
|
.cssRules
|
|
183
|
-
? Array.from(SelectStyleSheet.cssRules).map((rule) => rule.cssText).join("\n")
|
|
183
|
+
? Array.from(SelectStyleSheet.cssRules).map((rule) => rule.cssText.replace(/="([\w-]+)"/g, "=$1").replace(/>\s+\[/g, ">[")).join("\n")
|
|
184
184
|
: "";
|
|
185
185
|
|
|
186
186
|
expect(cssText).to.contain("--monster-select-container-overflow");
|
|
@@ -1513,7 +1513,7 @@ describe('Select', function () {
|
|
|
1513
1513
|
|
|
1514
1514
|
it('should keep option list scroll layout stable while overflow toggles', function () {
|
|
1515
1515
|
const cssText = Array.from(SelectStyleSheet.cssRules)
|
|
1516
|
-
.map((rule) => rule.cssText)
|
|
1516
|
+
.map((rule) => rule.cssText.replace(/="([\w-]+)"/g, "=$1").replace(/>\s+\[/g, ">["))
|
|
1517
1517
|
.join('\n')
|
|
1518
1518
|
.replace(/\s+/g, '');
|
|
1519
1519
|
|
|
@@ -98,6 +98,16 @@ describe("Sheet", function () {
|
|
|
98
98
|
clientX: 120,
|
|
99
99
|
clientY: 80,
|
|
100
100
|
});
|
|
101
|
+
// Keep the menu in a deliberately small viewport in every DOM engine.
|
|
102
|
+
const wrapper = sheet.shadowRoot.querySelector('[data-monster-role="grid-wrapper"]');
|
|
103
|
+
Object.defineProperties(wrapper, {
|
|
104
|
+
clientWidth: {configurable:true,value:80},
|
|
105
|
+
clientHeight: {configurable:true,value:40},
|
|
106
|
+
});
|
|
107
|
+
Object.defineProperties(menu, {
|
|
108
|
+
offsetWidth: {configurable:true,value:160},
|
|
109
|
+
offsetHeight: {configurable:true,value:96},
|
|
110
|
+
});
|
|
101
111
|
|
|
102
112
|
input.dispatchEvent(event);
|
|
103
113
|
|
|
@@ -110,7 +120,7 @@ describe("Sheet", function () {
|
|
|
110
120
|
|
|
111
121
|
const cssText = Sheet.getCSSStyleSheet()
|
|
112
122
|
.flatMap((styleSheet) => Array.from(styleSheet.cssRules || []))
|
|
113
|
-
.map((rule) => rule.cssText)
|
|
123
|
+
.map((rule) => rule.cssText.replace(/="([\w-]+)"/g, "=$1").replace(/>\s+\[/g, ">["))
|
|
114
124
|
.join("\n")
|
|
115
125
|
.replace(/\s+/g, "");
|
|
116
126
|
expect(cssText).to.contain("[part=context-menu]{position:absolute");
|
|
@@ -160,6 +170,8 @@ describe("Sheet", function () {
|
|
|
160
170
|
sheet.addEventListener("monster-sheet-resize-column", () => {
|
|
161
171
|
resizeEvents++;
|
|
162
172
|
});
|
|
173
|
+
// Ignore lifecycle frames queued while the element was connected.
|
|
174
|
+
scheduledCallbacks.length = 0;
|
|
163
175
|
|
|
164
176
|
const down = new Event("pointerdown", { bubbles: true });
|
|
165
177
|
Object.assign(down, {
|
|
@@ -41,6 +41,8 @@ describe("Popper", function () {
|
|
|
41
41
|
const popper = host.shadowRoot.querySelector(
|
|
42
42
|
'[data-monster-role="popper"]',
|
|
43
43
|
);
|
|
44
|
+
// Use viewport coordinates in both JSDOM and a real containing block.
|
|
45
|
+
host.setOption("popper.strategy", "fixed");
|
|
44
46
|
|
|
45
47
|
setElementRect(control, {
|
|
46
48
|
x: 100,
|
|
@@ -65,7 +67,7 @@ describe("Popper", function () {
|
|
|
65
67
|
|
|
66
68
|
await waitForCondition(() => popper.style.transform !== "");
|
|
67
69
|
|
|
68
|
-
expect(popper.style.transform).to.contain("translate(60px,140px)");
|
|
70
|
+
expect(popper.style.transform.replaceAll(" ", "")).to.contain("translate(60px,140px)");
|
|
69
71
|
host.hideDialog();
|
|
70
72
|
});
|
|
71
73
|
});
|
|
@@ -356,7 +356,7 @@ describe("Tabs", function () {
|
|
|
356
356
|
);
|
|
357
357
|
expect(cssText).to.contain("--monster-tabs-indicator-inline-size");
|
|
358
358
|
expect(cssText).to.contain("--monster-tabs-indicator-active-opacity");
|
|
359
|
-
expect(cssText).to.
|
|
359
|
+
expect(cssText).to.match(/button::?before/);
|
|
360
360
|
expect(cssText).to.contain("prefers-reduced-motion");
|
|
361
361
|
expect(cssText).to.contain(":focus-visible");
|
|
362
362
|
expect(cssText).to.contain(
|
|
@@ -555,6 +555,9 @@ describe("Tabs", function () {
|
|
|
555
555
|
const switchButton = tabs.shadowRoot.querySelector(
|
|
556
556
|
'[data-monster-role="switch"]',
|
|
557
557
|
);
|
|
558
|
+
// This fixture fits all tabs; expose the switch before testing focus.
|
|
559
|
+
switchButton.hidden = false;
|
|
560
|
+
switchButton.classList.remove("hidden");
|
|
558
561
|
switchButton.click();
|
|
559
562
|
expect(switchButton.getAttribute("aria-expanded")).to.equal("true");
|
|
560
563
|
switchButton.dispatchEvent(
|
|
@@ -30,7 +30,7 @@ describe("SiteNavigation", function () {
|
|
|
30
30
|
);
|
|
31
31
|
});
|
|
32
32
|
|
|
33
|
-
it("should coalesce resize layout updates", function (
|
|
33
|
+
it("should coalesce resize layout updates", async function () {
|
|
34
34
|
const OriginalResizeObserver = window.ResizeObserver;
|
|
35
35
|
const originalGlobalResizeObserver = globalThis.ResizeObserver;
|
|
36
36
|
const originalRequestAnimationFrame = window.requestAnimationFrame;
|
|
@@ -50,11 +50,6 @@ describe("SiteNavigation", function () {
|
|
|
50
50
|
try {
|
|
51
51
|
window.ResizeObserver = TrackingResizeObserver;
|
|
52
52
|
globalThis.ResizeObserver = TrackingResizeObserver;
|
|
53
|
-
window.requestAnimationFrame = (callback) => {
|
|
54
|
-
scheduledCallbacks.push(callback);
|
|
55
|
-
return scheduledCallbacks.length;
|
|
56
|
-
};
|
|
57
|
-
globalThis.requestAnimationFrame = window.requestAnimationFrame;
|
|
58
53
|
|
|
59
54
|
const mocks = document.getElementById("mocks");
|
|
60
55
|
mocks.innerHTML = `
|
|
@@ -72,33 +67,24 @@ describe("SiteNavigation", function () {
|
|
|
72
67
|
);
|
|
73
68
|
expect(resizeObserver).to.exist;
|
|
74
69
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
70
|
+
// Settle initial slot/layout work before measuring resize coalescing.
|
|
71
|
+
await new Promise(resolve => setTimeout(resolve, 260));
|
|
72
|
+
window.requestAnimationFrame = (callback) => {
|
|
73
|
+
scheduledCallbacks.push(callback);
|
|
74
|
+
return scheduledCallbacks.length;
|
|
75
|
+
};
|
|
76
|
+
globalThis.requestAnimationFrame = window.requestAnimationFrame;
|
|
78
77
|
resizeObserver.triggerResize([]);
|
|
79
78
|
resizeObserver.triggerResize([]);
|
|
80
79
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
done();
|
|
86
|
-
} catch (e) {
|
|
87
|
-
done(e);
|
|
88
|
-
} finally {
|
|
89
|
-
window.ResizeObserver = OriginalResizeObserver;
|
|
90
|
-
globalThis.ResizeObserver = originalGlobalResizeObserver;
|
|
91
|
-
window.requestAnimationFrame = originalRequestAnimationFrame;
|
|
92
|
-
globalThis.requestAnimationFrame =
|
|
93
|
-
originalGlobalRequestAnimationFrame;
|
|
94
|
-
}
|
|
95
|
-
}, 260);
|
|
96
|
-
} catch (e) {
|
|
80
|
+
await new Promise(resolve => setTimeout(resolve, 260));
|
|
81
|
+
expect(scheduledCallbacks.length).to.equal(1);
|
|
82
|
+
scheduledCallbacks.shift()();
|
|
83
|
+
} finally {
|
|
97
84
|
window.ResizeObserver = OriginalResizeObserver;
|
|
98
85
|
globalThis.ResizeObserver = originalGlobalResizeObserver;
|
|
99
86
|
window.requestAnimationFrame = originalRequestAnimationFrame;
|
|
100
87
|
globalThis.requestAnimationFrame = originalGlobalRequestAnimationFrame;
|
|
101
|
-
done(e);
|
|
102
88
|
}
|
|
103
89
|
});
|
|
104
90
|
});
|
|
@@ -73,6 +73,7 @@ describe('TableOfContent', function () {
|
|
|
73
73
|
tableOfContent.appendChild(paragraph);
|
|
74
74
|
|
|
75
75
|
Object.defineProperties(outerScroller, {
|
|
76
|
+
scrollTop: { configurable: true, writable: true, value: 0 },
|
|
76
77
|
clientHeight: { configurable: true, value: 200 },
|
|
77
78
|
scrollHeight: { configurable: true, value: 600 },
|
|
78
79
|
clientWidth: { configurable: true, value: 300 },
|
|
@@ -135,6 +136,7 @@ describe('TableOfContent', function () {
|
|
|
135
136
|
tableOfContent.appendChild(heading);
|
|
136
137
|
|
|
137
138
|
Object.defineProperties(outerScroller, {
|
|
139
|
+
scrollTop: { configurable: true, writable: true, value: 0 },
|
|
138
140
|
clientHeight: { configurable: true, value: 200 },
|
|
139
141
|
scrollHeight: { configurable: true, value: 600 },
|
|
140
142
|
clientWidth: { configurable: true, value: 300 },
|
|
@@ -187,6 +189,7 @@ describe('TableOfContent', function () {
|
|
|
187
189
|
tableOfContent.appendChild(heading);
|
|
188
190
|
|
|
189
191
|
Object.defineProperties(outerScroller, {
|
|
192
|
+
scrollTop: { configurable: true, writable: true, value: 0 },
|
|
190
193
|
clientHeight: { configurable: true, value: 200 },
|
|
191
194
|
scrollHeight: { configurable: true, value: 600 },
|
|
192
195
|
clientWidth: { configurable: true, value: 300 },
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { expect } from "chai";
|
|
2
|
+
import sinon from "sinon";
|
|
3
|
+
import { initJSDOM } from "../../../util/jsdom.mjs";
|
|
4
|
+
|
|
5
|
+
describe("State component tickers", () => {
|
|
6
|
+
before(async () => {
|
|
7
|
+
await initJSDOM();
|
|
8
|
+
await import("../../../../source/components/state/log.mjs");
|
|
9
|
+
await import("../../../../source/components/state/thread.mjs");
|
|
10
|
+
});
|
|
11
|
+
for (const tag of ["monster-log", "monster-thread"]) {
|
|
12
|
+
it(`${tag} stops on removal and resumes once without losing options`, () => {
|
|
13
|
+
const intervals = new Map();
|
|
14
|
+
let sequence = 0;
|
|
15
|
+
const set = sinon
|
|
16
|
+
.stub(globalThis, "setInterval")
|
|
17
|
+
.callsFake((callback, delay) => {
|
|
18
|
+
const id = ++sequence;
|
|
19
|
+
intervals.set(id, { callback, delay });
|
|
20
|
+
return id;
|
|
21
|
+
});
|
|
22
|
+
const clear = sinon
|
|
23
|
+
.stub(globalThis, "clearInterval")
|
|
24
|
+
.callsFake((id) => intervals.delete(id));
|
|
25
|
+
let element;
|
|
26
|
+
try {
|
|
27
|
+
element = document.createElement(tag);
|
|
28
|
+
element.setOption("updateFrequency", 1234);
|
|
29
|
+
document.getElementById("mocks").append(element);
|
|
30
|
+
expect(intervals.size).to.equal(1);
|
|
31
|
+
expect([...intervals.values()][0].delay).to.equal(1234);
|
|
32
|
+
const entries = element.getOption("entries");
|
|
33
|
+
element.remove();
|
|
34
|
+
expect(intervals.size).to.equal(0);
|
|
35
|
+
document.getElementById("mocks").append(element);
|
|
36
|
+
element.connectedCallback();
|
|
37
|
+
expect(intervals.size).to.equal(1);
|
|
38
|
+
expect(element.getOption("entries")).to.equal(entries);
|
|
39
|
+
expect(element.getOption("updateFrequency")).to.equal(1234);
|
|
40
|
+
} finally {
|
|
41
|
+
element?.remove();
|
|
42
|
+
set.restore();
|
|
43
|
+
clear.restore();
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
});
|