@grantcodes/ui 2.15.7 → 2.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (300) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/custom-elements.json +476 -346
  3. package/package.json +23 -23
  4. package/src/components/accordion/accordion-item.component.js +17 -19
  5. package/src/components/accordion/accordion-item.css +40 -44
  6. package/src/components/accordion/accordion.component.js +7 -7
  7. package/src/components/accordion/accordion.css +5 -5
  8. package/src/components/accordion/accordion.js +6 -6
  9. package/src/components/accordion/accordion.react.js +6 -6
  10. package/src/components/accordion/accordion.stories.js +10 -10
  11. package/src/components/accordion/accordion.test.js +100 -100
  12. package/src/components/accordion/index.js +6 -6
  13. package/src/components/app-bar/app-bar.component.js +86 -92
  14. package/src/components/app-bar/app-bar.css +187 -203
  15. package/src/components/app-bar/app-bar.js +3 -3
  16. package/src/components/app-bar/app-bar.react.js +9 -9
  17. package/src/components/app-bar/app-bar.stories.js +20 -20
  18. package/src/components/app-bar/app-bar.test.js +190 -237
  19. package/src/components/app-bar/index.js +1 -1
  20. package/src/components/app-bar/nav-link.component.js +7 -9
  21. package/src/components/app-bar/nav-link.css +23 -28
  22. package/src/components/app-bar/nav-link.js +3 -3
  23. package/src/components/app-bar/nav-link.react.js +6 -6
  24. package/src/components/avatar/avatar.component.js +79 -68
  25. package/src/components/avatar/avatar.css +18 -18
  26. package/src/components/avatar/avatar.js +3 -3
  27. package/src/components/avatar/avatar.react.js +6 -6
  28. package/src/components/avatar/avatar.stories.js +24 -23
  29. package/src/components/avatar/avatar.test.js +128 -84
  30. package/src/components/avatar/index.js +1 -1
  31. package/src/components/badge/badge.component.js +23 -23
  32. package/src/components/badge/badge.css +39 -52
  33. package/src/components/badge/badge.js +3 -3
  34. package/src/components/badge/badge.react.js +6 -6
  35. package/src/components/badge/badge.stories.js +23 -23
  36. package/src/components/badge/badge.test.js +53 -68
  37. package/src/components/badge/index.js +1 -1
  38. package/src/components/breadcrumb/breadcrumb.component.js +95 -96
  39. package/src/components/breadcrumb/breadcrumb.css +42 -44
  40. package/src/components/breadcrumb/breadcrumb.js +6 -9
  41. package/src/components/breadcrumb/breadcrumb.react.js +9 -12
  42. package/src/components/breadcrumb/breadcrumb.stories.js +5 -5
  43. package/src/components/breadcrumb/breadcrumb.test.js +139 -151
  44. package/src/components/breadcrumb/index.js +1 -1
  45. package/src/components/button/button.component.js +101 -103
  46. package/src/components/button/button.css +90 -94
  47. package/src/components/button/button.js +3 -3
  48. package/src/components/button/button.react.js +6 -6
  49. package/src/components/button/button.stories.js +25 -25
  50. package/src/components/button/button.test.js +324 -344
  51. package/src/components/button/index.js +1 -1
  52. package/src/components/button-group/button-group.component.js +18 -18
  53. package/src/components/button-group/button-group.css +16 -16
  54. package/src/components/button-group/button-group.js +3 -3
  55. package/src/components/button-group/button-group.react.js +6 -6
  56. package/src/components/button-group/button-group.stories.js +23 -23
  57. package/src/components/button-group/button-group.test.js +54 -54
  58. package/src/components/button-group/index.js +1 -1
  59. package/src/components/card/card.component.js +31 -33
  60. package/src/components/card/card.css +85 -85
  61. package/src/components/card/card.js +4 -4
  62. package/src/components/card/card.react.js +6 -6
  63. package/src/components/card/card.stories.js +27 -26
  64. package/src/components/card/card.test.js +56 -56
  65. package/src/components/card/index.js +1 -1
  66. package/src/components/code-preview/code-preview.component.js +47 -48
  67. package/src/components/code-preview/code-preview.css +11 -11
  68. package/src/components/code-preview/code-preview.js +3 -3
  69. package/src/components/code-preview/code-preview.react.js +6 -6
  70. package/src/components/code-preview/code-preview.stories.js +29 -29
  71. package/src/components/code-preview/code-preview.test.js +112 -135
  72. package/src/components/code-preview/index.js +1 -1
  73. package/src/components/container/container.component.js +27 -27
  74. package/src/components/container/container.css +22 -22
  75. package/src/components/container/container.js +3 -3
  76. package/src/components/container/container.react.js +6 -6
  77. package/src/components/container/container.stories.js +44 -44
  78. package/src/components/container/container.test.js +54 -66
  79. package/src/components/container/index.js +1 -1
  80. package/src/components/countdown/countdown.component.js +160 -162
  81. package/src/components/countdown/countdown.css +50 -34
  82. package/src/components/countdown/countdown.js +3 -3
  83. package/src/components/countdown/countdown.react.js +6 -6
  84. package/src/components/countdown/countdown.stories.js +34 -34
  85. package/src/components/countdown/index.js +1 -1
  86. package/src/components/cta/cta.component.js +77 -81
  87. package/src/components/cta/cta.css +24 -24
  88. package/src/components/cta/cta.js +3 -3
  89. package/src/components/cta/cta.react.js +6 -6
  90. package/src/components/cta/cta.stories.js +32 -32
  91. package/src/components/cta/index.js +1 -1
  92. package/src/components/dialog/dialog.component.js +95 -58
  93. package/src/components/dialog/dialog.css +45 -50
  94. package/src/components/dialog/dialog.js +3 -3
  95. package/src/components/dialog/dialog.react.js +6 -6
  96. package/src/components/dialog/dialog.stories.js +38 -29
  97. package/src/components/dialog/dialog.test.js +188 -102
  98. package/src/components/dialog/index.js +1 -1
  99. package/src/components/dropdown/dropdown.component.js +220 -201
  100. package/src/components/dropdown/dropdown.css +60 -84
  101. package/src/components/dropdown/dropdown.js +6 -9
  102. package/src/components/dropdown/dropdown.react.js +15 -15
  103. package/src/components/dropdown/dropdown.stories.js +10 -10
  104. package/src/components/dropdown/dropdown.test.js +211 -157
  105. package/src/components/dropdown/index.js +1 -1
  106. package/src/components/dropzone/dropzone.component.js +126 -129
  107. package/src/components/dropzone/dropzone.css +35 -37
  108. package/src/components/dropzone/dropzone.js +3 -3
  109. package/src/components/dropzone/dropzone.react.js +6 -6
  110. package/src/components/dropzone/dropzone.stories.js +28 -28
  111. package/src/components/dropzone/dropzone.test.js +91 -99
  112. package/src/components/dropzone/index.js +1 -1
  113. package/src/components/feature-list/feature-list.component.js +78 -86
  114. package/src/components/feature-list/feature-list.css +37 -37
  115. package/src/components/feature-list/feature-list.js +3 -3
  116. package/src/components/feature-list/feature-list.react.js +6 -6
  117. package/src/components/feature-list/feature-list.stories.js +86 -90
  118. package/src/components/feature-list/index.js +1 -1
  119. package/src/components/footer/footer-column.component.js +7 -7
  120. package/src/components/footer/footer-column.css +22 -22
  121. package/src/components/footer/footer.component.js +19 -19
  122. package/src/components/footer/footer.css +38 -38
  123. package/src/components/footer/footer.js +8 -8
  124. package/src/components/footer/footer.react.js +9 -9
  125. package/src/components/footer/footer.stories.js +9 -9
  126. package/src/components/footer/footer.test.js +101 -111
  127. package/src/components/footer/index.js +2 -2
  128. package/src/components/form-field/form-field.component.js +167 -125
  129. package/src/components/form-field/form-field.css +24 -24
  130. package/src/components/form-field/form-field.js +3 -3
  131. package/src/components/form-field/form-field.react.js +6 -6
  132. package/src/components/form-field/form-field.stories.js +53 -44
  133. package/src/components/form-field/form-field.test.js +280 -134
  134. package/src/components/form-field/index.js +1 -1
  135. package/src/components/gallery/gallery-image.component.js +30 -30
  136. package/src/components/gallery/gallery-image.js +3 -3
  137. package/src/components/gallery/gallery-image.react.js +6 -6
  138. package/src/components/gallery/gallery.component.js +24 -17
  139. package/src/components/gallery/gallery.css +79 -72
  140. package/src/components/gallery/gallery.js +3 -3
  141. package/src/components/gallery/gallery.react.js +6 -6
  142. package/src/components/gallery/gallery.stories.js +69 -68
  143. package/src/components/gallery/gallery.test.js +95 -88
  144. package/src/components/gallery/index.js +2 -2
  145. package/src/components/hero/hero.component.js +53 -57
  146. package/src/components/hero/hero.css +27 -27
  147. package/src/components/hero/hero.js +3 -3
  148. package/src/components/hero/hero.react.js +6 -6
  149. package/src/components/hero/hero.stories.js +39 -39
  150. package/src/components/hero/index.js +1 -1
  151. package/src/components/icon/icon.component.js +10 -10
  152. package/src/components/icon/icon.css +13 -13
  153. package/src/components/icon/icon.js +4 -4
  154. package/src/components/icon/icon.react.js +6 -6
  155. package/src/components/icon/icon.stories.js +8 -9
  156. package/src/components/icon/icon.test.js +54 -55
  157. package/src/components/icon/index.js +1 -1
  158. package/src/components/loading/index.js +1 -1
  159. package/src/components/loading/loading.component.js +41 -14
  160. package/src/components/loading/loading.css +47 -30
  161. package/src/components/loading/loading.js +4 -4
  162. package/src/components/loading/loading.react.js +6 -6
  163. package/src/components/loading/loading.stories.js +19 -18
  164. package/src/components/loading/loading.test.js +77 -54
  165. package/src/components/logo-cloud/index.js +1 -1
  166. package/src/components/logo-cloud/logo-cloud.component.js +39 -43
  167. package/src/components/logo-cloud/logo-cloud.css +31 -31
  168. package/src/components/logo-cloud/logo-cloud.js +3 -3
  169. package/src/components/logo-cloud/logo-cloud.react.js +6 -6
  170. package/src/components/logo-cloud/logo-cloud.stories.js +80 -80
  171. package/src/components/map/index.js +1 -1
  172. package/src/components/map/map.component.js +103 -107
  173. package/src/components/map/map.css +22 -22
  174. package/src/components/map/map.js +3 -3
  175. package/src/components/map/map.react.js +6 -6
  176. package/src/components/map/map.stories.js +38 -38
  177. package/src/components/media-text/index.js +1 -1
  178. package/src/components/media-text/media-text.component.js +76 -70
  179. package/src/components/media-text/media-text.css +32 -32
  180. package/src/components/media-text/media-text.js +3 -3
  181. package/src/components/media-text/media-text.react.js +6 -6
  182. package/src/components/media-text/media-text.stories.js +45 -45
  183. package/src/components/media-text/media-text.test.js +72 -0
  184. package/src/components/newsletter/index.js +1 -1
  185. package/src/components/newsletter/newsletter.component.js +56 -64
  186. package/src/components/newsletter/newsletter.css +48 -48
  187. package/src/components/newsletter/newsletter.js +3 -3
  188. package/src/components/newsletter/newsletter.react.js +6 -6
  189. package/src/components/newsletter/newsletter.stories.js +35 -35
  190. package/src/components/notice/index.js +1 -1
  191. package/src/components/notice/notice.component.js +66 -55
  192. package/src/components/notice/notice.css +37 -38
  193. package/src/components/notice/notice.js +3 -3
  194. package/src/components/notice/notice.react.js +6 -6
  195. package/src/components/notice/notice.stories.js +54 -53
  196. package/src/components/notice/notice.test.js +210 -156
  197. package/src/components/pagination/index.js +1 -1
  198. package/src/components/pagination/pagination.component.js +44 -44
  199. package/src/components/pagination/pagination.css +6 -6
  200. package/src/components/pagination/pagination.js +4 -4
  201. package/src/components/pagination/pagination.react.js +6 -6
  202. package/src/components/pagination/pagination.stories.js +20 -18
  203. package/src/components/pagination/pagination.test.js +95 -102
  204. package/src/components/person/index.js +1 -1
  205. package/src/components/person/person.component.js +29 -29
  206. package/src/components/person/person.css +17 -17
  207. package/src/components/person/person.js +4 -4
  208. package/src/components/person/person.react.js +6 -6
  209. package/src/components/person/person.stories.js +34 -33
  210. package/src/components/person/person.test.js +55 -68
  211. package/src/components/pricing/index.js +1 -1
  212. package/src/components/pricing/pricing.component.js +58 -70
  213. package/src/components/pricing/pricing.css +65 -65
  214. package/src/components/pricing/pricing.js +3 -3
  215. package/src/components/pricing/pricing.react.js +6 -6
  216. package/src/components/pricing/pricing.stories.js +96 -96
  217. package/src/components/sidebar/index.js +1 -1
  218. package/src/components/sidebar/sidebar.component.js +157 -130
  219. package/src/components/sidebar/sidebar.css +118 -115
  220. package/src/components/sidebar/sidebar.js +4 -4
  221. package/src/components/sidebar/sidebar.react.js +10 -10
  222. package/src/components/sidebar/sidebar.stories.js +10 -10
  223. package/src/components/sidebar/sidebar.test.js +250 -230
  224. package/src/components/stats/index.js +1 -1
  225. package/src/components/stats/stats.component.js +42 -50
  226. package/src/components/stats/stats.css +29 -29
  227. package/src/components/stats/stats.js +3 -3
  228. package/src/components/stats/stats.react.js +6 -6
  229. package/src/components/stats/stats.stories.js +40 -40
  230. package/src/components/tabs/index.js +2 -2
  231. package/src/components/tabs/internal/tabs-button.component.js +16 -27
  232. package/src/components/tabs/internal/tabs-button.js +3 -3
  233. package/src/components/tabs/internal/tabs-button.react.js +6 -6
  234. package/src/components/tabs/internal/tabs-item.component.js +30 -30
  235. package/src/components/tabs/tab.component.js +9 -11
  236. package/src/components/tabs/tab.js +3 -3
  237. package/src/components/tabs/tab.react.js +6 -6
  238. package/src/components/tabs/tabs.component.js +107 -109
  239. package/src/components/tabs/tabs.css +50 -59
  240. package/src/components/tabs/tabs.js +4 -4
  241. package/src/components/tabs/tabs.react.js +6 -6
  242. package/src/components/tabs/tabs.stories.js +23 -22
  243. package/src/components/tabs/tabs.test.js +201 -147
  244. package/src/components/testimonials/index.js +1 -1
  245. package/src/components/testimonials/testimonials.component.js +47 -51
  246. package/src/components/testimonials/testimonials.css +31 -31
  247. package/src/components/testimonials/testimonials.js +3 -3
  248. package/src/components/testimonials/testimonials.react.js +6 -6
  249. package/src/components/testimonials/testimonials.stories.js +49 -51
  250. package/src/components/toast/index.js +1 -1
  251. package/src/components/toast/toast.component.js +217 -148
  252. package/src/components/toast/toast.css +101 -99
  253. package/src/components/toast/toast.js +6 -9
  254. package/src/components/toast/toast.react.js +12 -12
  255. package/src/components/toast/toast.stories.js +75 -49
  256. package/src/components/toast/toast.test.js +260 -221
  257. package/src/components/tooltip/index.js +1 -1
  258. package/src/components/tooltip/tooltip.component.js +56 -49
  259. package/src/components/tooltip/tooltip.css +61 -50
  260. package/src/components/tooltip/tooltip.js +4 -4
  261. package/src/components/tooltip/tooltip.react.js +6 -6
  262. package/src/components/tooltip/tooltip.stories.js +24 -23
  263. package/src/components/tooltip/tooltip.test.js +143 -155
  264. package/src/constructor.test.js +31 -0
  265. package/src/css/base.css +1 -0
  266. package/src/css/colors.stories.js +120 -122
  267. package/src/css/elements/a.css +28 -28
  268. package/src/css/elements/forms/button.css +1 -1
  269. package/src/css/elements/forms/input.css +80 -80
  270. package/src/css/elements/forms/label.css +3 -3
  271. package/src/css/elements/media/image.css +2 -2
  272. package/src/css/elements.stories.js +48 -49
  273. package/src/css/helpers.css +8 -8
  274. package/src/css/layers.stories.js +5 -5
  275. package/src/css/reset.css +102 -102
  276. package/src/css/themes/todomap.css +1 -1
  277. package/src/css/tokens.stories.js +50 -53
  278. package/src/css/typography.css +30 -32
  279. package/src/css/typography.stories.js +92 -92
  280. package/src/css/util/focus-ring.css +12 -20
  281. package/src/css/view-transitions.css +16 -0
  282. package/src/exports.test.js +89 -105
  283. package/src/icons.js +1 -1
  284. package/src/lib/generate-id.js +1 -1
  285. package/src/lib/slot-presence-controller.js +48 -0
  286. package/src/lib/styles/all.css +38 -37
  287. package/src/lib/styles/focus-ring.css +12 -20
  288. package/src/lib/view-transition.js +42 -0
  289. package/src/lib/view-transition.test.js +105 -0
  290. package/src/main.js +29 -29
  291. package/src/pages/agency.stories.js +59 -59
  292. package/src/pages/blog-post.stories.js +48 -48
  293. package/src/pages/saas-landing.stories.js +180 -180
  294. package/src/react.js +44 -44
  295. package/src/react.test.js +42 -51
  296. package/src/test-utils/assert-helpers.js +39 -58
  297. package/src/test-utils/events.js +32 -34
  298. package/src/test-utils/fixture.js +31 -31
  299. package/src/test-utils/index.js +3 -3
  300. package/src/types.d.ts +3 -3
