@schukai/monster 4.148.9 → 4.149.1

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.
Files changed (207) hide show
  1. package/package.json +1 -1
  2. package/source/components/accessibility/stylesheet/locale-picker.mjs +61 -1
  3. package/source/components/content/image-editor.mjs +79 -11
  4. package/source/components/content/style/copy.pcss +1 -1
  5. package/source/components/content/stylesheet/camera-capture.mjs +1 -1
  6. package/source/components/content/stylesheet/copy.mjs +1 -1
  7. package/source/components/content/stylesheet/fetch-box.mjs +1 -1
  8. package/source/components/content/stylesheet/viewer.mjs +61 -1
  9. package/source/components/data/kpi-tile.mjs +5 -3
  10. package/source/components/data/metric-graph.mjs +22 -20
  11. package/source/components/data/metric.mjs +20 -18
  12. package/source/components/data/style/kpi-tile.pcss +65 -43
  13. package/source/components/data/style/metric-graph.pcss +35 -18
  14. package/source/components/data/style/metric.pcss +35 -28
  15. package/source/components/data/stylesheet/kpi-tile.mjs +61 -1
  16. package/source/components/data/stylesheet/metric-graph.mjs +61 -1
  17. package/source/components/data/stylesheet/metric.mjs +61 -1
  18. package/source/components/datatable/columnbar.mjs +4 -2
  19. package/source/components/datatable/datatable.mjs +73 -15
  20. package/source/components/datatable/filter/date-range.mjs +61 -61
  21. package/source/components/datatable/filter/date-time.mjs +5 -1
  22. package/source/components/datatable/filter/range.mjs +3 -3
  23. package/source/components/datatable/filter/select-operator.mjs +7 -1
  24. package/source/components/datatable/filter/text-operator.mjs +11 -3
  25. package/source/components/datatable/filter.mjs +16 -0
  26. package/source/components/datatable/pagination.mjs +122 -23
  27. package/source/components/datatable/style/column-bar.pcss +115 -23
  28. package/source/components/datatable/style/datatable.pcss +257 -29
  29. package/source/components/datatable/style/embedded-pagination.pcss +93 -43
  30. package/source/components/datatable/style/filter-button.pcss +1 -1
  31. package/source/components/datatable/style/filter-controls-defaults.pcss +49 -3
  32. package/source/components/datatable/style/filter-select.pcss +4 -1
  33. package/source/components/datatable/style/filter.pcss +18 -1
  34. package/source/components/datatable/stylesheet/change-button.mjs +61 -1
  35. package/source/components/datatable/stylesheet/column-bar.mjs +69 -1
  36. package/source/components/datatable/stylesheet/dataset.mjs +61 -1
  37. package/source/components/datatable/stylesheet/datatable.mjs +71 -1
  38. package/source/components/datatable/stylesheet/embedded-pagination.mjs +15 -1
  39. package/source/components/datatable/stylesheet/filter-button.mjs +1 -1
  40. package/source/components/datatable/stylesheet/filter-controls-defaults.mjs +1 -1
  41. package/source/components/datatable/stylesheet/filter-select.mjs +61 -1
  42. package/source/components/datatable/stylesheet/filter.mjs +61 -1
  43. package/source/components/datatable/stylesheet/pagination.mjs +67 -1
  44. package/source/components/datatable/stylesheet/save-button.mjs +61 -1
  45. package/source/components/datatable/stylesheet/status.mjs +61 -1
  46. package/source/components/form/action-button.mjs +2 -1
  47. package/source/components/form/button-bar.mjs +7 -2
  48. package/source/components/form/context-error.mjs +4 -3
  49. package/source/components/form/context-help.mjs +10 -4
  50. package/source/components/form/context-hint.mjs +1 -1
  51. package/source/components/form/context-info.mjs +1 -1
  52. package/source/components/form/context-note.mjs +1 -1
  53. package/source/components/form/context-success.mjs +1 -1
  54. package/source/components/form/context-warning.mjs +1 -1
  55. package/source/components/form/control-bar.mjs +300 -14
  56. package/source/components/form/credential-button.mjs +4 -2
  57. package/source/components/form/digits.mjs +28 -1
  58. package/source/components/form/field-set.mjs +30 -0
  59. package/source/components/form/login.mjs +13 -10
  60. package/source/components/form/password.mjs +6 -0
  61. package/source/components/form/popper-button.mjs +1 -1
  62. package/source/components/form/quantity.mjs +14 -6
  63. package/source/components/form/register-wizard.mjs +46 -26
  64. package/source/components/form/select.mjs +17 -10
  65. package/source/components/form/sheet.mjs +13 -2
  66. package/source/components/form/style/action-button.pcss +131 -5
  67. package/source/components/form/style/button-bar.pcss +204 -3
  68. package/source/components/form/style/control-bar.pcss +254 -26
  69. package/source/components/form/style/digits.pcss +7 -6
  70. package/source/components/form/style/input-group.pcss +65 -1
  71. package/source/components/form/style/login.pcss +1 -1
  72. package/source/components/form/style/mixin/field-set-layout.pcss +8 -0
  73. package/source/components/form/style/password.pcss +25 -1
  74. package/source/components/form/style/popper-button.pcss +1 -0
  75. package/source/components/form/style/quantity.pcss +11 -20
  76. package/source/components/form/style/select.pcss +52 -7
  77. package/source/components/form/style/sheet.pcss +70 -1
  78. package/source/components/form/style/state-button.pcss +5 -2
  79. package/source/components/form/style/toggle-switch.pcss +7 -4
  80. package/source/components/form/stylesheet/action-button.mjs +63 -1
  81. package/source/components/form/stylesheet/button-bar.mjs +153 -1
  82. package/source/components/form/stylesheet/button.mjs +67 -1
  83. package/source/components/form/stylesheet/control-bar.mjs +163 -1
  84. package/source/components/form/stylesheet/digits.mjs +1 -1
  85. package/source/components/form/stylesheet/field-layout.mjs +1 -1
  86. package/source/components/form/stylesheet/field-set.mjs +1 -1
  87. package/source/components/form/stylesheet/form.mjs +1 -1
  88. package/source/components/form/stylesheet/input-group.mjs +15 -1
  89. package/source/components/form/stylesheet/login.mjs +1 -1
  90. package/source/components/form/stylesheet/password.mjs +7 -1
  91. package/source/components/form/stylesheet/popper-button.mjs +61 -1
  92. package/source/components/form/stylesheet/quantity.mjs +1 -1
  93. package/source/components/form/stylesheet/register-wizard.mjs +1 -1
  94. package/source/components/form/stylesheet/select.mjs +3 -1
  95. package/source/components/form/stylesheet/sheet.mjs +1 -1
  96. package/source/components/form/stylesheet/state-button.mjs +3 -1
  97. package/source/components/form/stylesheet/toggle-switch.mjs +1 -1
  98. package/source/components/form/stylesheet/wizard.mjs +1 -1
  99. package/source/components/form/util/floating-ui.mjs +35 -7
  100. package/source/components/host/call-button.mjs +3 -1
  101. package/source/components/host/stylesheet/call-button.mjs +61 -1
  102. package/source/components/host/stylesheet/host.mjs +1 -1
  103. package/source/components/host/stylesheet/toggle-button.mjs +61 -1
  104. package/source/components/layout/full-screen.mjs +31 -23
  105. package/source/components/layout/iframe.mjs +5 -1
  106. package/source/components/layout/popper.mjs +34 -4
  107. package/source/components/layout/style/full-screen.pcss +94 -23
  108. package/source/components/layout/style/split-panel.pcss +57 -5
  109. package/source/components/layout/style/tabs.pcss +277 -47
  110. package/source/components/layout/style/vertical-tabs.pcss +147 -0
  111. package/source/components/layout/stylesheet/collapse.mjs +61 -1
  112. package/source/components/layout/stylesheet/details.mjs +61 -1
  113. package/source/components/layout/stylesheet/full-screen.mjs +23 -1
  114. package/source/components/layout/stylesheet/overlay.mjs +1 -1
  115. package/source/components/layout/stylesheet/panel.mjs +1 -1
  116. package/source/components/layout/stylesheet/slider.mjs +1 -1
  117. package/source/components/layout/stylesheet/split-panel.mjs +17 -1
  118. package/source/components/layout/stylesheet/tabs.mjs +97 -1
  119. package/source/components/layout/stylesheet/vertical-tabs.mjs +33 -95
  120. package/source/components/layout/tabs.mjs +63 -0
  121. package/source/components/layout/vertical-tabs.mjs +63 -0
  122. package/source/components/navigation/stylesheet/site-navigation.mjs +1 -1
  123. package/source/components/navigation/stylesheet/table-of-content.mjs +1 -1
  124. package/source/components/navigation/stylesheet/wizard-navigation.mjs +1 -1
  125. package/source/components/navigation/table-of-content.mjs +13 -0
  126. package/source/components/notify/style/notify.pcss +1 -2
  127. package/source/components/notify/stylesheet/message.mjs +1 -1
  128. package/source/components/notify/stylesheet/notify.mjs +1 -1
  129. package/source/components/state/style/log.pcss +1 -0
  130. package/source/components/state/style/state.pcss +5 -1
  131. package/source/components/state/stylesheet/log.mjs +1 -1
  132. package/source/components/state/stylesheet/state.mjs +7 -1
  133. package/source/components/style/button.css +61 -205
  134. package/source/components/style/button.pcss +78 -34
  135. package/source/components/style/common.css +1 -170
  136. package/source/components/style/common.pcss +5 -9
  137. package/source/components/style/form.css +1 -49
  138. package/source/components/style/form.pcss +34 -9
  139. package/source/components/style/link.css +1 -39
  140. package/source/components/style/link.pcss +8 -16
  141. package/source/components/style/mixin/button.pcss +21 -11
  142. package/source/components/style/mixin/property.pcss +8 -0
  143. package/source/components/style/mixin/skeleton.pcss +11 -5
  144. package/source/components/style/normalize.css +1 -144
  145. package/source/components/style/normalize.pcss +5 -5
  146. package/source/components/style/property.css +1 -387
  147. package/source/components/style/skeleton.css +1 -198
  148. package/source/components/style/skeleton.pcss +8 -2
  149. package/source/components/style/theme-modern.css +74 -0
  150. package/source/components/style/theme-modern.pcss +498 -0
  151. package/source/components/stylesheet/button.mjs +61 -1
  152. package/source/components/stylesheet/common.mjs +1 -1
  153. package/source/components/stylesheet/form.mjs +1 -1
  154. package/source/components/stylesheet/link.mjs +1 -1
  155. package/source/components/stylesheet/normalize.mjs +1 -1
  156. package/source/components/stylesheet/property.mjs +1 -1
  157. package/source/components/stylesheet/skeleton.mjs +1 -1
  158. package/source/components/stylesheet/theme-modern.mjs +110 -0
  159. package/source/components/time/stylesheet/day.mjs +61 -1
  160. package/source/components/time/stylesheet/month-calendar.mjs +61 -1
  161. package/source/components/time/timeline/stylesheet/appointment.mjs +61 -1
  162. package/source/components/time/timeline/stylesheet/segment.mjs +61 -1
  163. package/source/components/tree-menu/style/tree-menu.pcss +26 -37
  164. package/source/components/tree-menu/stylesheet/tree-menu.mjs +1 -1
  165. package/test/cases/components/content/image-editor.mjs +19 -0
  166. package/test/cases/components/datatable/accessibility-defaults.mjs +112 -0
  167. package/test/cases/components/datatable/filter-availability.mjs +37 -0
  168. package/test/cases/components/datatable/filter-date-range-layout.mjs +40 -0
  169. package/test/cases/components/datatable/filter-select-operator.mjs +118 -0
  170. package/test/cases/components/datatable/mobile-layout.mjs +254 -0
  171. package/test/cases/components/datatable/pagination.mjs +79 -0
  172. package/test/cases/components/datatable/responsive-breakpoint.mjs +41 -0
  173. package/test/cases/components/datatable/selection.mjs +149 -0
  174. package/test/cases/components/form/action-button-style.mjs +85 -0
  175. package/test/cases/components/form/button-bar.mjs +222 -3
  176. package/test/cases/components/form/context-theme-colors.mjs +59 -0
  177. package/test/cases/components/form/control-bar.mjs +228 -3
  178. package/test/cases/components/form/control-heights.mjs +103 -0
  179. package/test/cases/components/form/digits-accessibility.mjs +65 -0
  180. package/test/cases/components/form/field-set.mjs +54 -0
  181. package/test/cases/components/form/floating-ui.mjs +74 -0
  182. package/test/cases/components/form/input-group.mjs +61 -28
  183. package/test/cases/components/form/login.mjs +47 -0
  184. package/test/cases/components/form/password.mjs +63 -0
  185. package/test/cases/components/form/popper-button.mjs +30 -15
  186. package/test/cases/components/form/quantity-accessibility.mjs +46 -0
  187. package/test/cases/components/form/register-wizard.mjs +79 -0
  188. package/test/cases/components/form/select.mjs +94 -2
  189. package/test/cases/components/form/sheet.mjs +56 -2
  190. package/test/cases/components/host/toggle-button.mjs +50 -0
  191. package/test/cases/components/layout/full-screen.mjs +60 -0
  192. package/test/cases/components/layout/iframe-accessibility.mjs +44 -0
  193. package/test/cases/components/layout/popper.mjs +128 -0
  194. package/test/cases/components/layout/slit-panel.mjs +30 -1
  195. package/test/cases/components/layout/tabs.mjs +93 -0
  196. package/test/cases/components/navigation/table-of-content.mjs +18 -0
  197. package/test/cases/components/state/state-style.mjs +31 -0
  198. package/test/cases/components/style/action-menu.mjs +74 -0
  199. package/test/cases/components/style/button-sizing.mjs +31 -0
  200. package/test/cases/components/style/form-controls.mjs +50 -0
  201. package/test/cases/components/style/kpi-tile.mjs +60 -0
  202. package/test/cases/components/style/legacy-theme-compatibility.mjs +137 -0
  203. package/test/cases/components/style/metric-layout.mjs +94 -0
  204. package/test/cases/components/style/skeleton.mjs +69 -0
  205. package/test/cases/components/style/theme-component-catalog.mjs +853 -0
  206. package/test/cases/components/style/theme-modern.mjs +439 -0
  207. package/test/cases/components/style/theme-review-regressions.mjs +41 -0
