@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
package/src/styles.css CHANGED
@@ -1,6 +1,9 @@
1
1
  @import "tailwindcss";
2
2
  @import "../../tailwind-token-generator/dist/tokens.css";
3
3
 
4
+ /* Google Fonts - Inter and Inter Tight */
5
+ @import url("https://fonts.googleapis.com/css2?family=Inter:wght@100..900&family=Inter+Tight:wght@100..900&display=swap");
6
+
4
7
  @font-face {
5
8
  font-family: "PP Neue Montreal";
6
9
  src: url("./assets/fonts/PPNeueMontreal-Variable.woff2")
@@ -0,0 +1,301 @@
1
+ /**
2
+ * Token Resolution Tests
3
+ *
4
+ * These tests verify that CSS classes using design tokens resolve to the correct
5
+ * computed styles. This ensures that:
6
+ * 1. Semantic color tokens resolve to actual colors
7
+ * 2. Typography tokens apply correct font properties
8
+ * 3. Spacing tokens resolve to correct pixel values
9
+ */
10
+
11
+ import { describe, expect, test } from "vitest";
12
+ import { page } from "vitest/browser";
13
+ import { render } from "vitest-browser-react";
14
+ import { Button } from "../components/atoms/button/button";
15
+
16
+ /**
17
+ * Helper to get computed style of an element
18
+ */
19
+ function getComputedStyleValue(element: Element, property: string): string {
20
+ return window.getComputedStyle(element).getPropertyValue(property);
21
+ }
22
+
23
+ describe("Token Resolution", () => {
24
+ describe("Semantic Color Tokens", () => {
25
+ test("Button primary variant uses semantic bg token", async () => {
26
+ render(<Button data-testid="btn">Primary Button</Button>);
27
+ const button = page.getByTestId("btn");
28
+ await expect.element(button).toBeInTheDocument();
29
+
30
+ const element = button.element();
31
+ const bgColor = getComputedStyleValue(element, "background-color");
32
+
33
+ // The button should have a non-transparent background
34
+ expect(bgColor).not.toBe("rgba(0, 0, 0, 0)");
35
+ expect(bgColor).not.toBe("transparent");
36
+ });
37
+
38
+ test("Button text color resolves from semantic token", async () => {
39
+ render(<Button data-testid="btn">Text Color</Button>);
40
+ const button = page.getByTestId("btn");
41
+ await expect.element(button).toBeInTheDocument();
42
+
43
+ const element = button.element();
44
+ const textColor = getComputedStyleValue(element, "color");
45
+
46
+ // Should have a defined text color
47
+ expect(textColor).toBeTruthy();
48
+ expect(textColor).not.toBe("rgba(0, 0, 0, 0)");
49
+ });
50
+
51
+ test("Background page token resolves correctly", async () => {
52
+ render(
53
+ <div data-testid="bg-test" className="bg-bg-page p-16">
54
+ Background Test
55
+ </div>,
56
+ );
57
+ const element = page.getByTestId("bg-test").element();
58
+ const bgColor = getComputedStyleValue(element, "background-color");
59
+
60
+ // bg-page should resolve to a non-transparent color
61
+ expect(bgColor).not.toBe("rgba(0, 0, 0, 0)");
62
+ });
63
+
64
+ test("Text primary token resolves correctly", async () => {
65
+ render(
66
+ <p data-testid="text-test" className="text-text-primary">
67
+ Primary Text
68
+ </p>,
69
+ );
70
+ const element = page.getByTestId("text-test").element();
71
+ const textColor = getComputedStyleValue(element, "color");
72
+
73
+ // text-primary should be a dark color
74
+ expect(textColor).toBeTruthy();
75
+ expect(textColor).not.toBe("rgba(0, 0, 0, 0)");
76
+ });
77
+ });
78
+
79
+ describe("Primitive Color Tokens", () => {
80
+ test("Gray scale colors resolve", async () => {
81
+ render(
82
+ <div>
83
+ <div data-testid="gray-100" className="bg-gray-100">
84
+ Gray 100
85
+ </div>
86
+ <div data-testid="gray-500" className="bg-gray-500">
87
+ Gray 500
88
+ </div>
89
+ <div data-testid="gray-900" className="bg-gray-900">
90
+ Gray 900
91
+ </div>
92
+ </div>,
93
+ );
94
+
95
+ const gray100 = page.getByTestId("gray-100").element();
96
+ const gray500 = page.getByTestId("gray-500").element();
97
+ const gray900 = page.getByTestId("gray-900").element();
98
+
99
+ const bg100 = getComputedStyleValue(gray100, "background-color");
100
+ const bg500 = getComputedStyleValue(gray500, "background-color");
101
+ const bg900 = getComputedStyleValue(gray900, "background-color");
102
+
103
+ // All should have resolved background colors
104
+ expect(bg100).not.toBe("rgba(0, 0, 0, 0)");
105
+ expect(bg500).not.toBe("rgba(0, 0, 0, 0)");
106
+ expect(bg900).not.toBe("rgba(0, 0, 0, 0)");
107
+
108
+ // Gray 100 should be lighter than Gray 900
109
+ // (comparing luminance would be ideal, but we'll just verify they're different)
110
+ expect(bg100).not.toBe(bg900);
111
+ });
112
+
113
+ test("Indigo accent colors resolve", async () => {
114
+ render(
115
+ <div data-testid="indigo" className="bg-indigo-600">
116
+ Indigo 600
117
+ </div>,
118
+ );
119
+
120
+ const element = page.getByTestId("indigo").element();
121
+ const bgColor = getComputedStyleValue(element, "background-color");
122
+
123
+ // Should resolve to a blue-ish color (indigo)
124
+ expect(bgColor).not.toBe("rgba(0, 0, 0, 0)");
125
+ });
126
+ });
127
+
128
+ describe("Typography Tokens", () => {
129
+ test("Typography h1 class applies correct styles", async () => {
130
+ render(
131
+ <h1 data-testid="h1" className="typography-h1">
132
+ Heading 1
133
+ </h1>,
134
+ );
135
+
136
+ const element = page.getByTestId("h1").element();
137
+ const fontSize = getComputedStyleValue(element, "font-size");
138
+ const fontFamily = getComputedStyleValue(element, "font-family");
139
+
140
+ // H1 should have a large font size (48px+ depending on breakpoint)
141
+ const sizeNum = Number.parseFloat(fontSize);
142
+ expect(sizeNum).toBeGreaterThanOrEqual(48);
143
+
144
+ // Should use the heading font family (Inter Tight)
145
+ expect(fontFamily.toLowerCase()).toContain("inter");
146
+ });
147
+
148
+ test("Typography body-medium class applies correct styles", async () => {
149
+ render(
150
+ <p data-testid="body" className="typography-body-medium">
151
+ Body text
152
+ </p>,
153
+ );
154
+
155
+ const element = page.getByTestId("body").element();
156
+ const fontSize = getComputedStyleValue(element, "font-size");
157
+ const lineHeight = getComputedStyleValue(element, "line-height");
158
+
159
+ // Body medium should be around 14-18px depending on breakpoint
160
+ const sizeNum = Number.parseFloat(fontSize);
161
+ expect(sizeNum).toBeGreaterThanOrEqual(14);
162
+ expect(sizeNum).toBeLessThanOrEqual(18);
163
+
164
+ // Should have a line height set
165
+ expect(lineHeight).toBeTruthy();
166
+ });
167
+
168
+ test("Typography button class applies correct weight", async () => {
169
+ render(
170
+ <span data-testid="btn-text" className="typography-ui-button-medium">
171
+ Button Text
172
+ </span>,
173
+ );
174
+
175
+ const element = page.getByTestId("btn-text").element();
176
+ const fontWeight = getComputedStyleValue(element, "font-weight");
177
+
178
+ // Button text should be medium weight (500) or higher
179
+ const weightNum = Number.parseInt(fontWeight, 10);
180
+ expect(weightNum).toBeGreaterThanOrEqual(500);
181
+ });
182
+ });
183
+
184
+ describe("Spacing Tokens", () => {
185
+ test("Padding tokens resolve to correct pixel values", async () => {
186
+ render(
187
+ <div data-testid="padded" className="p-16">
188
+ Padded content
189
+ </div>,
190
+ );
191
+
192
+ const element = page.getByTestId("padded").element();
193
+ const padding = getComputedStyleValue(element, "padding");
194
+
195
+ // p-16 should resolve to 16px on all sides
196
+ expect(padding).toBe("16px");
197
+ });
198
+
199
+ test("Margin tokens resolve correctly", async () => {
200
+ render(
201
+ <div data-testid="margin" className="m-24">
202
+ Margin content
203
+ </div>,
204
+ );
205
+
206
+ const element = page.getByTestId("margin").element();
207
+ const margin = getComputedStyleValue(element, "margin");
208
+
209
+ // m-24 should resolve to 24px on all sides
210
+ expect(margin).toBe("24px");
211
+ });
212
+
213
+ test("Gap tokens resolve correctly", async () => {
214
+ render(
215
+ <div data-testid="gap" className="flex gap-8">
216
+ <span>Item 1</span>
217
+ <span>Item 2</span>
218
+ </div>,
219
+ );
220
+
221
+ const element = page.getByTestId("gap").element();
222
+ const gap = getComputedStyleValue(element, "gap");
223
+
224
+ // gap-8 should resolve to 8px
225
+ expect(gap).toBe("8px");
226
+ });
227
+
228
+ test("Width spacing tokens resolve", async () => {
229
+ render(
230
+ <div data-testid="width" className="w-spacing-64 h-8 bg-gray-500">
231
+ Width test
232
+ </div>,
233
+ );
234
+
235
+ const element = page.getByTestId("width").element();
236
+ const width = getComputedStyleValue(element, "width");
237
+
238
+ // w-spacing-64 should be 64px
239
+ expect(width).toBe("64px");
240
+ });
241
+ });
242
+
243
+ describe("Border Radius Tokens", () => {
244
+ test("Radius tokens resolve correctly", async () => {
245
+ render(
246
+ <div data-testid="rounded" className="rounded-8 bg-gray-200 p-16">
247
+ Rounded corners
248
+ </div>,
249
+ );
250
+
251
+ const element = page.getByTestId("rounded").element();
252
+ const radius = getComputedStyleValue(element, "border-radius");
253
+
254
+ // rounded-8 should be 8px
255
+ expect(radius).toBe("8px");
256
+ });
257
+ });
258
+
259
+ describe("Component Token Integration", () => {
260
+ test("Button has defined height from spacing tokens", async () => {
261
+ render(
262
+ <Button data-testid="btn" size="default">
263
+ Medium Button
264
+ </Button>,
265
+ );
266
+ const button = page.getByTestId("btn");
267
+ await expect.element(button).toBeInTheDocument();
268
+
269
+ const element = button.element();
270
+ const height = getComputedStyleValue(element, "height");
271
+
272
+ // Button should have a defined height (not auto)
273
+ const heightNum = Number.parseFloat(height);
274
+ expect(heightNum).toBeGreaterThan(0);
275
+ });
276
+
277
+ test("Button sizes use different padding tokens", async () => {
278
+ render(
279
+ <div>
280
+ <Button data-testid="btn-sm" size="sm">
281
+ Small
282
+ </Button>
283
+ <Button data-testid="btn-lg" size="lg">
284
+ Large
285
+ </Button>
286
+ </div>,
287
+ );
288
+
289
+ const smallBtn = page.getByTestId("btn-sm").element();
290
+ const largeBtn = page.getByTestId("btn-lg").element();
291
+
292
+ const smallPadding = getComputedStyleValue(smallBtn, "padding-left");
293
+ const largePadding = getComputedStyleValue(largeBtn, "padding-left");
294
+
295
+ // Large should have more padding than small
296
+ const smallPaddingNum = Number.parseFloat(smallPadding);
297
+ const largePaddingNum = Number.parseFloat(largePadding);
298
+ expect(largePaddingNum).toBeGreaterThan(smallPaddingNum);
299
+ });
300
+ });
301
+ });