@grantcodes/ui 2.15.7 → 2.16.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 (300) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/custom-elements.json +476 -346
  3. package/package.json +23 -23
  4. package/src/components/accordion/accordion-item.component.js +17 -19
  5. package/src/components/accordion/accordion-item.css +40 -44
  6. package/src/components/accordion/accordion.component.js +7 -7
  7. package/src/components/accordion/accordion.css +5 -5
  8. package/src/components/accordion/accordion.js +6 -6
  9. package/src/components/accordion/accordion.react.js +6 -6
  10. package/src/components/accordion/accordion.stories.js +10 -10
  11. package/src/components/accordion/accordion.test.js +100 -100
  12. package/src/components/accordion/index.js +6 -6
  13. package/src/components/app-bar/app-bar.component.js +86 -92
  14. package/src/components/app-bar/app-bar.css +187 -203
  15. package/src/components/app-bar/app-bar.js +3 -3
  16. package/src/components/app-bar/app-bar.react.js +9 -9
  17. package/src/components/app-bar/app-bar.stories.js +20 -20
  18. package/src/components/app-bar/app-bar.test.js +190 -237
  19. package/src/components/app-bar/index.js +1 -1
  20. package/src/components/app-bar/nav-link.component.js +7 -9
  21. package/src/components/app-bar/nav-link.css +23 -28
  22. package/src/components/app-bar/nav-link.js +3 -3
  23. package/src/components/app-bar/nav-link.react.js +6 -6
  24. package/src/components/avatar/avatar.component.js +79 -68
  25. package/src/components/avatar/avatar.css +18 -18
  26. package/src/components/avatar/avatar.js +3 -3
  27. package/src/components/avatar/avatar.react.js +6 -6
  28. package/src/components/avatar/avatar.stories.js +24 -23
  29. package/src/components/avatar/avatar.test.js +128 -84
  30. package/src/components/avatar/index.js +1 -1
  31. package/src/components/badge/badge.component.js +23 -23
  32. package/src/components/badge/badge.css +39 -52
  33. package/src/components/badge/badge.js +3 -3
  34. package/src/components/badge/badge.react.js +6 -6
  35. package/src/components/badge/badge.stories.js +23 -23
  36. package/src/components/badge/badge.test.js +53 -68
  37. package/src/components/badge/index.js +1 -1
  38. package/src/components/breadcrumb/breadcrumb.component.js +95 -96
  39. package/src/components/breadcrumb/breadcrumb.css +42 -44
  40. package/src/components/breadcrumb/breadcrumb.js +6 -9
  41. package/src/components/breadcrumb/breadcrumb.react.js +9 -12
  42. package/src/components/breadcrumb/breadcrumb.stories.js +5 -5
  43. package/src/components/breadcrumb/breadcrumb.test.js +139 -151
  44. package/src/components/breadcrumb/index.js +1 -1
  45. package/src/components/button/button.component.js +101 -103
  46. package/src/components/button/button.css +90 -94
  47. package/src/components/button/button.js +3 -3
  48. package/src/components/button/button.react.js +6 -6
  49. package/src/components/button/button.stories.js +25 -25
  50. package/src/components/button/button.test.js +324 -344
  51. package/src/components/button/index.js +1 -1
  52. package/src/components/button-group/button-group.component.js +18 -18
  53. package/src/components/button-group/button-group.css +16 -16
  54. package/src/components/button-group/button-group.js +3 -3
  55. package/src/components/button-group/button-group.react.js +6 -6
  56. package/src/components/button-group/button-group.stories.js +23 -23
  57. package/src/components/button-group/button-group.test.js +54 -54
  58. package/src/components/button-group/index.js +1 -1
  59. package/src/components/card/card.component.js +31 -33
  60. package/src/components/card/card.css +85 -85
  61. package/src/components/card/card.js +4 -4
  62. package/src/components/card/card.react.js +6 -6
  63. package/src/components/card/card.stories.js +27 -26
  64. package/src/components/card/card.test.js +56 -56
  65. package/src/components/card/index.js +1 -1
  66. package/src/components/code-preview/code-preview.component.js +47 -48
  67. package/src/components/code-preview/code-preview.css +11 -11
  68. package/src/components/code-preview/code-preview.js +3 -3
  69. package/src/components/code-preview/code-preview.react.js +6 -6
  70. package/src/components/code-preview/code-preview.stories.js +29 -29
  71. package/src/components/code-preview/code-preview.test.js +112 -135
  72. package/src/components/code-preview/index.js +1 -1
  73. package/src/components/container/container.component.js +27 -27
  74. package/src/components/container/container.css +22 -22
  75. package/src/components/container/container.js +3 -3
  76. package/src/components/container/container.react.js +6 -6
  77. package/src/components/container/container.stories.js +44 -44
  78. package/src/components/container/container.test.js +54 -66
  79. package/src/components/container/index.js +1 -1
  80. package/src/components/countdown/countdown.component.js +160 -162
  81. package/src/components/countdown/countdown.css +50 -34
  82. package/src/components/countdown/countdown.js +3 -3
  83. package/src/components/countdown/countdown.react.js +6 -6
  84. package/src/components/countdown/countdown.stories.js +34 -34
  85. package/src/components/countdown/index.js +1 -1
  86. package/src/components/cta/cta.component.js +77 -81
  87. package/src/components/cta/cta.css +24 -24
  88. package/src/components/cta/cta.js +3 -3
  89. package/src/components/cta/cta.react.js +6 -6
  90. package/src/components/cta/cta.stories.js +32 -32
  91. package/src/components/cta/index.js +1 -1
  92. package/src/components/dialog/dialog.component.js +95 -58
  93. package/src/components/dialog/dialog.css +45 -50
  94. package/src/components/dialog/dialog.js +3 -3
  95. package/src/components/dialog/dialog.react.js +6 -6
  96. package/src/components/dialog/dialog.stories.js +38 -29
  97. package/src/components/dialog/dialog.test.js +188 -102
  98. package/src/components/dialog/index.js +1 -1
  99. package/src/components/dropdown/dropdown.component.js +220 -201
  100. package/src/components/dropdown/dropdown.css +60 -84
  101. package/src/components/dropdown/dropdown.js +6 -9
  102. package/src/components/dropdown/dropdown.react.js +15 -15
  103. package/src/components/dropdown/dropdown.stories.js +10 -10
  104. package/src/components/dropdown/dropdown.test.js +211 -157
  105. package/src/components/dropdown/index.js +1 -1
  106. package/src/components/dropzone/dropzone.component.js +126 -129
  107. package/src/components/dropzone/dropzone.css +35 -37
  108. package/src/components/dropzone/dropzone.js +3 -3
  109. package/src/components/dropzone/dropzone.react.js +6 -6
  110. package/src/components/dropzone/dropzone.stories.js +28 -28
  111. package/src/components/dropzone/dropzone.test.js +91 -99
  112. package/src/components/dropzone/index.js +1 -1
  113. package/src/components/feature-list/feature-list.component.js +78 -86
  114. package/src/components/feature-list/feature-list.css +37 -37
  115. package/src/components/feature-list/feature-list.js +3 -3
  116. package/src/components/feature-list/feature-list.react.js +6 -6
  117. package/src/components/feature-list/feature-list.stories.js +86 -90
  118. package/src/components/feature-list/index.js +1 -1
  119. package/src/components/footer/footer-column.component.js +7 -7
  120. package/src/components/footer/footer-column.css +22 -22
  121. package/src/components/footer/footer.component.js +19 -19
  122. package/src/components/footer/footer.css +38 -38
  123. package/src/components/footer/footer.js +8 -8
  124. package/src/components/footer/footer.react.js +9 -9
  125. package/src/components/footer/footer.stories.js +9 -9
  126. package/src/components/footer/footer.test.js +101 -111
  127. package/src/components/footer/index.js +2 -2
  128. package/src/components/form-field/form-field.component.js +167 -125
  129. package/src/components/form-field/form-field.css +24 -24
  130. package/src/components/form-field/form-field.js +3 -3
  131. package/src/components/form-field/form-field.react.js +6 -6
  132. package/src/components/form-field/form-field.stories.js +53 -44
  133. package/src/components/form-field/form-field.test.js +280 -134
  134. package/src/components/form-field/index.js +1 -1
  135. package/src/components/gallery/gallery-image.component.js +30 -30
  136. package/src/components/gallery/gallery-image.js +3 -3
  137. package/src/components/gallery/gallery-image.react.js +6 -6
  138. package/src/components/gallery/gallery.component.js +24 -17
  139. package/src/components/gallery/gallery.css +79 -72
  140. package/src/components/gallery/gallery.js +3 -3
  141. package/src/components/gallery/gallery.react.js +6 -6
  142. package/src/components/gallery/gallery.stories.js +69 -68
  143. package/src/components/gallery/gallery.test.js +95 -88
  144. package/src/components/gallery/index.js +2 -2
  145. package/src/components/hero/hero.component.js +53 -57
  146. package/src/components/hero/hero.css +27 -27
  147. package/src/components/hero/hero.js +3 -3
  148. package/src/components/hero/hero.react.js +6 -6
  149. package/src/components/hero/hero.stories.js +39 -39
  150. package/src/components/hero/index.js +1 -1
  151. package/src/components/icon/icon.component.js +10 -10
  152. package/src/components/icon/icon.css +13 -13
  153. package/src/components/icon/icon.js +4 -4
  154. package/src/components/icon/icon.react.js +6 -6
  155. package/src/components/icon/icon.stories.js +8 -9
  156. package/src/components/icon/icon.test.js +54 -55
  157. package/src/components/icon/index.js +1 -1
  158. package/src/components/loading/index.js +1 -1
  159. package/src/components/loading/loading.component.js +41 -14
  160. package/src/components/loading/loading.css +47 -30
  161. package/src/components/loading/loading.js +4 -4
  162. package/src/components/loading/loading.react.js +6 -6
  163. package/src/components/loading/loading.stories.js +19 -18
  164. package/src/components/loading/loading.test.js +77 -54
  165. package/src/components/logo-cloud/index.js +1 -1
  166. package/src/components/logo-cloud/logo-cloud.component.js +39 -43
  167. package/src/components/logo-cloud/logo-cloud.css +31 -31
  168. package/src/components/logo-cloud/logo-cloud.js +3 -3
  169. package/src/components/logo-cloud/logo-cloud.react.js +6 -6
  170. package/src/components/logo-cloud/logo-cloud.stories.js +80 -80
  171. package/src/components/map/index.js +1 -1
  172. package/src/components/map/map.component.js +103 -107
  173. package/src/components/map/map.css +22 -22
  174. package/src/components/map/map.js +3 -3
  175. package/src/components/map/map.react.js +6 -6
  176. package/src/components/map/map.stories.js +38 -38
  177. package/src/components/media-text/index.js +1 -1
  178. package/src/components/media-text/media-text.component.js +76 -70
  179. package/src/components/media-text/media-text.css +32 -32
  180. package/src/components/media-text/media-text.js +3 -3
  181. package/src/components/media-text/media-text.react.js +6 -6
  182. package/src/components/media-text/media-text.stories.js +45 -45
  183. package/src/components/media-text/media-text.test.js +72 -0
  184. package/src/components/newsletter/index.js +1 -1
  185. package/src/components/newsletter/newsletter.component.js +56 -64
  186. package/src/components/newsletter/newsletter.css +48 -48
  187. package/src/components/newsletter/newsletter.js +3 -3
  188. package/src/components/newsletter/newsletter.react.js +6 -6
  189. package/src/components/newsletter/newsletter.stories.js +35 -35
  190. package/src/components/notice/index.js +1 -1
  191. package/src/components/notice/notice.component.js +66 -55
  192. package/src/components/notice/notice.css +37 -38
  193. package/src/components/notice/notice.js +3 -3
  194. package/src/components/notice/notice.react.js +6 -6
  195. package/src/components/notice/notice.stories.js +54 -53
  196. package/src/components/notice/notice.test.js +210 -156
  197. package/src/components/pagination/index.js +1 -1
  198. package/src/components/pagination/pagination.component.js +44 -44
  199. package/src/components/pagination/pagination.css +6 -6
  200. package/src/components/pagination/pagination.js +4 -4
  201. package/src/components/pagination/pagination.react.js +6 -6
  202. package/src/components/pagination/pagination.stories.js +20 -18
  203. package/src/components/pagination/pagination.test.js +95 -102
  204. package/src/components/person/index.js +1 -1
  205. package/src/components/person/person.component.js +29 -29
  206. package/src/components/person/person.css +17 -17
  207. package/src/components/person/person.js +4 -4
  208. package/src/components/person/person.react.js +6 -6
  209. package/src/components/person/person.stories.js +34 -33
  210. package/src/components/person/person.test.js +55 -68
  211. package/src/components/pricing/index.js +1 -1
  212. package/src/components/pricing/pricing.component.js +58 -70
  213. package/src/components/pricing/pricing.css +65 -65
  214. package/src/components/pricing/pricing.js +3 -3
  215. package/src/components/pricing/pricing.react.js +6 -6
  216. package/src/components/pricing/pricing.stories.js +96 -96
  217. package/src/components/sidebar/index.js +1 -1
  218. package/src/components/sidebar/sidebar.component.js +157 -130
  219. package/src/components/sidebar/sidebar.css +118 -115
  220. package/src/components/sidebar/sidebar.js +4 -4
  221. package/src/components/sidebar/sidebar.react.js +10 -10
  222. package/src/components/sidebar/sidebar.stories.js +10 -10
  223. package/src/components/sidebar/sidebar.test.js +250 -230
  224. package/src/components/stats/index.js +1 -1
  225. package/src/components/stats/stats.component.js +42 -50
  226. package/src/components/stats/stats.css +29 -29
  227. package/src/components/stats/stats.js +3 -3
  228. package/src/components/stats/stats.react.js +6 -6
  229. package/src/components/stats/stats.stories.js +40 -40
  230. package/src/components/tabs/index.js +2 -2
  231. package/src/components/tabs/internal/tabs-button.component.js +16 -27
  232. package/src/components/tabs/internal/tabs-button.js +3 -3
  233. package/src/components/tabs/internal/tabs-button.react.js +6 -6
  234. package/src/components/tabs/internal/tabs-item.component.js +30 -30
  235. package/src/components/tabs/tab.component.js +9 -11
  236. package/src/components/tabs/tab.js +3 -3
  237. package/src/components/tabs/tab.react.js +6 -6
  238. package/src/components/tabs/tabs.component.js +107 -109
  239. package/src/components/tabs/tabs.css +50 -59
  240. package/src/components/tabs/tabs.js +4 -4
  241. package/src/components/tabs/tabs.react.js +6 -6
  242. package/src/components/tabs/tabs.stories.js +23 -22
  243. package/src/components/tabs/tabs.test.js +201 -147
  244. package/src/components/testimonials/index.js +1 -1
  245. package/src/components/testimonials/testimonials.component.js +47 -51
  246. package/src/components/testimonials/testimonials.css +31 -31
  247. package/src/components/testimonials/testimonials.js +3 -3
  248. package/src/components/testimonials/testimonials.react.js +6 -6
  249. package/src/components/testimonials/testimonials.stories.js +49 -51
  250. package/src/components/toast/index.js +1 -1
  251. package/src/components/toast/toast.component.js +217 -148
  252. package/src/components/toast/toast.css +101 -99
  253. package/src/components/toast/toast.js +6 -9
  254. package/src/components/toast/toast.react.js +12 -12
  255. package/src/components/toast/toast.stories.js +75 -49
  256. package/src/components/toast/toast.test.js +260 -221
  257. package/src/components/tooltip/index.js +1 -1
  258. package/src/components/tooltip/tooltip.component.js +56 -49
  259. package/src/components/tooltip/tooltip.css +61 -50
  260. package/src/components/tooltip/tooltip.js +4 -4
  261. package/src/components/tooltip/tooltip.react.js +6 -6
  262. package/src/components/tooltip/tooltip.stories.js +24 -23
  263. package/src/components/tooltip/tooltip.test.js +143 -155
  264. package/src/constructor.test.js +31 -0
  265. package/src/css/base.css +1 -0
  266. package/src/css/colors.stories.js +120 -122
  267. package/src/css/elements/a.css +28 -28
  268. package/src/css/elements/forms/button.css +1 -1
  269. package/src/css/elements/forms/input.css +80 -80
  270. package/src/css/elements/forms/label.css +3 -3
  271. package/src/css/elements/media/image.css +2 -2
  272. package/src/css/elements.stories.js +48 -49
  273. package/src/css/helpers.css +8 -8
  274. package/src/css/layers.stories.js +5 -5
  275. package/src/css/reset.css +102 -102
  276. package/src/css/themes/todomap.css +1 -1
  277. package/src/css/tokens.stories.js +50 -53
  278. package/src/css/typography.css +30 -32
  279. package/src/css/typography.stories.js +92 -92
  280. package/src/css/util/focus-ring.css +12 -20
  281. package/src/css/view-transitions.css +16 -0
  282. package/src/exports.test.js +89 -105
  283. package/src/icons.js +1 -1
  284. package/src/lib/generate-id.js +1 -1
  285. package/src/lib/slot-presence-controller.js +48 -0
  286. package/src/lib/styles/all.css +38 -37
  287. package/src/lib/styles/focus-ring.css +12 -20
  288. package/src/lib/view-transition.js +42 -0
  289. package/src/lib/view-transition.test.js +105 -0
  290. package/src/main.js +29 -29
  291. package/src/pages/agency.stories.js +59 -59
  292. package/src/pages/blog-post.stories.js +48 -48
  293. package/src/pages/saas-landing.stories.js +180 -180
  294. package/src/react.js +44 -44
  295. package/src/react.test.js +42 -51
  296. package/src/test-utils/assert-helpers.js +39 -58
  297. package/src/test-utils/events.js +32 -34
  298. package/src/test-utils/fixture.js +31 -31
  299. package/src/test-utils/index.js +3 -3
  300. package/src/types.d.ts +3 -3
