@grantcodes/ui 2.15.6 → 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 +67 -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,238 +1,191 @@
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 "./app-bar.js";
5
-
6
- describe("App Bar 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-app-bar");
15
- assert.ok(element, "Element should be created");
16
- assert.ok(element.shadowRoot, "Element should have shadow root");
17
- });
18
-
19
- it("should not be sticky by default", async () => {
20
- element = await fixture("grantcodes-app-bar");
21
- assert.strictEqual(
22
- element.sticky,
23
- false,
24
- "Should not be sticky by default",
25
- );
26
- });
27
-
28
- it("should not be transparent by default", async () => {
29
- element = await fixture("grantcodes-app-bar");
30
- assert.strictEqual(
31
- element.transparent,
32
- false,
33
- "Should not be transparent by default",
34
- );
35
- });
36
-
37
- it("should render header element", async () => {
38
- element = await fixture("grantcodes-app-bar");
39
- const header = element.shadowRoot.querySelector("header");
40
- assert.ok(header, "Header element should exist");
41
- });
42
-
43
- it("should not have sticky class by default", async () => {
44
- element = await fixture("grantcodes-app-bar");
45
- const appBar = element.shadowRoot.querySelector(".app-bar--sticky");
46
- assert.ok(!appBar, "Should not have sticky class by default");
47
- });
48
-
49
- it("should apply sticky class when sticky", async () => {
50
- element = await fixture("grantcodes-app-bar", {
51
- sticky: true,
52
- });
53
-
54
- const appBar = element.shadowRoot.querySelector(".app-bar--sticky");
55
- assert.ok(appBar, "Should have sticky class");
56
- });
57
-
58
- it("should reflect sticky to the host attribute", async () => {
59
- element = await fixture("grantcodes-app-bar", {
60
- sticky: true,
61
- });
62
-
63
- assert.ok(element.hasAttribute("sticky"), "Should reflect sticky attribute");
64
- });
65
-
66
- it("should apply transparent class when transparent", async () => {
67
- element = await fixture("grantcodes-app-bar", {
68
- transparent: true,
69
- });
70
-
71
- const appBar = element.shadowRoot.querySelector(".app-bar--transparent");
72
- assert.ok(appBar, "Should have transparent class");
73
- });
74
-
75
- it("should reflect transparent to the host attribute", async () => {
76
- element = await fixture("grantcodes-app-bar", {
77
- transparent: true,
78
- });
79
-
80
- assert.ok(
81
- element.hasAttribute("transparent"),
82
- "Should reflect transparent attribute",
83
- );
84
- });
85
-
86
- it("should have logo slot", async () => {
87
- element = await fixture("grantcodes-app-bar");
88
- const logoSlot = element.shadowRoot.querySelector('slot[name="logo"]');
89
- assert.ok(logoSlot, "Logo slot should exist");
90
- });
91
-
92
- it("should have nav slot", async () => {
93
- element = await fixture("grantcodes-app-bar");
94
- const navSlot = element.shadowRoot.querySelector('slot[name="nav"]');
95
- assert.ok(navSlot, "Nav slot should exist");
96
- });
97
-
98
- it("should have actions slot", async () => {
99
- element = await fixture("grantcodes-app-bar");
100
- const actionsSlot = element.shadowRoot.querySelector(
101
- 'slot[name="actions"]',
102
- );
103
- assert.ok(actionsSlot, "Actions slot should exist");
104
- });
105
-
106
- it("should render mobile menu button", async () => {
107
- element = await fixture("grantcodes-app-bar");
108
- const menuButton = element.shadowRoot.querySelector(
109
- ".app-bar__menu-button",
110
- );
111
- assert.ok(menuButton, "Mobile menu button should exist");
112
- });
113
-
114
- it("should have mobile menu closed by default", async () => {
115
- element = await fixture("grantcodes-app-bar");
116
- assert.strictEqual(
117
- element._mobileMenuOpen,
118
- false,
119
- "Mobile menu should be closed by default",
120
- );
121
- });
122
-
123
- it("should not render mobile nav when closed", async () => {
124
- element = await fixture("grantcodes-app-bar");
125
- const mobileNav = element.shadowRoot.querySelector(".app-bar__mobile-nav");
126
- assert.ok(!mobileNav, "Mobile nav should not be rendered when closed");
127
- });
128
-
129
- it("should render mobile nav when opened", async () => {
130
- element = await fixture("grantcodes-app-bar");
131
- element._mobileMenuOpen = true;
132
-
133
- await element.updateComplete;
134
-
135
- const mobileNav = element.shadowRoot.querySelector(".app-bar__nav--open");
136
- assert.ok(mobileNav, "Mobile nav should be rendered when open");
137
- });
138
-
139
- it("should toggle mobile menu when button is clicked", async () => {
140
- element = await fixture("grantcodes-app-bar");
141
- const menuButton = element.shadowRoot.querySelector(
142
- ".app-bar__menu-button",
143
- );
144
-
145
- assert.strictEqual(element._mobileMenuOpen, false, "Should start closed");
146
-
147
- click(menuButton);
148
- await element.updateComplete;
149
-
150
- assert.strictEqual(
151
- element._mobileMenuOpen,
152
- true,
153
- "Should be open after click",
154
- );
155
-
156
- click(menuButton);
157
- await element.updateComplete;
158
-
159
- assert.strictEqual(
160
- element._mobileMenuOpen,
161
- false,
162
- "Should be closed after second click",
163
- );
164
- });
165
-
166
- it("should emit menu-toggle event when menu is toggled", async () => {
167
- element = await fixture("grantcodes-app-bar");
168
-
169
- let toggledState = null;
170
- element.addEventListener("menu-toggle", (e) => {
171
- toggledState = e.detail.open;
172
- });
173
-
174
- const menuButton = element.shadowRoot.querySelector(
175
- ".app-bar__menu-button",
176
- );
177
- click(menuButton);
178
-
179
- await element.updateComplete;
180
-
181
- assert.strictEqual(
182
- toggledState,
183
- true,
184
- "Toggle event should fire with open state",
185
- );
186
- });
187
-
188
- it("should have aria-label on menu button", async () => {
189
- element = await fixture("grantcodes-app-bar");
190
- const menuButton = element.shadowRoot.querySelector(
191
- ".app-bar__menu-button",
192
- );
193
- assert.ok(
194
- menuButton.hasAttribute("aria-label"),
195
- "Menu button should have aria-label",
196
- );
197
- });
198
-
199
- it("should have aria-expanded on menu button", async () => {
200
- element = await fixture("grantcodes-app-bar");
201
- const menuButton = element.shadowRoot.querySelector(
202
- ".app-bar__menu-button",
203
- );
204
- assert.ok(
205
- menuButton.hasAttribute("aria-expanded"),
206
- "Menu button should have aria-expanded",
207
- );
208
- });
209
-
210
- it("should update aria-expanded when menu is toggled", async () => {
211
- element = await fixture("grantcodes-app-bar");
212
- const menuButton = element.shadowRoot.querySelector(
213
- ".app-bar__menu-button",
214
- );
215
-
216
- assert.strictEqual(
217
- menuButton.getAttribute("aria-expanded"),
218
- "false",
219
- "Should be false initially",
220
- );
221
-
222
- click(menuButton);
223
- await element.updateComplete;
224
-
225
- assert.strictEqual(
226
- menuButton.getAttribute("aria-expanded"),
227
- "true",
228
- "Should be true when open",
229
- );
230
- });
231
-
232
- it("should have nav element with aria-label", async () => {
233
- element = await fixture("grantcodes-app-bar");
234
- const nav = element.shadowRoot.querySelector("nav");
235
- assert.ok(nav, "Nav element should exist");
236
- assert.ok(nav.hasAttribute("aria-label"), "Nav should have aria-label");
237
- });
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 './app-bar.js';
5
+
6
+ describe('App Bar 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-app-bar');
15
+ assert.ok(element, 'Element should be created');
16
+ assert.ok(element.shadowRoot, 'Element should have shadow root');
17
+ });
18
+
19
+ it('should not be sticky by default', async () => {
20
+ element = await fixture('grantcodes-app-bar');
21
+ assert.strictEqual(element.sticky, false, 'Should not be sticky by default');
22
+ });
23
+
24
+ it('should not be transparent by default', async () => {
25
+ element = await fixture('grantcodes-app-bar');
26
+ assert.strictEqual(element.transparent, false, 'Should not be transparent by default');
27
+ });
28
+
29
+ it('should render header element', async () => {
30
+ element = await fixture('grantcodes-app-bar');
31
+ const header = element.shadowRoot.querySelector('header');
32
+ assert.ok(header, 'Header element should exist');
33
+ });
34
+
35
+ it('should not have sticky class by default', async () => {
36
+ element = await fixture('grantcodes-app-bar');
37
+ const appBar = element.shadowRoot.querySelector('.app-bar--sticky');
38
+ assert.ok(!appBar, 'Should not have sticky class by default');
39
+ });
40
+
41
+ it('should apply sticky class when sticky', async () => {
42
+ element = await fixture('grantcodes-app-bar', {
43
+ sticky: true,
44
+ });
45
+
46
+ const appBar = element.shadowRoot.querySelector('.app-bar--sticky');
47
+ assert.ok(appBar, 'Should have sticky class');
48
+ });
49
+
50
+ it('should reflect sticky to the host attribute', async () => {
51
+ element = await fixture('grantcodes-app-bar', {
52
+ sticky: true,
53
+ });
54
+
55
+ assert.ok(element.hasAttribute('sticky'), 'Should reflect sticky attribute');
56
+ });
57
+
58
+ it('should apply transparent class when transparent', async () => {
59
+ element = await fixture('grantcodes-app-bar', {
60
+ transparent: true,
61
+ });
62
+
63
+ const appBar = element.shadowRoot.querySelector('.app-bar--transparent');
64
+ assert.ok(appBar, 'Should have transparent class');
65
+ });
66
+
67
+ it('should reflect transparent to the host attribute', async () => {
68
+ element = await fixture('grantcodes-app-bar', {
69
+ transparent: true,
70
+ });
71
+
72
+ assert.ok(element.hasAttribute('transparent'), 'Should reflect transparent attribute');
73
+ });
74
+
75
+ it('should have logo slot', async () => {
76
+ element = await fixture('grantcodes-app-bar');
77
+ const logoSlot = element.shadowRoot.querySelector('slot[name="logo"]');
78
+ assert.ok(logoSlot, 'Logo slot should exist');
79
+ });
80
+
81
+ it('should have nav slot', async () => {
82
+ element = await fixture('grantcodes-app-bar');
83
+ const navSlot = element.shadowRoot.querySelector('slot[name="nav"]');
84
+ assert.ok(navSlot, 'Nav slot should exist');
85
+ });
86
+
87
+ it('should have actions slot', async () => {
88
+ element = await fixture('grantcodes-app-bar');
89
+ const actionsSlot = element.shadowRoot.querySelector('slot[name="actions"]');
90
+ assert.ok(actionsSlot, 'Actions slot should exist');
91
+ });
92
+
93
+ it('should render mobile menu button', async () => {
94
+ element = await fixture('grantcodes-app-bar');
95
+ const menuButton = element.shadowRoot.querySelector('.app-bar__menu-button');
96
+ assert.ok(menuButton, 'Mobile menu button should exist');
97
+ });
98
+
99
+ it('should have mobile menu closed by default', async () => {
100
+ element = await fixture('grantcodes-app-bar');
101
+ assert.strictEqual(element._mobileMenuOpen, false, 'Mobile menu should be closed by default');
102
+ });
103
+
104
+ it('should not render mobile nav when closed', async () => {
105
+ element = await fixture('grantcodes-app-bar');
106
+ const mobileNav = element.shadowRoot.querySelector('.app-bar__mobile-nav');
107
+ assert.ok(!mobileNav, 'Mobile nav should not be rendered when closed');
108
+ });
109
+
110
+ it('should render mobile nav when opened', async () => {
111
+ element = await fixture('grantcodes-app-bar');
112
+ element._mobileMenuOpen = true;
113
+
114
+ await element.updateComplete;
115
+
116
+ const mobileNav = element.shadowRoot.querySelector('.app-bar__nav--open');
117
+ assert.ok(mobileNav, 'Mobile nav should be rendered when open');
118
+ });
119
+
120
+ it('should toggle mobile menu when button is clicked', async () => {
121
+ element = await fixture('grantcodes-app-bar');
122
+ const menuButton = element.shadowRoot.querySelector('.app-bar__menu-button');
123
+
124
+ assert.strictEqual(element._mobileMenuOpen, false, 'Should start closed');
125
+
126
+ click(menuButton);
127
+ await element.updateComplete;
128
+
129
+ assert.strictEqual(element._mobileMenuOpen, true, 'Should be open after click');
130
+
131
+ click(menuButton);
132
+ await element.updateComplete;
133
+
134
+ assert.strictEqual(element._mobileMenuOpen, false, 'Should be closed after second click');
135
+ });
136
+
137
+ it('should emit menu-toggle event when menu is toggled', async () => {
138
+ element = await fixture('grantcodes-app-bar');
139
+
140
+ let toggledState = null;
141
+ element.addEventListener('menu-toggle', (e) => {
142
+ toggledState = e.detail.open;
143
+ });
144
+
145
+ const menuButton = element.shadowRoot.querySelector('.app-bar__menu-button');
146
+ click(menuButton);
147
+
148
+ await element.updateComplete;
149
+
150
+ assert.strictEqual(toggledState, true, 'Toggle event should fire with open state');
151
+ });
152
+
153
+ it('should have aria-label on menu button', async () => {
154
+ element = await fixture('grantcodes-app-bar');
155
+ const menuButton = element.shadowRoot.querySelector('.app-bar__menu-button');
156
+ assert.ok(menuButton.hasAttribute('aria-label'), 'Menu button should have aria-label');
157
+ });
158
+
159
+ it('should have aria-expanded on menu button', async () => {
160
+ element = await fixture('grantcodes-app-bar');
161
+ const menuButton = element.shadowRoot.querySelector('.app-bar__menu-button');
162
+ assert.ok(menuButton.hasAttribute('aria-expanded'), 'Menu button should have aria-expanded');
163
+ });
164
+
165
+ it('should update aria-expanded when menu is toggled', async () => {
166
+ element = await fixture('grantcodes-app-bar');
167
+ const menuButton = element.shadowRoot.querySelector('.app-bar__menu-button');
168
+
169
+ assert.strictEqual(
170
+ menuButton.getAttribute('aria-expanded'),
171
+ 'false',
172
+ 'Should be false initially',
173
+ );
174
+
175
+ click(menuButton);
176
+ await element.updateComplete;
177
+
178
+ assert.strictEqual(
179
+ menuButton.getAttribute('aria-expanded'),
180
+ 'true',
181
+ 'Should be true when open',
182
+ );
183
+ });
184
+
185
+ it('should have nav element with aria-label', async () => {
186
+ element = await fixture('grantcodes-app-bar');
187
+ const nav = element.shadowRoot.querySelector('nav');
188
+ assert.ok(nav, 'Nav element should exist');
189
+ assert.ok(nav.hasAttribute('aria-label'), 'Nav should have aria-label');
190
+ });
238
191
  });
