@nationaldesignstudio/react 0.3.0 → 0.5.0

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 (79) hide show
  1. package/dist/component-registry.md +1310 -127
  2. package/dist/components/atoms/button/button.d.ts +55 -47
  3. package/dist/components/atoms/button/button.figma.d.ts +1 -0
  4. package/dist/components/atoms/input/input.d.ts +24 -24
  5. package/dist/components/atoms/popover/popover.d.ts +195 -0
  6. package/dist/components/atoms/select/select.d.ts +24 -24
  7. package/dist/components/atoms/tooltip/tooltip.d.ts +161 -0
  8. package/dist/components/organisms/card/card.d.ts +1 -1
  9. package/dist/components/sections/hero/hero.d.ts +2 -2
  10. package/dist/components/sections/tout/tout.d.ts +3 -3
  11. package/dist/components/shared/floating-arrow.d.ts +34 -0
  12. package/dist/index.d.ts +8 -0
  13. package/dist/index.js +11602 -8499
  14. package/dist/index.js.map +1 -1
  15. package/dist/lib/form-control.d.ts +25 -24
  16. package/dist/tokens.css +4797 -3940
  17. package/package.json +2 -1
  18. package/src/components/atoms/accordion/accordion.stories.tsx +1 -1
  19. package/src/components/atoms/accordion/accordion.tsx +2 -2
  20. package/src/components/atoms/button/button.figma.tsx +37 -0
  21. package/src/components/atoms/button/button.stories.tsx +236 -140
  22. package/src/components/atoms/button/button.test.tsx +289 -5
  23. package/src/components/atoms/button/button.tsx +37 -33
  24. package/src/components/atoms/button/button.visual.test.tsx +26 -76
  25. package/src/components/atoms/button/icon-button.stories.tsx +44 -101
  26. package/src/components/atoms/button/icon-button.test.tsx +26 -94
  27. package/src/components/atoms/button/icon-button.tsx +3 -3
  28. package/src/components/atoms/input/input-group.stories.tsx +4 -8
  29. package/src/components/atoms/input/input-group.test.tsx +14 -28
  30. package/src/components/atoms/input/input-group.tsx +57 -32
  31. package/src/components/atoms/input/input.stories.tsx +14 -18
  32. package/src/components/atoms/input/input.test.tsx +4 -20
  33. package/src/components/atoms/input/input.tsx +16 -9
  34. package/src/components/atoms/pager-control/pager-control.stories.tsx +6 -8
  35. package/src/components/atoms/pager-control/pager-control.tsx +12 -12
  36. package/src/components/atoms/popover/index.ts +30 -0
  37. package/src/components/atoms/popover/popover.stories.tsx +531 -0
  38. package/src/components/atoms/popover/popover.test.tsx +486 -0
  39. package/src/components/atoms/popover/popover.tsx +488 -0
  40. package/src/components/atoms/select/select.tsx +12 -8
  41. package/src/components/atoms/tooltip/index.ts +24 -0
  42. package/src/components/atoms/tooltip/tooltip.stories.tsx +348 -0
  43. package/src/components/atoms/tooltip/tooltip.test.tsx +363 -0
  44. package/src/components/atoms/tooltip/tooltip.tsx +347 -0
  45. package/src/components/dev-tools/dev-toolbar/dev-toolbar.stories.tsx +8 -13
  46. package/src/components/dev-tools/dev-toolbar/dev-toolbar.tsx +3 -3
  47. package/src/components/organisms/card/card.stories.tsx +19 -19
  48. package/src/components/organisms/card/card.tsx +1 -1
  49. package/src/components/organisms/card/card.visual.test.tsx +11 -11
  50. package/src/components/organisms/navbar/navbar.visual.test.tsx +2 -2
  51. package/src/components/organisms/us-gov-banner/us-gov-banner.tsx +2 -2
  52. package/src/components/sections/banner/banner.stories.tsx +1 -5
  53. package/src/components/sections/banner/banner.test.tsx +2 -2
  54. package/src/components/sections/banner/banner.tsx +6 -6
  55. package/src/components/sections/card-grid/card-grid.tsx +4 -4
  56. package/src/components/sections/hero/hero.stories.tsx +7 -7
  57. package/src/components/sections/hero/hero.tsx +10 -11
  58. package/src/components/sections/prose/prose.tsx +2 -2
  59. package/src/components/sections/river/river.test.tsx +3 -3
  60. package/src/components/sections/river/river.tsx +6 -12
  61. package/src/components/sections/tout/tout.stories.tsx +7 -31
  62. package/src/components/sections/tout/tout.tsx +9 -9
  63. package/src/components/sections/two-column-section/two-column-section.tsx +7 -9
  64. package/src/components/shared/floating-arrow.tsx +78 -0
  65. package/src/components/shared/index.ts +5 -0
  66. package/src/index.ts +57 -0
  67. package/src/lib/form-control.ts +8 -6
  68. package/src/stories/grid-system.stories.tsx +309 -0
  69. package/src/stories/{ThemeProvider.stories.tsx → theme-provider.stories.tsx} +7 -19
  70. package/src/stories/{TokenShowcase.stories.tsx → token-showcase.stories.tsx} +1 -1
  71. package/src/stories/{TokenShowcase.tsx → token-showcase.tsx} +34 -34
  72. package/src/styles.css +3 -3
  73. package/src/tests/token-resolution.test.tsx +6 -9
  74. package/src/theme/hooks.ts +1 -1
  75. package/src/theme/index.ts +1 -1
  76. package/src/theme/theme-provider.test.tsx +270 -0
  77. package/src/theme/{ThemeProvider.tsx → theme-provider.tsx} +18 -2
  78. package/src/stories/GridSystem.stories.tsx +0 -84
  79. /package/src/stories/{Introduction.mdx → introduction.mdx} +0 -0
