@grantcodes/ui 2.0.2 → 2.1.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 (179) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/custom-elements.json +1926 -191
  3. package/package.json +6 -5
  4. package/src/components/accordion/accordion.component.js +33 -0
  5. package/src/components/accordion/accordion.js +6 -0
  6. package/src/components/accordion/accordion.stories.js +88 -0
  7. package/src/components/accordion/accordion.styles.js +66 -0
  8. package/src/components/accordion/index.js +6 -0
  9. package/src/components/app-bar/app-bar.component.js +1 -3
  10. package/src/components/app-bar/app-bar.js +0 -2
  11. package/src/components/app-bar/app-bar.styles.js +222 -221
  12. package/src/components/app-bar/app-bar.test.js +58 -17
  13. package/src/components/app-bar/index.js +0 -2
  14. package/src/components/avatar/avatar.js +0 -12
  15. package/src/components/avatar/avatar.stories.js +0 -12
  16. package/src/components/avatar/avatar.styles.js +19 -19
  17. package/src/components/avatar/avatar.test.js +4 -4
  18. package/src/components/avatar/index.js +1 -13
  19. package/src/components/badge/badge.js +0 -2
  20. package/src/components/badge/badge.styles.js +78 -81
  21. package/src/components/badge/badge.test.js +18 -5
  22. package/src/components/badge/index.js +0 -2
  23. package/src/components/breadcrumb/breadcrumb.component.js +9 -10
  24. package/src/components/breadcrumb/breadcrumb.js +6 -4
  25. package/src/components/breadcrumb/breadcrumb.styles.js +86 -90
  26. package/src/components/breadcrumb/breadcrumb.test.js +15 -5
  27. package/src/components/breadcrumb/index.js +0 -2
  28. package/src/components/button/button.component.js +2 -2
  29. package/src/components/button/button.styles.js +58 -86
  30. package/src/components/button/button.test.js +8 -4
  31. package/src/components/button/index.js +1 -1
  32. package/src/components/button-group/button-group.test.js +0 -2
  33. package/src/components/button-group/index.js +1 -1
  34. package/src/components/card/card.component.js +40 -9
  35. package/src/components/card/card.js +3 -1
  36. package/src/components/card/card.stories.js +18 -5
  37. package/src/components/card/card.styles.js +46 -20
  38. package/src/components/card/card.test.js +0 -2
  39. package/src/components/card/index.js +1 -1
  40. package/src/components/code-preview/code-preview.component.js +9 -9
  41. package/src/components/code-preview/code-preview.js +0 -1
  42. package/src/components/code-preview/code-preview.styles.js +3 -3
  43. package/src/components/code-preview/code-preview.test.js +29 -8
  44. package/src/components/code-preview/index.js +1 -1
  45. package/src/components/container/container.component.js +1 -0
  46. package/src/components/container/container.js +0 -1
  47. package/src/components/container/container.stories.js +12 -4
  48. package/src/components/container/container.styles.js +37 -35
  49. package/src/components/container/container.test.js +0 -2
  50. package/src/components/container/index.js +1 -1
  51. package/src/components/cta/cta.component.js +108 -0
  52. package/src/components/cta/cta.js +6 -0
  53. package/src/components/cta/cta.stories.js +56 -0
  54. package/src/components/cta/cta.styles.js +64 -0
  55. package/src/components/cta/index.js +1 -0
  56. package/src/components/dialog/dialog.js +0 -1
  57. package/src/components/dialog/dialog.styles.js +8 -8
  58. package/src/components/dialog/dialog.test.js +11 -5
  59. package/src/components/dialog/index.js +1 -1
  60. package/src/components/dropdown/dropdown.component.js +5 -3
  61. package/src/components/dropdown/dropdown.js +6 -4
  62. package/src/components/dropdown/dropdown.styles.js +5 -5
  63. package/src/components/dropdown/dropdown.test.js +20 -4
  64. package/src/components/dropdown/index.js +0 -2
  65. package/src/components/dropzone/dropzone.component.js +7 -6
  66. package/src/components/dropzone/dropzone.styles.js +4 -4
  67. package/src/components/dropzone/dropzone.test.js +6 -4
  68. package/src/components/dropzone/index.js +1 -1
  69. package/src/components/feature-list/feature-list.component.js +130 -0
  70. package/src/components/feature-list/feature-list.js +6 -0
  71. package/src/components/feature-list/feature-list.stories.js +117 -0
  72. package/src/components/feature-list/feature-list.styles.js +82 -0
  73. package/src/components/feature-list/index.js +1 -0
  74. package/src/components/footer/footer-column.styles.js +46 -47
  75. package/src/components/footer/footer.js +6 -2
  76. package/src/components/footer/footer.styles.js +6 -6
  77. package/src/components/footer/footer.test.js +9 -4
  78. package/src/components/footer/index.js +1 -1
  79. package/src/components/form-field/form-field.component.js +1 -3
  80. package/src/components/form-field/form-field.js +0 -1
  81. package/src/components/form-field/form-field.styles.js +35 -37
  82. package/src/components/form-field/form-field.test.js +9 -4
  83. package/src/components/form-field/index.js +1 -1
  84. package/src/components/gallery/gallery-image.js +0 -1
  85. package/src/components/gallery/gallery.js +0 -1
  86. package/src/components/gallery/gallery.styles.js +1 -1
  87. package/src/components/gallery/gallery.test.js +5 -3
  88. package/src/components/gallery/index.js +2 -2
  89. package/src/components/hero/hero.component.js +66 -0
  90. package/src/components/hero/hero.js +6 -0
  91. package/src/components/hero/hero.stories.js +53 -0
  92. package/src/components/hero/hero.styles.js +46 -0
  93. package/src/components/hero/index.js +1 -0
  94. package/src/components/icon/icon.js +3 -2
  95. package/src/components/icon/icon.stories.js +2 -1
  96. package/src/components/icon/icon.styles.js +23 -21
  97. package/src/components/icon/icon.test.js +2 -3
  98. package/src/components/icon/index.js +1 -1
  99. package/src/components/loading/index.js +1 -1
  100. package/src/components/loading/loading.js +3 -2
  101. package/src/components/loading/loading.styles.js +1 -1
  102. package/src/components/loading/loading.test.js +0 -2
  103. package/src/components/logo-cloud/index.js +1 -0
  104. package/src/components/logo-cloud/logo-cloud.component.js +81 -0
  105. package/src/components/logo-cloud/logo-cloud.js +6 -0
  106. package/src/components/logo-cloud/logo-cloud.stories.js +107 -0
  107. package/src/components/logo-cloud/logo-cloud.styles.js +68 -0
  108. package/src/components/media-text/index.js +1 -0
  109. package/src/components/media-text/media-text.component.js +100 -0
  110. package/src/components/media-text/media-text.js +6 -0
  111. package/src/components/media-text/media-text.stories.js +69 -0
  112. package/src/components/media-text/media-text.styles.js +66 -0
  113. package/src/components/newsletter/index.js +1 -0
  114. package/src/components/newsletter/newsletter.component.js +101 -0
  115. package/src/components/newsletter/newsletter.js +6 -0
  116. package/src/components/newsletter/newsletter.stories.js +59 -0
  117. package/src/components/newsletter/newsletter.styles.js +89 -0
  118. package/src/components/notice/index.js +1 -1
  119. package/src/components/notice/notice.js +0 -1
  120. package/src/components/notice/notice.styles.js +7 -7
  121. package/src/components/notice/notice.test.js +15 -5
  122. package/src/components/pagination/index.js +1 -1
  123. package/src/components/pagination/pagination.stories.js +1 -3
  124. package/src/components/pagination/pagination.styles.js +1 -1
  125. package/src/components/pagination/pagination.test.js +9 -4
  126. package/src/components/pricing/index.js +1 -0
  127. package/src/components/pricing/pricing.component.js +119 -0
  128. package/src/components/pricing/pricing.js +6 -0
  129. package/src/components/pricing/pricing.stories.js +123 -0
  130. package/src/components/pricing/pricing.styles.js +135 -0
  131. package/src/components/sidebar/index.js +0 -2
  132. package/src/components/sidebar/sidebar.component.js +12 -10
  133. package/src/components/sidebar/sidebar.js +3 -3
  134. package/src/components/sidebar/sidebar.stories.js +0 -2
  135. package/src/components/sidebar/sidebar.styles.js +181 -186
  136. package/src/components/sidebar/sidebar.test.js +48 -13
  137. package/src/components/stats/index.js +1 -0
  138. package/src/components/stats/stats.component.js +73 -0
  139. package/src/components/stats/stats.js +6 -0
  140. package/src/components/stats/stats.stories.js +64 -0
  141. package/src/components/stats/stats.styles.js +66 -0
  142. package/src/components/tabs/index.js +2 -2
  143. package/src/components/tabs/internal/tabs-button.component.js +1 -1
  144. package/src/components/tabs/internal/tabs-button.js +0 -1
  145. package/src/components/tabs/tab.js +0 -1
  146. package/src/components/tabs/tabs.js +3 -2
  147. package/src/components/tabs/tabs.styles.js +84 -74
  148. package/src/components/testimonials/index.js +1 -0
  149. package/src/components/testimonials/testimonials.component.js +97 -0
  150. package/src/components/testimonials/testimonials.js +6 -0
  151. package/src/components/testimonials/testimonials.stories.js +78 -0
  152. package/src/components/testimonials/testimonials.styles.js +82 -0
  153. package/src/components/toast/index.js +0 -2
  154. package/src/components/toast/toast.component.js +1 -3
  155. package/src/components/toast/toast.js +10 -5
  156. package/src/components/toast/toast.stories.js +9 -5
  157. package/src/components/toast/toast.styles.js +199 -201
  158. package/src/components/toast/toast.test.js +38 -10
  159. package/src/components/tooltip/index.js +1 -1
  160. package/src/components/tooltip/tooltip.js +3 -2
  161. package/src/components/tooltip/tooltip.styles.js +3 -3
  162. package/src/components/tooltip/tooltip.test.js +10 -4
  163. package/src/css/base.css +8 -5
  164. package/src/css/colors.stories.js +27 -28
  165. package/src/css/elements/forms/input.css +9 -41
  166. package/src/css/elements/media/image.css +1 -1
  167. package/src/css/themes/todomap.css +1 -0
  168. package/src/css/tokens.stories.js +26 -21
  169. package/src/css/typography.css +1 -3
  170. package/src/css/util/focus-ring.css +30 -0
  171. package/src/css/util/index.css +1 -2
  172. package/src/lib/styles/focus-ring.styles.js +34 -0
  173. package/src/main.js +10 -1
  174. package/src/pages/agency.stories.js +164 -0
  175. package/src/pages/blog-post.stories.js +381 -0
  176. package/src/pages/saas-landing.stories.js +307 -0
  177. package/src/test-utils/assert-helpers.js +10 -8
  178. package/src/css/util/functions.css +0 -16
  179. package/src/css/util/mixins.css +0 -63
