@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.
Files changed (160) hide show
  1. package/package.json +10 -2
  2. package/src/App.css +0 -0
  3. package/src/App.tsx +0 -7
  4. package/src/assets/fonts/PPNeueMontreal-Variable.woff2 +0 -0
  5. package/src/assets/react.svg +0 -1
  6. package/src/components/atoms/accordion/accordion.stories.tsx +0 -228
  7. package/src/components/atoms/accordion/accordion.test.tsx +0 -231
  8. package/src/components/atoms/accordion/index.ts +0 -6
  9. package/src/components/atoms/background/background.test.tsx +0 -213
  10. package/src/components/atoms/background/index.ts +0 -22
  11. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-charcoal-chromium-darwin.png +0 -0
  12. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-charcoal-chromium-linux.png +0 -0
  13. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-charcoal-outline-chromium-darwin.png +0 -0
  14. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-charcoal-outline-chromium-linux.png +0 -0
  15. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-charcoal-outline-quiet-chromium-darwin.png +0 -0
  16. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-charcoal-outline-quiet-chromium-linux.png +0 -0
  17. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-disabled-chromium-darwin.png +0 -0
  18. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-disabled-chromium-linux.png +0 -0
  19. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-ivory-chromium-darwin.png +0 -0
  20. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-ivory-chromium-linux.png +0 -0
  21. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-ivory-outline-chromium-darwin.png +0 -0
  22. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-ivory-outline-chromium-linux.png +0 -0
  23. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-ivory-outline-quiet-chromium-darwin.png +0 -0
  24. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-ivory-outline-quiet-chromium-linux.png +0 -0
  25. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-size-large-chromium-darwin.png +0 -0
  26. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-size-large-chromium-linux.png +0 -0
  27. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-size-medium-chromium-darwin.png +0 -0
  28. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-size-medium-chromium-linux.png +0 -0
  29. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-size-small-chromium-darwin.png +0 -0
  30. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-size-small-chromium-linux.png +0 -0
  31. package/src/components/atoms/button/button.stories.tsx +0 -289
  32. package/src/components/atoms/button/button.test.tsx +0 -419
  33. package/src/components/atoms/button/button.visual.test.tsx +0 -98
  34. package/src/components/atoms/button/icon-button.stories.tsx +0 -260
  35. package/src/components/atoms/button/icon-button.test.tsx +0 -186
  36. package/src/components/atoms/button/index.ts +0 -6
  37. package/src/components/atoms/input/index.ts +0 -17
  38. package/src/components/atoms/input/input-group.stories.tsx +0 -646
  39. package/src/components/atoms/input/input-group.test.tsx +0 -362
  40. package/src/components/atoms/input/input.stories.tsx +0 -228
  41. package/src/components/atoms/input/input.test.tsx +0 -167
  42. package/src/components/atoms/ndstudio-footer/index.ts +0 -1
  43. package/src/components/atoms/pager-control/index.ts +0 -5
  44. package/src/components/atoms/pager-control/pager-control.stories.tsx +0 -207
  45. package/src/components/atoms/pager-control/pager-control.test.tsx +0 -130
  46. package/src/components/atoms/popover/index.ts +0 -30
  47. package/src/components/atoms/popover/popover.stories.tsx +0 -531
  48. package/src/components/atoms/popover/popover.test.tsx +0 -486
  49. package/src/components/atoms/select/index.ts +0 -18
  50. package/src/components/atoms/select/select.stories.tsx +0 -455
  51. package/src/components/atoms/tooltip/index.ts +0 -24
  52. package/src/components/atoms/tooltip/tooltip.stories.tsx +0 -348
  53. package/src/components/atoms/tooltip/tooltip.test.tsx +0 -363
  54. package/src/components/dev-tools/dev-toolbar/dev-toolbar.stories.tsx +0 -73
  55. package/src/components/dev-tools/dev-toolbar/index.ts +0 -1
  56. package/src/components/dev-tools/grid-overlay/index.ts +0 -1
  57. package/src/components/dev-tools/index.ts +0 -2
  58. package/src/components/foundation/typography/typography.stories.tsx +0 -401
  59. package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-default-vertical-chromium-darwin.png +0 -0
  60. package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-default-vertical-chromium-linux.png +0 -0
  61. package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-horizontal-chromium-darwin.png +0 -0
  62. package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-horizontal-chromium-linux.png +0 -0
  63. package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-minimal-chromium-darwin.png +0 -0
  64. package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-minimal-chromium-linux.png +0 -0
  65. package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-without-actions-chromium-darwin.png +0 -0
  66. package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-without-actions-chromium-linux.png +0 -0
  67. package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-without-eyebrow-chromium-darwin.png +0 -0
  68. package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-without-eyebrow-chromium-linux.png +0 -0
  69. package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-without-image-chromium-darwin.png +0 -0
  70. package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-without-image-chromium-linux.png +0 -0
  71. package/src/components/organisms/card/card.stories.tsx +0 -293
  72. package/src/components/organisms/card/card.test.tsx +0 -247
  73. package/src/components/organisms/card/card.visual.test.tsx +0 -197
  74. package/src/components/organisms/card/index.ts +0 -26
  75. package/src/components/organisms/navbar/__screenshots__/navbar.visual.test.tsx/navbar-active-link-chromium-darwin.png +0 -0
  76. package/src/components/organisms/navbar/__screenshots__/navbar.visual.test.tsx/navbar-active-link-chromium-linux.png +0 -0
  77. package/src/components/organisms/navbar/__screenshots__/navbar.visual.test.tsx/navbar-brand-only-chromium-darwin.png +0 -0
  78. package/src/components/organisms/navbar/__screenshots__/navbar.visual.test.tsx/navbar-brand-only-chromium-linux.png +0 -0
  79. package/src/components/organisms/navbar/__screenshots__/navbar.visual.test.tsx/navbar-default-chromium-darwin.png +0 -0
  80. package/src/components/organisms/navbar/__screenshots__/navbar.visual.test.tsx/navbar-default-chromium-linux.png +0 -0
  81. package/src/components/organisms/navbar/index.ts +0 -18
  82. package/src/components/organisms/navbar/navbar.stories.tsx +0 -313
  83. package/src/components/organisms/navbar/navbar.test.tsx +0 -190
  84. package/src/components/organisms/navbar/navbar.visual.test.tsx +0 -85
  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
  86. package/src/components/organisms/us-gov-banner/__screenshots__/us-gov-banner.visual.test.tsx/us-gov-banner-custom-icon-chromium-linux.png +0 -0
  87. package/src/components/organisms/us-gov-banner/__screenshots__/us-gov-banner.visual.test.tsx/us-gov-banner-custom-text-chromium-darwin.png +0 -0
  88. package/src/components/organisms/us-gov-banner/__screenshots__/us-gov-banner.visual.test.tsx/us-gov-banner-custom-text-chromium-linux.png +0 -0
  89. package/src/components/organisms/us-gov-banner/__screenshots__/us-gov-banner.visual.test.tsx/us-gov-banner-default-chromium-darwin.png +0 -0
  90. package/src/components/organisms/us-gov-banner/__screenshots__/us-gov-banner.visual.test.tsx/us-gov-banner-default-chromium-linux.png +0 -0
  91. package/src/components/organisms/us-gov-banner/index.ts +0 -5
  92. package/src/components/organisms/us-gov-banner/us-gov-banner.stories.tsx +0 -35
  93. package/src/components/organisms/us-gov-banner/us-gov-banner.test.tsx +0 -107
  94. package/src/components/organisms/us-gov-banner/us-gov-banner.visual.test.tsx +0 -46
  95. package/src/components/sections/banner/banner.stories.tsx +0 -150
  96. package/src/components/sections/banner/banner.test.tsx +0 -185
  97. package/src/components/sections/banner/index.ts +0 -2
  98. package/src/components/sections/card-grid/card-grid.stories.tsx +0 -351
  99. package/src/components/sections/card-grid/index.ts +0 -1
  100. package/src/components/sections/faq-section/faq-section.stories.tsx +0 -453
  101. package/src/components/sections/faq-section/index.ts +0 -2
  102. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a1-desktop-chromium-darwin.png +0 -0
  103. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a1-desktop-chromium-linux.png +0 -0
  104. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a1-mobile-chromium-darwin.png +0 -0
  105. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a1-mobile-chromium-linux.png +0 -0
  106. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a1-tablet-chromium-darwin.png +0 -0
  107. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a1-tablet-chromium-linux.png +0 -0
  108. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a2-desktop-chromium-darwin.png +0 -0
  109. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a2-desktop-chromium-linux.png +0 -0
  110. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a2-mobile-chromium-darwin.png +0 -0
  111. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a2-mobile-chromium-linux.png +0 -0
  112. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a2-tablet-chromium-darwin.png +0 -0
  113. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a2-tablet-chromium-linux.png +0 -0
  114. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a3-desktop-chromium-darwin.png +0 -0
  115. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a3-desktop-chromium-linux.png +0 -0
  116. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a3-mobile-chromium-darwin.png +0 -0
  117. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a3-mobile-chromium-linux.png +0 -0
  118. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a3-tablet-chromium-darwin.png +0 -0
  119. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a3-tablet-chromium-linux.png +0 -0
  120. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-custom-class-chromium-darwin.png +0 -0
  121. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-custom-class-chromium-linux.png +0 -0
  122. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-default-chromium-linux.png +0 -0
  123. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-long-title-chromium-darwin.png +0 -0
  124. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-long-title-chromium-linux.png +0 -0
  125. package/src/components/sections/hero/hero.stories.tsx +0 -397
  126. package/src/components/sections/hero/hero.test.tsx +0 -138
  127. package/src/components/sections/hero/hero.visual.test.tsx +0 -140
  128. package/src/components/sections/hero/index.ts +0 -23
  129. package/src/components/sections/prose/__screenshots__/prose.visual.test.tsx/prose-h3-heading-chromium-darwin.png +0 -0
  130. package/src/components/sections/prose/__screenshots__/prose.visual.test.tsx/prose-h3-heading-chromium-linux.png +0 -0
  131. package/src/components/sections/prose/__screenshots__/prose.visual.test.tsx/prose-multiple-paragraphs-chromium-darwin.png +0 -0
  132. package/src/components/sections/prose/__screenshots__/prose.visual.test.tsx/prose-multiple-paragraphs-chromium-linux.png +0 -0
  133. package/src/components/sections/prose/__screenshots__/prose.visual.test.tsx/prose-multiple-sections-chromium-darwin.png +0 -0
  134. package/src/components/sections/prose/__screenshots__/prose.visual.test.tsx/prose-multiple-sections-chromium-linux.png +0 -0
  135. package/src/components/sections/prose/__screenshots__/prose.visual.test.tsx/prose-single-section-chromium-darwin.png +0 -0
  136. package/src/components/sections/prose/__screenshots__/prose.visual.test.tsx/prose-single-section-chromium-linux.png +0 -0
  137. package/src/components/sections/prose/index.ts +0 -6
  138. package/src/components/sections/prose/prose.stories.tsx +0 -144
  139. package/src/components/sections/prose/prose.test.tsx +0 -178
  140. package/src/components/sections/prose/prose.visual.test.tsx +0 -105
  141. package/src/components/sections/quote-block/index.ts +0 -5
  142. package/src/components/sections/river/index.ts +0 -1
  143. package/src/components/sections/river/river.stories.tsx +0 -237
  144. package/src/components/sections/river/river.test.tsx +0 -268
  145. package/src/components/sections/tout/index.ts +0 -1
  146. package/src/components/sections/tout/tout.stories.tsx +0 -171
  147. package/src/components/sections/tout/tout.test.tsx +0 -242
  148. package/src/components/sections/two-column-section/index.ts +0 -5
  149. package/src/components/sections/two-column-section/two-column-section.stories.tsx +0 -285
  150. package/src/components/shared/index.ts +0 -5
  151. package/src/index.ts +0 -293
  152. package/src/main.tsx +0 -13
  153. package/src/stories/grid-system.stories.tsx +0 -309
  154. package/src/stories/introduction.mdx +0 -128
  155. package/src/stories/theme-provider.stories.tsx +0 -349
  156. package/src/stories/token-showcase.stories.tsx +0 -73
  157. package/src/stories/token-showcase.tsx +0 -777
  158. package/src/styles.css +0 -14
  159. package/src/tests/token-resolution.test.tsx +0 -298
  160. 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
- );