@styleframe/core 1.0.1 → 1.0.2
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/CHANGELOG.md +6 -0
- package/LICENSE +21 -0
- package/dist/styleframe.d.ts +301 -0
- package/dist/styleframe.js +528 -0
- package/dist/styleframe.umd.cjs +1 -0
- package/package.json +13 -3
- package/.tsbuildinfo +0 -1
- package/src/index.ts +0 -5
- package/src/styleframe.ts +0 -64
- package/src/tokens/atRule.test.ts +0 -1013
- package/src/tokens/atRule.ts +0 -67
- package/src/tokens/css.test.ts +0 -404
- package/src/tokens/css.ts +0 -23
- package/src/tokens/declarations.test.ts +0 -584
- package/src/tokens/declarations.ts +0 -71
- package/src/tokens/index.ts +0 -11
- package/src/tokens/modifier.test.ts +0 -90
- package/src/tokens/modifier.ts +0 -86
- package/src/tokens/recipe.test.ts +0 -105
- package/src/tokens/recipe.ts +0 -32
- package/src/tokens/ref.test.ts +0 -430
- package/src/tokens/ref.ts +0 -24
- package/src/tokens/root.test.ts +0 -70
- package/src/tokens/root.ts +0 -14
- package/src/tokens/selector.test.ts +0 -440
- package/src/tokens/selector.ts +0 -47
- package/src/tokens/theme.test.ts +0 -338
- package/src/tokens/theme.ts +0 -26
- package/src/tokens/utility.test.ts +0 -1456
- package/src/tokens/utility.ts +0 -92
- package/src/tokens/variable.test.ts +0 -235
- package/src/tokens/variable.ts +0 -42
- package/src/typeGuards.test.ts +0 -33
- package/src/typeGuards.ts +0 -98
- package/src/types/declarations.ts +0 -42
- package/src/types/index.ts +0 -3
- package/src/types/options.ts +0 -22
- package/src/types/tokens.ts +0 -149
- package/src/utils/capitalizeFirst.ts +0 -9
- package/src/utils/deepClone.ts +0 -317
- package/src/utils/getters.test.ts +0 -399
- package/src/utils/getters.ts +0 -36
- package/src/utils/index.ts +0 -4
- package/src/utils/merge.test.ts +0 -978
- package/src/utils/merge.ts +0 -73
- package/src/vite-env.d.ts +0 -1
- package/tsconfig.json +0 -7
- package/vite.config.ts +0 -5
|
@@ -1,1456 +0,0 @@
|
|
|
1
|
-
import type { Container, ModifierFactory, Root, Utility } from "../types";
|
|
2
|
-
import { createRoot } from "./root";
|
|
3
|
-
import { createUtilityFunction } from "./utility";
|
|
4
|
-
import { applyModifiers } from "./modifier";
|
|
5
|
-
import { isUtility } from "../typeGuards";
|
|
6
|
-
|
|
7
|
-
describe("createUtilityFunction", () => {
|
|
8
|
-
let root: Root;
|
|
9
|
-
let parent: Container;
|
|
10
|
-
let utility: ReturnType<typeof createUtilityFunction>;
|
|
11
|
-
|
|
12
|
-
beforeEach(() => {
|
|
13
|
-
root = createRoot();
|
|
14
|
-
parent = root;
|
|
15
|
-
utility = createUtilityFunction(parent, root);
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
test("should create a utility function", () => {
|
|
19
|
-
expect(utility).toBeTypeOf("function");
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
test("should create utility with proper structure", () => {
|
|
23
|
-
const createMarginUtility = utility("margin", ({ value }) => ({
|
|
24
|
-
margin: value,
|
|
25
|
-
}));
|
|
26
|
-
|
|
27
|
-
expect(createMarginUtility).toBeTypeOf("function");
|
|
28
|
-
expect(root.utilities).toHaveLength(1); // Factory is registered immediately
|
|
29
|
-
expect(root.utilities[0]).toMatchObject({
|
|
30
|
-
type: "utility",
|
|
31
|
-
name: "margin",
|
|
32
|
-
});
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
test("should create utility instances without creating selectors", () => {
|
|
36
|
-
const createPaddingUtility = utility("padding", ({ value }) => ({
|
|
37
|
-
padding: value,
|
|
38
|
-
}));
|
|
39
|
-
|
|
40
|
-
createPaddingUtility({
|
|
41
|
-
sm: "8px",
|
|
42
|
-
md: "16px",
|
|
43
|
-
lg: "24px",
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
// Three utility instances should be created in children
|
|
47
|
-
expect(root.children).toHaveLength(3);
|
|
48
|
-
|
|
49
|
-
const smUtility = root.children.find(
|
|
50
|
-
(u) => isUtility(u) && u.name === "padding" && u.value === "sm",
|
|
51
|
-
);
|
|
52
|
-
const mdUtility = root.children.find(
|
|
53
|
-
(u) => isUtility(u) && u.name === "padding" && u.value === "md",
|
|
54
|
-
);
|
|
55
|
-
const lgUtility = root.children.find(
|
|
56
|
-
(u) => isUtility(u) && u.name === "padding" && u.value === "lg",
|
|
57
|
-
);
|
|
58
|
-
|
|
59
|
-
expect(smUtility).toEqual({
|
|
60
|
-
type: "utility",
|
|
61
|
-
name: "padding",
|
|
62
|
-
value: "sm",
|
|
63
|
-
declarations: { padding: "8px" },
|
|
64
|
-
variables: [],
|
|
65
|
-
children: [],
|
|
66
|
-
modifiers: [],
|
|
67
|
-
});
|
|
68
|
-
expect(mdUtility).toEqual({
|
|
69
|
-
type: "utility",
|
|
70
|
-
name: "padding",
|
|
71
|
-
value: "md",
|
|
72
|
-
declarations: { padding: "16px" },
|
|
73
|
-
variables: [],
|
|
74
|
-
children: [],
|
|
75
|
-
modifiers: [],
|
|
76
|
-
});
|
|
77
|
-
expect(lgUtility).toEqual({
|
|
78
|
-
type: "utility",
|
|
79
|
-
name: "padding",
|
|
80
|
-
value: "lg",
|
|
81
|
-
declarations: { padding: "24px" },
|
|
82
|
-
variables: [],
|
|
83
|
-
children: [],
|
|
84
|
-
modifiers: [],
|
|
85
|
-
});
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
test("should handle boolean values correctly", () => {
|
|
89
|
-
const createHiddenUtility = utility("hidden", ({ value }) => ({
|
|
90
|
-
display: value === true ? "none" : "block",
|
|
91
|
-
}));
|
|
92
|
-
|
|
93
|
-
createHiddenUtility({
|
|
94
|
-
default: true,
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
// One utility instance should be created in children
|
|
98
|
-
expect(root.children).toHaveLength(1);
|
|
99
|
-
|
|
100
|
-
const hiddenUtility = root.children.find(
|
|
101
|
-
(u) => isUtility(u) && u.name === "hidden",
|
|
102
|
-
);
|
|
103
|
-
expect(hiddenUtility).toEqual({
|
|
104
|
-
type: "utility",
|
|
105
|
-
name: "hidden",
|
|
106
|
-
value: "default",
|
|
107
|
-
declarations: { display: "none" },
|
|
108
|
-
variables: [],
|
|
109
|
-
children: [],
|
|
110
|
-
modifiers: [],
|
|
111
|
-
});
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
test("should store utility instances with modifiers", () => {
|
|
115
|
-
const hoverModifier: ModifierFactory = {
|
|
116
|
-
type: "modifier",
|
|
117
|
-
key: ["hover"],
|
|
118
|
-
factory: ({ declarations }) => ({
|
|
119
|
-
"&:hover": declarations,
|
|
120
|
-
}),
|
|
121
|
-
};
|
|
122
|
-
|
|
123
|
-
const createColorUtility = utility("color", ({ value }) => ({
|
|
124
|
-
color: value,
|
|
125
|
-
}));
|
|
126
|
-
|
|
127
|
-
createColorUtility(
|
|
128
|
-
{
|
|
129
|
-
primary: "#007bff",
|
|
130
|
-
secondary: "#6c757d",
|
|
131
|
-
},
|
|
132
|
-
[hoverModifier],
|
|
133
|
-
);
|
|
134
|
-
|
|
135
|
-
// Base utilities + modified variants in children
|
|
136
|
-
// 2 base utilities + 2 modified = 4 total
|
|
137
|
-
expect(root.children).toHaveLength(4);
|
|
138
|
-
|
|
139
|
-
const primaryUtility = root.children.find(
|
|
140
|
-
(u) =>
|
|
141
|
-
isUtility(u) &&
|
|
142
|
-
u.name === "color" &&
|
|
143
|
-
u.value === "primary" &&
|
|
144
|
-
u.modifiers.length === 0,
|
|
145
|
-
);
|
|
146
|
-
const secondaryUtility = root.children.find(
|
|
147
|
-
(u) =>
|
|
148
|
-
isUtility(u) &&
|
|
149
|
-
u.name === "color" &&
|
|
150
|
-
u.value === "secondary" &&
|
|
151
|
-
u.modifiers.length === 0,
|
|
152
|
-
);
|
|
153
|
-
|
|
154
|
-
expect(primaryUtility).toEqual({
|
|
155
|
-
type: "utility",
|
|
156
|
-
name: "color",
|
|
157
|
-
value: "primary",
|
|
158
|
-
declarations: { color: "#007bff" },
|
|
159
|
-
variables: [],
|
|
160
|
-
children: [],
|
|
161
|
-
modifiers: [],
|
|
162
|
-
});
|
|
163
|
-
expect(secondaryUtility).toEqual({
|
|
164
|
-
type: "utility",
|
|
165
|
-
name: "color",
|
|
166
|
-
value: "secondary",
|
|
167
|
-
declarations: { color: "#6c757d" },
|
|
168
|
-
variables: [],
|
|
169
|
-
children: [],
|
|
170
|
-
modifiers: [],
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
// Check modified variants exist
|
|
174
|
-
const primaryHover = root.children.find(
|
|
175
|
-
(u) =>
|
|
176
|
-
isUtility(u) &&
|
|
177
|
-
u.name === "color" &&
|
|
178
|
-
u.value === "primary" &&
|
|
179
|
-
u.modifiers.includes("hover"),
|
|
180
|
-
);
|
|
181
|
-
const secondaryHover = root.children.find(
|
|
182
|
-
(u) =>
|
|
183
|
-
isUtility(u) &&
|
|
184
|
-
u.name === "color" &&
|
|
185
|
-
u.value === "secondary" &&
|
|
186
|
-
u.modifiers.includes("hover"),
|
|
187
|
-
);
|
|
188
|
-
|
|
189
|
-
expect(primaryHover).toBeDefined();
|
|
190
|
-
expect(secondaryHover).toBeDefined();
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
test("should store utility instances with multiple modifiers", () => {
|
|
194
|
-
const hoverModifier: ModifierFactory = {
|
|
195
|
-
type: "modifier",
|
|
196
|
-
key: ["hover"],
|
|
197
|
-
factory: ({ declarations }) => ({
|
|
198
|
-
"&:hover": declarations,
|
|
199
|
-
}),
|
|
200
|
-
};
|
|
201
|
-
|
|
202
|
-
const focusModifier: ModifierFactory = {
|
|
203
|
-
type: "modifier",
|
|
204
|
-
key: ["focus"],
|
|
205
|
-
factory: ({ declarations }) => ({
|
|
206
|
-
"&:focus": declarations,
|
|
207
|
-
}),
|
|
208
|
-
};
|
|
209
|
-
|
|
210
|
-
const createBackgroundUtility = utility("background", ({ value }) => ({
|
|
211
|
-
background: value,
|
|
212
|
-
}));
|
|
213
|
-
|
|
214
|
-
createBackgroundUtility(
|
|
215
|
-
{
|
|
216
|
-
primary: "#007bff",
|
|
217
|
-
},
|
|
218
|
-
[hoverModifier, focusModifier],
|
|
219
|
-
);
|
|
220
|
-
|
|
221
|
-
// 1 base utility + combinations of 2 modifiers
|
|
222
|
-
// Combinations: [hover], [focus], [focus,hover] = 3 modified variants
|
|
223
|
-
// Total: 1 base + 3 modified = 4
|
|
224
|
-
expect(root.children).toHaveLength(4);
|
|
225
|
-
|
|
226
|
-
const backgroundUtility = root.children.find(
|
|
227
|
-
(u) =>
|
|
228
|
-
isUtility(u) && u.name === "background" && u.modifiers.length === 0,
|
|
229
|
-
);
|
|
230
|
-
expect(backgroundUtility).toEqual({
|
|
231
|
-
type: "utility",
|
|
232
|
-
name: "background",
|
|
233
|
-
value: "primary",
|
|
234
|
-
declarations: { background: "#007bff" },
|
|
235
|
-
variables: [],
|
|
236
|
-
children: [],
|
|
237
|
-
modifiers: [],
|
|
238
|
-
});
|
|
239
|
-
|
|
240
|
-
// Check modified variants exist
|
|
241
|
-
const hoverVariant = root.children.find(
|
|
242
|
-
(u) =>
|
|
243
|
-
isUtility(u) &&
|
|
244
|
-
u.name === "background" &&
|
|
245
|
-
u.modifiers.includes("hover") &&
|
|
246
|
-
!u.modifiers.includes("focus"),
|
|
247
|
-
);
|
|
248
|
-
const focusVariant = root.children.find(
|
|
249
|
-
(u) =>
|
|
250
|
-
isUtility(u) &&
|
|
251
|
-
u.name === "background" &&
|
|
252
|
-
u.modifiers.includes("focus") &&
|
|
253
|
-
!u.modifiers.includes("hover"),
|
|
254
|
-
);
|
|
255
|
-
const bothVariant = root.children.find(
|
|
256
|
-
(u) =>
|
|
257
|
-
isUtility(u) &&
|
|
258
|
-
u.name === "background" &&
|
|
259
|
-
u.modifiers.includes("hover") &&
|
|
260
|
-
u.modifiers.includes("focus"),
|
|
261
|
-
);
|
|
262
|
-
|
|
263
|
-
expect(hoverVariant).toBeDefined();
|
|
264
|
-
expect(focusVariant).toBeDefined();
|
|
265
|
-
expect(bothVariant).toBeDefined();
|
|
266
|
-
});
|
|
267
|
-
|
|
268
|
-
test("should handle multi-key modifiers correctly", () => {
|
|
269
|
-
const breakpointModifier: ModifierFactory = {
|
|
270
|
-
type: "modifier",
|
|
271
|
-
key: ["sm", "md", "lg"],
|
|
272
|
-
factory: ({ declarations }) => ({
|
|
273
|
-
"@media screen and (min-width: 640px)": declarations,
|
|
274
|
-
}),
|
|
275
|
-
};
|
|
276
|
-
|
|
277
|
-
const createTextUtility = utility("text", ({ value }) => ({
|
|
278
|
-
fontSize: value,
|
|
279
|
-
}));
|
|
280
|
-
|
|
281
|
-
createTextUtility(
|
|
282
|
-
{
|
|
283
|
-
base: "14px",
|
|
284
|
-
},
|
|
285
|
-
[breakpointModifier],
|
|
286
|
-
);
|
|
287
|
-
|
|
288
|
-
// 1 base utility + combinations for multi-key modifier
|
|
289
|
-
// Multi-key ["sm", "md", "lg"] creates: [sm], [md], [lg] = 3 variants
|
|
290
|
-
// Total: 1 base + 3 modified = 4
|
|
291
|
-
expect(root.children).toHaveLength(4);
|
|
292
|
-
|
|
293
|
-
const textUtility = root.children.find(
|
|
294
|
-
(u) => isUtility(u) && u.name === "text" && u.modifiers.length === 0,
|
|
295
|
-
);
|
|
296
|
-
expect(textUtility).toEqual({
|
|
297
|
-
type: "utility",
|
|
298
|
-
name: "text",
|
|
299
|
-
value: "base",
|
|
300
|
-
declarations: { fontSize: "14px" },
|
|
301
|
-
variables: [],
|
|
302
|
-
children: [],
|
|
303
|
-
modifiers: [],
|
|
304
|
-
});
|
|
305
|
-
|
|
306
|
-
// Check breakpoint variants exist
|
|
307
|
-
const smVariant = root.children.find(
|
|
308
|
-
(u) => isUtility(u) && u.name === "text" && u.modifiers.includes("sm"),
|
|
309
|
-
);
|
|
310
|
-
const mdVariant = root.children.find(
|
|
311
|
-
(u) => isUtility(u) && u.name === "text" && u.modifiers.includes("md"),
|
|
312
|
-
);
|
|
313
|
-
const lgVariant = root.children.find(
|
|
314
|
-
(u) => isUtility(u) && u.name === "text" && u.modifiers.includes("lg"),
|
|
315
|
-
);
|
|
316
|
-
|
|
317
|
-
expect(smVariant).toBeDefined();
|
|
318
|
-
expect(mdVariant).toBeDefined();
|
|
319
|
-
expect(lgVariant).toBeDefined();
|
|
320
|
-
});
|
|
321
|
-
|
|
322
|
-
test("should handle empty modifiers array", () => {
|
|
323
|
-
const createWidthUtility = utility("width", ({ value }) => ({
|
|
324
|
-
width: value,
|
|
325
|
-
}));
|
|
326
|
-
|
|
327
|
-
createWidthUtility(
|
|
328
|
-
{
|
|
329
|
-
full: "100%",
|
|
330
|
-
},
|
|
331
|
-
[],
|
|
332
|
-
);
|
|
333
|
-
|
|
334
|
-
// One utility instance should be created with empty modifiers array
|
|
335
|
-
expect(root.children).toHaveLength(1);
|
|
336
|
-
|
|
337
|
-
const widthUtility = root.children.find(
|
|
338
|
-
(u) => isUtility(u) && u.name === "width",
|
|
339
|
-
);
|
|
340
|
-
expect(widthUtility).toEqual({
|
|
341
|
-
type: "utility",
|
|
342
|
-
name: "width",
|
|
343
|
-
value: "full",
|
|
344
|
-
declarations: { width: "100%" },
|
|
345
|
-
variables: [],
|
|
346
|
-
children: [],
|
|
347
|
-
modifiers: [],
|
|
348
|
-
});
|
|
349
|
-
});
|
|
350
|
-
|
|
351
|
-
test("should handle undefined modifiers", () => {
|
|
352
|
-
const createHeightUtility = utility("height", ({ value }) => ({
|
|
353
|
-
height: value,
|
|
354
|
-
}));
|
|
355
|
-
|
|
356
|
-
createHeightUtility({
|
|
357
|
-
screen: "100vh",
|
|
358
|
-
});
|
|
359
|
-
|
|
360
|
-
// One utility instance should be created with empty modifiers array (default)
|
|
361
|
-
expect(root.children).toHaveLength(1);
|
|
362
|
-
|
|
363
|
-
const heightUtility = root.children.find(
|
|
364
|
-
(u) => isUtility(u) && u.name === "height",
|
|
365
|
-
);
|
|
366
|
-
expect(heightUtility).toEqual({
|
|
367
|
-
type: "utility",
|
|
368
|
-
name: "height",
|
|
369
|
-
value: "screen",
|
|
370
|
-
declarations: { height: "100vh" },
|
|
371
|
-
variables: [],
|
|
372
|
-
children: [],
|
|
373
|
-
modifiers: [],
|
|
374
|
-
});
|
|
375
|
-
});
|
|
376
|
-
|
|
377
|
-
test("should handle complex declarations", () => {
|
|
378
|
-
const createFlexUtility = utility("flex", ({ value }) => ({
|
|
379
|
-
display: "flex",
|
|
380
|
-
flexDirection: value === "col" ? "column" : "row",
|
|
381
|
-
gap: "1rem",
|
|
382
|
-
}));
|
|
383
|
-
|
|
384
|
-
createFlexUtility({
|
|
385
|
-
row: "row",
|
|
386
|
-
col: "col",
|
|
387
|
-
});
|
|
388
|
-
|
|
389
|
-
// Two utility instances should be created
|
|
390
|
-
expect(root.children).toHaveLength(2);
|
|
391
|
-
|
|
392
|
-
const rowUtility = root.children.find(
|
|
393
|
-
(u) => isUtility(u) && u.name === "flex" && u.value === "row",
|
|
394
|
-
);
|
|
395
|
-
const colUtility = root.children.find(
|
|
396
|
-
(u) => isUtility(u) && u.name === "flex" && u.value === "col",
|
|
397
|
-
);
|
|
398
|
-
|
|
399
|
-
expect(rowUtility).toEqual({
|
|
400
|
-
type: "utility",
|
|
401
|
-
name: "flex",
|
|
402
|
-
value: "row",
|
|
403
|
-
declarations: {
|
|
404
|
-
display: "flex",
|
|
405
|
-
flexDirection: "row",
|
|
406
|
-
gap: "1rem",
|
|
407
|
-
},
|
|
408
|
-
variables: [],
|
|
409
|
-
children: [],
|
|
410
|
-
modifiers: [],
|
|
411
|
-
});
|
|
412
|
-
expect(colUtility).toEqual({
|
|
413
|
-
type: "utility",
|
|
414
|
-
name: "flex",
|
|
415
|
-
value: "col",
|
|
416
|
-
declarations: {
|
|
417
|
-
display: "flex",
|
|
418
|
-
flexDirection: "column",
|
|
419
|
-
gap: "1rem",
|
|
420
|
-
},
|
|
421
|
-
variables: [],
|
|
422
|
-
children: [],
|
|
423
|
-
modifiers: [],
|
|
424
|
-
});
|
|
425
|
-
});
|
|
426
|
-
|
|
427
|
-
test("should preserve utility order in root utilities array", () => {
|
|
428
|
-
const createMarginUtility = utility("margin", ({ value }) => ({
|
|
429
|
-
margin: value,
|
|
430
|
-
}));
|
|
431
|
-
|
|
432
|
-
const createPaddingUtility = utility("padding", ({ value }) => ({
|
|
433
|
-
padding: value,
|
|
434
|
-
}));
|
|
435
|
-
|
|
436
|
-
// Create some utilities
|
|
437
|
-
createMarginUtility({ sm: "8px" });
|
|
438
|
-
createPaddingUtility({ md: "16px" });
|
|
439
|
-
|
|
440
|
-
expect(root.utilities).toHaveLength(2);
|
|
441
|
-
expect(root.utilities[0]?.name).toBe("margin");
|
|
442
|
-
expect(root.utilities[1]?.name).toBe("padding");
|
|
443
|
-
});
|
|
444
|
-
|
|
445
|
-
test("should handle empty entries object", () => {
|
|
446
|
-
const createEmptyUtility = utility("empty", ({ value }) => ({
|
|
447
|
-
display: value,
|
|
448
|
-
}));
|
|
449
|
-
|
|
450
|
-
createEmptyUtility({});
|
|
451
|
-
|
|
452
|
-
// No utility instances should be created for empty entries
|
|
453
|
-
expect(root.children).toHaveLength(0);
|
|
454
|
-
});
|
|
455
|
-
|
|
456
|
-
test("should create multiple utility instances when called multiple times", () => {
|
|
457
|
-
const createMarginUtility = utility("margin", ({ value }) => ({
|
|
458
|
-
margin: value,
|
|
459
|
-
}));
|
|
460
|
-
|
|
461
|
-
createMarginUtility({
|
|
462
|
-
sm: "8px",
|
|
463
|
-
md: "16px",
|
|
464
|
-
});
|
|
465
|
-
|
|
466
|
-
expect(root.children).toHaveLength(2);
|
|
467
|
-
|
|
468
|
-
const smUtility = root.children.find(
|
|
469
|
-
(u) => isUtility(u) && u.name === "margin" && u.value === "sm",
|
|
470
|
-
);
|
|
471
|
-
const mdUtility = root.children.find(
|
|
472
|
-
(u) => isUtility(u) && u.name === "margin" && u.value === "md",
|
|
473
|
-
);
|
|
474
|
-
|
|
475
|
-
expect(smUtility).toEqual({
|
|
476
|
-
type: "utility",
|
|
477
|
-
name: "margin",
|
|
478
|
-
value: "sm",
|
|
479
|
-
declarations: { margin: "8px" },
|
|
480
|
-
variables: [],
|
|
481
|
-
children: [],
|
|
482
|
-
modifiers: [],
|
|
483
|
-
});
|
|
484
|
-
expect(mdUtility).toEqual({
|
|
485
|
-
type: "utility",
|
|
486
|
-
name: "margin",
|
|
487
|
-
value: "md",
|
|
488
|
-
declarations: { margin: "16px" },
|
|
489
|
-
variables: [],
|
|
490
|
-
children: [],
|
|
491
|
-
modifiers: [],
|
|
492
|
-
});
|
|
493
|
-
});
|
|
494
|
-
|
|
495
|
-
test("should create additional utilities when utility is called multiple times", () => {
|
|
496
|
-
const createSpacingUtility = utility("spacing", ({ value }) => ({
|
|
497
|
-
margin: value,
|
|
498
|
-
}));
|
|
499
|
-
|
|
500
|
-
// First call
|
|
501
|
-
createSpacingUtility({
|
|
502
|
-
sm: "8px",
|
|
503
|
-
md: "16px",
|
|
504
|
-
});
|
|
505
|
-
|
|
506
|
-
// Second call - should create additional utilities
|
|
507
|
-
createSpacingUtility({
|
|
508
|
-
lg: "24px",
|
|
509
|
-
xl: "32px",
|
|
510
|
-
});
|
|
511
|
-
|
|
512
|
-
expect(root.children).toHaveLength(4);
|
|
513
|
-
|
|
514
|
-
const smUtility = root.children.find(
|
|
515
|
-
(u) => isUtility(u) && u.name === "spacing" && u.value === "sm",
|
|
516
|
-
);
|
|
517
|
-
const mdUtility = root.children.find(
|
|
518
|
-
(u) => isUtility(u) && u.name === "spacing" && u.value === "md",
|
|
519
|
-
);
|
|
520
|
-
const lgUtility = root.children.find(
|
|
521
|
-
(u) => isUtility(u) && u.name === "spacing" && u.value === "lg",
|
|
522
|
-
);
|
|
523
|
-
const xlUtility = root.children.find(
|
|
524
|
-
(u) => isUtility(u) && u.name === "spacing" && u.value === "xl",
|
|
525
|
-
);
|
|
526
|
-
|
|
527
|
-
expect(smUtility).toBeDefined();
|
|
528
|
-
expect(mdUtility).toBeDefined();
|
|
529
|
-
expect(lgUtility).toBeDefined();
|
|
530
|
-
expect(xlUtility).toBeDefined();
|
|
531
|
-
});
|
|
532
|
-
|
|
533
|
-
test("should create additional utilities with same key", () => {
|
|
534
|
-
const createColorUtility = utility("color", ({ value }) => ({
|
|
535
|
-
color: value,
|
|
536
|
-
}));
|
|
537
|
-
|
|
538
|
-
// First call
|
|
539
|
-
createColorUtility({
|
|
540
|
-
primary: "#007bff",
|
|
541
|
-
});
|
|
542
|
-
|
|
543
|
-
// Second call with same key - should create another utility instance
|
|
544
|
-
createColorUtility({
|
|
545
|
-
primary: "#0056b3",
|
|
546
|
-
});
|
|
547
|
-
|
|
548
|
-
expect(root.children).toHaveLength(2);
|
|
549
|
-
|
|
550
|
-
const utilities = root.children.filter(
|
|
551
|
-
(u): u is Utility =>
|
|
552
|
-
isUtility(u) && u.name === "color" && u.value === "primary",
|
|
553
|
-
);
|
|
554
|
-
expect(utilities).toHaveLength(2);
|
|
555
|
-
expect(utilities[0]?.declarations?.color).toBe("#007bff");
|
|
556
|
-
expect(utilities[1]?.declarations?.color).toBe("#0056b3");
|
|
557
|
-
});
|
|
558
|
-
|
|
559
|
-
test("should handle different modifiers on subsequent calls", () => {
|
|
560
|
-
const hoverModifier: ModifierFactory = {
|
|
561
|
-
type: "modifier",
|
|
562
|
-
key: ["hover"],
|
|
563
|
-
factory: ({ declarations }) => ({
|
|
564
|
-
"&:hover": declarations,
|
|
565
|
-
}),
|
|
566
|
-
};
|
|
567
|
-
|
|
568
|
-
const focusModifier: ModifierFactory = {
|
|
569
|
-
type: "modifier",
|
|
570
|
-
key: ["focus"],
|
|
571
|
-
factory: ({ declarations }) => ({
|
|
572
|
-
"&:focus": declarations,
|
|
573
|
-
}),
|
|
574
|
-
};
|
|
575
|
-
|
|
576
|
-
const createButtonUtility = utility("button", ({ value }) => ({
|
|
577
|
-
backgroundColor: value,
|
|
578
|
-
}));
|
|
579
|
-
|
|
580
|
-
// First call with hover modifier
|
|
581
|
-
createButtonUtility(
|
|
582
|
-
{
|
|
583
|
-
primary: "#007bff",
|
|
584
|
-
},
|
|
585
|
-
[hoverModifier],
|
|
586
|
-
);
|
|
587
|
-
|
|
588
|
-
// Second call with focus modifier
|
|
589
|
-
createButtonUtility(
|
|
590
|
-
{
|
|
591
|
-
secondary: "#6c757d",
|
|
592
|
-
},
|
|
593
|
-
[focusModifier],
|
|
594
|
-
);
|
|
595
|
-
|
|
596
|
-
// First call creates 1 base + 1 hover variant = 2
|
|
597
|
-
// Second call creates 1 base + 1 focus variant = 2
|
|
598
|
-
// Total: 4
|
|
599
|
-
expect(root.children).toHaveLength(4);
|
|
600
|
-
|
|
601
|
-
const primaryUtility = root.children.find(
|
|
602
|
-
(u): u is Utility =>
|
|
603
|
-
isUtility(u) && u.name === "button" && u.value === "primary",
|
|
604
|
-
);
|
|
605
|
-
const secondaryUtility = root.children.find(
|
|
606
|
-
(u): u is Utility =>
|
|
607
|
-
isUtility(u) && u.name === "button" && u.value === "secondary",
|
|
608
|
-
);
|
|
609
|
-
|
|
610
|
-
expect(primaryUtility?.modifiers).toEqual([]);
|
|
611
|
-
expect(secondaryUtility?.modifiers).toEqual([]);
|
|
612
|
-
|
|
613
|
-
// Check that modified variants were created
|
|
614
|
-
const primaryHoverVariant = root.children.find(
|
|
615
|
-
(u) =>
|
|
616
|
-
isUtility(u) &&
|
|
617
|
-
u.name === "button" &&
|
|
618
|
-
u.value === "primary" &&
|
|
619
|
-
u.modifiers.includes("hover"),
|
|
620
|
-
);
|
|
621
|
-
const secondaryFocusVariant = root.children.find(
|
|
622
|
-
(u): u is Utility =>
|
|
623
|
-
isUtility(u) &&
|
|
624
|
-
u.name === "button" &&
|
|
625
|
-
u.value === "secondary" &&
|
|
626
|
-
u.modifiers.includes("focus"),
|
|
627
|
-
);
|
|
628
|
-
|
|
629
|
-
expect(primaryHoverVariant).toBeDefined();
|
|
630
|
-
expect(secondaryFocusVariant).toBeDefined();
|
|
631
|
-
});
|
|
632
|
-
|
|
633
|
-
test("should support callback context functions", () => {
|
|
634
|
-
const createAdvancedUtility = utility(
|
|
635
|
-
"advanced",
|
|
636
|
-
({ value, variable, selector }) => {
|
|
637
|
-
const colorVar = variable("color-var", value);
|
|
638
|
-
selector(".nested", { color: colorVar.value });
|
|
639
|
-
|
|
640
|
-
return {
|
|
641
|
-
backgroundColor: colorVar.value,
|
|
642
|
-
};
|
|
643
|
-
},
|
|
644
|
-
);
|
|
645
|
-
|
|
646
|
-
createAdvancedUtility({
|
|
647
|
-
primary: "#007bff",
|
|
648
|
-
});
|
|
649
|
-
|
|
650
|
-
expect(root.children).toHaveLength(1);
|
|
651
|
-
|
|
652
|
-
const advancedUtility = root.children.find(
|
|
653
|
-
(u): u is Utility => isUtility(u) && u.name === "advanced",
|
|
654
|
-
);
|
|
655
|
-
expect(advancedUtility?.declarations).toEqual({
|
|
656
|
-
backgroundColor: "#007bff",
|
|
657
|
-
});
|
|
658
|
-
expect(advancedUtility?.variables).toHaveLength(1);
|
|
659
|
-
expect(advancedUtility?.children).toHaveLength(1);
|
|
660
|
-
});
|
|
661
|
-
});
|
|
662
|
-
|
|
663
|
-
describe("createModifiedUtilityFunction", () => {
|
|
664
|
-
let root: Root;
|
|
665
|
-
|
|
666
|
-
beforeEach(() => {
|
|
667
|
-
root = createRoot();
|
|
668
|
-
});
|
|
669
|
-
|
|
670
|
-
test("should create a modified utility function", () => {
|
|
671
|
-
expect(applyModifiers).toBeTypeOf("function");
|
|
672
|
-
});
|
|
673
|
-
|
|
674
|
-
test("should return a new utility instance with modified modifiers", () => {
|
|
675
|
-
const baseUtility: Utility = {
|
|
676
|
-
type: "utility",
|
|
677
|
-
name: "padding",
|
|
678
|
-
value: "sm",
|
|
679
|
-
declarations: { padding: "8px" },
|
|
680
|
-
variables: [],
|
|
681
|
-
children: [],
|
|
682
|
-
modifiers: [],
|
|
683
|
-
};
|
|
684
|
-
|
|
685
|
-
const hoverModifier: ModifierFactory = {
|
|
686
|
-
type: "modifier",
|
|
687
|
-
key: ["hover"],
|
|
688
|
-
factory: ({ declarations }) => ({
|
|
689
|
-
"&:hover": declarations,
|
|
690
|
-
}),
|
|
691
|
-
};
|
|
692
|
-
|
|
693
|
-
const result = applyModifiers(
|
|
694
|
-
baseUtility,
|
|
695
|
-
root,
|
|
696
|
-
new Map(Object.entries({ hover: hoverModifier })),
|
|
697
|
-
);
|
|
698
|
-
|
|
699
|
-
expect(result).not.toBe(baseUtility); // Should be a new instance
|
|
700
|
-
expect(result.type).toBe("utility");
|
|
701
|
-
expect(result.name).toBe("padding");
|
|
702
|
-
expect(result.value).toBe("sm");
|
|
703
|
-
expect(result.modifiers).toEqual(["hover"]);
|
|
704
|
-
});
|
|
705
|
-
|
|
706
|
-
test("should preserve base utility properties while updating modifiers", () => {
|
|
707
|
-
const baseUtility: Utility = {
|
|
708
|
-
type: "utility",
|
|
709
|
-
name: "margin",
|
|
710
|
-
value: "lg",
|
|
711
|
-
declarations: { margin: "24px" },
|
|
712
|
-
variables: [],
|
|
713
|
-
children: [],
|
|
714
|
-
modifiers: [],
|
|
715
|
-
};
|
|
716
|
-
|
|
717
|
-
const focusModifier: ModifierFactory = {
|
|
718
|
-
type: "modifier",
|
|
719
|
-
key: ["focus"],
|
|
720
|
-
factory: ({ declarations }) => ({
|
|
721
|
-
"&:focus": declarations,
|
|
722
|
-
}),
|
|
723
|
-
};
|
|
724
|
-
|
|
725
|
-
const result = applyModifiers(
|
|
726
|
-
baseUtility,
|
|
727
|
-
root,
|
|
728
|
-
new Map(Object.entries({ focus: focusModifier })),
|
|
729
|
-
);
|
|
730
|
-
|
|
731
|
-
expect(result.type).toBe("utility");
|
|
732
|
-
expect(result.name).toBe("margin");
|
|
733
|
-
expect(result.value).toBe("lg");
|
|
734
|
-
expect(result.declarations).toEqual({ margin: "24px" });
|
|
735
|
-
expect(result.modifiers).toEqual(["focus"]);
|
|
736
|
-
});
|
|
737
|
-
|
|
738
|
-
test("should apply multiple modifiers to utility", () => {
|
|
739
|
-
const baseUtility: Utility = {
|
|
740
|
-
type: "utility",
|
|
741
|
-
name: "color",
|
|
742
|
-
value: "primary",
|
|
743
|
-
declarations: { color: "#007bff" },
|
|
744
|
-
variables: [],
|
|
745
|
-
children: [],
|
|
746
|
-
modifiers: [],
|
|
747
|
-
};
|
|
748
|
-
|
|
749
|
-
const hoverModifier: ModifierFactory = {
|
|
750
|
-
type: "modifier",
|
|
751
|
-
key: ["hover"],
|
|
752
|
-
factory: ({ declarations }) => ({
|
|
753
|
-
"&:hover": declarations,
|
|
754
|
-
}),
|
|
755
|
-
};
|
|
756
|
-
|
|
757
|
-
const focusModifier: ModifierFactory = {
|
|
758
|
-
type: "modifier",
|
|
759
|
-
key: ["focus"],
|
|
760
|
-
factory: ({ declarations }) => ({
|
|
761
|
-
"&:focus": declarations,
|
|
762
|
-
}),
|
|
763
|
-
};
|
|
764
|
-
|
|
765
|
-
const result = applyModifiers(
|
|
766
|
-
baseUtility,
|
|
767
|
-
root,
|
|
768
|
-
new Map([
|
|
769
|
-
["hover", hoverModifier],
|
|
770
|
-
["focus", focusModifier],
|
|
771
|
-
]),
|
|
772
|
-
);
|
|
773
|
-
|
|
774
|
-
expect(result.modifiers).toEqual(["hover", "focus"]);
|
|
775
|
-
});
|
|
776
|
-
|
|
777
|
-
test("should handle modifiers that add variables", () => {
|
|
778
|
-
const baseUtility: Utility = {
|
|
779
|
-
type: "utility",
|
|
780
|
-
name: "background",
|
|
781
|
-
value: "primary",
|
|
782
|
-
declarations: { background: "#007bff" },
|
|
783
|
-
variables: [],
|
|
784
|
-
children: [],
|
|
785
|
-
modifiers: [],
|
|
786
|
-
};
|
|
787
|
-
|
|
788
|
-
const variableModifier: ModifierFactory = {
|
|
789
|
-
type: "modifier",
|
|
790
|
-
key: ["var"],
|
|
791
|
-
factory: ({ variable }) => {
|
|
792
|
-
const colorVar = variable("bg-color", "#007bff");
|
|
793
|
-
return {
|
|
794
|
-
background: colorVar.value,
|
|
795
|
-
};
|
|
796
|
-
},
|
|
797
|
-
};
|
|
798
|
-
|
|
799
|
-
const result = applyModifiers(
|
|
800
|
-
baseUtility,
|
|
801
|
-
root,
|
|
802
|
-
new Map(
|
|
803
|
-
Object.entries({
|
|
804
|
-
var: variableModifier,
|
|
805
|
-
}),
|
|
806
|
-
),
|
|
807
|
-
);
|
|
808
|
-
|
|
809
|
-
expect(result.variables).toHaveLength(1);
|
|
810
|
-
expect(result.variables[0]).toEqual({
|
|
811
|
-
type: "variable",
|
|
812
|
-
name: "bg-color",
|
|
813
|
-
value: "#007bff",
|
|
814
|
-
});
|
|
815
|
-
expect(result.modifiers).toEqual(["var"]);
|
|
816
|
-
});
|
|
817
|
-
|
|
818
|
-
test("should handle modifiers that add child selectors", () => {
|
|
819
|
-
const baseUtility: Utility = {
|
|
820
|
-
type: "utility",
|
|
821
|
-
name: "text",
|
|
822
|
-
value: "underline",
|
|
823
|
-
declarations: { textDecoration: "underline" },
|
|
824
|
-
variables: [],
|
|
825
|
-
children: [],
|
|
826
|
-
modifiers: [],
|
|
827
|
-
};
|
|
828
|
-
|
|
829
|
-
const childModifier: ModifierFactory = {
|
|
830
|
-
type: "modifier",
|
|
831
|
-
key: ["child"],
|
|
832
|
-
factory: ({ selector }) => {
|
|
833
|
-
selector("& > span", { textDecoration: "inherit" });
|
|
834
|
-
return {};
|
|
835
|
-
},
|
|
836
|
-
};
|
|
837
|
-
|
|
838
|
-
const result = applyModifiers(
|
|
839
|
-
baseUtility,
|
|
840
|
-
root,
|
|
841
|
-
new Map(Object.entries({ child: childModifier })),
|
|
842
|
-
);
|
|
843
|
-
|
|
844
|
-
expect(result.children).toHaveLength(1);
|
|
845
|
-
expect(result.children[0]).toMatchObject({
|
|
846
|
-
type: "selector",
|
|
847
|
-
query: "& > span",
|
|
848
|
-
declarations: { textDecoration: "inherit" },
|
|
849
|
-
});
|
|
850
|
-
expect(result.modifiers).toEqual(["child"]);
|
|
851
|
-
});
|
|
852
|
-
|
|
853
|
-
test("should handle empty modifiers array", () => {
|
|
854
|
-
const baseUtility: Utility = {
|
|
855
|
-
type: "utility",
|
|
856
|
-
name: "display",
|
|
857
|
-
value: "block",
|
|
858
|
-
declarations: { display: "block" },
|
|
859
|
-
variables: [],
|
|
860
|
-
children: [],
|
|
861
|
-
modifiers: [],
|
|
862
|
-
};
|
|
863
|
-
|
|
864
|
-
const result = applyModifiers(baseUtility, root, new Map());
|
|
865
|
-
|
|
866
|
-
expect(result).not.toBe(baseUtility);
|
|
867
|
-
expect(result.modifiers).toEqual([]);
|
|
868
|
-
expect(result.declarations).toEqual({ display: "block" });
|
|
869
|
-
});
|
|
870
|
-
|
|
871
|
-
test("should handle modifier combination with different keys", () => {
|
|
872
|
-
const baseUtility: Utility = {
|
|
873
|
-
type: "utility",
|
|
874
|
-
name: "opacity",
|
|
875
|
-
value: "50",
|
|
876
|
-
declarations: { opacity: "0.5" },
|
|
877
|
-
variables: [],
|
|
878
|
-
children: [],
|
|
879
|
-
modifiers: [],
|
|
880
|
-
};
|
|
881
|
-
|
|
882
|
-
const hoverModifier: ModifierFactory = {
|
|
883
|
-
type: "modifier",
|
|
884
|
-
key: ["hover"],
|
|
885
|
-
factory: ({ declarations }) => ({
|
|
886
|
-
"&:hover": declarations,
|
|
887
|
-
}),
|
|
888
|
-
};
|
|
889
|
-
|
|
890
|
-
const darkModeModifier: ModifierFactory = {
|
|
891
|
-
type: "modifier",
|
|
892
|
-
key: ["dark"],
|
|
893
|
-
factory: ({ declarations }) => ({
|
|
894
|
-
".dark &": declarations,
|
|
895
|
-
}),
|
|
896
|
-
};
|
|
897
|
-
|
|
898
|
-
const result = applyModifiers(
|
|
899
|
-
baseUtility,
|
|
900
|
-
root,
|
|
901
|
-
new Map([
|
|
902
|
-
["dark", darkModeModifier],
|
|
903
|
-
["hover", hoverModifier],
|
|
904
|
-
]),
|
|
905
|
-
);
|
|
906
|
-
|
|
907
|
-
expect(result.modifiers).toEqual(["dark", "hover"]);
|
|
908
|
-
});
|
|
909
|
-
|
|
910
|
-
test("should apply modifiers in order", () => {
|
|
911
|
-
const baseUtility: Utility = {
|
|
912
|
-
type: "utility",
|
|
913
|
-
name: "transform",
|
|
914
|
-
value: "scale",
|
|
915
|
-
declarations: { transform: "scale(1)" },
|
|
916
|
-
variables: [],
|
|
917
|
-
children: [],
|
|
918
|
-
modifiers: [],
|
|
919
|
-
};
|
|
920
|
-
|
|
921
|
-
const executionOrder: string[] = [];
|
|
922
|
-
|
|
923
|
-
const firstModifier: ModifierFactory = {
|
|
924
|
-
type: "modifier",
|
|
925
|
-
key: ["first"],
|
|
926
|
-
factory: ({ variable }) => {
|
|
927
|
-
executionOrder.push("first");
|
|
928
|
-
variable("first-var", "1");
|
|
929
|
-
return {};
|
|
930
|
-
},
|
|
931
|
-
};
|
|
932
|
-
|
|
933
|
-
const secondModifier: ModifierFactory = {
|
|
934
|
-
type: "modifier",
|
|
935
|
-
key: ["second"],
|
|
936
|
-
factory: ({ variable }) => {
|
|
937
|
-
executionOrder.push("second");
|
|
938
|
-
variable("second-var", "2");
|
|
939
|
-
return {};
|
|
940
|
-
},
|
|
941
|
-
};
|
|
942
|
-
|
|
943
|
-
const result = applyModifiers(
|
|
944
|
-
baseUtility,
|
|
945
|
-
root,
|
|
946
|
-
new Map([
|
|
947
|
-
["first", firstModifier],
|
|
948
|
-
["second", secondModifier],
|
|
949
|
-
]),
|
|
950
|
-
);
|
|
951
|
-
|
|
952
|
-
expect(executionOrder).toEqual(["first", "second"]);
|
|
953
|
-
expect(result.variables).toHaveLength(2);
|
|
954
|
-
expect(result.variables[0]?.name).toBe("first-var");
|
|
955
|
-
expect(result.variables[1]?.name).toBe("second-var");
|
|
956
|
-
});
|
|
957
|
-
|
|
958
|
-
test("should handle modifiers that modify existing declarations", () => {
|
|
959
|
-
const baseUtility: Utility = {
|
|
960
|
-
type: "utility",
|
|
961
|
-
name: "border",
|
|
962
|
-
value: "solid",
|
|
963
|
-
declarations: { borderStyle: "solid" },
|
|
964
|
-
variables: [],
|
|
965
|
-
children: [],
|
|
966
|
-
modifiers: [],
|
|
967
|
-
};
|
|
968
|
-
|
|
969
|
-
const widthModifier: ModifierFactory = {
|
|
970
|
-
type: "modifier",
|
|
971
|
-
key: ["thick"],
|
|
972
|
-
factory: ({ declarations }) => ({
|
|
973
|
-
...declarations,
|
|
974
|
-
borderWidth: "4px",
|
|
975
|
-
}),
|
|
976
|
-
};
|
|
977
|
-
|
|
978
|
-
const result = applyModifiers(
|
|
979
|
-
baseUtility,
|
|
980
|
-
root,
|
|
981
|
-
new Map([["thick", widthModifier]]),
|
|
982
|
-
);
|
|
983
|
-
|
|
984
|
-
// The original declarations should be preserved
|
|
985
|
-
expect(result.declarations).toEqual({ borderStyle: "solid" });
|
|
986
|
-
expect(result.modifiers).toEqual(["thick"]);
|
|
987
|
-
});
|
|
988
|
-
|
|
989
|
-
test("should handle modifiers with multi-key definitions", () => {
|
|
990
|
-
const baseUtility: Utility = {
|
|
991
|
-
type: "utility",
|
|
992
|
-
name: "fontSize",
|
|
993
|
-
value: "base",
|
|
994
|
-
declarations: { fontSize: "16px" },
|
|
995
|
-
variables: [],
|
|
996
|
-
children: [],
|
|
997
|
-
modifiers: [],
|
|
998
|
-
};
|
|
999
|
-
|
|
1000
|
-
const responsiveModifier: ModifierFactory = {
|
|
1001
|
-
type: "modifier",
|
|
1002
|
-
key: ["sm", "md", "lg"],
|
|
1003
|
-
factory: ({ declarations }) => ({
|
|
1004
|
-
"@media (min-width: 640px)": declarations,
|
|
1005
|
-
}),
|
|
1006
|
-
};
|
|
1007
|
-
|
|
1008
|
-
const result = applyModifiers(
|
|
1009
|
-
baseUtility,
|
|
1010
|
-
root,
|
|
1011
|
-
new Map([["md", responsiveModifier]]),
|
|
1012
|
-
);
|
|
1013
|
-
|
|
1014
|
-
expect(result.modifiers).toEqual(["md"]);
|
|
1015
|
-
});
|
|
1016
|
-
|
|
1017
|
-
test("should create independent instances for each modification", () => {
|
|
1018
|
-
const baseUtility: Utility = {
|
|
1019
|
-
type: "utility",
|
|
1020
|
-
name: "gap",
|
|
1021
|
-
value: "sm",
|
|
1022
|
-
declarations: { gap: "8px" },
|
|
1023
|
-
variables: [],
|
|
1024
|
-
children: [],
|
|
1025
|
-
modifiers: [],
|
|
1026
|
-
};
|
|
1027
|
-
|
|
1028
|
-
const hoverModifier: ModifierFactory = {
|
|
1029
|
-
type: "modifier",
|
|
1030
|
-
key: ["hover"],
|
|
1031
|
-
factory: ({ variable }) => {
|
|
1032
|
-
variable("hover-gap", "16px");
|
|
1033
|
-
return {};
|
|
1034
|
-
},
|
|
1035
|
-
};
|
|
1036
|
-
|
|
1037
|
-
const result1 = applyModifiers(
|
|
1038
|
-
baseUtility,
|
|
1039
|
-
root,
|
|
1040
|
-
new Map(Object.entries({ hover: hoverModifier })),
|
|
1041
|
-
);
|
|
1042
|
-
const result2 = applyModifiers(
|
|
1043
|
-
baseUtility,
|
|
1044
|
-
root,
|
|
1045
|
-
new Map(Object.entries({ hover: hoverModifier })),
|
|
1046
|
-
);
|
|
1047
|
-
|
|
1048
|
-
// The instances themselves should be different
|
|
1049
|
-
expect(result1).not.toBe(result2);
|
|
1050
|
-
// Note: variables and children arrays are shared from baseUtility due to spread operator
|
|
1051
|
-
// This is the actual behavior of the function - it mutates the arrays
|
|
1052
|
-
expect(result1.variables).toBe(result2.variables);
|
|
1053
|
-
expect(result1.children).toBe(result2.children);
|
|
1054
|
-
});
|
|
1055
|
-
|
|
1056
|
-
test("should handle complex modifier factory with multiple operations", () => {
|
|
1057
|
-
const baseUtility: Utility = {
|
|
1058
|
-
type: "utility",
|
|
1059
|
-
name: "button",
|
|
1060
|
-
value: "primary",
|
|
1061
|
-
declarations: { backgroundColor: "#007bff", color: "white" },
|
|
1062
|
-
variables: [],
|
|
1063
|
-
children: [],
|
|
1064
|
-
modifiers: [],
|
|
1065
|
-
};
|
|
1066
|
-
|
|
1067
|
-
const complexModifier: ModifierFactory = {
|
|
1068
|
-
type: "modifier",
|
|
1069
|
-
key: ["interactive"],
|
|
1070
|
-
factory: ({ variable, selector }) => {
|
|
1071
|
-
const bgVar = variable("button-bg", "#007bff");
|
|
1072
|
-
const textVar = variable("button-text", "white");
|
|
1073
|
-
|
|
1074
|
-
selector("&:hover", {
|
|
1075
|
-
backgroundColor: "darken(" + bgVar.value + ", 10%)",
|
|
1076
|
-
});
|
|
1077
|
-
|
|
1078
|
-
selector("&:focus", {
|
|
1079
|
-
outline: "2px solid " + bgVar.value,
|
|
1080
|
-
});
|
|
1081
|
-
|
|
1082
|
-
selector("&:disabled", {
|
|
1083
|
-
opacity: "0.5",
|
|
1084
|
-
cursor: "not-allowed",
|
|
1085
|
-
});
|
|
1086
|
-
|
|
1087
|
-
return {
|
|
1088
|
-
backgroundColor: bgVar.value,
|
|
1089
|
-
color: textVar.value,
|
|
1090
|
-
};
|
|
1091
|
-
},
|
|
1092
|
-
};
|
|
1093
|
-
|
|
1094
|
-
const result = applyModifiers(
|
|
1095
|
-
baseUtility,
|
|
1096
|
-
root,
|
|
1097
|
-
new Map([["interactive", complexModifier]]),
|
|
1098
|
-
);
|
|
1099
|
-
|
|
1100
|
-
expect(result.variables).toHaveLength(2);
|
|
1101
|
-
expect(result.variables[0]?.name).toBe("button-bg");
|
|
1102
|
-
expect(result.variables[1]?.name).toBe("button-text");
|
|
1103
|
-
|
|
1104
|
-
expect(result.children).toHaveLength(3);
|
|
1105
|
-
expect(result.children[0]).toMatchObject({
|
|
1106
|
-
type: "selector",
|
|
1107
|
-
query: "&:hover",
|
|
1108
|
-
});
|
|
1109
|
-
expect(result.children[1]).toMatchObject({
|
|
1110
|
-
type: "selector",
|
|
1111
|
-
query: "&:focus",
|
|
1112
|
-
});
|
|
1113
|
-
expect(result.children[2]).toMatchObject({
|
|
1114
|
-
type: "selector",
|
|
1115
|
-
query: "&:disabled",
|
|
1116
|
-
});
|
|
1117
|
-
|
|
1118
|
-
expect(result.modifiers).toEqual(["interactive"]);
|
|
1119
|
-
});
|
|
1120
|
-
|
|
1121
|
-
test("should mutate base utility's arrays when modifiers add items", () => {
|
|
1122
|
-
const baseUtility: Utility = {
|
|
1123
|
-
type: "utility",
|
|
1124
|
-
name: "position",
|
|
1125
|
-
value: "absolute",
|
|
1126
|
-
declarations: { position: "absolute" },
|
|
1127
|
-
variables: [],
|
|
1128
|
-
children: [],
|
|
1129
|
-
modifiers: [],
|
|
1130
|
-
};
|
|
1131
|
-
|
|
1132
|
-
const originalDeclarations = { ...baseUtility.declarations };
|
|
1133
|
-
const originalModifiers = [...baseUtility.modifiers];
|
|
1134
|
-
|
|
1135
|
-
const modifier: ModifierFactory = {
|
|
1136
|
-
type: "modifier",
|
|
1137
|
-
key: ["test"],
|
|
1138
|
-
factory: ({ variable }) => {
|
|
1139
|
-
variable("test-var", "value");
|
|
1140
|
-
return { top: "0" };
|
|
1141
|
-
},
|
|
1142
|
-
};
|
|
1143
|
-
|
|
1144
|
-
const result = applyModifiers(
|
|
1145
|
-
baseUtility,
|
|
1146
|
-
root,
|
|
1147
|
-
new Map([["test", modifier]]),
|
|
1148
|
-
);
|
|
1149
|
-
|
|
1150
|
-
// The declarations object should remain unchanged
|
|
1151
|
-
expect(baseUtility.declarations).toEqual(originalDeclarations);
|
|
1152
|
-
// The modifiers array should remain unchanged
|
|
1153
|
-
expect(baseUtility.modifiers).toEqual(originalModifiers);
|
|
1154
|
-
// But variables array will be mutated due to spread operator shallow copy
|
|
1155
|
-
expect(baseUtility.variables).toHaveLength(1);
|
|
1156
|
-
expect(baseUtility.variables[0]).toEqual({
|
|
1157
|
-
type: "variable",
|
|
1158
|
-
name: "test-var",
|
|
1159
|
-
value: "value",
|
|
1160
|
-
});
|
|
1161
|
-
// The result should have the combination in its modifiers
|
|
1162
|
-
expect(result.modifiers).toEqual(["test"]);
|
|
1163
|
-
});
|
|
1164
|
-
|
|
1165
|
-
test("should handle modifier that returns null declarations", () => {
|
|
1166
|
-
const baseUtility: Utility = {
|
|
1167
|
-
type: "utility",
|
|
1168
|
-
name: "display",
|
|
1169
|
-
value: "flex",
|
|
1170
|
-
declarations: { display: "flex" },
|
|
1171
|
-
variables: [],
|
|
1172
|
-
children: [],
|
|
1173
|
-
modifiers: [],
|
|
1174
|
-
};
|
|
1175
|
-
|
|
1176
|
-
const nullModifier: ModifierFactory = {
|
|
1177
|
-
type: "modifier",
|
|
1178
|
-
key: ["null"],
|
|
1179
|
-
factory: () => ({}),
|
|
1180
|
-
};
|
|
1181
|
-
|
|
1182
|
-
const result = applyModifiers(
|
|
1183
|
-
baseUtility,
|
|
1184
|
-
root,
|
|
1185
|
-
new Map([["null", nullModifier]]),
|
|
1186
|
-
);
|
|
1187
|
-
|
|
1188
|
-
expect(result.declarations).toEqual({ display: "flex" });
|
|
1189
|
-
expect(result.modifiers).toEqual(["null"]);
|
|
1190
|
-
});
|
|
1191
|
-
|
|
1192
|
-
test("should handle modifier that returns undefined declarations", () => {
|
|
1193
|
-
const baseUtility: Utility = {
|
|
1194
|
-
type: "utility",
|
|
1195
|
-
name: "width",
|
|
1196
|
-
value: "auto",
|
|
1197
|
-
declarations: { width: "auto" },
|
|
1198
|
-
variables: [],
|
|
1199
|
-
children: [],
|
|
1200
|
-
modifiers: [],
|
|
1201
|
-
};
|
|
1202
|
-
|
|
1203
|
-
const undefinedModifier: ModifierFactory = {
|
|
1204
|
-
type: "modifier",
|
|
1205
|
-
key: ["undefined"],
|
|
1206
|
-
factory: () => undefined,
|
|
1207
|
-
};
|
|
1208
|
-
|
|
1209
|
-
const result = applyModifiers(
|
|
1210
|
-
baseUtility,
|
|
1211
|
-
root,
|
|
1212
|
-
new Map([["undefined", undefinedModifier]]),
|
|
1213
|
-
);
|
|
1214
|
-
|
|
1215
|
-
expect(result.declarations).toEqual({ width: "auto" });
|
|
1216
|
-
expect(result.modifiers).toEqual(["undefined"]);
|
|
1217
|
-
});
|
|
1218
|
-
|
|
1219
|
-
test("should handle modifier that returns empty object", () => {
|
|
1220
|
-
const baseUtility: Utility = {
|
|
1221
|
-
type: "utility",
|
|
1222
|
-
name: "height",
|
|
1223
|
-
value: "100vh",
|
|
1224
|
-
declarations: { height: "100vh" },
|
|
1225
|
-
variables: [],
|
|
1226
|
-
children: [],
|
|
1227
|
-
modifiers: [],
|
|
1228
|
-
};
|
|
1229
|
-
|
|
1230
|
-
const emptyModifier: ModifierFactory = {
|
|
1231
|
-
type: "modifier",
|
|
1232
|
-
key: ["empty"],
|
|
1233
|
-
factory: () => ({}),
|
|
1234
|
-
};
|
|
1235
|
-
|
|
1236
|
-
const result = applyModifiers(
|
|
1237
|
-
baseUtility,
|
|
1238
|
-
root,
|
|
1239
|
-
new Map([["empty", emptyModifier]]),
|
|
1240
|
-
);
|
|
1241
|
-
|
|
1242
|
-
expect(result.declarations).toEqual({ height: "100vh" });
|
|
1243
|
-
expect(result.modifiers).toEqual(["empty"]);
|
|
1244
|
-
});
|
|
1245
|
-
|
|
1246
|
-
test("should handle combination array that is empty", () => {
|
|
1247
|
-
const baseUtility: Utility = {
|
|
1248
|
-
type: "utility",
|
|
1249
|
-
name: "overflow",
|
|
1250
|
-
value: "hidden",
|
|
1251
|
-
declarations: { overflow: "hidden" },
|
|
1252
|
-
variables: [],
|
|
1253
|
-
children: [],
|
|
1254
|
-
modifiers: [],
|
|
1255
|
-
};
|
|
1256
|
-
|
|
1257
|
-
const result = applyModifiers(baseUtility, root, new Map());
|
|
1258
|
-
|
|
1259
|
-
expect(result.modifiers).toEqual([]);
|
|
1260
|
-
expect(result.declarations).toEqual({ overflow: "hidden" });
|
|
1261
|
-
});
|
|
1262
|
-
|
|
1263
|
-
test("should handle nested selector creation within modifiers", () => {
|
|
1264
|
-
const baseUtility: Utility = {
|
|
1265
|
-
type: "utility",
|
|
1266
|
-
name: "card",
|
|
1267
|
-
value: "default",
|
|
1268
|
-
declarations: { padding: "1rem" },
|
|
1269
|
-
variables: [],
|
|
1270
|
-
children: [],
|
|
1271
|
-
modifiers: [],
|
|
1272
|
-
};
|
|
1273
|
-
|
|
1274
|
-
const nestedModifier: ModifierFactory = {
|
|
1275
|
-
type: "modifier",
|
|
1276
|
-
key: ["nested"],
|
|
1277
|
-
factory: ({ selector }) => {
|
|
1278
|
-
selector(".card-header", { fontWeight: "bold" });
|
|
1279
|
-
selector(".card-body", { padding: "0.5rem" });
|
|
1280
|
-
selector(".card-footer", { borderTop: "1px solid #ccc" });
|
|
1281
|
-
return {};
|
|
1282
|
-
},
|
|
1283
|
-
};
|
|
1284
|
-
|
|
1285
|
-
const result = applyModifiers(
|
|
1286
|
-
baseUtility,
|
|
1287
|
-
root,
|
|
1288
|
-
new Map([["nested", nestedModifier]]),
|
|
1289
|
-
);
|
|
1290
|
-
|
|
1291
|
-
expect(result.children).toHaveLength(3);
|
|
1292
|
-
expect(result.children[0]).toMatchObject({
|
|
1293
|
-
type: "selector",
|
|
1294
|
-
query: ".card-header",
|
|
1295
|
-
declarations: { fontWeight: "bold" },
|
|
1296
|
-
});
|
|
1297
|
-
expect(result.children[1]).toMatchObject({
|
|
1298
|
-
type: "selector",
|
|
1299
|
-
query: ".card-body",
|
|
1300
|
-
declarations: { padding: "0.5rem" },
|
|
1301
|
-
});
|
|
1302
|
-
expect(result.children[2]).toMatchObject({
|
|
1303
|
-
type: "selector",
|
|
1304
|
-
query: ".card-footer",
|
|
1305
|
-
declarations: { borderTop: "1px solid #ccc" },
|
|
1306
|
-
});
|
|
1307
|
-
});
|
|
1308
|
-
|
|
1309
|
-
test("should handle modifier with same key appearing in combination multiple times", () => {
|
|
1310
|
-
const baseUtility: Utility = {
|
|
1311
|
-
type: "utility",
|
|
1312
|
-
name: "state",
|
|
1313
|
-
value: "active",
|
|
1314
|
-
declarations: { opacity: "1" },
|
|
1315
|
-
variables: [],
|
|
1316
|
-
children: [],
|
|
1317
|
-
modifiers: [],
|
|
1318
|
-
};
|
|
1319
|
-
|
|
1320
|
-
const stateModifier: ModifierFactory = {
|
|
1321
|
-
type: "modifier",
|
|
1322
|
-
key: ["hover", "focus"],
|
|
1323
|
-
factory: ({ variable }) => {
|
|
1324
|
-
variable("state-opacity", "0.8");
|
|
1325
|
-
return {};
|
|
1326
|
-
},
|
|
1327
|
-
};
|
|
1328
|
-
|
|
1329
|
-
// Duplicate keys in combination
|
|
1330
|
-
const result = applyModifiers(
|
|
1331
|
-
baseUtility,
|
|
1332
|
-
root,
|
|
1333
|
-
new Map([["hover", stateModifier]]),
|
|
1334
|
-
);
|
|
1335
|
-
|
|
1336
|
-
expect(result.modifiers).toEqual(["hover"]);
|
|
1337
|
-
expect(result.variables).toHaveLength(1);
|
|
1338
|
-
});
|
|
1339
|
-
|
|
1340
|
-
test("should handle utility with pre-existing variables and children", () => {
|
|
1341
|
-
const existingVar = {
|
|
1342
|
-
type: "variable" as const,
|
|
1343
|
-
name: "existing-var",
|
|
1344
|
-
value: "existing-value",
|
|
1345
|
-
};
|
|
1346
|
-
|
|
1347
|
-
const existingChild = {
|
|
1348
|
-
type: "selector" as const,
|
|
1349
|
-
query: ".existing",
|
|
1350
|
-
declarations: { color: "red" },
|
|
1351
|
-
variables: [],
|
|
1352
|
-
children: [],
|
|
1353
|
-
};
|
|
1354
|
-
|
|
1355
|
-
const baseUtility: Utility = {
|
|
1356
|
-
type: "utility",
|
|
1357
|
-
name: "complex",
|
|
1358
|
-
value: "base",
|
|
1359
|
-
declarations: { margin: "0" },
|
|
1360
|
-
variables: [existingVar],
|
|
1361
|
-
children: [existingChild],
|
|
1362
|
-
modifiers: [],
|
|
1363
|
-
};
|
|
1364
|
-
|
|
1365
|
-
const additionalModifier: ModifierFactory = {
|
|
1366
|
-
type: "modifier",
|
|
1367
|
-
key: ["additional"],
|
|
1368
|
-
factory: ({ variable, selector }) => {
|
|
1369
|
-
variable("new-var", "new-value");
|
|
1370
|
-
selector(".new", { color: "blue" });
|
|
1371
|
-
return {};
|
|
1372
|
-
},
|
|
1373
|
-
};
|
|
1374
|
-
|
|
1375
|
-
const result = applyModifiers(
|
|
1376
|
-
baseUtility,
|
|
1377
|
-
root,
|
|
1378
|
-
new Map([["additional", additionalModifier]]),
|
|
1379
|
-
);
|
|
1380
|
-
|
|
1381
|
-
expect(result.variables).toHaveLength(2);
|
|
1382
|
-
expect(result.variables[0]).toEqual(existingVar);
|
|
1383
|
-
expect(result.variables[1]).toEqual({
|
|
1384
|
-
type: "variable",
|
|
1385
|
-
name: "new-var",
|
|
1386
|
-
value: "new-value",
|
|
1387
|
-
});
|
|
1388
|
-
|
|
1389
|
-
expect(result.children).toHaveLength(2);
|
|
1390
|
-
expect(result.children[0]).toEqual(existingChild);
|
|
1391
|
-
expect(result.children[1]).toMatchObject({
|
|
1392
|
-
type: "selector",
|
|
1393
|
-
query: ".new",
|
|
1394
|
-
declarations: { color: "blue" },
|
|
1395
|
-
});
|
|
1396
|
-
});
|
|
1397
|
-
|
|
1398
|
-
test("should handle deeply nested declarations from modifiers", () => {
|
|
1399
|
-
const baseUtility: Utility = {
|
|
1400
|
-
type: "utility",
|
|
1401
|
-
name: "grid",
|
|
1402
|
-
value: "layout",
|
|
1403
|
-
declarations: { display: "grid" },
|
|
1404
|
-
variables: [],
|
|
1405
|
-
children: [],
|
|
1406
|
-
modifiers: [],
|
|
1407
|
-
};
|
|
1408
|
-
|
|
1409
|
-
const deepModifier: ModifierFactory = {
|
|
1410
|
-
type: "modifier",
|
|
1411
|
-
key: ["deep"],
|
|
1412
|
-
factory: ({ selector }) => {
|
|
1413
|
-
selector("& > .row", { gridRow: "span 1" });
|
|
1414
|
-
selector("& > .row > .col", { gridColumn: "span 1" });
|
|
1415
|
-
return {
|
|
1416
|
-
gridTemplateColumns: "repeat(12, 1fr)",
|
|
1417
|
-
gridGap: "1rem",
|
|
1418
|
-
};
|
|
1419
|
-
},
|
|
1420
|
-
};
|
|
1421
|
-
|
|
1422
|
-
const result = applyModifiers(
|
|
1423
|
-
baseUtility,
|
|
1424
|
-
root,
|
|
1425
|
-
new Map([["deep", deepModifier]]),
|
|
1426
|
-
);
|
|
1427
|
-
|
|
1428
|
-
expect(result.declarations).toEqual({ display: "grid" });
|
|
1429
|
-
expect(result.children).toHaveLength(2);
|
|
1430
|
-
expect(result.modifiers).toEqual(["deep"]);
|
|
1431
|
-
});
|
|
1432
|
-
|
|
1433
|
-
test("should handle modifier factory that throws an error", () => {
|
|
1434
|
-
const baseUtility: Utility = {
|
|
1435
|
-
type: "utility",
|
|
1436
|
-
name: "error",
|
|
1437
|
-
value: "test",
|
|
1438
|
-
declarations: { display: "block" },
|
|
1439
|
-
variables: [],
|
|
1440
|
-
children: [],
|
|
1441
|
-
modifiers: [],
|
|
1442
|
-
};
|
|
1443
|
-
|
|
1444
|
-
const errorModifier: ModifierFactory = {
|
|
1445
|
-
type: "modifier",
|
|
1446
|
-
key: ["error"],
|
|
1447
|
-
factory: () => {
|
|
1448
|
-
throw new Error("Test error");
|
|
1449
|
-
},
|
|
1450
|
-
};
|
|
1451
|
-
|
|
1452
|
-
expect(() => {
|
|
1453
|
-
applyModifiers(baseUtility, root, new Map([["error", errorModifier]]));
|
|
1454
|
-
}).toThrow("Test error");
|
|
1455
|
-
});
|
|
1456
|
-
});
|