@grantcodes/ui 2.0.0-beta.9 → 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 (314) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +6 -8
  3. package/custom-elements.json +4273 -0
  4. package/package.json +80 -90
  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/_virtual/_commonjsHelpers.js +0 -1
  181. package/dist/_virtual/index.js +0 -1
  182. package/dist/_virtual/react.production.min.js +0 -1
  183. package/dist/_virtual/react.production.min2.js +0 -1
  184. package/dist/components/avatar/avatar.component.js +0 -3
  185. package/dist/components/avatar/avatar.js +0 -1
  186. package/dist/components/avatar/avatar.react.js +0 -1
  187. package/dist/components/avatar/avatar.scss.js +0 -1
  188. package/dist/components/avatar/index.js +0 -1
  189. package/dist/components/button/button.component.js +0 -5
  190. package/dist/components/button/button.js +0 -1
  191. package/dist/components/button/button.react.js +0 -1
  192. package/dist/components/button/button.scss.js +0 -1
  193. package/dist/components/button/index.js +0 -1
  194. package/dist/components/button-group/button-group.component.js +0 -5
  195. package/dist/components/button-group/button-group.js +0 -1
  196. package/dist/components/button-group/button-group.react.js +0 -1
  197. package/dist/components/button-group/button-group.scss.js +0 -1
  198. package/dist/components/button-group/index.js +0 -1
  199. package/dist/components/card/card.component.js +0 -8
  200. package/dist/components/card/card.js +0 -1
  201. package/dist/components/card/card.react.js +0 -1
  202. package/dist/components/card/card.scss.js +0 -1
  203. package/dist/components/card/index.js +0 -1
  204. package/dist/components/code-preview/code-preview.component.js +0 -3
  205. package/dist/components/code-preview/code-preview.js +0 -1
  206. package/dist/components/code-preview/code-preview.react.js +0 -1
  207. package/dist/components/code-preview/code-preview.scss.js +0 -1
  208. package/dist/components/code-preview/index.js +0 -1
  209. package/dist/components/container/container.component.js +0 -5
  210. package/dist/components/container/container.js +0 -1
  211. package/dist/components/container/container.react.js +0 -1
  212. package/dist/components/container/container.scss.js +0 -1
  213. package/dist/components/container/index.js +0 -1
  214. package/dist/components/dialog/dialog.component.js +0 -23
  215. package/dist/components/dialog/dialog.js +0 -1
  216. package/dist/components/dialog/dialog.react.js +0 -1
  217. package/dist/components/dialog/dialog.scss.js +0 -1
  218. package/dist/components/dialog/index.js +0 -1
  219. package/dist/components/dropzone/dropzone.component.js +0 -11
  220. package/dist/components/dropzone/dropzone.js +0 -1
  221. package/dist/components/dropzone/dropzone.react.js +0 -1
  222. package/dist/components/dropzone/dropzone.scss.js +0 -1
  223. package/dist/components/dropzone/index.js +0 -1
  224. package/dist/components/form-field/form-field.component.js +0 -22
  225. package/dist/components/form-field/form-field.js +0 -1
  226. package/dist/components/form-field/form-field.react.js +0 -1
  227. package/dist/components/form-field/form-field.scss.js +0 -1
  228. package/dist/components/form-field/index.js +0 -1
  229. package/dist/components/gallery/gallery-image.component.js +0 -14
  230. package/dist/components/gallery/gallery-image.js +0 -1
  231. package/dist/components/gallery/gallery.component.js +0 -5
  232. package/dist/components/gallery/gallery.js +0 -1
  233. package/dist/components/gallery/gallery.react.js +0 -1
  234. package/dist/components/gallery/gallery.scss.js +0 -1
  235. package/dist/components/gallery/index.js +0 -1
  236. package/dist/components/icon/icon.component.js +0 -1
  237. package/dist/components/icon/icon.js +0 -1
  238. package/dist/components/icon/icon.react.js +0 -1
  239. package/dist/components/icon/icon.scss.js +0 -1
  240. package/dist/components/icon/index.js +0 -1
  241. package/dist/components/loading/index.js +0 -1
  242. package/dist/components/loading/loading.component.js +0 -5
  243. package/dist/components/loading/loading.js +0 -1
  244. package/dist/components/loading/loading.react.js +0 -1
  245. package/dist/components/loading/loading.scss.js +0 -1
  246. package/dist/components/notice/index.js +0 -1
  247. package/dist/components/notice/notice.component.js +0 -16
  248. package/dist/components/notice/notice.js +0 -1
  249. package/dist/components/notice/notice.react.js +0 -1
  250. package/dist/components/notice/notice.scss.js +0 -1
  251. package/dist/components/pagination/index.js +0 -1
  252. package/dist/components/pagination/pagination.component.js +0 -13
  253. package/dist/components/pagination/pagination.js +0 -1
  254. package/dist/components/pagination/pagination.react.js +0 -1
  255. package/dist/components/pagination/pagination.scss.js +0 -1
  256. package/dist/components/tabs/index.js +0 -1
  257. package/dist/components/tabs/internal/tabs-button.component.js +0 -13
  258. package/dist/components/tabs/internal/tabs-button.js +0 -1
  259. package/dist/components/tabs/internal/tabs-item.component.js +0 -1
  260. package/dist/components/tabs/tab.component.js +0 -10
  261. package/dist/components/tabs/tab.js +0 -1
  262. package/dist/components/tabs/tabs.component.js +0 -22
  263. package/dist/components/tabs/tabs.js +0 -1
  264. package/dist/components/tabs/tabs.react.js +0 -1
  265. package/dist/components/tabs/tabs.scss.js +0 -1
  266. package/dist/components/tooltip/index.js +0 -1
  267. package/dist/components/tooltip/tooltip.component.js +0 -10
  268. package/dist/components/tooltip/tooltip.js +0 -1
  269. package/dist/components/tooltip/tooltip.react.js +0 -1
  270. package/dist/components/tooltip/tooltip.scss.js +0 -1
  271. package/dist/css/base.css +0 -1
  272. package/dist/css/themes/grantcodes.css +0 -1
  273. package/dist/fonts/greycliff-bold-oblique.woff +0 -0
  274. package/dist/fonts/greycliff-bold-oblique.woff2 +0 -0
  275. package/dist/fonts/greycliff-bold.woff +0 -0
  276. package/dist/fonts/greycliff-bold.woff2 +0 -0
  277. package/dist/fonts/greycliff-demi-bold-oblique.woff +0 -0
  278. package/dist/fonts/greycliff-demi-bold-oblique.woff2 +0 -0
  279. package/dist/fonts/greycliff-demi-bold.woff +0 -0
  280. package/dist/fonts/greycliff-demi-bold.woff2 +0 -0
  281. package/dist/fonts/greycliff-extra-bold-oblique.woff +0 -0
  282. package/dist/fonts/greycliff-extra-bold-oblique.woff2 +0 -0
  283. package/dist/fonts/greycliff-extra-bold.woff +0 -0
  284. package/dist/fonts/greycliff-extra-bold.woff2 +0 -0
  285. package/dist/fonts/greycliff-extra-light-oblique.woff +0 -0
  286. package/dist/fonts/greycliff-extra-light-oblique.woff2 +0 -0
  287. package/dist/fonts/greycliff-extra-light.woff +0 -0
  288. package/dist/fonts/greycliff-extra-light.woff2 +0 -0
  289. package/dist/fonts/greycliff-heavy-oblique.woff +0 -0
  290. package/dist/fonts/greycliff-heavy-oblique.woff2 +0 -0
  291. package/dist/fonts/greycliff-heavy.woff +0 -0
  292. package/dist/fonts/greycliff-heavy.woff2 +0 -0
  293. package/dist/fonts/greycliff-light-oblique.woff +0 -0
  294. package/dist/fonts/greycliff-light-oblique.woff2 +0 -0
  295. package/dist/fonts/greycliff-light.woff +0 -0
  296. package/dist/fonts/greycliff-light.woff2 +0 -0
  297. package/dist/fonts/greycliff-medium-oblique.woff +0 -0
  298. package/dist/fonts/greycliff-medium-oblique.woff2 +0 -0
  299. package/dist/fonts/greycliff-medium.woff +0 -0
  300. package/dist/fonts/greycliff-medium.woff2 +0 -0
  301. package/dist/fonts/greycliff-regular-oblique.woff +0 -0
  302. package/dist/fonts/greycliff-regular-oblique.woff2 +0 -0
  303. package/dist/fonts/greycliff-regular.woff +0 -0
  304. package/dist/fonts/greycliff-regular.woff2 +0 -0
  305. package/dist/fonts/greycliff-thin-oblique.woff +0 -0
  306. package/dist/fonts/greycliff-thin-oblique.woff2 +0 -0
  307. package/dist/fonts/greycliff-thin.woff +0 -0
  308. package/dist/fonts/greycliff-thin.woff2 +0 -0
  309. package/dist/icons.js +0 -1
  310. package/dist/lib/generate-id.js +0 -1
  311. package/dist/main.js +0 -1
  312. package/dist/node_modules/react/cjs/react.production.min.js +0 -9
  313. package/dist/node_modules/react/index.js +0 -1
  314. package/dist/react.js +0 -1
