@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,174 @@
|
|
|
1
|
+
import { describe, it, afterEach } from "node:test";
|
|
2
|
+
import { strict as assert } from "node:assert";
|
|
3
|
+
import { fixture, cleanup, click } from "../../test-utils/index.js";
|
|
4
|
+
import "./app-bar.js";
|
|
5
|
+
|
|
6
|
+
describe("App Bar 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-app-bar");
|
|
15
|
+
assert.ok(element, "Element should be created");
|
|
16
|
+
assert.ok(element.shadowRoot, "Element should have shadow root");
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it("should not be sticky by default", async () => {
|
|
20
|
+
element = await fixture("grantcodes-app-bar");
|
|
21
|
+
assert.strictEqual(element.sticky, false, "Should not be sticky by default");
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it("should not be transparent by default", async () => {
|
|
25
|
+
element = await fixture("grantcodes-app-bar");
|
|
26
|
+
assert.strictEqual(element.transparent, false, "Should not be transparent by default");
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("should render header element", async () => {
|
|
30
|
+
element = await fixture("grantcodes-app-bar");
|
|
31
|
+
const header = element.shadowRoot.querySelector("header");
|
|
32
|
+
assert.ok(header, "Header element should exist");
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it("should apply sticky class when sticky", async () => {
|
|
36
|
+
element = await fixture("grantcodes-app-bar", {
|
|
37
|
+
sticky: true,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const appBar = element.shadowRoot.querySelector(".app-bar--sticky");
|
|
41
|
+
assert.ok(appBar, "Should have sticky class");
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it("should apply transparent class when transparent", async () => {
|
|
45
|
+
element = await fixture("grantcodes-app-bar", {
|
|
46
|
+
transparent: true,
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
const appBar = element.shadowRoot.querySelector(".app-bar--transparent");
|
|
50
|
+
assert.ok(appBar, "Should have transparent class");
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("should have logo slot", async () => {
|
|
54
|
+
element = await fixture("grantcodes-app-bar");
|
|
55
|
+
const logoSlot = element.shadowRoot.querySelector('slot[name="logo"]');
|
|
56
|
+
assert.ok(logoSlot, "Logo slot should exist");
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("should have nav slot", async () => {
|
|
60
|
+
element = await fixture("grantcodes-app-bar");
|
|
61
|
+
const navSlot = element.shadowRoot.querySelector('slot[name="nav"]');
|
|
62
|
+
assert.ok(navSlot, "Nav slot should exist");
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it("should have actions slot", async () => {
|
|
66
|
+
element = await fixture("grantcodes-app-bar");
|
|
67
|
+
const actionsSlot = element.shadowRoot.querySelector('slot[name="actions"]');
|
|
68
|
+
assert.ok(actionsSlot, "Actions slot should exist");
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it("should render mobile menu button", async () => {
|
|
72
|
+
element = await fixture("grantcodes-app-bar");
|
|
73
|
+
const menuButton = element.shadowRoot.querySelector(".app-bar__menu-button");
|
|
74
|
+
assert.ok(menuButton, "Mobile menu button should exist");
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("should have mobile menu closed by default", async () => {
|
|
78
|
+
element = await fixture("grantcodes-app-bar");
|
|
79
|
+
assert.strictEqual(element._mobileMenuOpen, false, "Mobile menu should be closed by default");
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it("should not render mobile nav when closed", async () => {
|
|
83
|
+
element = await fixture("grantcodes-app-bar");
|
|
84
|
+
const mobileNav = element.shadowRoot.querySelector(".app-bar__mobile-nav");
|
|
85
|
+
assert.ok(!mobileNav, "Mobile nav should not be rendered when closed");
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it("should render mobile nav when opened", async () => {
|
|
89
|
+
element = await fixture("grantcodes-app-bar");
|
|
90
|
+
element._mobileMenuOpen = true;
|
|
91
|
+
|
|
92
|
+
await element.updateComplete;
|
|
93
|
+
|
|
94
|
+
const mobileNav = element.shadowRoot.querySelector(".app-bar__nav--mobile-open");
|
|
95
|
+
assert.ok(mobileNav, "Mobile nav should be rendered when open");
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it("should toggle mobile menu when button is clicked", async () => {
|
|
99
|
+
element = await fixture("grantcodes-app-bar");
|
|
100
|
+
const menuButton = element.shadowRoot.querySelector(".app-bar__menu-button");
|
|
101
|
+
|
|
102
|
+
assert.strictEqual(element._mobileMenuOpen, false, "Should start closed");
|
|
103
|
+
|
|
104
|
+
click(menuButton);
|
|
105
|
+
await element.updateComplete;
|
|
106
|
+
|
|
107
|
+
assert.strictEqual(element._mobileMenuOpen, true, "Should be open after click");
|
|
108
|
+
|
|
109
|
+
click(menuButton);
|
|
110
|
+
await element.updateComplete;
|
|
111
|
+
|
|
112
|
+
assert.strictEqual(element._mobileMenuOpen, false, "Should be closed after second click");
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it("should emit menu-toggle event when menu is toggled", async () => {
|
|
116
|
+
element = await fixture("grantcodes-app-bar");
|
|
117
|
+
|
|
118
|
+
let toggledState = null;
|
|
119
|
+
element.addEventListener("menu-toggle", (e) => {
|
|
120
|
+
toggledState = e.detail.open;
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
const menuButton = element.shadowRoot.querySelector(".app-bar__menu-button");
|
|
124
|
+
click(menuButton);
|
|
125
|
+
|
|
126
|
+
await element.updateComplete;
|
|
127
|
+
|
|
128
|
+
assert.strictEqual(toggledState, true, "Toggle event should fire with open state");
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it("should have aria-label on menu button", async () => {
|
|
132
|
+
element = await fixture("grantcodes-app-bar");
|
|
133
|
+
const menuButton = element.shadowRoot.querySelector(".app-bar__menu-button");
|
|
134
|
+
assert.ok(menuButton.hasAttribute("aria-label"), "Menu button should have aria-label");
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it("should have aria-expanded on menu button", async () => {
|
|
138
|
+
element = await fixture("grantcodes-app-bar");
|
|
139
|
+
const menuButton = element.shadowRoot.querySelector(".app-bar__menu-button");
|
|
140
|
+
assert.ok(
|
|
141
|
+
menuButton.hasAttribute("aria-expanded"),
|
|
142
|
+
"Menu button should have aria-expanded",
|
|
143
|
+
);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it("should update aria-expanded when menu is toggled", async () => {
|
|
147
|
+
element = await fixture("grantcodes-app-bar");
|
|
148
|
+
const menuButton = element.shadowRoot.querySelector(".app-bar__menu-button");
|
|
149
|
+
|
|
150
|
+
assert.strictEqual(
|
|
151
|
+
menuButton.getAttribute("aria-expanded"),
|
|
152
|
+
"false",
|
|
153
|
+
"Should be false initially",
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
click(menuButton);
|
|
157
|
+
await element.updateComplete;
|
|
158
|
+
|
|
159
|
+
assert.strictEqual(
|
|
160
|
+
menuButton.getAttribute("aria-expanded"),
|
|
161
|
+
"true",
|
|
162
|
+
"Should be true when open",
|
|
163
|
+
);
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it("should have nav element with aria-label", async () => {
|
|
167
|
+
element = await fixture("grantcodes-app-bar");
|
|
168
|
+
const nav = element.shadowRoot.querySelector("nav");
|
|
169
|
+
assert.ok(nav, "Nav element should exist");
|
|
170
|
+
assert.ok(nav.hasAttribute("aria-label"), "Nav should have aria-label");
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { LitElement } from "lit";
|
|
2
|
+
import { html } from "lit/static-html.js";
|
|
3
|
+
import { avatarStyles } from "./avatar.styles.js";
|
|
4
|
+
|
|
5
|
+
export class GrantCodesAvatar extends LitElement {
|
|
6
|
+
static properties = {
|
|
7
|
+
src: { type: String },
|
|
8
|
+
name: { type: String },
|
|
9
|
+
alt: { type: String },
|
|
10
|
+
initials: { type: String },
|
|
11
|
+
size: { type: String },
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
static styles = [avatarStyles];
|
|
15
|
+
|
|
16
|
+
constructor() {
|
|
17
|
+
super();
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Image source for the avatar
|
|
21
|
+
*/
|
|
22
|
+
this.src = "";
|
|
23
|
+
/**
|
|
24
|
+
* The full name of the person
|
|
25
|
+
*/
|
|
26
|
+
this.name = "";
|
|
27
|
+
/**
|
|
28
|
+
* Alt attribute text
|
|
29
|
+
*/
|
|
30
|
+
this.alt = "";
|
|
31
|
+
/**
|
|
32
|
+
* Optional initials, used when there is no src set
|
|
33
|
+
*/
|
|
34
|
+
this.initials = "";
|
|
35
|
+
/**
|
|
36
|
+
* Size variant: 'small', 'medium', or 'large'
|
|
37
|
+
*/
|
|
38
|
+
this.size = "medium";
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Helper function to get initials from the name (if not already provided)
|
|
43
|
+
*
|
|
44
|
+
* @return {String} First 2 initials of the passed name
|
|
45
|
+
*/
|
|
46
|
+
getInitials() {
|
|
47
|
+
if (this.initials) return this.initials;
|
|
48
|
+
if (this.name) {
|
|
49
|
+
const [first = "", second = ""] = this.name.trim().split(" ");
|
|
50
|
+
const firstInitial = first ? first[0] : "";
|
|
51
|
+
const secondInitial = second ? second[0] : "";
|
|
52
|
+
return `${firstInitial}${secondInitial}`;
|
|
53
|
+
}
|
|
54
|
+
return "";
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
getAlt() {
|
|
58
|
+
if (this.alt) return this.alt;
|
|
59
|
+
return `${this.name || this.initials} avatar`;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** @returns {import('lit').TemplateResult} */
|
|
63
|
+
getImg() {
|
|
64
|
+
if (this.src) {
|
|
65
|
+
return html`<img src=${this.src} alt=${this.getAlt()} />`;
|
|
66
|
+
}
|
|
67
|
+
return html``;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** @returns {import('lit').TemplateResult} */
|
|
71
|
+
render() {
|
|
72
|
+
const content = this.src
|
|
73
|
+
? this.getImg()
|
|
74
|
+
: html`<span class="avatar__initials">${this.getInitials()}</span>`;
|
|
75
|
+
const sizeClass = this.size ? `avatar--${this.size}` : "";
|
|
76
|
+
return html` <div class="avatar ${sizeClass}">${content}</div> `;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { getStorybookHelpers } from "@wc-toolkit/storybook-helpers";
|
|
2
|
+
const { events, args, argTypes, template } =
|
|
3
|
+
getStorybookHelpers("grantcodes-avatar");
|
|
4
|
+
import "./avatar.js";
|
|
5
|
+
|
|
6
|
+
const meta = {
|
|
7
|
+
title: "Components/Avatar",
|
|
8
|
+
component: "grantcodes-avatar",
|
|
9
|
+
args: {
|
|
10
|
+
...args,
|
|
11
|
+
src: "https://placehold.co/160x160",
|
|
12
|
+
name: "Tommy Tobasco",
|
|
13
|
+
alt: "Tommy Tomasco Avatar",
|
|
14
|
+
initials: "2T",
|
|
15
|
+
},
|
|
16
|
+
argTypes,
|
|
17
|
+
render: (args) => template(args),
|
|
18
|
+
parameters: {
|
|
19
|
+
actions: {
|
|
20
|
+
handles: events,
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export default meta;
|
|
26
|
+
|
|
27
|
+
export const Avatar = {};
|
|
28
|
+
|
|
29
|
+
export const Initials = {
|
|
30
|
+
args: {
|
|
31
|
+
src: undefined,
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { css } from "lit";
|
|
2
|
+
|
|
3
|
+
export const avatarStyles = css`
|
|
4
|
+
|
|
5
|
+
/* Inlined component-base mixin */
|
|
6
|
+
*,
|
|
7
|
+
*::before,
|
|
8
|
+
*::after {
|
|
9
|
+
box-sizing: border-box;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
:host {
|
|
13
|
+
display: block;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.avatar {
|
|
17
|
+
display: flex;
|
|
18
|
+
align-items: center;
|
|
19
|
+
justify-content: center;
|
|
20
|
+
width: 3rem;
|
|
21
|
+
height: 3rem;
|
|
22
|
+
border-radius: 50%;
|
|
23
|
+
overflow: hidden;
|
|
24
|
+
background-color: var(--g-color-brand-purple-200);
|
|
25
|
+
color: var(--g-color-brand-purple-900);
|
|
26
|
+
text-align: center;
|
|
27
|
+
font-size: 1.2rem;
|
|
28
|
+
font-weight: bold;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.avatar__image {
|
|
32
|
+
width: 100%;
|
|
33
|
+
height: 100%;
|
|
34
|
+
object-fit: cover;
|
|
35
|
+
object-position: center;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.avatar__fallback {
|
|
39
|
+
line-height: 1;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
`;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { describe, it, afterEach } from "node:test";
|
|
2
|
+
import { strict as assert } from "node:assert";
|
|
3
|
+
import { fixture, cleanup } from "../../test-utils/index.js";
|
|
4
|
+
import "./avatar.js";
|
|
5
|
+
|
|
6
|
+
describe("Avatar Component", () => {
|
|
7
|
+
let element;
|
|
8
|
+
|
|
9
|
+
afterEach(() => {
|
|
10
|
+
cleanup(element);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it("should render with default properties", async () => {
|
|
14
|
+
element = await fixture("grantcodes-avatar");
|
|
15
|
+
assert.ok(element, "Element should be created");
|
|
16
|
+
assert.ok(element.shadowRoot, "Element should have shadow root");
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it("should display initials when name is provided", async () => {
|
|
20
|
+
element = await fixture("grantcodes-avatar", {
|
|
21
|
+
name: "John Doe",
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const initialsElement = element.shadowRoot.querySelector(".avatar__initials");
|
|
25
|
+
assert.ok(initialsElement, "Initials element should exist");
|
|
26
|
+
assert.strictEqual(
|
|
27
|
+
initialsElement.textContent,
|
|
28
|
+
"JD",
|
|
29
|
+
"Should display correct initials",
|
|
30
|
+
);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("should handle single name", async () => {
|
|
34
|
+
element = await fixture("grantcodes-avatar", {
|
|
35
|
+
name: "John",
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
const initialsElement = element.shadowRoot.querySelector(".avatar__initials");
|
|
39
|
+
assert.strictEqual(
|
|
40
|
+
initialsElement.textContent,
|
|
41
|
+
"J",
|
|
42
|
+
"Should display single initial",
|
|
43
|
+
);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("should display image when src is provided", async () => {
|
|
47
|
+
element = await fixture("grantcodes-avatar", {
|
|
48
|
+
src: "https://example.com/avatar.jpg",
|
|
49
|
+
alt: "User avatar",
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const imgElement = element.shadowRoot.querySelector("img");
|
|
53
|
+
assert.ok(imgElement, "Image element should exist");
|
|
54
|
+
assert.strictEqual(imgElement.src, "https://example.com/avatar.jpg");
|
|
55
|
+
assert.strictEqual(imgElement.alt, "User avatar");
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it("should apply size variant", async () => {
|
|
59
|
+
element = await fixture("grantcodes-avatar", {
|
|
60
|
+
size: "large",
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
const avatarElement = element.shadowRoot.querySelector(".avatar");
|
|
64
|
+
assert.ok(
|
|
65
|
+
avatarElement.classList.contains("avatar--large"),
|
|
66
|
+
"Should have large size class",
|
|
67
|
+
);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("should support different sizes", async () => {
|
|
71
|
+
const sizes = ["small", "medium", "large"];
|
|
72
|
+
|
|
73
|
+
for (const size of sizes) {
|
|
74
|
+
element = await fixture("grantcodes-avatar", { size });
|
|
75
|
+
const avatarElement = element.shadowRoot.querySelector(".avatar");
|
|
76
|
+
assert.ok(
|
|
77
|
+
avatarElement.classList.contains(`avatar--${size}`),
|
|
78
|
+
`Should have ${size} size class`,
|
|
79
|
+
);
|
|
80
|
+
cleanup(element);
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { LitElement } from "lit";
|
|
2
|
+
import { html } from "lit/static-html.js";
|
|
3
|
+
import { cx } from "../../lib/classnames.js";
|
|
4
|
+
import { badgeStyles } from "./badge.styles.js";
|
|
5
|
+
|
|
6
|
+
export class GrantCodesBadge extends LitElement {
|
|
7
|
+
static styles = [badgeStyles];
|
|
8
|
+
|
|
9
|
+
static properties = {
|
|
10
|
+
variant: { type: String },
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
constructor() {
|
|
14
|
+
super();
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Visual variant of the badge
|
|
18
|
+
* @type {'primary' | 'success' | 'warning' | 'error' | 'info' | 'neutral'}
|
|
19
|
+
*/
|
|
20
|
+
this.variant = "neutral";
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
render() {
|
|
24
|
+
const badgeClass = cx("badge", {
|
|
25
|
+
[`badge--${this.variant}`]: Boolean(this.variant),
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
return html`
|
|
29
|
+
<span class="${badgeClass}">
|
|
30
|
+
<span class="badge__content">
|
|
31
|
+
<slot></slot>
|
|
32
|
+
</span>
|
|
33
|
+
</span>
|
|
34
|
+
`;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { html } from "lit";
|
|
2
|
+
import "./badge.js";
|
|
3
|
+
|
|
4
|
+
const meta = {
|
|
5
|
+
title: "Components/Badge",
|
|
6
|
+
component: "grantcodes-badge",
|
|
7
|
+
args: {
|
|
8
|
+
variant: "neutral",
|
|
9
|
+
},
|
|
10
|
+
argTypes: {
|
|
11
|
+
variant: {
|
|
12
|
+
control: "select",
|
|
13
|
+
options: ["primary", "success", "warning", "error", "info", "neutral"],
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
render: (args) => html`
|
|
17
|
+
<grantcodes-badge variant=${args.variant} style=${args.style}>
|
|
18
|
+
Badge
|
|
19
|
+
</grantcodes-badge>
|
|
20
|
+
`,
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export default meta;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Default badge with neutral variant
|
|
27
|
+
*/
|
|
28
|
+
export const Badge = {};
|
|
29
|
+
|
|
30
|
+
export const Primary = {
|
|
31
|
+
args: {
|
|
32
|
+
variant: "primary",
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export const Success = {
|
|
37
|
+
args: {
|
|
38
|
+
variant: "success",
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export const Warning = {
|
|
43
|
+
args: {
|
|
44
|
+
variant: "warning",
|
|
45
|
+
},
|
|
46
|
+
};
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { css } from "lit";
|
|
2
|
+
|
|
3
|
+
export const badgeStyles = css`
|
|
4
|
+
*,
|
|
5
|
+
*::before,
|
|
6
|
+
*::after {
|
|
7
|
+
box-sizing: border-box;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
:host {
|
|
11
|
+
display: inline-block;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.badge {
|
|
15
|
+
display: inline-flex;
|
|
16
|
+
align-items: center;
|
|
17
|
+
gap: 0.375rem;
|
|
18
|
+
padding-inline: 0.625em;
|
|
19
|
+
padding-block: 0.375em;
|
|
20
|
+
border-radius: var(--g-theme-border-radius-md, 0.375rem);
|
|
21
|
+
font-size: var(--g-typography-font-size-14);
|
|
22
|
+
font-weight: 500;
|
|
23
|
+
line-height: 1;
|
|
24
|
+
white-space: nowrap;
|
|
25
|
+
border-width: 1px;
|
|
26
|
+
border-style: solid;
|
|
27
|
+
transition: all 0.2s ease;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
/* Soft style variants */
|
|
33
|
+
.badge--primary {
|
|
34
|
+
background-color: color-mix(
|
|
35
|
+
in srgb,
|
|
36
|
+
var(--g-color-brand-purple-500) 15%,
|
|
37
|
+
transparent
|
|
38
|
+
);
|
|
39
|
+
color: var(--g-color-brand-purple-700);
|
|
40
|
+
border-color: transparent;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.badge--success {
|
|
44
|
+
background-color: color-mix(
|
|
45
|
+
in srgb,
|
|
46
|
+
var(--g-color-utility-green-500) 15%,
|
|
47
|
+
transparent
|
|
48
|
+
);
|
|
49
|
+
color: var(--g-color-utility-green-700);
|
|
50
|
+
border-color: transparent;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.badge--warning {
|
|
54
|
+
background-color: color-mix(
|
|
55
|
+
in srgb,
|
|
56
|
+
var(--g-color-utility-yellow-500) 15%,
|
|
57
|
+
transparent
|
|
58
|
+
);
|
|
59
|
+
color: var(--g-color-utility-yellow-700);
|
|
60
|
+
border-color: transparent;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.badge--error {
|
|
64
|
+
background-color: color-mix(
|
|
65
|
+
in srgb,
|
|
66
|
+
var(--g-color-utility-red-500) 15%,
|
|
67
|
+
transparent
|
|
68
|
+
);
|
|
69
|
+
color: var(--g-color-utility-red-700);
|
|
70
|
+
border-color: transparent;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.badge--info {
|
|
74
|
+
background-color: color-mix(
|
|
75
|
+
in srgb,
|
|
76
|
+
var(--g-color-utility-blue-500) 15%,
|
|
77
|
+
transparent
|
|
78
|
+
);
|
|
79
|
+
color: var(--g-color-utility-blue-700);
|
|
80
|
+
border-color: transparent;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.badge--neutral {
|
|
84
|
+
background-color: var(--g-color-brand-purple-100);
|
|
85
|
+
color: var(--g-color-brand-purple-800);
|
|
86
|
+
border-color: transparent;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.badge__content {
|
|
90
|
+
display: inline-flex;
|
|
91
|
+
align-items: center;
|
|
92
|
+
gap: 0.25em;
|
|
93
|
+
}
|
|
94
|
+
`;
|