@@ -1,78 +1,89 @@
1
- import { LitElement } from "lit";
2
- import { html } from "lit/static-html.js";
3
- import avatarStyles from "./avatar.css" with { type: "css" };
1
+ import { LitElement } from 'lit';
2
+ import { html } from 'lit/static-html.js';
3
+ import avatarStyles from './avatar.css' with { type: 'css' };
4
+
5
+ // Mirrors the 3rem square in avatar.css (--g-spacing-2xl); the image fills that box.
6
+ const AVATAR_SIZE = 48;
4
7
 
5
8
  export class GrantCodesAvatar extends LitElement {
6
- static properties = {
7
- src: { type: String },
8
- name: { type: String },
9
- alt: { type: String },
10
- initials: { type: String },
11
- size: { type: String },
12
- };
9
+ static properties = {
10
+ src: { type: String },
11
+ name: { type: String },
12
+ alt: { type: String },
13
+ initials: { type: String },
14
+ size: { type: String },
15
+ };
13
16
 
14
- static styles = [avatarStyles];
17
+ static styles = [avatarStyles];
15
18
 
16
- constructor() {
17
- super();
19
+ constructor() {
20
+ super();
18
21
 
19
- /**
20
- * Image source for the avatar
21
- */
22
- this.src = "";
23
- /**
24
- * The full name of the person
25
- */
26
- this.name = "";
27
- /**
28
- * Alt attribute text
29
- */
30
- this.alt = "";
31
- /**
32
- * Optional initials, used when there is no src set
33
- */
34
- this.initials = "";
35
- /**
36
- * Size variant: 'small', 'medium', or 'large'
37
- */
38
- this.size = "medium";
39
- }
22
+ /**
23
+ * Image source for the avatar
24
+ */
25
+ this.src = '';
26
+ /**
27
+ * The full name of the person
28
+ */
29
+ this.name = '';
30
+ /**
31
+ * Alt attribute text
32
+ */
33
+ this.alt = '';
34
+ /**
35
+ * Optional initials, used when there is no src set
36
+ */
37
+ this.initials = '';
38
+ /**
39
+ * Size variant: 'small', 'medium', or 'large'
40
+ */
41
+ this.size = 'medium';
42
+ }
40
43
 
41
- /**
42
- * Helper function to get initials from the name (if not already provided)
43
- *
44
- * @return {String} First 2 initials of the passed name
45
- */
46
- getInitials() {
47
- if (this.initials) return this.initials;
48
- if (this.name) {
49
- const [first = "", second = ""] = this.name.trim().split(" ");
50
- const firstInitial = first ? first[0] : "";
51
- const secondInitial = second ? second[0] : "";
52
- return `${firstInitial}${secondInitial}`;
53
- }
54
- return "";
55
- }
44
+ /**
45
+ * Helper function to get initials from the name (if not already provided)
46
+ *
47
+ * @return {String} First 2 initials of the passed name
48
+ */
49
+ getInitials() {
50
+ if (this.initials) return this.initials;
51
+ if (this.name) {
52
+ const [first = '', second = ''] = this.name.trim().split(' ');
53
+ const firstInitial = first ? first[0] : '';
54
+ const secondInitial = second ? second[0] : '';
55
+ return `${firstInitial}${secondInitial}`;
56
+ }
57
+ return '';
58
+ }
56
59
 
57
- getAlt() {
58
- if (this.alt) return this.alt;
59
- return `${this.name || this.initials} avatar`;
60
- }
60
+ getAlt() {
61
+ if (this.alt) return this.alt;
62
+ return this.name || this.initials || '';
63
+ }
61
64
 
62
- /** @returns {import('lit').TemplateResult} */
63
- getImg() {
64
- if (this.src) {
65
- return html`<img src=${this.src} alt=${this.getAlt()} />`;
66
- }
67
- return html``;
68
- }
65
+ /** @returns {import('lit').TemplateResult} */
66
+ getImg() {
67
+ if (this.src) {
68
+ return html`<img
69
+ src=${this.src}
70
+ alt=${this.getAlt()}
71
+ class="avatar__image"
72
+ width=${AVATAR_SIZE}
73
+ height=${AVATAR_SIZE}
74
+ loading="lazy"
75
+ decoding="async"
76
+ />`;
77
+ }
78
+ return html``;
79
+ }
69
80
 
70
- /** @returns {import('lit').TemplateResult} */
71
- render() {
72
- const content = this.src
73
- ? this.getImg()
74
- : html`<span class="avatar__initials">${this.getInitials()}</span>`;
75
- const sizeClass = this.size ? `avatar--${this.size}` : "";
76
- return html` <div class="avatar ${sizeClass}">${content}</div> `;
77
- }
81
+ /** @returns {import('lit').TemplateResult} */
82
+ render() {
83
+ const content = this.src
84
+ ? this.getImg()
85
+ : html`<span class="avatar__initials">${this.getInitials()}</span>`;
86
+ const sizeClass = this.size ? `avatar--${this.size}` : '';
87
+ return html` <div class="avatar ${sizeClass}">${content}</div> `;
88
+ }
78
89
  }