@@ -0,0 +1,6 @@
1
+ import { GrantCodesMediaText } from "./media-text.component.js";
2
+
3
+ export * from "./media-text.component.js";
4
+ export default GrantCodesMediaText;
5
+
6
+ customElements.define("grantcodes-media-text", GrantCodesMediaText);
@@ -0,0 +1,69 @@
1
+ import { getStorybookHelpers } from "@wc-toolkit/storybook-helpers";
2
+ import "./media-text.js";
3
+
4
+ const { events, args, argTypes } = getStorybookHelpers("grantcodes-media-text");
5
+
6
+ const meta = {
7
+ title: "Blocks/MediaText",
8
+ component: "grantcodes-media-text",
9
+ args,
10
+ argTypes,
11
+ parameters: {
12
+ actions: {
13
+ handles: events,
14
+ },
15
+ layout: "fullscreen",
16
+ },
17
+ };
18
+
19
+ export default meta;
20
+
21
+ /**
22
+ * Default media-text — image on the left, text on the right.
23
+ */
24
+ export const Default = {
25
+ args: {
26
+ title: "Design tokens done right",
27
+ text: "Our style dictionary generates CSS custom properties, JS tokens, and JSON outputs from a single source of truth — keeping every theme in perfect sync.",
28
+ media: JSON.stringify({
29
+ src: "https://placehold.co/640x480",
30
+ alt: "Design tokens diagram",
31
+ kind: "image",
32
+ }),
33
+ cta: JSON.stringify({ label: "Read the docs", href: "/docs/tokens" }),
34
+ reverse: false,
35
+ },
36
+ };
37
+
38
+ /**
39
+ * Reversed layout — image on the right, text on the left.
40
+ */
41
+ export const Reversed = {
42
+ args: {
43
+ title: "Accessible by default",
44
+ text: "Every component ships with ARIA attributes, keyboard navigation, and focus management built in. No extra configuration required.",
45
+ media: JSON.stringify({
46
+ src: "https://placehold.co/640x480",
47
+ alt: "Accessibility diagram",
48
+ kind: "image",
49
+ }),
50
+ reverse: true,
51
+ },
52
+ };
53
+
54
+ /**
55
+ * Video media variant.
56
+ */
57
+ export const Video = {
58
+ args: {
59
+ title: "See it in action",
60
+ text: "Watch how quickly you can spin up a fully-themed UI with our component library.",
61
+ media: JSON.stringify({
62
+ src: "https://www.w3schools.com/html/mov_bbb.mp4",
63
+ alt: "Demo video",
64
+ kind: "video",
65
+ }),
66
+ cta: JSON.stringify({ label: "Try it yourself", href: "/playground" }),
67
+ reverse: false,
68
+ },
69
+ };
@@ -0,0 +1,66 @@
1
+ import { css } from "lit";
2
+
3
+ export const mediaTextStyles = css`
4
+ :host {
5
+ display: block;
6
+ }
7
+
8
+ .media-text {
9
+ padding-block: var(--g-theme-spacing-3xl);
10
+ padding-inline: var(--g-theme-spacing-md);
11
+ }
12
+
13
+ .media-text__container {
14
+ max-width: 1200px;
15
+ margin: 0 auto;
16
+ display: grid;
17
+ grid-template-columns: 1fr 1fr;
18
+ gap: var(--g-theme-spacing-3xl);
19
+ align-items: center;
20
+ }
21
+
22
+ :host([reverse]) .media-text__container {
23
+ direction: rtl;
24
+ }
25
+
26
+ :host([reverse]) .media-text__content,
27
+ :host([reverse]) .media-text__media {
28
+ direction: ltr;
29
+ }
30
+
31
+ @media (max-width: 768px) {
32
+ .media-text__container {
33
+ grid-template-columns: 1fr;
34
+ gap: var(--g-theme-spacing-xl);
35
+ direction: ltr;
36
+ }
37
+ }
38
+
39
+ .media-text__image,
40
+ .media-text__video {
41
+ inline-size: 100%;
42
+ block-size: auto;
43
+ display: block;
44
+ border-radius: var(--g-theme-border-radius-md, 0.5rem);
45
+ object-fit: cover;
46
+ aspect-ratio: 4 / 3;
47
+ }
48
+
49
+ .media-text__content {
50
+ display: flex;
51
+ flex-direction: column;
52
+ gap: var(--g-theme-spacing-lg);
53
+ }
54
+
55
+ .media-text__title {
56
+ margin: 0;
57
+ font: var(--g-theme-typography-headline-sm);
58
+ color: var(--g-theme-color-content-default);
59
+ }
60
+
61
+ .media-text__text {
62
+ margin: 0;
63
+ font: var(--g-theme-typography-body-lg);
64
+ color: var(--g-theme-color-content-secondary);
65
+ }
66
+ `;
@@ -0,0 +1 @@
1
+ export * from "./newsletter.js";
@@ -0,0 +1,101 @@
1
+ import { LitElement, html } from "lit";
2
+ import { newsletterStyles } from "./newsletter.styles.js";
3
+ import "../button/button.js";
4
+
5
+ export class GrantCodesNewsletter extends LitElement {
6
+ static styles = [newsletterStyles];
7
+
8
+ static properties = {
9
+ /**
10
+ * Section heading.
11
+ * @type {string}
12
+ */
13
+ title: { type: String },
14
+ /**
15
+ * Optional supporting paragraph.
16
+ * @type {string}
17
+ */
18
+ text: { type: String },
19
+ /**
20
+ * Form action URL.
21
+ * @type {string}
22
+ */
23
+ action: { type: String },
24
+ /**
25
+ * Form submission method.
26
+ * @type {'get' | 'post'}
27
+ */
28
+ method: { type: String },
29
+ /**
30
+ * Email input placeholder.
31
+ * @type {string}
32
+ */
33
+ placeholder: { type: String },
34
+ /**
35
+ * Submit button label.
36
+ * @type {string}
37
+ */
38
+ buttonLabel: { type: String, attribute: "button-label" },
39
+ /**
40
+ * Optional small disclaimer text below the form.
41
+ * @type {string}
42
+ */
43
+ disclaimer: { type: String },
44
+ };
45
+
46
+ constructor() {
47
+ super();
48
+ this.title = "";
49
+ this.text = "";
50
+ this.action = "";
51
+ this.method = "post";
52
+ this.placeholder = "Your email address";
53
+ this.buttonLabel = "Subscribe";
54
+ this.disclaimer = "";
55
+ }
56
+
57
+ render() {
58
+ return html`
59
+ <section class="newsletter">
60
+ <div class="newsletter__container">
61
+ <h2 class="newsletter__title">${this.title}</h2>
62
+ ${
63
+ this.text
64
+ ? html`<p class="newsletter__text">${this.text}</p>`
65
+ : null
66
+ }
67
+ <form
68
+ class="newsletter__form"
69
+ action=${this.action}
70
+ method=${this.method}
71
+ >
72
+ <div class="newsletter__field">
73
+ <label for="newsletter-email" class="newsletter__label"
74
+ >Email address</label
75
+ >
76
+ <div class="newsletter__input-wrap">
77
+ <input
78
+ id="newsletter-email"
79
+ type="email"
80
+ name="email"
81
+ class="newsletter__input"
82
+ placeholder=${this.placeholder}
83
+ required
84
+ autocomplete="email"
85
+ />
86
+ <grantcodes-button type="submit">
87
+ ${this.buttonLabel}
88
+ </grantcodes-button>
89
+ </div>
90
+ </div>
91
+ </form>
92
+ ${
93
+ this.disclaimer
94
+ ? html`<p class="newsletter__disclaimer">${this.disclaimer}</p>`
95
+ : null
96
+ }
97
+ </div>
98
+ </section>
99
+ `;
100
+ }
101
+ }
@@ -0,0 +1,6 @@
1
+ import { GrantCodesNewsletter } from "./newsletter.component.js";
2
+
3
+ export * from "./newsletter.component.js";
4
+ export default GrantCodesNewsletter;
5
+
6
+ customElements.define("grantcodes-newsletter", GrantCodesNewsletter);
@@ -0,0 +1,59 @@
1
+ import { getStorybookHelpers } from "@wc-toolkit/storybook-helpers";
2
+ import "./newsletter.js";
3
+
4
+ const { events, args, argTypes } = getStorybookHelpers("grantcodes-newsletter");
5
+
6
+ const meta = {
7
+ title: "Blocks/Newsletter",
8
+ component: "grantcodes-newsletter",
9
+ args,
10
+ argTypes,
11
+ parameters: {
12
+ actions: {
13
+ handles: events,
14
+ },
15
+ layout: "fullscreen",
16
+ },
17
+ };
18
+
19
+ export default meta;
20
+
21
+ /**
22
+ * Default newsletter signup with disclaimer.
23
+ */
24
+ export const Default = {
25
+ args: {
26
+ title: "Stay in the loop",
27
+ text: "Get the latest updates, articles, and resources delivered straight to your inbox.",
28
+ action: "/subscribe",
29
+ method: "post",
30
+ placeholder: "you@example.com",
31
+ "button-label": "Subscribe",
32
+ disclaimer: "No spam. Unsubscribe at any time.",
33
+ },
34
+ };
35
+
36
+ /**
37
+ * Minimal newsletter — title and input only.
38
+ */
39
+ export const Minimal = {
40
+ args: {
41
+ title: "Subscribe for updates",
42
+ action: "/subscribe",
43
+ "button-label": "Subscribe",
44
+ },
45
+ };
46
+
47
+ /**
48
+ * Newsletter with a custom button label and placeholder.
49
+ */
50
+ export const CustomLabels = {
51
+ args: {
52
+ title: "Join the newsletter",
53
+ text: "Weekly tips on building great web experiences.",
54
+ action: "/newsletter",
55
+ placeholder: "Enter your email",
56
+ "button-label": "Join now",
57
+ disclaimer: "We respect your privacy.",
58
+ },
59
+ };
@@ -0,0 +1,89 @@
1
+ import { css } from "lit";
2
+
3
+ export const newsletterStyles = css`
4
+ :host {
5
+ display: block;
6
+ }
7
+
8
+ .newsletter {
9
+ padding-block: var(--g-theme-spacing-3xl);
10
+ padding-inline: var(--g-theme-spacing-md);
11
+ background: var(--g-theme-color-background-brand-knockout, #7c3aed);
12
+ text-align: center;
13
+ }
14
+
15
+ .newsletter__container {
16
+ max-width: 55ch;
17
+ margin: 0 auto;
18
+ }
19
+
20
+ .newsletter__title {
21
+ margin: 0 0 var(--g-theme-spacing-sm);
22
+ font: var(--g-theme-typography-headline-sm);
23
+ color: var(--g-theme-color-content-brand-knockout, #ffffff);
24
+ }
25
+
26
+ .newsletter__text {
27
+ margin: 0 0 var(--g-theme-spacing-xl);
28
+ font: var(--g-theme-typography-body-lg);
29
+ color: var(--g-theme-color-content-brand-knockout, #ffffff);
30
+ opacity: 0.9;
31
+ }
32
+
33
+ .newsletter__form {
34
+ width: 100%;
35
+ }
36
+
37
+ .newsletter__label {
38
+ position: absolute;
39
+ width: 1px;
40
+ height: 1px;
41
+ padding: 0;
42
+ margin: -1px;
43
+ overflow: hidden;
44
+ clip: rect(0, 0, 0, 0);
45
+ white-space: nowrap;
46
+ border: 0;
47
+ }
48
+
49
+ .newsletter__field {
50
+ display: flex;
51
+ flex-direction: column;
52
+ gap: var(--g-theme-spacing-sm);
53
+ }
54
+
55
+ .newsletter__input-wrap {
56
+ display: flex;
57
+ gap: var(--g-theme-spacing-sm);
58
+ }
59
+
60
+ .newsletter__input {
61
+ flex: 1;
62
+ min-width: 0;
63
+ padding-block: var(--g-theme-spacing-sm);
64
+ padding-inline: var(--g-theme-spacing-md);
65
+ font-size: var(--g-theme-typography-body-default-font-size);
66
+ border: 2px solid transparent;
67
+ border-radius: var(--g-theme-border-radius-md, 0.5rem);
68
+ background: var(--g-theme-color-background-default);
69
+ color: var(--g-theme-color-content-default);
70
+ outline: none;
71
+ }
72
+
73
+ .newsletter__input:focus {
74
+ border-color: var(--g-theme-color-border-default);
75
+ }
76
+
77
+ .newsletter__disclaimer {
78
+ margin: var(--g-theme-spacing-md) 0 0;
79
+ font: var(--g-theme-typography-body-sm);
80
+ color: var(--g-theme-color-content-brand-knockout, #ffffff);
81
+ opacity: 0.75;
82
+ }
83
+
84
+ @media (max-width: 480px) {
85
+ .newsletter__input-wrap {
86
+ flex-direction: column;
87
+ }
88
+ }
89
+ `;
@@ -1 +1 @@
1
- export * from "./notice";
1
+ export * from "./notice.js";
@@ -4,4 +4,3 @@ export * from "./notice.component.js";
4
4
  export default GrantCodesNotice;
