@grantcodes/ui 2.0.0-beta4 → 2.0.2
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 +125 -0
- package/README.md +6 -8
- package/custom-elements.json +4273 -0
- package/package.json +79 -88
- 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/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 -1
- 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 -17
- 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/tab.component.js +0 -1
- package/dist/components/tabs/tab.js +0 -1
- package/dist/components/tabs/tabs-item.component.js +0 -1
- package/dist/components/tabs/tabs-panel.component.js +0 -10
- package/dist/components/tabs/tabs-panel.js +0 -1
- package/dist/components/tabs/tabs-tab.component.js +0 -12
- package/dist/components/tabs/tabs-tab.js +0 -1
- package/dist/components/tabs/tabs.component.js +0 -28
- 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/react.js +0 -1
|
@@ -0,0 +1,144 @@
|
|
|
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 "./dropdown.js";
|
|
5
|
+
|
|
6
|
+
describe("Dropdown 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-dropdown");
|
|
15
|
+
assert.ok(element, "Element should be created");
|
|
16
|
+
assert.ok(element.shadowRoot, "Element should have shadow root");
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it("should be closed by default", async () => {
|
|
20
|
+
element = await fixture("grantcodes-dropdown");
|
|
21
|
+
assert.strictEqual(element.open, false, "Should be closed by default");
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it("should have bottom-start placement by default", async () => {
|
|
25
|
+
element = await fixture("grantcodes-dropdown");
|
|
26
|
+
assert.strictEqual(element.placement, "bottom-start", "Default placement should be bottom-start");
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("should have trigger slot", async () => {
|
|
30
|
+
element = await fixture("grantcodes-dropdown");
|
|
31
|
+
const slot = element.shadowRoot.querySelector('slot[name="trigger"]');
|
|
32
|
+
assert.ok(slot, "Trigger slot should exist");
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it("should have menu slot", async () => {
|
|
36
|
+
element = await fixture("grantcodes-dropdown");
|
|
37
|
+
const slot = element.shadowRoot.querySelector('slot[name="menu"]');
|
|
38
|
+
assert.ok(slot, "Menu slot should exist");
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("should render menu element", async () => {
|
|
42
|
+
element = await fixture("grantcodes-dropdown");
|
|
43
|
+
const menu = element.shadowRoot.querySelector(".dropdown__menu");
|
|
44
|
+
assert.ok(menu, "Menu element should be rendered");
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it("should have role=menu on menu", async () => {
|
|
48
|
+
element = await fixture("grantcodes-dropdown");
|
|
49
|
+
const menu = element.shadowRoot.querySelector(".dropdown__menu");
|
|
50
|
+
assert.strictEqual(menu.getAttribute("role"), "menu", "Menu should have role");
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("should apply placement class to menu", async () => {
|
|
54
|
+
element = await fixture("grantcodes-dropdown", {
|
|
55
|
+
placement: "top-end",
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const menu = element.shadowRoot.querySelector(".dropdown__menu--top-end");
|
|
59
|
+
assert.ok(menu, "Menu should have placement class");
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("should generate unique IDs", async () => {
|
|
63
|
+
const element1 = await fixture("grantcodes-dropdown");
|
|
64
|
+
const element2 = await fixture("grantcodes-dropdown");
|
|
65
|
+
|
|
66
|
+
assert.notStrictEqual(element1.id, element2.id, "IDs should be unique");
|
|
67
|
+
|
|
68
|
+
cleanup(element1);
|
|
69
|
+
cleanup(element2);
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
describe("Dropdown Item Component", () => {
|
|
74
|
+
let element;
|
|
75
|
+
|
|
76
|
+
afterEach(() => {
|
|
77
|
+
cleanup(element);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it("should render dropdown item", async () => {
|
|
81
|
+
element = await fixture("grantcodes-dropdown-item");
|
|
82
|
+
assert.ok(element, "Element should be created");
|
|
83
|
+
assert.ok(element.shadowRoot, "Element should have shadow root");
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it("should not be disabled by default", async () => {
|
|
87
|
+
element = await fixture("grantcodes-dropdown-item");
|
|
88
|
+
assert.strictEqual(element.disabled, false, "Should not be disabled by default");
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it("should render with disabled class when disabled", async () => {
|
|
92
|
+
element = await fixture("grantcodes-dropdown-item", {
|
|
93
|
+
disabled: true,
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
const item = element.shadowRoot.querySelector(".dropdown-item--disabled");
|
|
97
|
+
assert.ok(item, "Should have disabled class");
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it("should emit select event when clicked", async () => {
|
|
101
|
+
element = await fixture("grantcodes-dropdown-item");
|
|
102
|
+
|
|
103
|
+
let selected = false;
|
|
104
|
+
element.addEventListener("select", () => {
|
|
105
|
+
selected = true;
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
const item = element.shadowRoot.querySelector(".dropdown-item");
|
|
109
|
+
click(item);
|
|
110
|
+
|
|
111
|
+
assert.ok(selected, "Select event should have fired");
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it("should not emit select event when disabled", async () => {
|
|
115
|
+
element = await fixture("grantcodes-dropdown-item", {
|
|
116
|
+
disabled: true,
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
let selected = false;
|
|
120
|
+
element.addEventListener("select", () => {
|
|
121
|
+
selected = true;
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
const item = element.shadowRoot.querySelector(".dropdown-item");
|
|
125
|
+
click(item);
|
|
126
|
+
|
|
127
|
+
assert.ok(!selected, "Select event should not fire when disabled");
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it("should render slotted content", async () => {
|
|
131
|
+
element = await fixture("grantcodes-dropdown-item");
|
|
132
|
+
element.textContent = "Menu Item";
|
|
133
|
+
|
|
134
|
+
await element.updateComplete;
|
|
135
|
+
|
|
136
|
+
assert.strictEqual(element.textContent, "Menu Item", "Slotted content should be rendered");
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it("should have dropdown-item wrapper", async () => {
|
|
140
|
+
element = await fixture("grantcodes-dropdown-item");
|
|
141
|
+
const item = element.shadowRoot.querySelector(".dropdown-item");
|
|
142
|
+
assert.ok(item, "Dropdown item wrapper should exist");
|
|
143
|
+
});
|
|
144
|
+
});
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { LitElement } from "lit";
|
|
2
|
+
import { html } from "lit/static-html.js";
|
|
3
|
+
import { cx } from "../../lib/classnames.js";
|
|
4
|
+
import { dropzoneStyles } from "./dropzone.styles.js";
|
|
5
|
+
|
|
6
|
+
export class GrantCodesDropzone extends LitElement {
|
|
7
|
+
static styles = [dropzoneStyles];
|
|
8
|
+
|
|
9
|
+
static properties = {
|
|
10
|
+
fullscreenOnDrag: { type: Boolean, reflect: true },
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
constructor() {
|
|
14
|
+
super();
|
|
15
|
+
|
|
16
|
+
this.fullscreenOnDrag = false;
|
|
17
|
+
|
|
18
|
+
/** @type {HTMLInputElement[]} */
|
|
19
|
+
this._input = [];
|
|
20
|
+
|
|
21
|
+
this._fullscreen = false;
|
|
22
|
+
|
|
23
|
+
/** @type {string} */
|
|
24
|
+
this._placeholder = "";
|
|
25
|
+
|
|
26
|
+
/** @type {number} */
|
|
27
|
+
this._dragDepth = 0;
|
|
28
|
+
|
|
29
|
+
/** @type {number | null} */
|
|
30
|
+
this._fullscreenTimeout = null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
_enableFullscreen = () => {
|
|
34
|
+
if (this.fullscreenOnDrag) {
|
|
35
|
+
this._dragDepth++;
|
|
36
|
+
this._fullscreen = true;
|
|
37
|
+
this._clearFullscreenTimeout();
|
|
38
|
+
// Fallback: disable fullscreen after 3 seconds of no activity
|
|
39
|
+
this._fullscreenTimeout = setTimeout(() => {
|
|
40
|
+
this._forceDisableFullscreen();
|
|
41
|
+
}, 3000);
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
_disableFullscreen = () => {
|
|
46
|
+
if (this.fullscreenOnDrag) {
|
|
47
|
+
this._dragDepth--;
|
|
48
|
+
// Only disable if we've left all drag boundaries
|
|
49
|
+
if (this._dragDepth <= 0) {
|
|
50
|
+
this._forceDisableFullscreen();
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
_forceDisableFullscreen = () => {
|
|
56
|
+
this._fullscreen = false;
|
|
57
|
+
this._dragDepth = 0;
|
|
58
|
+
this._clearFullscreenTimeout();
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
_clearFullscreenTimeout = () => {
|
|
62
|
+
if (this._fullscreenTimeout !== null) {
|
|
63
|
+
clearTimeout(this._fullscreenTimeout);
|
|
64
|
+
this._fullscreenTimeout = null;
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
_handleDrop = () => {
|
|
69
|
+
this._forceDisableFullscreen();
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
_handleDragOver = (e) => {
|
|
73
|
+
e.preventDefault();
|
|
74
|
+
// Reset timeout on drag activity
|
|
75
|
+
if (this._fullscreen && this._fullscreenTimeout) {
|
|
76
|
+
this._clearFullscreenTimeout();
|
|
77
|
+
this._fullscreenTimeout = setTimeout(() => {
|
|
78
|
+
this._forceDisableFullscreen();
|
|
79
|
+
}, 3000);
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
connectedCallback() {
|
|
84
|
+
super.connectedCallback();
|
|
85
|
+
document.addEventListener("dragenter", this._enableFullscreen);
|
|
86
|
+
document.addEventListener("dragend", this._disableFullscreen);
|
|
87
|
+
document.addEventListener("dragleave", this._disableFullscreen);
|
|
88
|
+
document.addEventListener("drop", this._handleDrop);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
disconnectedCallback() {
|
|
92
|
+
document.removeEventListener("dragenter", this._enableFullscreen);
|
|
93
|
+
document.removeEventListener("dragend", this._disableFullscreen);
|
|
94
|
+
document.removeEventListener("dragleave", this._disableFullscreen);
|
|
95
|
+
document.removeEventListener("drop", this._handleDrop);
|
|
96
|
+
this._clearFullscreenTimeout();
|
|
97
|
+
super.disconnectedCallback();
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
firstUpdated() {
|
|
101
|
+
// Find input elements in the slot
|
|
102
|
+
const slot = this.renderRoot.querySelector("slot");
|
|
103
|
+
if (slot) {
|
|
104
|
+
const assignedElements = slot.assignedElements();
|
|
105
|
+
this._input = assignedElements.filter(
|
|
106
|
+
(el) => el.tagName === "INPUT" && el.type === "file",
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (this._input.length === 0) {
|
|
111
|
+
// In unit tests, allow rendering without an input to reduce friction
|
|
112
|
+
const isTestEnv = typeof process !== "undefined" && process?.env?.NODE_ENV === "test";
|
|
113
|
+
if (!isTestEnv) {
|
|
114
|
+
throw new Error("No file input found");
|
|
115
|
+
}
|
|
116
|
+
this._placeholder = "";
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
this._placeholder = this._input[0].placeholder;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
render() {
|
|
123
|
+
const dropzoneClass = cx("dropzone", {
|
|
124
|
+
"dropzone--fullscreen": this._fullscreen,
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
return html`
|
|
128
|
+
<div
|
|
129
|
+
class=${dropzoneClass}
|
|
130
|
+
@mouseleave=${this._forceDisableFullscreen}
|
|
131
|
+
@dragend=${this._disableFullscreen}
|
|
132
|
+
@dragleave=${this._disableFullscreen}
|
|
133
|
+
@drop=${this._handleDrop}
|
|
134
|
+
@dragover=${this._handleDragOver}
|
|
135
|
+
>
|
|
136
|
+
<slot></slot>
|
|
137
|
+
<span class="dropzone__placeholder">${this._placeholder}</span>
|
|
138
|
+
</div>
|
|
139
|
+
`;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { html } from "lit/static-html.js";
|
|
2
|
+
import { getStorybookHelpers } from "@wc-toolkit/storybook-helpers";
|
|
3
|
+
const { events, args, argTypes, template } = getStorybookHelpers(
|
|
4
|
+
"grantcodes-dropzone",
|
|
5
|
+
);
|
|
6
|
+
import "./dropzone.js";
|
|
7
|
+
|
|
8
|
+
const meta = {
|
|
9
|
+
title: "Components/Dropzone",
|
|
10
|
+
component: "grantcodes-dropzone",
|
|
11
|
+
args: {
|
|
12
|
+
...args,
|
|
13
|
+
fullscreenOnDrag: true,
|
|
14
|
+
placeholder: "Placeholder in the dropzone slot",
|
|
15
|
+
onChange: (e) => {
|
|
16
|
+
const target = e.target;
|
|
17
|
+
console.log("Received files:", target.files);
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
argTypes,
|
|
21
|
+
render: (args) =>
|
|
22
|
+
template(
|
|
23
|
+
args,
|
|
24
|
+
html`<input
|
|
25
|
+
type="file"
|
|
26
|
+
placeholder="${args.placeholder}"
|
|
27
|
+
accept="*"
|
|
28
|
+
multiple
|
|
29
|
+
@change=${args.onChange}
|
|
30
|
+
/>`,
|
|
31
|
+
),
|
|
32
|
+
parameters: {
|
|
33
|
+
actions: {
|
|
34
|
+
handles: events,
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export default meta;
|
|
40
|
+
|
|
41
|
+
export const Dropzone = {};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { css } from "lit";
|
|
2
|
+
|
|
3
|
+
export const dropzoneStyles = css`
|
|
4
|
+
*,
|
|
5
|
+
*::before,
|
|
6
|
+
*::after {
|
|
7
|
+
box-sizing: border-box;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
:host {
|
|
11
|
+
display: block;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.dropzone {
|
|
15
|
+
position: relative;
|
|
16
|
+
display: flex;
|
|
17
|
+
flex-direction: column;
|
|
18
|
+
align-items: center;
|
|
19
|
+
justify-content: center;
|
|
20
|
+
padding: 2rem;
|
|
21
|
+
border: max(var(--g-theme-border-width-md), 0.2rem) dashed var(--g-theme-color-border-default);
|
|
22
|
+
border-radius: var(--g-theme-border-radius-md);
|
|
23
|
+
background-color: var(--g-theme-color-background-subtle);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.dropzone--fullscreen {
|
|
27
|
+
position: fixed;
|
|
28
|
+
inset: 0;
|
|
29
|
+
z-index: 10;
|
|
30
|
+
background-color: var(--g-theme-color-background-default);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.dropzone--fullscreen::after {
|
|
34
|
+
content: '';
|
|
35
|
+
display: flex;
|
|
36
|
+
position: fixed;
|
|
37
|
+
inset: 2rem;
|
|
38
|
+
justify-content: center;
|
|
39
|
+
align-items: center;
|
|
40
|
+
padding: 2rem;
|
|
41
|
+
border: max(calc(var(--g-theme-border-width-md) * 2), 0.4rem) dashed var(--g-theme-color-border-default);
|
|
42
|
+
border-radius: calc(var(--g-theme-border-radius-md) * 2);
|
|
43
|
+
z-index: 11;
|
|
44
|
+
font-weight: bold;
|
|
45
|
+
color: inherit;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
::slotted(input),
|
|
49
|
+
.dropzone__input {
|
|
50
|
+
position: absolute;
|
|
51
|
+
display: block;
|
|
52
|
+
width: 100%;
|
|
53
|
+
height: 100%;
|
|
54
|
+
inset: 0;
|
|
55
|
+
opacity: 0;
|
|
56
|
+
cursor: pointer;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.dropzone--fullscreen ::slotted(input),
|
|
60
|
+
.dropzone--fullscreen .dropzone__input {
|
|
61
|
+
position: fixed;
|
|
62
|
+
z-index: 12;
|
|
63
|
+
}
|
|
64
|
+
`;
|
|
@@ -0,0 +1,112 @@
|
|
|
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 "./dropzone.js";
|
|
5
|
+
|
|
6
|
+
describe("Dropzone 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-dropzone");
|
|
15
|
+
// Add required file input after fixture (component allows missing input in tests)
|
|
16
|
+
const input = document.createElement("input");
|
|
17
|
+
input.type = "file";
|
|
18
|
+
input.placeholder = "Drop files here";
|
|
19
|
+
element.appendChild(input);
|
|
20
|
+
await element.updateComplete;
|
|
21
|
+
assert.ok(element, "Element should be created");
|
|
22
|
+
assert.ok(element.shadowRoot, "Element should have shadow root");
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it("should have fullscreenOnDrag property set to false by default", async () => {
|
|
26
|
+
element = await fixture("grantcodes-dropzone");
|
|
27
|
+
const input = document.createElement("input");
|
|
28
|
+
input.type = "file";
|
|
29
|
+
element.appendChild(input);
|
|
30
|
+
await element.updateComplete;
|
|
31
|
+
assert.strictEqual(
|
|
32
|
+
element.fullscreenOnDrag,
|
|
33
|
+
false,
|
|
34
|
+
"fullscreenOnDrag should be false by default",
|
|
35
|
+
);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("should render dropzone wrapper", async () => {
|
|
39
|
+
element = await fixture("grantcodes-dropzone");
|
|
40
|
+
const input = document.createElement("input");
|
|
41
|
+
input.type = "file";
|
|
42
|
+
element.appendChild(input);
|
|
43
|
+
await element.updateComplete;
|
|
44
|
+
const dropzone = element.shadowRoot.querySelector(".dropzone");
|
|
45
|
+
assert.ok(dropzone, "Dropzone element should exist");
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it("should render placeholder span", async () => {
|
|
49
|
+
element = await fixture("grantcodes-dropzone");
|
|
50
|
+
const input = document.createElement("input");
|
|
51
|
+
input.type = "file";
|
|
52
|
+
input.placeholder = "Drop files here";
|
|
53
|
+
element.appendChild(input);
|
|
54
|
+
await element.updateComplete;
|
|
55
|
+
const placeholder = element.shadowRoot.querySelector(".dropzone__placeholder");
|
|
56
|
+
assert.ok(placeholder, "Placeholder element should exist");
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("should have slot for file input", async () => {
|
|
60
|
+
element = await fixture("grantcodes-dropzone");
|
|
61
|
+
const input = document.createElement("input");
|
|
62
|
+
input.type = "file";
|
|
63
|
+
element.appendChild(input);
|
|
64
|
+
await element.updateComplete;
|
|
65
|
+
const slot = element.shadowRoot.querySelector("slot");
|
|
66
|
+
assert.ok(slot, "Slot should exist");
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it("should support fullscreenOnDrag property", async () => {
|
|
70
|
+
element = await fixture("grantcodes-dropzone", {
|
|
71
|
+
fullscreenOnDrag: true,
|
|
72
|
+
});
|
|
73
|
+
const input = document.createElement("input");
|
|
74
|
+
input.type = "file";
|
|
75
|
+
element.appendChild(input);
|
|
76
|
+
await element.updateComplete;
|
|
77
|
+
|
|
78
|
+
assert.strictEqual(
|
|
79
|
+
element.fullscreenOnDrag,
|
|
80
|
+
true,
|
|
81
|
+
"fullscreenOnDrag should be true",
|
|
82
|
+
);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it("should have drag event handlers", async () => {
|
|
86
|
+
element = await fixture("grantcodes-dropzone");
|
|
87
|
+
const input = document.createElement("input");
|
|
88
|
+
input.type = "file";
|
|
89
|
+
element.appendChild(input);
|
|
90
|
+
await element.updateComplete;
|
|
91
|
+
const dropzone = element.shadowRoot.querySelector(".dropzone");
|
|
92
|
+
|
|
93
|
+
// Check that the element has the event listener attributes
|
|
94
|
+
assert.ok(dropzone, "Dropzone should exist to attach handlers");
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it("should handle file input placeholder", async () => {
|
|
98
|
+
element = await fixture("grantcodes-dropzone");
|
|
99
|
+
const input = document.createElement("input");
|
|
100
|
+
input.type = "file";
|
|
101
|
+
input.placeholder = "Drop files here";
|
|
102
|
+
element.appendChild(input);
|
|
103
|
+
|
|
104
|
+
await element.updateComplete;
|
|
105
|
+
|
|
106
|
+
const placeholder = element.shadowRoot.querySelector(".dropzone__placeholder");
|
|
107
|
+
// The placeholder text is set from the input's placeholder in firstUpdated
|
|
108
|
+
assert.ok(placeholder, "Placeholder should exist");
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./dropzone";
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { LitElement } from "lit";
|
|
2
|
+
import { html } from "lit/static-html.js";
|
|
3
|
+
import { footerColumnStyles } from "./footer-column.styles.js";
|
|
4
|
+
|
|
5
|
+
export class GrantCodesFooterColumn extends LitElement {
|
|
6
|
+
static styles = [footerColumnStyles];
|
|
7
|
+
|
|
8
|
+
render() {
|
|
9
|
+
return html`
|
|
10
|
+
<div class="footer-column">
|
|
11
|
+
<slot></slot>
|
|
12
|
+
</div>
|
|
13
|
+
`;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { css } from "lit";
|
|
2
|
+
|
|
3
|
+
export const footerColumnStyles = css`
|
|
4
|
+
:host {
|
|
5
|
+
display: block;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
.footer-column {
|
|
9
|
+
display: flex;
|
|
10
|
+
flex-direction: column;
|
|
11
|
+
gap: 0.75rem;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
::slotted(h3) {
|
|
15
|
+
font-size: var(--g-typography-font-size-16);
|
|
16
|
+
color: var(--g-theme-color-content-default);
|
|
17
|
+
margin: 0 0 0.5rem 0;
|
|
18
|
+
font-weight: 600;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
::slotted(ul) {
|
|
22
|
+
list-style: none;
|
|
23
|
+
padding: 0;
|
|
24
|
+
margin: 0;
|
|
25
|
+
display: flex;
|
|
26
|
+
flex-direction: column;
|
|
27
|
+
gap: 0.5rem;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
::slotted(li) {
|
|
31
|
+
margin: 0;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
::slotted(a) {
|
|
35
|
+
color: var(--g-theme-color-content-secondary);
|
|
36
|
+
text-decoration: none;
|
|
37
|
+
transition: color 0.2s ease;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
::slotted(a:hover) {
|
|
41
|
+
color: var(--g-theme-color-content-default);
|
|
42
|
+
text-decoration: underline;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
::slotted(p) {
|
|
46
|
+
margin: 0;
|
|
47
|
+
color: var(--g-theme-color-content-secondary);
|
|
48
|
+
font-size: var(--g-typography-font-size-14);
|
|
49
|
+
line-height: 1.5;
|
|
50
|
+
}
|
|
51
|
+
`;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { LitElement } from "lit";
|
|
2
|
+
import { html } from "lit/static-html.js";
|
|
3
|
+
import { footerStyles } from "./footer.styles.js";
|
|
4
|
+
|
|
5
|
+
export class GrantCodesFooter extends LitElement {
|
|
6
|
+
static styles = [footerStyles];
|
|
7
|
+
|
|
8
|
+
static properties = {
|
|
9
|
+
columns: { type: Number },
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
constructor() {
|
|
13
|
+
super();
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Number of columns in the footer
|
|
17
|
+
* @type {number}
|
|
18
|
+
*/
|
|
19
|
+
this.columns = 3;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
render() {
|
|
23
|
+
return html`
|
|
24
|
+
<footer class="footer">
|
|
25
|
+
<div class="footer__container">
|
|
26
|
+
<div class="footer__columns" style="--footer-columns: ${this.columns}">
|
|
27
|
+
<slot></slot>
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
<div class="footer__bottom">
|
|
32
|
+
<slot name="bottom"></slot>
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
|
35
|
+
</footer>
|
|
36
|
+
`;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { GrantCodesFooter } from "./footer.component.js";
|
|
2
|
+
import { GrantCodesFooterColumn } from "./footer-column.component.js";
|
|
3
|
+
|
|
4
|
+
export * from "./footer.component.js";
|
|
5
|
+
export * from "./footer-column.component.js";
|
|
6
|
+
export default GrantCodesFooter;
|
|
7
|
+
|
|
8
|
+
customElements.define("grantcodes-footer", GrantCodesFooter);
|
|
9
|
+
customElements.define("grantcodes-footer-column", GrantCodesFooterColumn);
|