@@ -2,34 +2,34 @@
2
2
  *,
3
3
  *::before,
4
4
  *::after {
5
- box-sizing: border-box;
5
+ box-sizing: border-box;
6
6
  }
7
7
 
8
8
  :host {
9
- display: block;
9
+ display: block;
10
10
  }
11
11
 
12
12
  .avatar {
13
- display: flex;
14
- align-items: center;
15
- justify-content: center;
16
- inline-size: var(--g-spacing-2xl);
17
- block-size: var(--g-spacing-2xl);
18
- border-radius: 50%;
19
- overflow: hidden;
20
- background-color: var(--g-color-primary-200);
21
- color: var(--g-color-primary-900);
22
- text-align: center;
23
- font: var(--g-typography-h6-font)
13
+ display: flex;
14
+ align-items: center;
15
+ justify-content: center;
16
+ inline-size: var(--g-spacing-2xl);
17
+ block-size: var(--g-spacing-2xl);
18
+ border-radius: 50%;
19
+ overflow: hidden;
20
+ background-color: var(--g-color-primary-200);
21
+ color: var(--g-color-primary-900);
22
+ text-align: center;
23
+ font: var(--g-typography-h6-font);
24
24
  }
25
25
 
26
26
  .avatar__image {
27
- inline-size: 100%;
28
- block-size: 100%;
29
- object-fit: cover;
30
- object-position: center;
27
+ inline-size: 100%;
28
+ block-size: 100%;
29
+ object-fit: cover;
30
+ object-position: center;
31
31
  }