5
5
 
6
6
  customElements.define("grantcodes-notice", GrantCodesNotice);
7
-
@@ -6,9 +6,9 @@ export const noticeStyles = css`
6
6
  display: flex;
7
7
  flex-direction: row;
8
8
  align-items: flex-start;
9
- gap: 1rem;
10
- padding: 1rem;
11
- border-radius: 0.25rem;
9
+ gap: var(--g-theme-spacing-md);
10
+ padding: var(--g-theme-spacing-md);
11
+ border-radius: var(--g-theme-border-radius-sm, 0.25rem);
12
12
  view-transition-name: notice;
13
13
  transition: opacity 0.5s;
14
14
  }
@@ -34,7 +34,7 @@ export const noticeStyles = css`
34
34
  }
35
35
 
36
36
  .notice__title {
37
- font-size: 1rem;
37
+ font-size: var(--g-theme-typography-body-default-font-size);
38
38
  margin: 0;
39
39
  }
40
40
 
@@ -53,8 +53,8 @@ export const noticeStyles = css`
53
53
  transition: 0.2s;
54
54
  font-size: calc(var(--g-theme-typography-body-default-line-height) * 1em);
55
55
  line-height: 1;
56
- padding: 0.5rem;
57
- margin: -0.5rem -0.5rem 0 0;
56
+ padding: var(--g-theme-spacing-sm);
57
+ margin: calc(-1 * var(--g-theme-spacing-sm)) calc(-1 * var(--g-theme-spacing-sm)) 0 0;
58
58
  border: 0;
59
59
 
60
60
  &:hover,
@@ -69,4 +69,4 @@ export const noticeStyles = css`
69
69
  margin: 0;
