@nationaldesignstudio/react 0.2.0 → 0.3.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 (58) hide show
  1. package/dist/components/atoms/background/background.d.ts +13 -27
  2. package/dist/components/atoms/button/button.d.ts +55 -71
  3. package/dist/components/atoms/button/icon-button.d.ts +62 -110
  4. package/dist/components/atoms/input/input-group.d.ts +278 -0
  5. package/dist/components/atoms/input/input.d.ts +121 -0
  6. package/dist/components/atoms/select/select.d.ts +131 -0
  7. package/dist/components/organisms/card/card.d.ts +2 -2
  8. package/dist/components/sections/prose/prose.d.ts +3 -3
  9. package/dist/components/sections/river/river.d.ts +1 -1
  10. package/dist/components/sections/tout/tout.d.ts +1 -1
  11. package/dist/index.d.ts +4 -0
  12. package/dist/index.js +11034 -7824
  13. package/dist/index.js.map +1 -1
  14. package/dist/lib/form-control.d.ts +105 -0
  15. package/dist/tokens.css +2132 -17329
  16. package/package.json +1 -1
  17. package/src/components/atoms/background/background.tsx +71 -109
  18. package/src/components/atoms/button/button.stories.tsx +42 -0
  19. package/src/components/atoms/button/button.test.tsx +1 -1
  20. package/src/components/atoms/button/button.tsx +38 -103
  21. package/src/components/atoms/button/button.visual.test.tsx +70 -24
  22. package/src/components/atoms/button/icon-button.tsx +81 -224
  23. package/src/components/atoms/input/index.ts +17 -0
  24. package/src/components/atoms/input/input-group.stories.tsx +650 -0
  25. package/src/components/atoms/input/input-group.test.tsx +376 -0
  26. package/src/components/atoms/input/input-group.tsx +384 -0
  27. package/src/components/atoms/input/input.stories.tsx +232 -0
  28. package/src/components/atoms/input/input.test.tsx +183 -0
  29. package/src/components/atoms/input/input.tsx +97 -0
  30. package/src/components/atoms/select/index.ts +18 -0
  31. package/src/components/atoms/select/select.stories.tsx +455 -0
  32. package/src/components/atoms/select/select.tsx +320 -0
  33. package/src/components/dev-tools/dev-toolbar/dev-toolbar.stories.tsx +2 -6
  34. package/src/components/foundation/typography/typography.stories.tsx +401 -0
  35. package/src/components/organisms/card/card.stories.tsx +11 -11
  36. package/src/components/organisms/card/card.test.tsx +1 -1
  37. package/src/components/organisms/card/card.tsx +2 -2
  38. package/src/components/organisms/card/card.visual.test.tsx +6 -6
  39. package/src/components/organisms/navbar/navbar.tsx +2 -2
  40. package/src/components/organisms/navbar/navbar.visual.test.tsx +2 -2
  41. package/src/components/sections/card-grid/card-grid.tsx +1 -1
  42. package/src/components/sections/faq-section/faq-section.tsx +2 -2
  43. package/src/components/sections/hero/hero.test.tsx +5 -5
  44. package/src/components/sections/prose/prose.test.tsx +2 -2
  45. package/src/components/sections/prose/prose.tsx +4 -5
  46. package/src/components/sections/river/river.stories.tsx +8 -8
  47. package/src/components/sections/river/river.test.tsx +1 -1
  48. package/src/components/sections/river/river.tsx +2 -4
  49. package/src/components/sections/tout/tout.test.tsx +1 -1
  50. package/src/components/sections/tout/tout.tsx +2 -2
  51. package/src/index.ts +41 -0
  52. package/src/lib/form-control.ts +69 -0
  53. package/src/stories/Introduction.mdx +29 -15
  54. package/src/stories/ThemeProvider.stories.tsx +1 -3
  55. package/src/stories/TokenShowcase.stories.tsx +0 -19
  56. package/src/stories/TokenShowcase.tsx +714 -1366
  57. package/src/styles.css +3 -0
  58. package/src/tests/token-resolution.test.tsx +301 -0