@@ -1 +1 @@
1
- export * from "./app-bar.js";
1
+ export * from './app-bar.js';
@@ -1,17 +1,15 @@
1
- import { LitElement, html } from "lit";
2
- import focusRingStyles from "../../lib/styles/focus-ring.css" with {
3
- type: "css",
4
- };
5
- import navLinkStyles from "./nav-link.css" with { type: "css" };
1
+ import { html, LitElement } from 'lit';
2
+ import focusRingStyles from '../../lib/styles/focus-ring.css' with { type: 'css' };
3
+ import navLinkStyles from './nav-link.css' with { type: 'css' };
6
4
 
7
5
  export class GrantCodesNavLink extends LitElement {
8
- static styles = [focusRingStyles, navLinkStyles];
6
+ static styles = [focusRingStyles, navLinkStyles];
9
7
 
10
- render() {
11
- return html`
8
+ render() {
9
+ return html`
12
10
  <span class="nav-link">
13
11
  <slot></slot>
14
12
  </span>
15
13
  `;
16
- }
14
+ }
17
15
  }
@@ -1,27 +1,23 @@
1
1
  *,
2
2
  *::before,
3
3
  *::after {
4
- box-sizing: border-box;
4
+ box-sizing: border-box;
5
5
  }
6
6
 
7
7
  :host {
8
- display: inline-flex;
8
+ display: inline-flex;
9
9
  }
10
10
 
11
11
  .nav-link {
12
- display: inline-flex;
13
- align-items: center;
14
- border-radius: var(--g-border-radius-md);
15
- background-color: transparent;
16
- transition: background-color 0.15s ease;
12
+ display: inline-flex;
13
+ align-items: center;
14
+ border-radius: var(--g-border-radius-md);
15
+ background-color: transparent;
16
+ transition: background-color 0.15s ease;
17
17
  }
18
18
 
19
19
  .nav-link:hover {
20
- background-color: color-mix(
21
- in srgb,
22
- var(--g-color-content-default) 8%,
23
- transparent
24
- );
20
+ background-color: color-mix(in srgb, var(--g-color-content-default) 8%, transparent);
25
21
  }
26
22
 
27
23
  /*
@@ -37,26 +33,25 @@
37
33
  ::slotted(a:hover),
38
34
  ::slotted(a:active),
39
35
  ::slotted(a:focus-visible) {
40
- display: inline-flex;
41
- align-items: center;
42
- color: var(--g-color-content-default) !important;
43
- text-decoration: none !important;
44
- opacity: 1 !important;
45
- padding-block: var(--g-spacing-xs, 0.25rem);
46
- padding-inline: var(--g-spacing-sm);
47
- font: var(--g-typography-body-sm-font);
48
- white-space: nowrap;
36
+ display: inline-flex;
37
+ align-items: center;
38
+ color: var(--g-color-content-default) !important;
39
+ text-decoration: none !important;
40
+ opacity: 1 !important;
41
+ padding-block: var(--g-spacing-xs, 0.25rem);
42
+ padding-inline: var(--g-spacing-sm);
43
+ font: var(--g-typography-body-sm-font);
44
+ white-space: nowrap;
49
45
  }
50
46
 
51
47
  ::slotted(a:focus-visible) {
52
- outline: var(--g-focus-ring-width-default) solid
53
- var(--g-focus-ring-color-default);
54
- outline-offset: var(--g-focus-ring-offset-default);
55
- border-radius: var(--g-border-radius-md);
48
+ outline: var(--g-focus-ring-width-default) solid var(--g-focus-ring-color-default);
49
+ outline-offset: var(--g-focus-ring-offset-default);
50
+ border-radius: var(--g-border-radius-md);
56
51
  }
57
52
 
58
53
  @media (prefers-reduced-motion: reduce) {
59
- .nav-link {
60
- transition: none;
61
- }
54
+ .nav-link {
55
+ transition: none;
56
+ }
62
57
  }
@@ -1,6 +1,6 @@
1
- import { GrantCodesNavLink } from "./nav-link.component.js";
1
+ import { GrantCodesNavLink } from './nav-link.component.js';
2
2
 
3
- export * from "./nav-link.component.js";
3
+ export * from './nav-link.component.js';
4
4
  export default GrantCodesNavLink;
5
5
 
6
- customElements.define("grantcodes-nav-link", GrantCodesNavLink);
6
+ customElements.define('grantcodes-nav-link', GrantCodesNavLink);
@@ -1,9 +1,9 @@
1
- import React from "react";
2
- import { createComponent } from "@lit/react";
3
- import { GrantCodesNavLink } from "./nav-link.js";
1
+ import { createComponent } from '@lit/react';
2
+ import React from 'react';
3
+ import { GrantCodesNavLink } from './nav-link.js';
4
4
 
5
5
  export const NavLink = createComponent({
6
- tagName: "grantcodes-nav-link",
7
- elementClass: GrantCodesNavLink,
8
- react: React,
6
+ tagName: 'grantcodes-nav-link',
7
+ elementClass: GrantCodesNavLink,
8
+ react: React,
9
9
  });