@@ -1,7 +1,6 @@
1
1
  import type { Meta, StoryObj } from "@storybook/react-vite";
2
2
  import { IconButton } from ".";
3
3
 
4
- // Simple placeholder icons for stories
5
4
  const SearchIcon = () => (
6
5
  <svg
7
6
  width="16"
@@ -66,7 +65,7 @@ Playground.argTypes = {
66
65
  control: {
67
66
  type: "radio",
68
67
  },
69
- options: ["sm", "default", "lg"],
68
+ options: ["sm", "md", "lg"],
70
69
  },
71
70
  disabled: {
72
71
  control: {
@@ -77,42 +76,38 @@ Playground.argTypes = {
77
76
  control: {
78
77
  type: "radio",
79
78
  },
80
- options: ["solid", "outline", "ghost", "subtle"],
81
- },
82
- colorScheme: {
83
- control: {
84
- type: "radio",
85
- },
86
- options: ["dark", "light"],
79
+ options: [
80
+ "primary",
81
+ "primary-outline",
82
+ "secondary",
83
+ "secondary-outline",
84
+ "ghost",
85
+ "ghost-inverse",
86
+ ],
87
87
  },
88
88
  rounded: {
89
89
  control: {
90
90
  type: "radio",
91
91
  },
92
- options: ["default", "sm", "full"],
92
+ options: ["default", "full"],
93
93
  },
94
94
  };
95
95
  Playground.args = {
96
- size: "default",
96
+ size: "md",
97
97
  disabled: false,
98
- variant: "solid",
99
- colorScheme: "dark",
98
+ variant: "primary",
100
99
  rounded: "default",
101
100
  "aria-label": "Search",
102
101
  };
103
102
 
104
- // =============================================================================
105
- // Variants (Dark Color Scheme - for light backgrounds)
106
- // =============================================================================
107
-
108
- export const Solid = () => (
109
- <IconButton variant="solid" aria-label="Search">
103
+ export const Primary = () => (
104
+ <IconButton variant="primary" aria-label="Search">
110
105
  <SearchIcon />
111
106
  </IconButton>
112
107
  );
113
108
 
114
- export const Outline = () => (
115
- <IconButton variant="outline" aria-label="Search">
109
+ export const PrimaryOutline = () => (
110
+ <IconButton variant="primary-outline" aria-label="Search">
116
111
  <SearchIcon />
117
112
  </IconButton>
118
113
  );
@@ -123,92 +118,64 @@ export const Ghost = () => (
123
118
  </IconButton>
124
119
  );
125
120
 
126
- export const Subtle = () => (
127
- <IconButton variant="subtle" aria-label="Search">
128
- <SearchIcon />
129
- </IconButton>
130
- );
131
-
132
- // =============================================================================
133
- // Variants (Light Color Scheme - for dark backgrounds)
134
- // =============================================================================
135
-
136
121
  const DarkBackground = ({ children }: { children: React.ReactNode }) => (
137
- <div className="rounded-radius-12 bg-gray-1200 p-spacing-32">{children}</div>
138
- );
139
-
140
- export const SolidLight = () => (
141
- <DarkBackground>
142
- <IconButton variant="solid" colorScheme="light" aria-label="Navigate">
143
- <ArrowRightIcon />
144
- </IconButton>
145
- </DarkBackground>
122
+ <div className="rounded-radius-12 bg-gray-1200 p-32">{children}</div>
146
123
  );
147
124
 
148
- export const OutlineLight = () => (
125
+ export const Secondary = () => (
149
126
  <DarkBackground>
150
- <IconButton variant="outline" colorScheme="light" aria-label="Navigate">
127
+ <IconButton variant="secondary" aria-label="Navigate">
151
128
  <ArrowRightIcon />
152
129
  </IconButton>
153
130
  </DarkBackground>
154
131
  );
155
132
 
156
- export const GhostLight = () => (
133
+ export const SecondaryOutline = () => (
157
134
  <DarkBackground>
158
- <IconButton variant="ghost" colorScheme="light" aria-label="Navigate">
135
+ <IconButton variant="secondary-outline" aria-label="Navigate">
159
136
  <ArrowRightIcon />
160
137
  </IconButton>
161
138
  </DarkBackground>
162
139
  );
163
140
 
164
- export const SubtleLight = () => (
141
+ export const GhostInverse = () => (
165
142
  <DarkBackground>
166
- <IconButton variant="subtle" colorScheme="light" aria-label="Navigate">
143
+ <IconButton variant="ghost-inverse" aria-label="Navigate">
167
144
  <ArrowRightIcon />
168
145
  </IconButton>
169
146
  </DarkBackground>
170
147
  );
171
148
 
172
- // =============================================================================
173
- // All Variants Comparison
174
- // =============================================================================
175
-
176
149
  export const AllVariants = () => (
177
- <div className="flex flex-col gap-spacing-32">
150
+ <div className="flex flex-col gap-32">
178
151
  <div>
179
- <h3 className="mb-spacing-16 text-14 font-medium text-text-secondary">
180
- Dark Color Scheme (for light backgrounds)
152
+ <h3 className="mb-16 text-14 font-medium text-text-secondary">
153
+ For Light Backgrounds
181
154
  </h3>
182
- <div className="flex gap-spacing-16">
183
- <IconButton variant="solid" aria-label="Solid">
155
+ <div className="flex gap-16">
156
+ <IconButton variant="primary" aria-label="Primary">
184
157
  <SearchIcon />
185
158
  </IconButton>
186
- <IconButton variant="outline" aria-label="Outline">
159
+ <IconButton variant="primary-outline" aria-label="Primary Outline">
187
160
  <SearchIcon />
188
161
  </IconButton>
189
162
  <IconButton variant="ghost" aria-label="Ghost">
190
163
  <SearchIcon />
191
164
  </IconButton>
192
- <IconButton variant="subtle" aria-label="Subtle">
193
- <SearchIcon />
194
- </IconButton>
195
165
  </div>
196
166
  </div>
197
167
  <DarkBackground>
198
- <h3 className="mb-spacing-16 text-14 font-medium text-gray-400">
199
- Light Color Scheme (for dark backgrounds)
168
+ <h3 className="mb-16 text-14 font-medium text-gray-400">
169
+ For Dark Backgrounds
200
170
  </h3>
201
- <div className="flex gap-spacing-16">
202
- <IconButton variant="solid" colorScheme="light" aria-label="Solid">
203
- <ArrowRightIcon />
204
- </IconButton>
205
- <IconButton variant="outline" colorScheme="light" aria-label="Outline">
171
+ <div className="flex gap-16">
172
+ <IconButton variant="secondary" aria-label="Secondary">
206
173
  <ArrowRightIcon />
207
174
  </IconButton>
208
- <IconButton variant="ghost" colorScheme="light" aria-label="Ghost">
175
+ <IconButton variant="secondary-outline" aria-label="Secondary Outline">
209
176
  <ArrowRightIcon />
210
177
  </IconButton>
211
- <IconButton variant="subtle" colorScheme="light" aria-label="Subtle">
178
+ <IconButton variant="ghost-inverse" aria-label="Ghost Inverse">
212
179
  <ArrowRightIcon />
213
180
  </IconButton>
214
181
  </div>
@@ -216,22 +183,12 @@ export const AllVariants = () => (
216
183
  </div>
217
184
  );
218
185
 
219
- // =============================================================================
220
- // Rounded Variants
221
- // =============================================================================
222
-
223
186
  export const RoundedDefault = () => (
224
187
  <IconButton rounded="default" aria-label="Search">
225
188
  <SearchIcon />
226
189
  </IconButton>
227
190
  );
228
191
 
229
- export const RoundedSm = () => (
230
- <IconButton rounded="sm" aria-label="Search">
231
- <SearchIcon />
232
- </IconButton>
233
- );
234
-
235
192
  export const RoundedFull = () => (
236
193
  <IconButton rounded="full" aria-label="Search">
237
194
  <SearchIcon />
@@ -239,32 +196,22 @@ export const RoundedFull = () => (
239
196
  );
240
197
 
241
198
  export const AllRounded = () => (
242
- <div className="flex gap-spacing-16">
199
+ <div className="flex gap-16">
243
200
  <div className="text-center">
244
201
  <IconButton rounded="default" aria-label="Default rounded">
245
202
  <SearchIcon />
246
203
  </IconButton>
247
- <p className="mt-spacing-8 text-12 text-text-muted">default</p>
248
- </div>
249
- <div className="text-center">
250
- <IconButton rounded="sm" aria-label="Small rounded">
251
- <SearchIcon />
252
- </IconButton>
253
- <p className="mt-spacing-8 text-12 text-text-muted">sm</p>
204
+ <p className="mt-8 text-12 text-text-muted">default</p>
254
205
  </div>
255
206
  <div className="text-center">
256
207
  <IconButton rounded="full" aria-label="Full rounded">
257
208
  <SearchIcon />
258
209
  </IconButton>
259
- <p className="mt-spacing-8 text-12 text-text-muted">full</p>
210
+ <p className="mt-8 text-12 text-text-muted">full</p>
260
211
  </div>
261
212
  </div>
262
213
  );
263
214
 
264
- // =============================================================================
265
- // Sizes
266
- // =============================================================================
267
-
268
215
  export const Small = () => (
269
216
  <IconButton size="sm" aria-label="Search">
270
217
  <ArrowRightIcon />
@@ -272,7 +219,7 @@ export const Small = () => (
272
219
  );
273
220
 
274
221
  export const Medium = () => (
275
- <IconButton size="default" aria-label="Search">
222
+ <IconButton size="md" aria-label="Search">
276
223
  <SearchIcon />
277
224
  </IconButton>
278
225
  );
@@ -284,32 +231,28 @@ export const Large = () => (
284
231
  );
285
232
 
286
233
  export const AllSizes = () => (
287
- <div className="flex items-center gap-spacing-16">
234
+ <div className="flex items-center gap-16">
288
235
  <div className="text-center">
289
236
  <IconButton size="sm" aria-label="Small">
290
237
  <ArrowRightIcon />
291
238
  </IconButton>
292
- <p className="mt-spacing-8 text-12 text-text-muted">sm (32px)</p>
239
+ <p className="mt-8 text-12 text-text-muted">sm (28px)</p>
293
240
  </div>
294
241
  <div className="text-center">
295
- <IconButton size="default" aria-label="Default">
242
+ <IconButton size="md" aria-label="Default">
296
243
  <SearchIcon />
297
244
  </IconButton>
298
- <p className="mt-spacing-8 text-12 text-text-muted">default (40px)</p>
245
+ <p className="mt-8 text-12 text-text-muted">md (40px)</p>
299
246
  </div>
300
247
  <div className="text-center">
301
248
  <IconButton size="lg" aria-label="Large">
302
249
  <SearchIcon />
303
250
  </IconButton>
304
- <p className="mt-spacing-8 text-12 text-text-muted">lg (48px)</p>
251
+ <p className="mt-8 text-12 text-text-muted">lg (56px)</p>
305
252
  </div>
306
253
  </div>
307
254
  );
308
255
 
309
- // =============================================================================
310
- // States
311
- // =============================================================================
312
-
313
256
  export const Disabled = () => (
314
257
  <IconButton disabled aria-label="Search">
315
258
  <SearchIcon />
@@ -3,7 +3,6 @@ import { page, userEvent } from "vitest/browser";
3
3
  import { render } from "vitest-browser-react";
4
4
  import { IconButton } from "./icon-button";
5
5
 
6
- // Simple icon for testing
7
6
  const TestIcon = () => (
8
7
  // biome-ignore lint/a11y/noSvgWithoutTitle: Test component doesn't need accessibility title
9
8
  <svg data-testid="test-icon" width="24" height="24" viewBox="0 0 24 24">
@@ -139,116 +138,49 @@ describe("IconButton", () => {
139
138
  });
140
139
  });
141
140
 
142
- describe("Data attributes", () => {
143
- test("includes data-variant attribute", async () => {
144
- render(
145
- <IconButton variant="ghost" aria-label="Ghost variant">
146
- <TestIcon />
147
- </IconButton>,
148
- );
149
- await expect
150
- .element(page.getByRole("button", { name: "Ghost variant" }))
151
- .toHaveAttribute("data-variant", "ghost");
152
- });
153
-
154
- test("includes data-size attribute", async () => {
155
- render(
156
- <IconButton size="lg" aria-label="Large size">
157
- <TestIcon />
158
- </IconButton>,
159
- );
160
- await expect
161
- .element(page.getByRole("button", { name: "Large size" }))
162
- .toHaveAttribute("data-size", "lg");
163
- });
164
-
165
- test("includes data-color-scheme attribute", async () => {
166
- render(
167
- <IconButton colorScheme="light" aria-label="Light scheme">
168
- <TestIcon />
169
- </IconButton>,
170
- );
171
- await expect
172
- .element(page.getByRole("button", { name: "Light scheme" }))
173
- .toHaveAttribute("data-color-scheme", "light");
174
- });
175
-
176
- test("includes data-rounded attribute", async () => {
177
- render(
178
- <IconButton rounded="full" aria-label="Full rounded">
179
- <TestIcon />
180
- </IconButton>,
181
- );
182
- await expect
183
- .element(page.getByRole("button", { name: "Full rounded" }))
184
- .toHaveAttribute("data-rounded", "full");
185
- });
186
-
187
- test("has default data attributes when not specified", async () => {
188
- render(
189
- <IconButton aria-label="Defaults">
190
- <TestIcon />
191
- </IconButton>,
192
- );
193
- const button = page.getByRole("button", { name: "Defaults" });
194
- await expect.element(button).toHaveAttribute("data-variant", "solid");
195
- await expect.element(button).toHaveAttribute("data-size", "default");
196
- await expect.element(button).toHaveAttribute("data-color-scheme", "dark");
197
- await expect.element(button).toHaveAttribute("data-rounded", "default");
198
- });
199
- });
200
-
201
141
  describe("Variants", () => {
202
- test("applies solid variant classes by default", async () => {
142
+ test("primary variant has visible background color", async () => {
203
143
  render(
204
144
  <IconButton aria-label="Default">
205
145
  <TestIcon />
206
146
  </IconButton>,
207
147
  );
208
148
  const button = page.getByRole("button", { name: "Default" });
209
- await expect.element(button).toHaveClass(/bg-gray-1200/);
210
- });
149
+ await expect.element(button).toBeInTheDocument();
211
150
 
212
- test("applies ghost variant classes", async () => {
213
- render(
214
- <IconButton variant="ghost" aria-label="Ghost">
215
- <TestIcon />
216
- </IconButton>,
217
- );
218
- const button = page.getByRole("button", { name: "Ghost" });
219
- await expect.element(button).toHaveClass(/text-gray-700/);
151
+ const element = button.element();
152
+ const bgColor = window.getComputedStyle(element).backgroundColor;
153
+ expect(bgColor).not.toBe("rgba(0, 0, 0, 0)");
154
+ expect(bgColor).not.toBe("transparent");
220
155
  });
221
156
  });
222
157
 
223
158
  describe("Sizes", () => {
224
- test("applies small size classes", async () => {
159
+ test("different sizes have different dimensions", async () => {
225
160
  render(
226
- <IconButton size="sm" aria-label="Small">
227
- <TestIcon />
228
- </IconButton>,
161
+ <>
162
+ <IconButton size="sm" aria-label="Small">
163
+ <TestIcon />
164
+ </IconButton>
165
+ <IconButton size="lg" aria-label="Large">
166
+ <TestIcon />
167
+ </IconButton>
168
+ </>,
229
169
  );
230
- const button = page.getByRole("button", { name: "Small" });
231
- await expect.element(button).toHaveClass(/size-32/);
232
- });
233
170
 
234
- test("applies default size classes", async () => {
235
- render(
236
- <IconButton size="default" aria-label="Default">
237
- <TestIcon />
238
- </IconButton>,
239
- );
240
- const button = page.getByRole("button", { name: "Default" });
241
- await expect.element(button).toHaveClass(/size-40/);
242
- });
171
+ const smallBtn = page.getByRole("button", { name: "Small" });
172
+ const largeBtn = page.getByRole("button", { name: "Large" });
173
+ await expect.element(smallBtn).toBeInTheDocument();
174
+ await expect.element(largeBtn).toBeInTheDocument();
243
175
 
244
- test("applies large size classes", async () => {
245
- render(
246
- <IconButton size="lg" aria-label="Large">
247
- <TestIcon />
248
- </IconButton>,
176
+ const smallWidth = Number.parseFloat(
177
+ window.getComputedStyle(smallBtn.element()).width,
178
+ );
179
+ const largeWidth = Number.parseFloat(
180
+ window.getComputedStyle(largeBtn.element()).width,
249
181
  );
250
- const button = page.getByRole("button", { name: "Large" });
251
- await expect.element(button).toHaveClass(/size-48/);
182
+
183
+ expect(largeWidth).toBeGreaterThan(smallWidth);
252
184
  });
253
185
  });
254
186
  });
@@ -61,9 +61,9 @@ const iconButtonVariants = tv({
61
61
  "bg-button-ghost-inverse-bg text-button-ghost-inverse-text hover:bg-button-ghost-inverse-bg-hover hover:text-button-ghost-inverse-text-hover border-transparent focus-visible:ring-gray-50 focus-visible:ring-offset-gray-1000",
62
62
  },
63
63
  size: {
64
- sm: "size-28 rounded-4",
65
- md: "size-40 rounded-6",
66
- lg: "size-56 rounded-10",
64
+ sm: "size-28 rounded-surface-button-small",
65
+ md: "size-40 rounded-surface-button-medium",
66
+ lg: "size-56 rounded-surface-button-large",
67
67
  },
68
68
  rounded: {
69
69
  default: "",
@@ -279,7 +279,7 @@ export const ChatInput = () => (
279
279
  <InputGroupText>52% used</InputGroupText>
280
280
  <span className="ml-auto h-16 w-px bg-ui-color-border" />
281
281
  <InputGroupButton
282
- variant="solid"
282
+ variant="primary"
283
283
  className="rounded-full"
284
284
  size="icon-sm"
285
285
  aria-label="Send"
@@ -343,7 +343,7 @@ export const PillInput = () => (
343
343
  <InputGroup className="max-w-sm rounded-full">
344
344
  <InputGroupAddon>
345
345
  <InputGroupButton
346
- variant="outline"
346
+ variant="primary-outline"
347
347
  size="icon-xs"
348
348
  className="rounded-full"
349
349
  >
@@ -474,7 +474,7 @@ export const TextareaWithActions = () => (
474
474
  className="border-t border-ui-color-border pt-8"
475
475
  >
476
476
  <InputGroupText>Line 1, Column 1</InputGroupText>
477
- <InputGroupButton className="ml-auto" size="sm" variant="solid">
477
+ <InputGroupButton className="ml-auto" size="sm" variant="primary">
478
478
  Run <SendIcon />
479
479
  </InputGroupButton>
480
480
  </InputGroupAddon>
@@ -552,11 +552,7 @@ export const WithLabel = () => (
552
552
  </InputGroupAddon>
553
553
  </InputGroup>
554
554
  <InputGroup>
555
- <InputGroupInput
556
- id="email-2"
557
- placeholder="shadcn@vercel.com"
558
- className="!pl-12"
559
- />
555
+ <InputGroupInput id="email-2" placeholder="shadcn@vercel.com" />
560
556
  <InputGroupAddon align="block-start">
561
557
  <label
562
558
  htmlFor="email-2"
@@ -151,34 +151,34 @@ describe("InputGroup", () => {
151
151
  });
152
152
 
153
153
  describe("Variants", () => {
154
- test("applies default size classes", async () => {
154
+ test("renders with default size", async () => {
155
155
  render(
156
156
  <InputGroup>
157
157
  <InputGroupInput placeholder="Default" />
158
158
  </InputGroup>,
159
159
  );
160
160
  const group = page.getByRole("group");
161
- await expect.element(group).toHaveClass(/h-36/);
161
+ await expect.element(group).toBeInTheDocument();
162
162
  });
163
163
 
164
- test("applies small size classes", async () => {
164
+ test("renders with small size", async () => {
165
165
  render(
166
166
  <InputGroup size="sm">
167
167
  <InputGroupInput placeholder="Small" />
168
168
  </InputGroup>,
169
169
  );
170
170
  const group = page.getByRole("group");
171
- await expect.element(group).toHaveClass(/h-32/);
171
+ await expect.element(group).toBeInTheDocument();
172
172
  });
173
173
 
174
- test("applies large size classes", async () => {
174
+ test("renders with large size", async () => {
175
175
  render(
176
176
  <InputGroup size="lg">
177
177
  <InputGroupInput placeholder="Large" />
178
178
  </InputGroup>,
179
179
  );
180
180
  const group = page.getByRole("group");
181
- await expect.element(group).toHaveClass(/h-48/);
181
+ await expect.element(group).toBeInTheDocument();
182
182
  });
183
183
 
184
184
  test("applies disabled data attribute", async () => {
@@ -191,29 +191,19 @@ describe("InputGroup", () => {
191
191
  await expect.element(group).toHaveAttribute("data-disabled", "true");
192
192
  });
193
193
 
194
- test("applies semantic token classes for background", async () => {
194
+ test("has expected data-slot attribute", async () => {
195
195
  render(
196
196
  <InputGroup>
197
197
  <InputGroupInput placeholder="Default" />
198
198
  </InputGroup>,
199
199
  );
200
200
  const group = page.getByRole("group").first();
201
- await expect.element(group).toHaveClass(/bg-ui-control-background/);
202
- });
203
-
204
- test("applies semantic token classes for border", async () => {
205
- render(
206
- <InputGroup>
207
- <InputGroupInput placeholder="Default" />
208
- </InputGroup>,
209
- );
210
- const group = page.getByRole("group").first();
211
- await expect.element(group).toHaveClass(/border-ui-color-border/);
201
+ await expect.element(group).toHaveAttribute("data-slot", "input-group");
212
202
  });
213
203
  });
214
204
 
215
205
  describe("Addon Positioning", () => {
216
- test("inline-start addon appears first", async () => {
206
+ test("inline-start addon has correct data-align", async () => {
217
207
  render(
218
208
  <InputGroup>
219
209
  <InputGroupAddon data-testid="start-addon">
@@ -224,10 +214,9 @@ describe("InputGroup", () => {
224
214
  );
225
215
  const addon = page.getByTestId("start-addon");
226
216
  await expect.element(addon).toHaveAttribute("data-align", "inline-start");
227
- await expect.element(addon).toHaveClass(/order-first/);
228
217
  });
229
218
 
230
- test("inline-end addon appears last", async () => {
219
+ test("inline-end addon has correct data-align", async () => {
231
220
  render(
232
221
  <InputGroup>
233
222
  <InputGroupInput placeholder="Input" />
@@ -238,10 +227,9 @@ describe("InputGroup", () => {
238
227
  );
239
228
  const addon = page.getByTestId("end-addon");
240
229
  await expect.element(addon).toHaveAttribute("data-align", "inline-end");
241
- await expect.element(addon).toHaveClass(/order-last/);
242
230
  });
243
231
 
244
- test("block-start addon appears first with full width", async () => {
232
+ test("block-start addon has correct data-align", async () => {
245
233
  render(
246
234
  <InputGroup>
247
235
  <InputGroupAddon align="block-start" data-testid="block-start-addon">
@@ -252,10 +240,9 @@ describe("InputGroup", () => {
252
240
  );
253
241
  const addon = page.getByTestId("block-start-addon");
254
242
  await expect.element(addon).toHaveAttribute("data-align", "block-start");
255
- await expect.element(addon).toHaveClass(/w-full/);
256
243
  });
257
244
 
258
- test("block-end addon appears last with full width", async () => {
245
+ test("block-end addon has correct data-align", async () => {
259
246
  render(
260
247
  <InputGroup>
261
248
  <InputGroupInput placeholder="Input" />
@@ -266,7 +253,6 @@ describe("InputGroup", () => {
266
253
  );
267
254
  const addon = page.getByTestId("block-end-addon");
268
255
  await expect.element(addon).toHaveAttribute("data-align", "block-end");
269
- await expect.element(addon).toHaveClass(/w-full/);
270
256
  });
271
257
  });
272
258
 
@@ -327,7 +313,7 @@ describe("InputGroup", () => {
327
313
  await expect.element(page.getByText("https://")).toBeInTheDocument();
328
314
  });
329
315
 
330
- test("applies text styling", async () => {
316
+ test("renders text element", async () => {
331
317
  render(
332
318
  <InputGroup>
333
319
  <InputGroupAddon>
@@ -337,7 +323,7 @@ describe("InputGroup", () => {
337
323
  </InputGroup>,
338
324
  );
339
325
  const text = page.getByTestId("text");
340
- await expect.element(text).toHaveClass(/text-text-muted/);
326
+ await expect.element(text).toBeInTheDocument();
341
327
  });
342
328
  });
343
329