32
32
 
33
33
  .avatar__fallback {
34
- line-height: 1;
34
+ line-height: 1;
35
35
  }
@@ -1,6 +1,6 @@
1
- import { GrantCodesAvatar } from "./avatar.component.js";
1
+ import { GrantCodesAvatar } from './avatar.component.js';
2
2
 
3
- export * from "./avatar.component.js";
3
+ export * from './avatar.component.js';
4
4
  export default GrantCodesAvatar;
5
5
 
6
- customElements.define("grantcodes-avatar", GrantCodesAvatar);
6
+ customElements.define('grantcodes-avatar', GrantCodesAvatar);
@@ -1,9 +1,9 @@
1
- import React from "react";
2
- import { createComponent } from "@lit/react";
3
- import { GrantCodesAvatar } from "./avatar.js";
1
+ import { createComponent } from '@lit/react';
2
+ import React from 'react';
3
+ import { GrantCodesAvatar } from './avatar.js';
4
4
 
5
5
  export const Avatar = createComponent({
6
- tagName: "grantcodes-avatar",
7
- elementClass: GrantCodesAvatar,
8
- react: React,
6
+ tagName: 'grantcodes-avatar',
7
+ elementClass: GrantCodesAvatar,
8
+ react: React,
9
9
  });
@@ -1,25 +1,26 @@
1
- import { getStorybookHelpers } from "@wc-toolkit/storybook-helpers";
2
- const { events, args, argTypes, template } =
3
- getStorybookHelpers("grantcodes-avatar");
4
- import "./avatar.js";
1
+ import { getStorybookHelpers } from '@wc-toolkit/storybook-helpers';
2
+
3
+ const { events, args, argTypes, template } = getStorybookHelpers('grantcodes-avatar');
4
+
5
+ import './avatar.js';
5
6
 
