@grantcodes/ui 2.15.6 → 2.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +67 -0
- package/custom-elements.json +476 -346
- package/package.json +23 -23
- package/src/components/accordion/accordion-item.component.js +17 -19
- package/src/components/accordion/accordion-item.css +40 -44
- package/src/components/accordion/accordion.component.js +7 -7
- package/src/components/accordion/accordion.css +5 -5
- package/src/components/accordion/accordion.js +6 -6
- package/src/components/accordion/accordion.react.js +6 -6
- package/src/components/accordion/accordion.stories.js +10 -10
- package/src/components/accordion/accordion.test.js +100 -100
- package/src/components/accordion/index.js +6 -6
- package/src/components/app-bar/app-bar.component.js +86 -92
- package/src/components/app-bar/app-bar.css +187 -203
- package/src/components/app-bar/app-bar.js +3 -3
- package/src/components/app-bar/app-bar.react.js +9 -9
- package/src/components/app-bar/app-bar.stories.js +20 -20
- package/src/components/app-bar/app-bar.test.js +190 -237
- package/src/components/app-bar/index.js +1 -1
- package/src/components/app-bar/nav-link.component.js +7 -9
- package/src/components/app-bar/nav-link.css +23 -28
- package/src/components/app-bar/nav-link.js +3 -3
- package/src/components/app-bar/nav-link.react.js +6 -6
- package/src/components/avatar/avatar.component.js +79 -68
- package/src/components/avatar/avatar.css +18 -18
- package/src/components/avatar/avatar.js +3 -3
- package/src/components/avatar/avatar.react.js +6 -6
- package/src/components/avatar/avatar.stories.js +24 -23
- package/src/components/avatar/avatar.test.js +128 -84
- package/src/components/avatar/index.js +1 -1
- package/src/components/badge/badge.component.js +23 -23
- package/src/components/badge/badge.css +39 -52
- package/src/components/badge/badge.js +3 -3
- package/src/components/badge/badge.react.js +6 -6
- package/src/components/badge/badge.stories.js +23 -23
- package/src/components/badge/badge.test.js +53 -68
- package/src/components/badge/index.js +1 -1
- package/src/components/breadcrumb/breadcrumb.component.js +95 -96
- package/src/components/breadcrumb/breadcrumb.css +42 -44
- package/src/components/breadcrumb/breadcrumb.js +6 -9
- package/src/components/breadcrumb/breadcrumb.react.js +9 -12
- package/src/components/breadcrumb/breadcrumb.stories.js +5 -5
- package/src/components/breadcrumb/breadcrumb.test.js +139 -151
- package/src/components/breadcrumb/index.js +1 -1
- package/src/components/button/button.component.js +101 -103
- package/src/components/button/button.css +90 -94
- package/src/components/button/button.js +3 -3
- package/src/components/button/button.react.js +6 -6
- package/src/components/button/button.stories.js +25 -25
- package/src/components/button/button.test.js +324 -344
- package/src/components/button/index.js +1 -1
- package/src/components/button-group/button-group.component.js +18 -18
- package/src/components/button-group/button-group.css +16 -16
- package/src/components/button-group/button-group.js +3 -3
- package/src/components/button-group/button-group.react.js +6 -6
- package/src/components/button-group/button-group.stories.js +23 -23
- package/src/components/button-group/button-group.test.js +54 -54
- package/src/components/button-group/index.js +1 -1
- package/src/components/card/card.component.js +31 -33
- package/src/components/card/card.css +85 -85
- package/src/components/card/card.js +4 -4
- package/src/components/card/card.react.js +6 -6
- package/src/components/card/card.stories.js +27 -26
- package/src/components/card/card.test.js +56 -56
- package/src/components/card/index.js +1 -1
- package/src/components/code-preview/code-preview.component.js +47 -48
- package/src/components/code-preview/code-preview.css +11 -11
- package/src/components/code-preview/code-preview.js +3 -3
- package/src/components/code-preview/code-preview.react.js +6 -6
- package/src/components/code-preview/code-preview.stories.js +29 -29
- package/src/components/code-preview/code-preview.test.js +112 -135
- package/src/components/code-preview/index.js +1 -1
- package/src/components/container/container.component.js +27 -27
- package/src/components/container/container.css +22 -22
- package/src/components/container/container.js +3 -3
- package/src/components/container/container.react.js +6 -6
- package/src/components/container/container.stories.js +44 -44
- package/src/components/container/container.test.js +54 -66
- package/src/components/container/index.js +1 -1
- package/src/components/countdown/countdown.component.js +160 -162
- package/src/components/countdown/countdown.css +50 -34
- package/src/components/countdown/countdown.js +3 -3
- package/src/components/countdown/countdown.react.js +6 -6
- package/src/components/countdown/countdown.stories.js +34 -34
- package/src/components/countdown/index.js +1 -1
- package/src/components/cta/cta.component.js +77 -81
- package/src/components/cta/cta.css +24 -24
- package/src/components/cta/cta.js +3 -3
- package/src/components/cta/cta.react.js +6 -6
- package/src/components/cta/cta.stories.js +32 -32
- package/src/components/cta/index.js +1 -1
- package/src/components/dialog/dialog.component.js +95 -58
- package/src/components/dialog/dialog.css +45 -50
- package/src/components/dialog/dialog.js +3 -3
- package/src/components/dialog/dialog.react.js +6 -6
- package/src/components/dialog/dialog.stories.js +38 -29
- package/src/components/dialog/dialog.test.js +188 -102
- package/src/components/dialog/index.js +1 -1
- package/src/components/dropdown/dropdown.component.js +220 -201
- package/src/components/dropdown/dropdown.css +60 -84
- package/src/components/dropdown/dropdown.js +6 -9
- package/src/components/dropdown/dropdown.react.js +15 -15
- package/src/components/dropdown/dropdown.stories.js +10 -10
- package/src/components/dropdown/dropdown.test.js +211 -157
- package/src/components/dropdown/index.js +1 -1
- package/src/components/dropzone/dropzone.component.js +126 -129
- package/src/components/dropzone/dropzone.css +35 -37
- package/src/components/dropzone/dropzone.js +3 -3
- package/src/components/dropzone/dropzone.react.js +6 -6
- package/src/components/dropzone/dropzone.stories.js +28 -28
- package/src/components/dropzone/dropzone.test.js +91 -99
- package/src/components/dropzone/index.js +1 -1
- package/src/components/feature-list/feature-list.component.js +78 -86
- package/src/components/feature-list/feature-list.css +37 -37
- package/src/components/feature-list/feature-list.js +3 -3
- package/src/components/feature-list/feature-list.react.js +6 -6
- package/src/components/feature-list/feature-list.stories.js +86 -90
- package/src/components/feature-list/index.js +1 -1
- package/src/components/footer/footer-column.component.js +7 -7
- package/src/components/footer/footer-column.css +22 -22
- package/src/components/footer/footer.component.js +19 -19
- package/src/components/footer/footer.css +38 -38
- package/src/components/footer/footer.js +8 -8
- package/src/components/footer/footer.react.js +9 -9
- package/src/components/footer/footer.stories.js +9 -9
- package/src/components/footer/footer.test.js +101 -111
- package/src/components/footer/index.js +2 -2
- package/src/components/form-field/form-field.component.js +167 -125
- package/src/components/form-field/form-field.css +24 -24
- package/src/components/form-field/form-field.js +3 -3
- package/src/components/form-field/form-field.react.js +6 -6
- package/src/components/form-field/form-field.stories.js +53 -44
- package/src/components/form-field/form-field.test.js +280 -134
- package/src/components/form-field/index.js +1 -1
- package/src/components/gallery/gallery-image.component.js +30 -30
- package/src/components/gallery/gallery-image.js +3 -3
- package/src/components/gallery/gallery-image.react.js +6 -6
- package/src/components/gallery/gallery.component.js +24 -17
- package/src/components/gallery/gallery.css +79 -72
- package/src/components/gallery/gallery.js +3 -3
- package/src/components/gallery/gallery.react.js +6 -6
- package/src/components/gallery/gallery.stories.js +69 -68
- package/src/components/gallery/gallery.test.js +95 -88
- package/src/components/gallery/index.js +2 -2
- package/src/components/hero/hero.component.js +53 -57
- package/src/components/hero/hero.css +27 -27
- package/src/components/hero/hero.js +3 -3
- package/src/components/hero/hero.react.js +6 -6
- package/src/components/hero/hero.stories.js +39 -39
- package/src/components/hero/index.js +1 -1
- package/src/components/icon/icon.component.js +10 -10
- package/src/components/icon/icon.css +13 -13
- package/src/components/icon/icon.js +4 -4
- package/src/components/icon/icon.react.js +6 -6
- package/src/components/icon/icon.stories.js +8 -9
- package/src/components/icon/icon.test.js +54 -55
- package/src/components/icon/index.js +1 -1
- package/src/components/loading/index.js +1 -1
- package/src/components/loading/loading.component.js +41 -14
- package/src/components/loading/loading.css +47 -30
- package/src/components/loading/loading.js +4 -4
- package/src/components/loading/loading.react.js +6 -6
- package/src/components/loading/loading.stories.js +19 -18
- package/src/components/loading/loading.test.js +77 -54
- package/src/components/logo-cloud/index.js +1 -1
- package/src/components/logo-cloud/logo-cloud.component.js +39 -43
- package/src/components/logo-cloud/logo-cloud.css +31 -31
- package/src/components/logo-cloud/logo-cloud.js +3 -3
- package/src/components/logo-cloud/logo-cloud.react.js +6 -6
- package/src/components/logo-cloud/logo-cloud.stories.js +80 -80
- package/src/components/map/index.js +1 -1
- package/src/components/map/map.component.js +103 -107
- package/src/components/map/map.css +22 -22
- package/src/components/map/map.js +3 -3
- package/src/components/map/map.react.js +6 -6
- package/src/components/map/map.stories.js +38 -38
- package/src/components/media-text/index.js +1 -1
- package/src/components/media-text/media-text.component.js +76 -70
- package/src/components/media-text/media-text.css +32 -32
- package/src/components/media-text/media-text.js +3 -3
- package/src/components/media-text/media-text.react.js +6 -6
- package/src/components/media-text/media-text.stories.js +45 -45
- package/src/components/media-text/media-text.test.js +72 -0
- package/src/components/newsletter/index.js +1 -1
- package/src/components/newsletter/newsletter.component.js +56 -64
- package/src/components/newsletter/newsletter.css +48 -48
- package/src/components/newsletter/newsletter.js +3 -3
- package/src/components/newsletter/newsletter.react.js +6 -6
- package/src/components/newsletter/newsletter.stories.js +35 -35
- package/src/components/notice/index.js +1 -1
- package/src/components/notice/notice.component.js +66 -55
- package/src/components/notice/notice.css +37 -38
- package/src/components/notice/notice.js +3 -3
- package/src/components/notice/notice.react.js +6 -6
- package/src/components/notice/notice.stories.js +54 -53
- package/src/components/notice/notice.test.js +210 -156
- package/src/components/pagination/index.js +1 -1
- package/src/components/pagination/pagination.component.js +44 -44
- package/src/components/pagination/pagination.css +6 -6
- package/src/components/pagination/pagination.js +4 -4
- package/src/components/pagination/pagination.react.js +6 -6
- package/src/components/pagination/pagination.stories.js +20 -18
- package/src/components/pagination/pagination.test.js +95 -102
- package/src/components/person/index.js +1 -1
- package/src/components/person/person.component.js +29 -29
- package/src/components/person/person.css +17 -17
- package/src/components/person/person.js +4 -4
- package/src/components/person/person.react.js +6 -6
- package/src/components/person/person.stories.js +34 -33
- package/src/components/person/person.test.js +55 -68
- package/src/components/pricing/index.js +1 -1
- package/src/components/pricing/pricing.component.js +58 -70
- package/src/components/pricing/pricing.css +65 -65
- package/src/components/pricing/pricing.js +3 -3
- package/src/components/pricing/pricing.react.js +6 -6
- package/src/components/pricing/pricing.stories.js +96 -96
- package/src/components/sidebar/index.js +1 -1
- package/src/components/sidebar/sidebar.component.js +157 -130
- package/src/components/sidebar/sidebar.css +118 -115
- package/src/components/sidebar/sidebar.js +4 -4
- package/src/components/sidebar/sidebar.react.js +10 -10
- package/src/components/sidebar/sidebar.stories.js +10 -10
- package/src/components/sidebar/sidebar.test.js +250 -230
- package/src/components/stats/index.js +1 -1
- package/src/components/stats/stats.component.js +42 -50
- package/src/components/stats/stats.css +29 -29
- package/src/components/stats/stats.js +3 -3
- package/src/components/stats/stats.react.js +6 -6
- package/src/components/stats/stats.stories.js +40 -40
- package/src/components/tabs/index.js +2 -2
- package/src/components/tabs/internal/tabs-button.component.js +16 -27
- package/src/components/tabs/internal/tabs-button.js +3 -3
- package/src/components/tabs/internal/tabs-button.react.js +6 -6
- package/src/components/tabs/internal/tabs-item.component.js +30 -30
- package/src/components/tabs/tab.component.js +9 -11
- package/src/components/tabs/tab.js +3 -3
- package/src/components/tabs/tab.react.js +6 -6
- package/src/components/tabs/tabs.component.js +107 -109
- package/src/components/tabs/tabs.css +50 -59
- package/src/components/tabs/tabs.js +4 -4
- package/src/components/tabs/tabs.react.js +6 -6
- package/src/components/tabs/tabs.stories.js +23 -22
- package/src/components/tabs/tabs.test.js +201 -147
- package/src/components/testimonials/index.js +1 -1
- package/src/components/testimonials/testimonials.component.js +47 -51
- package/src/components/testimonials/testimonials.css +31 -31
- package/src/components/testimonials/testimonials.js +3 -3
- package/src/components/testimonials/testimonials.react.js +6 -6
- package/src/components/testimonials/testimonials.stories.js +49 -51
- package/src/components/toast/index.js +1 -1
- package/src/components/toast/toast.component.js +217 -148
- package/src/components/toast/toast.css +101 -99
- package/src/components/toast/toast.js +6 -9
- package/src/components/toast/toast.react.js +12 -12
- package/src/components/toast/toast.stories.js +75 -49
- package/src/components/toast/toast.test.js +260 -221
- package/src/components/tooltip/index.js +1 -1
- package/src/components/tooltip/tooltip.component.js +56 -49
- package/src/components/tooltip/tooltip.css +61 -50
- package/src/components/tooltip/tooltip.js +4 -4
- package/src/components/tooltip/tooltip.react.js +6 -6
- package/src/components/tooltip/tooltip.stories.js +24 -23
- package/src/components/tooltip/tooltip.test.js +143 -155
- package/src/constructor.test.js +31 -0
- package/src/css/base.css +1 -0
- package/src/css/colors.stories.js +120 -122
- package/src/css/elements/a.css +28 -28
- package/src/css/elements/forms/button.css +1 -1
- package/src/css/elements/forms/input.css +80 -80
- package/src/css/elements/forms/label.css +3 -3
- package/src/css/elements/media/image.css +2 -2
- package/src/css/elements.stories.js +48 -49
- package/src/css/helpers.css +8 -8
- package/src/css/layers.stories.js +5 -5
- package/src/css/reset.css +102 -102
- package/src/css/themes/todomap.css +1 -1
- package/src/css/tokens.stories.js +50 -53
- package/src/css/typography.css +30 -32
- package/src/css/typography.stories.js +92 -92
- package/src/css/util/focus-ring.css +12 -20
- package/src/css/view-transitions.css +16 -0
- package/src/exports.test.js +89 -105
- package/src/icons.js +1 -1
- package/src/lib/generate-id.js +1 -1
- package/src/lib/slot-presence-controller.js +48 -0
- package/src/lib/styles/all.css +38 -37
- package/src/lib/styles/focus-ring.css +12 -20
- package/src/lib/view-transition.js +42 -0
- package/src/lib/view-transition.test.js +105 -0
- package/src/main.js +29 -29
- package/src/pages/agency.stories.js +59 -59
- package/src/pages/blog-post.stories.js +48 -48
- package/src/pages/saas-landing.stories.js +180 -180
- package/src/react.js +44 -44
- package/src/react.test.js +42 -51
- package/src/test-utils/assert-helpers.js +39 -58
- package/src/test-utils/events.js +32 -34
- package/src/test-utils/fixture.js +31 -31
- package/src/test-utils/index.js +3 -3
- package/src/types.d.ts +3 -3
|
@@ -1,28 +1,28 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
import
|
|
1
|
+
import { getStorybookHelpers } from '@wc-toolkit/storybook-helpers';
|
|
2
|
+
import { html } from 'lit/static-html.js';
|
|
3
|
+
|
|
4
|
+
const { events, args, argTypes, template } = getStorybookHelpers('grantcodes-form-field');
|
|
5
|
+
|
|
6
|
+
import './form-field.js';
|
|
7
7
|
|
|
8
8
|
const meta = {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
26
|
};
|
|
27
27
|
|
|
28
28
|
export default meta;
|
|
@@ -30,52 +30,61 @@ export default meta;
|
|
|
30
30
|
export const FormField = {};
|
|
31
31
|
|
|
32
32
|
export const FormFieldWithError = {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
args: {
|
|
34
|
+
error: 'This is an error',
|
|
35
|
+
slot: html`<input type="text" placeholder="Type, then blur" required />`,
|
|
36
|
+
},
|
|
37
|
+
parameters: {
|
|
38
|
+
docs: {
|
|
39
|
+
description: {
|
|
40
|
+
story:
|
|
41
|
+
'The error stays hidden until the field is interacted with or reports :user-invalid — type and blur to reveal it.',
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
},
|
|
36
45
|
};
|
|
37
46
|
|
|
38
47
|
export const FormFieldWithHelp = {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
48
|
+
args: {
|
|
49
|
+
help: 'This is help text',
|
|
50
|
+
},
|
|
42
51
|
};
|
|
43
52
|
|
|
44
53
|
export const FormFieldWithSelect = {
|
|
45
54
|
args: {
|
|
46
|
-
|
|
55
|
+
slot: html`
|
|
47
56
|
<select>
|
|
48
57
|
<option value="1">Option 1</option>
|
|
49
58
|
<option value="2">Option 2</option>
|
|
50
59
|
<option value="3">Option 3</option>
|
|
51
60
|
</select>
|
|
52
61
|
`,
|
|
53
|
-
|
|
62
|
+
},
|
|
54
63
|
};
|
|
55
64
|
|
|
56
65
|
export const FormFieldWithTextArea = {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
66
|
+
args: {
|
|
67
|
+
slot: html`<textarea placeholder="This is a placeholder"></textarea>`,
|
|
68
|
+
},
|
|
60
69
|
};
|
|
61
70
|
|
|
62
71
|
export const FormFieldWithCheckbox = {
|
|
63
72
|
args: {
|
|
64
73
|
direction: 'horizontal',
|
|
65
|
-
|
|
66
|
-
|
|
74
|
+
slot: html`<input type="checkbox" value="1" name="name" />`,
|
|
75
|
+
},
|
|
67
76
|
};
|
|
68
77
|
|
|
69
78
|
export const FormFieldWithRadio = {
|
|
70
79
|
args: {
|
|
71
80
|
direction: 'horizontal',
|
|
72
|
-
|
|
73
|
-
|
|
81
|
+
slot: html`<input type="radio" />`,
|
|
82
|
+
},
|
|
74
83
|
};
|
|
75
84
|
|
|
76
85
|
export const FormFieldWithRadioGroup = {
|
|
77
|
-
|
|
78
|
-
|
|
86
|
+
args: {
|
|
87
|
+
slot: html`
|
|
79
88
|
<grantcodes-form-field label="Radio number 1" direction="horizontal">
|
|
80
89
|
<input type="radio" name="radio-group" value="1" />
|
|
81
90
|
</grantcodes-form-field>
|
|
@@ -86,12 +95,12 @@ export const FormFieldWithRadioGroup = {
|
|
|
86
95
|
<input type="radio" name="radio-group" value="3" />
|
|
87
96
|
</grantcodes-form-field>
|
|
88
97
|
`,
|
|
89
|
-
|
|
98
|
+
},
|
|
90
99
|
};
|
|
91
100
|
|
|
92
101
|
export const FormFieldWithCheckboxGroup = {
|
|
93
|
-
|
|
94
|
-
|
|
102
|
+
args: {
|
|
103
|
+
slot: html`
|
|
95
104
|
<grantcodes-form-field label="Checkbox number 1" direction="horizontal"
|
|
96
105
|
><input type="checkbox"
|
|
97
106
|
/></grantcodes-form-field>
|
|
@@ -102,5 +111,5 @@ export const FormFieldWithCheckboxGroup = {
|
|
|
102
111
|
><input type="checkbox"
|
|
103
112
|
/></grantcodes-form-field>
|
|
104
113
|
`,
|
|
105
|
-
|
|
114
|
+
},
|
|
106
115
|
};
|
|
@@ -1,135 +1,281 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
5
|
-
|
|
6
|
-
describe(
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
1
|
+
import { strict as assert } from 'node:assert';
|
|
2
|
+
import { afterEach, describe, it } from 'node:test';
|
|
3
|
+
import { cleanup, click, fixture } from '../../test-utils/index.js';
|
|
4
|
+
import './form-field.js';
|
|
5
|
+
|
|
6
|
+
describe('Form Field Component', () => {
|
|
7
|
+
let element;
|
|
8
|
+
|
|
9
|
+
afterEach(() => {
|
|
10
|
+
cleanup(element);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('should render with default properties', async () => {
|
|
14
|
+
element = await fixture('grantcodes-form-field');
|
|
15
|
+
assert.ok(element, 'Element should be created');
|
|
16
|
+
assert.ok(element.shadowRoot, 'Element should have shadow root');
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('should display label text', async () => {
|
|
20
|
+
element = await fixture('grantcodes-form-field', {
|
|
21
|
+
label: 'Username',
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const labelElement = element.shadowRoot.querySelector('.form-field__label');
|
|
25
|
+
assert.ok(labelElement, 'Label element should exist');
|
|
26
|
+
assert.strictEqual(labelElement.textContent, 'Username', 'Label text should match');
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('should reflect label text for SSR client upgrade', async () => {
|
|
30
|
+
element = await fixture('grantcodes-form-field', {
|
|
31
|
+
label: 'Username',
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
assert.strictEqual(
|
|
35
|
+
element.getAttribute('label'),
|
|
36
|
+
'Username',
|
|
37
|
+
'Label should be serialized to an attribute for client upgrade',
|
|
38
|
+
);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('should display error message when error is set', async () => {
|
|
42
|
+
element = await fixture('grantcodes-form-field', {
|
|
43
|
+
label: 'Email',
|
|
44
|
+
error: 'Invalid email format',
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const errorElement = element.shadowRoot.querySelector('.form-field__error');
|
|
48
|
+
assert.ok(errorElement, 'Error element should exist');
|
|
49
|
+
assert.ok(
|
|
50
|
+
errorElement.textContent.includes('Invalid email format'),
|
|
51
|
+
'Error message should be displayed',
|
|
52
|
+
);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('should not display error when no error is set', async () => {
|
|
56
|
+
element = await fixture('grantcodes-form-field', {
|
|
57
|
+
label: 'Email',
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const errorElement = element.shadowRoot.querySelector('.form-field__error');
|
|
61
|
+
assert.ok(!errorElement, 'Error element should not exist');
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('should display help text when help is provided', async () => {
|
|
65
|
+
element = await fixture('grantcodes-form-field', {
|
|
66
|
+
label: 'Password',
|
|
67
|
+
help: 'Must be at least 8 characters',
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const helpElement = element.shadowRoot.querySelector('.form-field__help');
|
|
71
|
+
assert.ok(helpElement, 'Help element should exist');
|
|
72
|
+
assert.strictEqual(
|
|
73
|
+
helpElement.textContent,
|
|
74
|
+
'Must be at least 8 characters',
|
|
75
|
+
'Help text should match',
|
|
76
|
+
);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('should not display help when no help is provided', async () => {
|
|
80
|
+
element = await fixture('grantcodes-form-field', {
|
|
81
|
+
label: 'Password',
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
const helpElement = element.shadowRoot.querySelector('.form-field__help');
|
|
85
|
+
assert.ok(!helpElement, 'Help element should not exist when help is not provided');
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('should generate unique IDs', async () => {
|
|
89
|
+
const element1 = await fixture('grantcodes-form-field');
|
|
90
|
+
const element2 = await fixture('grantcodes-form-field');
|
|
91
|
+
|
|
92
|
+
assert.notStrictEqual(element1.id, element2.id, 'IDs should be unique');
|
|
93
|
+
|
|
94
|
+
cleanup(element1);
|
|
95
|
+
cleanup(element2);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('should not copy the host id onto the inner control', async () => {
|
|
99
|
+
element = document.createElement('grantcodes-form-field');
|
|
100
|
+
element.label = 'Username';
|
|
101
|
+
element.innerHTML = '<input type="text" />';
|
|
102
|
+
document.body.appendChild(element);
|
|
103
|
+
await element.updateComplete;
|
|
104
|
+
|
|
105
|
+
const input = element.querySelector('input');
|
|
106
|
+
assert.notStrictEqual(
|
|
107
|
+
input.id,
|
|
108
|
+
element.id,
|
|
109
|
+
'Inner control must not duplicate the host id',
|
|
110
|
+
);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('should have label element', async () => {
|
|
114
|
+
element = await fixture('grantcodes-form-field', {
|
|
115
|
+
label: 'Test Field',
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
const label = element.shadowRoot.querySelector('label');
|
|
119
|
+
assert.ok(label, 'Label element should exist');
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('should render as fieldset for grouped inputs', async () => {
|
|
123
|
+
element = await fixture('grantcodes-form-field', {
|
|
124
|
+
label: 'Select options',
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
// Add nested form fields to trigger group mode
|
|
128
|
+
const nestedField = document.createElement('grantcodes-form-field');
|
|
129
|
+
element.appendChild(nestedField);
|
|
130
|
+
|
|
131
|
+
await element.updateComplete;
|
|
132
|
+
// This would need to check if groupInput was set, which happens in firstUpdated
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it('should have slot for input elements', async () => {
|
|
136
|
+
element = await fixture('grantcodes-form-field', {
|
|
137
|
+
label: 'Input',
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
const slot = element.shadowRoot.querySelector('slot');
|
|
141
|
+
assert.ok(slot, 'Slot should exist for input elements');
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it('should follow later error and help changes with aria-describedby', async () => {
|
|
145
|
+
element = document.createElement('grantcodes-form-field');
|
|
146
|
+
element.label = 'Email';
|
|
147
|
+
element.innerHTML = '<input type="text" />';
|
|
148
|
+
document.body.appendChild(element);
|
|
149
|
+
await element.updateComplete;
|
|
150
|
+
|
|
151
|
+
const input = element.querySelector('input');
|
|
152
|
+
assert.ok(!input.getAttribute('aria-describedby'), 'No description before error/help');
|
|
153
|
+
|
|
154
|
+
element.error = 'Invalid email';
|
|
155
|
+
element.help = 'Use your work email';
|
|
156
|
+
await element.updateComplete;
|
|
157
|
+
|
|
158
|
+
const describedBy = input.getAttribute('aria-describedby');
|
|
159
|
+
assert.ok(describedBy.includes(`${element.id}-error`), 'Error id should be described');
|
|
160
|
+
assert.ok(describedBy.includes(`${element.id}-help`), 'Help id should be described');
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it('should drop aria-describedby when error and help are cleared', async () => {
|
|
164
|
+
element = document.createElement('grantcodes-form-field');
|
|
165
|
+
element.label = 'Email';
|
|
166
|
+
element.error = 'Invalid email';
|
|
167
|
+
element.innerHTML = '<input type="text" />';
|
|
168
|
+
document.body.appendChild(element);
|
|
169
|
+
await element.updateComplete;
|
|
170
|
+
|
|
171
|
+
const input = element.querySelector('input');
|
|
172
|
+
assert.ok(input.getAttribute('aria-describedby'), 'Error should be described initially');
|
|
173
|
+
|
|
174
|
+
element.error = undefined;
|
|
175
|
+
await element.updateComplete;
|
|
176
|
+
|
|
177
|
+
assert.strictEqual(
|
|
178
|
+
input.hasAttribute('aria-describedby'),
|
|
179
|
+
false,
|
|
180
|
+
'Description should be removed when error and help are cleared',
|
|
181
|
+
);
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
it('should keep aria-invalid in sync with the error state', async () => {
|
|
185
|
+
element = document.createElement('grantcodes-form-field');
|
|
186
|
+
element.label = 'Email';
|
|
187
|
+
element.innerHTML = '<input type="email" />';
|
|
188
|
+
document.body.appendChild(element);
|
|
189
|
+
await element.updateComplete;
|
|
190
|
+
|
|
191
|
+
const input = element.querySelector('input');
|
|
192
|
+
assert.strictEqual(input.hasAttribute('aria-invalid'), false, 'No error, no aria-invalid');
|
|
193
|
+
|
|
194
|
+
element.error = 'Invalid email';
|
|
195
|
+
await element.updateComplete;
|
|
196
|
+
assert.strictEqual(input.getAttribute('aria-invalid'), 'true', 'Error sets aria-invalid');
|
|
197
|
+
|
|
198
|
+
element.error = undefined;
|
|
199
|
+
await element.updateComplete;
|
|
200
|
+
assert.strictEqual(input.hasAttribute('aria-invalid'), false, 'Clearing clears aria-invalid');
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it('should hold the error message until the control is user-invalid', async () => {
|
|
204
|
+
element = document.createElement('grantcodes-form-field');
|
|
205
|
+
element.label = 'Email';
|
|
206
|
+
element.error = 'Invalid email';
|
|
207
|
+
element.innerHTML = '<input type="email" />';
|
|
208
|
+
document.body.appendChild(element);
|
|
209
|
+
await element.updateComplete;
|
|
210
|
+
|
|
211
|
+
const error = element.shadowRoot.querySelector('.form-field__error');
|
|
212
|
+
assert.ok(error, 'Error text should be rendered');
|
|
213
|
+
assert.strictEqual(error.textContent.trim(), 'Invalid email', 'The "Error: " prefix is gone');
|
|
214
|
+
assert.ok(error.hasAttribute('hidden'), 'Untouched control must not show the error yet');
|
|
215
|
+
|
|
216
|
+
// happy-dom cannot put a control into :user-invalid, so the state is stubbed.
|
|
217
|
+
const input = element.querySelector('input');
|
|
218
|
+
input.matches = (selector) => selector === ':user-invalid';
|
|
219
|
+
input.dispatchEvent(new Event('input', { bubbles: true, composed: true }));
|
|
220
|
+
await element.updateComplete;
|
|
221
|
+
|
|
222
|
+
assert.strictEqual(
|
|
223
|
+
element.shadowRoot.querySelector('.form-field__error').hasAttribute('hidden'),
|
|
224
|
+
false,
|
|
225
|
+
'Error should appear once the control is user-invalid',
|
|
226
|
+
);
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
it('should show the error when there is no control to validate', async () => {
|
|
230
|
+
element = await fixture('grantcodes-form-field', {
|
|
231
|
+
label: 'Email',
|
|
232
|
+
error: 'Invalid email',
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
const error = element.shadowRoot.querySelector('.form-field__error');
|
|
236
|
+
assert.ok(error, 'Error should render without a control');
|
|
237
|
+
assert.strictEqual(error.hasAttribute('hidden'), false, 'Nothing to validate, so show it');
|
|
238
|
+
});
|
|
239
|
+
it('should toggle a wrapped checkbox from the label text', async () => {
|
|
240
|
+
element = document.createElement('grantcodes-form-field');
|
|
241
|
+
element.label = 'Accept terms';
|
|
242
|
+
element.innerHTML = '<input type="checkbox" />';
|
|
243
|
+
document.body.appendChild(element);
|
|
244
|
+
await element.updateComplete;
|
|
245
|
+
|
|
246
|
+
const input = element.querySelector('input');
|
|
247
|
+
let changes = 0;
|
|
248
|
+
input.addEventListener('change', () => {
|
|
249
|
+
changes += 1;
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
click(element.shadowRoot.querySelector('.form-field__label'));
|
|
253
|
+
|
|
254
|
+
assert.strictEqual(input.checked, true, 'A label click must toggle the checkbox');
|
|
255
|
+
assert.strictEqual(changes, 1, 'It must behave like a real click and emit change');
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
it('should reveal a consumer-set error once the field is touched', async () => {
|
|
259
|
+
element = document.createElement('grantcodes-form-field');
|
|
260
|
+
element.label = 'Email';
|
|
261
|
+
element.error = 'Invalid email';
|
|
262
|
+
element.innerHTML = '<input type="text" />';
|
|
263
|
+
document.body.appendChild(element);
|
|
264
|
+
await element.updateComplete;
|
|
265
|
+
|
|
266
|
+
assert.ok(
|
|
267
|
+
element.shadowRoot.querySelector('.form-field__error').hasAttribute('hidden'),
|
|
268
|
+
'Untouched field keeps the error hidden',
|
|
269
|
+
);
|
|
270
|
+
|
|
271
|
+
const input = element.querySelector('input');
|
|
272
|
+
input.dispatchEvent(new Event('input', { bubbles: true, composed: true }));
|
|
273
|
+
await element.updateComplete;
|
|
274
|
+
|
|
275
|
+
assert.strictEqual(
|
|
276
|
+
element.shadowRoot.querySelector('.form-field__error').hasAttribute('hidden'),
|
|
277
|
+
false,
|
|
278
|
+
'Touching the field reveals a consumer-set error without native invalidity',
|
|
279
|
+
);
|
|
280
|
+
});
|
|
135
281
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export * from
|
|
1
|
+
export * from './form-field.js';
|
|
@@ -1,42 +1,42 @@
|
|
|
1
|
-
import { LitElement } from
|
|
2
|
-
import { html } from
|
|
3
|
-
import galleryStyles from
|
|
1
|
+
import { LitElement } from 'lit';
|
|
2
|
+
import { html } from 'lit/static-html.js';
|
|
3
|
+
import galleryStyles from './gallery.css' with { type: 'css' };
|
|
4
4
|
|
|
5
5
|
export class GrantCodesGalleryImage extends LitElement {
|
|
6
|
-
|
|
6
|
+
static styles = [galleryStyles];
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
8
|
+
static properties = {
|
|
9
|
+
width: { type: Number },
|
|
10
|
+
height: { type: Number },
|
|
11
|
+
src: { type: String },
|
|
12
|
+
alt: { type: String },
|
|
13
|
+
caption: { type: String },
|
|
14
|
+
thumbnail: { type: String },
|
|
15
|
+
};
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
constructor() {
|
|
18
|
+
super();
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
20
|
+
this.width = 0;
|
|
21
|
+
this.height = 0;
|
|
22
|
+
this.src = '';
|
|
23
|
+
this.alt = '';
|
|
24
|
+
this.caption = '';
|
|
25
|
+
this.thumbnail = '';
|
|
26
|
+
}
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
captionTemplate() {
|
|
29
|
+
if (!this.caption) {
|
|
30
|
+
return html``;
|
|
31
|
+
}
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
return html`
|
|
34
34
|
<figcaption class="gallery__image__caption">${this.caption}</figcaption>
|
|
35
35
|
`;
|
|
36
|
-
|
|
36
|
+
}
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
render() {
|
|
39
|
+
return html`
|
|
40
40
|
<figure class="gallery__image">
|
|
41
41
|
<img
|
|
42
42
|
width=${this.width}
|
|
@@ -48,5 +48,5 @@ export class GrantCodesGalleryImage extends LitElement {
|
|
|
48
48
|
${this.captionTemplate()}
|
|
49
49
|
</figure>
|
|
50
50
|
`;
|
|
51
|
-
|
|
51
|
+
}
|
|
52
52
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { GrantCodesGalleryImage } from
|
|
1
|
+
import { GrantCodesGalleryImage } from './gallery-image.component.js';
|
|
2
2
|
|
|
3
|
-
export * from
|
|
3
|
+
export * from './gallery-image.component.js';
|
|
4
4
|
export default GrantCodesGalleryImage;
|
|
5
5
|
|
|
6
|
-
customElements.define(
|
|
6
|
+
customElements.define('grantcodes-gallery-image', GrantCodesGalleryImage);
|