@nationaldesignstudio/react 0.5.2 → 0.5.3
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/package.json +10 -2
- package/src/App.css +0 -0
- package/src/App.tsx +0 -7
- package/src/assets/fonts/PPNeueMontreal-Variable.woff2 +0 -0
- package/src/assets/react.svg +0 -1
- package/src/components/atoms/accordion/accordion.stories.tsx +0 -228
- package/src/components/atoms/accordion/accordion.test.tsx +0 -231
- package/src/components/atoms/accordion/index.ts +0 -6
- package/src/components/atoms/background/background.test.tsx +0 -213
- package/src/components/atoms/background/index.ts +0 -22
- package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-charcoal-chromium-darwin.png +0 -0
- package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-charcoal-chromium-linux.png +0 -0
- package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-charcoal-outline-chromium-darwin.png +0 -0
- package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-charcoal-outline-chromium-linux.png +0 -0
- package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-charcoal-outline-quiet-chromium-darwin.png +0 -0
- package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-charcoal-outline-quiet-chromium-linux.png +0 -0
- package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-disabled-chromium-darwin.png +0 -0
- package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-disabled-chromium-linux.png +0 -0
- package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-ivory-chromium-darwin.png +0 -0
- package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-ivory-chromium-linux.png +0 -0
- package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-ivory-outline-chromium-darwin.png +0 -0
- package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-ivory-outline-chromium-linux.png +0 -0
- package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-ivory-outline-quiet-chromium-darwin.png +0 -0
- package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-ivory-outline-quiet-chromium-linux.png +0 -0
- package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-size-large-chromium-darwin.png +0 -0
- package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-size-large-chromium-linux.png +0 -0
- package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-size-medium-chromium-darwin.png +0 -0
- package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-size-medium-chromium-linux.png +0 -0
- package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-size-small-chromium-darwin.png +0 -0
- package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-size-small-chromium-linux.png +0 -0
- package/src/components/atoms/button/button.stories.tsx +0 -289
- package/src/components/atoms/button/button.test.tsx +0 -419
- package/src/components/atoms/button/button.visual.test.tsx +0 -98
- package/src/components/atoms/button/icon-button.stories.tsx +0 -260
- package/src/components/atoms/button/icon-button.test.tsx +0 -186
- package/src/components/atoms/button/index.ts +0 -6
- package/src/components/atoms/input/index.ts +0 -17
- package/src/components/atoms/input/input-group.stories.tsx +0 -646
- package/src/components/atoms/input/input-group.test.tsx +0 -362
- package/src/components/atoms/input/input.stories.tsx +0 -228
- package/src/components/atoms/input/input.test.tsx +0 -167
- package/src/components/atoms/ndstudio-footer/index.ts +0 -1
- package/src/components/atoms/pager-control/index.ts +0 -5
- package/src/components/atoms/pager-control/pager-control.stories.tsx +0 -207
- package/src/components/atoms/pager-control/pager-control.test.tsx +0 -130
- package/src/components/atoms/popover/index.ts +0 -30
- package/src/components/atoms/popover/popover.stories.tsx +0 -531
- package/src/components/atoms/popover/popover.test.tsx +0 -486
- package/src/components/atoms/select/index.ts +0 -18
- package/src/components/atoms/select/select.stories.tsx +0 -455
- package/src/components/atoms/tooltip/index.ts +0 -24
- package/src/components/atoms/tooltip/tooltip.stories.tsx +0 -348
- package/src/components/atoms/tooltip/tooltip.test.tsx +0 -363
- package/src/components/dev-tools/dev-toolbar/dev-toolbar.stories.tsx +0 -73
- package/src/components/dev-tools/dev-toolbar/index.ts +0 -1
- package/src/components/dev-tools/grid-overlay/index.ts +0 -1
- package/src/components/dev-tools/index.ts +0 -2
- package/src/components/foundation/typography/typography.stories.tsx +0 -401
- package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-default-vertical-chromium-darwin.png +0 -0
- package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-default-vertical-chromium-linux.png +0 -0
- package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-horizontal-chromium-darwin.png +0 -0
- package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-horizontal-chromium-linux.png +0 -0
- package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-minimal-chromium-darwin.png +0 -0
- package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-minimal-chromium-linux.png +0 -0
- package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-without-actions-chromium-darwin.png +0 -0
- package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-without-actions-chromium-linux.png +0 -0
- package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-without-eyebrow-chromium-darwin.png +0 -0
- package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-without-eyebrow-chromium-linux.png +0 -0
- package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-without-image-chromium-darwin.png +0 -0
- package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-without-image-chromium-linux.png +0 -0
- package/src/components/organisms/card/card.stories.tsx +0 -293
- package/src/components/organisms/card/card.test.tsx +0 -247
- package/src/components/organisms/card/card.visual.test.tsx +0 -197
- package/src/components/organisms/card/index.ts +0 -26
- package/src/components/organisms/navbar/__screenshots__/navbar.visual.test.tsx/navbar-active-link-chromium-darwin.png +0 -0
- package/src/components/organisms/navbar/__screenshots__/navbar.visual.test.tsx/navbar-active-link-chromium-linux.png +0 -0
- package/src/components/organisms/navbar/__screenshots__/navbar.visual.test.tsx/navbar-brand-only-chromium-darwin.png +0 -0
- package/src/components/organisms/navbar/__screenshots__/navbar.visual.test.tsx/navbar-brand-only-chromium-linux.png +0 -0
- package/src/components/organisms/navbar/__screenshots__/navbar.visual.test.tsx/navbar-default-chromium-darwin.png +0 -0
- package/src/components/organisms/navbar/__screenshots__/navbar.visual.test.tsx/navbar-default-chromium-linux.png +0 -0
- package/src/components/organisms/navbar/index.ts +0 -18
- package/src/components/organisms/navbar/navbar.stories.tsx +0 -313
- package/src/components/organisms/navbar/navbar.test.tsx +0 -190
- package/src/components/organisms/navbar/navbar.visual.test.tsx +0 -85
- package/src/components/organisms/us-gov-banner/__screenshots__/us-gov-banner.visual.test.tsx/us-gov-banner-custom-icon-chromium-darwin.png +0 -0
- package/src/components/organisms/us-gov-banner/__screenshots__/us-gov-banner.visual.test.tsx/us-gov-banner-custom-icon-chromium-linux.png +0 -0
- package/src/components/organisms/us-gov-banner/__screenshots__/us-gov-banner.visual.test.tsx/us-gov-banner-custom-text-chromium-darwin.png +0 -0
- package/src/components/organisms/us-gov-banner/__screenshots__/us-gov-banner.visual.test.tsx/us-gov-banner-custom-text-chromium-linux.png +0 -0
- package/src/components/organisms/us-gov-banner/__screenshots__/us-gov-banner.visual.test.tsx/us-gov-banner-default-chromium-darwin.png +0 -0
- package/src/components/organisms/us-gov-banner/__screenshots__/us-gov-banner.visual.test.tsx/us-gov-banner-default-chromium-linux.png +0 -0
- package/src/components/organisms/us-gov-banner/index.ts +0 -5
- package/src/components/organisms/us-gov-banner/us-gov-banner.stories.tsx +0 -35
- package/src/components/organisms/us-gov-banner/us-gov-banner.test.tsx +0 -107
- package/src/components/organisms/us-gov-banner/us-gov-banner.visual.test.tsx +0 -46
- package/src/components/sections/banner/banner.stories.tsx +0 -150
- package/src/components/sections/banner/banner.test.tsx +0 -185
- package/src/components/sections/banner/index.ts +0 -2
- package/src/components/sections/card-grid/card-grid.stories.tsx +0 -351
- package/src/components/sections/card-grid/index.ts +0 -1
- package/src/components/sections/faq-section/faq-section.stories.tsx +0 -453
- package/src/components/sections/faq-section/index.ts +0 -2
- package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a1-desktop-chromium-darwin.png +0 -0
- package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a1-desktop-chromium-linux.png +0 -0
- package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a1-mobile-chromium-darwin.png +0 -0
- package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a1-mobile-chromium-linux.png +0 -0
- package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a1-tablet-chromium-darwin.png +0 -0
- package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a1-tablet-chromium-linux.png +0 -0
- package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a2-desktop-chromium-darwin.png +0 -0
- package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a2-desktop-chromium-linux.png +0 -0
- package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a2-mobile-chromium-darwin.png +0 -0
- package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a2-mobile-chromium-linux.png +0 -0
- package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a2-tablet-chromium-darwin.png +0 -0
- package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a2-tablet-chromium-linux.png +0 -0
- package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a3-desktop-chromium-darwin.png +0 -0
- package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a3-desktop-chromium-linux.png +0 -0
- package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a3-mobile-chromium-darwin.png +0 -0
- package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a3-mobile-chromium-linux.png +0 -0
- package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a3-tablet-chromium-darwin.png +0 -0
- package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a3-tablet-chromium-linux.png +0 -0
- package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-custom-class-chromium-darwin.png +0 -0
- package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-custom-class-chromium-linux.png +0 -0
- package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-default-chromium-linux.png +0 -0
- package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-long-title-chromium-darwin.png +0 -0
- package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-long-title-chromium-linux.png +0 -0
- package/src/components/sections/hero/hero.stories.tsx +0 -397
- package/src/components/sections/hero/hero.test.tsx +0 -138
- package/src/components/sections/hero/hero.visual.test.tsx +0 -140
- package/src/components/sections/hero/index.ts +0 -23
- package/src/components/sections/prose/__screenshots__/prose.visual.test.tsx/prose-h3-heading-chromium-darwin.png +0 -0
- package/src/components/sections/prose/__screenshots__/prose.visual.test.tsx/prose-h3-heading-chromium-linux.png +0 -0
- package/src/components/sections/prose/__screenshots__/prose.visual.test.tsx/prose-multiple-paragraphs-chromium-darwin.png +0 -0
- package/src/components/sections/prose/__screenshots__/prose.visual.test.tsx/prose-multiple-paragraphs-chromium-linux.png +0 -0
- package/src/components/sections/prose/__screenshots__/prose.visual.test.tsx/prose-multiple-sections-chromium-darwin.png +0 -0
- package/src/components/sections/prose/__screenshots__/prose.visual.test.tsx/prose-multiple-sections-chromium-linux.png +0 -0
- package/src/components/sections/prose/__screenshots__/prose.visual.test.tsx/prose-single-section-chromium-darwin.png +0 -0
- package/src/components/sections/prose/__screenshots__/prose.visual.test.tsx/prose-single-section-chromium-linux.png +0 -0
- package/src/components/sections/prose/index.ts +0 -6
- package/src/components/sections/prose/prose.stories.tsx +0 -144
- package/src/components/sections/prose/prose.test.tsx +0 -178
- package/src/components/sections/prose/prose.visual.test.tsx +0 -105
- package/src/components/sections/quote-block/index.ts +0 -5
- package/src/components/sections/river/index.ts +0 -1
- package/src/components/sections/river/river.stories.tsx +0 -237
- package/src/components/sections/river/river.test.tsx +0 -268
- package/src/components/sections/tout/index.ts +0 -1
- package/src/components/sections/tout/tout.stories.tsx +0 -171
- package/src/components/sections/tout/tout.test.tsx +0 -242
- package/src/components/sections/two-column-section/index.ts +0 -5
- package/src/components/sections/two-column-section/two-column-section.stories.tsx +0 -285
- package/src/components/shared/index.ts +0 -5
- package/src/index.ts +0 -293
- package/src/main.tsx +0 -13
- package/src/stories/grid-system.stories.tsx +0 -309
- package/src/stories/introduction.mdx +0 -128
- package/src/stories/theme-provider.stories.tsx +0 -349
- package/src/stories/token-showcase.stories.tsx +0 -73
- package/src/stories/token-showcase.tsx +0 -777
- package/src/styles.css +0 -14
- package/src/tests/token-resolution.test.tsx +0 -298
- package/src/theme/theme-provider.test.tsx +0 -270
|
@@ -1,419 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test, vi } from "vitest";
|
|
2
|
-
import { page, userEvent } from "vitest/browser";
|
|
3
|
-
import { render } from "vitest-browser-react";
|
|
4
|
-
import { Button } from "./button";
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Helper to get computed styles of an element
|
|
8
|
-
*/
|
|
9
|
-
function getStyles(element: HTMLElement) {
|
|
10
|
-
return window.getComputedStyle(element);
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Helper to convert rgb/rgba to hex for easier comparison
|
|
15
|
-
*/
|
|
16
|
-
function _rgbToHex(rgb: string): string {
|
|
17
|
-
// Handle rgba format
|
|
18
|
-
const match = rgb.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*[\d.]+)?\)/);
|
|
19
|
-
if (!match) return rgb;
|
|
20
|
-
|
|
21
|
-
const r = parseInt(match[1], 10);
|
|
22
|
-
const g = parseInt(match[2], 10);
|
|
23
|
-
const b = parseInt(match[3], 10);
|
|
24
|
-
|
|
25
|
-
return `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)}`;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
describe("Button", () => {
|
|
29
|
-
describe("Accessibility", () => {
|
|
30
|
-
test("has correct button role", async () => {
|
|
31
|
-
render(<Button>Click me</Button>);
|
|
32
|
-
await expect
|
|
33
|
-
.element(page.getByRole("button", { name: "Click me" }))
|
|
34
|
-
.toBeInTheDocument();
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
test("is focusable via keyboard", async () => {
|
|
38
|
-
render(<Button>Focusable</Button>);
|
|
39
|
-
await userEvent.keyboard("{Tab}");
|
|
40
|
-
await expect
|
|
41
|
-
.element(page.getByRole("button", { name: "Focusable" }))
|
|
42
|
-
.toHaveFocus();
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
test("disabled button has disabled attribute", async () => {
|
|
46
|
-
render(<Button disabled>Disabled</Button>);
|
|
47
|
-
await expect
|
|
48
|
-
.element(page.getByRole("button", { name: "Disabled" }))
|
|
49
|
-
.toBeDisabled();
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
test("disabled button is not focusable", async () => {
|
|
53
|
-
render(
|
|
54
|
-
<>
|
|
55
|
-
<Button disabled>Disabled</Button>
|
|
56
|
-
<Button>After</Button>
|
|
57
|
-
</>,
|
|
58
|
-
);
|
|
59
|
-
await userEvent.keyboard("{Tab}");
|
|
60
|
-
// Focus should skip the disabled button and go to the next one
|
|
61
|
-
await expect
|
|
62
|
-
.element(page.getByRole("button", { name: "After" }))
|
|
63
|
-
.toHaveFocus();
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
test("button with aria-label has accessible name", async () => {
|
|
67
|
-
render(<Button aria-label="Close dialog">×</Button>);
|
|
68
|
-
await expect
|
|
69
|
-
.element(page.getByRole("button", { name: "Close dialog" }))
|
|
70
|
-
.toBeInTheDocument();
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
test("button type defaults to button (not submit)", async () => {
|
|
74
|
-
render(<Button>Click me</Button>);
|
|
75
|
-
await expect
|
|
76
|
-
.element(page.getByRole("button", { name: "Click me" }))
|
|
77
|
-
.toHaveAttribute("type", "button");
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
test("button type can be set explicitly to submit", async () => {
|
|
81
|
-
render(<Button type="submit">Submit</Button>);
|
|
82
|
-
await expect
|
|
83
|
-
.element(page.getByRole("button", { name: "Submit" }))
|
|
84
|
-
.toHaveAttribute("type", "submit");
|
|
85
|
-
});
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
describe("Interactions", () => {
|
|
89
|
-
test("calls onClick when clicked", async () => {
|
|
90
|
-
const handleClick = vi.fn();
|
|
91
|
-
render(<Button onClick={handleClick}>Click me</Button>);
|
|
92
|
-
await page.getByRole("button", { name: "Click me" }).click();
|
|
93
|
-
expect(handleClick).toHaveBeenCalledOnce();
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
test("responds to Enter key when focused", async () => {
|
|
97
|
-
const handleClick = vi.fn();
|
|
98
|
-
render(<Button onClick={handleClick}>Enter key</Button>);
|
|
99
|
-
page.getByRole("button", { name: "Enter key" }).element().focus();
|
|
100
|
-
await userEvent.keyboard("{Enter}");
|
|
101
|
-
expect(handleClick).toHaveBeenCalledOnce();
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
test("responds to Space key when focused", async () => {
|
|
105
|
-
const handleClick = vi.fn();
|
|
106
|
-
render(<Button onClick={handleClick}>Space key</Button>);
|
|
107
|
-
page.getByRole("button", { name: "Space key" }).element().focus();
|
|
108
|
-
await userEvent.keyboard(" ");
|
|
109
|
-
expect(handleClick).toHaveBeenCalledOnce();
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
test("does not fire onClick when disabled", async () => {
|
|
113
|
-
const handleClick = vi.fn();
|
|
114
|
-
render(
|
|
115
|
-
<Button disabled onClick={handleClick}>
|
|
116
|
-
Disabled
|
|
117
|
-
</Button>,
|
|
118
|
-
);
|
|
119
|
-
await page
|
|
120
|
-
.getByRole("button", { name: "Disabled" })
|
|
121
|
-
.click({ force: true });
|
|
122
|
-
expect(handleClick).not.toHaveBeenCalled();
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
test("supports multiple clicks", async () => {
|
|
126
|
-
const handleClick = vi.fn();
|
|
127
|
-
render(<Button onClick={handleClick}>Multi click</Button>);
|
|
128
|
-
const button = page.getByRole("button", { name: "Multi click" });
|
|
129
|
-
await button.click();
|
|
130
|
-
await button.click();
|
|
131
|
-
await button.click();
|
|
132
|
-
expect(handleClick).toHaveBeenCalledTimes(3);
|
|
133
|
-
});
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
describe("render prop", () => {
|
|
137
|
-
test("renders as anchor element with button role when render prop is used", async () => {
|
|
138
|
-
render(
|
|
139
|
-
// biome-ignore lint/a11y/useAnchorContent: Content provided via Button children
|
|
140
|
-
<Button render={<a href="/test" />}>Link Button</Button>,
|
|
141
|
-
);
|
|
142
|
-
// Base UI keeps role="button" for accessibility when rendering as another element
|
|
143
|
-
const button = page.getByRole("button", { name: "Link Button" });
|
|
144
|
-
await expect.element(button).toBeInTheDocument();
|
|
145
|
-
await expect.element(button).toHaveAttribute("href", "/test");
|
|
146
|
-
});
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
describe("Variant Styles", () => {
|
|
150
|
-
test("default variant has dark background", async () => {
|
|
151
|
-
render(<Button variant="default">Default</Button>);
|
|
152
|
-
const button = page.getByRole("button", { name: "Default" });
|
|
153
|
-
await expect.element(button).toBeInTheDocument();
|
|
154
|
-
|
|
155
|
-
const element = button.element();
|
|
156
|
-
const styles = getStyles(element);
|
|
157
|
-
|
|
158
|
-
// Default variant should have a dark background (gray-1200)
|
|
159
|
-
// The actual color depends on CSS variables, but it should not be transparent
|
|
160
|
-
expect(styles.backgroundColor).not.toBe("rgba(0, 0, 0, 0)");
|
|
161
|
-
expect(styles.backgroundColor).not.toBe("transparent");
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
test("primary variant has blue background", async () => {
|
|
165
|
-
render(<Button variant="primary">Primary</Button>);
|
|
166
|
-
const button = page.getByRole("button", { name: "Primary" });
|
|
167
|
-
await expect.element(button).toBeInTheDocument();
|
|
168
|
-
|
|
169
|
-
const element = button.element();
|
|
170
|
-
const styles = getStyles(element);
|
|
171
|
-
|
|
172
|
-
// Primary variant should have a visible background
|
|
173
|
-
expect(styles.backgroundColor).not.toBe("rgba(0, 0, 0, 0)");
|
|
174
|
-
expect(styles.backgroundColor).not.toBe("transparent");
|
|
175
|
-
});
|
|
176
|
-
|
|
177
|
-
test("destructive variant has red-tinted background", async () => {
|
|
178
|
-
render(<Button variant="destructive">Delete</Button>);
|
|
179
|
-
const button = page.getByRole("button", { name: "Delete" });
|
|
180
|
-
await expect.element(button).toBeInTheDocument();
|
|
181
|
-
|
|
182
|
-
const element = button.element();
|
|
183
|
-
const styles = getStyles(element);
|
|
184
|
-
|
|
185
|
-
// Destructive variant should have a visible background
|
|
186
|
-
expect(styles.backgroundColor).not.toBe("rgba(0, 0, 0, 0)");
|
|
187
|
-
expect(styles.backgroundColor).not.toBe("transparent");
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
test("ghost variant has transparent background", async () => {
|
|
191
|
-
render(<Button variant="ghost">Ghost</Button>);
|
|
192
|
-
const button = page.getByRole("button", { name: "Ghost" });
|
|
193
|
-
await expect.element(button).toBeInTheDocument();
|
|
194
|
-
|
|
195
|
-
const element = button.element();
|
|
196
|
-
const styles = getStyles(element);
|
|
197
|
-
|
|
198
|
-
// Ghost variant should have transparent background
|
|
199
|
-
expect(
|
|
200
|
-
styles.backgroundColor === "rgba(0, 0, 0, 0)" ||
|
|
201
|
-
styles.backgroundColor === "transparent",
|
|
202
|
-
).toBe(true);
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
test("link variant has no background and underline on hover behavior", async () => {
|
|
206
|
-
render(<Button variant="link">Link</Button>);
|
|
207
|
-
const button = page.getByRole("button", { name: "Link" });
|
|
208
|
-
await expect.element(button).toBeInTheDocument();
|
|
209
|
-
|
|
210
|
-
const element = button.element();
|
|
211
|
-
const styles = getStyles(element);
|
|
212
|
-
|
|
213
|
-
// Link variant should have transparent background
|
|
214
|
-
expect(
|
|
215
|
-
styles.backgroundColor === "rgba(0, 0, 0, 0)" ||
|
|
216
|
-
styles.backgroundColor === "transparent",
|
|
217
|
-
).toBe(true);
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
test("outline variant has visible border", async () => {
|
|
221
|
-
render(<Button variant="outline">Outline</Button>);
|
|
222
|
-
const button = page.getByRole("button", { name: "Outline" });
|
|
223
|
-
await expect.element(button).toBeInTheDocument();
|
|
224
|
-
|
|
225
|
-
const element = button.element();
|
|
226
|
-
const styles = getStyles(element);
|
|
227
|
-
|
|
228
|
-
// Outline variant should have a visible border
|
|
229
|
-
const borderWidth = parseFloat(styles.borderWidth) || 0;
|
|
230
|
-
expect(borderWidth).toBeGreaterThan(0);
|
|
231
|
-
});
|
|
232
|
-
|
|
233
|
-
test("secondary variant has light gray background with border", async () => {
|
|
234
|
-
render(<Button variant="secondary">Secondary</Button>);
|
|
235
|
-
const button = page.getByRole("button", { name: "Secondary" });
|
|
236
|
-
await expect.element(button).toBeInTheDocument();
|
|
237
|
-
|
|
238
|
-
const element = button.element();
|
|
239
|
-
const styles = getStyles(element);
|
|
240
|
-
|
|
241
|
-
// Secondary variant should have a visible background
|
|
242
|
-
expect(styles.backgroundColor).not.toBe("rgba(0, 0, 0, 0)");
|
|
243
|
-
expect(styles.backgroundColor).not.toBe("transparent");
|
|
244
|
-
|
|
245
|
-
// Secondary should also have a border
|
|
246
|
-
const borderWidth = parseFloat(styles.borderWidth) || 0;
|
|
247
|
-
expect(borderWidth).toBeGreaterThan(0);
|
|
248
|
-
});
|
|
249
|
-
});
|
|
250
|
-
|
|
251
|
-
describe("Size Styles", () => {
|
|
252
|
-
test("small size has 32px height", async () => {
|
|
253
|
-
render(<Button size="sm">Small</Button>);
|
|
254
|
-
const button = page.getByRole("button", { name: "Small" });
|
|
255
|
-
await expect.element(button).toBeInTheDocument();
|
|
256
|
-
|
|
257
|
-
const element = button.element();
|
|
258
|
-
const styles = getStyles(element);
|
|
259
|
-
|
|
260
|
-
// Small button should be 32px height
|
|
261
|
-
expect(styles.height).toBe("32px");
|
|
262
|
-
});
|
|
263
|
-
|
|
264
|
-
test("default size has 36px height", async () => {
|
|
265
|
-
render(<Button size="default">Default</Button>);
|
|
266
|
-
const button = page.getByRole("button", { name: "Default" });
|
|
267
|
-
await expect.element(button).toBeInTheDocument();
|
|
268
|
-
|
|
269
|
-
const element = button.element();
|
|
270
|
-
const styles = getStyles(element);
|
|
271
|
-
|
|
272
|
-
// Default button should be 36px height
|
|
273
|
-
expect(styles.height).toBe("36px");
|
|
274
|
-
});
|
|
275
|
-
|
|
276
|
-
test("large size has 40px height", async () => {
|
|
277
|
-
render(<Button size="lg">Large</Button>);
|
|
278
|
-
const button = page.getByRole("button", { name: "Large" });
|
|
279
|
-
await expect.element(button).toBeInTheDocument();
|
|
280
|
-
|
|
281
|
-
const element = button.element();
|
|
282
|
-
const styles = getStyles(element);
|
|
283
|
-
|
|
284
|
-
// Large button should be 40px height
|
|
285
|
-
expect(styles.height).toBe("40px");
|
|
286
|
-
});
|
|
287
|
-
|
|
288
|
-
test("size affects padding appropriately", async () => {
|
|
289
|
-
render(
|
|
290
|
-
<>
|
|
291
|
-
<Button size="sm">Small</Button>
|
|
292
|
-
<Button size="lg">Large</Button>
|
|
293
|
-
</>,
|
|
294
|
-
);
|
|
295
|
-
const smallButton = page.getByRole("button", { name: "Small" });
|
|
296
|
-
const largeButton = page.getByRole("button", { name: "Large" });
|
|
297
|
-
|
|
298
|
-
const smallStyles = getStyles(smallButton.element());
|
|
299
|
-
const largeStyles = getStyles(largeButton.element());
|
|
300
|
-
|
|
301
|
-
const smallPaddingLeft = parseFloat(smallStyles.paddingLeft);
|
|
302
|
-
const largePaddingLeft = parseFloat(largeStyles.paddingLeft);
|
|
303
|
-
|
|
304
|
-
// Large should have more padding than small
|
|
305
|
-
expect(largePaddingLeft).toBeGreaterThan(smallPaddingLeft);
|
|
306
|
-
});
|
|
307
|
-
|
|
308
|
-
test("size affects border radius appropriately", async () => {
|
|
309
|
-
render(
|
|
310
|
-
<>
|
|
311
|
-
<Button size="sm">Small</Button>
|
|
312
|
-
<Button size="lg">Large</Button>
|
|
313
|
-
</>,
|
|
314
|
-
);
|
|
315
|
-
const smallButton = page.getByRole("button", { name: "Small" });
|
|
316
|
-
const largeButton = page.getByRole("button", { name: "Large" });
|
|
317
|
-
|
|
318
|
-
const smallStyles = getStyles(smallButton.element());
|
|
319
|
-
const largeStyles = getStyles(largeButton.element());
|
|
320
|
-
|
|
321
|
-
const smallRadius = parseFloat(smallStyles.borderRadius);
|
|
322
|
-
const largeRadius = parseFloat(largeStyles.borderRadius);
|
|
323
|
-
|
|
324
|
-
// Large should have more border radius than small
|
|
325
|
-
// Figma: sm=4px, default=6px, lg=10px
|
|
326
|
-
expect(largeRadius).toBeGreaterThan(smallRadius);
|
|
327
|
-
});
|
|
328
|
-
});
|
|
329
|
-
|
|
330
|
-
describe("Disabled State Styles", () => {
|
|
331
|
-
test("disabled button has reduced opacity", async () => {
|
|
332
|
-
render(<Button disabled>Disabled</Button>);
|
|
333
|
-
const button = page.getByRole("button", { name: "Disabled" });
|
|
334
|
-
await expect.element(button).toBeInTheDocument();
|
|
335
|
-
|
|
336
|
-
const element = button.element();
|
|
337
|
-
const styles = getStyles(element);
|
|
338
|
-
|
|
339
|
-
// Disabled button should have opacity of 0.5
|
|
340
|
-
expect(parseFloat(styles.opacity)).toBe(0.5);
|
|
341
|
-
});
|
|
342
|
-
|
|
343
|
-
test("disabled button has pointer-events none", async () => {
|
|
344
|
-
render(<Button disabled>Disabled</Button>);
|
|
345
|
-
const button = page.getByRole("button", { name: "Disabled" });
|
|
346
|
-
await expect.element(button).toBeInTheDocument();
|
|
347
|
-
|
|
348
|
-
const element = button.element();
|
|
349
|
-
const styles = getStyles(element);
|
|
350
|
-
|
|
351
|
-
expect(styles.pointerEvents).toBe("none");
|
|
352
|
-
});
|
|
353
|
-
});
|
|
354
|
-
|
|
355
|
-
describe("Data Attributes", () => {
|
|
356
|
-
test("button has correct data-variant attribute", async () => {
|
|
357
|
-
render(<Button variant="destructive">Test</Button>);
|
|
358
|
-
const button = page.getByRole("button", { name: "Test" });
|
|
359
|
-
|
|
360
|
-
await expect
|
|
361
|
-
.element(button)
|
|
362
|
-
.toHaveAttribute("data-variant", "destructive");
|
|
363
|
-
});
|
|
364
|
-
|
|
365
|
-
test("button has correct data-size attribute", async () => {
|
|
366
|
-
render(<Button size="lg">Test</Button>);
|
|
367
|
-
const button = page.getByRole("button", { name: "Test" });
|
|
368
|
-
|
|
369
|
-
await expect.element(button).toHaveAttribute("data-size", "lg");
|
|
370
|
-
});
|
|
371
|
-
|
|
372
|
-
test("default variant and size are reflected in data attributes", async () => {
|
|
373
|
-
render(<Button>Test</Button>);
|
|
374
|
-
const button = page.getByRole("button", { name: "Test" });
|
|
375
|
-
|
|
376
|
-
await expect.element(button).toHaveAttribute("data-variant", "default");
|
|
377
|
-
await expect.element(button).toHaveAttribute("data-size", "default");
|
|
378
|
-
});
|
|
379
|
-
});
|
|
380
|
-
|
|
381
|
-
describe("Layout Styles", () => {
|
|
382
|
-
test("button uses flexbox for content alignment", async () => {
|
|
383
|
-
render(<Button>Test</Button>);
|
|
384
|
-
const button = page.getByRole("button", { name: "Test" });
|
|
385
|
-
await expect.element(button).toBeInTheDocument();
|
|
386
|
-
|
|
387
|
-
const element = button.element();
|
|
388
|
-
const styles = getStyles(element);
|
|
389
|
-
|
|
390
|
-
expect(styles.display).toBe("inline-flex");
|
|
391
|
-
expect(styles.alignItems).toBe("center");
|
|
392
|
-
expect(styles.justifyContent).toBe("center");
|
|
393
|
-
});
|
|
394
|
-
|
|
395
|
-
test("button has cursor pointer", async () => {
|
|
396
|
-
render(<Button>Test</Button>);
|
|
397
|
-
const button = page.getByRole("button", { name: "Test" });
|
|
398
|
-
await expect.element(button).toBeInTheDocument();
|
|
399
|
-
|
|
400
|
-
const element = button.element();
|
|
401
|
-
const styles = getStyles(element);
|
|
402
|
-
|
|
403
|
-
expect(styles.cursor).toBe("pointer");
|
|
404
|
-
});
|
|
405
|
-
|
|
406
|
-
test("button has gap for icon-text spacing", async () => {
|
|
407
|
-
render(<Button>Test</Button>);
|
|
408
|
-
const button = page.getByRole("button", { name: "Test" });
|
|
409
|
-
await expect.element(button).toBeInTheDocument();
|
|
410
|
-
|
|
411
|
-
const element = button.element();
|
|
412
|
-
const styles = getStyles(element);
|
|
413
|
-
|
|
414
|
-
// Gap should be set (6px from Figma)
|
|
415
|
-
const gap = parseFloat(styles.gap);
|
|
416
|
-
expect(gap).toBeGreaterThan(0);
|
|
417
|
-
});
|
|
418
|
-
});
|
|
419
|
-
});
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
import { render } from "@testing-library/react";
|
|
2
|
-
import { describe, expect, test } from "vitest";
|
|
3
|
-
import { page } from "vitest/browser";
|
|
4
|
-
import { Button } from "./button";
|
|
5
|
-
|
|
6
|
-
describe("Button Visual Regression", () => {
|
|
7
|
-
test("primary variant renders correctly", async () => {
|
|
8
|
-
render(<Button variant="primary">Primary Button</Button>);
|
|
9
|
-
|
|
10
|
-
await expect(
|
|
11
|
-
page.getByRole("button", { name: "Primary Button" }),
|
|
12
|
-
).toMatchScreenshot("button-primary");
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
test("primary-outline variant renders correctly", async () => {
|
|
16
|
-
render(<Button variant="primary-outline">Primary Outline Button</Button>);
|
|
17
|
-
|
|
18
|
-
await expect(
|
|
19
|
-
page.getByRole("button", { name: "Primary Outline Button" }),
|
|
20
|
-
).toMatchScreenshot("button-primary-outline");
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
test("secondary variant renders correctly", async () => {
|
|
24
|
-
render(
|
|
25
|
-
<div style={{ background: "#1a1a1a", padding: "20px" }}>
|
|
26
|
-
<Button variant="secondary">Secondary Button</Button>
|
|
27
|
-
</div>,
|
|
28
|
-
);
|
|
29
|
-
|
|
30
|
-
await expect(
|
|
31
|
-
page.getByRole("button", { name: "Secondary Button" }),
|
|
32
|
-
).toMatchScreenshot("button-secondary");
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
test("secondary-outline variant renders correctly", async () => {
|
|
36
|
-
render(
|
|
37
|
-
<div style={{ background: "#1a1a1a", padding: "20px" }}>
|
|
38
|
-
<Button variant="secondary-outline">Secondary Outline Button</Button>
|
|
39
|
-
</div>,
|
|
40
|
-
);
|
|
41
|
-
|
|
42
|
-
await expect(
|
|
43
|
-
page.getByRole("button", { name: "Secondary Outline Button" }),
|
|
44
|
-
).toMatchScreenshot("button-secondary-outline");
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
test("ghost variant renders correctly", async () => {
|
|
48
|
-
render(<Button variant="ghost">Ghost Button</Button>);
|
|
49
|
-
|
|
50
|
-
await expect(
|
|
51
|
-
page.getByRole("button", { name: "Ghost Button" }),
|
|
52
|
-
).toMatchScreenshot("button-ghost");
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
test("ghost-inverse variant renders correctly", async () => {
|
|
56
|
-
render(
|
|
57
|
-
<div style={{ background: "#1a1a1a", padding: "20px" }}>
|
|
58
|
-
<Button variant="ghost-inverse">Ghost Inverse Button</Button>
|
|
59
|
-
</div>,
|
|
60
|
-
);
|
|
61
|
-
|
|
62
|
-
await expect(
|
|
63
|
-
page.getByRole("button", { name: "Ghost Inverse Button" }),
|
|
64
|
-
).toMatchScreenshot("button-ghost-inverse");
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
test("small size renders correctly", async () => {
|
|
68
|
-
render(<Button size="sm">Small Button</Button>);
|
|
69
|
-
|
|
70
|
-
await expect(
|
|
71
|
-
page.getByRole("button", { name: "Small Button" }),
|
|
72
|
-
).toMatchScreenshot("button-size-small");
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
test("medium size renders correctly", async () => {
|
|
76
|
-
render(<Button size="md">Medium Button</Button>);
|
|
77
|
-
|
|
78
|
-
await expect(
|
|
79
|
-
page.getByRole("button", { name: "Medium Button" }),
|
|
80
|
-
).toMatchScreenshot("button-size-medium");
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
test("large size renders correctly", async () => {
|
|
84
|
-
render(<Button size="lg">Large Button</Button>);
|
|
85
|
-
|
|
86
|
-
await expect(
|
|
87
|
-
page.getByRole("button", { name: "Large Button" }),
|
|
88
|
-
).toMatchScreenshot("button-size-large");
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
test("disabled state renders correctly", async () => {
|
|
92
|
-
render(<Button disabled>Disabled Button</Button>);
|
|
93
|
-
|
|
94
|
-
await expect(
|
|
95
|
-
page.getByRole("button", { name: "Disabled Button" }),
|
|
96
|
-
).toMatchScreenshot("button-disabled");
|
|
97
|
-
});
|
|
98
|
-
});
|