@@ -0,0 +1,112 @@
1
+ import * as chai from "chai";
2
+ import { getGlobal } from "../../../../source/types/global.mjs";
3
+ import { initJSDOM } from "../../../util/jsdom.mjs";
4
+ import { ResizeObserverMock } from "../../../util/resize-observer.mjs";
5
+
6
+ const { expect } = chai;
7
+ const nextTask = () => new Promise((resolve) => setTimeout(resolve, 0));
8
+
9
+ describe("Datatable accessibility defaults", function () {
10
+ before(async () => {
11
+ await initJSDOM();
12
+ await import("element-internals-polyfill");
13
+ const global = getGlobal();
14
+ global.ResizeObserver ??= ResizeObserverMock;
15
+ await Promise.all([
16
+ import("../../../../source/components/datatable/filter/date-range.mjs"),
17
+ import("../../../../source/components/datatable/filter/date-time.mjs"),
18
+ import("../../../../source/components/datatable/filter/range.mjs"),
19
+ import(
20
+ "../../../../source/components/datatable/filter/text-operator.mjs"
21
+ ),
22
+ import("../../../../source/components/datatable/columnbar.mjs"),
23
+ ]);
24
+ });
25
+
26
+ afterEach(() => {
27
+ document.getElementById("mocks").replaceChildren();
28
+ });
29
+
30
+ for (const [tag, option, selector] of [
31
+ [
32
+ "monster-filter-date-range",
33
+ "Choose reporting period",
34
+ '[data-monster-role="input"]',
35
+ ],
36
+ [
37
+ "monster-filter-range",
38
+ "Choose score range",
39
+ '[data-monster-role="input"]',
40
+ ],
41
+ ]) {
42
+ it(`should name the ${tag} trigger from labels.button`, async function () {
43
+ const filter = document.createElement(tag);
44
+ filter.setOption("labels.button", option);
45
+ document.getElementById("mocks").append(filter);
46
+ await nextTask();
47
+
48
+ expect(
49
+ filter.shadowRoot.querySelector(selector).getAttribute("aria-label"),
50
+ ).to.equal(option);
51
+ });
52
+ }
53
+
54
+ it("should name the date-time query input", async function () {
55
+ const filter = document.createElement("monster-filter-date-time");
56
+ filter.setOption("labels.query", "Updated after");
57
+ document.getElementById("mocks").append(filter);
58
+ await nextTask();
59
+
60
+ expect(
61
+ filter.shadowRoot
62
+ .querySelector('[data-monster-role="query"]')
63
+ .getAttribute("aria-label"),
64
+ ).to.equal("Updated after");
65
+ });
66
+
67
+ it("should name both text-operator controls", async function () {
68
+ const filter = document.createElement("monster-filter-text-operator");
69
+ filter.setOptions({
70
+ labels: {
71
+ operator: "Comparison",
72
+ query: "Search term",
73
+ },
74
+ });
75
+ document.getElementById("mocks").append(filter);
76
+ await nextTask();
77
+
78
+ expect(
79
+ filter.shadowRoot
80
+ .querySelector('[data-monster-role="operator"]')
81
+ .getAttribute("aria-label"),
82
+ ).to.equal("Comparison");
83
+ expect(
84
+ filter.shadowRoot
85
+ .querySelector('[data-monster-role="query"]')
86
+ .getAttribute("aria-label"),
87
+ ).to.equal("Search term");
88
+ });
89
+
90
+ it("should render column toggles as named stateful buttons", async function () {
91
+ const columnBar = document.createElement("monster-column-bar");
92
+ columnBar.setOption("columns", [
93
+ { index: 0, name: "Name", visible: true },
94
+ { index: 1, name: "Status", visible: false },
95
+ ]);
96
+ document.getElementById("mocks").append(columnBar);
97
+ await nextTask();
98
+
99
+ const buttons = [
100
+ ...columnBar.shadowRoot.querySelectorAll(
101
+ '[data-monster-role="dots"] button[data-monster-role="column"]',
102
+ ),
103
+ ];
104
+ expect(buttons).to.have.length(2);
105
+ expect(
106
+ buttons.map((button) => button.getAttribute("aria-label")),
107
+ ).to.deep.equal(["Name", "Status"]);
108
+ expect(
109
+ buttons.map((button) => button.getAttribute("aria-pressed")),
110
+ ).to.deep.equal(["true", "false"]);
111
+ });
112
+ });
@@ -0,0 +1,37 @@
1
+ import { strict as assert } from "node:assert";
2
+ import { getGlobal } from "../../../../source/types/global.mjs";
3
+ import { initJSDOM } from "../../../util/jsdom.mjs";
4
+ import { ResizeObserverMock } from "../../../util/resize-observer.mjs";
5
+
6
+ describe("Datatable filter availability", () => {
7
+ before(async () => {
8
+ await initJSDOM();
9
+ await import("element-internals-polyfill");
10
+
11
+ const global = getGlobal();
12
+ if (!global.ResizeObserver) {
13
+ global.ResizeObserver = ResizeObserverMock;
14
+ }
15
+
16
+ await import("../../../../source/components/datatable/filter.mjs");
17
+ });
18
+
19
+ afterEach(() => {
20
+ document.getElementById("mocks").replaceChildren();
21
+ });
22
+
23
+ it("disables filter actions when no filter definitions exist", async () => {
24
+ globalThis.name = "";
25
+ globalThis.location = window.location;
26
+ const element = document.createElement("monster-datatable-filter");
27
+ document.getElementById("mocks").append(element);
28
+ await new Promise((resolve) => setTimeout(resolve, 100));
29
+
30
+ for (const role of ["filter-select", "search-button", "reset-button"]) {
31
+ const control = element.shadowRoot.querySelector(
32
+ `[data-monster-role="${role}"]`,
33
+ );
34
+ assert.equal(control.getOption("disabled"), true, role);
35
+ }
36
+ });
37
+ });
@@ -0,0 +1,40 @@
1
+ import { strict as assert } from "node:assert";
2
+ import { getGlobal } from "../../../../source/types/global.mjs";
3
+ import { initJSDOM } from "../../../util/jsdom.mjs";
4
+ import { ResizeObserverMock } from "../../../util/resize-observer.mjs";
5
+
6
+ describe("Datatable date-range filter layout", () => {
7
+ before(async () => {
8
+ await initJSDOM();
9
+ await import("element-internals-polyfill");
10
+
11
+ const global = getGlobal();
12
+ if (!global.ResizeObserver) {
13
+ global.ResizeObserver = ResizeObserverMock;
14
+ }
15
+
16
+ await import(
17
+ "../../../../source/components/datatable/filter/date-range.mjs"
18
+ );
19
+ });
20
+
21
+ afterEach(() => {
22
+ document.getElementById("mocks").replaceChildren();
23
+ });
24
+
25
+ it("keeps its fields inside the adaptive popper content area", async () => {
26
+ const element = document.createElement("monster-filter-date-range");
27
+ document.getElementById("mocks").append(element);
28
+ await new Promise((resolve) => setTimeout(resolve, 0));
29
+
30
+ const popper = element.shadowRoot.querySelector(
31
+ '[data-monster-role="popper"]',
32
+ );
33
+ const content = popper.querySelector("[part=content]");
34
+ assert.ok(content);
35
+
36
+ const filter = content.querySelector("[part=filter]");
37
+ assert.ok(filter);
38
+ assert.ok(filter.querySelector('[data-monster-role="form"]'));
39
+ });
40
+ });
@@ -0,0 +1,118 @@
1
+ import { strict as assert } from "node:assert";
2
+ import { readFileSync } from "node:fs";
3
+ import { getGlobal } from "../../../../source/types/global.mjs";
4
+ import { initJSDOM } from "../../../util/jsdom.mjs";
5
+ import { ResizeObserverMock } from "../../../util/resize-observer.mjs";
6
+
7
+ const filterSelectSource = readFileSync(
8
+ new URL(
9
+ "../../../../source/components/datatable/style/filter-select.pcss",
10
+ import.meta.url,
11
+ ),
12
+ "utf8",
13
+ );
14
+
15
+ const filterSelectGenerated = readFileSync(
16
+ new URL(
17
+ "../../../../source/components/datatable/stylesheet/filter-select.mjs",
18
+ import.meta.url,
19
+ ),
20
+ "utf8",
21
+ );
22
+
23
+ const waitForUpdates = () =>
24
+ new Promise((resolve) => {
25
+ setTimeout(resolve, 300);
26
+ });
27
+
28
+ describe("Datatable select operator filter", () => {
29
+ before(async () => {
30
+ await initJSDOM();
31
+ await import("element-internals-polyfill");
32
+
33
+ const global = getGlobal();
34
+ if (!global.ResizeObserver) {
35
+ global.ResizeObserver = ResizeObserverMock;
36
+ }
37
+
38
+ await import(
39
+ "../../../../source/components/datatable/filter/select-operator.mjs"
40
+ );
41
+ });
42
+
43
+ afterEach(() => {
44
+ document.getElementById("mocks").replaceChildren();
45
+ });
46
+
47
+ it("keeps option markers compact after shared form defaults", () => {
48
+ for (const property of ["height", "min-height", "min-width", "width"]) {
49
+ assert.match(
50
+ filterSelectSource,
51
+ new RegExp(
52
+ `${property}: var\\(--monster-select-option-control-size, 1rem\\)`,
53
+ "u",
54
+ ),
55
+ );
56
+ }
57
+ assert.match(
58
+ filterSelectGenerated,
59
+ /height:var\(--monster-select-option-control-size,1rem\)/u,
60
+ );
61
+ assert.match(
62
+ filterSelectGenerated,
63
+ /min-height:var\(--monster-select-option-control-size,1rem\)/u,
64
+ );
65
+ });
66
+
67
+ it("summarizes multiple selected values inside the fixed-height control", async () => {
68
+ const element = document.createElement(
69
+ "monster-filter-select-operator",
70
+ );
71
+ const options = [
72
+ { value: "ready", label: "Ready" },
73
+ { value: "review", label: "Review" },
74
+ { value: "blocked", label: "Blocked" },
75
+ { value: "progress", label: "In progress" },
76
+ ];
77
+
78
+ element.setOptions({
79
+ select: {
80
+ options,
81
+ },
82
+ });
83
+ document.getElementById("mocks").append(element);
84
+ await waitForUpdates();
85
+ element.selection = options.map(({ value }) => value);
86
+ await waitForUpdates();
87
+
88
+ const select = element.shadowRoot.querySelector(
89
+ "monster-filter-select",
90
+ );
91
+ const summary = select.shadowRoot.querySelector('[part="summary"]');
92
+
93
+ assert.equal(
94
+ select.getAttribute("data-monster-selected-template"),
95
+ "summary",
96
+ );
97
+ assert.ok(summary);
98
+ assert.equal(
99
+ select.shadowRoot.querySelectorAll('[data-monster-role="badge"]')
100
+ .length,
101
+ 0,
102
+ );
103
+ assert.match(
104
+ summary.querySelector('[part="summary-messages"]').textContent,
105
+ /4/u,
106
+ );
107
+ assert.deepEqual(element.selection, [
108
+ "ready",
109
+ "review",
110
+ "blocked",
111
+ "progress",
112
+ ]);
113
+ assert.equal(
114
+ element.value,
115
+ "one-of:ready,review,blocked,progress",
116
+ );
117
+ });
118
+ });
@@ -0,0 +1,254 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { expect } from "chai";
3
+
4
+ const source = readFileSync(
5
+ new URL(
6
+ "../../../../source/components/datatable/style/datatable.pcss",
7
+ import.meta.url,
8
+ ),
9
+ "utf8",
10
+ );
11
+
12
+ const generated = readFileSync(
13
+ new URL(
14
+ "../../../../source/components/datatable/stylesheet/datatable.mjs",
15
+ import.meta.url,
16
+ ),
17
+ "utf8",
18
+ );
19
+
20
+ const columnBarSource = readFileSync(
21
+ new URL(
22
+ "../../../../source/components/datatable/style/column-bar.pcss",
23
+ import.meta.url,
24
+ ),
25
+ "utf8",
26
+ );
27
+
28
+ const columnBarGenerated = readFileSync(
29
+ new URL(
30
+ "../../../../source/components/datatable/stylesheet/column-bar.mjs",
31
+ import.meta.url,
32
+ ),
33
+ "utf8",
34
+ );
35
+
36
+ const embeddedPaginationSource = readFileSync(
37
+ new URL(
38
+ "../../../../source/components/datatable/style/embedded-pagination.pcss",
39
+ import.meta.url,
40
+ ),
41
+ "utf8",
42
+ );
43
+
44
+ const embeddedPaginationGenerated = readFileSync(
45
+ new URL(
46
+ "../../../../source/components/datatable/stylesheet/embedded-pagination.mjs",
47
+ import.meta.url,
48
+ ),
49
+ "utf8",
50
+ );
51
+
52
+ describe("Datatable mobile layout", () => {
53
+ it("groups responsive cells into readable row cards", () => {
54
+ expect(source).to.include("[data-monster-datatable-row-start]");
55
+ expect(source).to.include("[data-monster-datatable-row-end]");
56
+ expect(source).to.include("grid-template-columns:");
57
+ expect(source).to.include("content: attr(data-monster-head)");
58
+ });
59
+
60
+ it("gives mobile actions and multiselects the full card width", () => {
61
+ for (const selector of [
62
+ "monster-button",
63
+ "monster-button-bar",
64
+ "monster-filter-select",
65
+ "monster-select",
66
+ "select[multiple]",
67
+ ]) {
68
+ expect(source).to.include(selector);
69
+ }
70
+
71
+ expect(source).to.include("var(--monster-control-min-block-size, 44px)");
72
+ expect(source).to.include("var(--monster-bg-color-selection-2)");
73
+ expect(source).to.include("width: 100%");
74
+ });
75
+
76
+ it("keeps adjoining mobile row actions visually joined", () => {
77
+ expect(source).to.include("--monster-button-bar-gap: 0");
78
+ expect(source).not.to.include("--monster-button-bar-gap: 0.5rem");
79
+ expect(generated).to.include("--monster-button-bar-gap:0");
80
+ expect(generated).not.to.include("--monster-button-bar-gap:0.5rem");
81
+ });
82
+
83
+ it("stacks labels above values when a responsive card becomes narrow", () => {
84
+ expect(source).to.include("container-name: monster-datatable-responsive");
85
+ expect(source).to.include("container-type: inline-size");
86
+ expect(source).to.include(
87
+ "@container monster-datatable-responsive (max-width: 26rem)",
88
+ );
89
+ expect(source).to.include("max-inline-size: 100%");
90
+ expect(source).to.include("min-inline-size: 0");
91
+ expect(source).to.include("grid-template-columns: minmax(0, 1fr)");
92
+ });
93
+
94
+ it("groups narrow toolbar utilities into a compact action cluster", () => {
95
+ expect(source).to.include('slot[name="bar"]::slotted(*)');
96
+ expect(source).to.include("flex-flow: row wrap");
97
+ expect(source).to.include(
98
+ "--monster-datatable-utility-action-size: 2.25rem",
99
+ );
100
+ expect(source).to.include("margin-inline-start: auto");
101
+ expect(source).to.include("font-size: 0");
102
+ expect(source).to.include("monster-context-help::part(button)");
103
+ expect(source).to.include("var(--monster-control-min-block-size, 44px)");
104
+ expect(columnBarSource).to.include(":host(.small)");
105
+ expect(columnBarSource).to.include(
106
+ "--monster-datatable-utility-action-size: 2.25rem",
107
+ );
108
+ expect(columnBarSource).to.include("font-size: 0");
109
+ expect(columnBarSource).to.include("display: none");
110
+ expect(columnBarSource).to.include("@media (pointer: coarse)");
111
+ expect(generated).to.include(
112
+ "--monster-datatable-utility-action-size:2.25rem",
113
+ );
114
+ expect(generated).to.include("flex-flow:row wrap");
115
+ expect(columnBarGenerated).to.include(
116
+ "--monster-datatable-utility-action-size:2.25rem",
117
+ );
118
+ expect(columnBarGenerated).to.include("font-size:0");
119
+ });
120
+
121
+ it("keeps column visibility dots compact and touch friendly", () => {
122
+ expect(columnBarSource).to.include(
123
+ "var(--monster-column-toggle-target-size, 16px)",
124
+ );
125
+ expect(columnBarSource).to.include(
126
+ "--monster-column-toggle-target-inline-size",
127
+ );
128
+ expect(columnBarSource).to.include(
129
+ "--monster-column-toggle-target-block-size",
130
+ );
131
+ expect(columnBarSource).to.include("& li {");
132
+ expect(columnBarSource).to.include("margin: 0;");
133
+ expect(columnBarSource).not.to.include("margin: 0 5px 0 0");
134
+ expect(columnBarSource).to.include("@media (pointer: coarse)");
135
+ expect(columnBarSource).to.include("min-width: 24px");
136
+ expect(columnBarSource).to.include("min-height: 24px");
137
+ expect(columnBarSource).to.include(
138
+ "min-height: var(--monster-column-settings-target-size, 24px)",
139
+ );
140
+ expect(columnBarGenerated).to.include(
141
+ "[data-monster-role=dots]) li{list-style:none;padding:0;margin:0;",
142
+ );
143
+ expect(columnBarGenerated).not.to.include("margin:0 5px 0 0");
144
+ expect(columnBarGenerated).to.include(
145
+ "--monster-column-toggle-target-size,16px",
146
+ );
147
+ expect(columnBarGenerated).to.include("@media (pointer:coarse)");
148
+ expect(columnBarGenerated).to.include("min-width:24px");
149
+ });
150
+
151
+ it("keeps embedded pagination compact and visually aligned with regular pagination", () => {
152
+ expect(embeddedPaginationSource).to.include("margin: 0");
153
+ expect(embeddedPaginationSource).to.include("grid-template-columns:");
154
+ expect(embeddedPaginationSource).to.include("minmax(max-content, 1fr)");
155
+ expect(embeddedPaginationSource).to.include(
156
+ "var(--monster-control-min-block-size, 44px)",
157
+ );
158
+ expect(embeddedPaginationSource).to.include("min-width:");
159
+ expect(embeddedPaginationSource).to.include(
160
+ "gap: var(--monster-space-1, 2px)",
161
+ );
162
+ expect(embeddedPaginationSource).to.include("overflow: visible");
163
+ expect(embeddedPaginationSource).to.include(
164
+ "border-width: var(",
165
+ );
166
+ expect(embeddedPaginationSource).to.include(
167
+ "--monster-button-border-radius",
168
+ );
169
+ expect(embeddedPaginationSource).to.include(
170
+ "background-color: var(--monster-bg-color-primary-4)",
171
+ );
172
+ expect(embeddedPaginationSource).to.include(
173
+ "padding: 0 var(--monster-space-2, 8px)",
174
+ );
175
+ expect(embeddedPaginationSource).to.include(
176
+ "--monster-embedded-pagination-summary-min-inline-size",
177
+ );
178
+ expect(embeddedPaginationSource).to.include("5rem");
179
+ expect(embeddedPaginationSource).to.include(
180
+ "font-variant-numeric: tabular-nums",
181
+ );
182
+ const compactSummaryStyles = embeddedPaginationSource.slice(
183
+ embeddedPaginationSource.indexOf(
184
+ '& ul.pagination-numbers-hidden [data-monster-role="pagination-summary"],',
185
+ ),
186
+ embeddedPaginationSource.indexOf(
187
+ '& ul.pagination-numbers-hidden [data-monster-role="pagination-item"]',
188
+ ),
189
+ );
190
+ expect(compactSummaryStyles).to.include(
191
+ "padding: 0 var(--monster-space-2, 8px)",
192
+ );
193
+ expect(compactSummaryStyles).to.include(
194
+ "--monster-embedded-pagination-summary-min-inline-size",
195
+ );
196
+ expect(embeddedPaginationSource).to.include(
197
+ "background-color: var(--monster-bg-color-primary-disabled-1)",
198
+ );
199
+ expect(embeddedPaginationSource).to.include(
200
+ "color: var(--monster-color-primary-disabled-1)",
201
+ );
202
+ expect(embeddedPaginationSource).to.include("opacity: 1");
203
+ expect(embeddedPaginationSource).not.to.include("&::after");
204
+ expect(embeddedPaginationSource).not.to.include("@mixin viewport-7");
205
+ });
206
+
207
+ it("ships the generated responsive card styles", () => {
208
+ expect(generated).to.include("data-monster-datatable-row-start");
209
+ expect(generated).to.include("data-monster-datatable-row-end");
210
+ expect(generated).to.include("monster-filter-select");
211
+ expect(generated).to.include("container-name:monster-datatable-responsive");
212
+ expect(generated).to.include("container-type:inline-size");
213
+ expect(generated).to.include(
214
+ "@container monster-datatable-responsive (max-width: 26rem)",
215
+ );
216
+ expect(generated).to.include("slot[name=bar]::slotted(*)");
217
+ expect(generated).to.include("monster-context-help::part(button)");
218
+ expect(embeddedPaginationGenerated).to.include(
219
+ "grid-template-columns:var(--monster-control-min-block-size,44px)",
220
+ );
221
+ expect(embeddedPaginationGenerated).to.include(
222
+ "minmax(max-content,1fr)",
223
+ );
224
+ expect(embeddedPaginationGenerated).to.include(
225
+ "min-height:var(--monster-control-min-block-size,44px)",
226
+ );
227
+ expect(embeddedPaginationGenerated).to.include(
228
+ "gap:var(--monster-space-1,2px)",
229
+ );
230
+ expect(embeddedPaginationGenerated).to.include("overflow:visible");
231
+ expect(embeddedPaginationGenerated).to.include(
232
+ "--monster-button-border-radius",
233
+ );
234
+ expect(embeddedPaginationGenerated).to.include(
235
+ "background-color:var(--monster-bg-color-primary-4)",
236
+ );
237
+ expect(embeddedPaginationGenerated).to.include(
238
+ "padding:0 var(--monster-space-2,8px)",
239
+ );
240
+ expect(embeddedPaginationGenerated).to.include(
241
+ "--monster-embedded-pagination-summary-min-inline-size,5rem",
242
+ );
243
+ expect(embeddedPaginationGenerated).to.include(
244
+ "font-variant-numeric:tabular-nums",
245
+ );
246
+ expect(embeddedPaginationGenerated).to.include(
247
+ "background-color:var(--monster-bg-color-primary-disabled-1)",
248
+ );
249
+ expect(embeddedPaginationGenerated).to.include("opacity:1");
250
+ expect(embeddedPaginationGenerated).not.to.include(
251
+ "[data-monster-role=pagination] ul.pagination-compact:after",
252
+ );
253
+ });
254
+ });
@@ -2,6 +2,7 @@
2
2
  import * as chai from 'chai';
