@schukai/monster 4.148.8 → 4.149.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.
Files changed (209) 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 +2 -2
  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 +63 -34
  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 +1 -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 +61 -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 +24 -6
  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 +19 -10
  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/source/dom/customelement.mjs +105 -40
  166. package/source/dom/updater.mjs +381 -119
  167. package/test/cases/components/content/image-editor.mjs +19 -0
  168. package/test/cases/components/datatable/accessibility-defaults.mjs +112 -0
  169. package/test/cases/components/datatable/filter-availability.mjs +37 -0
  170. package/test/cases/components/datatable/filter-date-range-layout.mjs +40 -0
  171. package/test/cases/components/datatable/filter-select-operator.mjs +118 -0
  172. package/test/cases/components/datatable/mobile-layout.mjs +203 -0
  173. package/test/cases/components/datatable/responsive-breakpoint.mjs +41 -0
  174. package/test/cases/components/datatable/selection.mjs +149 -0
  175. package/test/cases/components/form/action-button-style.mjs +85 -0
  176. package/test/cases/components/form/button-bar.mjs +222 -3
  177. package/test/cases/components/form/context-theme-colors.mjs +59 -0
  178. package/test/cases/components/form/control-bar.mjs +228 -3
  179. package/test/cases/components/form/control-heights.mjs +103 -0
  180. package/test/cases/components/form/digits-accessibility.mjs +65 -0
  181. package/test/cases/components/form/field-set.mjs +54 -0
  182. package/test/cases/components/form/floating-ui.mjs +74 -0
  183. package/test/cases/components/form/input-group.mjs +61 -28
  184. package/test/cases/components/form/login.mjs +47 -0
  185. package/test/cases/components/form/password.mjs +63 -0
  186. package/test/cases/components/form/popper-button.mjs +30 -15
  187. package/test/cases/components/form/quantity-accessibility.mjs +46 -0
  188. package/test/cases/components/form/register-wizard.mjs +79 -0
  189. package/test/cases/components/form/select.mjs +94 -2
  190. package/test/cases/components/form/sheet.mjs +56 -2
  191. package/test/cases/components/host/toggle-button.mjs +50 -0
  192. package/test/cases/components/layout/full-screen.mjs +60 -0
  193. package/test/cases/components/layout/iframe-accessibility.mjs +44 -0
  194. package/test/cases/components/layout/popper.mjs +128 -0
  195. package/test/cases/components/layout/slit-panel.mjs +30 -1
  196. package/test/cases/components/layout/tabs.mjs +93 -0
  197. package/test/cases/components/navigation/table-of-content.mjs +18 -0
  198. package/test/cases/components/state/state-style.mjs +31 -0
  199. package/test/cases/components/style/action-menu.mjs +74 -0
  200. package/test/cases/components/style/form-controls.mjs +37 -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
  208. package/test/cases/dom/customelement.mjs +136 -1
  209. package/test/cases/dom/updater.mjs +174 -0
@@ -1,5 +1,6 @@
1
1
  'use strict';
2
2
 
3
+ import {readFileSync} from "node:fs";
3
4
  import * as chai from 'chai';
4
5
  import {getDocument} from "../../../../source/dom/util.mjs";
5
6
  import {chaiDom} from "../../../util/chai-dom.mjs";
@@ -90,4 +91,22 @@ describe('ImageEditor', function () {
90
91
  }
91
92
  }, 20);
92
93
  });
94
+
95
+ it('keeps narrow split-panel form controls in full-width field rows', function () {
96
+ const source = readFileSync(
97
+ new URL(
98
+ "../../../../source/components/content/image-editor.mjs",
99
+ import.meta.url,
100
+ ),
101
+ "utf8",
102
+ );
103
+
104
+ expect(source).to.include(".image-editor-field");
105
+ expect(source).to.include(
106
+ '<label class="grid-span-full image-editor-field">',
107
+ );
108
+ expect(source).to.include(
109
+ '<monster-button-bar class="grid-span-full monster-margin-top-2">',
110
+ );
111
+ });
93
112
  });
