clava 0.4.2 → 0.6.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/CHANGELOG.md +85 -2
- package/README.md +78 -31
- package/dist/index.d.ts +193 -3
- package/dist/index.js +187 -115
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/index.ts +451 -214
- package/src/refine-warning.ts +2 -2
- package/src/types.ts +129 -2
- package/tests/component-api.test.ts +81 -55
- package/tests/extend.test.ts +44 -10
- package/tests/language-service.test.ts +12 -1
- package/tests/prototype-pollution.test.ts +3 -4
- package/tests/refine.test.ts +267 -369
- package/tests/variants-inference.test.ts +149 -0
package/src/refine-warning.ts
CHANGED
|
@@ -145,8 +145,8 @@ export function warnRefineLimit({
|
|
|
145
145
|
runState.warned = true;
|
|
146
146
|
let message =
|
|
147
147
|
"Clava: Maximum refine iterations exceeded. This can happen when a " +
|
|
148
|
-
"
|
|
149
|
-
"
|
|
148
|
+
"computed default variant or refine callback changes one of the " +
|
|
149
|
+
"variants on every run.";
|
|
150
150
|
if (unstableChanges && unstableChanges.size > 0) {
|
|
151
151
|
message += `\nVariant(s) that did not stabilize: ${Array.from(unstableChanges.keys()).join(", ")}.`;
|
|
152
152
|
message += `\nLatest variant changes before warning: ${formatVariantChanges(unstableChanges)}.`;
|
package/src/types.ts
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
import type * as CSS from "csstype";
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* A class value accepted by Clava. It supports strings, numbers, booleans,
|
|
5
|
+
* nullish values, and nested arrays.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```ts
|
|
9
|
+
* import type { ClassValue } from "clava";
|
|
10
|
+
*
|
|
11
|
+
* const className: ClassValue = [
|
|
12
|
+
* "button",
|
|
13
|
+
* false && "button-hidden",
|
|
14
|
+
* ["button-primary"],
|
|
15
|
+
* ];
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
3
18
|
export type ClassValue =
|
|
4
19
|
| string
|
|
5
20
|
| number
|
|
@@ -17,21 +32,68 @@ export type CSSProperties = CSS.Properties;
|
|
|
17
32
|
|
|
18
33
|
export type StyleProperty = JSXCSSProperties | HTMLCSSProperties | string;
|
|
19
34
|
|
|
35
|
+
/**
|
|
36
|
+
* The prop object returned by a component's `.jsx()` mode.
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```ts
|
|
40
|
+
* import { type JSXProps, cv } from "clava";
|
|
41
|
+
*
|
|
42
|
+
* const button = cv({ class: "button" });
|
|
43
|
+
* const props: JSXProps = button.jsx();
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
20
46
|
export interface JSXProps {
|
|
21
47
|
className: string;
|
|
22
48
|
style: JSXCSSProperties;
|
|
23
49
|
}
|
|
24
50
|
|
|
51
|
+
/**
|
|
52
|
+
* The prop object returned by a component's `.html()` mode. The `style` value
|
|
53
|
+
* is serialized as an HTML style string.
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```ts
|
|
57
|
+
* import { type HTMLProps, cv } from "clava";
|
|
58
|
+
*
|
|
59
|
+
* const button = cv({ style: { color: "red" } });
|
|
60
|
+
* const props: HTMLProps = button.html();
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
25
63
|
export interface HTMLProps {
|
|
26
64
|
class: string;
|
|
27
65
|
style: string;
|
|
28
66
|
}
|
|
29
67
|
|
|
68
|
+
/**
|
|
69
|
+
* The prop object returned by a component's `.htmlObj()` mode. The `style`
|
|
70
|
+
* value uses hyphenated CSS property names.
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* ```ts
|
|
74
|
+
* import { type HTMLObjProps, cv } from "clava";
|
|
75
|
+
*
|
|
76
|
+
* const button = cv({ style: { fontSize: "16px" } });
|
|
77
|
+
* const props: HTMLObjProps = button.htmlObj();
|
|
78
|
+
* ```
|
|
79
|
+
*/
|
|
30
80
|
export interface HTMLObjProps {
|
|
31
81
|
class: string;
|
|
32
82
|
style: HTMLCSSProperties;
|
|
33
83
|
}
|
|
34
84
|
|
|
85
|
+
/**
|
|
86
|
+
* The default prop object returned by a Clava component. It uses `class`
|
|
87
|
+
* rather than `className` and keeps styles as a normalized object.
|
|
88
|
+
*
|
|
89
|
+
* @example
|
|
90
|
+
* ```ts
|
|
91
|
+
* import { type StyleClassProps, cv } from "clava";
|
|
92
|
+
*
|
|
93
|
+
* const button = cv({ class: "button" });
|
|
94
|
+
* const props: StyleClassProps = button();
|
|
95
|
+
* ```
|
|
96
|
+
*/
|
|
35
97
|
export interface StyleClassProps {
|
|
36
98
|
class: string;
|
|
37
99
|
style: StyleValue;
|
|
@@ -177,6 +239,25 @@ export interface ModalComponent<V, R extends ComponentResult> {
|
|
|
177
239
|
propKeys: (keyof V | ComponentPropKey<R>)[];
|
|
178
240
|
}
|
|
179
241
|
|
|
242
|
+
/**
|
|
243
|
+
* A callable Clava component returned by `cv()`. It includes the default
|
|
244
|
+
* output mode plus `.jsx()`, `.html()`, and `.htmlObj()` mode helpers.
|
|
245
|
+
*
|
|
246
|
+
* @example
|
|
247
|
+
* ```ts
|
|
248
|
+
* import { type CVComponent, cv } from "clava";
|
|
249
|
+
*
|
|
250
|
+
* const button: CVComponent<{
|
|
251
|
+
* size: { sm: string; lg: string };
|
|
252
|
+
* }> = cv({
|
|
253
|
+
* variants: {
|
|
254
|
+
* size: { sm: "button-sm", lg: "button-lg" },
|
|
255
|
+
* },
|
|
256
|
+
* });
|
|
257
|
+
*
|
|
258
|
+
* button.jsx({ size: "lg" });
|
|
259
|
+
* ```
|
|
260
|
+
*/
|
|
180
261
|
export interface CVComponent<
|
|
181
262
|
V extends Variants = {},
|
|
182
263
|
E extends AnyComponent[] = [],
|
|
@@ -254,13 +335,60 @@ type ExtractVariantValue<T> = T extends null
|
|
|
254
335
|
: never;
|
|
255
336
|
|
|
256
337
|
export type VariantValues<V> = {
|
|
257
|
-
[K in keyof V]?: ExtractVariantValue<V[K]
|
|
338
|
+
[K in keyof V]?: ExtractVariantValue<V[K]> | undefined;
|
|
258
339
|
};
|
|
259
340
|
|
|
341
|
+
type ComputedDefaultVariant<V, K extends keyof V> = (
|
|
342
|
+
defaultValue: ExtractVariantValue<V[K]> | undefined,
|
|
343
|
+
variants: Readonly<VariantValues<V>>,
|
|
344
|
+
) => ExtractVariantValue<V[K]> | undefined;
|
|
345
|
+
|
|
346
|
+
type NonFunctionVariantValue<T> = Exclude<T, (...args: any[]) => any>;
|
|
347
|
+
|
|
348
|
+
type DefaultVariantValue<V, K extends keyof V> = [
|
|
349
|
+
NonFunctionVariantValue<ExtractVariantValue<V[K]>>,
|
|
350
|
+
] extends [never]
|
|
351
|
+
? ComputedDefaultVariant<V, K>
|
|
352
|
+
:
|
|
353
|
+
| NonFunctionVariantValue<ExtractVariantValue<V[K]>>
|
|
354
|
+
| ComputedDefaultVariant<V, K>;
|
|
355
|
+
|
|
356
|
+
export type DefaultVariants<V> = {
|
|
357
|
+
[K in keyof V]?: DefaultVariantValue<V, K> | undefined;
|
|
358
|
+
};
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* A normalized style object accepted by Clava config, variant, and refine
|
|
362
|
+
* style entries. CSS custom properties are supported with string values.
|
|
363
|
+
*
|
|
364
|
+
* @example
|
|
365
|
+
* ```ts
|
|
366
|
+
* import type { StyleValue } from "clava";
|
|
367
|
+
*
|
|
368
|
+
* const style: StyleValue = {
|
|
369
|
+
* color: "red",
|
|
370
|
+
* "--button-accent": "oklch(62% 0.2 250)",
|
|
371
|
+
* };
|
|
372
|
+
* ```
|
|
373
|
+
*/
|
|
260
374
|
export type StyleValue = CSS.Properties & {
|
|
261
375
|
[key: `--${string}`]: string;
|
|
262
376
|
};
|
|
263
377
|
|
|
378
|
+
/**
|
|
379
|
+
* A value that contributes both class and style output from a base config,
|
|
380
|
+
* variant value, function variant, or refine callback.
|
|
381
|
+
*
|
|
382
|
+
* @example
|
|
383
|
+
* ```ts
|
|
384
|
+
* import type { StyleClassValue } from "clava";
|
|
385
|
+
*
|
|
386
|
+
* const tone: StyleClassValue = {
|
|
387
|
+
* class: "button-primary",
|
|
388
|
+
* style: { color: "white" },
|
|
389
|
+
* };
|
|
390
|
+
* ```
|
|
391
|
+
*/
|
|
264
392
|
export interface StyleClassValue {
|
|
265
393
|
style?: StyleValue;
|
|
266
394
|
class?: ClassValue;
|
|
@@ -269,7 +397,6 @@ export interface StyleClassValue {
|
|
|
269
397
|
export interface RefineContext<V> {
|
|
270
398
|
variants: VariantValues<V>;
|
|
271
399
|
setVariants: (variants: VariantValues<V>) => void;
|
|
272
|
-
setDefaultVariants: (variants: VariantValues<V>) => void;
|
|
273
400
|
addClass: (className: ClassValue) => void;
|
|
274
401
|
addStyle: (style: StyleValue) => void;
|
|
275
402
|
}
|
|
@@ -59,6 +59,33 @@ for (const config of Object.values(CONFIGS)) {
|
|
|
59
59
|
expect(variants).toEqual({ size: "sm" });
|
|
60
60
|
});
|
|
61
61
|
|
|
62
|
+
test("getVariants omits undefined defaultVariants", () => {
|
|
63
|
+
const component = getModeComponent(
|
|
64
|
+
mode,
|
|
65
|
+
cv({
|
|
66
|
+
variants: { size: { sm: "sm", lg: "lg" } },
|
|
67
|
+
defaultVariants: { size: undefined },
|
|
68
|
+
}),
|
|
69
|
+
);
|
|
70
|
+
const variants = component.getVariants();
|
|
71
|
+
expect(variants).toStrictEqual({});
|
|
72
|
+
expect(Object.hasOwn(variants, "size")).toBe(false);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test("getVariants undefined defaultVariants clear inherited defaultVariants", () => {
|
|
76
|
+
const base = cv({
|
|
77
|
+
variants: { size: { sm: "sm", lg: "lg" } },
|
|
78
|
+
defaultVariants: { size: "sm" },
|
|
79
|
+
});
|
|
80
|
+
const component = getModeComponent(
|
|
81
|
+
mode,
|
|
82
|
+
cv({ extend: [base], defaultVariants: { size: undefined } }),
|
|
83
|
+
);
|
|
84
|
+
const variants = component.getVariants();
|
|
85
|
+
expect(variants).toStrictEqual({});
|
|
86
|
+
expect(Object.hasOwn(variants, "size")).toBe(false);
|
|
87
|
+
});
|
|
88
|
+
|
|
62
89
|
test("getVariants returns variants set by refine setVariants", () => {
|
|
63
90
|
const component = getModeComponent(
|
|
64
91
|
mode,
|
|
@@ -100,7 +127,7 @@ for (const config of Object.values(CONFIGS)) {
|
|
|
100
127
|
expect(variants).toEqual({ size: "sm", color: "red" });
|
|
101
128
|
});
|
|
102
129
|
|
|
103
|
-
test("getVariants re-runs when
|
|
130
|
+
test("getVariants re-runs when computed defaultVariants change variants", () => {
|
|
104
131
|
const component = getModeComponent(
|
|
105
132
|
mode,
|
|
106
133
|
cv({
|
|
@@ -108,11 +135,10 @@ for (const config of Object.values(CONFIGS)) {
|
|
|
108
135
|
size: { sm: "sm", lg: "lg" },
|
|
109
136
|
color: { red: "red", blue: "blue" },
|
|
110
137
|
},
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
}
|
|
138
|
+
defaultVariants: {
|
|
139
|
+
color: () => "red" as const,
|
|
140
|
+
size: (defaultValue, variants) =>
|
|
141
|
+
variants.color === "red" ? "lg" : defaultValue,
|
|
116
142
|
},
|
|
117
143
|
}),
|
|
118
144
|
);
|
|
@@ -120,7 +146,7 @@ for (const config of Object.values(CONFIGS)) {
|
|
|
120
146
|
expect(variants).toEqual({ size: "lg", color: "red" });
|
|
121
147
|
});
|
|
122
148
|
|
|
123
|
-
test("getVariants returns
|
|
149
|
+
test("getVariants returns computed defaultVariants", () => {
|
|
124
150
|
const component = getModeComponent(
|
|
125
151
|
mode,
|
|
126
152
|
cv({
|
|
@@ -128,10 +154,9 @@ for (const config of Object.values(CONFIGS)) {
|
|
|
128
154
|
size: { sm: "sm", lg: "lg" },
|
|
129
155
|
color: { red: "red", blue: "blue" },
|
|
130
156
|
},
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
}
|
|
157
|
+
defaultVariants: {
|
|
158
|
+
color: (defaultValue, variants) =>
|
|
159
|
+
variants.size === "lg" ? "blue" : defaultValue,
|
|
135
160
|
},
|
|
136
161
|
}),
|
|
137
162
|
);
|
|
@@ -139,7 +164,7 @@ for (const config of Object.values(CONFIGS)) {
|
|
|
139
164
|
expect(variants).toEqual({ size: "lg", color: "blue" });
|
|
140
165
|
});
|
|
141
166
|
|
|
142
|
-
test("getVariants
|
|
167
|
+
test("getVariants computed defaultVariants do not override props", () => {
|
|
143
168
|
const component = getModeComponent(
|
|
144
169
|
mode,
|
|
145
170
|
cv({
|
|
@@ -147,8 +172,8 @@ for (const config of Object.values(CONFIGS)) {
|
|
|
147
172
|
size: { sm: "sm", lg: "lg" },
|
|
148
173
|
color: { red: "red", blue: "blue" },
|
|
149
174
|
},
|
|
150
|
-
|
|
151
|
-
|
|
175
|
+
defaultVariants: {
|
|
176
|
+
color: () => "blue" as const,
|
|
152
177
|
},
|
|
153
178
|
}),
|
|
154
179
|
);
|
|
@@ -173,11 +198,11 @@ for (const config of Object.values(CONFIGS)) {
|
|
|
173
198
|
expect(variants).toEqual({ color: "blue" });
|
|
174
199
|
});
|
|
175
200
|
|
|
176
|
-
test("getVariants picks up
|
|
201
|
+
test("getVariants picks up computed defaultVariants from extended component", () => {
|
|
177
202
|
const base = cv({
|
|
178
203
|
variants: { size: { sm: "sm", lg: "lg" } },
|
|
179
|
-
|
|
180
|
-
|
|
204
|
+
defaultVariants: {
|
|
205
|
+
size: () => "lg" as const,
|
|
181
206
|
},
|
|
182
207
|
});
|
|
183
208
|
const component = getModeComponent(
|
|
@@ -192,11 +217,11 @@ for (const config of Object.values(CONFIGS)) {
|
|
|
192
217
|
expect(variants).toEqual({ size: "lg", color: "red" });
|
|
193
218
|
});
|
|
194
219
|
|
|
195
|
-
test("getVariants picks up
|
|
220
|
+
test("getVariants picks up computed defaultVariants from grandparent component", () => {
|
|
196
221
|
const grandparent = cv({
|
|
197
222
|
variants: { size: { sm: "sm", lg: "lg" } },
|
|
198
|
-
|
|
199
|
-
|
|
223
|
+
defaultVariants: {
|
|
224
|
+
size: () => "lg" as const,
|
|
200
225
|
},
|
|
201
226
|
});
|
|
202
227
|
const parent = cv({ extend: [grandparent] });
|
|
@@ -238,21 +263,21 @@ for (const config of Object.values(CONFIGS)) {
|
|
|
238
263
|
expect(variants).toEqual({ size: "lg", active: true, color: "red" });
|
|
239
264
|
});
|
|
240
265
|
|
|
241
|
-
test("getVariants preserves
|
|
266
|
+
test("getVariants preserves computed defaultVariants after a setVariants re-run", () => {
|
|
242
267
|
const base = cv({
|
|
243
268
|
variants: {
|
|
244
269
|
size: { sm: "sm", lg: "lg" },
|
|
245
270
|
active: "",
|
|
246
271
|
mode: { on: "on" },
|
|
247
272
|
},
|
|
248
|
-
defaultVariants: {
|
|
249
|
-
|
|
273
|
+
defaultVariants: {
|
|
274
|
+
size: (defaultValue, variants) =>
|
|
275
|
+
variants.mode === "on" ? "lg" : defaultValue,
|
|
276
|
+
},
|
|
277
|
+
refine: ({ variants, setVariants }) => {
|
|
250
278
|
if (variants.active) {
|
|
251
279
|
setVariants({ mode: "on" });
|
|
252
280
|
}
|
|
253
|
-
if (variants.mode === "on") {
|
|
254
|
-
setDefaultVariants({ size: "lg" });
|
|
255
|
-
}
|
|
256
281
|
},
|
|
257
282
|
});
|
|
258
283
|
const component = getModeComponent(mode, cv({ extend: [base] }));
|
|
@@ -276,11 +301,11 @@ for (const config of Object.values(CONFIGS)) {
|
|
|
276
301
|
expect(variants).toEqual({ size: "sm" });
|
|
277
302
|
});
|
|
278
303
|
|
|
279
|
-
test("getVariants child setVariants keeps overriding base
|
|
304
|
+
test("getVariants child setVariants keeps overriding base computed defaultVariants across re-runs", () => {
|
|
280
305
|
const base = cv({
|
|
281
306
|
variants: { color: { red: "red", blue: "blue" } },
|
|
282
|
-
|
|
283
|
-
|
|
307
|
+
defaultVariants: {
|
|
308
|
+
color: () => "blue" as const,
|
|
284
309
|
},
|
|
285
310
|
});
|
|
286
311
|
const component = getModeComponent(
|
|
@@ -300,11 +325,11 @@ for (const config of Object.values(CONFIGS)) {
|
|
|
300
325
|
expect(variants).toEqual({ color: "red", size: "sm" });
|
|
301
326
|
});
|
|
302
327
|
|
|
303
|
-
test("getVariants setVariants sticks across re-runs", () => {
|
|
328
|
+
test("getVariants setVariants sticks across computed default re-runs", () => {
|
|
304
329
|
const base = cv({
|
|
305
330
|
variants: { color: { red: "red", blue: "blue" } },
|
|
306
|
-
|
|
307
|
-
|
|
331
|
+
defaultVariants: {
|
|
332
|
+
color: () => "blue" as const,
|
|
308
333
|
},
|
|
309
334
|
});
|
|
310
335
|
const component = getModeComponent(
|
|
@@ -323,20 +348,21 @@ for (const config of Object.values(CONFIGS)) {
|
|
|
323
348
|
expect(variants).toEqual({ color: "red", done: true });
|
|
324
349
|
});
|
|
325
350
|
|
|
326
|
-
test("getVariants base
|
|
351
|
+
test("getVariants base computed defaultVariants can override child static defaults after a re-run", () => {
|
|
327
352
|
const base = cv({
|
|
328
353
|
variants: {
|
|
329
354
|
size: { sm: "sm", lg: "lg" },
|
|
330
355
|
active: "",
|
|
331
356
|
mode: { on: "on" },
|
|
332
357
|
},
|
|
333
|
-
|
|
358
|
+
defaultVariants: {
|
|
359
|
+
size: (defaultValue, variants) =>
|
|
360
|
+
variants.mode === "on" ? "lg" : defaultValue,
|
|
361
|
+
},
|
|
362
|
+
refine: ({ variants, setVariants }) => {
|
|
334
363
|
if (variants.active) {
|
|
335
364
|
setVariants({ mode: "on" });
|
|
336
365
|
}
|
|
337
|
-
if (variants.mode === "on") {
|
|
338
|
-
setDefaultVariants({ size: "lg" });
|
|
339
|
-
}
|
|
340
366
|
},
|
|
341
367
|
});
|
|
342
368
|
const component = getModeComponent(
|
|
@@ -347,7 +373,7 @@ for (const config of Object.values(CONFIGS)) {
|
|
|
347
373
|
expect(variants).toEqual({ size: "lg", active: true, mode: "on" });
|
|
348
374
|
});
|
|
349
375
|
|
|
350
|
-
test("getVariants setVariants from earlier extends overrides
|
|
376
|
+
test("getVariants setVariants from earlier extends overrides computed defaultVariants from later extends", () => {
|
|
351
377
|
const first = cv({
|
|
352
378
|
variants: { color: { red: "first-red", blue: "first-blue" } },
|
|
353
379
|
refine: ({ setVariants }) => {
|
|
@@ -356,8 +382,8 @@ for (const config of Object.values(CONFIGS)) {
|
|
|
356
382
|
});
|
|
357
383
|
const second = cv({
|
|
358
384
|
variants: { color: { red: "second-red", blue: "second-blue" } },
|
|
359
|
-
|
|
360
|
-
|
|
385
|
+
defaultVariants: {
|
|
386
|
+
color: () => "blue" as const,
|
|
361
387
|
},
|
|
362
388
|
});
|
|
363
389
|
const component = getModeComponent(mode, cv({ extend: [first, second] }));
|
|
@@ -365,17 +391,17 @@ for (const config of Object.values(CONFIGS)) {
|
|
|
365
391
|
expect(variants).toEqual({ color: "red" });
|
|
366
392
|
});
|
|
367
393
|
|
|
368
|
-
test("getVariants
|
|
394
|
+
test("getVariants computed defaultVariants from later extends override earlier extends", () => {
|
|
369
395
|
const first = cv({
|
|
370
396
|
variants: { color: { red: "first-red", blue: "first-blue" } },
|
|
371
|
-
|
|
372
|
-
|
|
397
|
+
defaultVariants: {
|
|
398
|
+
color: () => "red" as const,
|
|
373
399
|
},
|
|
374
400
|
});
|
|
375
401
|
const second = cv({
|
|
376
402
|
variants: { color: { red: "second-red", blue: "second-blue" } },
|
|
377
|
-
|
|
378
|
-
|
|
403
|
+
defaultVariants: {
|
|
404
|
+
color: () => "blue" as const,
|
|
379
405
|
},
|
|
380
406
|
});
|
|
381
407
|
const component = getModeComponent(mode, cv({ extend: [first, second] }));
|
|
@@ -383,7 +409,7 @@ for (const config of Object.values(CONFIGS)) {
|
|
|
383
409
|
expect(variants).toEqual({ color: "blue" });
|
|
384
410
|
});
|
|
385
411
|
|
|
386
|
-
test("getVariants
|
|
412
|
+
test("getVariants computed defaultVariants do not override stable setVariants on later passes", () => {
|
|
387
413
|
const base = cv({
|
|
388
414
|
variants: { color: { red: "base-red", blue: "base-blue" } },
|
|
389
415
|
refine: ({ setVariants }) => {
|
|
@@ -395,10 +421,9 @@ for (const config of Object.values(CONFIGS)) {
|
|
|
395
421
|
cv({
|
|
396
422
|
extend: [base],
|
|
397
423
|
variants: { color: { red: "child-red", blue: "child-blue" } },
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
}
|
|
424
|
+
defaultVariants: {
|
|
425
|
+
color: (defaultValue, variants) =>
|
|
426
|
+
variants.color === "red" ? "blue" : defaultValue,
|
|
402
427
|
},
|
|
403
428
|
}),
|
|
404
429
|
);
|
|
@@ -406,7 +431,7 @@ for (const config of Object.values(CONFIGS)) {
|
|
|
406
431
|
expect(variants).toEqual({ color: "red" });
|
|
407
432
|
});
|
|
408
433
|
|
|
409
|
-
test("getVariants
|
|
434
|
+
test("getVariants computed defaultVariants do not override setVariants from a previous pass", () => {
|
|
410
435
|
const component = getModeComponent(
|
|
411
436
|
mode,
|
|
412
437
|
cv({
|
|
@@ -414,13 +439,14 @@ for (const config of Object.values(CONFIGS)) {
|
|
|
414
439
|
color: { red: "red", blue: "blue" },
|
|
415
440
|
done: "",
|
|
416
441
|
},
|
|
417
|
-
|
|
442
|
+
defaultVariants: {
|
|
443
|
+
color: (defaultValue, variants) =>
|
|
444
|
+
variants.done ? "blue" : defaultValue,
|
|
445
|
+
},
|
|
446
|
+
refine: ({ variants, setVariants }) => {
|
|
418
447
|
if (!variants.done) {
|
|
419
448
|
setVariants({ color: "red", done: true });
|
|
420
449
|
}
|
|
421
|
-
if (variants.done) {
|
|
422
|
-
setDefaultVariants({ color: "blue" });
|
|
423
|
-
}
|
|
424
450
|
},
|
|
425
451
|
}),
|
|
426
452
|
);
|
package/tests/extend.test.ts
CHANGED
|
@@ -144,7 +144,7 @@ for (const config of Object.values(CONFIGS)) {
|
|
|
144
144
|
});
|
|
145
145
|
});
|
|
146
146
|
|
|
147
|
-
test("extend disabled variant value with
|
|
147
|
+
test("extend disabled variant value with computed defaultVariants", () => {
|
|
148
148
|
const base = cv({
|
|
149
149
|
variants: {
|
|
150
150
|
size: {
|
|
@@ -158,8 +158,8 @@ for (const config of Object.values(CONFIGS)) {
|
|
|
158
158
|
cv({
|
|
159
159
|
extend: [base],
|
|
160
160
|
variants: { size: { sm: null } },
|
|
161
|
-
|
|
162
|
-
|
|
161
|
+
defaultVariants: {
|
|
162
|
+
size: () => "lg" as const,
|
|
163
163
|
},
|
|
164
164
|
}),
|
|
165
165
|
);
|
|
@@ -173,19 +173,53 @@ for (const config of Object.values(CONFIGS)) {
|
|
|
173
173
|
cv({
|
|
174
174
|
extend: [base],
|
|
175
175
|
variants: { size: { sm: null } },
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
"sm",
|
|
182
|
-
});
|
|
176
|
+
defaultVariants: {
|
|
177
|
+
// @ts-expect-error disabled variant value cannot be set
|
|
178
|
+
size: () =>
|
|
179
|
+
// no error
|
|
180
|
+
"sm",
|
|
183
181
|
},
|
|
184
182
|
}),
|
|
185
183
|
);
|
|
186
184
|
expect(getStyleClass(invalidComponent())).toEqual({ class: "" });
|
|
187
185
|
});
|
|
188
186
|
|
|
187
|
+
test("extend filters disabled values from inherited computed defaultVariants", () => {
|
|
188
|
+
const base = cv({
|
|
189
|
+
variants: {
|
|
190
|
+
size: {
|
|
191
|
+
sm: { class: "base-sm", style: { fontSize: "12px" } },
|
|
192
|
+
lg: { class: "base-lg", style: { fontSize: "16px" } },
|
|
193
|
+
},
|
|
194
|
+
},
|
|
195
|
+
defaultVariants: {
|
|
196
|
+
size: () => "sm" as const,
|
|
197
|
+
},
|
|
198
|
+
});
|
|
199
|
+
const component = getModeComponent(
|
|
200
|
+
mode,
|
|
201
|
+
cv({
|
|
202
|
+
extend: [base],
|
|
203
|
+
variants: {
|
|
204
|
+
size: { sm: null },
|
|
205
|
+
color: { red: "red", blue: "blue" },
|
|
206
|
+
},
|
|
207
|
+
defaultVariants: {
|
|
208
|
+
size: "lg",
|
|
209
|
+
color: (_, variants) => (variants.size === "lg" ? "blue" : "red"),
|
|
210
|
+
},
|
|
211
|
+
}),
|
|
212
|
+
);
|
|
213
|
+
expect(getStyleClass(component())).toEqual({
|
|
214
|
+
class: cls("base-lg blue"),
|
|
215
|
+
fontSize: "16px",
|
|
216
|
+
});
|
|
217
|
+
expect(component.getVariants()).toEqual({
|
|
218
|
+
size: "lg",
|
|
219
|
+
color: "blue",
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
|
|
189
223
|
test("extend disabled variant value with refine setVariants", () => {
|
|
190
224
|
const base = cv({
|
|
191
225
|
variants: {
|
|
@@ -80,7 +80,10 @@ function createLanguageServiceHost(
|
|
|
80
80
|
function createLanguageServiceFixture(
|
|
81
81
|
consumerSource: string,
|
|
82
82
|
): LanguageServiceFixture {
|
|
83
|
-
const
|
|
83
|
+
const tempRoot = join(workspaceDir, ".tmp");
|
|
84
|
+
mkdirSync(tempRoot, { recursive: true });
|
|
85
|
+
|
|
86
|
+
const tempDir = mkdtempSync(join(tempRoot, "language-service-"));
|
|
84
87
|
tempDirs.push(tempDir);
|
|
85
88
|
|
|
86
89
|
const fixtureSourceDir = join(tempDir, "src");
|
|
@@ -134,6 +137,14 @@ afterEach(() => {
|
|
|
134
137
|
});
|
|
135
138
|
|
|
136
139
|
describe("TypeScript language service", () => {
|
|
140
|
+
test("creates fixtures inside the workspace .tmp directory", () => {
|
|
141
|
+
const fixture = createLanguageServiceFixture(getVariantFixtureSource());
|
|
142
|
+
const fixtureDir = dirname(fixture.consumerFile);
|
|
143
|
+
const fixturePrefix = join(workspaceDir, ".tmp", "language-service-");
|
|
144
|
+
|
|
145
|
+
expect(fixtureDir.startsWith(fixturePrefix)).toBe(true);
|
|
146
|
+
});
|
|
147
|
+
|
|
137
148
|
test("goes to the local variant definition from a variant prop usage", () => {
|
|
138
149
|
const fixture = createLanguageServiceFixture(getVariantFixtureSource());
|
|
139
150
|
const definitionStart = getFixturePosition(
|
|
@@ -27,11 +27,10 @@ test("render ignores keys inherited from Object.prototype", () => {
|
|
|
27
27
|
expect(component({}).class).toBe("sm");
|
|
28
28
|
});
|
|
29
29
|
|
|
30
|
-
test("extend's
|
|
30
|
+
test("extend's resolver ignores polluted prototype on parent's refine", () => {
|
|
31
31
|
// Base's refine branches on its own variants.size — if the polluted "size"
|
|
32
|
-
// key leaks into
|
|
33
|
-
//
|
|
34
|
-
// lg-specific class.
|
|
32
|
+
// key leaks into resolved variants, the refine callback would see size =
|
|
33
|
+
// "lg" instead of the staticDefault "sm" and emit the lg-specific class.
|
|
35
34
|
proto.size = "lg";
|
|
36
35
|
const base = cv({
|
|
37
36
|
variants: { size: { sm: "sm", lg: "lg" } },
|