@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,21 +1,21 @@
1
- import React from "react";
2
- import { createComponent } from "@lit/react";
3
- import { GrantCodesDropdown, GrantCodesDropdownItem } from "./dropdown.js";
1
+ import { createComponent } from '@lit/react';
2
+ import React from 'react';
3
+ import { GrantCodesDropdown, GrantCodesDropdownItem } from './dropdown.js';
4
4
 
5
5
  export const Dropdown = createComponent({
6
- tagName: "grantcodes-dropdown",
7
- elementClass: GrantCodesDropdown,
8
- react: React,
9
- events: {
10
- onToggle: "toggle",
11
- },
6
+ tagName: 'grantcodes-dropdown',
7
+ elementClass: GrantCodesDropdown,
8
+ react: React,
9
+ events: {
10
+ onToggle: 'toggle',
11
+ },
12
12
  });
13
13
 
14
14
  export const DropdownItem = createComponent({
15
- tagName: "grantcodes-dropdown-item",
16
- elementClass: GrantCodesDropdownItem,
17
- react: React,
18
- events: {
19
- onSelect: "select",
20
- },
15
+ tagName: 'grantcodes-dropdown-item',
16
+ elementClass: GrantCodesDropdownItem,
17
+ react: React,
18
+ events: {
19
+ onSelect: 'select',
20
+ },
21
21
  });
@@ -1,10 +1,10 @@
1
- import { html } from "lit";
2
- import "./dropdown.js";
3
- import "../button/button.js";
1
+ import { html } from 'lit';
2
+ import './dropdown.js';
3
+ import '../button/button.js';
4
4
 
5
5
  const meta = {
6
- title: "Components/Dropdown",
7
- component: "grantcodes-dropdown",
6
+ title: 'Components/Dropdown',
7
+ component: 'grantcodes-dropdown',
8
8
  };
9
9
 
10
10
  export default meta;
@@ -13,7 +13,7 @@ export default meta;
13
13
  * Basic dropdown menu with trigger button
14
14
  */