@@ -0,0 +1,143 @@
1
+ import { html } from "lit";
2
+ import "./footer.js";
3
+
4
+ const meta = {
5
+ title: "Components/Footer",
6
+ component: "grantcodes-footer",
7
+ };
8
+
9
+ export default meta;
10
+
11
+ const columns = html`
12
+ <grantcodes-footer-column>
13
+ <h3>Company</h3>
14
+ <ul>
15
+ <li><a href="/about">About Us</a></li>
16
+ <li><a href="/team">Team</a></li>
17
+ <li><a href="/careers">Careers</a></li>
18
+ <li><a href="/press">Press</a></li>
19
+ </ul>
20
+ </grantcodes-footer-column>
21
+
22
+ <grantcodes-footer-column>
23
+ <h3>Product</h3>
24
+ <ul>
25
+ <li><a href="/features">Features</a></li>
26
+ <li><a href="/pricing">Pricing</a></li>
27
+ <li><a href="/docs">Documentation</a></li>
28
+ <li><a href="/api">API</a></li>
29
+ </ul>
30
+ </grantcodes-footer-column>
31
+
32
+ <grantcodes-footer-column>
33
+ <h3>Support</h3>
34
+ <ul>
35
+ <li><a href="/help">Help Center</a></li>
36
+ <li><a href="/contact">Contact</a></li>
37
+ <li><a href="/status">Status</a></li>
38
+ <li><a href="/security">Security</a></li>
39
+ </ul>
40
+ </grantcodes-footer-column>
41
+ `;
42
+
43
+ /**
44
+ * Basic footer with three columns
45
+ */
46
+ export const Footer = {
47
+ render: () => html`
48
+ <grantcodes-footer>
49
+ ${columns}
50
+
51
+ <div slot="bottom">
52
+ <p>&copy; 2024 MyCompany. All rights reserved.</p>
53
+ </div>
54
+ </grantcodes-footer>
55
+ `,
56
+ };
57
+
58
+ /**
59
+ * Footer with four columns
60
+ */
61
+ export const FourColumns = {
62
+ render: () => html`
63
+ <grantcodes-footer columns="4">
64
+ ${columns}
65
+
66
+ <div slot="bottom">
67
+ <p>&copy; 2024 Company Name</p>
68
+ </div>
69
+ <div slot="bottom" style="display: flex; gap: 1rem;">
70
+ <a href="/twitter">Twitter</a>
71
+ <a href="/github">GitHub</a>
72
+ <a href="/linkedin">LinkedIn</a>
73
+ </div>
74
+ </grantcodes-footer>
75
+ `,
76
+ };
77
+
78
+ /**
79
+ * Footer with social links in bottom section
80
+ */
81
+ export const WithSocialLinks = {
82
+ render: () => html`
83
+ <grantcodes-footer>
84
+ ${columns}
85
+
86
+ <div slot="bottom" style="display: flex; gap: 0.5rem; align-items: center;">
87
+ <span>&copy; 2024 MyApp</span>
88
+ <span>•</span>
89
+ <a href="/privacy">Privacy</a>
90
+ <span>•</span>
91
+ <a href="/terms">Terms</a>
92
+ </div>
93
+
94
+ <!-- <div slot="bottom" style="display: flex; gap: 1rem; font-size: 1.5rem;">
95
+ <a href="https://twitter.com">🐦</a>
96
+ <a href="https://github.com">🐙</a>
97
+ <a href="https://linkedin.com">💼</a>
98
+ <a href="https://discord.com">💬</a>
99
+ </div> -->
100
+ </grantcodes-footer>
101
+ `,
102
+ };
103
+
104
+ /**
105
+ * Simple footer with minimal content
106
+ */
107
+ export const Minimal = {
108
+ render: () => html`
109
+ <grantcodes-footer columns="1">
110
+ <grantcodes-footer-column>
111
+ <p style="text-align: center;">&copy; 2024 MyCompany. All rights reserved.</p>
112
+ <ul style="justify-content: center;">
113
+ <li><a href="/privacy">Privacy Policy</a></li>
114
+ <li><a href="/terms">Terms of Service</a></li>
115
+ <li><a href="/contact">Contact</a></li>
116
+ </ul>
117
+ </grantcodes-footer-column>
118
+ </grantcodes-footer>
119
+ `,
120
+ };
121
+
122
+ /**
123
+ * Footer with rich content including description
124
+ */
125
+ export const WithDescription = {
126
+ render: () => html`
127
+ <grantcodes-footer>
128
+ <grantcodes-footer-column>
129
+ <h3>MyCompany</h3>
130
+ <p>Building amazing products that help teams work better together.</p>
131
+ </grantcodes-footer-column>
132
+
133
+ ${columns}
134
+
135
+ <div slot="bottom">
136
+ <p>&copy; 2024 MyCompany, Inc. All rights reserved.</p>
137
+ </div>
138
+ <div slot="bottom">
139
+ <a href="/privacy">Privacy</a> • <a href="/terms">Terms</a> • <a href="/cookies">Cookies</a>
140
+ </div>
141
+ </grantcodes-footer>
142
+ `,
143
+ };
@@ -0,0 +1,90 @@
1
+ import { css } from "lit";
2
+
3
+ export const footerStyles = css`
4
+ *,
5
+ *::before,
6
+ *::after {
7
+ box-sizing: border-box;
8
+ }
9
+
10
+ :host {
11
+ display: block;
12
+ container-type: inline-size;
13
+ container-name: footer;
14
+ }
15
+
16
+ .footer {
17
+ margin-block-start: auto;
18
+ }
19
+
20
+ .footer__container {
21
+ padding-block: 3rem 2rem;
22
+ padding-inline: 1rem;
23
+ max-inline-size: 1400px;
24
+ margin-inline: auto;
25
+ }
26
+
27
+ .footer__columns {
28
+ display: grid;
29
+ grid-template-columns: repeat(var(--footer-columns, 3), 1fr);
30
+ gap: 2rem;
31
+ }
32
+
33
+ /* Responsive grid using container queries */
34
+ @container footer (max-width: 768px) {
35
+ .footer__columns {
36
+ grid-template-columns: repeat(2, 1fr);
37
+ }
38
+ }
39
+
40
+ @container footer (max-width: 480px) {
41
+ .footer__columns {
42
+ grid-template-columns: 1fr;
43
+ }
44
+ }
45
+
46
+ /* Bottom section (copyright, social links, etc.) */
47
+ .footer__bottom {
48
+ margin-block-start: 2rem;
49
+ padding-block-start: 2rem;
50
+ border-block-start: 1px solid var(--g-theme-color-border-default);
51
+ display: flex;
52
+ flex-wrap: wrap;
53
+ justify-content: space-between;
54
+ align-items: center;
55
+ gap: 1rem;
56
+ }
57
+
58
+ .footer__bottom:has(slot[name="bottom"]:empty) {
59
+ display: none;
60
+ }
61
+
62
+ @container footer (max-width: 640px) {
63
+ .footer__bottom {
64
+ flex-direction: column;
65
+ text-align: center;
66
+ }
67
+ }
68
+
69
+ /* Slotted content styling */
70
+ ::slotted(*) {
71
+ font-size: var(--g-typography-font-size-14);
72
+ color: var(--g-theme-color-content-secondary);
73
+ }
74
+
75
+ ::slotted(p) {
76
+ margin: 0;
77
+ line-height: 1.5;
78
+ }
79
+
80
+ ::slotted(a) {
81
+ color: var(--g-theme-color-content-secondary);
82
+ text-decoration: none;
83
+ transition: color 0.2s ease;
84
+ }
85
+
86
+ ::slotted(a:hover) {
87
+ color: var(--g-theme-color-content-default);
88
+ text-decoration: underline;
89
+ }
90
+ `;
@@ -0,0 +1,107 @@
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 "./footer.js";
5
+
6
+ describe("Footer 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-footer");
15
+ assert.ok(element, "Element should be created");
16
+ assert.ok(element.shadowRoot, "Element should have shadow root");
17
+ });
18
+
19
+ it("should have 3 columns by default", async () => {
20
+ element = await fixture("grantcodes-footer");
21
+ assert.strictEqual(element.columns, 3, "Should have 3 columns by default");
22
+ });
23
+
24
+ it("should render footer element", async () => {
25
+ element = await fixture("grantcodes-footer");
26
+ const footer = element.shadowRoot.querySelector("footer");
27
+ assert.ok(footer, "Footer element should exist");
28
+ });
29
+
30
+ it("should render footer columns container", async () => {
31
+ element = await fixture("grantcodes-footer");
32
+ const columns = element.shadowRoot.querySelector(".footer__columns");
33
+ assert.ok(columns, "Columns container should exist");
34
+ });
35
+
36
+ it("should apply columns CSS variable", async () => {
37
+ element = await fixture("grantcodes-footer", {
38
+ columns: 4,
39
+ });
40
+
41
+ const columns = element.shadowRoot.querySelector(".footer__columns");
42
+ const style = columns.getAttribute("style");
43
+ assert.ok(style.includes("--footer-columns: 4"), "Should set columns CSS variable");
44
+ });
45
+
46
+ it("should have default content slot", async () => {
47
+ element = await fixture("grantcodes-footer");
48
+ const slot = element.shadowRoot.querySelector("slot:not([name])");
49
+ assert.ok(slot, "Default slot should exist");
50
+ });
51
+
52
+ it("should render bottom section when content is provided", async () => {
53
+ element = await fixture("grantcodes-footer");
54
+
55
+ // Add content to bottom slot
56
+ const bottomContent = document.createElement("div");
57
+ bottomContent.setAttribute("slot", "bottom");
58
+ bottomContent.textContent = "Bottom content";
59
+ element.appendChild(bottomContent);
60
+
61
+ await element.updateComplete;
62
+ // Force a re-render to check the slot
63
+ element.requestUpdate();
64
+ await element.updateComplete;
65
+
66
+ const bottom = element.shadowRoot.querySelector(".footer__bottom");
67
+ assert.ok(bottom, "Bottom section should exist when content is slotted");
68
+ });
69
+
70
+ it("should support different column counts", async () => {
71
+ const columnCounts = [1, 2, 3, 4, 5, 6];
72
+
73
+ for (const count of columnCounts) {
74
+ element = await fixture("grantcodes-footer", { columns: count });
75
+ assert.strictEqual(element.columns, count, `Should support ${count} columns`);
76
+ cleanup(element);
77
+ }
78
+ });
79
+
80
+ it("should have footer container", async () => {
81
+ element = await fixture("grantcodes-footer");
82
+ const container = element.shadowRoot.querySelector(".footer__container");
83
+ assert.ok(container, "Footer container should exist");
84
+ });
85
+
86
+ it("should have footer class", async () => {
87
+ element = await fixture("grantcodes-footer");
88
+ const footer = element.shadowRoot.querySelector(".footer");
89
+ assert.ok(footer, "Should have footer class");
90
+ });
91
+
92
+ it("should render slotted content", async () => {
93
+ element = await fixture("grantcodes-footer");
94
+ const content = document.createElement("div");
95
+ content.textContent = "Footer content";
96
+ element.appendChild(content);
97
+
98
+ await element.updateComplete;
99
+
100
+ assert.ok(
101
+ element.querySelector("div"),
102
+ "Slotted content should be present",
103
+ );
104
+ });
105
+ });
106
+
107
+
@@ -0,0 +1,2 @@
1
+ export { GrantCodesFooter } from "./footer.component.js";
2
+ export { GrantCodesFooterColumn } from "./footer-column.component.js";
@@ -0,0 +1,173 @@
1
+ import { LitElement } from "lit";
2
+ import { html } from "lit/static-html.js";
3
+ import { formFieldStyles } from "./form-field.styles.js";
4
+ import { cx } from "../../lib/classnames.js";
5
+ import { generateId } from "../../lib/generate-id.js";
6
+
7
+ export class GrantCodesFormField extends LitElement {
8
+ static formAssociated = true;
9
+ static styles = [formFieldStyles];
10
+
11
+ static properties = {
12
+ label: { type: String },
13
+ error: { type: String },
14
+ help: { type: String },
15
+ };
16
+
17
+ constructor() {
18
+ super();
19
+
20
+ this.label = "";
21
+ this.error = undefined;
22
+ this.help = undefined;
23
+
24
+ this.inlineInput = false;
25
+ this.groupInput = false;
26
+
27
+ /** @type {NodeListOf<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>} */
28
+ this.inputElements;
29
+
30
+ /** @type {NodeListOf<GrantCodesFormField>} */
31
+ this.nestedFields;
32
+
33
+ if (!this.id) {
34
+ this.id = generateId("form-field");
35
+ }
36
+ }
37
+
38
+ get errorId() {
39
+ return `${this.id}-error`;
40
+ }
41
+
42
+ get helpId() {
43
+ return `${this.id}-help`;
44
+ }
45
+
46
+ get ariaDescribedBy() {
47
+ const ids = [];
48
+ if (this.error) {
49
+ ids.push(this.errorId);
50
+ }
51
+ if (this.help) {
52
+ ids.push(this.helpId);
53
+ }
54
+ return ids.join(" ");
55
+ }
56
+
57
+ firstUpdated() {
58
+ // Initialize inputs and nested fields if not already set
59
+ if (!this.inputElements) {
60
+ this.inputElements = this.querySelectorAll(
61
+ "input, select, textarea",
62
+ );
63
+ }
64
+ if (!this.nestedFields) {
65
+ this.nestedFields = this.querySelectorAll("grantcodes-form-field");
66
+ }
67
+
68
+ const input = this.inputElements[0];
69
+
70
+ if (this.nestedFields.length > 0) {
71
+ this.groupInput = true;
72
+ this.requestUpdate();
73
+ }
74
+
75
+ if (!input) {
76
+ return;
77
+ }
78
+
79
+ input.id = this.id;
80
+ input.setAttribute("aria-describedby", this.ariaDescribedBy);
81
+
82
+ if (input.type === "checkbox" || input.type === "radio") {
83
+ this.inlineInput = true;
84
+ this.requestUpdate();
85
+ }
86
+ }
87
+
88
+ handleLabelClick() {
89
+ const input = this.inputElements[0];
90
+ if (input) {
91
+ input.focus();
92
+ if (
93
+ input instanceof HTMLInputElement &&
94
+ (input.type === "checkbox" || input.type === "radio")
95
+ ) {
96
+ input.checked = !input.checked;
97
+ }
98
+ }
99
+ }
100
+
101
+ // handleError() {
102
+ // if (this.error) {
103
+ // const input = this.inputElements[0]
104
+ // if (input) {
105
+ // if (this.help) {
106
+ // input.setAttribute('aria-describedby', `${this.errorId} ${this.helpId}`)
107
+ // } else {
108
+ // input.setAttribute('aria-describedby', this.errorId)
109
+ // }
110
+ // }
111
+ // }
112
+ // }
113
+
114
+ errorTemplate() {
115
+ if (!this.error) {
116
+ return html``;
117
+ }
118
+
119
+ return html`
120
+ <p class="form-field__error" id=${this.errorId}>Error: ${this.error}</p>
121
+ `;
122
+ }
123
+
124
+ helpTemplate() {
125
+ if (!this.help) {
126
+ return html``;
127
+ }
128
+
129
+ return html`
130
+ <span class="form-field__help" id=${this.helpId}>${this.help}</span>
131
+ `;
132
+ }
133
+
134
+ /**
135
+ * Template for a group of form components
136
+ *
137
+ * @param {string} className html class attribute
138
+ *
139
+ * @return {} [return description]
140
+ */
141
+ groupTemplate(className) {
142
+ return html`
143
+ <fieldset class="${className}">
144
+ <legend class="form-field__label">${this.label}</legend>
145
+ <slot></slot>
146
+ ${this.errorTemplate()}
147
+ </fieldset>
148
+ `;
149
+ }
150
+
151
+ render() {
152
+ const elementClass = cx("form-field", {
153
+ "form-field--inline": this.inlineInput,
154
+ });
155
+
156
+ if (this.groupInput) {
157
+ return this.groupTemplate(elementClass);
158
+ }
159
+
160
+ return html`
161
+ <div class="${elementClass}">
162
+ <label>
163
+ <span class="form-field__label" @click=${this.handleLabelClick}
164
+ >${this.label}</span
165
+ >
166
+ ${this.helpTemplate()}
167
+ <slot></slot>
168
+ </label>
169
+ ${this.errorTemplate()}
170
+ </div>
171
+ `;
172
+ }
173
+ }
@@ -0,0 +1,7 @@
1
+ import { GrantCodesFormField } from "./form-field.component.js";
2
+
3
+ export * from "./form-field.component.js";
4
+ export default GrantCodesFormField;
5
+
6
+ customElements.define("grantcodes-form-field", GrantCodesFormField);
7
+
@@ -0,0 +1,104 @@
1
+ import { html } from "lit/static-html.js";
2
+ import { getStorybookHelpers } from "@wc-toolkit/storybook-helpers";
3
+ const { events, args, argTypes, template } = getStorybookHelpers(
4
+ "grantcodes-form-field",
5
+ );
6
+ import "./form-field.js";
7
+
8
+ const meta = {
9
+ title: "Components/FormField",
10
+ component: "grantcodes-form-field",
11
+ args: {
12
+ ...args,
13
+ label: "Label",
14
+ error: "",
15
+ help: "",
16
+ slot: html`<input type="text" placeholder="This is a placeholder" />`,
17
+ },
18
+ argTypes,
19
+ decorators: [(story) => html`<form>${story()}</form>`],
20
+ render: (args) => template(args, args.slot),
21
+ parameters: {
22
+ actions: {
23
+ handles: events,
24
+ },
25
+ },
26
+ };
27
+
28
+ export default meta;
29
+
30
+ export const FormField = {};
31
+
32
+ export const FormFieldWithError = {
33
+ args: {
34
+ error: "This is an error",
35
+ },
36
+ };
37
+
38
+ export const FormFieldWithHelp = {
39
+ args: {
40
+ help: "This is help text",
41
+ },
42
+ };
43
+
44
+ export const FormFieldWithSelect = {
45
+ args: {
46
+ slot: html`
47
+ <select>
48
+ <option value="1">Option 1</option>
49
+ <option value="2">Option 2</option>
50
+ <option value="3">Option 3</option>
51
+ </select>
52
+ `,
53
+ },
54
+ };
55
+
56
+ export const FormFieldWithTextArea = {
57
+ args: {
58
+ slot: html`<textarea placeholder="This is a placeholder"></textarea>`,
59
+ },
60
+ };
61
+
62
+ export const FormFieldWithCheckbox = {
63
+ args: {
64
+ slot: html`<input type="checkbox" value="1" name="name" />`,
65
+ },
66
+ };
67
+
68
+ export const FormFieldWithRadio = {
69
+ args: {
70
+ slot: html`<input type="radio" />`,
71
+ },
72
+ };
73
+
74
+ export const FormFieldWithRadioGroup = {
75
+ args: {
76
+ slot: html`
77
+ <grantcodes-form-field label="Radio number 1">
78
+ <input type="radio" name="radio-group" value="1" />
79
+ </grantcodes-form-field>
80
+ <grantcodes-form-field label="Radio number 2">
81
+ <input type="radio" name="radio-group" value="2" />
82
+ </grantcodes-form-field>
83
+ <grantcodes-form-field label="Radio number 3">
84
+ <input type="radio" name="radio-group" value="3" />
85
+ </grantcodes-form-field>
86
+ `,
87
+ },
88
+ };
89
+
90
+ export const FormFieldWithCheckboxGroup = {
91
+ args: {
92
+ slot: html`
93
+ <grantcodes-form-field label="Checkbox number 1"
94
+ ><input type="checkbox"
95
+ /></grantcodes-form-field>
96
+ <grantcodes-form-field label="Checkbox number 2"
97
+ ><input type="checkbox"
98
+ /></grantcodes-form-field>
99
+ <grantcodes-form-field label="Checkbox number 3"
100
+ ><input type="checkbox"
101
+ /></grantcodes-form-field>
102
+ `,
103
+ },
104
+ };
@@ -0,0 +1,47 @@
1
+ import { css } from "lit";
2
+
3
+ export const formFieldStyles = css`
4
+
5
+ .form-field {
6
+ display: flex;
7
+ flex-direction: column;
8
+ gap: 0.25rem;
9
+ padding: 0;
10
+ margin: 0;
11
+ border: none;
12
+ }
13
+
14
+ .form-field label,
15
+ .form-field slot {
16
+ display: flex;
17
+ flex-direction: column;
18
+ gap: inherit;
19
+ }
20
+
21
+ .form-field--inline label {
22
+ flex-direction: row-reverse;
23
+ align-items: center;
24
+ justify-content: flex-end;
25
+ }
26
+
27
+ .form-field__label {
28
+ margin: 0;
29
+ padding: 0;
30
+
31
+ &:where(legend) {
32
+ margin-bottom: 0.25rem;
33
+ }
34
+ }
35
+
36
+ .form-field__help {
37
+ font-size: var(--g-typography-font-size-14);
38
+ opacity: 0.7;
39
+ }
40
+
41
+ .form-field__error {
42
+ margin: 0;
43
+ font-size: var(--g-typography-font-size-14);
44
+ color: var(--g-color-utility-red-700);
45
+ }
46
+
47
+ `;