3
3
  import {chaiDom} from "../../../util/chai-dom.mjs";
4
4
  import {initJSDOM} from "../../../util/jsdom.mjs";
5
+ import {currentSupportedLanguages} from "../../../../source/i18n/map/languages.mjs";
5
6
 
6
7
  let expect = chai.expect;
7
8
  chai.use(chaiDom);
@@ -9,6 +10,29 @@ chai.use(chaiDom);
9
10
  let Pagination;
10
11
  let EmbeddedPagination;
11
12
 
13
+ const expectedSummaries = {
14
+ en: "${current} of ${max}",
15
+ de: "Seite ${current} von ${max}",
16
+ es: "Página ${current} de ${max}",
17
+ zh: "第 ${current} 页,共 ${max} 页",
18
+ hi: "पृष्ठ ${current} / ${max}",
19
+ bn: "পৃষ্ঠা ${current} / ${max}",
20
+ pt: "Página ${current} de ${max}",
21
+ ru: "Страница ${current} из ${max}",
22
+ ja: "${max} ページ中 ${current} ページ",
23
+ pa: "ਪੰਨਾ ${current} / ${max}",
24
+ mr: "पृष्ठ ${current} / ${max}",
25
+ fr: "Page ${current} sur ${max}",
26
+ it: "Pagina ${current} di ${max}",
27
+ nl: "Pagina ${current} van ${max}",
28
+ sv: "Sida ${current} av ${max}",
29
+ pl: "Strona ${current} z ${max}",
30
+ da: "Side ${current} af ${max}",
31
+ fi: "Sivu ${current} / ${max}",
32
+ no: "Side ${current} av ${max}",
33
+ cs: "Stránka ${current} z ${max}",
34
+ };
35
+
12
36
  describe('Pagination', function () {
13
37
  this.timeout(10000);
14
38
 
@@ -27,6 +51,61 @@ describe('Pagination', function () {
27
51
  afterEach(() => {
28
52
  let mocks = document.getElementById('mocks');
29
53
  mocks.innerHTML = "";
54
+ document.documentElement.lang = "en";
55
+ });
56
+
57
+ it('provides pagination labels for every supported language', function () {
58
+ expect(Object.keys(expectedSummaries)).to.have.members(currentSupportedLanguages);
59
+
60
+ for (const language of currentSupportedLanguages) {
61
+ document.documentElement.lang = language;
62
+ const control = document.createElement('monster-pagination');
63
+ const labels = control.getOption('labels');
64
+
65
+ expect(labels.summary, language).to.equal(expectedSummaries[language]);
66
+ for (const name of ['page', 'description', 'previous', 'next', 'of']) {
67
+ expect(labels[name], `${language}.${name}`).to.be.a('string').and.not.empty;
68
+ }
69
+ }
70
+ });
71
+
72
+ it('uses canonical ISO language codes and falls back to English', function () {
73
+ for (const [locale, expected] of [
74
+ ['es-MX', expectedSummaries.es],
75
+ ['da-DK', expectedSummaries.da],
76
+ ['sv-SE', expectedSummaries.sv],
77
+ ['sp', expectedSummaries.en],
78
+ ['dk', expectedSummaries.en],
79
+ ['sw', expectedSummaries.en],
80
+ ['eo', expectedSummaries.en],
81
+ ]) {
82
+ document.documentElement.lang = locale;
83
+ const control = document.createElement('monster-pagination');
84
+ expect(control.getOption('labels.summary'), locale).to.equal(expected);
85
+ }
86
+ });
87
+
88
+ it('formats a custom pagination summary label', function (done) {
89
+ const mocks = document.getElementById('mocks');
90
+ const control = document.createElement('monster-pagination');
91
+ mocks.appendChild(control);
92
+
93
+ setTimeout(() => {
94
+ try {
95
+ control.setOption('labels.summary', 'Result ${current} / ${max}');
96
+ control.setPaginationState({currentPage: 2, totalPages: 7});
97
+
98
+ expect(control.getOption('pagination.summary')).to.equal('Result 2 / 7');
99
+ expect(
100
+ control.shadowRoot.querySelector(
101
+ '[data-monster-role=pagination-summary] span'
102
+ ).textContent
103
+ ).to.equal('Result 2 / 7');
104
+ done();
105
+ } catch (error) {
106
+ done(error);
107
+ }
108
+ }, 0);
30
109
  });
31
110
 
32
111
  it('normalizes NaN and avoids NaN hrefs', function (done) {