15
15
  export const Dropdown = {
16
- render: () => html`
16
+ render: () => html`
17
17
  <grantcodes-dropdown>
18
18
  <grantcodes-button slot="trigger">Open Menu</grantcodes-button>
19
19
  <div slot="menu">
@@ -29,7 +29,7 @@ export const Dropdown = {
29
29
  * Dropdown with disabled items
30
30
  */
31
31
  export const WithDisabledItems = {
32
- render: () => html`
32
+ render: () => html`
33
33
  <grantcodes-dropdown>
34
34
  <grantcodes-button slot="trigger">Actions</grantcodes-button>
35
35
  <div slot="menu">
@@ -46,7 +46,7 @@ export const WithDisabledItems = {
46
46
  * Auto try flip
47
47
  */
48
48
  export const AutoTryFlip = {
49
- render: () => html`
49
+ render: () => html`
50
50
  <div style="height: 100vh; overflow: hidden; display: flex; justify-content: flex-end; align-items: flex-end;">
51
51
  <grantcodes-dropdown placement="bottom-start">
52
52
  <grantcodes-button slot="trigger">Bottom Start</grantcodes-button>
@@ -64,7 +64,7 @@ export const AutoTryFlip = {
64
64
  * Dropdown with custom trigger content
65
65
  */
66
66
  export const CustomTrigger = {
67
- render: () => html`
67
+ render: () => html`
68
68
  <grantcodes-dropdown>
69
69
  <div slot="trigger" style="padding: 0.5rem 1rem; border: 1px solid #ccc; border-radius: 0.25rem; cursor: pointer;">
70
70
  <span style="font-weight: 600;">User Menu</span>
@@ -84,7 +84,7 @@ export const CustomTrigger = {
84
84
  * Dropdown with many items showing keyboard navigation
85
85
  */
86
86
  export const ManyItems = {
87
- render: () => html`
87
+ render: () => html`
88
88
  <grantcodes-dropdown>
89
89
  <grantcodes-button slot="trigger">Select Country</grantcodes-button>
90
90
  <div slot="menu">
@@ -1,160 +1,214 @@
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 "./dropdown.js";
5
-
6
- describe("Dropdown 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-dropdown");
15
- assert.ok(element, "Element should be created");
16
- assert.ok(element.shadowRoot, "Element should have shadow root");
17
- });
18
-
19
- it("should be closed by default", async () => {
20
- element = await fixture("grantcodes-dropdown");
21
- assert.strictEqual(element.open, false, "Should be closed by default");
22
- });
23
-
24
- it("should have bottom-start placement by default", async () => {
25
- element = await fixture("grantcodes-dropdown");
26
- assert.strictEqual(
27
- element.placement,
28
- "bottom-start",
29
- "Default placement should be bottom-start",
30
- );
31
- });
32
-
33
- it("should have trigger slot", async () => {
34
- element = await fixture("grantcodes-dropdown");
35
- const slot = element.shadowRoot.querySelector('slot[name="trigger"]');
36
- assert.ok(slot, "Trigger slot should exist");
37
- });
38
-
39
- it("should have menu slot", async () => {
40
- element = await fixture("grantcodes-dropdown");
41
- const slot = element.shadowRoot.querySelector('slot[name="menu"]');
42
- assert.ok(slot, "Menu slot should exist");
43
- });
44
-
45
- it("should render menu element", async () => {
46
- element = await fixture("grantcodes-dropdown");
47
- const menu = element.shadowRoot.querySelector(".dropdown__menu");
48
- assert.ok(menu, "Menu element should be rendered");
49
- });
50
-
51
- it("should have role=menu on menu", async () => {
52
- element = await fixture("grantcodes-dropdown");
53
- const menu = element.shadowRoot.querySelector(".dropdown__menu");
54
- assert.strictEqual(
55
- menu.getAttribute("role"),
56
- "menu",
57
- "Menu should have role",
58
- );
59
- });
60
-
61
- it("should apply placement class to menu", async () => {
62
- element = await fixture("grantcodes-dropdown", {
63
- placement: "top-end",
64
- });
65
-
66
- const menu = element.shadowRoot.querySelector(".dropdown__menu--top-end");
67
- assert.ok(menu, "Menu should have placement class");
68
- });
69
-
70
- it("should generate unique IDs", async () => {
71
- const element1 = await fixture("grantcodes-dropdown");
72
- const element2 = await fixture("grantcodes-dropdown");
73
-
74
- assert.notStrictEqual(element1.id, element2.id, "IDs should be unique");
75
-
76
- cleanup(element1);
77
- cleanup(element2);
78
- });
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 './dropdown.js';
5
+
6
+ describe('Dropdown 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-dropdown');
15
+ assert.ok(element, 'Element should be created');
16
+ assert.ok(element.shadowRoot, 'Element should have shadow root');
17
+ });
18
+
19
+ it('should be closed by default', async () => {
20
+ element = await fixture('grantcodes-dropdown');
21
+ assert.strictEqual(element.open, false, 'Should be closed by default');
22
+ });
23
+
24
+ it('should have bottom-start placement by default', async () => {
25
+ element = await fixture('grantcodes-dropdown');
26
+ assert.strictEqual(
27
+ element.placement,
28
+ 'bottom-start',
29
+ 'Default placement should be bottom-start',
30
+ );
31
+ });
32
+
33
+ it('should have trigger slot', async () => {
34
+ element = await fixture('grantcodes-dropdown');
35
+ const slot = element.shadowRoot.querySelector('slot[name="trigger"]');
36
+ assert.ok(slot, 'Trigger slot should exist');
37
+ });
38
+
39
+ it('should have menu slot', async () => {
40
+ element = await fixture('grantcodes-dropdown');
41
+ const slot = element.shadowRoot.querySelector('slot[name="menu"]');
42
+ assert.ok(slot, 'Menu slot should exist');
43
+ });
44
+
45
+ it('should render menu element', async () => {
46
+ element = await fixture('grantcodes-dropdown');
47
+ const menu = element.shadowRoot.querySelector('.dropdown__menu');
48
+ assert.ok(menu, 'Menu element should be rendered');
49
+ });
50
+
51
+ it('should have role=menu on menu', async () => {
52
+ element = await fixture('grantcodes-dropdown');
53
+ const menu = element.shadowRoot.querySelector('.dropdown__menu');
54
+ assert.strictEqual(menu.getAttribute('role'), 'menu', 'Menu should have role');
55
+ });
56
+
57
+ it('should apply placement class to menu', async () => {
58
+ element = await fixture('grantcodes-dropdown', {
59
+ placement: 'top-end',
60
+ });
61
+
62
+ const menu = element.shadowRoot.querySelector('.dropdown__menu--top-end');
63
+ assert.ok(menu, 'Menu should have placement class');
64
+ });
65
+
66
+ it('should generate unique IDs', async () => {
67
+ const element1 = await fixture('grantcodes-dropdown');
68
+ const element2 = await fixture('grantcodes-dropdown');
69
+
70
+ assert.notStrictEqual(element1.id, element2.id, 'IDs should be unique');
71
+
72
+ cleanup(element1);
73
+ cleanup(element2);
74
+ });
75
+
76
+ it('should expose a unique anchor name for the trigger', async () => {
77
+ const element1 = await fixture('grantcodes-dropdown');
78
+ const element2 = await fixture('grantcodes-dropdown');
79
+
80
+ const anchorName = element1.style.getPropertyValue('--dropdown-anchor');
81
+ assert.match(anchorName, /^--/, 'Anchor name should be a dashed ident');
82
+ assert.notStrictEqual(
83
+ anchorName,
84
+ element2.style.getPropertyValue('--dropdown-anchor'),
85
+ 'Anchor name should be unique per instance',
86
+ );
87
+
88
+ cleanup(element1);
89
+ cleanup(element2);
90
+ });
91
+
92
+ it('should preserve two rapid trigger clicks before the transition update', async () => {
93
+ element = await fixture('grantcodes-dropdown');
94
+ const matchMedia = window.matchMedia;
95
+ const startViewTransition = document.startViewTransition;
96
+ const callbacks = [];
97
+ const states = [];
98
+ element.addEventListener('toggle', (event) => states.push(event.detail.open));
99
+ window.matchMedia = () => ({ matches: false });
100
+ document.startViewTransition = (callback) => {
101
+ callbacks.push(callback);
102
+ return { finished: Promise.resolve() };
103
+ };
104
+
105
+ try {
106
+ const trigger = element.shadowRoot.querySelector('.dropdown__trigger');
107
+ click(trigger);
108
+ click(trigger);
109
+ for (const callback of callbacks) await callback();
110
+ await element.updateComplete;
111
+
112
+ assert.strictEqual(element.open, false, 'Two clicks should return to closed');
113
+ assert.deepStrictEqual(states, [true, false], 'Each click should report its intended state');
114
+ } finally {
115
+ window.matchMedia = matchMedia;
116
+ document.startViewTransition = startViewTransition;
117
+ }
118
+ });
119
+
120
+ it('should open through a view transition when motion is allowed', async () => {
121
+ element = await fixture('grantcodes-dropdown');
122
+
123
+ const originalMatchMedia = globalThis.window.matchMedia;
124
+ let transitions = 0;
125
+ globalThis.window.matchMedia = () => ({ matches: false });
126
+ globalThis.document.startViewTransition = (callback) => {
127
+ transitions++;
128
+ callback();
129
+ return { finished: Promise.resolve() };
130
+ };
131
+
132
+ element._handleTriggerClick();
133
+ await element.updateComplete;
134
+
135
+ globalThis.window.matchMedia = originalMatchMedia;
136
+ globalThis.document.startViewTransition = undefined;
137
+
138
+ assert.strictEqual(transitions, 1, 'Opening should animate');
139
+ assert.strictEqual(element.open, true, 'The dropdown should still open');
140
+ });
79
141
  });
80
142
 
81
- describe("Dropdown Item Component", () => {
82
- let element;
83
-
84
- afterEach(() => {
85
- cleanup(element);
86
- });
87
-
88
- it("should render dropdown item", async () => {
89
- element = await fixture("grantcodes-dropdown-item");
90
- assert.ok(element, "Element should be created");
91
- assert.ok(element.shadowRoot, "Element should have shadow root");
92
- });
93
-
94
- it("should not be disabled by default", async () => {
95
- element = await fixture("grantcodes-dropdown-item");
96
- assert.strictEqual(
97
- element.disabled,
98
- false,
99
- "Should not be disabled by default",
100
- );
101
- });
102
-
103
- it("should render with disabled class when disabled", async () => {
104
- element = await fixture("grantcodes-dropdown-item", {
105
- disabled: true,
106
- });
107
-
108
- const item = element.shadowRoot.querySelector(".dropdown-item--disabled");
109
- assert.ok(item, "Should have disabled class");
110
- });
111
-
112
- it("should emit select event when clicked", async () => {
113
- element = await fixture("grantcodes-dropdown-item");
114
-
115
- let selected = false;
116
- element.addEventListener("select", () => {
117
- selected = true;
118
- });
119
-
120
- const item = element.shadowRoot.querySelector(".dropdown-item");
121
- click(item);
122
-
123
- assert.ok(selected, "Select event should have fired");
124
- });
125
-
126
- it("should not emit select event when disabled", async () => {
127
- element = await fixture("grantcodes-dropdown-item", {
128
- disabled: true,
129
- });
130
-
131
- let selected = false;
132
- element.addEventListener("select", () => {
133
- selected = true;
134
- });
135
-
136
- const item = element.shadowRoot.querySelector(".dropdown-item");
137
- click(item);
138
-
139
- assert.ok(!selected, "Select event should not fire when disabled");
140
- });
141
-
142
- it("should render slotted content", async () => {
143
- element = await fixture("grantcodes-dropdown-item");
144
- element.textContent = "Menu Item";
145
-
146
- await element.updateComplete;
147
-
148
- assert.strictEqual(
149
- element.textContent,
150
- "Menu Item",
151
- "Slotted content should be rendered",
152
- );
153
- });
154
-
155
- it("should have dropdown-item wrapper", async () => {
156
- element = await fixture("grantcodes-dropdown-item");
157
- const item = element.shadowRoot.querySelector(".dropdown-item");
158
- assert.ok(item, "Dropdown item wrapper should exist");
159
- });
143
+ describe('Dropdown Item Component', () => {
144
+ let element;
145
+
146
+ afterEach(() => {
147
+ cleanup(element);
148
+ });
149
+
150
+ it('should render dropdown item', async () => {
151
+ element = await fixture('grantcodes-dropdown-item');
152
+ assert.ok(element, 'Element should be created');
153
+ assert.ok(element.shadowRoot, 'Element should have shadow root');
154
+ });
155
+
156
+ it('should not be disabled by default', async () => {
157
+ element = await fixture('grantcodes-dropdown-item');
158
+ assert.strictEqual(element.disabled, false, 'Should not be disabled by default');
159
+ });
160
+
161
+ it('should render with disabled class when disabled', async () => {
162
+ element = await fixture('grantcodes-dropdown-item', {
163
+ disabled: true,
164
+ });
165
+
166
+ const item = element.shadowRoot.querySelector('.dropdown-item--disabled');
167
+ assert.ok(item, 'Should have disabled class');
168
+ });
169
+
170
+ it('should emit select event when clicked', async () => {
171
+ element = await fixture('grantcodes-dropdown-item');
172
+
173
+ let selected = false;
174
+ element.addEventListener('select', () => {
175
+ selected = true;
176
+ });
177
+
178
+ const item = element.shadowRoot.querySelector('.dropdown-item');
179
+ click(item);
180
+
181
+ assert.ok(selected, 'Select event should have fired');
182
+ });
183
+
184
+ it('should not emit select event when disabled', async () => {
185
+ element = await fixture('grantcodes-dropdown-item', {
186
+ disabled: true,
187
+ });
188
+
189
+ let selected = false;
190
+ element.addEventListener('select', () => {
191
+ selected = true;
192
+ });
193
+
194
+ const item = element.shadowRoot.querySelector('.dropdown-item');
195
+ click(item);
196
+
197
+ assert.ok(!selected, 'Select event should not fire when disabled');
198
+ });
199
+
200
+ it('should render slotted content', async () => {
201
+ element = await fixture('grantcodes-dropdown-item');
202
+ element.textContent = 'Menu Item';
203
+
204
+ await element.updateComplete;
205
+
206
+ assert.strictEqual(element.textContent, 'Menu Item', 'Slotted content should be rendered');
207
+ });
208
+
209
+ it('should have dropdown-item wrapper', async () => {
210
+ element = await fixture('grantcodes-dropdown-item');
211
+ const item = element.shadowRoot.querySelector('.dropdown-item');
212
+ assert.ok(item, 'Dropdown item wrapper should exist');
213
+ });
160
214
  });
@@ -1 +1 @@
1
- export * from "./dropdown.js";
1
+ export * from './dropdown.js';