@@ -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,203 @@
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 touch friendly", () => {
152
+ expect(embeddedPaginationSource).to.include("margin: 0");
153
+ expect(embeddedPaginationSource).to.include("grid-template-columns:");
154
+ expect(embeddedPaginationSource).to.include(
155
+ "var(--monster-control-min-block-size, 44px)",
156
+ );
157
+ expect(embeddedPaginationSource).to.include("min-width:");
158
+ expect(embeddedPaginationSource).to.include("&::after");
159
+ expect(embeddedPaginationSource).to.include(
160
+ "border: 1px solid var(--monster-theme-control-border-color)",
161
+ );
162
+ expect(embeddedPaginationSource).not.to.include(
163
+ "inset 0 0 0 1px",
164
+ );
165
+ expect(embeddedPaginationSource).to.include(
166
+ "background-color: var(--monster-bg-color-primary-disabled-1)",
167
+ );
168
+ expect(embeddedPaginationSource).to.include(
169
+ "color: var(--monster-color-primary-disabled-1)",
170
+ );
171
+ expect(embeddedPaginationSource).to.include("opacity: 1");
172
+ expect(embeddedPaginationSource).not.to.include("@mixin viewport-7");
173
+ });
174
+
175
+ it("ships the generated responsive card styles", () => {
176
+ expect(generated).to.include("data-monster-datatable-row-start");
177
+ expect(generated).to.include("data-monster-datatable-row-end");
178
+ expect(generated).to.include("monster-filter-select");
179
+ expect(generated).to.include("container-name:monster-datatable-responsive");
180
+ expect(generated).to.include("container-type:inline-size");
181
+ expect(generated).to.include(
182
+ "@container monster-datatable-responsive (max-width: 26rem)",
183
+ );
184
+ expect(generated).to.include("slot[name=bar]::slotted(*)");
185
+ expect(generated).to.include("monster-context-help::part(button)");
186
+ expect(embeddedPaginationGenerated).to.include(
187
+ "grid-template-columns:var(--monster-control-min-block-size,44px)",
188
+ );
189
+ expect(embeddedPaginationGenerated).to.include(
190
+ "min-height:var(--monster-control-min-block-size,44px)",
191
+ );
192
+ expect(embeddedPaginationGenerated).to.include(
193
+ "border:1px solid var(--monster-theme-control-border-color)",
194
+ );
195
+ expect(embeddedPaginationGenerated).to.include(
196
+ "background-color:var(--monster-bg-color-primary-disabled-1)",
197
+ );
198
+ expect(embeddedPaginationGenerated).to.include("opacity:1");
199
+ expect(embeddedPaginationGenerated).not.to.include(
200
+ "box-shadow:inset 0 0 0 1px",
201
+ );
202
+ });
203
+ });
@@ -0,0 +1,41 @@
1
+ import { expect } from "chai";
2
+ import { initJSDOM } from "../../../util/jsdom.mjs";
3
+ import { ResizeObserverMock } from "../../../util/resize-observer.mjs";
4
+
5
+ const nextTask = () => new Promise((resolve) => setTimeout(resolve, 0));
6
+ let declarativeBreakpoint;
7
+
8
+ describe("Datatable responsive breakpoint", function () {
9
+ before(async function () {
10
+ await initJSDOM();
11
+ await import("element-internals-polyfill").catch(() => {});
12
+ globalThis.ResizeObserver ??= ResizeObserverMock;
13
+
14
+ document.getElementById("mocks").innerHTML = `
15
+ <monster-datatable
16
+ data-monster-responsive-breakpoint="720"
17
+ ></monster-datatable>
18
+ `;
19
+ await import("../../../../source/components/datatable/datatable.mjs");
20
+ await nextTask();
21
+
22
+ declarativeBreakpoint = document
23
+ .querySelector("monster-datatable")
24
+ .getOption("responsive.breakpoint");
25
+ document.getElementById("mocks").replaceChildren();
26
+ });
27
+
28
+ afterEach(function () {
29
+ document.getElementById("mocks").replaceChildren();
30
+ });
31
+
32
+ it("uses 960px as the default responsive threshold", function () {
33
+ const datatable = document.createElement("monster-datatable");
34
+
35
+ expect(datatable.getOption("responsive.breakpoint")).to.equal(960);
36
+ });
37
+
38
+ it("keeps the responsive breakpoint attribute as an explicit override", function () {
39
+ expect(declarativeBreakpoint).to.equal(720);
40
+ });
41
+ });