6
7
  const meta = {
7
- title: "Components/Avatar",
8
- component: "grantcodes-avatar",
9
- args: {
10
- ...args,
11
- src: "https://placehold.co/160x160",
12
- name: "Tommy Tobasco",
13
- alt: "Tommy Tomasco Avatar",
14
- initials: "2T",
15
- },
16
- argTypes,
17
- render: (args) => template(args),
18
- parameters: {
19
- actions: {
20
- handles: events,
21
- },
22
- },
8
+ title: 'Components/Avatar',
9
+ component: 'grantcodes-avatar',
10
+ args: {
11
+ ...args,
12
+ src: 'https://placehold.co/160x160',
13
+ name: 'Tommy Tobasco',
14
+ alt: 'Tommy Tomasco Avatar',
15
+ initials: '2T',
16
+ },
17
+ argTypes,
18
+ render: (args) => template(args),
19
+ parameters: {
20
+ actions: {
21
+ handles: events,
22
+ },
23
+ },
23
24
  };
24
25
 
25
26
  export default meta;
@@ -27,7 +28,7 @@ export default meta;
27
28
  export const Avatar = {};
28
29
 
29
30
  export const Initials = {
30
- args: {
31
- src: undefined,
32
- },
31
+ args: {
32
+ src: undefined,
33
+ },
33
34
  };