@@ -1,231 +1,251 @@
1
- import { describe, it, afterEach } from "node:test";
2
- import { strict as assert } from "node:assert";
3
- import { fixture, cleanup, click } from "../../test-utils/index.js";
4
- import "./sidebar.js";
5
-
6
- describe("Sidebar Component", () => {
7
- let element;
8
-
9
- afterEach(() => {
10
- cleanup(element);
11
- });
12
-
13
- it("should render with default properties", async () => {
14
- element = await fixture("grantcodes-sidebar");
15
- assert.ok(element, "Element should be created");
16
- assert.ok(element.shadowRoot, "Element should have shadow root");
17
- });
18
-
19
- it("should have left position by default", async () => {
20
- element = await fixture("grantcodes-sidebar");
21
- assert.strictEqual(
22
- element.position,
23
- "left",
24
- "Should have left position by default",
25
- );
26
- });
27
-
28
- it("should not be collapsed by default", async () => {
29
- element = await fixture("grantcodes-sidebar");
30
- assert.strictEqual(
31
- element.collapsed,
32
- false,
33
- "Should not be collapsed by default",
34
- );
35
- });
36
-
37
- it("should be collapsible by default", async () => {
38
- element = await fixture("grantcodes-sidebar");
39
- assert.strictEqual(
40
- element.collapsible,
41
- true,
42
- "Should be collapsible by default",
43
- );
44
- });
45
-
46
- it("should have default width", async () => {
47
- element = await fixture("grantcodes-sidebar");
48
- assert.strictEqual(element.width, "280px", "Should have default width");
49
- });
50
-
51
- it("should render aside element", async () => {
52
- element = await fixture("grantcodes-sidebar");
53
- const aside = element.shadowRoot.querySelector("aside");
54
- assert.ok(aside, "Aside element should exist");
55
- });
56
-
57
- it("should apply left position class", async () => {
58
- element = await fixture("grantcodes-sidebar", {
59
- position: "left",
60
- });
61
-
62
- const sidebar = element.shadowRoot.querySelector(".sidebar--left");
63
- assert.ok(sidebar, "Should have left position class");
64
- });
65
-
66
- it("should apply right position class", async () => {
67
- element = await fixture("grantcodes-sidebar", {
68
- position: "right",
69
- });
70
-
71
- const sidebar = element.shadowRoot.querySelector(".sidebar--right");
72
- assert.ok(sidebar, "Should have right position class");
73
- });
74
-
75
- it("should apply collapsed class when collapsed", async () => {
76
- element = await fixture("grantcodes-sidebar", {
77
- collapsed: true,
78
- });
79
-
80
- const sidebar = element.shadowRoot.querySelector(".sidebar--collapsed");
81
- assert.ok(sidebar, "Should have collapsed class");
82
- });
83
-
84
- it("should render toggle button when collapsible", async () => {
85
- element = await fixture("grantcodes-sidebar", {
86
- collapsible: true,
87
- });
88
-
89
- const toggle = element.shadowRoot.querySelector(".sidebar__toggle");
90
- assert.ok(toggle, "Toggle button should exist");
91
- });
92
-
93
- it("should not render toggle button when not collapsible", async () => {
94
- element = await fixture("grantcodes-sidebar", {
95
- collapsible: false,
96
- });
97
-
98
- const toggle = element.shadowRoot.querySelector(".sidebar__toggle");
99
- assert.ok(!toggle, "Toggle button should not exist");
100
- });
101
-
102
- it("should toggle collapsed state when button is clicked", async () => {
103
- element = await fixture("grantcodes-sidebar", {
104
- collapsible: true,
105
- });
106
-
107
- const toggle = element.shadowRoot.querySelector(".sidebar__toggle");
108
-
109
- assert.strictEqual(element.collapsed, false, "Should start not collapsed");
110
-
111
- click(toggle);
112
- await element.updateComplete;
113
-
114
- assert.strictEqual(
115
- element.collapsed,
116
- true,
117
- "Should be collapsed after click",
118
- );
119
- });
120
-
121
- it("should emit toggle event when collapsed state changes", async () => {
122
- element = await fixture("grantcodes-sidebar", {
123
- collapsible: true,
124
- });
125
-
126
- let toggledState = null;
127
- element.addEventListener("toggle", (e) => {
128
- toggledState = e.detail.collapsed;
129
- });
130
-
131
- const toggle = element.shadowRoot.querySelector(".sidebar__toggle");
132
- click(toggle);
133
-
134
- await element.updateComplete;
135
-
136
- assert.strictEqual(
137
- toggledState,
138
- true,
139
- "Toggle event should fire with collapsed state",
140
- );
141
- });
142
-
143
- it("should apply custom width", async () => {
144
- element = await fixture("grantcodes-sidebar", {
145
- width: "350px",
146
- });
147
-
148
- const aside = element.shadowRoot.querySelector("aside");
149
- const style = aside.getAttribute("style");
150
- assert.ok(
151
- style.includes("--sidebar-width: 350px"),
152
- "Should set custom width",
153
- );
154
- });
155
-
156
- it("should have mobile toggle button", async () => {
157
- element = await fixture("grantcodes-sidebar");
158
- const mobileToggle = element.shadowRoot.querySelector(
159
- ".sidebar__mobile-toggle",
160
- );
161
- assert.ok(mobileToggle, "Mobile toggle button should exist");
162
- });
163
-
164
- it("should have content container", async () => {
165
- element = await fixture("grantcodes-sidebar");
166
- const content = element.shadowRoot.querySelector(".sidebar__content");
167
- assert.ok(content, "Content container should exist");
168
- });
169
-
170
- it("should have slot for content", async () => {
171
- element = await fixture("grantcodes-sidebar");
172
- const slot = element.shadowRoot.querySelector("slot");
173
- assert.ok(slot, "Slot should exist for content");
174
- });
175
-
176
- it("should toggle mobile drawer when mobile toggle is clicked", async () => {
177
- element = await fixture("grantcodes-sidebar");
178
- const mobileToggle = element.shadowRoot.querySelector(
179
- ".sidebar__mobile-toggle",
180
- );
181
-
182
- assert.strictEqual(
183
- element._drawerOpen,
184
- false,
185
- "Drawer should start closed",
186
- );
187
-
188
- click(mobileToggle);
189
- await element.updateComplete;
190
-
191
- assert.strictEqual(
192
- element._drawerOpen,
193
- true,
194
- "Drawer should be open after click",
195
- );
196
- });
197
-
198
- it("should emit drawer-toggle event", async () => {
199
- element = await fixture("grantcodes-sidebar");
200
-
201
- let drawerState = null;
202
- element.addEventListener("drawer-toggle", (e) => {
203
- drawerState = e.detail.open;
204
- });
205
-
206
- const mobileToggle = element.shadowRoot.querySelector(
207
- ".sidebar__mobile-toggle",
208
- );
209
- click(mobileToggle);
210
-
211
- await element.updateComplete;
212
-
213
- assert.strictEqual(drawerState, true, "Drawer toggle event should fire");
214
- });
215
-
216
- it("should render overlay when drawer is open", async () => {
217
- element = await fixture("grantcodes-sidebar");
218
- element._drawerOpen = true;
219
-
220
- await element.updateComplete;
221
-
222
- const overlay = element.shadowRoot.querySelector(".sidebar__overlay");
223
- assert.ok(overlay, "Overlay should exist when drawer is open");
224
- });
225
-
226
- it("should not render overlay when drawer is closed", async () => {
227
- element = await fixture("grantcodes-sidebar");
228
- const overlay = element.shadowRoot.querySelector(".sidebar__overlay");
229
- assert.ok(!overlay, "Overlay should not exist when drawer is closed");
230
- });
1
+ import { strict as assert } from 'node:assert';
2
+ import { afterEach, describe, it } from 'node:test';
3
+ import { cleanup, click, fixture } from '../../test-utils/index.js';
4
+ import './sidebar.js';
5
+
6
+ describe('Sidebar Component', () => {
7
+ let element;
8
+
9
+ afterEach(() => {
10
+ cleanup(element);
11
+ });
12
+
13
+ it('should render with default properties', async () => {
14
+ element = await fixture('grantcodes-sidebar');
15
+ assert.ok(element, 'Element should be created');
16
+ assert.ok(element.shadowRoot, 'Element should have shadow root');
17
+ });
18
+
19
+ it('should have left position by default', async () => {
20
+ element = await fixture('grantcodes-sidebar');
21
+ assert.strictEqual(element.position, 'left', 'Should have left position by default');
22
+ });
23
+
24
+ it('should not be collapsed by default', async () => {
25
+ element = await fixture('grantcodes-sidebar');
26
+ assert.strictEqual(element.collapsed, false, 'Should not be collapsed by default');
27
+ });
28
+
29
+ it('should be collapsible by default', async () => {
30
+ element = await fixture('grantcodes-sidebar');
31
+ assert.strictEqual(element.collapsible, true, 'Should be collapsible by default');
32
+ });
33
+
34
+ it('should have default width', async () => {
35
+ element = await fixture('grantcodes-sidebar');
36
+ assert.strictEqual(element.width, '280px', 'Should have default width');
37
+ });
38
+
39
+ it('should render aside element', async () => {
40
+ element = await fixture('grantcodes-sidebar');
41
+ const aside = element.shadowRoot.querySelector('aside');
42
+ assert.ok(aside, 'Aside element should exist');
43
+ });
44
+
45
+ it('should apply left position class', async () => {
46
+ element = await fixture('grantcodes-sidebar', {
47
+ position: 'left',
48
+ });
49
+
50
+ const sidebar = element.shadowRoot.querySelector('.sidebar--left');
51
+ assert.ok(sidebar, 'Should have left position class');
52
+ });
53
+
54
+ it('should apply right position class', async () => {
55
+ element = await fixture('grantcodes-sidebar', {
56
+ position: 'right',
57
+ });
58
+
59
+ const sidebar = element.shadowRoot.querySelector('.sidebar--right');
60
+ assert.ok(sidebar, 'Should have right position class');
61
+ });
62
+
63
+ it('should apply collapsed class when collapsed', async () => {
64
+ element = await fixture('grantcodes-sidebar', {
65
+ collapsed: true,
66
+ });
67
+
68
+ const sidebar = element.shadowRoot.querySelector('.sidebar--collapsed');
69
+ assert.ok(sidebar, 'Should have collapsed class');
70
+ });
71
+
72
+ it('should render toggle button when collapsible', async () => {
73
+ element = await fixture('grantcodes-sidebar', {
74
+ collapsible: true,
75
+ });
76
+
77
+ const toggle = element.shadowRoot.querySelector('.sidebar__toggle');
78
+ assert.ok(toggle, 'Toggle button should exist');
79
+ });
80
+
81
+ it('should not render toggle button when not collapsible', async () => {
82
+ element = await fixture('grantcodes-sidebar', {
83
+ collapsible: false,
84
+ });
85
+
86
+ const toggle = element.shadowRoot.querySelector('.sidebar__toggle');
87
+ assert.ok(!toggle, 'Toggle button should not exist');
88
+ });
89
+
90
+ it('should toggle collapsed state when button is clicked', async () => {
91
+ element = await fixture('grantcodes-sidebar', {
92
+ collapsible: true,
93
+ });
94
+
95
+ const toggle = element.shadowRoot.querySelector('.sidebar__toggle');
96
+
97
+ assert.strictEqual(element.collapsed, false, 'Should start not collapsed');
98
+
99
+ click(toggle);
100
+ await element.updateComplete;
101
+
102
+ assert.strictEqual(element.collapsed, true, 'Should be collapsed after click');
103
+ });
104
+
105
+ it('should emit toggle event when collapsed state changes', async () => {
106
+ element = await fixture('grantcodes-sidebar', {
107
+ collapsible: true,
108
+ });
109
+
110
+ let toggledState = null;
111
+ element.addEventListener('toggle', (e) => {
112
+ toggledState = e.detail.collapsed;
113
+ });
114
+
115
+ const toggle = element.shadowRoot.querySelector('.sidebar__toggle');
116
+ click(toggle);
117
+
118
+ await element.updateComplete;
119
+
120
+ assert.strictEqual(toggledState, true, 'Toggle event should fire with collapsed state');
121
+ });
122
+
123
+ it('should apply custom width', async () => {
124
+ element = await fixture('grantcodes-sidebar', {
125
+ width: '350px',
126
+ });
127
+
128
+ const aside = element.shadowRoot.querySelector('aside');
129
+ const style = aside.getAttribute('style');
130
+ assert.ok(style.includes('--sidebar-width: 350px'), 'Should set custom width');
131
+ });
132
+
133
+ it('should have mobile toggle button', async () => {
134
+ element = await fixture('grantcodes-sidebar');
135
+ const mobileToggle = element.shadowRoot.querySelector('.sidebar__mobile-toggle');
136
+ assert.ok(mobileToggle, 'Mobile toggle button should exist');
137
+ });
138
+
139
+ it('should have content container', async () => {
140
+ element = await fixture('grantcodes-sidebar');
141
+ const content = element.shadowRoot.querySelector('.sidebar__content');
142
+ assert.ok(content, 'Content container should exist');
143
+ });
144
+
145
+ it('should have slot for content', async () => {
146
+ element = await fixture('grantcodes-sidebar');
147
+ const slot = element.shadowRoot.querySelector('slot');
148
+ assert.ok(slot, 'Slot should exist for content');
149
+ });
150
+
151
+ it('should toggle mobile drawer when mobile toggle is clicked', async () => {
152
+ element = await fixture('grantcodes-sidebar');
153
+ const mobileToggle = element.shadowRoot.querySelector('.sidebar__mobile-toggle');
154
+
155
+ assert.strictEqual(element._drawerOpen, false, 'Drawer should start closed');
156
+
157
+ click(mobileToggle);
158
+ await element.updateComplete;
159
+
160
+ assert.strictEqual(element._drawerOpen, true, 'Drawer should be open after click');
161
+ });
162
+
163
+ it('should emit drawer-toggle event', async () => {
164
+ element = await fixture('grantcodes-sidebar');
165
+
166
+ let drawerState = null;
167
+ element.addEventListener('drawer-toggle', (e) => {
168
+ drawerState = e.detail.open;
169
+ });
170
+
171
+ const mobileToggle = element.shadowRoot.querySelector('.sidebar__mobile-toggle');
172
+ click(mobileToggle);
173
+
174
+ await element.updateComplete;
175
+
176
+ assert.strictEqual(drawerState, true, 'Drawer toggle event should fire');
177
+ });
178
+
179
+ it('should render overlay when drawer is open', async () => {
180
+ element = await fixture('grantcodes-sidebar');
181
+ element._drawerOpen = true;
182
+
183
+ await element.updateComplete;
184
+
185
+ const overlay = element.shadowRoot.querySelector('.sidebar__overlay');
186
+ assert.ok(overlay, 'Overlay should exist when drawer is open');
187
+ });
188
+
189
+ it('should not render overlay when drawer is closed', async () => {
190
+ element = await fixture('grantcodes-sidebar');
191
+ const overlay = element.shadowRoot.querySelector('.sidebar__overlay');
192
+ assert.ok(!overlay, 'Overlay should not exist when drawer is closed');
193
+ });
194
+
195
+ it('should preserve rapid collapse and drawer toggles before the transition update', async () => {
196
+ element = await fixture('grantcodes-sidebar');
197
+ const matchMedia = window.matchMedia;
198
+ const startViewTransition = document.startViewTransition;
199
+ const callbacks = [];
200
+ const collapsedStates = [];
201
+ const drawerStates = [];
202
+ element.addEventListener('toggle', (event) => collapsedStates.push(event.detail.collapsed));
203
+ element.addEventListener('drawer-toggle', (event) => drawerStates.push(event.detail.open));
204
+ window.matchMedia = () => ({ matches: false });
205
+ document.startViewTransition = (callback) => {
206
+ callbacks.push(callback);
207
+ return { finished: Promise.resolve() };
208
+ };
209
+
210
+ try {
211
+ const collapse = element.shadowRoot.querySelector('.sidebar__toggle');
212
+ const drawer = element.shadowRoot.querySelector('.sidebar__mobile-toggle');
213
+ click(collapse);
214
+ click(collapse);
215
+ click(drawer);
216
+ click(drawer);
217
+ for (const callback of callbacks) await callback();
218
+ await element.updateComplete;
219
+
220
+ assert.strictEqual(element.collapsed, false, 'Two clicks should return to expanded');
221
+ assert.strictEqual(element._drawerOpen, false, 'Two clicks should return to closed');
222
+ assert.deepStrictEqual(collapsedStates, [true, false]);
223
+ assert.deepStrictEqual(drawerStates, [true, false]);
224
+ } finally {
225
+ window.matchMedia = matchMedia;
226
+ document.startViewTransition = startViewTransition;
227
+ }
228
+ });
229
+
230
+ it('should run the collapse inside a view transition when motion is allowed', async () => {
231
+ element = await fixture('grantcodes-sidebar', { collapsible: true });
232
+
233
+ const originalMatchMedia = globalThis.window.matchMedia;
234
+ let transitions = 0;
235
+ globalThis.window.matchMedia = () => ({ matches: false });
236
+ globalThis.document.startViewTransition = (callback) => {
237
+ transitions++;
238
+ callback();
239
+ return { finished: Promise.resolve() };
240
+ };
241
+
242
+ click(element.shadowRoot.querySelector('.sidebar__toggle'));
243
+ await element.updateComplete;
244
+
245
+ globalThis.window.matchMedia = originalMatchMedia;
246
+ globalThis.document.startViewTransition = undefined;
247
+
248
+ assert.strictEqual(transitions, 1, 'The collapse should animate');
249
+ assert.strictEqual(element.collapsed, true, 'The collapse should still apply');
250
+ });
231
251
  });
@@ -1 +1 @@
1
- export * from "./stats.js";
1
+ export * from './stats.js';
@@ -1,74 +1,66 @@
1
- import { LitElement, html } from "lit";
2
- import statsStyles from "./stats.css" with { type: "css" };
3
- import "../container/container.js";
1
+ import { html, LitElement } from 'lit';
2
+ import statsStyles from './stats.css' with { type: 'css' };
3
+ import '../container/container.js';
4
4
 
5
5
  export class GrantCodesStats extends LitElement {
6
- static styles = [statsStyles];
6
+ static styles = [statsStyles];
7
7
 
8
- static properties = {
9
- /**
10
- * Optional section heading.
11
- * @type {string}
12
- */
13
- title: { type: String },
14
- /**
15
- * Stat items as a JSON string array: `[{"label":"...","value":"...","context":"..."}]`.
16
- * @type {string}
17
- */
18
- items: { type: String },
19
- /**
20
- * Number of columns in the grid (1–6).
21
- * @type {number}
22
- */
23
- columns: { type: Number },
24
- };
8
+ static properties = {
9
+ /**
10
+ * Optional section heading.
11
+ * @type {string}
12
+ */
13
+ title: { type: String },
14
+ /**
15
+ * Stat items as a JSON string array: `[{"label":"...","value":"...","context":"..."}]`.
16
+ * @type {string}
17
+ */
18
+ items: { type: String },
19
+ /**
20
+ * Number of columns in the grid (1–6).
21
+ * @type {number}
22
+ */
23
+ columns: { type: Number },
24
+ };
25
25
 
26
- constructor() {
27
- super();
28
- this.title = "";
29
- this.items = "[]";
30
- this.columns = 4;
31
- }
26
+ constructor() {
27
+ super();
28
+ this.title = '';
29
+ this.items = '[]';
30
+ this.columns = 4;
31
+ }
32
32
 
33
- get _items() {
34
- try {
35
- return JSON.parse(this.items);
36
- } catch {
37
- return [];
38
- }
39
- }
33
+ get _items() {
34
+ try {
35
+ return JSON.parse(this.items);
36
+ } catch {
37
+ return [];
38
+ }
39
+ }
40
40
 
41
- render() {
42
- const items = this._items;
43
- return html`
41
+ render() {
42
+ const items = this._items;
43
+ return html`
44
44
  <section class="stats">
45
45
  <grantcodes-container>
46
- ${
47
- this.title
48
- ? html`<h2 class="stats__title">${this.title}</h2>`
49
- : null
50
- }
46
+ ${this.title ? html`<h2 class="stats__title">${this.title}</h2>` : null}
51
47
  <ul
52
48
  class="stats__grid"
53
49
  style="--columns: ${this.columns}"
54
50
  role="list"
55
51
  >
56
52
  ${items.map(
57
- (item) => html`
53
+ (item) => html`
58
54
  <li class="stats__item">
59
55
  <span class="stats__value">${item.value}</span>
60
56
  <span class="stats__label">${item.label}</span>
61
- ${
62
- item.context
63
- ? html`<span class="stats__context">${item.context}</span>`
64
- : null
65
- }
57
+ ${item.context ? html`<span class="stats__context">${item.context}</span>` : null}
66
58
  </li>
67
59
  `,
68
- )}
60
+ )}
69
61
  </ul>
70
62
  </grantcodes-container>
71
63
  </section>
72
64
  `;
73
- }
65
+ }
74
66
  }