@@ -0,0 +1,455 @@
1
+ import type { Meta, StoryObj } from "@storybook/react-vite";
2
+ import { Select, SelectOption, SelectPopup, SelectTrigger } from ".";
3
+
4
+ const meta: Meta<typeof Select> = {
5
+ title: "Atoms/Select",
6
+ component: Select,
7
+ } as Meta<typeof Select>;
8
+
9
+ export default meta;
10
+ type Story = StoryObj<typeof Select>;
11
+
12
+ // Sample options for stories
13
+ const states = [
14
+ { value: "al", label: "Alabama" },
15
+ { value: "ak", label: "Alaska" },
16
+ { value: "az", label: "Arizona" },
17
+ { value: "ar", label: "Arkansas" },
18
+ { value: "ca", label: "California" },
19
+ { value: "co", label: "Colorado" },
20
+ { value: "ct", label: "Connecticut" },
21
+ { value: "de", label: "Delaware" },
22
+ { value: "fl", label: "Florida" },
23
+ { value: "ga", label: "Georgia" },
24
+ ];
25
+
26
+ // =============================================================================
27
+ // Playground
28
+ // =============================================================================
29
+
30
+ export const Playground: Story = {
31
+ render: (args) => (
32
+ <Select {...args}>
33
+ <SelectTrigger placeholder="Select a state..." />
34
+ <SelectPopup>
35
+ {states.map((state) => (
36
+ <SelectOption key={state.value} value={state.value}>
37
+ {state.label}
38
+ </SelectOption>
39
+ ))}
40
+ </SelectPopup>
41
+ </Select>
42
+ ),
43
+ };
44
+ Playground.argTypes = {
45
+ disabled: {
46
+ control: {
47
+ type: "boolean",
48
+ },
49
+ },
50
+ defaultValue: {
51
+ control: {
52
+ type: "select",
53
+ },
54
+ options: ["", ...states.map((s) => s.value)],
55
+ },
56
+ };
57
+ Playground.args = {
58
+ disabled: false,
59
+ defaultValue: "",
60
+ };
61
+
62
+ // =============================================================================
63
+ // States
64
+ // =============================================================================
65
+
66
+ /**
67
+ * Default state - white background with subtle border
68
+ */
69
+ export const Default = () => (
70
+ <div className="w-[320px]">
71
+ <Select>
72
+ <SelectTrigger placeholder="Select option..." />
73
+ <SelectPopup>
74
+ <SelectOption value="option1">Option 1</SelectOption>
75
+ <SelectOption value="option2">Option 2</SelectOption>
76
+ <SelectOption value="option3">Option 3</SelectOption>
77
+ </SelectPopup>
78
+ </Select>
79
+ </div>
80
+ );
81
+
82
+ /**
83
+ * With a selected value
84
+ */
85
+ export const WithValue = () => (
86
+ <div className="w-[320px]">
87
+ <Select defaultValue="option2">
88
+ <SelectTrigger placeholder="Select option..." />
89
+ <SelectPopup>
90
+ <SelectOption value="option1">Option 1</SelectOption>
91
+ <SelectOption value="option2">Option 2</SelectOption>
92
+ <SelectOption value="option3">Option 3</SelectOption>
93
+ </SelectPopup>
94
+ </Select>
95
+ </div>
96
+ );
97
+
98
+ /**
99
+ * Disabled state - grayed out and not interactive
100
+ */
101
+ export const Disabled = () => (
102
+ <div className="w-[320px]">
103
+ <Select disabled>
104
+ <SelectTrigger placeholder="Select option..." />
105
+ <SelectPopup>
106
+ <SelectOption value="option1">Option 1</SelectOption>
107
+ <SelectOption value="option2">Option 2</SelectOption>
108
+ <SelectOption value="option3">Option 3</SelectOption>
109
+ </SelectPopup>
110
+ </Select>
111
+ </div>
112
+ );
113
+
114
+ /**
115
+ * Error state - red border
116
+ */
117
+ export const ErrorState = () => (
118
+ <div className="w-[320px]">
119
+ <Select>
120
+ <SelectTrigger placeholder="Select option..." error />
121
+ <SelectPopup>
122
+ <SelectOption value="option1">Option 1</SelectOption>
123
+ <SelectOption value="option2">Option 2</SelectOption>
124
+ <SelectOption value="option3">Option 3</SelectOption>
125
+ </SelectPopup>
126
+ </Select>
127
+ </div>
128
+ );
129
+
130
+ /**
131
+ * All states comparison
132
+ */
133
+ export const AllStates = () => (
134
+ <div className="flex flex-col gap-16 max-w-[320px]">
135
+ <div>
136
+ <p className="mb-8 text-12 text-text-muted">Default</p>
137
+ <Select>
138
+ <SelectTrigger placeholder="Select option..." />
139
+ <SelectPopup>
140
+ <SelectOption value="option1">Option 1</SelectOption>
141
+ <SelectOption value="option2">Option 2</SelectOption>
142
+ <SelectOption value="option3">Option 3</SelectOption>
143
+ </SelectPopup>
144
+ </Select>
145
+ </div>
146
+ <div>
147
+ <p className="mb-8 text-12 text-text-muted">With Value</p>
148
+ <Select defaultValue="option2">
149
+ <SelectTrigger placeholder="Select option..." />
150
+ <SelectPopup>
151
+ <SelectOption value="option1">Option 1</SelectOption>
152
+ <SelectOption value="option2">Option 2</SelectOption>
153
+ <SelectOption value="option3">Option 3</SelectOption>
154
+ </SelectPopup>
155
+ </Select>
156
+ </div>
157
+ <div>
158
+ <p className="mb-8 text-12 text-text-muted">Error</p>
159
+ <Select>
160
+ <SelectTrigger placeholder="Select option..." error />
161
+ <SelectPopup>
162
+ <SelectOption value="option1">Option 1</SelectOption>
163
+ <SelectOption value="option2">Option 2</SelectOption>
164
+ <SelectOption value="option3">Option 3</SelectOption>
165
+ </SelectPopup>
166
+ </Select>
167
+ </div>
168
+ <div>
169
+ <p className="mb-8 text-12 text-text-muted">Disabled</p>
170
+ <Select disabled>
171
+ <SelectTrigger placeholder="Select option..." />
172
+ <SelectPopup>
173
+ <SelectOption value="option1">Option 1</SelectOption>
174
+ <SelectOption value="option2">Option 2</SelectOption>
175
+ <SelectOption value="option3">Option 3</SelectOption>
176
+ </SelectPopup>
177
+ </Select>
178
+ </div>
179
+ </div>
180
+ );
181
+
182
+ // =============================================================================
183
+ // Sizes
184
+ // =============================================================================
185
+
186
+ /**
187
+ * Small size - compact select for tight layouts
188
+ */
189
+ export const Small = () => (
190
+ <div className="w-[320px]">
191
+ <Select>
192
+ <SelectTrigger size="sm" placeholder="Select option..." />
193
+ <SelectPopup>
194
+ <SelectOption value="option1">Option 1</SelectOption>
195
+ <SelectOption value="option2">Option 2</SelectOption>
196
+ <SelectOption value="option3">Option 3</SelectOption>
197
+ </SelectPopup>
198
+ </Select>
199
+ </div>
200
+ );
201
+
202
+ /**
203
+ * Default size - standard 48px height
204
+ */
205
+ export const Medium = () => (
206
+ <div className="w-[320px]">
207
+ <Select>
208
+ <SelectTrigger size="default" placeholder="Select option..." />
209
+ <SelectPopup>
210
+ <SelectOption value="option1">Option 1</SelectOption>
211
+ <SelectOption value="option2">Option 2</SelectOption>
212
+ <SelectOption value="option3">Option 3</SelectOption>
213
+ </SelectPopup>
214
+ </Select>
215
+ </div>
216
+ );
217
+
218
+ /**
219
+ * Large size - more prominent select
220
+ */
221
+ export const Large = () => (
222
+ <div className="w-[320px]">
223
+ <Select>
224
+ <SelectTrigger size="lg" placeholder="Select option..." />
225
+ <SelectPopup>
226
+ <SelectOption value="option1">Option 1</SelectOption>
227
+ <SelectOption value="option2">Option 2</SelectOption>
228
+ <SelectOption value="option3">Option 3</SelectOption>
229
+ </SelectPopup>
230
+ </Select>
231
+ </div>
232
+ );
233
+
234
+ /**
235
+ * All sizes comparison
236
+ */
237
+ export const AllSizes = () => (
238
+ <div className="flex flex-col gap-16 max-w-[320px]">
239
+ <div>
240
+ <p className="mb-8 text-12 text-text-muted">Small (36px)</p>
241
+ <Select>
242
+ <SelectTrigger size="sm" placeholder="Select option..." />
243
+ <SelectPopup>
244
+ <SelectOption value="option1">Option 1</SelectOption>
245
+ <SelectOption value="option2">Option 2</SelectOption>
246
+ <SelectOption value="option3">Option 3</SelectOption>
247
+ </SelectPopup>
248
+ </Select>
249
+ </div>
250
+ <div>
251
+ <p className="mb-8 text-12 text-text-muted">Default (48px)</p>
252
+ <Select>
253
+ <SelectTrigger size="default" placeholder="Select option..." />
254
+ <SelectPopup>
255
+ <SelectOption value="option1">Option 1</SelectOption>
256
+ <SelectOption value="option2">Option 2</SelectOption>
257
+ <SelectOption value="option3">Option 3</SelectOption>
258
+ </SelectPopup>
259
+ </Select>
260
+ </div>
261
+ <div>
262
+ <p className="mb-8 text-12 text-text-muted">Large (56px)</p>
263
+ <Select>
264
+ <SelectTrigger size="lg" placeholder="Select option..." />
265
+ <SelectPopup>
266
+ <SelectOption value="option1">Option 1</SelectOption>
267
+ <SelectOption value="option2">Option 2</SelectOption>
268
+ <SelectOption value="option3">Option 3</SelectOption>
269
+ </SelectPopup>
270
+ </Select>
271
+ </div>
272
+ </div>
273
+ );
274
+
275
+ // =============================================================================
276
+ // Examples
277
+ // =============================================================================
278
+
279
+ /**
280
+ * Form field example with label
281
+ */
282
+ export const WithLabel = () => (
283
+ <div className="flex flex-col gap-8 max-w-[320px]">
284
+ <label
285
+ htmlFor="state-select"
286
+ className="text-14 font-medium text-text-primary"
287
+ >
288
+ State / Territory
289
+ </label>
290
+ <Select>
291
+ <SelectTrigger placeholder="Select a state..." />
292
+ <SelectPopup>
293
+ {states.map((state) => (
294
+ <SelectOption key={state.value} value={state.value}>
295
+ {state.label}
296
+ </SelectOption>
297
+ ))}
298
+ </SelectPopup>
299
+ </Select>
300
+ </div>
301
+ );
302
+
303
+ /**
304
+ * Form field with error message
305
+ */
306
+ export const WithErrorMessage = () => (
307
+ <div className="flex flex-col gap-8 max-w-[320px]">
308
+ <label
309
+ htmlFor="state-error"
310
+ className="text-14 font-medium text-text-primary"
311
+ >
312
+ State / Territory
313
+ </label>
314
+ <Select>
315
+ <SelectTrigger placeholder="Select a state..." error />
316
+ <SelectPopup>
317
+ {states.map((state) => (
318
+ <SelectOption key={state.value} value={state.value}>
319
+ {state.label}
320
+ </SelectOption>
321
+ ))}
322
+ </SelectPopup>
323
+ </Select>
324
+ <p className="text-12 text-ui-error-color">Please select a state</p>
325
+ </div>
326
+ );
327
+
328
+ /**
329
+ * Required field indicator
330
+ */
331
+ export const RequiredField = () => (
332
+ <div className="flex flex-col gap-8 max-w-[320px]">
333
+ <label
334
+ htmlFor="required-select"
335
+ className="text-14 font-medium text-text-primary"
336
+ >
337
+ State / Territory <span className="text-ui-error-color">*</span>
338
+ </label>
339
+ <Select required>
340
+ <SelectTrigger placeholder="Select a state..." />
341
+ <SelectPopup>
342
+ {states.map((state) => (
343
+ <SelectOption key={state.value} value={state.value}>
344
+ {state.label}
345
+ </SelectOption>
346
+ ))}
347
+ </SelectPopup>
348
+ </Select>
349
+ </div>
350
+ );
351
+
352
+ /**
353
+ * With disabled options
354
+ */
355
+ export const WithDisabledOptions = () => (
356
+ <div className="w-[320px]">
357
+ <Select>
358
+ <SelectTrigger placeholder="Select option..." />
359
+ <SelectPopup>
360
+ <SelectOption value="option1">Option 1</SelectOption>
361
+ <SelectOption value="option2" disabled>
362
+ Option 2 (Disabled)
363
+ </SelectOption>
364
+ <SelectOption value="option3">Option 3</SelectOption>
365
+ <SelectOption value="option4" disabled>
366
+ Option 4 (Disabled)
367
+ </SelectOption>
368
+ <SelectOption value="option5">Option 5</SelectOption>
369
+ </SelectPopup>
370
+ </Select>
371
+ </div>
372
+ );
373
+
374
+ /**
375
+ * Many options (scrollable)
376
+ */
377
+ export const ManyOptions = () => (
378
+ <div className="w-[320px]">
379
+ <Select>
380
+ <SelectTrigger placeholder="Select a state..." />
381
+ <SelectPopup>
382
+ {states.map((state) => (
383
+ <SelectOption key={state.value} value={state.value}>
384
+ {state.label}
385
+ </SelectOption>
386
+ ))}
387
+ </SelectPopup>
388
+ </Select>
389
+ </div>
390
+ );
391
+
392
+ /**
393
+ * Side by side with Input - demonstrating shared styling
394
+ */
395
+ export const WithInputComparison = () => {
396
+ // Import Input dynamically to avoid circular deps in stories
397
+ const Input = require("../input").Input;
398
+ return (
399
+ <div className="flex flex-col gap-16 max-w-[500px]">
400
+ <p className="text-14 text-text-muted">
401
+ Select and Input share the same base styling for consistent forms
402
+ </p>
403
+ <div className="flex gap-12">
404
+ <div className="flex-1">
405
+ <p className="block mb-8 text-12 text-text-muted">Input</p>
406
+ <Input placeholder="Enter text..." />
407
+ </div>
408
+ <div className="flex-1">
409
+ <p className="block mb-8 text-12 text-text-muted">Select</p>
410
+ <Select>
411
+ <SelectTrigger placeholder="Select option..." />
412
+ <SelectPopup>
413
+ <SelectOption value="option1">Option 1</SelectOption>
414
+ <SelectOption value="option2">Option 2</SelectOption>
415
+ </SelectPopup>
416
+ </Select>
417
+ </div>
418
+ </div>
419
+ <div className="flex gap-12">
420
+ <div className="flex-1">
421
+ <p className="block mb-8 text-12 text-text-muted">Input (Error)</p>
422
+ <Input error placeholder="Enter text..." />
423
+ </div>
424
+ <div className="flex-1">
425
+ <p className="block mb-8 text-12 text-text-muted">Select (Error)</p>
426
+ <Select>
427
+ <SelectTrigger error placeholder="Select option..." />
428
+ <SelectPopup>
429
+ <SelectOption value="option1">Option 1</SelectOption>
430
+ <SelectOption value="option2">Option 2</SelectOption>
431
+ </SelectPopup>
432
+ </Select>
433
+ </div>
434
+ </div>
435
+ <div className="flex gap-12">
436
+ <div className="flex-1">
437
+ <p className="block mb-8 text-12 text-text-muted">Input (Disabled)</p>
438
+ <Input disabled placeholder="Enter text..." />
439
+ </div>
440
+ <div className="flex-1">
441
+ <p className="block mb-8 text-12 text-text-muted">
442
+ Select (Disabled)
443
+ </p>
444
+ <Select disabled>
445
+ <SelectTrigger placeholder="Select option..." />
446
+ <SelectPopup>
447
+ <SelectOption value="option1">Option 1</SelectOption>
448
+ <SelectOption value="option2">Option 2</SelectOption>
449
+ </SelectPopup>
450
+ </Select>
451
+ </div>
452
+ </div>
453
+ </div>
454
+ );
455
+ };