@@ -1,85 +1,129 @@
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 "./avatar.js";
5
-
6
- describe("Avatar 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-avatar");
15
- assert.ok(element, "Element should be created");
16
- assert.ok(element.shadowRoot, "Element should have shadow root");
17
- });
18
-
19
- it("should display initials when name is provided", async () => {
20
- element = await fixture("grantcodes-avatar", {
21
- name: "John Doe",
22
- });
23
-
24
- const initialsElement =
25
- element.shadowRoot.querySelector(".avatar__initials");
26
- assert.ok(initialsElement, "Initials element should exist");
27
- assert.strictEqual(
28
- initialsElement.textContent,
29
- "JD",
30
- "Should display correct initials",
31
- );
32
- });
33
-
34
- it("should handle single name", async () => {
35
- element = await fixture("grantcodes-avatar", {
36
- name: "John",
37
- });
38
-
39
- const initialsElement =
40
- element.shadowRoot.querySelector(".avatar__initials");
41
- assert.strictEqual(
42
- initialsElement.textContent,
43
- "J",
44
- "Should display single initial",
45
- );
46
- });
47
-
48
- it("should display image when src is provided", async () => {
49
- element = await fixture("grantcodes-avatar", {
50
- src: "https://example.com/avatar.jpg",
51
- alt: "User avatar",
52
- });
53
-
54
- const imgElement = element.shadowRoot.querySelector("img");
55
- assert.ok(imgElement, "Image element should exist");
56
- assert.strictEqual(imgElement.src, "https://example.com/avatar.jpg");
57
- assert.strictEqual(imgElement.alt, "User avatar");
58
- });
59
-
60
- it("should apply size variant", async () => {
61
- element = await fixture("grantcodes-avatar", {
62
- size: "large",
63
- });
64
-
65
- const avatarElement = element.shadowRoot.querySelector(".avatar");
66
- assert.ok(
67
- avatarElement.classList.contains("avatar--large"),
68
- "Should have large size class",
69
- );
70
- });
71
-
72
- it("should support different sizes", async () => {
73
- const sizes = ["small", "medium", "large"];
74
-
75
- for (const size of sizes) {
76
- element = await fixture("grantcodes-avatar", { size });
77
- const avatarElement = element.shadowRoot.querySelector(".avatar");
78
- assert.ok(
79
- avatarElement.classList.contains(`avatar--${size}`),
80
- `Should have ${size} size class`,
81
- );
82
- cleanup(element);
83
- }
84
- });
1
+ import { strict as assert } from 'node:assert';
2
+ import { afterEach, describe, it } from 'node:test';
3
+ import { cleanup, fixture } from '../../test-utils/index.js';
4
+ import './avatar.js';
5
+
6
+ describe('Avatar 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-avatar');
15
+ assert.ok(element, 'Element should be created');
16
+ assert.ok(element.shadowRoot, 'Element should have shadow root');
17
+ });
18
+
19
+ it('should display initials when name is provided', async () => {
20
+ element = await fixture('grantcodes-avatar', {
21
+ name: 'John Doe',
22
+ });
23
+
24
+ const initialsElement = element.shadowRoot.querySelector('.avatar__initials');
25
+ assert.ok(initialsElement, 'Initials element should exist');
26
+ assert.strictEqual(initialsElement.textContent, 'JD', 'Should display correct initials');
27
+ });
28
+
29
+ it('should handle single name', async () => {
30
+ element = await fixture('grantcodes-avatar', {
31
+ name: 'John',
32
+ });
33
+
34
+ const initialsElement = element.shadowRoot.querySelector('.avatar__initials');
35
+ assert.strictEqual(initialsElement.textContent, 'J', 'Should display single initial');
36
+ });
37
+
38
+ it('should display image when src is provided', async () => {
39
+ element = await fixture('grantcodes-avatar', {
40
+ src: 'https://example.com/avatar.jpg',
41
+ alt: 'User avatar',
42
+ });
43
+
44
+ const imgElement = element.shadowRoot.querySelector('img');
45
+ assert.ok(imgElement, 'Image element should exist');
46
+ assert.strictEqual(imgElement.src, 'https://example.com/avatar.jpg');
47
+ assert.strictEqual(imgElement.alt, 'User avatar');
48
+ });
49
+
50
+ it('should apply size variant', async () => {
51
+ element = await fixture('grantcodes-avatar', {
52
+ size: 'large',
53
+ });
54
+
55
+ const avatarElement = element.shadowRoot.querySelector('.avatar');
56
+ assert.ok(avatarElement.classList.contains('avatar--large'), 'Should have large size class');
57
+ });
58
+
59
+ it('should support different sizes', async () => {
60
+ const sizes = ['small', 'medium', 'large'];
61
+
62
+ for (const size of sizes) {
63
+ element = await fixture('grantcodes-avatar', { size });
64
+ const avatarElement = element.shadowRoot.querySelector('.avatar');
65
+ assert.ok(
66
+ avatarElement.classList.contains(`avatar--${size}`),
67
+ `Should have ${size} size class`,
68
+ );
69
+ cleanup(element);
70
+ }
71
+ });
72
+
73
+ it('should fill the avatar box with the image', async () => {
74
+ element = await fixture('grantcodes-avatar', {
75
+ src: 'https://example.com/avatar.jpg',
76
+ });
77
+
78
+ const img = element.shadowRoot.querySelector('img');
79
+ assert.ok(
80
+ img.classList.contains('avatar__image'),
81
+ 'Image must carry the class that sizes it to 100% of the avatar box',
82
+ );
83
+ });
84
+
85
+ it('should defer the avatar image and reserve its square box', async () => {
86
+ element = await fixture('grantcodes-avatar', {
87
+ src: 'https://example.com/avatar.jpg',
88
+ name: 'John Doe',
89
+ });
90
+
91
+ const img = element.shadowRoot.querySelector('img');
92
+ assert.strictEqual(img.getAttribute('loading'), 'lazy', 'Avatars are off the critical path');
93
+ assert.strictEqual(img.getAttribute('decoding'), 'async');
94
+ assert.ok(Number(img.getAttribute('width')) > 0, 'Width should be set to avoid CLS');
95
+ assert.strictEqual(
96
+ img.getAttribute('width'),
97
+ img.getAttribute('height'),
98
+ 'Avatar images are square',
99
+ );
100
+ });
101
+
102
+ it('should not invent alt text when nothing is known about the avatar', async () => {
103
+ element = await fixture('grantcodes-avatar', { src: 'https://example.com/avatar.jpg' });
104
+
105
+ const img = element.shadowRoot.querySelector('img');
106
+ assert.strictEqual(img.getAttribute('alt'), '', 'An unknown avatar is decorative');
107
+ });
108
+
109
+ it('should use the name as alt text without an " avatar" suffix', async () => {
110
+ element = await fixture('grantcodes-avatar', {
111
+ src: 'https://example.com/avatar.jpg',
112
+ name: 'John Doe',
113
+ });
114
+
115
+ const img = element.shadowRoot.querySelector('img');
116
+ assert.strictEqual(img.getAttribute('alt'), 'John Doe');
117
+ });
118
+
119
+ it('should prefer an explicit alt over the name', async () => {
120
+ element = await fixture('grantcodes-avatar', {
121
+ src: 'https://example.com/avatar.jpg',
122
+ name: 'John Doe',
123
+ alt: 'Custom alt',
124
+ });
125
+
126
+ const img = element.shadowRoot.querySelector('img');
127
+ assert.strictEqual(img.getAttribute('alt'), 'Custom alt');
128
+ });
85
129
  });