70
70
  }
71
71
 
72
- `;
72
+ `;
@@ -18,7 +18,11 @@ describe("Notice Component", () => {
18
18
 
19
19
  it("should have info variant by default", async () => {
20
20
  element = await fixture("grantcodes-notice");
21
- assert.strictEqual(element.variant, "info", "Default variant should be info");
21
+ assert.strictEqual(
22
+ element.variant,
23
+ "info",
24
+ "Default variant should be info",
25
+ );
22
26
  });
23
27
 
24
28
  it("should render with info variant class", async () => {
@@ -64,7 +68,11 @@ describe("Notice Component", () => {
64
68
 
65
69
  const title = element.shadowRoot.querySelector(".notice__title");
66
70
  assert.ok(title, "Title element should exist");
67
- assert.strictEqual(title.textContent, "Important Notice", "Title text should match");
71
+ assert.strictEqual(
72
+ title.textContent,
73
+ "Important Notice",
74
+ "Title text should match",
75
+ );
68
76
  });
69
77
 
70
78
  it("should not display title when not provided", async () => {
@@ -76,7 +84,11 @@ describe("Notice Component", () => {
76
84
 
77
85
  it("should not be dismissable by default", async () => {
78
86
  element = await fixture("grantcodes-notice");
79
- assert.strictEqual(element.dismissable, false, "Should not be dismissable by default");
87
+ assert.strictEqual(
88
+ element.dismissable,
89
+ false,
90
+ "Should not be dismissable by default",
91
+ );
80
92
  });
81
93
 
82
94
  it("should render dismiss button when dismissable", async () => {
@@ -142,5 +154,3 @@ describe("Notice Component", () => {
142
154
  await new Promise((resolve) => setTimeout(resolve, 10));
143
155
  });
144
156
  });
145
-
146
-
@@ -1 +1 @@
1
- export * from "./pagination";
1
+ export * from "./pagination.js";
@@ -1,8 +1,6 @@
1
1
  import { html } from "lit/static-html.js";
2
2
  import { getStorybookHelpers } from "@wc-toolkit/storybook-helpers";
3
- const { events, args, argTypes } = getStorybookHelpers(
4
- "grantcodes-pagination",
5
- );
3
+ const { events, args, argTypes } = getStorybookHelpers("grantcodes-pagination");
6
4
  import "./pagination.js";
7
5
 
8
6
  const meta = {
@@ -16,4 +16,4 @@ export const paginationStyles = css`
16
16
  margin-inline-end: auto;
17
17
  }
18
18
 
19
- `;
19
+ `;
@@ -51,7 +51,11 @@ describe("Pagination Component", () => {
51
51
  href: "/page/{page}",
52
52
  });
53
53
 
54
- assert.strictEqual(element.href, "/page/{page}", "Href property should be set");
54
+ assert.strictEqual(
55
+ element.href,
56
+ "/page/{page}",
57
+ "Href property should be set",
58
+ );
55
59
  });
56
60
 
57
61
  it("should indicate current page", async () => {
@@ -91,8 +95,9 @@ describe("Pagination Component", () => {
91
95
 
92
96
  // The component should allow navigating to previous and next pages
93
97
  assert.ok(element.page > 1, "Should be able to go to previous page");
94
- assert.ok(element.page < element.pages, "Should be able to go to next page");
98
+ assert.ok(
99
+ element.page < element.pages,
100
+ "Should be able to go to next page",
101
+ );
95
102
  });
96
103
  });
97
-
98
-
@@ -0,0 +1 @@
1
+ export * from "./pricing.js";
@@ -0,0 +1,119 @@
1
+ import { LitElement, html } from "lit";
2
+ import { pricingStyles } from "./pricing.styles.js";
3
+ import "../button/button.js";
4
+
5
+ export class GrantCodesPricing extends LitElement {
6
+ static styles = [pricingStyles];
7
+
8
+ static properties = {
9
+ /**
10
+ * Optional section heading.
11
+ * @type {string}
12
+ */
13
+ title: { type: String },
14
+ /**
15
+ * Optional supporting text below the heading.
16
+ * @type {string}
17
+ */
18
+ subtitle: { type: String },
19
+ /**
20
+ * Pricing tiers as a JSON string array.
21
+ * Each tier: `{"name":"...","price":"...","period":"...","description":"...","features":[{"text":"...","included":true}],"cta":{"label":"...","href":"..."},"highlighted":false}`.
22
+ * @type {string}
23
+ */
24
+ tiers: { type: String },
25
+ };
26
+
27
+ constructor() {
28
+ super();
29
+ this.title = "";
30
+ this.subtitle = "";
31
+ this.tiers = "[]";
32
+ }
33
+
34
+ get _tiers() {
35
+ try {
36
+ return JSON.parse(this.tiers);
37
+ } catch {
38
+ return [];
39
+ }
40
+ }
41
+
42
+ render() {
43
+ const tiers = this._tiers;
44
+ return html`
45
+ <section class="pricing">
46
+ <div class="pricing__container">
47
+ ${
48
+ this.title
49
+ ? html`<h2 class="pricing__title">${this.title}</h2>`
50
+ : null
51
+ }
52
+ ${
53
+ this.subtitle
54
+ ? html`<p class="pricing__subtitle">${this.subtitle}</p>`
55
+ : null
56
+ }
57
+ <ul class="pricing__grid" role="list">
58
+ ${tiers.map(
59
+ (tier) => html`
60
+ <li
61
+ class="pricing__tier${
62
+ tier.highlighted ? " pricing__tier--highlighted" : ""
63
+ }"
64
+ >
65
+ <div class="pricing__tier-header">
66
+ <h3 class="pricing__tier-name">${tier.name}</h3>
67
+ <div class="pricing__price-wrap">
68
+ <span class="pricing__price">${tier.price}</span>
69
+ ${
70
+ tier.period
71
+ ? html`<span class="pricing__period"
72
+ >/${tier.period}</span
73
+ >`
74
+ : null
75
+ }
76
+ </div>
77
+ ${
78
+ tier.description
79
+ ? html`<p class="pricing__tier-desc">
80
+ ${tier.description}
81
+ </p>`
82
+ : null
83
+ }
84
+ </div>
85
+ <ul class="pricing__features" role="list">
86
+ ${(tier.features ?? []).map(
87
+ (feature) => html`
88
+ <li
89
+ class="pricing__feature${
90
+ feature.included
91
+ ? ""
92
+ : " pricing__feature--excluded"
93
+ }"
94
+ >
95
+ <span
96
+ class="pricing__feature-icon"
97
+ aria-hidden="true"
98
+ >
99
+ ${feature.included ? "✓" : "✗"}
100
+ </span>
101
+ ${feature.text}
102
+ </li>
103
+ `,
104
+ )}
105
+ </ul>
106
+ <div class="pricing__cta">
107
+ <grantcodes-button href=${tier.cta.href}
108
+ >${tier.cta.label}</grantcodes-button
109
+ >
110
+ </div>
111
+ </li>
112
+ `,
113
+ )}
114
+ </ul>
115
+ </div>
116
+ </section>
117
+ `;
118
+ }
119
+ }
@@ -0,0 +1,6 @@
1
+ import { GrantCodesPricing } from "./pricing.component.js";
2
+
3
+ export * from "./pricing.component.js";
4
+ export default GrantCodesPricing;
5
+
6
+ customElements.define("grantcodes-pricing", GrantCodesPricing);