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