@grantcodes/ui 2.0.0-beta4 → 2.0.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 (310) hide show
  1. package/CHANGELOG.md +111 -0
  2. package/README.md +6 -8
  3. package/custom-elements.json +4273 -0
  4. package/package.json +80 -89
  5. package/src/components/app-bar/app-bar.component.js +90 -0
  6. package/src/components/app-bar/app-bar.js +8 -0
  7. package/src/components/app-bar/app-bar.stories.js +84 -0
  8. package/src/components/app-bar/app-bar.styles.js +227 -0
  9. package/src/components/app-bar/app-bar.test.js +174 -0
  10. package/src/components/app-bar/index.js +3 -0
  11. package/src/components/avatar/avatar.component.js +78 -0
  12. package/src/components/avatar/avatar.js +18 -0
  13. package/src/components/avatar/avatar.stories.js +45 -0
  14. package/src/components/avatar/avatar.styles.js +42 -0
  15. package/src/components/avatar/avatar.test.js +85 -0
  16. package/src/components/avatar/index.js +13 -0
  17. package/src/components/badge/badge.component.js +36 -0
  18. package/src/components/badge/badge.js +8 -0
  19. package/src/components/badge/badge.stories.js +46 -0
  20. package/src/components/badge/badge.styles.js +94 -0
  21. package/src/components/badge/badge.test.js +70 -0
  22. package/src/components/badge/index.js +3 -0
  23. package/src/components/breadcrumb/breadcrumb.component.js +110 -0
  24. package/src/components/breadcrumb/breadcrumb.js +12 -0
  25. package/src/components/breadcrumb/breadcrumb.stories.js +25 -0
  26. package/src/components/breadcrumb/breadcrumb.styles.js +96 -0
  27. package/src/components/breadcrumb/breadcrumb.test.js +144 -0
  28. package/src/components/breadcrumb/index.js +3 -0
  29. package/src/components/button/button.component.js +64 -0
  30. package/src/components/button/button.js +6 -0
  31. package/src/components/button/button.stories.js +78 -0
  32. package/src/components/button/button.styles.js +97 -0
  33. package/src/components/button/button.test.js +98 -0
  34. package/src/components/button/index.js +1 -0
  35. package/src/components/button-group/button-group.component.js +27 -0
  36. package/src/components/button-group/button-group.js +6 -0
  37. package/src/components/button-group/button-group.stories.js +33 -0
  38. package/src/components/button-group/button-group.styles.js +43 -0
  39. package/src/components/button-group/button-group.test.js +57 -0
  40. package/src/components/button-group/index.js +1 -0
  41. package/src/components/card/card.component.js +17 -0
  42. package/src/components/card/card.js +6 -0
  43. package/src/components/card/card.stories.js +36 -0
  44. package/src/components/card/card.styles.js +128 -0
  45. package/src/components/card/card.test.js +59 -0
  46. package/src/components/card/index.js +1 -0
  47. package/src/components/code-preview/code-preview.component.js +53 -0
  48. package/src/components/code-preview/code-preview.js +7 -0
  49. package/src/components/code-preview/code-preview.stories.js +67 -0
  50. package/src/components/code-preview/code-preview.styles.js +18 -0
  51. package/src/components/code-preview/code-preview.test.js +118 -0
  52. package/src/components/code-preview/index.js +1 -0
  53. package/src/components/container/container.component.js +38 -0
  54. package/src/components/container/container.js +7 -0
  55. package/src/components/container/container.stories.js +59 -0
  56. package/src/components/container/container.styles.js +43 -0
  57. package/src/components/container/container.test.js +84 -0
  58. package/src/components/container/index.js +1 -0
  59. package/src/components/dialog/dialog.component.js +78 -0
  60. package/src/components/dialog/dialog.js +7 -0
  61. package/src/components/dialog/dialog.stories.js +43 -0
  62. package/src/components/dialog/dialog.styles.js +74 -0
  63. package/src/components/dialog/dialog.test.js +97 -0
  64. package/src/components/dialog/index.js +1 -0
  65. package/src/components/dropdown/dropdown.component.js +225 -0
  66. package/src/components/dropdown/dropdown.js +12 -0
  67. package/src/components/dropdown/dropdown.stories.js +107 -0
  68. package/src/components/dropdown/dropdown.styles.js +128 -0
  69. package/src/components/dropdown/dropdown.test.js +144 -0
  70. package/src/components/dropdown/index.js +3 -0
  71. package/src/components/dropzone/dropzone.component.js +141 -0
  72. package/src/components/dropzone/dropzone.js +6 -0
  73. package/src/components/dropzone/dropzone.stories.js +41 -0
  74. package/src/components/dropzone/dropzone.styles.js +64 -0
  75. package/src/components/dropzone/dropzone.test.js +112 -0
  76. package/src/components/dropzone/index.js +1 -0
  77. package/src/components/footer/footer-column.component.js +15 -0
  78. package/src/components/footer/footer-column.styles.js +51 -0
  79. package/src/components/footer/footer.component.js +38 -0
  80. package/src/components/footer/footer.js +9 -0
  81. package/src/components/footer/footer.stories.js +143 -0
  82. package/src/components/footer/footer.styles.js +90 -0
  83. package/src/components/footer/footer.test.js +107 -0
  84. package/src/components/footer/index.js +2 -0
  85. package/src/components/form-field/form-field.component.js +173 -0
  86. package/src/components/form-field/form-field.js +7 -0
  87. package/src/components/form-field/form-field.stories.js +104 -0
  88. package/src/components/form-field/form-field.styles.js +47 -0
  89. package/src/components/form-field/form-field.test.js +118 -0
  90. package/src/components/form-field/index.js +1 -0
  91. package/src/components/gallery/gallery-image.component.js +52 -0
  92. package/src/components/gallery/gallery-image.js +7 -0
  93. package/src/components/gallery/gallery.component.js +25 -0
  94. package/src/components/gallery/gallery.js +7 -0
  95. package/src/components/gallery/gallery.stories.js +60 -0
  96. package/src/components/gallery/gallery.styles.js +56 -0
  97. package/src/components/gallery/gallery.test.js +58 -0
  98. package/src/components/gallery/index.js +2 -0
  99. package/src/components/icon/icon.component.js +14 -0
  100. package/src/components/icon/icon.js +7 -0
  101. package/src/components/icon/icon.stories.js +26 -0
  102. package/src/components/icon/icon.styles.js +28 -0
  103. package/src/components/icon/icon.test.js +57 -0
  104. package/src/components/icon/index.js +1 -0
  105. package/src/components/loading/index.js +1 -0
  106. package/src/components/loading/loading.component.js +21 -0
  107. package/src/components/loading/loading.js +7 -0
  108. package/src/components/loading/loading.stories.js +25 -0
  109. package/src/components/loading/loading.styles.js +43 -0
  110. package/src/components/loading/loading.test.js +57 -0
  111. package/src/components/notice/index.js +1 -0
  112. package/src/components/notice/notice.component.js +77 -0
  113. package/src/components/notice/notice.js +7 -0
  114. package/src/components/notice/notice.stories.js +122 -0
  115. package/src/components/notice/notice.styles.js +72 -0
  116. package/src/components/notice/notice.test.js +146 -0
  117. package/src/components/pagination/index.js +1 -0
  118. package/src/components/pagination/pagination.component.js +62 -0
  119. package/src/components/pagination/pagination.js +7 -0
  120. package/src/components/pagination/pagination.stories.js +34 -0
  121. package/src/components/pagination/pagination.styles.js +19 -0
  122. package/src/components/pagination/pagination.test.js +98 -0
  123. package/src/components/sidebar/index.js +3 -0
  124. package/src/components/sidebar/sidebar.component.js +165 -0
  125. package/src/components/sidebar/sidebar.js +8 -0
  126. package/src/components/sidebar/sidebar.stories.js +155 -0
  127. package/src/components/sidebar/sidebar.styles.js +192 -0
  128. package/src/components/sidebar/sidebar.test.js +196 -0
  129. package/src/components/tabs/index.js +2 -0
  130. package/src/components/tabs/internal/tabs-button.component.js +39 -0
  131. package/src/components/tabs/internal/tabs-button.js +7 -0
  132. package/src/components/tabs/internal/tabs-item.component.js +39 -0
  133. package/src/components/tabs/tab.component.js +20 -0
  134. package/src/components/tabs/tab.js +7 -0
  135. package/src/components/tabs/tabs.component.js +130 -0
  136. package/src/components/tabs/tabs.js +7 -0
  137. package/src/components/tabs/tabs.stories.js +39 -0
  138. package/src/components/tabs/tabs.styles.js +88 -0
  139. package/src/components/tabs/tabs.test.js +148 -0
  140. package/src/components/toast/index.js +3 -0
  141. package/src/components/toast/toast.component.js +187 -0
  142. package/src/components/toast/toast.js +9 -0
  143. package/src/components/toast/toast.stories.js +169 -0
  144. package/src/components/toast/toast.styles.js +207 -0
  145. package/src/components/toast/toast.test.js +196 -0
  146. package/src/components/tooltip/index.js +1 -0
  147. package/src/components/tooltip/tooltip.component.js +70 -0
  148. package/src/components/tooltip/tooltip.js +7 -0
  149. package/src/components/tooltip/tooltip.stories.js +33 -0
  150. package/src/components/tooltip/tooltip.styles.js +78 -0
  151. package/src/components/tooltip/tooltip.test.js +150 -0
  152. package/src/css/all.css +1 -0
  153. package/src/css/base.css +31 -0
  154. package/src/css/colors.stories.js +192 -0
  155. package/src/css/elements/a.css +50 -0
  156. package/src/css/elements/forms/button.css +15 -0
  157. package/src/css/elements/forms/input.css +183 -0
  158. package/src/css/elements/forms/label.css +5 -0
  159. package/src/css/elements/media/image.css +3 -0
  160. package/src/css/elements.css +5 -0
  161. package/src/css/elements.stories.js +108 -0
  162. package/src/css/helpers.css +16 -0
  163. package/src/css/themes/grantcodes.css +3 -0
  164. package/src/css/themes/todomap.css +2 -0
  165. package/src/css/themes/wireframe.css +2 -0
  166. package/src/css/tokens.stories.js +183 -0
  167. package/src/css/typography.css +64 -0
  168. package/src/css/typography.stories.js +179 -0
  169. package/src/css/util/functions.css +16 -0
  170. package/src/css/util/index.css +2 -0
  171. package/src/css/util/mixins.css +63 -0
  172. package/src/icons.js +3 -0
  173. package/src/lib/classnames.js +61 -0
  174. package/src/lib/generate-id.js +10 -0
  175. package/src/main.js +17 -0
  176. package/src/test-utils/assert-helpers.js +150 -0
  177. package/src/test-utils/events.js +88 -0
  178. package/src/test-utils/fixture.js +77 -0
  179. package/src/test-utils/index.js +7 -0
  180. package/dist/components/avatar/avatar.component.js +0 -3
  181. package/dist/components/avatar/avatar.js +0 -1
  182. package/dist/components/avatar/avatar.react.js +0 -1
  183. package/dist/components/avatar/avatar.scss.js +0 -1
  184. package/dist/components/avatar/index.js +0 -1
  185. package/dist/components/button/button.component.js +0 -5
  186. package/dist/components/button/button.js +0 -1
  187. package/dist/components/button/button.react.js +0 -1
  188. package/dist/components/button/button.scss.js +0 -1
  189. package/dist/components/button/index.js +0 -1
  190. package/dist/components/button-group/button-group.component.js +0 -5
  191. package/dist/components/button-group/button-group.js +0 -1
  192. package/dist/components/button-group/button-group.react.js +0 -1
  193. package/dist/components/button-group/button-group.scss.js +0 -1
  194. package/dist/components/button-group/index.js +0 -1
  195. package/dist/components/card/card.component.js +0 -8
  196. package/dist/components/card/card.js +0 -1
  197. package/dist/components/card/card.react.js +0 -1
  198. package/dist/components/card/card.scss.js +0 -1
  199. package/dist/components/card/index.js +0 -1
  200. package/dist/components/code-preview/code-preview.component.js +0 -1
  201. package/dist/components/code-preview/code-preview.js +0 -1
  202. package/dist/components/code-preview/code-preview.react.js +0 -1
  203. package/dist/components/code-preview/code-preview.scss.js +0 -1
  204. package/dist/components/code-preview/index.js +0 -1
  205. package/dist/components/container/container.component.js +0 -5
  206. package/dist/components/container/container.js +0 -1
  207. package/dist/components/container/container.react.js +0 -1
  208. package/dist/components/container/container.scss.js +0 -1
  209. package/dist/components/container/index.js +0 -1
  210. package/dist/components/dialog/dialog.component.js +0 -23
  211. package/dist/components/dialog/dialog.js +0 -1
  212. package/dist/components/dialog/dialog.react.js +0 -1
  213. package/dist/components/dialog/dialog.scss.js +0 -1
  214. package/dist/components/dialog/index.js +0 -1
  215. package/dist/components/dropzone/dropzone.component.js +0 -11
  216. package/dist/components/dropzone/dropzone.js +0 -1
  217. package/dist/components/dropzone/dropzone.react.js +0 -1
  218. package/dist/components/dropzone/dropzone.scss.js +0 -1
  219. package/dist/components/dropzone/index.js +0 -1
  220. package/dist/components/form-field/form-field.component.js +0 -22
  221. package/dist/components/form-field/form-field.js +0 -1
  222. package/dist/components/form-field/form-field.react.js +0 -1
  223. package/dist/components/form-field/form-field.scss.js +0 -1
  224. package/dist/components/form-field/index.js +0 -1
  225. package/dist/components/gallery/gallery-image.component.js +0 -14
  226. package/dist/components/gallery/gallery-image.js +0 -1
  227. package/dist/components/gallery/gallery.component.js +0 -5
  228. package/dist/components/gallery/gallery.js +0 -1
  229. package/dist/components/gallery/gallery.react.js +0 -1
  230. package/dist/components/gallery/gallery.scss.js +0 -1
  231. package/dist/components/gallery/index.js +0 -1
  232. package/dist/components/icon/icon.component.js +0 -1
  233. package/dist/components/icon/icon.js +0 -1
  234. package/dist/components/icon/icon.react.js +0 -1
  235. package/dist/components/icon/icon.scss.js +0 -1
  236. package/dist/components/icon/index.js +0 -1
  237. package/dist/components/loading/index.js +0 -1
  238. package/dist/components/loading/loading.component.js +0 -5
  239. package/dist/components/loading/loading.js +0 -1
  240. package/dist/components/loading/loading.react.js +0 -1
  241. package/dist/components/loading/loading.scss.js +0 -1
  242. package/dist/components/notice/index.js +0 -1
  243. package/dist/components/notice/notice.component.js +0 -17
  244. package/dist/components/notice/notice.js +0 -1
  245. package/dist/components/notice/notice.react.js +0 -1
  246. package/dist/components/notice/notice.scss.js +0 -1
  247. package/dist/components/pagination/index.js +0 -1
  248. package/dist/components/pagination/pagination.component.js +0 -13
  249. package/dist/components/pagination/pagination.js +0 -1
  250. package/dist/components/pagination/pagination.react.js +0 -1
  251. package/dist/components/pagination/pagination.scss.js +0 -1
  252. package/dist/components/tabs/index.js +0 -1
  253. package/dist/components/tabs/tab.component.js +0 -1
  254. package/dist/components/tabs/tab.js +0 -1
  255. package/dist/components/tabs/tabs-item.component.js +0 -1
  256. package/dist/components/tabs/tabs-panel.component.js +0 -10
  257. package/dist/components/tabs/tabs-panel.js +0 -1
  258. package/dist/components/tabs/tabs-tab.component.js +0 -12
  259. package/dist/components/tabs/tabs-tab.js +0 -1
  260. package/dist/components/tabs/tabs.component.js +0 -28
  261. package/dist/components/tabs/tabs.js +0 -1
  262. package/dist/components/tabs/tabs.react.js +0 -1
  263. package/dist/components/tabs/tabs.scss.js +0 -1
  264. package/dist/components/tooltip/index.js +0 -1
  265. package/dist/components/tooltip/tooltip.component.js +0 -10
  266. package/dist/components/tooltip/tooltip.js +0 -1
  267. package/dist/components/tooltip/tooltip.react.js +0 -1
  268. package/dist/components/tooltip/tooltip.scss.js +0 -1
  269. package/dist/css/base.css +0 -1
  270. package/dist/css/themes/grantcodes.css +0 -1
  271. package/dist/fonts/greycliff-bold-oblique.woff +0 -0
  272. package/dist/fonts/greycliff-bold-oblique.woff2 +0 -0
  273. package/dist/fonts/greycliff-bold.woff +0 -0
  274. package/dist/fonts/greycliff-bold.woff2 +0 -0
  275. package/dist/fonts/greycliff-demi-bold-oblique.woff +0 -0
  276. package/dist/fonts/greycliff-demi-bold-oblique.woff2 +0 -0
  277. package/dist/fonts/greycliff-demi-bold.woff +0 -0
  278. package/dist/fonts/greycliff-demi-bold.woff2 +0 -0
  279. package/dist/fonts/greycliff-extra-bold-oblique.woff +0 -0
  280. package/dist/fonts/greycliff-extra-bold-oblique.woff2 +0 -0
  281. package/dist/fonts/greycliff-extra-bold.woff +0 -0
  282. package/dist/fonts/greycliff-extra-bold.woff2 +0 -0
  283. package/dist/fonts/greycliff-extra-light-oblique.woff +0 -0
  284. package/dist/fonts/greycliff-extra-light-oblique.woff2 +0 -0
  285. package/dist/fonts/greycliff-extra-light.woff +0 -0
  286. package/dist/fonts/greycliff-extra-light.woff2 +0 -0
  287. package/dist/fonts/greycliff-heavy-oblique.woff +0 -0
  288. package/dist/fonts/greycliff-heavy-oblique.woff2 +0 -0
  289. package/dist/fonts/greycliff-heavy.woff +0 -0
  290. package/dist/fonts/greycliff-heavy.woff2 +0 -0
  291. package/dist/fonts/greycliff-light-oblique.woff +0 -0
  292. package/dist/fonts/greycliff-light-oblique.woff2 +0 -0
  293. package/dist/fonts/greycliff-light.woff +0 -0
  294. package/dist/fonts/greycliff-light.woff2 +0 -0
  295. package/dist/fonts/greycliff-medium-oblique.woff +0 -0
  296. package/dist/fonts/greycliff-medium-oblique.woff2 +0 -0
  297. package/dist/fonts/greycliff-medium.woff +0 -0
  298. package/dist/fonts/greycliff-medium.woff2 +0 -0
  299. package/dist/fonts/greycliff-regular-oblique.woff +0 -0
  300. package/dist/fonts/greycliff-regular-oblique.woff2 +0 -0
  301. package/dist/fonts/greycliff-regular.woff +0 -0
  302. package/dist/fonts/greycliff-regular.woff2 +0 -0
  303. package/dist/fonts/greycliff-thin-oblique.woff +0 -0
  304. package/dist/fonts/greycliff-thin-oblique.woff2 +0 -0
  305. package/dist/fonts/greycliff-thin.woff +0 -0
  306. package/dist/fonts/greycliff-thin.woff2 +0 -0
  307. package/dist/icons.js +0 -1
  308. package/dist/lib/generate-id.js +0 -1
  309. package/dist/main.js +0 -1
  310. package/dist/react.js +0 -1
