@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.
- package/dist/components/atoms/background/background.d.ts +13 -27
- package/dist/components/atoms/button/button.d.ts +55 -71
- package/dist/components/atoms/button/icon-button.d.ts +62 -110
- package/dist/components/atoms/input/input-group.d.ts +278 -0
- package/dist/components/atoms/input/input.d.ts +121 -0
- package/dist/components/atoms/select/select.d.ts +131 -0
- package/dist/components/organisms/card/card.d.ts +2 -2
- package/dist/components/sections/prose/prose.d.ts +3 -3
- package/dist/components/sections/river/river.d.ts +1 -1
- package/dist/components/sections/tout/tout.d.ts +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.js +11034 -7824
- package/dist/index.js.map +1 -1
- package/dist/lib/form-control.d.ts +105 -0
- package/dist/tokens.css +2132 -17329
- package/package.json +1 -1
- package/src/components/atoms/background/background.tsx +71 -109
- package/src/components/atoms/button/button.stories.tsx +42 -0
- package/src/components/atoms/button/button.test.tsx +1 -1
- package/src/components/atoms/button/button.tsx +38 -103
- package/src/components/atoms/button/button.visual.test.tsx +70 -24
- package/src/components/atoms/button/icon-button.tsx +81 -224
- package/src/components/atoms/input/index.ts +17 -0
- package/src/components/atoms/input/input-group.stories.tsx +650 -0
- package/src/components/atoms/input/input-group.test.tsx +376 -0
- package/src/components/atoms/input/input-group.tsx +384 -0
- package/src/components/atoms/input/input.stories.tsx +232 -0
- package/src/components/atoms/input/input.test.tsx +183 -0
- package/src/components/atoms/input/input.tsx +97 -0
- package/src/components/atoms/select/index.ts +18 -0
- package/src/components/atoms/select/select.stories.tsx +455 -0
- package/src/components/atoms/select/select.tsx +320 -0
- package/src/components/dev-tools/dev-toolbar/dev-toolbar.stories.tsx +2 -6
- package/src/components/foundation/typography/typography.stories.tsx +401 -0
- package/src/components/organisms/card/card.stories.tsx +11 -11
- package/src/components/organisms/card/card.test.tsx +1 -1
- package/src/components/organisms/card/card.tsx +2 -2
- package/src/components/organisms/card/card.visual.test.tsx +6 -6
- package/src/components/organisms/navbar/navbar.tsx +2 -2
- package/src/components/organisms/navbar/navbar.visual.test.tsx +2 -2
- package/src/components/sections/card-grid/card-grid.tsx +1 -1
- package/src/components/sections/faq-section/faq-section.tsx +2 -2
- package/src/components/sections/hero/hero.test.tsx +5 -5
- package/src/components/sections/prose/prose.test.tsx +2 -2
- package/src/components/sections/prose/prose.tsx +4 -5
- package/src/components/sections/river/river.stories.tsx +8 -8
- package/src/components/sections/river/river.test.tsx +1 -1
- package/src/components/sections/river/river.tsx +2 -4
- package/src/components/sections/tout/tout.test.tsx +1 -1
- package/src/components/sections/tout/tout.tsx +2 -2
- package/src/index.ts +41 -0
- package/src/lib/form-control.ts +69 -0
- package/src/stories/Introduction.mdx +29 -15
- package/src/stories/ThemeProvider.stories.tsx +1 -3
- package/src/stories/TokenShowcase.stories.tsx +0 -19
- package/src/stories/TokenShowcase.tsx +714 -1366
- package/src/styles.css +3 -0
- 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
|
+
});
|