@@ -1 +1 @@
1
- export * from "./avatar.js";
1
+ export * from './avatar.js';
@@ -1,37 +1,37 @@
1
- import { LitElement } from "lit";
2
- import { html } from "lit/static-html.js";
3
- import { classMap } from "lit/directives/class-map.js";
4
- import badgeStyles from "./badge.css" with { type: "css" };
1
+ import { LitElement } from 'lit';
2
+ import { classMap } from 'lit/directives/class-map.js';
3
+ import { html } from 'lit/static-html.js';
4
+ import badgeStyles from './badge.css' with { type: 'css' };
5
5
 
6
6
  export class GrantCodesBadge extends LitElement {
7
- static styles = [badgeStyles];
7
+ static styles = [badgeStyles];
8
8
 
9
- static properties = {
10
- variant: { type: String },
11
- };
9
+ static properties = {
10
+ variant: { type: String },
11
+ };
12
12
 
13
- constructor() {
14
- super();
13
+ constructor() {
14
+ super();
15
15
 
16
- /**
17
- * Visual variant of the badge
18
- * @type {'primary' | 'success' | 'warning' | 'error' | 'info' | 'neutral'}
19
- */
20
- this.variant = "neutral";
21
- }
16
+ /**
17
+ * Visual variant of the badge
18
+ * @type {'primary' | 'success' | 'warning' | 'error' | 'info' | 'neutral'}
19
+ */
20
+ this.variant = 'neutral';
21
+ }
22
22
 
23
- render() {
24
- const classes = classMap({
25
- badge: true,
26
- [`badge--${this.variant}`]: Boolean(this.variant),
27
- });
23
+ render() {
24
+ const classes = classMap({
25
+ badge: true,
26
+ [`badge--${this.variant}`]: Boolean(this.variant),
27
+ });
28
28
 
29
- return html`
29
+ return html`
30
30
  <span class=${classes}>
31
31
  <span class="badge__content">
32
32
  <slot></slot>
33
33
  </span>
34
34
  </span>
35
35
  `;
36
- }
36
+ }
37
37
  }