@nationaldesignstudio/react 0.5.2 → 0.5.4
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/components/organisms/navbar/navbar.tsx +8 -8
- 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,362 +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 {
|
|
5
|
-
InputGroup,
|
|
6
|
-
InputGroupAddon,
|
|
7
|
-
InputGroupButton,
|
|
8
|
-
InputGroupInput,
|
|
9
|
-
InputGroupText,
|
|
10
|
-
InputGroupTextarea,
|
|
11
|
-
} from "./input-group";
|
|
12
|
-
|
|
13
|
-
// Simple placeholder icon for tests
|
|
14
|
-
const SearchIcon = () => (
|
|
15
|
-
<svg
|
|
16
|
-
width="16"
|
|
17
|
-
height="16"
|
|
18
|
-
viewBox="0 0 16 16"
|
|
19
|
-
fill="none"
|
|
20
|
-
data-testid="search-icon"
|
|
21
|
-
aria-hidden="true"
|
|
22
|
-
>
|
|
23
|
-
<circle cx="7" cy="7" r="5" stroke="currentColor" strokeWidth="1.5" />
|
|
24
|
-
<path d="M14 14L11 11" stroke="currentColor" strokeWidth="1.5" />
|
|
25
|
-
</svg>
|
|
26
|
-
);
|
|
27
|
-
|
|
28
|
-
describe("InputGroup", () => {
|
|
29
|
-
describe("Accessibility", () => {
|
|
30
|
-
test("has group role", async () => {
|
|
31
|
-
render(
|
|
32
|
-
<InputGroup>
|
|
33
|
-
<InputGroupInput placeholder="Test" />
|
|
34
|
-
</InputGroup>,
|
|
35
|
-
);
|
|
36
|
-
await expect.element(page.getByRole("group")).toBeInTheDocument();
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
test("input is focusable via keyboard", async () => {
|
|
40
|
-
render(
|
|
41
|
-
<InputGroup>
|
|
42
|
-
<InputGroupInput placeholder="Focusable" />
|
|
43
|
-
</InputGroup>,
|
|
44
|
-
);
|
|
45
|
-
await userEvent.keyboard("{Tab}");
|
|
46
|
-
await expect.element(page.getByRole("textbox")).toHaveFocus();
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
test("disabled group prevents input focus", async () => {
|
|
50
|
-
render(
|
|
51
|
-
<>
|
|
52
|
-
<InputGroup disabled>
|
|
53
|
-
<InputGroupInput disabled placeholder="Disabled" />
|
|
54
|
-
</InputGroup>
|
|
55
|
-
<InputGroup>
|
|
56
|
-
<InputGroupInput placeholder="After" />
|
|
57
|
-
</InputGroup>
|
|
58
|
-
</>,
|
|
59
|
-
);
|
|
60
|
-
await userEvent.keyboard("{Tab}");
|
|
61
|
-
await expect.element(page.getByPlaceholder("After")).toHaveFocus();
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
test("error state sets aria-invalid on input", async () => {
|
|
65
|
-
render(
|
|
66
|
-
<InputGroup>
|
|
67
|
-
<InputGroupInput aria-invalid="true" placeholder="Error" />
|
|
68
|
-
</InputGroup>,
|
|
69
|
-
);
|
|
70
|
-
await expect
|
|
71
|
-
.element(page.getByRole("textbox"))
|
|
72
|
-
.toHaveAttribute("aria-invalid", "true");
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
test("addon button is accessible", async () => {
|
|
76
|
-
render(
|
|
77
|
-
<InputGroup>
|
|
78
|
-
<InputGroupInput placeholder="Input" />
|
|
79
|
-
<InputGroupAddon align="inline-end">
|
|
80
|
-
<InputGroupButton aria-label="Submit">Submit</InputGroupButton>
|
|
81
|
-
</InputGroupAddon>
|
|
82
|
-
</InputGroup>,
|
|
83
|
-
);
|
|
84
|
-
await expect
|
|
85
|
-
.element(page.getByRole("button", { name: "Submit" }))
|
|
86
|
-
.toBeInTheDocument();
|
|
87
|
-
});
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
describe("Interactions", () => {
|
|
91
|
-
test("input accepts text", async () => {
|
|
92
|
-
render(
|
|
93
|
-
<InputGroup>
|
|
94
|
-
<InputGroupInput placeholder="Type here" />
|
|
95
|
-
</InputGroup>,
|
|
96
|
-
);
|
|
97
|
-
const input = page.getByRole("textbox");
|
|
98
|
-
await input.fill("Hello World");
|
|
99
|
-
await expect.element(input).toHaveValue("Hello World");
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
test("clicking addon focuses input", async () => {
|
|
103
|
-
render(
|
|
104
|
-
<InputGroup>
|
|
105
|
-
<InputGroupAddon data-testid="addon">
|
|
106
|
-
<SearchIcon />
|
|
107
|
-
</InputGroupAddon>
|
|
108
|
-
<InputGroupInput placeholder="Test" />
|
|
109
|
-
</InputGroup>,
|
|
110
|
-
);
|
|
111
|
-
await page.getByTestId("addon").click();
|
|
112
|
-
await expect.element(page.getByRole("textbox")).toHaveFocus();
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
test("clicking button in addon does not focus input", async () => {
|
|
116
|
-
const handleClick = vi.fn();
|
|
117
|
-
render(
|
|
118
|
-
<InputGroup>
|
|
119
|
-
<InputGroupInput placeholder="Test" />
|
|
120
|
-
<InputGroupAddon align="inline-end">
|
|
121
|
-
<InputGroupButton onClick={handleClick}>Click</InputGroupButton>
|
|
122
|
-
</InputGroupAddon>
|
|
123
|
-
</InputGroup>,
|
|
124
|
-
);
|
|
125
|
-
await page.getByRole("button", { name: "Click" }).click();
|
|
126
|
-
expect(handleClick).toHaveBeenCalledOnce();
|
|
127
|
-
// Input should not have focus (button click should work independently)
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
test("input onChange is called", async () => {
|
|
131
|
-
const handleChange = vi.fn();
|
|
132
|
-
render(
|
|
133
|
-
<InputGroup>
|
|
134
|
-
<InputGroupInput onChange={handleChange} placeholder="Type" />
|
|
135
|
-
</InputGroup>,
|
|
136
|
-
);
|
|
137
|
-
await page.getByRole("textbox").fill("Test");
|
|
138
|
-
expect(handleChange).toHaveBeenCalled();
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
test("textarea accepts multiline text", async () => {
|
|
142
|
-
render(
|
|
143
|
-
<InputGroup>
|
|
144
|
-
<InputGroupTextarea placeholder="Enter text" rows={4} />
|
|
145
|
-
</InputGroup>,
|
|
146
|
-
);
|
|
147
|
-
const textarea = page.getByRole("textbox");
|
|
148
|
-
await textarea.fill("Line 1\nLine 2");
|
|
149
|
-
await expect.element(textarea).toHaveValue("Line 1\nLine 2");
|
|
150
|
-
});
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
describe("Variants", () => {
|
|
154
|
-
test("renders with default size", async () => {
|
|
155
|
-
render(
|
|
156
|
-
<InputGroup>
|
|
157
|
-
<InputGroupInput placeholder="Default" />
|
|
158
|
-
</InputGroup>,
|
|
159
|
-
);
|
|
160
|
-
const group = page.getByRole("group");
|
|
161
|
-
await expect.element(group).toBeInTheDocument();
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
test("renders with small size", async () => {
|
|
165
|
-
render(
|
|
166
|
-
<InputGroup size="sm">
|
|
167
|
-
<InputGroupInput placeholder="Small" />
|
|
168
|
-
</InputGroup>,
|
|
169
|
-
);
|
|
170
|
-
const group = page.getByRole("group");
|
|
171
|
-
await expect.element(group).toBeInTheDocument();
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
test("renders with large size", async () => {
|
|
175
|
-
render(
|
|
176
|
-
<InputGroup size="lg">
|
|
177
|
-
<InputGroupInput placeholder="Large" />
|
|
178
|
-
</InputGroup>,
|
|
179
|
-
);
|
|
180
|
-
const group = page.getByRole("group");
|
|
181
|
-
await expect.element(group).toBeInTheDocument();
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
test("applies disabled data attribute", async () => {
|
|
185
|
-
render(
|
|
186
|
-
<InputGroup disabled>
|
|
187
|
-
<InputGroupInput disabled placeholder="Disabled" />
|
|
188
|
-
</InputGroup>,
|
|
189
|
-
);
|
|
190
|
-
const group = page.getByRole("group").first();
|
|
191
|
-
await expect.element(group).toHaveAttribute("data-disabled", "true");
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
test("has expected data-slot attribute", async () => {
|
|
195
|
-
render(
|
|
196
|
-
<InputGroup>
|
|
197
|
-
<InputGroupInput placeholder="Default" />
|
|
198
|
-
</InputGroup>,
|
|
199
|
-
);
|
|
200
|
-
const group = page.getByRole("group").first();
|
|
201
|
-
await expect.element(group).toHaveAttribute("data-slot", "input-group");
|
|
202
|
-
});
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
describe("Addon Positioning", () => {
|
|
206
|
-
test("inline-start addon has correct data-align", async () => {
|
|
207
|
-
render(
|
|
208
|
-
<InputGroup>
|
|
209
|
-
<InputGroupAddon data-testid="start-addon">
|
|
210
|
-
<SearchIcon />
|
|
211
|
-
</InputGroupAddon>
|
|
212
|
-
<InputGroupInput placeholder="Input" />
|
|
213
|
-
</InputGroup>,
|
|
214
|
-
);
|
|
215
|
-
const addon = page.getByTestId("start-addon");
|
|
216
|
-
await expect.element(addon).toHaveAttribute("data-align", "inline-start");
|
|
217
|
-
});
|
|
218
|
-
|
|
219
|
-
test("inline-end addon has correct data-align", async () => {
|
|
220
|
-
render(
|
|
221
|
-
<InputGroup>
|
|
222
|
-
<InputGroupInput placeholder="Input" />
|
|
223
|
-
<InputGroupAddon align="inline-end" data-testid="end-addon">
|
|
224
|
-
<SearchIcon />
|
|
225
|
-
</InputGroupAddon>
|
|
226
|
-
</InputGroup>,
|
|
227
|
-
);
|
|
228
|
-
const addon = page.getByTestId("end-addon");
|
|
229
|
-
await expect.element(addon).toHaveAttribute("data-align", "inline-end");
|
|
230
|
-
});
|
|
231
|
-
|
|
232
|
-
test("block-start addon has correct data-align", async () => {
|
|
233
|
-
render(
|
|
234
|
-
<InputGroup>
|
|
235
|
-
<InputGroupAddon align="block-start" data-testid="block-start-addon">
|
|
236
|
-
<InputGroupText>Label</InputGroupText>
|
|
237
|
-
</InputGroupAddon>
|
|
238
|
-
<InputGroupInput placeholder="Input" />
|
|
239
|
-
</InputGroup>,
|
|
240
|
-
);
|
|
241
|
-
const addon = page.getByTestId("block-start-addon");
|
|
242
|
-
await expect.element(addon).toHaveAttribute("data-align", "block-start");
|
|
243
|
-
});
|
|
244
|
-
|
|
245
|
-
test("block-end addon has correct data-align", async () => {
|
|
246
|
-
render(
|
|
247
|
-
<InputGroup>
|
|
248
|
-
<InputGroupInput placeholder="Input" />
|
|
249
|
-
<InputGroupAddon align="block-end" data-testid="block-end-addon">
|
|
250
|
-
<InputGroupText>Helper</InputGroupText>
|
|
251
|
-
</InputGroupAddon>
|
|
252
|
-
</InputGroup>,
|
|
253
|
-
);
|
|
254
|
-
const addon = page.getByTestId("block-end-addon");
|
|
255
|
-
await expect.element(addon).toHaveAttribute("data-align", "block-end");
|
|
256
|
-
});
|
|
257
|
-
});
|
|
258
|
-
|
|
259
|
-
describe("InputGroupButton", () => {
|
|
260
|
-
test("renders as button with ghost variant by default", async () => {
|
|
261
|
-
render(
|
|
262
|
-
<InputGroup>
|
|
263
|
-
<InputGroupInput placeholder="Input" />
|
|
264
|
-
<InputGroupAddon align="inline-end">
|
|
265
|
-
<InputGroupButton>Click</InputGroupButton>
|
|
266
|
-
</InputGroupAddon>
|
|
267
|
-
</InputGroup>,
|
|
268
|
-
);
|
|
269
|
-
const button = page.getByRole("button", { name: "Click" });
|
|
270
|
-
await expect.element(button).toHaveAttribute("type", "button");
|
|
271
|
-
});
|
|
272
|
-
|
|
273
|
-
test("button click is handled", async () => {
|
|
274
|
-
const handleClick = vi.fn();
|
|
275
|
-
render(
|
|
276
|
-
<InputGroup>
|
|
277
|
-
<InputGroupInput placeholder="Input" />
|
|
278
|
-
<InputGroupAddon align="inline-end">
|
|
279
|
-
<InputGroupButton onClick={handleClick}>Submit</InputGroupButton>
|
|
280
|
-
</InputGroupAddon>
|
|
281
|
-
</InputGroup>,
|
|
282
|
-
);
|
|
283
|
-
await page.getByRole("button", { name: "Submit" }).click();
|
|
284
|
-
expect(handleClick).toHaveBeenCalledOnce();
|
|
285
|
-
});
|
|
286
|
-
|
|
287
|
-
test("supports different sizes", async () => {
|
|
288
|
-
render(
|
|
289
|
-
<InputGroup>
|
|
290
|
-
<InputGroupInput placeholder="Input" />
|
|
291
|
-
<InputGroupAddon align="inline-end">
|
|
292
|
-
<InputGroupButton size="sm" data-testid="sm-button">
|
|
293
|
-
Small
|
|
294
|
-
</InputGroupButton>
|
|
295
|
-
</InputGroupAddon>
|
|
296
|
-
</InputGroup>,
|
|
297
|
-
);
|
|
298
|
-
const button = page.getByTestId("sm-button");
|
|
299
|
-
await expect.element(button).toHaveAttribute("data-size", "sm");
|
|
300
|
-
});
|
|
301
|
-
});
|
|
302
|
-
|
|
303
|
-
describe("InputGroupText", () => {
|
|
304
|
-
test("renders text content", async () => {
|
|
305
|
-
render(
|
|
306
|
-
<InputGroup>
|
|
307
|
-
<InputGroupAddon>
|
|
308
|
-
<InputGroupText>https://</InputGroupText>
|
|
309
|
-
</InputGroupAddon>
|
|
310
|
-
<InputGroupInput placeholder="example.com" />
|
|
311
|
-
</InputGroup>,
|
|
312
|
-
);
|
|
313
|
-
await expect.element(page.getByText("https://")).toBeInTheDocument();
|
|
314
|
-
});
|
|
315
|
-
|
|
316
|
-
test("renders text element", async () => {
|
|
317
|
-
render(
|
|
318
|
-
<InputGroup>
|
|
319
|
-
<InputGroupAddon>
|
|
320
|
-
<InputGroupText data-testid="text">Prefix</InputGroupText>
|
|
321
|
-
</InputGroupAddon>
|
|
322
|
-
<InputGroupInput placeholder="Input" />
|
|
323
|
-
</InputGroup>,
|
|
324
|
-
);
|
|
325
|
-
const text = page.getByTestId("text");
|
|
326
|
-
await expect.element(text).toBeInTheDocument();
|
|
327
|
-
});
|
|
328
|
-
});
|
|
329
|
-
|
|
330
|
-
describe("InputGroupTextarea", () => {
|
|
331
|
-
test("renders textarea element", async () => {
|
|
332
|
-
render(
|
|
333
|
-
<InputGroup>
|
|
334
|
-
<InputGroupTextarea placeholder="Enter text" rows={4} />
|
|
335
|
-
</InputGroup>,
|
|
336
|
-
);
|
|
337
|
-
await expect.element(page.getByRole("textbox")).toBeInTheDocument();
|
|
338
|
-
});
|
|
339
|
-
|
|
340
|
-
test("supports rows attribute", async () => {
|
|
341
|
-
render(
|
|
342
|
-
<InputGroup>
|
|
343
|
-
<InputGroupTextarea placeholder="Enter text" rows={6} />
|
|
344
|
-
</InputGroup>,
|
|
345
|
-
);
|
|
346
|
-
await expect
|
|
347
|
-
.element(page.getByRole("textbox"))
|
|
348
|
-
.toHaveAttribute("rows", "6");
|
|
349
|
-
});
|
|
350
|
-
|
|
351
|
-
test("has data-slot attribute", async () => {
|
|
352
|
-
render(
|
|
353
|
-
<InputGroup>
|
|
354
|
-
<InputGroupTextarea placeholder="Enter text" />
|
|
355
|
-
</InputGroup>,
|
|
356
|
-
);
|
|
357
|
-
await expect
|
|
358
|
-
.element(page.getByRole("textbox"))
|
|
359
|
-
.toHaveAttribute("data-slot", "input-group-control");
|
|
360
|
-
});
|
|
361
|
-
});
|
|
362
|
-
});
|
|
@@ -1,228 +0,0 @@
|
|
|
1
|
-
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
2
|
-
import { Input } from ".";
|
|
3
|
-
|
|
4
|
-
const meta: Meta<typeof Input> = {
|
|
5
|
-
title: "Atoms/Input",
|
|
6
|
-
component: Input,
|
|
7
|
-
} as Meta<typeof Input>;
|
|
8
|
-
|
|
9
|
-
export default meta;
|
|
10
|
-
type Story = StoryObj<typeof Input>;
|
|
11
|
-
|
|
12
|
-
export const Playground: Story = {
|
|
13
|
-
render: (args) => <Input {...args} />,
|
|
14
|
-
};
|
|
15
|
-
Playground.argTypes = {
|
|
16
|
-
size: {
|
|
17
|
-
control: {
|
|
18
|
-
type: "radio",
|
|
19
|
-
},
|
|
20
|
-
options: ["sm", "default", "lg"],
|
|
21
|
-
},
|
|
22
|
-
disabled: {
|
|
23
|
-
control: {
|
|
24
|
-
type: "boolean",
|
|
25
|
-
},
|
|
26
|
-
},
|
|
27
|
-
error: {
|
|
28
|
-
control: {
|
|
29
|
-
type: "boolean",
|
|
30
|
-
},
|
|
31
|
-
},
|
|
32
|
-
placeholder: {
|
|
33
|
-
control: {
|
|
34
|
-
type: "text",
|
|
35
|
-
},
|
|
36
|
-
},
|
|
37
|
-
};
|
|
38
|
-
Playground.args = {
|
|
39
|
-
size: "default",
|
|
40
|
-
disabled: false,
|
|
41
|
-
error: false,
|
|
42
|
-
placeholder: "Enter text...",
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
// =============================================================================
|
|
46
|
-
// States
|
|
47
|
-
// =============================================================================
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Default state - white background with subtle border
|
|
51
|
-
*/
|
|
52
|
-
export const Default = () => <Input placeholder="Enter text..." />;
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Filled state - shows how a filled input looks
|
|
56
|
-
*/
|
|
57
|
-
export const Filled = () => <Input defaultValue="Filled content" />;
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Error state - red border and text
|
|
61
|
-
*/
|
|
62
|
-
export const ErrorState = () => <Input error placeholder="Invalid input" />;
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Disabled state - grayed out and not interactive
|
|
66
|
-
*/
|
|
67
|
-
export const Disabled = () => <Input disabled placeholder="Disabled input" />;
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* All states comparison
|
|
71
|
-
*/
|
|
72
|
-
export const AllStates = () => (
|
|
73
|
-
<div className="flex flex-col gap-16 max-w-[320px]">
|
|
74
|
-
<div>
|
|
75
|
-
<p className="mb-8 text-12 text-text-muted">Default</p>
|
|
76
|
-
<Input placeholder="Enter text..." />
|
|
77
|
-
</div>
|
|
78
|
-
<div>
|
|
79
|
-
<p className="mb-8 text-12 text-text-muted">Hover (hover the input)</p>
|
|
80
|
-
<Input placeholder="Hover me..." />
|
|
81
|
-
</div>
|
|
82
|
-
<div>
|
|
83
|
-
<p className="mb-8 text-12 text-text-muted">Focus (click the input)</p>
|
|
84
|
-
<Input placeholder="Click to focus..." />
|
|
85
|
-
</div>
|
|
86
|
-
<div>
|
|
87
|
-
<p className="mb-8 text-12 text-text-muted">Filled</p>
|
|
88
|
-
<Input defaultValue="Filled content" />
|
|
89
|
-
</div>
|
|
90
|
-
<div>
|
|
91
|
-
<p className="mb-8 text-12 text-text-muted">Error</p>
|
|
92
|
-
<Input error placeholder="Invalid input" />
|
|
93
|
-
</div>
|
|
94
|
-
<div>
|
|
95
|
-
<p className="mb-8 text-12 text-text-muted">Disabled</p>
|
|
96
|
-
<Input disabled placeholder="Disabled input" />
|
|
97
|
-
</div>
|
|
98
|
-
</div>
|
|
99
|
-
);
|
|
100
|
-
|
|
101
|
-
// =============================================================================
|
|
102
|
-
// Sizes
|
|
103
|
-
// =============================================================================
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Small size - compact input for tight layouts
|
|
107
|
-
*/
|
|
108
|
-
export const Small = () => <Input size="sm" placeholder="Small input" />;
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Default size - standard 48px height
|
|
112
|
-
*/
|
|
113
|
-
export const Medium = () => (
|
|
114
|
-
<Input size="default" placeholder="Default input" />
|
|
115
|
-
);
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Large size - more prominent input
|
|
119
|
-
*/
|
|
120
|
-
export const Large = () => <Input size="lg" placeholder="Large input" />;
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* All sizes comparison
|
|
124
|
-
*/
|
|
125
|
-
export const AllSizes = () => (
|
|
126
|
-
<div className="flex flex-col gap-16 max-w-[320px]">
|
|
127
|
-
<div>
|
|
128
|
-
<p className="mb-8 text-12 text-text-muted">Small (36px)</p>
|
|
129
|
-
<Input size="sm" placeholder="Small input" />
|
|
130
|
-
</div>
|
|
131
|
-
<div>
|
|
132
|
-
<p className="mb-8 text-12 text-text-muted">Default (48px)</p>
|
|
133
|
-
<Input size="default" placeholder="Default input" />
|
|
134
|
-
</div>
|
|
135
|
-
<div>
|
|
136
|
-
<p className="mb-8 text-12 text-text-muted">Large (56px)</p>
|
|
137
|
-
<Input size="lg" placeholder="Large input" />
|
|
138
|
-
</div>
|
|
139
|
-
</div>
|
|
140
|
-
);
|
|
141
|
-
|
|
142
|
-
// =============================================================================
|
|
143
|
-
// Types
|
|
144
|
-
// =============================================================================
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* Email input type
|
|
148
|
-
*/
|
|
149
|
-
export const EmailType = () => (
|
|
150
|
-
<Input type="email" placeholder="Enter your email" />
|
|
151
|
-
);
|
|
152
|
-
|
|
153
|
-
/**
|
|
154
|
-
* Password input type
|
|
155
|
-
*/
|
|
156
|
-
export const PasswordType = () => (
|
|
157
|
-
<Input type="password" placeholder="Enter your password" />
|
|
158
|
-
);
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* Number input type
|
|
162
|
-
*/
|
|
163
|
-
export const NumberType = () => (
|
|
164
|
-
<Input type="number" placeholder="Enter a number" />
|
|
165
|
-
);
|
|
166
|
-
|
|
167
|
-
/**
|
|
168
|
-
* Search input type
|
|
169
|
-
*/
|
|
170
|
-
export const SearchType = () => <Input type="search" placeholder="Search..." />;
|
|
171
|
-
|
|
172
|
-
// =============================================================================
|
|
173
|
-
// Examples
|
|
174
|
-
// =============================================================================
|
|
175
|
-
|
|
176
|
-
/**
|
|
177
|
-
* Form field example with label
|
|
178
|
-
*/
|
|
179
|
-
export const WithLabel = () => (
|
|
180
|
-
<div className="flex flex-col gap-8 max-w-[320px]">
|
|
181
|
-
<label
|
|
182
|
-
htmlFor="email-input"
|
|
183
|
-
className="text-14 font-medium text-text-primary"
|
|
184
|
-
>
|
|
185
|
-
Email Address
|
|
186
|
-
</label>
|
|
187
|
-
<Input id="email-input" type="email" placeholder="you@example.com" />
|
|
188
|
-
</div>
|
|
189
|
-
);
|
|
190
|
-
|
|
191
|
-
/**
|
|
192
|
-
* Form field with error message
|
|
193
|
-
*/
|
|
194
|
-
export const WithErrorMessage = () => (
|
|
195
|
-
<div className="flex flex-col gap-8 max-w-[320px]">
|
|
196
|
-
<label
|
|
197
|
-
htmlFor="email-error"
|
|
198
|
-
className="text-14 font-medium text-text-primary"
|
|
199
|
-
>
|
|
200
|
-
Email Address
|
|
201
|
-
</label>
|
|
202
|
-
<Input
|
|
203
|
-
id="email-error"
|
|
204
|
-
type="email"
|
|
205
|
-
error
|
|
206
|
-
placeholder="you@example.com"
|
|
207
|
-
defaultValue="invalid-email"
|
|
208
|
-
/>
|
|
209
|
-
<p className="text-12 text-ui-error-color">
|
|
210
|
-
Please enter a valid email address
|
|
211
|
-
</p>
|
|
212
|
-
</div>
|
|
213
|
-
);
|
|
214
|
-
|
|
215
|
-
/**
|
|
216
|
-
* Required field indicator
|
|
217
|
-
*/
|
|
218
|
-
export const RequiredField = () => (
|
|
219
|
-
<div className="flex flex-col gap-8 max-w-[320px]">
|
|
220
|
-
<label
|
|
221
|
-
htmlFor="required-input"
|
|
222
|
-
className="text-14 font-medium text-text-primary"
|
|
223
|
-
>
|
|
224
|
-
Full Name <span className="text-ui-error-color">*</span>
|
|
225
|
-
</label>
|
|
226
|
-
<Input id="required-input" required placeholder="John Doe" />
|
|
227
|
-
</div>
|
|
228
|
-
);
|