@@ -0,0 +1,118 @@
1
+ import { describe, it, afterEach } from "node:test";
2
+ import { strict as assert } from "node:assert";
3
+ import { fixture, cleanup } from "../../test-utils/index.js";
4
+ import "./form-field.js";
5
+
6
+ describe("Form Field 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-form-field");
15
+ assert.ok(element, "Element should be created");
16
+ assert.ok(element.shadowRoot, "Element should have shadow root");
17
+ });
18
+
19
+ it("should display label text", async () => {
20
+ element = await fixture("grantcodes-form-field", {
21
+ label: "Username",
22
+ });
23
+
24
+ const labelElement = element.shadowRoot.querySelector(".form-field__label");
25
+ assert.ok(labelElement, "Label element should exist");
26
+ assert.strictEqual(labelElement.textContent, "Username", "Label text should match");
27
+ });
28
+
29
+ it("should display error message when error is set", async () => {
30
+ element = await fixture("grantcodes-form-field", {
31
+ label: "Email",
32
+ error: "Invalid email format",
33
+ });
34
+
35
+ const errorElement = element.shadowRoot.querySelector(".form-field__error");
36
+ assert.ok(errorElement, "Error element should exist");
37
+ assert.ok(
38
+ errorElement.textContent.includes("Invalid email format"),
39
+ "Error message should be displayed",
40
+ );
41
+ });
42
+
43
+ it("should not display error when no error is set", async () => {
44
+ element = await fixture("grantcodes-form-field", {
45
+ label: "Email",
46
+ });
47
+
48
+ const errorElement = element.shadowRoot.querySelector(".form-field__error");
49
+ assert.ok(!errorElement, "Error element should not exist");
50
+ });
51
+
52
+ it("should display help text when help is provided", async () => {
53
+ element = await fixture("grantcodes-form-field", {
54
+ label: "Password",
55
+ help: "Must be at least 8 characters",
56
+ });
57
+
58
+ const helpElement = element.shadowRoot.querySelector(".form-field__help");
59
+ assert.ok(helpElement, "Help element should exist");
60
+ assert.strictEqual(
61
+ helpElement.textContent,
62
+ "Must be at least 8 characters",
63
+ "Help text should match",
64
+ );
65
+ });
66
+
67
+ it("should not display help when no help is provided", async () => {
68
+ element = await fixture("grantcodes-form-field", {
69
+ label: "Password",
70
+ });
71
+
72
+ const helpElement = element.shadowRoot.querySelector(".form-field__help");
73
+ assert.ok(!helpElement, "Help element should not exist when help is not provided");
74
+ });
75
+
76
+ it("should generate unique IDs", async () => {
77
+ const element1 = await fixture("grantcodes-form-field");
78
+ const element2 = await fixture("grantcodes-form-field");
79
+
80
+ assert.notStrictEqual(element1.id, element2.id, "IDs should be unique");
81
+
82
+ cleanup(element1);
83
+ cleanup(element2);
84
+ });
85
+
86
+ it("should have label element", async () => {
87
+ element = await fixture("grantcodes-form-field", {
88
+ label: "Test Field",
89
+ });
90
+
91
+ const label = element.shadowRoot.querySelector("label");
92
+ assert.ok(label, "Label element should exist");
93
+ });
94
+
95
+ it("should render as fieldset for grouped inputs", async () => {
96
+ element = await fixture("grantcodes-form-field", {
97
+ label: "Select options",
98
+ });
99
+
100
+ // Add nested form fields to trigger group mode
101
+ const nestedField = document.createElement("grantcodes-form-field");
102
+ element.appendChild(nestedField);
103
+
104
+ await element.updateComplete;
105
+ // This would need to check if groupInput was set, which happens in firstUpdated
106
+ });
107
+
108
+ it("should have slot for input elements", async () => {
109
+ element = await fixture("grantcodes-form-field", {
110
+ label: "Input",
111
+ });
112
+
113
+ const slot = element.shadowRoot.querySelector("slot");
114
+ assert.ok(slot, "Slot should exist for input elements");
115
+ });
116
+ });
117
+
118
+
@@ -0,0 +1 @@
1
+ export * from "./form-field";
@@ -0,0 +1,52 @@
1
+ import { LitElement } from "lit";
2
+ import { html } from "lit/static-html.js";
3
+ import { galleryStyles } from "./gallery.styles.js";
4
+
5
+ export class GrantCodesGalleryImage extends LitElement {
6
+ static styles = [galleryStyles];
7
+
8
+ static properties = {
9
+ width: { type: Number },
10
+ height: { type: Number },
11
+ src: { type: String },
12
+ alt: { type: String },
13
+ caption: { type: String },
14
+ thumbnail: { type: String },
15
+ };
16
+
17
+ constructor() {
18
+ super();
19
+
20
+ this.width = 0;
21
+ this.height = 0;
22
+ this.src = "";
23
+ this.alt = "";
24
+ this.caption = "";
25
+ this.thumbnail = "";
26
+ }
27
+
28
+ captionTemplate() {
29
+ if (!this.caption) {
30
+ return html``;
31
+ }
32
+
33
+ return html`
34
+ <figcaption class="gallery__image__caption">${this.caption}</figcaption>
35
+ `;
36
+ }
37
+
38
+ render() {
39
+ return html`
40
+ <figure class="gallery__image">
41
+ <img
42
+ width=${this.width}
43
+ height=${this.height}
44
+ src=${this.src}
45
+ alt=${this.alt}
46
+ loading="lazy"
47
+ />
48
+ ${this.captionTemplate()}
49
+ </figure>
50
+ `;
51
+ }
52
+ }
@@ -0,0 +1,7 @@
1
+ import { GrantCodesGalleryImage } from "./gallery-image.component.js";
2
+
3
+ export * from "./gallery-image.component.js";
4
+ export default GrantCodesGalleryImage;
5
+
6
+ customElements.define("grantcodes-gallery-image", GrantCodesGalleryImage);
7
+
@@ -0,0 +1,25 @@
1
+ import { LitElement } from "lit";
2
+ import { html } from "lit/static-html.js";
3
+ import { galleryStyles } from "./gallery.styles.js";
4
+
5
+ export class GrantCodesGallery extends LitElement {
6
+ // Styles are scoped to this element: they won't conflict with styles
7
+ // on the main page or in other components. Styling API can be exposed
8
+ // via CSS custom properties.
9
+ static styles = [galleryStyles];
10
+
11
+ /** @type {any[]} */
12
+ images = [];
13
+
14
+ // Define reactive properties--updating a reactive property causes
15
+ // the component to update.
16
+ // @property() label = 'Button Label'
17
+
18
+ render() {
19
+ return html`
20
+ <div class="gallery">
21
+ <slot class="gallery__slot"></slot>
22
+ </div>
23
+ `;
24
+ }
25
+ }
@@ -0,0 +1,7 @@
1
+ import { GrantCodesGallery } from "./gallery.component.js";
2
+
3
+ export * from "./gallery.component.js";
4
+ export default GrantCodesGallery;
5
+
6
+ customElements.define("grantcodes-gallery", GrantCodesGallery);
7
+
@@ -0,0 +1,60 @@
1
+ import { html } from "lit/static-html.js";
2
+ import { getStorybookHelpers } from "@wc-toolkit/storybook-helpers";
3
+ const { events, args, argTypes, template } =
4
+ getStorybookHelpers("grantcodes-gallery");
5
+ import "./gallery.js";
6
+ import "./gallery-image.js";
7
+
8
+ function getRandomSize() {
9
+ return Math.floor(Math.random() * (800 - 100 + 1) + 100);
10
+ }
11
+
12
+ const getTestImage = () => {
13
+ const width = getRandomSize();
14
+ const height = getRandomSize();
15
+
16
+ return {
17
+ src: `https://picsum.photos/${width}/${height}`,
18
+ alt: "Image",
19
+ width,
20
+ height,
21
+ };
22
+ };
23
+
24
+ const testImages = [];
25
+ for (let i = 0; i < 12; i++) {
26
+ testImages.push(getTestImage());
27
+ }
28
+
29
+ const meta = {
30
+ title: "Components/Gallery",
31
+ component: "grantcodes-gallery",
32
+ args: {
33
+ ...args,
34
+ caption: "This is a test caption",
35
+ },
36
+ argTypes,
37
+ render: (args) =>
38
+ template(
39
+ args,
40
+ html`${testImages.map(
41
+ ({ src, alt, width, height }) =>
42
+ html`<grantcodes-gallery-image
43
+ src=${src}
44
+ alt=${alt}
45
+ width=${width}
46
+ height=${height}
47
+ caption=${args.caption}
48
+ ></grantcodes-gallery-image>`,
49
+ )}`,
50
+ ),
51
+ parameters: {
52
+ actions: {
53
+ handles: events,
54
+ },
55
+ },
56
+ };
57
+
58
+ export default meta;
59
+
60
+ export const Gallery = {};
@@ -0,0 +1,56 @@
1
+ import { css } from "lit";
2
+
3
+ export const galleryStyles = css`
4
+
5
+ .gallery {
6
+ display: block;
7
+ }
8
+
9
+ .gallery__slot {
10
+ display: grid;
11
+ grid-template-columns: repeat(var(--gallery-columns, 3), 1fr);
12
+ width: 100%;
13
+ flex-direction: row;
14
+ flex-wrap: wrap;
15
+ gap: var(--gallery-gap, 0);
16
+ }
17
+
18
+ .gallery__slot > * {
19
+ display: block;
20
+ width: 100%;
21
+ }
22
+
23
+ .gallery--has-lightbox > * {
24
+ cursor: pointer;
25
+ }
26
+
27
+ .gallery__image {
28
+ position: relative;
29
+ width: 100%;
30
+ display: block;
31
+ flex-direction: column;
32
+ margin: 0;
33
+ }
34
+
35
+ .gallery__image img {
36
+ display: block;
37
+ width: 100%;
38
+ height: auto;
39
+ /* Make the image square */
40
+ aspect-ratio: 1;
41
+ object-fit: cover;
42
+ object-position: center;
43
+ }
44
+
45
+ .gallery__image__caption {
46
+ position: absolute;
47
+ inset: 0;
48
+ inset-block-start: auto;
49
+ background-color: rgba(0, 0, 0, 0.4);
50
+ padding-inline: 0.5em;
51
+ padding-block: 0.3em;
52
+ line-height: 1.2;
53
+ color: white;
54
+ }
55
+
56
+ `;
@@ -0,0 +1,58 @@
1
+ import { describe, it, afterEach } from "node:test";
2
+ import { strict as assert } from "node:assert";
3
+ import { fixture, cleanup } from "../../test-utils/index.js";
4
+ import "./gallery.js";
5
+
6
+ describe("Gallery 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-gallery");
15
+ assert.ok(element, "Element should be created");
16
+ assert.ok(element.shadowRoot, "Element should have shadow root");
17
+ });
18
+
19
+ it("should render gallery wrapper", async () => {
20
+ element = await fixture("grantcodes-gallery");
21
+ const gallery = element.shadowRoot.querySelector(".gallery");
22
+ assert.ok(gallery, "Gallery element should exist");
23
+ });
24
+
25
+ it("should have slot for gallery items", async () => {
26
+ element = await fixture("grantcodes-gallery");
27
+ const slot = element.shadowRoot.querySelector("slot");
28
+ assert.ok(slot, "Slot should exist");
29
+ assert.ok(
30
+ slot.classList.contains("gallery__slot"),
31
+ "Slot should have gallery__slot class",
32
+ );
33
+ });
34
+
35
+ it("should support multiple image slots", async () => {
36
+ element = await fixture("grantcodes-gallery");
37
+
38
+ const img1 = document.createElement("img");
39
+ img1.src = "image1.jpg";
40
+ const img2 = document.createElement("img");
41
+ img2.src = "image2.jpg";
42
+
43
+ element.appendChild(img1);
44
+ element.appendChild(img2);
45
+
46
+ await element.updateComplete;
47
+
48
+ assert.strictEqual(element.children.length, 2, "Should have two images");
49
+ });
50
+
51
+ it("should initialize images array", async () => {
52
+ element = await fixture("grantcodes-gallery");
53
+ assert.ok(Array.isArray(element.images), "Images should be an array");
54
+ assert.strictEqual(element.images.length, 0, "Images array should be empty initially");
55
+ });
56
+ });
57
+
58
+
@@ -0,0 +1,2 @@
1
+ export * from "./gallery";
2
+ export * from "./gallery-image";
@@ -0,0 +1,14 @@
1
+ import { LitElement } from "lit";
2
+ import { html } from "lit/static-html.js";
3
+ import { iconStyles } from "./icon.styles.js";
4
+
5
+ export class GrantCodesIcon extends LitElement {
6
+ // Styles are scoped to this element: they won't conflict with styles
7
+ // on the main page or in other components. Styling API can be exposed
8
+ // via CSS custom properties.
9
+ static styles = [iconStyles];
10
+
11
+ render() {
12
+ return html`<span class="icon"><slot></slot></span>`;
13
+ }
14
+ }
@@ -0,0 +1,7 @@
1
+ import { GrantCodesIcon } from "./icon.component.js";
2
+
3
+ export * from "./icon.component.js";
4
+ export default GrantCodesIcon;
5
+
6
+ customElements.define("grantcodes-icon", GrantCodesIcon);
7
+
@@ -0,0 +1,26 @@
1
+ import { html } from "lit/static-html.js";
2
+ import { unsafeHTML } from "lit/directives/unsafe-html.js";
3
+ import "./icon.js";
4
+ import { ArrowRight, Heart, Star, CheckCircle2 } from "../../icons.js";
5
+
6
+ const meta = {
7
+ title: "Components/Icon",
8
+ component: "grantcodes-icon",
9
+ };
10
+
11
+ export default meta;
12
+
13
+ export const Icon = {
14
+ render: () => html`<grantcodes-icon>${unsafeHTML(ArrowRight)}</grantcodes-icon>`,
15
+ };
16
+
17
+ export const MultipleIcons = {
18
+ render: () => html`
19
+ <div style="display: flex; gap: 1rem;">
20
+ <grantcodes-icon>${unsafeHTML(Heart)}</grantcodes-icon>
21
+ <grantcodes-icon>${unsafeHTML(Star)}</grantcodes-icon>
22
+ <grantcodes-icon>${unsafeHTML(CheckCircle2)}</grantcodes-icon>
23
+ <grantcodes-icon>${unsafeHTML(ArrowRight)}</grantcodes-icon>
24
+ </div>
25
+ `,
26
+ };
@@ -0,0 +1,28 @@
1
+ import { css } from "lit";
2
+
3
+ export const iconStyles = css`
4
+
5
+ .icon {
6
+ display: inline-block;
7
+ color: inherit;
8
+ width: 1em;
9
+ height: 1em;
10
+ }
11
+
12
+ .icon svg {
13
+ display: block;
14
+ width: 100%;
15
+ height: 100%;
16
+ object-fit: contain;
17
+ object-position: center;
18
+ }
19
+
20
+ .icon svg:not([fill]) {
21
+ fill: currentColor;
22
+ }
23
+
24
+ .icon svg:not([stoke]) {
25
+ stroke: currentColor;
26
+ }
27
+
28
+ `;
@@ -0,0 +1,57 @@
1
+ import { describe, it, afterEach } from "node:test";
2
+ import { strict as assert } from "node:assert";
3
+ import { fixture, cleanup } from "../../test-utils/index.js";
4
+ import "./icon.js";
5
+
6
+ describe("Icon 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-icon");
15
+ assert.ok(element, "Element should be created");
16
+ assert.ok(element.shadowRoot, "Element should have shadow root");
17
+ });
18
+
19
+ it("should have icon wrapper span", async () => {
20
+ element = await fixture("grantcodes-icon");
21
+ const iconSpan = element.shadowRoot.querySelector(".icon");
22
+ assert.ok(iconSpan, "Icon span should exist");
23
+ });
24
+
25
+ it("should have slot for SVG content", async () => {
26
+ element = await fixture("grantcodes-icon");
27
+ const slot = element.shadowRoot.querySelector("slot");
28
+ assert.ok(slot, "Slot should exist");
29
+ });
30
+
31
+ it("should render slotted SVG content", async () => {
32
+ element = await fixture("grantcodes-icon");
33
+ const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
34
+ svg.setAttribute("width", "24");
35
+ svg.setAttribute("height", "24");
36
+ element.appendChild(svg);
37
+
38
+ await element.updateComplete;
39
+
40
+ const slottedSvg = element.querySelector("svg");
41
+ assert.ok(slottedSvg, "SVG should be slotted");
42
+ assert.strictEqual(slottedSvg.getAttribute("width"), "24");
43
+ });
44
+
45
+ it("should accept any SVG as slot content", async () => {
46
+ element = await fixture("grantcodes-icon");
47
+ element.innerHTML = '<svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"/></svg>';
48
+
49
+ await element.updateComplete;
50
+
51
+ const svg = element.querySelector("svg");
52
+ assert.ok(svg, "SVG should be present");
53
+ assert.ok(svg.querySelector("circle"), "SVG content should be preserved");
54
+ });
55
+ });
56
+
57
+
@@ -0,0 +1 @@
1
+ export * from "./icon";
@@ -0,0 +1 @@
1
+ export * from "./loading";
@@ -0,0 +1,21 @@
1
+ import { LitElement } from "lit";
2
+ import { html } from "lit/static-html.js";
3
+ import { loadingStyles } from "./loading.styles.js";
4
+
5
+ export class GrantCodesLoading extends LitElement {
6
+ // Styles are scoped to this element: they won't conflict with styles
7
+ // on the main page or in other components. Styling API can be exposed
8
+ // via CSS custom properties.
9
+ static styles = [loadingStyles];
10
+
11
+ // Define reactive properties--updating a reactive property causes
12
+ // the component to update.
13
+
14
+ render() {
15
+ return html`
16
+ <span class="loading">
17
+ <slot></slot>
18
+ </span>
19
+ `;
20
+ }
21
+ }
@@ -0,0 +1,7 @@
1
+ import { GrantCodesLoading } from "./loading.component.js";
2
+
3
+ export * from "./loading.component.js";
4
+ export default GrantCodesLoading;
5
+
6
+ customElements.define("grantcodes-loading", GrantCodesLoading);
7
+
@@ -0,0 +1,25 @@
1
+ import { html } from "lit/static-html.js";
2
+ import { getStorybookHelpers } from "@wc-toolkit/storybook-helpers";
3
+ const { events, args, argTypes, template } =
4
+ getStorybookHelpers("grantcodes-loading");
5
+ import "./loading.js";
6
+
7
+ const meta = {
8
+ title: "Components/Loading",
9
+ component: "grantcodes-loading",
10
+ args: {
11
+ ...args,
12
+ text: "Loading...",
13
+ },
14
+ argTypes,
15
+ render: (args) => template(args, html`${args.text}`),
16
+ parameters: {
17
+ actions: {
18
+ handles: events,
19
+ },
20
+ },
21
+ };
22
+
23
+ export default meta;
24
+
25
+ export const Loading = {};
@@ -0,0 +1,43 @@
1
+ import { css } from "lit";
2
+
3
+ export const loadingStyles = css`
4
+
5
+ :host {
6
+ --height: 0.5rem;
7
+ --inner-width: 40%;
8
+ --starting-position: calc(0% - (var(--inner-width) * 2));
9
+ --ending-position: calc(100% + (var(--inner-width) * 2));
10
+ }
11
+
12
+ .loading {
13
+ display: block;
14
+ height: var(--height);
15
+ border-radius: calc(var(--height) / 2);
16
+ overflow: hidden;
17
+ padding-top: var(--height);
18
+ background-repeat: no-repeat;
19
+ background-color: var(--g-color-brand-purple-500);
20
+ background-image: linear-gradient(
21
+ to right,
22
+ transparent 0%,
23
+ var(--g-color-neutral-500) 10%,
24
+ var(--g-color-neutral-500) 90%,
25
+ transparent 100%
26
+ );
27
+ background-size: var(--inner-width) 100%;
28
+ background-position-x: var(--starting-position);
29
+ animation: loading 2s linear infinite;
30
+ /* This is probably a bad way of hiding the text */
31
+ font-size: 0;
32
+ }
33
+
34
+ @keyframes loading {
35
+ 0% {
36
+ background-position-x: var(--starting-position);
37
+ }
38
+ 100% {
39
+ background-position-x: var(--ending-position);
40
+ }
41
+ }
42
+
43
+ `;