@grantcodes/ui 2.0.0-beta.9 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/README.md +6 -8
- package/custom-elements.json +4273 -0
- package/package.json +80 -90
- package/src/components/app-bar/app-bar.component.js +90 -0
- package/src/components/app-bar/app-bar.js +8 -0
- package/src/components/app-bar/app-bar.stories.js +84 -0
- package/src/components/app-bar/app-bar.styles.js +227 -0
- package/src/components/app-bar/app-bar.test.js +174 -0
- package/src/components/app-bar/index.js +3 -0
- package/src/components/avatar/avatar.component.js +78 -0
- package/src/components/avatar/avatar.js +18 -0
- package/src/components/avatar/avatar.stories.js +45 -0
- package/src/components/avatar/avatar.styles.js +42 -0
- package/src/components/avatar/avatar.test.js +85 -0
- package/src/components/avatar/index.js +13 -0
- package/src/components/badge/badge.component.js +36 -0
- package/src/components/badge/badge.js +8 -0
- package/src/components/badge/badge.stories.js +46 -0
- package/src/components/badge/badge.styles.js +94 -0
- package/src/components/badge/badge.test.js +70 -0
- package/src/components/badge/index.js +3 -0
- package/src/components/breadcrumb/breadcrumb.component.js +110 -0
- package/src/components/breadcrumb/breadcrumb.js +12 -0
- package/src/components/breadcrumb/breadcrumb.stories.js +25 -0
- package/src/components/breadcrumb/breadcrumb.styles.js +96 -0
- package/src/components/breadcrumb/breadcrumb.test.js +144 -0
- package/src/components/breadcrumb/index.js +3 -0
- package/src/components/button/button.component.js +64 -0
- package/src/components/button/button.js +6 -0
- package/src/components/button/button.stories.js +78 -0
- package/src/components/button/button.styles.js +97 -0
- package/src/components/button/button.test.js +98 -0
- package/src/components/button/index.js +1 -0
- package/src/components/button-group/button-group.component.js +27 -0
- package/src/components/button-group/button-group.js +6 -0
- package/src/components/button-group/button-group.stories.js +33 -0
- package/src/components/button-group/button-group.styles.js +43 -0
- package/src/components/button-group/button-group.test.js +57 -0
- package/src/components/button-group/index.js +1 -0
- package/src/components/card/card.component.js +17 -0
- package/src/components/card/card.js +6 -0
- package/src/components/card/card.stories.js +36 -0
- package/src/components/card/card.styles.js +128 -0
- package/src/components/card/card.test.js +59 -0
- package/src/components/card/index.js +1 -0
- package/src/components/code-preview/code-preview.component.js +53 -0
- package/src/components/code-preview/code-preview.js +7 -0
- package/src/components/code-preview/code-preview.stories.js +67 -0
- package/src/components/code-preview/code-preview.styles.js +18 -0
- package/src/components/code-preview/code-preview.test.js +118 -0
- package/src/components/code-preview/index.js +1 -0
- package/src/components/container/container.component.js +38 -0
- package/src/components/container/container.js +7 -0
- package/src/components/container/container.stories.js +59 -0
- package/src/components/container/container.styles.js +43 -0
- package/src/components/container/container.test.js +84 -0
- package/src/components/container/index.js +1 -0
- package/src/components/dialog/dialog.component.js +78 -0
- package/src/components/dialog/dialog.js +7 -0
- package/src/components/dialog/dialog.stories.js +43 -0
- package/src/components/dialog/dialog.styles.js +74 -0
- package/src/components/dialog/dialog.test.js +97 -0
- package/src/components/dialog/index.js +1 -0
- package/src/components/dropdown/dropdown.component.js +225 -0
- package/src/components/dropdown/dropdown.js +12 -0
- package/src/components/dropdown/dropdown.stories.js +107 -0
- package/src/components/dropdown/dropdown.styles.js +128 -0
- package/src/components/dropdown/dropdown.test.js +144 -0
- package/src/components/dropdown/index.js +3 -0
- package/src/components/dropzone/dropzone.component.js +141 -0
- package/src/components/dropzone/dropzone.js +6 -0
- package/src/components/dropzone/dropzone.stories.js +41 -0
- package/src/components/dropzone/dropzone.styles.js +64 -0
- package/src/components/dropzone/dropzone.test.js +112 -0
- package/src/components/dropzone/index.js +1 -0
- package/src/components/footer/footer-column.component.js +15 -0
- package/src/components/footer/footer-column.styles.js +51 -0
- package/src/components/footer/footer.component.js +38 -0
- package/src/components/footer/footer.js +9 -0
- package/src/components/footer/footer.stories.js +143 -0
- package/src/components/footer/footer.styles.js +90 -0
- package/src/components/footer/footer.test.js +107 -0
- package/src/components/footer/index.js +2 -0
- package/src/components/form-field/form-field.component.js +173 -0
- package/src/components/form-field/form-field.js +7 -0
- package/src/components/form-field/form-field.stories.js +104 -0
- package/src/components/form-field/form-field.styles.js +47 -0
- package/src/components/form-field/form-field.test.js +118 -0
- package/src/components/form-field/index.js +1 -0
- package/src/components/gallery/gallery-image.component.js +52 -0
- package/src/components/gallery/gallery-image.js +7 -0
- package/src/components/gallery/gallery.component.js +25 -0
- package/src/components/gallery/gallery.js +7 -0
- package/src/components/gallery/gallery.stories.js +60 -0
- package/src/components/gallery/gallery.styles.js +56 -0
- package/src/components/gallery/gallery.test.js +58 -0
- package/src/components/gallery/index.js +2 -0
- package/src/components/icon/icon.component.js +14 -0
- package/src/components/icon/icon.js +7 -0
- package/src/components/icon/icon.stories.js +26 -0
- package/src/components/icon/icon.styles.js +28 -0
- package/src/components/icon/icon.test.js +57 -0
- package/src/components/icon/index.js +1 -0
- package/src/components/loading/index.js +1 -0
- package/src/components/loading/loading.component.js +21 -0
- package/src/components/loading/loading.js +7 -0
- package/src/components/loading/loading.stories.js +25 -0
- package/src/components/loading/loading.styles.js +43 -0
- package/src/components/loading/loading.test.js +57 -0
- package/src/components/notice/index.js +1 -0
- package/src/components/notice/notice.component.js +77 -0
- package/src/components/notice/notice.js +7 -0
- package/src/components/notice/notice.stories.js +122 -0
- package/src/components/notice/notice.styles.js +72 -0
- package/src/components/notice/notice.test.js +146 -0
- package/src/components/pagination/index.js +1 -0
- package/src/components/pagination/pagination.component.js +62 -0
- package/src/components/pagination/pagination.js +7 -0
- package/src/components/pagination/pagination.stories.js +34 -0
- package/src/components/pagination/pagination.styles.js +19 -0
- package/src/components/pagination/pagination.test.js +98 -0
- package/src/components/sidebar/index.js +3 -0
- package/src/components/sidebar/sidebar.component.js +165 -0
- package/src/components/sidebar/sidebar.js +8 -0
- package/src/components/sidebar/sidebar.stories.js +155 -0
- package/src/components/sidebar/sidebar.styles.js +192 -0
- package/src/components/sidebar/sidebar.test.js +196 -0
- package/src/components/tabs/index.js +2 -0
- package/src/components/tabs/internal/tabs-button.component.js +39 -0
- package/src/components/tabs/internal/tabs-button.js +7 -0
- package/src/components/tabs/internal/tabs-item.component.js +39 -0
- package/src/components/tabs/tab.component.js +20 -0
- package/src/components/tabs/tab.js +7 -0
- package/src/components/tabs/tabs.component.js +130 -0
- package/src/components/tabs/tabs.js +7 -0
- package/src/components/tabs/tabs.stories.js +39 -0
- package/src/components/tabs/tabs.styles.js +88 -0
- package/src/components/tabs/tabs.test.js +148 -0
- package/src/components/toast/index.js +3 -0
- package/src/components/toast/toast.component.js +187 -0
- package/src/components/toast/toast.js +9 -0
- package/src/components/toast/toast.stories.js +169 -0
- package/src/components/toast/toast.styles.js +207 -0
- package/src/components/toast/toast.test.js +196 -0
- package/src/components/tooltip/index.js +1 -0
- package/src/components/tooltip/tooltip.component.js +70 -0
- package/src/components/tooltip/tooltip.js +7 -0
- package/src/components/tooltip/tooltip.stories.js +33 -0
- package/src/components/tooltip/tooltip.styles.js +78 -0
- package/src/components/tooltip/tooltip.test.js +150 -0
- package/src/css/all.css +1 -0
- package/src/css/base.css +31 -0
- package/src/css/colors.stories.js +192 -0
- package/src/css/elements/a.css +50 -0
- package/src/css/elements/forms/button.css +15 -0
- package/src/css/elements/forms/input.css +183 -0
- package/src/css/elements/forms/label.css +5 -0
- package/src/css/elements/media/image.css +3 -0
- package/src/css/elements.css +5 -0
- package/src/css/elements.stories.js +108 -0
- package/src/css/helpers.css +16 -0
- package/src/css/themes/grantcodes.css +3 -0
- package/src/css/themes/todomap.css +2 -0
- package/src/css/themes/wireframe.css +2 -0
- package/src/css/tokens.stories.js +183 -0
- package/src/css/typography.css +64 -0
- package/src/css/typography.stories.js +179 -0
- package/src/css/util/functions.css +16 -0
- package/src/css/util/index.css +2 -0
- package/src/css/util/mixins.css +63 -0
- package/src/icons.js +3 -0
- package/src/lib/classnames.js +61 -0
- package/src/lib/generate-id.js +10 -0
- package/src/main.js +17 -0
- package/src/test-utils/assert-helpers.js +150 -0
- package/src/test-utils/events.js +88 -0
- package/src/test-utils/fixture.js +77 -0
- package/src/test-utils/index.js +7 -0
- package/dist/_virtual/_commonjsHelpers.js +0 -1
- package/dist/_virtual/index.js +0 -1
- package/dist/_virtual/react.production.min.js +0 -1
- package/dist/_virtual/react.production.min2.js +0 -1
- package/dist/components/avatar/avatar.component.js +0 -3
- package/dist/components/avatar/avatar.js +0 -1
- package/dist/components/avatar/avatar.react.js +0 -1
- package/dist/components/avatar/avatar.scss.js +0 -1
- package/dist/components/avatar/index.js +0 -1
- package/dist/components/button/button.component.js +0 -5
- package/dist/components/button/button.js +0 -1
- package/dist/components/button/button.react.js +0 -1
- package/dist/components/button/button.scss.js +0 -1
- package/dist/components/button/index.js +0 -1
- package/dist/components/button-group/button-group.component.js +0 -5
- package/dist/components/button-group/button-group.js +0 -1
- package/dist/components/button-group/button-group.react.js +0 -1
- package/dist/components/button-group/button-group.scss.js +0 -1
- package/dist/components/button-group/index.js +0 -1
- package/dist/components/card/card.component.js +0 -8
- package/dist/components/card/card.js +0 -1
- package/dist/components/card/card.react.js +0 -1
- package/dist/components/card/card.scss.js +0 -1
- package/dist/components/card/index.js +0 -1
- package/dist/components/code-preview/code-preview.component.js +0 -3
- package/dist/components/code-preview/code-preview.js +0 -1
- package/dist/components/code-preview/code-preview.react.js +0 -1
- package/dist/components/code-preview/code-preview.scss.js +0 -1
- package/dist/components/code-preview/index.js +0 -1
- package/dist/components/container/container.component.js +0 -5
- package/dist/components/container/container.js +0 -1
- package/dist/components/container/container.react.js +0 -1
- package/dist/components/container/container.scss.js +0 -1
- package/dist/components/container/index.js +0 -1
- package/dist/components/dialog/dialog.component.js +0 -23
- package/dist/components/dialog/dialog.js +0 -1
- package/dist/components/dialog/dialog.react.js +0 -1
- package/dist/components/dialog/dialog.scss.js +0 -1
- package/dist/components/dialog/index.js +0 -1
- package/dist/components/dropzone/dropzone.component.js +0 -11
- package/dist/components/dropzone/dropzone.js +0 -1
- package/dist/components/dropzone/dropzone.react.js +0 -1
- package/dist/components/dropzone/dropzone.scss.js +0 -1
- package/dist/components/dropzone/index.js +0 -1
- package/dist/components/form-field/form-field.component.js +0 -22
- package/dist/components/form-field/form-field.js +0 -1
- package/dist/components/form-field/form-field.react.js +0 -1
- package/dist/components/form-field/form-field.scss.js +0 -1
- package/dist/components/form-field/index.js +0 -1
- package/dist/components/gallery/gallery-image.component.js +0 -14
- package/dist/components/gallery/gallery-image.js +0 -1
- package/dist/components/gallery/gallery.component.js +0 -5
- package/dist/components/gallery/gallery.js +0 -1
- package/dist/components/gallery/gallery.react.js +0 -1
- package/dist/components/gallery/gallery.scss.js +0 -1
- package/dist/components/gallery/index.js +0 -1
- package/dist/components/icon/icon.component.js +0 -1
- package/dist/components/icon/icon.js +0 -1
- package/dist/components/icon/icon.react.js +0 -1
- package/dist/components/icon/icon.scss.js +0 -1
- package/dist/components/icon/index.js +0 -1
- package/dist/components/loading/index.js +0 -1
- package/dist/components/loading/loading.component.js +0 -5
- package/dist/components/loading/loading.js +0 -1
- package/dist/components/loading/loading.react.js +0 -1
- package/dist/components/loading/loading.scss.js +0 -1
- package/dist/components/notice/index.js +0 -1
- package/dist/components/notice/notice.component.js +0 -16
- package/dist/components/notice/notice.js +0 -1
- package/dist/components/notice/notice.react.js +0 -1
- package/dist/components/notice/notice.scss.js +0 -1
- package/dist/components/pagination/index.js +0 -1
- package/dist/components/pagination/pagination.component.js +0 -13
- package/dist/components/pagination/pagination.js +0 -1
- package/dist/components/pagination/pagination.react.js +0 -1
- package/dist/components/pagination/pagination.scss.js +0 -1
- package/dist/components/tabs/index.js +0 -1
- package/dist/components/tabs/internal/tabs-button.component.js +0 -13
- package/dist/components/tabs/internal/tabs-button.js +0 -1
- package/dist/components/tabs/internal/tabs-item.component.js +0 -1
- package/dist/components/tabs/tab.component.js +0 -10
- package/dist/components/tabs/tab.js +0 -1
- package/dist/components/tabs/tabs.component.js +0 -22
- package/dist/components/tabs/tabs.js +0 -1
- package/dist/components/tabs/tabs.react.js +0 -1
- package/dist/components/tabs/tabs.scss.js +0 -1
- package/dist/components/tooltip/index.js +0 -1
- package/dist/components/tooltip/tooltip.component.js +0 -10
- package/dist/components/tooltip/tooltip.js +0 -1
- package/dist/components/tooltip/tooltip.react.js +0 -1
- package/dist/components/tooltip/tooltip.scss.js +0 -1
- package/dist/css/base.css +0 -1
- package/dist/css/themes/grantcodes.css +0 -1
- package/dist/fonts/greycliff-bold-oblique.woff +0 -0
- package/dist/fonts/greycliff-bold-oblique.woff2 +0 -0
- package/dist/fonts/greycliff-bold.woff +0 -0
- package/dist/fonts/greycliff-bold.woff2 +0 -0
- package/dist/fonts/greycliff-demi-bold-oblique.woff +0 -0
- package/dist/fonts/greycliff-demi-bold-oblique.woff2 +0 -0
- package/dist/fonts/greycliff-demi-bold.woff +0 -0
- package/dist/fonts/greycliff-demi-bold.woff2 +0 -0
- package/dist/fonts/greycliff-extra-bold-oblique.woff +0 -0
- package/dist/fonts/greycliff-extra-bold-oblique.woff2 +0 -0
- package/dist/fonts/greycliff-extra-bold.woff +0 -0
- package/dist/fonts/greycliff-extra-bold.woff2 +0 -0
- package/dist/fonts/greycliff-extra-light-oblique.woff +0 -0
- package/dist/fonts/greycliff-extra-light-oblique.woff2 +0 -0
- package/dist/fonts/greycliff-extra-light.woff +0 -0
- package/dist/fonts/greycliff-extra-light.woff2 +0 -0
- package/dist/fonts/greycliff-heavy-oblique.woff +0 -0
- package/dist/fonts/greycliff-heavy-oblique.woff2 +0 -0
- package/dist/fonts/greycliff-heavy.woff +0 -0
- package/dist/fonts/greycliff-heavy.woff2 +0 -0
- package/dist/fonts/greycliff-light-oblique.woff +0 -0
- package/dist/fonts/greycliff-light-oblique.woff2 +0 -0
- package/dist/fonts/greycliff-light.woff +0 -0
- package/dist/fonts/greycliff-light.woff2 +0 -0
- package/dist/fonts/greycliff-medium-oblique.woff +0 -0
- package/dist/fonts/greycliff-medium-oblique.woff2 +0 -0
- package/dist/fonts/greycliff-medium.woff +0 -0
- package/dist/fonts/greycliff-medium.woff2 +0 -0
- package/dist/fonts/greycliff-regular-oblique.woff +0 -0
- package/dist/fonts/greycliff-regular-oblique.woff2 +0 -0
- package/dist/fonts/greycliff-regular.woff +0 -0
- package/dist/fonts/greycliff-regular.woff2 +0 -0
- package/dist/fonts/greycliff-thin-oblique.woff +0 -0
- package/dist/fonts/greycliff-thin-oblique.woff2 +0 -0
- package/dist/fonts/greycliff-thin.woff +0 -0
- package/dist/fonts/greycliff-thin.woff2 +0 -0
- package/dist/icons.js +0 -1
- package/dist/lib/generate-id.js +0 -1
- package/dist/main.js +0 -1
- package/dist/node_modules/react/cjs/react.production.min.js +0 -9
- package/dist/node_modules/react/index.js +0 -1
- package/dist/react.js +0 -1
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { describe, it, afterEach } from "node:test";
|
|
2
|
+
import { strict as assert } from "node:assert";
|
|
3
|
+
import { fixture, cleanup } from "../../test-utils/index.js";
|
|
4
|
+
import "./tabs.js";
|
|
5
|
+
import "./tab.js";
|
|
6
|
+
|
|
7
|
+
describe("Tabs 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-tabs");
|
|
16
|
+
assert.ok(element, "Element should be created");
|
|
17
|
+
assert.ok(element.shadowRoot, "Element should have shadow root");
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("should have label property", async () => {
|
|
21
|
+
element = await fixture("grantcodes-tabs", {
|
|
22
|
+
label: "Navigation tabs",
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
assert.strictEqual(element.label, "Navigation tabs", "Label should be set");
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it("should render tabs wrapper", async () => {
|
|
29
|
+
element = await fixture("grantcodes-tabs");
|
|
30
|
+
const tabs = element.shadowRoot.querySelector(".tabs");
|
|
31
|
+
assert.ok(tabs, "Tabs wrapper should exist");
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it("should render tablist with role", async () => {
|
|
35
|
+
element = await fixture("grantcodes-tabs");
|
|
36
|
+
const tablist = element.shadowRoot.querySelector('[role="tablist"]');
|
|
37
|
+
assert.ok(tablist, "Tablist should exist with role");
|
|
38
|
+
assert.ok(
|
|
39
|
+
tablist.classList.contains("tabs__tablist"),
|
|
40
|
+
"Should have tablist class",
|
|
41
|
+
);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it("should render panels container", async () => {
|
|
45
|
+
element = await fixture("grantcodes-tabs");
|
|
46
|
+
const panels = element.shadowRoot.querySelector(".tabs__panels");
|
|
47
|
+
assert.ok(panels, "Panels container should exist");
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it("should have slot for tab panels", async () => {
|
|
51
|
+
element = await fixture("grantcodes-tabs");
|
|
52
|
+
const slot = element.shadowRoot.querySelector(".tabs__panels slot");
|
|
53
|
+
assert.ok(slot, "Slot should exist for tab panels");
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it("should generate unique ID", async () => {
|
|
57
|
+
const element1 = await fixture("grantcodes-tabs");
|
|
58
|
+
const element2 = await fixture("grantcodes-tabs");
|
|
59
|
+
|
|
60
|
+
assert.notStrictEqual(element1.id, element2.id, "IDs should be unique");
|
|
61
|
+
|
|
62
|
+
cleanup(element1);
|
|
63
|
+
cleanup(element2);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it("should initialize tabs array", async () => {
|
|
67
|
+
element = await fixture("grantcodes-tabs");
|
|
68
|
+
assert.ok(Array.isArray(element.tabs), "Tabs should be an array");
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it("should initialize tabButtons array", async () => {
|
|
72
|
+
element = await fixture("grantcodes-tabs");
|
|
73
|
+
assert.ok(
|
|
74
|
+
Array.isArray(element.tabButtons),
|
|
75
|
+
"Tab buttons should be an array",
|
|
76
|
+
);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it("should have focusedTabIndex property", async () => {
|
|
80
|
+
element = await fixture("grantcodes-tabs");
|
|
81
|
+
// After firstUpdated, if there are tabs, _focusedTabIndex will be set to 0
|
|
82
|
+
// If there are no tabs, it should remain -1
|
|
83
|
+
// Since we're not adding tabs, it should remain -1, but firstUpdated may have run
|
|
84
|
+
// Check that the property exists and is a number
|
|
85
|
+
assert.ok(
|
|
86
|
+
typeof element._focusedTabIndex === "number",
|
|
87
|
+
"Should have focusedTabIndex property",
|
|
88
|
+
);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it("should support keyboard navigation", async () => {
|
|
92
|
+
element = await fixture("grantcodes-tabs");
|
|
93
|
+
// The handleTabKeyDown method handles ArrowRight and ArrowLeft
|
|
94
|
+
assert.ok(
|
|
95
|
+
element.handleTabKeyDown,
|
|
96
|
+
"Should have keyboard navigation handler",
|
|
97
|
+
);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it("should set aria-label on tablist when label is provided", async () => {
|
|
101
|
+
element = await fixture("grantcodes-tabs", {
|
|
102
|
+
label: "Main navigation",
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
const tablist = element.shadowRoot.querySelector('[role="tablist"]');
|
|
106
|
+
assert.strictEqual(
|
|
107
|
+
tablist.getAttribute("aria-label"),
|
|
108
|
+
"Main navigation",
|
|
109
|
+
"Tablist should have aria-label",
|
|
110
|
+
);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it("should manage active tab state", async () => {
|
|
114
|
+
element = await fixture("grantcodes-tabs");
|
|
115
|
+
|
|
116
|
+
// Create and add tab elements
|
|
117
|
+
const tab1 = document.createElement("grantcodes-tab");
|
|
118
|
+
tab1.label = "Tab 1";
|
|
119
|
+
const tab2 = document.createElement("grantcodes-tab");
|
|
120
|
+
tab2.label = "Tab 2";
|
|
121
|
+
|
|
122
|
+
element.appendChild(tab1);
|
|
123
|
+
element.appendChild(tab2);
|
|
124
|
+
|
|
125
|
+
await element.updateComplete;
|
|
126
|
+
|
|
127
|
+
// The tabs are initialized in firstUpdated
|
|
128
|
+
// Check that activeTab getter works
|
|
129
|
+
assert.ok(element.activeTab !== undefined, "Should have activeTab getter");
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it("should render tab buttons for each tab", async () => {
|
|
133
|
+
element = await fixture("grantcodes-tabs");
|
|
134
|
+
|
|
135
|
+
const tab1 = document.createElement("grantcodes-tab");
|
|
136
|
+
tab1.label = "First";
|
|
137
|
+
const tab2 = document.createElement("grantcodes-tab");
|
|
138
|
+
tab2.label = "Second";
|
|
139
|
+
|
|
140
|
+
element.appendChild(tab1);
|
|
141
|
+
element.appendChild(tab2);
|
|
142
|
+
|
|
143
|
+
await element.updateComplete;
|
|
144
|
+
|
|
145
|
+
// Tab buttons are rendered via renderTabButtons method
|
|
146
|
+
// In a real browser, these would be visible in the shadow DOM
|
|
147
|
+
});
|
|
148
|
+
});
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { LitElement } from "lit";
|
|
2
|
+
import { html } from "lit/static-html.js";
|
|
3
|
+
import { unsafeHTML } from "lit/directives/unsafe-html.js";
|
|
4
|
+
import { cx } from "../../lib/classnames.js";
|
|
5
|
+
import { toastStyles } from "./toast.styles.js";
|
|
6
|
+
import { GrantCodesIcon } from "../icon/icon.component.js";
|
|
7
|
+
import { AlertCircle, Info, CheckCircle2, XCircle, X } from "../../icons.js";
|
|
8
|
+
|
|
9
|
+
const ICONS = {
|
|
10
|
+
info: Info,
|
|
11
|
+
success: CheckCircle2,
|
|
12
|
+
warning: AlertCircle,
|
|
13
|
+
error: XCircle,
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export class GrantCodesToast extends LitElement {
|
|
17
|
+
static dependencies = { "grancodes-icon": GrantCodesIcon };
|
|
18
|
+
static styles = [toastStyles];
|
|
19
|
+
|
|
20
|
+
static properties = {
|
|
21
|
+
variant: { type: String },
|
|
22
|
+
title: { type: String },
|
|
23
|
+
duration: { type: Number },
|
|
24
|
+
position: { type: String },
|
|
25
|
+
dismissible: { type: Boolean },
|
|
26
|
+
_visible: { type: Boolean, state: true },
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
constructor() {
|
|
30
|
+
super();
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Visual variant
|
|
34
|
+
* @type {'info' | 'success' | 'warning' | 'error'}
|
|
35
|
+
*/
|
|
36
|
+
this.variant = "info";
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Toast title
|
|
40
|
+
* @type {string}
|
|
41
|
+
*/
|
|
42
|
+
this.title = "";
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Auto-dismiss duration in milliseconds (0 = no auto-dismiss)
|
|
46
|
+
* @type {number}
|
|
47
|
+
*/
|
|
48
|
+
this.duration = 5000;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Toast position
|
|
52
|
+
* @type {'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right'}
|
|
53
|
+
*/
|
|
54
|
+
this.position = "top-right";
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Whether toast can be manually dismissed
|
|
58
|
+
* @type {boolean}
|
|
59
|
+
*/
|
|
60
|
+
this.dismissible = true;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Internal visibility state
|
|
64
|
+
* @type {boolean}
|
|
65
|
+
*/
|
|
66
|
+
this._visible = false;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Timeout ID for auto-dismiss
|
|
70
|
+
* @type {number | null}
|
|
71
|
+
*/
|
|
72
|
+
this._dismissTimeout = null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
connectedCallback() {
|
|
76
|
+
super.connectedCallback();
|
|
77
|
+
// Show toast after a brief delay for animation
|
|
78
|
+
requestAnimationFrame(() => {
|
|
79
|
+
this._visible = true;
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// Set up auto-dismiss
|
|
83
|
+
if (this.duration > 0) {
|
|
84
|
+
this._dismissTimeout = setTimeout(() => {
|
|
85
|
+
this._handleDismiss();
|
|
86
|
+
}, this.duration);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
disconnectedCallback() {
|
|
91
|
+
if (this._dismissTimeout) {
|
|
92
|
+
clearTimeout(this._dismissTimeout);
|
|
93
|
+
}
|
|
94
|
+
super.disconnectedCallback();
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
_handleDismiss() {
|
|
98
|
+
this._visible = false;
|
|
99
|
+
|
|
100
|
+
// Remove element after animation completes
|
|
101
|
+
setTimeout(() => {
|
|
102
|
+
this.dispatchEvent(
|
|
103
|
+
new CustomEvent("dismiss", {
|
|
104
|
+
bubbles: true,
|
|
105
|
+
composed: true,
|
|
106
|
+
}),
|
|
107
|
+
);
|
|
108
|
+
this.remove();
|
|
109
|
+
}, 300); // Match animation duration
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
_renderDismissButton() {
|
|
113
|
+
if (!this.dismissible) {
|
|
114
|
+
return html``;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return html`
|
|
118
|
+
<button
|
|
119
|
+
type="button"
|
|
120
|
+
class="toast__close"
|
|
121
|
+
@click=${this._handleDismiss}
|
|
122
|
+
aria-label="Dismiss notification"
|
|
123
|
+
>
|
|
124
|
+
<grantcodes-icon>${unsafeHTML(X)}</grantcodes-icon>
|
|
125
|
+
</button>
|
|
126
|
+
`;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
render() {
|
|
130
|
+
const icon = ICONS[this.variant];
|
|
131
|
+
const toastClass = cx("toast", {
|
|
132
|
+
[`toast--${this.variant}`]: true,
|
|
133
|
+
[`toast--${this.position}`]: true,
|
|
134
|
+
"toast--visible": this._visible,
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
return html`
|
|
138
|
+
<div class="${toastClass}" role="status" aria-live="polite">
|
|
139
|
+
<grantcodes-icon class="toast__icon">${unsafeHTML(icon)}</grantcodes-icon>
|
|
140
|
+
|
|
141
|
+
<div class="toast__content">
|
|
142
|
+
${this.title ? html`<div class="toast__title">${this.title}</div>` : ""}
|
|
143
|
+
<div class="toast__message">
|
|
144
|
+
<slot></slot>
|
|
145
|
+
</div>
|
|
146
|
+
</div>
|
|
147
|
+
|
|
148
|
+
${this._renderDismissButton()}
|
|
149
|
+
</div>
|
|
150
|
+
`;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Toast container for managing multiple toasts
|
|
156
|
+
*/
|
|
157
|
+
export class GrantCodesToastContainer extends LitElement {
|
|
158
|
+
static styles = [toastStyles];
|
|
159
|
+
|
|
160
|
+
static properties = {
|
|
161
|
+
position: { type: String },
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
constructor() {
|
|
165
|
+
super();
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Position of the toast container
|
|
169
|
+
* @type {'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right'}
|
|
170
|
+
*/
|
|
171
|
+
this.position = "top-right";
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
render() {
|
|
175
|
+
const containerClass = cx("toast-container", {
|
|
176
|
+
[`toast-container--${this.position}`]: true,
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
return html`
|
|
180
|
+
<div class="${containerClass}">
|
|
181
|
+
<slot></slot>
|
|
182
|
+
</div>
|
|
183
|
+
`;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { GrantCodesToast, GrantCodesToastContainer } from "./toast.component.js";
|
|
2
|
+
|
|
3
|
+
export * from "./toast.component.js";
|
|
4
|
+
export default GrantCodesToast;
|
|
5
|
+
|
|
6
|
+
customElements.define("grantcodes-toast", GrantCodesToast);
|
|
7
|
+
customElements.define("grantcodes-toast-container", GrantCodesToastContainer);
|
|
8
|
+
|
|
9
|
+
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import { html } from "lit";
|
|
2
|
+
import "./toast.js";
|
|
3
|
+
|
|
4
|
+
const meta = {
|
|
5
|
+
title: "Components/Toast",
|
|
6
|
+
component: "grantcodes-toast",
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export default meta;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Basic toast notification that auto-dismisses after 5 seconds
|
|
13
|
+
*/
|
|
14
|
+
export const Toast = {
|
|
15
|
+
render: () => html`
|
|
16
|
+
<grantcodes-toast-container position="top-right">
|
|
17
|
+
<grantcodes-toast variant="info" title="Information">
|
|
18
|
+
This is an informational toast message.
|
|
19
|
+
</grantcodes-toast>
|
|
20
|
+
</grantcodes-toast-container>
|
|
21
|
+
`,
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* All toast variants
|
|
26
|
+
*/
|
|
27
|
+
export const AllVariants = {
|
|
28
|
+
render: () => html`
|
|
29
|
+
<grantcodes-toast-container position="top-right">
|
|
30
|
+
<grantcodes-toast variant="info" title="Info" duration="0">
|
|
31
|
+
This is an info toast message.
|
|
32
|
+
</grantcodes-toast>
|
|
33
|
+
<grantcodes-toast variant="success" title="Success" duration="0">
|
|
34
|
+
Operation completed successfully!
|
|
35
|
+
</grantcodes-toast>
|
|
36
|
+
<grantcodes-toast variant="warning" title="Warning" duration="0">
|
|
37
|
+
Please review before continuing.
|
|
38
|
+
</grantcodes-toast>
|
|
39
|
+
<grantcodes-toast variant="error" title="Error" duration="0">
|
|
40
|
+
An error occurred. Please try again.
|
|
41
|
+
</grantcodes-toast>
|
|
42
|
+
</grantcodes-toast-container>
|
|
43
|
+
`,
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Toast without title
|
|
48
|
+
*/
|
|
49
|
+
export const WithoutTitle = {
|
|
50
|
+
render: () => html`
|
|
51
|
+
<grantcodes-toast-container position="top-right">
|
|
52
|
+
<grantcodes-toast variant="success" duration="0">
|
|
53
|
+
This toast has no title, just the message content.
|
|
54
|
+
</grantcodes-toast>
|
|
55
|
+
</grantcodes-toast-container>
|
|
56
|
+
`,
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Non-dismissible toast (no close button)
|
|
61
|
+
*/
|
|
62
|
+
export const NonDismissible = {
|
|
63
|
+
render: () => html`
|
|
64
|
+
<grantcodes-toast-container position="top-right">
|
|
65
|
+
<grantcodes-toast variant="info" title="Loading" ?dismissible=${false} duration="0">
|
|
66
|
+
Please wait while we process your request...
|
|
67
|
+
</grantcodes-toast>
|
|
68
|
+
</grantcodes-toast-container>
|
|
69
|
+
`,
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Different positions for toast container
|
|
74
|
+
*/
|
|
75
|
+
export const Positions = {
|
|
76
|
+
render: () => html`
|
|
77
|
+
<div style="position: relative; height: 400px; border: 1px solid #ccc;">
|
|
78
|
+
<grantcodes-toast-container position="top-left">
|
|
79
|
+
<grantcodes-toast variant="info" title="Top Left" duration="0">
|
|
80
|
+
Toast in top-left corner
|
|
81
|
+
</grantcodes-toast>
|
|
82
|
+
</grantcodes-toast-container>
|
|
83
|
+
|
|
84
|
+
<grantcodes-toast-container position="top-center">
|
|
85
|
+
<grantcodes-toast variant="success" title="Top Center" duration="0">
|
|
86
|
+
Toast in top-center
|
|
87
|
+
</grantcodes-toast>
|
|
88
|
+
</grantcodes-toast-container>
|
|
89
|
+
|
|
90
|
+
<grantcodes-toast-container position="top-right">
|
|
91
|
+
<grantcodes-toast variant="warning" title="Top Right" duration="0">
|
|
92
|
+
Toast in top-right corner
|
|
93
|
+
</grantcodes-toast>
|
|
94
|
+
</grantcodes-toast-container>
|
|
95
|
+
|
|
96
|
+
<grantcodes-toast-container position="bottom-left">
|
|
97
|
+
<grantcodes-toast variant="info" title="Bottom Left" duration="0">
|
|
98
|
+
Toast in bottom-left corner
|
|
99
|
+
</grantcodes-toast>
|
|
100
|
+
</grantcodes-toast-container>
|
|
101
|
+
|
|
102
|
+
<grantcodes-toast-container position="bottom-center">
|
|
103
|
+
<grantcodes-toast variant="success" title="Bottom Center" duration="0">
|
|
104
|
+
Toast in bottom-center
|
|
105
|
+
</grantcodes-toast>
|
|
106
|
+
</grantcodes-toast-container>
|
|
107
|
+
|
|
108
|
+
<grantcodes-toast-container position="bottom-right">
|
|
109
|
+
<grantcodes-toast variant="error" title="Bottom Right" duration="0">
|
|
110
|
+
Toast in bottom-right corner
|
|
111
|
+
</grantcodes-toast>
|
|
112
|
+
</grantcodes-toast-container>
|
|
113
|
+
</div>
|
|
114
|
+
`,
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Stacked toasts showing multiple notifications
|
|
119
|
+
*/
|
|
120
|
+
export const Stacked = {
|
|
121
|
+
render: () => html`
|
|
122
|
+
<grantcodes-toast-container position="top-right">
|
|
123
|
+
<grantcodes-toast variant="info" title="First notification" duration="0">
|
|
124
|
+
This is the first toast message.
|
|
125
|
+
</grantcodes-toast>
|
|
126
|
+
<grantcodes-toast variant="success" title="Second notification" duration="0">
|
|
127
|
+
This is the second toast message.
|
|
128
|
+
</grantcodes-toast>
|
|
129
|
+
<grantcodes-toast variant="warning" title="Third notification" duration="0">
|
|
130
|
+
This is the third toast message.
|
|
131
|
+
</grantcodes-toast>
|
|
132
|
+
</grantcodes-toast-container>
|
|
133
|
+
`,
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Interactive demo with button to trigger toasts
|
|
138
|
+
*/
|
|
139
|
+
export const Interactive = {
|
|
140
|
+
render: () => html`
|
|
141
|
+
<div>
|
|
142
|
+
<button
|
|
143
|
+
@click=${() => {
|
|
144
|
+
const container = document.querySelector("grantcodes-toast-container");
|
|
145
|
+
if (!container) {
|
|
146
|
+
const newContainer = document.createElement("grantcodes-toast-container");
|
|
147
|
+
newContainer.position = "top-right";
|
|
148
|
+
document.body.appendChild(newContainer);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const toast = document.createElement("grantcodes-toast");
|
|
152
|
+
toast.variant = "success";
|
|
153
|
+
toast.title = "Toast Created!";
|
|
154
|
+
toast.textContent = "This toast was created dynamically.";
|
|
155
|
+
toast.duration = 3000;
|
|
156
|
+
|
|
157
|
+
const finalContainer =
|
|
158
|
+
document.querySelector("grantcodes-toast-container") ||
|
|
159
|
+
document.body.appendChild(document.createElement("grantcodes-toast-container"));
|
|
160
|
+
finalContainer.appendChild(toast);
|
|
161
|
+
}}
|
|
162
|
+
>
|
|
163
|
+
Show Toast
|
|
164
|
+
</button>
|
|
165
|
+
</div>
|
|
166
|
+
`,
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
|