@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,349 +1,329 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { GrantCodesButton } from
|
|
5
|
-
import
|
|
6
|
-
|
|
7
|
-
describe(
|
|
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
|
-
const slot = element.shadowRoot.querySelector("slot");
|
|
104
|
-
assert.ok(slot, "Slot should exist");
|
|
105
|
-
});
|
|
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 { GrantCodesButton } from './button.component.js';
|
|
5
|
+
import './button.js';
|
|
6
|
+
|
|
7
|
+
describe('Button Component', () => {
|
|
8
|
+
let element;
|
|
9
|
+
|
|
10
|
+
afterEach(() => {
|
|
11
|
+
cleanup(element);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('should render with default properties', async () => {
|
|
15
|
+
element = await fixture('grantcodes-button');
|
|
16
|
+
assert.ok(element, 'Element should be created');
|
|
17
|
+
assert.ok(element.shadowRoot, 'Element should have shadow root');
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('should render button element by default', async () => {
|
|
21
|
+
element = await fixture('grantcodes-button');
|
|
22
|
+
const button = element.shadowRoot.querySelector('button');
|
|
23
|
+
assert.ok(button, 'Button element should exist');
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('should render link when href is provided', async () => {
|
|
27
|
+
element = await fixture('grantcodes-button', {
|
|
28
|
+
href: 'https://example.com',
|
|
29
|
+
});
|
|
30
|
+
await element.updateComplete;
|
|
31
|
+
|
|
32
|
+
const link = element.shadowRoot.querySelector('a');
|
|
33
|
+
assert.ok(link, 'Link element should exist');
|
|
34
|
+
const href = link.getAttribute('href');
|
|
35
|
+
assert.ok(href, 'Link should have href attribute');
|
|
36
|
+
assert.ok(
|
|
37
|
+
href.includes('example.com') || href === 'https://example.com',
|
|
38
|
+
'Link href should contain example.com',
|
|
39
|
+
);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('should be disabled when disabled property is set', async () => {
|
|
43
|
+
element = await fixture('grantcodes-button', {
|
|
44
|
+
disabled: true,
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const button = element.shadowRoot.querySelector('button');
|
|
48
|
+
assert.ok(button.disabled, 'Button should be disabled');
|
|
49
|
+
assert.ok(button.hasAttribute('disabled'), 'Button should have disabled attribute');
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('should emit click event when clicked', async () => {
|
|
53
|
+
element = await fixture('grantcodes-button');
|
|
54
|
+
const button = element.shadowRoot.querySelector('button');
|
|
55
|
+
|
|
56
|
+
let clicked = false;
|
|
57
|
+
element.addEventListener('click', () => {
|
|
58
|
+
clicked = true;
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
click(button);
|
|
62
|
+
assert.ok(clicked, 'Click event should have fired');
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('should not emit click event when disabled', async () => {
|
|
66
|
+
element = await fixture('grantcodes-button', {
|
|
67
|
+
disabled: true,
|
|
68
|
+
});
|
|
69
|
+
const button = element.shadowRoot.querySelector('button');
|
|
70
|
+
|
|
71
|
+
let clicked = false;
|
|
72
|
+
element.addEventListener('click', () => {
|
|
73
|
+
clicked = true;
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
click(button);
|
|
77
|
+
assert.ok(!clicked, 'Click event should not fire when disabled');
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('should support different button types', async () => {
|
|
81
|
+
const types = ['button', 'submit', 'reset'];
|
|
82
|
+
|
|
83
|
+
for (const type of types) {
|
|
84
|
+
const testElement = await fixture('grantcodes-button', { type });
|
|
85
|
+
// Internal button is always type="button" — form behavior is handled
|
|
86
|
+
// by the host element via FACE (requestSubmit/reset on ElementInternals)
|
|
87
|
+
const button = testElement.shadowRoot.querySelector('button');
|
|
88
|
+
assert.strictEqual(button.type, 'button', `Internal button should always be type="button"`);
|
|
89
|
+
assert.strictEqual(testElement.type, type, `Host type property should be ${type}`);
|
|
90
|
+
cleanup(testElement);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('should render slotted content', async () => {
|
|
95
|
+
element = await fixture('grantcodes-button');
|
|
96
|
+
element.textContent = 'Click me';
|
|
97
|
+
|
|
98
|
+
await element.updateComplete;
|
|
99
|
+
|
|
100
|
+
const slot = element.shadowRoot.querySelector('slot');
|
|
101
|
+
assert.ok(slot, 'Slot should exist');
|
|
102
|
+
});
|
|
106
103
|
});
|
|
107
104
|
|
|
108
|
-
describe(
|
|
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
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
"Link href should contain example.com",
|
|
203
|
-
);
|
|
204
|
-
});
|
|
105
|
+
describe('Form Context', () => {
|
|
106
|
+
let element;
|
|
107
|
+
let form;
|
|
108
|
+
|
|
109
|
+
afterEach(() => {
|
|
110
|
+
cleanup(element);
|
|
111
|
+
cleanup(form);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('should render button inside a form element', async () => {
|
|
115
|
+
form = document.createElement('form');
|
|
116
|
+
document.body.appendChild(form);
|
|
117
|
+
|
|
118
|
+
element = document.createElement('grantcodes-button');
|
|
119
|
+
form.appendChild(element);
|
|
120
|
+
await element.updateComplete;
|
|
121
|
+
|
|
122
|
+
const button = element.shadowRoot.querySelector('button');
|
|
123
|
+
assert.ok(button, 'Button should exist in shadow DOM when inside form');
|
|
124
|
+
const link = element.shadowRoot.querySelector('a');
|
|
125
|
+
assert.strictEqual(link, null, 'Should not render a link when inside form');
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it("should use type='button' as default on internal button", async () => {
|
|
129
|
+
element = await fixture('grantcodes-button');
|
|
130
|
+
|
|
131
|
+
const button = element.shadowRoot.querySelector('button');
|
|
132
|
+
assert.strictEqual(button.type, 'button', "Default type should be 'button'");
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it("should keep internal button as type='button' when host type is submit", async () => {
|
|
136
|
+
element = await fixture('grantcodes-button', { type: 'submit' });
|
|
137
|
+
|
|
138
|
+
const button = element.shadowRoot.querySelector('button');
|
|
139
|
+
// Internal button is always type="button" — FACE handles submit via requestSubmit()
|
|
140
|
+
assert.strictEqual(button.type, 'button', "Internal button should be type='button'");
|
|
141
|
+
assert.strictEqual(element.type, 'submit', "Host type property should be 'submit'");
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it("should keep internal button as type='button' when host type is reset", async () => {
|
|
145
|
+
element = await fixture('grantcodes-button', { type: 'reset' });
|
|
146
|
+
|
|
147
|
+
const button = element.shadowRoot.querySelector('button');
|
|
148
|
+
// Internal button is always type="button" — FACE handles reset via form.reset()
|
|
149
|
+
assert.strictEqual(button.type, 'button', "Internal button should be type='button'");
|
|
150
|
+
assert.strictEqual(element.type, 'reset', "Host type property should be 'reset'");
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it('should reflect name property on internal button', async () => {
|
|
154
|
+
element = await fixture('grantcodes-button', { name: 'myButton' });
|
|
155
|
+
|
|
156
|
+
const button = element.shadowRoot.querySelector('button');
|
|
157
|
+
assert.strictEqual(
|
|
158
|
+
button.getAttribute('name'),
|
|
159
|
+
'myButton',
|
|
160
|
+
"Internal button name attribute should be 'myButton'",
|
|
161
|
+
);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it('should reflect value property on internal button', async () => {
|
|
165
|
+
element = await fixture('grantcodes-button', { value: 'send' });
|
|
166
|
+
|
|
167
|
+
const button = element.shadowRoot.querySelector('button');
|
|
168
|
+
assert.strictEqual(
|
|
169
|
+
button.getAttribute('value'),
|
|
170
|
+
'send',
|
|
171
|
+
"Internal button value attribute should be 'send'",
|
|
172
|
+
);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it('should reflect disabled property on internal button', async () => {
|
|
176
|
+
element = await fixture('grantcodes-button', { disabled: true });
|
|
177
|
+
|
|
178
|
+
const button = element.shadowRoot.querySelector('button');
|
|
179
|
+
assert.strictEqual(button.disabled, true, 'Internal button should be disabled');
|
|
180
|
+
assert.ok(button.hasAttribute('disabled'), 'Internal button should have disabled attribute');
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it('should render as link not button when href is set', async () => {
|
|
184
|
+
element = await fixture('grantcodes-button', {
|
|
185
|
+
href: 'https://example.com',
|
|
186
|
+
});
|
|
187
|
+
await element.updateComplete;
|
|
188
|
+
|
|
189
|
+
const button = element.shadowRoot.querySelector('button');
|
|
190
|
+
const link = element.shadowRoot.querySelector('a');
|
|
191
|
+
assert.strictEqual(button, null, 'Should not render a button when href is set');
|
|
192
|
+
assert.ok(link, 'Should render a link when href is set');
|
|
193
|
+
const href = link.getAttribute('href');
|
|
194
|
+
assert.ok(
|
|
195
|
+
href === 'https://example.com' || href.includes('example.com'),
|
|
196
|
+
'Link href should contain example.com',
|
|
197
|
+
);
|
|
198
|
+
});
|
|
205
199
|
});
|
|
206
200
|
|
|
207
|
-
describe(
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
);
|
|
336
|
-
});
|
|
337
|
-
|
|
338
|
-
it("should have type='button' as default on internal button", async () => {
|
|
339
|
-
element = await fixture("grantcodes-button");
|
|
340
|
-
|
|
341
|
-
const button = element.shadowRoot.querySelector("button");
|
|
342
|
-
assert.ok(button, "Internal button should exist");
|
|
343
|
-
assert.strictEqual(
|
|
344
|
-
button.type,
|
|
345
|
-
"button",
|
|
346
|
-
"Default internal button type should be 'button'",
|
|
347
|
-
);
|
|
348
|
-
});
|
|
201
|
+
describe('FACE Infrastructure', () => {
|
|
202
|
+
let element;
|
|
203
|
+
|
|
204
|
+
afterEach(() => {
|
|
205
|
+
cleanup(element);
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
it('should declare formAssociated as true', () => {
|
|
209
|
+
assert.strictEqual(
|
|
210
|
+
GrantCodesButton.formAssociated,
|
|
211
|
+
true,
|
|
212
|
+
'GrantCodesButton.formAssociated should be true',
|
|
213
|
+
);
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
it('should have internals property after construction', async () => {
|
|
217
|
+
element = await fixture('grantcodes-button');
|
|
218
|
+
// internals may be null in happy-dom (attachInternals throws), but the
|
|
219
|
+
// property must exist — it was assigned in the constructor's try/catch.
|
|
220
|
+
assert.ok('internals' in element, 'Element should have internals property');
|
|
221
|
+
// In happy-dom: element.internals === null (expected — DOM shim limitation)
|
|
222
|
+
// In real browser: element.internals instanceof ElementInternals
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
it('should reflect form attribute when set as property', async () => {
|
|
226
|
+
element = await fixture('grantcodes-button', {
|
|
227
|
+
form: 'myForm',
|
|
228
|
+
});
|
|
229
|
+
await element.updateComplete;
|
|
230
|
+
|
|
231
|
+
assert.strictEqual(
|
|
232
|
+
element.getAttribute('form'),
|
|
233
|
+
'myForm',
|
|
234
|
+
"form attribute should reflect property value 'myForm'",
|
|
235
|
+
);
|
|
236
|
+
assert.strictEqual(element.form, 'myForm', "element.form property should be 'myForm'");
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
it('should not have form attribute by default', async () => {
|
|
240
|
+
element = await fixture('grantcodes-button');
|
|
241
|
+
await element.updateComplete;
|
|
242
|
+
|
|
243
|
+
const formAttr = element.getAttribute('form');
|
|
244
|
+
assert.ok(
|
|
245
|
+
formAttr === null || formAttr === '',
|
|
246
|
+
'form attribute should be null or empty by default',
|
|
247
|
+
);
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
it("should use type='button' on internal button after FACE changes", async () => {
|
|
251
|
+
element = await fixture('grantcodes-button', { type: 'submit' });
|
|
252
|
+
|
|
253
|
+
const button = element.shadowRoot.querySelector('button');
|
|
254
|
+
assert.ok(button, 'Internal button should exist');
|
|
255
|
+
assert.strictEqual(
|
|
256
|
+
button.type,
|
|
257
|
+
'button',
|
|
258
|
+
"Internal button should be type='button' — FACE handles submit via requestSubmit()",
|
|
259
|
+
);
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
it('should still render as link not button when href is set', async () => {
|
|
263
|
+
element = await fixture('grantcodes-button', {
|
|
264
|
+
href: 'https://example.com',
|
|
265
|
+
});
|
|
266
|
+
await element.updateComplete;
|
|
267
|
+
|
|
268
|
+
const button = element.shadowRoot.querySelector('button');
|
|
269
|
+
const link = element.shadowRoot.querySelector('a');
|
|
270
|
+
assert.strictEqual(button, null, 'Should not render a button when href is set');
|
|
271
|
+
assert.ok(link, 'Should render a link when href is set');
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
it('should have disabled attribute on internal button when disabled', async () => {
|
|
275
|
+
element = await fixture('grantcodes-button', {
|
|
276
|
+
disabled: true,
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
const button = element.shadowRoot.querySelector('button');
|
|
280
|
+
assert.ok(button, 'Internal button should exist');
|
|
281
|
+
assert.strictEqual(
|
|
282
|
+
button.disabled,
|
|
283
|
+
true,
|
|
284
|
+
'Internal button should be disabled when element.disabled is true',
|
|
285
|
+
);
|
|
286
|
+
assert.ok(button.hasAttribute('disabled'), 'Internal button should have disabled attribute');
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
it('should not throw when clicking internal button while disabled', async () => {
|
|
290
|
+
element = await fixture('grantcodes-button', {
|
|
291
|
+
disabled: true,
|
|
292
|
+
type: 'submit',
|
|
293
|
+
});
|
|
294
|
+
await element.updateComplete;
|
|
295
|
+
|
|
296
|
+
const button = element.shadowRoot.querySelector('button');
|
|
297
|
+
assert.ok(button, 'Internal button should exist');
|
|
298
|
+
|
|
299
|
+
// _handleFormClick should early-return when this.disabled is true.
|
|
300
|
+
// Verify clicking does not throw (no internals.form access attempted).
|
|
301
|
+
let threw = false;
|
|
302
|
+
try {
|
|
303
|
+
button.click();
|
|
304
|
+
} catch (e) {
|
|
305
|
+
threw = true;
|
|
306
|
+
}
|
|
307
|
+
assert.strictEqual(threw, false, 'Clicking disabled button should not throw');
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
it("should keep internal button as type='button' when host type is reset", async () => {
|
|
311
|
+
element = await fixture('grantcodes-button', { type: 'reset' });
|
|
312
|
+
|
|
313
|
+
const button = element.shadowRoot.querySelector('button');
|
|
314
|
+
assert.ok(button, 'Internal button should exist');
|
|
315
|
+
assert.strictEqual(
|
|
316
|
+
button.type,
|
|
317
|
+
'button',
|
|
318
|
+
"Internal button should be type='button' — FACE handles reset via form.reset()",
|
|
319
|
+
);
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
it("should have type='button' as default on internal button", async () => {
|
|
323
|
+
element = await fixture('grantcodes-button');
|
|
324
|
+
|
|
325
|
+
const button = element.shadowRoot.querySelector('button');
|
|
326
|
+
assert.ok(button, 'Internal button should exist');
|
|
327
|
+
assert.strictEqual(button.type, 'button', "Default internal button type should be 'button'");
|
|
328
|
+
});
|
|
349
329
|
});
|