clava 0.3.0 → 0.4.1
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 +86 -0
- package/README.md +38 -38
- package/dist/index.d.ts +19 -27
- package/dist/index.js +143 -86
- package/dist/index.js.map +1 -1
- package/package.json +5 -1
- package/src/index.ts +430 -215
- package/src/types.ts +38 -46
- package/src/utils.ts +31 -10
- package/tests/_utils.ts +7 -5
- package/tests/build.test.ts +81 -7
- package/tests/component-api.test.ts +28 -28
- package/tests/extend.test.ts +13 -8
- package/tests/{computed-variants.test.ts → function-variants.test.ts} +105 -25
- package/tests/prototype-pollution.test.ts +6 -6
- package/tests/{computed.test.ts → refine.test.ts} +292 -149
- package/tests/variants-inference.test.ts +252 -0
package/src/types.ts
CHANGED
|
@@ -76,11 +76,11 @@ type ComponentPropKey<R extends ComponentResult> =
|
|
|
76
76
|
|
|
77
77
|
// Key source types - what can be passed as additional parameters to splitProps
|
|
78
78
|
export type KeySourceArray = readonly string[];
|
|
79
|
-
export
|
|
79
|
+
export interface KeySourceComponent {
|
|
80
80
|
propKeys: readonly string[];
|
|
81
81
|
variantKeys: readonly string[];
|
|
82
82
|
getVariants: () => Record<string, unknown>;
|
|
83
|
-
}
|
|
83
|
+
}
|
|
84
84
|
export type KeySource = KeySourceArray | KeySourceComponent;
|
|
85
85
|
|
|
86
86
|
// Check if source is a component (has getVariants)
|
|
@@ -179,46 +179,46 @@ export interface ModalComponent<V, R extends ComponentResult> {
|
|
|
179
179
|
|
|
180
180
|
export interface CVComponent<
|
|
181
181
|
V extends Variants = {},
|
|
182
|
-
CV extends ComputedVariants = {},
|
|
183
182
|
E extends AnyComponent[] = [],
|
|
184
183
|
R extends ComponentResult = StyleClassProps,
|
|
185
|
-
> extends ModalComponent<MergeVariants<V,
|
|
186
|
-
jsx: ModalComponent<MergeVariants<V,
|
|
187
|
-
html: ModalComponent<MergeVariants<V,
|
|
188
|
-
htmlObj: ModalComponent<MergeVariants<V,
|
|
184
|
+
> extends ModalComponent<MergeVariants<V, E>, R> {
|
|
185
|
+
jsx: ModalComponent<MergeVariants<V, E>, JSXProps>;
|
|
186
|
+
html: ModalComponent<MergeVariants<V, E>, HTMLProps>;
|
|
187
|
+
htmlObj: ModalComponent<MergeVariants<V, E>, HTMLObjProps>;
|
|
189
188
|
}
|
|
190
189
|
|
|
191
190
|
export type AnyComponent =
|
|
192
|
-
| CVComponent<any, any, any
|
|
191
|
+
| CVComponent<any, any, any>
|
|
193
192
|
| ModalComponent<any, any>;
|
|
194
193
|
|
|
195
194
|
type MergeExtendedVariants<T> = T extends readonly [infer First, ...infer Rest]
|
|
196
195
|
? ExtractVariants<First> & MergeExtendedVariants<Rest>
|
|
197
196
|
: {};
|
|
198
197
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
: {};
|
|
205
|
-
|
|
198
|
+
// Returns a component's effective variants — merged with its own extends —
|
|
199
|
+
// so that a later intermediate component's static variant correctly hides a
|
|
200
|
+
// grandparent's function variant from further descendants. Using a raw
|
|
201
|
+
// intersection here would re-expose the grandparent function through the
|
|
202
|
+
// type chain even after the middle layer replaced it.
|
|
206
203
|
type ExtractVariants<T> =
|
|
207
|
-
T extends CVComponent<infer V,
|
|
208
|
-
? V
|
|
204
|
+
T extends CVComponent<infer V, infer E, any>
|
|
205
|
+
? MergeVariantMaps<V, MergeExtendedVariants<E>>
|
|
209
206
|
: {};
|
|
210
207
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
Child
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
208
|
+
// A function value in `variants` (a function variant) replaces any inherited
|
|
209
|
+
// variant for the same key. An object value merges value-by-value with an
|
|
210
|
+
// inherited object, but replaces an inherited function.
|
|
211
|
+
type MergeVariantDefinition<Child, Parent> = Child extends (
|
|
212
|
+
...args: any[]
|
|
213
|
+
) => any
|
|
214
|
+
? Child
|
|
215
|
+
: Parent extends (...args: any[]) => any
|
|
216
|
+
? Child
|
|
217
|
+
: Child extends Record<string, any>
|
|
218
|
+
? Parent extends Record<string, any>
|
|
219
|
+
? Omit<Parent, keyof Child> & Child
|
|
220
|
+
: Child
|
|
221
|
+
: Child;
|
|
222
222
|
|
|
223
223
|
type MergeVariantMaps<Child, Parent> = Omit<Parent, keyof Child> &
|
|
224
224
|
Child & {
|
|
@@ -228,17 +228,11 @@ type MergeVariantMaps<Child, Parent> = Omit<Parent, keyof Child> &
|
|
|
228
228
|
>;
|
|
229
229
|
};
|
|
230
230
|
|
|
231
|
-
type
|
|
232
|
-
MergeExtendedVariants<E> & MergeExtendedComputedVariants<E>;
|
|
233
|
-
|
|
234
|
-
type MergeBaseVariants<V, E extends AnyComponent[]> = MergeVariantMaps<
|
|
231
|
+
export type MergeVariants<V, E extends AnyComponent[]> = MergeVariantMaps<
|
|
235
232
|
NoInfer<V>,
|
|
236
|
-
|
|
233
|
+
MergeExtendedVariants<E>
|
|
237
234
|
>;
|
|
238
235
|
|
|
239
|
-
export type MergeVariants<V, CV, E extends AnyComponent[]> = NoInfer<CV> &
|
|
240
|
-
Omit<MergeBaseVariants<V, E>, keyof CV>;
|
|
241
|
-
|
|
242
236
|
type StringToBoolean<T> = T extends "true" | "false" ? boolean : T;
|
|
243
237
|
|
|
244
238
|
type VariantValue = ClassValue | StyleClassValue;
|
|
@@ -272,7 +266,7 @@ export interface StyleClassValue {
|
|
|
272
266
|
class?: ClassValue;
|
|
273
267
|
}
|
|
274
268
|
|
|
275
|
-
export interface
|
|
269
|
+
export interface RefineContext<V> {
|
|
276
270
|
variants: VariantValues<V>;
|
|
277
271
|
setVariants: (variants: VariantValues<V>) => void;
|
|
278
272
|
setDefaultVariants: (variants: VariantValues<V>) => void;
|
|
@@ -280,16 +274,14 @@ export interface ComputedContext<V> {
|
|
|
280
274
|
addStyle: (style: StyleValue) => void;
|
|
281
275
|
}
|
|
282
276
|
|
|
283
|
-
export type
|
|
277
|
+
export type Refine<V> = (context: RefineContext<V>) => VariantValue;
|
|
284
278
|
|
|
285
|
-
export type
|
|
286
|
-
|
|
287
|
-
|
|
279
|
+
export type Variant =
|
|
280
|
+
| ClassValue
|
|
281
|
+
| Record<string, VariantValue>
|
|
282
|
+
| ((value: any) => VariantValue);
|
|
288
283
|
export type Variants = Record<string, Variant>;
|
|
289
284
|
|
|
290
|
-
type ExtendedVariants<E extends AnyComponent[]> = MergeExtendedVariants<E> &
|
|
291
|
-
MergeExtendedComputedVariants<E>;
|
|
292
|
-
|
|
293
285
|
type NullablePartial<T> =
|
|
294
286
|
T extends Record<string, any> ? { [K in keyof T]?: T[K] | null } : T | null;
|
|
295
287
|
|
|
@@ -297,7 +289,7 @@ export type ExtendableVariants<
|
|
|
297
289
|
V extends Variants,
|
|
298
290
|
E extends AnyComponent[],
|
|
299
291
|
> = V & {
|
|
300
|
-
[K in keyof
|
|
301
|
-
| NullablePartial<
|
|
292
|
+
[K in keyof MergeExtendedVariants<E>]?:
|
|
293
|
+
| NullablePartial<MergeExtendedVariants<E>[K]>
|
|
302
294
|
| Variant;
|
|
303
295
|
};
|
package/src/utils.ts
CHANGED
|
@@ -39,7 +39,9 @@ export function hyphenToCamel(str: string) {
|
|
|
39
39
|
}
|
|
40
40
|
// Fast path: no hyphen -> return as-is
|
|
41
41
|
let hyphenIndex = str.indexOf("-");
|
|
42
|
-
if (hyphenIndex === -1)
|
|
42
|
+
if (hyphenIndex === -1) {
|
|
43
|
+
return str;
|
|
44
|
+
}
|
|
43
45
|
|
|
44
46
|
let result = "";
|
|
45
47
|
let lastIndex = 0;
|
|
@@ -91,7 +93,9 @@ export function camelToHyphen(str: string) {
|
|
|
91
93
|
lastIndex = i + 1;
|
|
92
94
|
}
|
|
93
95
|
|
|
94
|
-
if (lastIndex === 0)
|
|
96
|
+
if (lastIndex === 0) {
|
|
97
|
+
return str;
|
|
98
|
+
}
|
|
95
99
|
return result + str.slice(lastIndex);
|
|
96
100
|
}
|
|
97
101
|
|
|
@@ -102,7 +106,9 @@ export function camelToHyphen(str: string) {
|
|
|
102
106
|
* parseLengthValue("2em"); // "2em"
|
|
103
107
|
*/
|
|
104
108
|
export function parseLengthValue(value: string | number) {
|
|
105
|
-
if (typeof value === "string")
|
|
109
|
+
if (typeof value === "string") {
|
|
110
|
+
return value;
|
|
111
|
+
}
|
|
106
112
|
return `${value}px`;
|
|
107
113
|
}
|
|
108
114
|
|
|
@@ -135,7 +141,10 @@ export function htmlStyleToStyleValue(styleString: string) {
|
|
|
135
141
|
}
|
|
136
142
|
if (i >= len || styleString.charCodeAt(i) === 59) {
|
|
137
143
|
// No colon found - skip this declaration
|
|
138
|
-
if (i < len)
|
|
144
|
+
if (i < len) {
|
|
145
|
+
// Skip ';'.
|
|
146
|
+
i++;
|
|
147
|
+
}
|
|
139
148
|
continue;
|
|
140
149
|
}
|
|
141
150
|
let propEnd = i;
|
|
@@ -147,12 +156,17 @@ export function htmlStyleToStyleValue(styleString: string) {
|
|
|
147
156
|
}
|
|
148
157
|
if (propEnd === propStart) {
|
|
149
158
|
// Empty property - skip
|
|
150
|
-
while (i < len && styleString.charCodeAt(i) !== 59)
|
|
151
|
-
|
|
159
|
+
while (i < len && styleString.charCodeAt(i) !== 59) {
|
|
160
|
+
i++;
|
|
161
|
+
}
|
|
162
|
+
if (i < len) {
|
|
163
|
+
i++;
|
|
164
|
+
}
|
|
152
165
|
continue;
|
|
153
166
|
}
|
|
154
167
|
const property = styleString.slice(propStart, propEnd);
|
|
155
|
-
|
|
168
|
+
// Skip ':'.
|
|
169
|
+
i++;
|
|
156
170
|
// Skip whitespace before value
|
|
157
171
|
while (i < len) {
|
|
158
172
|
const c = styleString.charCodeAt(i);
|
|
@@ -160,14 +174,19 @@ export function htmlStyleToStyleValue(styleString: string) {
|
|
|
160
174
|
i++;
|
|
161
175
|
}
|
|
162
176
|
const valStart = i;
|
|
163
|
-
while (i < len && styleString.charCodeAt(i) !== 59)
|
|
177
|
+
while (i < len && styleString.charCodeAt(i) !== 59) {
|
|
178
|
+
i++;
|
|
179
|
+
}
|
|
164
180
|
let valEnd = i;
|
|
165
181
|
while (valEnd > valStart) {
|
|
166
182
|
const c = styleString.charCodeAt(valEnd - 1);
|
|
167
183
|
if (c !== 32 && c !== 9 && c !== 10 && c !== 13) break;
|
|
168
184
|
valEnd--;
|
|
169
185
|
}
|
|
170
|
-
if (i < len)
|
|
186
|
+
if (i < len) {
|
|
187
|
+
// Skip ';'.
|
|
188
|
+
i++;
|
|
189
|
+
}
|
|
171
190
|
if (valEnd === valStart) continue;
|
|
172
191
|
const value = styleString.slice(valStart, valEnd);
|
|
173
192
|
// CSS property names and values are dynamic - cast required for index access
|
|
@@ -229,7 +248,9 @@ export function styleValueToHTMLStyle(style: StyleValue): string {
|
|
|
229
248
|
if (!hasOwn.call(style, key)) continue;
|
|
230
249
|
const value = (style as Record<string, unknown>)[key];
|
|
231
250
|
if (value == null) continue;
|
|
232
|
-
if (result)
|
|
251
|
+
if (result) {
|
|
252
|
+
result += "; ";
|
|
253
|
+
}
|
|
233
254
|
result += camelToHyphen(key);
|
|
234
255
|
result += ": ";
|
|
235
256
|
result += value as string | number;
|
package/tests/_utils.ts
CHANGED
|
@@ -4,7 +4,6 @@ import type {
|
|
|
4
4
|
AnyComponent,
|
|
5
5
|
CVComponent,
|
|
6
6
|
ComponentResult,
|
|
7
|
-
ComputedVariants,
|
|
8
7
|
HTMLObjProps,
|
|
9
8
|
HTMLProps,
|
|
10
9
|
JSXProps,
|
|
@@ -86,15 +85,18 @@ export function createCVFromConfig(
|
|
|
86
85
|
export function getModeComponent<
|
|
87
86
|
M extends Mode,
|
|
88
87
|
V extends Variants = {},
|
|
89
|
-
CV extends ComputedVariants = {},
|
|
90
88
|
const E extends AnyComponent[] = [],
|
|
91
|
-
>(mode: M, component: CVComponent<V,
|
|
92
|
-
if (!mode)
|
|
89
|
+
>(mode: M, component: CVComponent<V, E>) {
|
|
90
|
+
if (!mode) {
|
|
91
|
+
return component;
|
|
92
|
+
}
|
|
93
93
|
return component[mode];
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
function getClass(props: ComponentResult) {
|
|
97
|
-
if ("class" in props)
|
|
97
|
+
if ("class" in props) {
|
|
98
|
+
return props.class;
|
|
99
|
+
}
|
|
98
100
|
return props.className;
|
|
99
101
|
}
|
|
100
102
|
|
package/tests/build.test.ts
CHANGED
|
@@ -1,18 +1,92 @@
|
|
|
1
1
|
import { execFile } from "node:child_process";
|
|
2
|
-
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
3
4
|
import { dirname, join } from "node:path";
|
|
4
|
-
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
5
6
|
import { promisify } from "node:util";
|
|
7
|
+
import { withPackageBuildLock } from "test-utils/build-lock";
|
|
8
|
+
import { build } from "vite";
|
|
6
9
|
import { expect, test } from "vitest";
|
|
7
10
|
|
|
8
11
|
const root = join(dirname(fileURLToPath(import.meta.url)), "..");
|
|
9
12
|
const exec = promisify(execFile);
|
|
13
|
+
let buildPromise: Promise<unknown> | undefined;
|
|
14
|
+
|
|
15
|
+
function buildPackage() {
|
|
16
|
+
buildPromise ??= exec("pnpm", ["--dir", root, "build"]);
|
|
17
|
+
return buildPromise;
|
|
18
|
+
}
|
|
10
19
|
|
|
11
20
|
test("build preserves the production warning guard for consumers", async () => {
|
|
12
|
-
await
|
|
21
|
+
await withPackageBuildLock(async () => {
|
|
22
|
+
await buildPackage();
|
|
23
|
+
|
|
24
|
+
const code = await readFile(join(root, "dist/index.js"), "utf8");
|
|
25
|
+
expect(code).toMatch(
|
|
26
|
+
/warnRefineLimit[\s\S]*?process\.env\.NODE_ENV === "production"[\s\S]*?console\.warn\(/,
|
|
27
|
+
);
|
|
28
|
+
});
|
|
29
|
+
}, 60_000);
|
|
30
|
+
|
|
31
|
+
test("vite removes warning logic from the production bundle", async () => {
|
|
32
|
+
await withPackageBuildLock(async () => {
|
|
33
|
+
await buildPackage();
|
|
34
|
+
|
|
35
|
+
const tempDir = await mkdtemp(join(tmpdir(), "clava-vite-"));
|
|
36
|
+
try {
|
|
37
|
+
const entry = join(tempDir, "entry.js");
|
|
38
|
+
const bundle = join(tempDir, "dist/bundle.js");
|
|
39
|
+
const clavaUrl = pathToFileURL(join(root, "dist/index.js")).href;
|
|
40
|
+
|
|
41
|
+
await writeFile(
|
|
42
|
+
entry,
|
|
43
|
+
`
|
|
44
|
+
import { cv } from ${JSON.stringify(clavaUrl)};
|
|
45
|
+
|
|
46
|
+
export const button = cv({
|
|
47
|
+
variants: {
|
|
48
|
+
tone: {
|
|
49
|
+
primary: "primary",
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
refine({ setVariants }) {
|
|
53
|
+
setVariants({ tone: "primary" });
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
button();
|
|
58
|
+
`,
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
await build({
|
|
62
|
+
configFile: false,
|
|
63
|
+
logLevel: "silent",
|
|
64
|
+
root: tempDir,
|
|
65
|
+
mode: "production",
|
|
66
|
+
define: {
|
|
67
|
+
"process.env.NODE_ENV": JSON.stringify("production"),
|
|
68
|
+
},
|
|
69
|
+
build: {
|
|
70
|
+
emptyOutDir: true,
|
|
71
|
+
lib: {
|
|
72
|
+
entry,
|
|
73
|
+
fileName: () => "bundle.js",
|
|
74
|
+
formats: ["es"],
|
|
75
|
+
},
|
|
76
|
+
minify: true,
|
|
77
|
+
outDir: "dist",
|
|
78
|
+
},
|
|
79
|
+
});
|
|
13
80
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
81
|
+
const code = await readFile(bundle, "utf8");
|
|
82
|
+
expect(code).not.toContain("console.warn");
|
|
83
|
+
expect(code).not.toContain("Clava: Maximum refine iterations exceeded");
|
|
84
|
+
expect(code).not.toContain("Variant(s) that did not stabilize");
|
|
85
|
+
expect(code).not.toContain("Component created at");
|
|
86
|
+
expect(code).not.toContain("captureStackTrace");
|
|
87
|
+
expect(code).not.toMatch(/\.warned\b|["']warned["']/);
|
|
88
|
+
} finally {
|
|
89
|
+
await rm(tempDir, { force: true, recursive: true });
|
|
90
|
+
}
|
|
91
|
+
});
|
|
18
92
|
}, 60_000);
|
|
@@ -59,7 +59,7 @@ for (const config of Object.values(CONFIGS)) {
|
|
|
59
59
|
expect(variants).toEqual({ size: "sm" });
|
|
60
60
|
});
|
|
61
61
|
|
|
62
|
-
test("getVariants returns variants set by
|
|
62
|
+
test("getVariants returns variants set by refine setVariants", () => {
|
|
63
63
|
const component = getModeComponent(
|
|
64
64
|
mode,
|
|
65
65
|
cv({
|
|
@@ -67,7 +67,7 @@ for (const config of Object.values(CONFIGS)) {
|
|
|
67
67
|
size: { sm: "sm", lg: "lg" },
|
|
68
68
|
color: { red: "red", blue: "blue" },
|
|
69
69
|
},
|
|
70
|
-
|
|
70
|
+
refine: ({ variants, setVariants }) => {
|
|
71
71
|
if (variants.size === "lg") {
|
|
72
72
|
setVariants({ color: "red" });
|
|
73
73
|
}
|
|
@@ -78,7 +78,7 @@ for (const config of Object.values(CONFIGS)) {
|
|
|
78
78
|
expect(variants).toEqual({ size: "lg", color: "red" });
|
|
79
79
|
});
|
|
80
80
|
|
|
81
|
-
test("getVariants re-runs when
|
|
81
|
+
test("getVariants re-runs when refine changes variants", () => {
|
|
82
82
|
const component = getModeComponent(
|
|
83
83
|
mode,
|
|
84
84
|
cv({
|
|
@@ -86,7 +86,7 @@ for (const config of Object.values(CONFIGS)) {
|
|
|
86
86
|
size: { sm: "sm", lg: "lg" },
|
|
87
87
|
color: { red: "red", blue: "blue" },
|
|
88
88
|
},
|
|
89
|
-
|
|
89
|
+
refine: ({ variants, setVariants }) => {
|
|
90
90
|
if (variants.size === "lg") {
|
|
91
91
|
setVariants({ color: "red" });
|
|
92
92
|
}
|
|
@@ -108,7 +108,7 @@ for (const config of Object.values(CONFIGS)) {
|
|
|
108
108
|
size: { sm: "sm", lg: "lg" },
|
|
109
109
|
color: { red: "red", blue: "blue" },
|
|
110
110
|
},
|
|
111
|
-
|
|
111
|
+
refine: ({ variants, setDefaultVariants }) => {
|
|
112
112
|
setDefaultVariants({ color: "red" });
|
|
113
113
|
if (variants.color === "red") {
|
|
114
114
|
setDefaultVariants({ size: "lg" });
|
|
@@ -120,7 +120,7 @@ for (const config of Object.values(CONFIGS)) {
|
|
|
120
120
|
expect(variants).toEqual({ size: "lg", color: "red" });
|
|
121
121
|
});
|
|
122
122
|
|
|
123
|
-
test("getVariants returns variants set by
|
|
123
|
+
test("getVariants returns variants set by refine setDefaultVariants", () => {
|
|
124
124
|
const component = getModeComponent(
|
|
125
125
|
mode,
|
|
126
126
|
cv({
|
|
@@ -128,7 +128,7 @@ for (const config of Object.values(CONFIGS)) {
|
|
|
128
128
|
size: { sm: "sm", lg: "lg" },
|
|
129
129
|
color: { red: "red", blue: "blue" },
|
|
130
130
|
},
|
|
131
|
-
|
|
131
|
+
refine: ({ variants, setDefaultVariants }) => {
|
|
132
132
|
if (variants.size === "lg") {
|
|
133
133
|
setDefaultVariants({ color: "blue" });
|
|
134
134
|
}
|
|
@@ -147,7 +147,7 @@ for (const config of Object.values(CONFIGS)) {
|
|
|
147
147
|
size: { sm: "sm", lg: "lg" },
|
|
148
148
|
color: { red: "red", blue: "blue" },
|
|
149
149
|
},
|
|
150
|
-
|
|
150
|
+
refine: ({ setDefaultVariants }) => {
|
|
151
151
|
setDefaultVariants({ color: "blue" });
|
|
152
152
|
},
|
|
153
153
|
}),
|
|
@@ -164,7 +164,7 @@ for (const config of Object.values(CONFIGS)) {
|
|
|
164
164
|
size: { sm: "sm", lg: "lg" },
|
|
165
165
|
color: { red: "red", blue: "blue" },
|
|
166
166
|
},
|
|
167
|
-
|
|
167
|
+
refine: ({ setVariants }) => {
|
|
168
168
|
setVariants({ color: "blue" });
|
|
169
169
|
},
|
|
170
170
|
}),
|
|
@@ -176,7 +176,7 @@ for (const config of Object.values(CONFIGS)) {
|
|
|
176
176
|
test("getVariants picks up setDefaultVariants from extended component", () => {
|
|
177
177
|
const base = cv({
|
|
178
178
|
variants: { size: { sm: "sm", lg: "lg" } },
|
|
179
|
-
|
|
179
|
+
refine: ({ setDefaultVariants }) => {
|
|
180
180
|
setDefaultVariants({ size: "lg" });
|
|
181
181
|
},
|
|
182
182
|
});
|
|
@@ -195,7 +195,7 @@ for (const config of Object.values(CONFIGS)) {
|
|
|
195
195
|
test("getVariants picks up setDefaultVariants from grandparent component", () => {
|
|
196
196
|
const grandparent = cv({
|
|
197
197
|
variants: { size: { sm: "sm", lg: "lg" } },
|
|
198
|
-
|
|
198
|
+
refine: ({ setDefaultVariants }) => {
|
|
199
199
|
setDefaultVariants({ size: "lg" });
|
|
200
200
|
},
|
|
201
201
|
});
|
|
@@ -212,11 +212,11 @@ for (const config of Object.values(CONFIGS)) {
|
|
|
212
212
|
expect(variants).toEqual({ size: "lg", color: "red" });
|
|
213
213
|
});
|
|
214
214
|
|
|
215
|
-
test("getVariants re-runs when base component
|
|
215
|
+
test("getVariants re-runs when base component refine changes variants", () => {
|
|
216
216
|
const base = cv({
|
|
217
217
|
variants: { size: { sm: "sm", lg: "lg" }, active: "" },
|
|
218
218
|
defaultVariants: { size: "sm" },
|
|
219
|
-
|
|
219
|
+
refine: ({ variants, setVariants }) => {
|
|
220
220
|
if (variants.active) {
|
|
221
221
|
setVariants({ size: "lg" });
|
|
222
222
|
}
|
|
@@ -227,7 +227,7 @@ for (const config of Object.values(CONFIGS)) {
|
|
|
227
227
|
cv({
|
|
228
228
|
extend: [base],
|
|
229
229
|
variants: { color: { red: "red", blue: "blue" } },
|
|
230
|
-
|
|
230
|
+
refine: ({ variants, setVariants }) => {
|
|
231
231
|
if (variants.size === "lg") {
|
|
232
232
|
setVariants({ color: "red" });
|
|
233
233
|
}
|
|
@@ -246,7 +246,7 @@ for (const config of Object.values(CONFIGS)) {
|
|
|
246
246
|
mode: { on: "on" },
|
|
247
247
|
},
|
|
248
248
|
defaultVariants: { size: "sm" },
|
|
249
|
-
|
|
249
|
+
refine: ({ variants, setVariants, setDefaultVariants }) => {
|
|
250
250
|
if (variants.active) {
|
|
251
251
|
setVariants({ mode: "on" });
|
|
252
252
|
}
|
|
@@ -266,7 +266,7 @@ for (const config of Object.values(CONFIGS)) {
|
|
|
266
266
|
cv({
|
|
267
267
|
variants: { size: { sm: "sm", lg: "lg" } },
|
|
268
268
|
defaultVariants: { size: "sm" },
|
|
269
|
-
|
|
269
|
+
refine: ({ setVariants }) => {
|
|
270
270
|
setVariants({ size: "lg" });
|
|
271
271
|
setVariants({ size: "sm" });
|
|
272
272
|
},
|
|
@@ -279,7 +279,7 @@ for (const config of Object.values(CONFIGS)) {
|
|
|
279
279
|
test("getVariants child setVariants keeps overriding base setDefaultVariants across re-runs", () => {
|
|
280
280
|
const base = cv({
|
|
281
281
|
variants: { color: { red: "red", blue: "blue" } },
|
|
282
|
-
|
|
282
|
+
refine: ({ setDefaultVariants }) => {
|
|
283
283
|
setDefaultVariants({ color: "blue" });
|
|
284
284
|
},
|
|
285
285
|
});
|
|
@@ -289,7 +289,7 @@ for (const config of Object.values(CONFIGS)) {
|
|
|
289
289
|
extend: [base],
|
|
290
290
|
variants: { size: { sm: "sm", lg: "lg" } },
|
|
291
291
|
defaultVariants: { size: "sm" },
|
|
292
|
-
|
|
292
|
+
refine: ({ variants, setVariants }) => {
|
|
293
293
|
if (variants.size === "sm") {
|
|
294
294
|
setVariants({ color: "red" });
|
|
295
295
|
}
|
|
@@ -303,7 +303,7 @@ for (const config of Object.values(CONFIGS)) {
|
|
|
303
303
|
test("getVariants setVariants sticks across re-runs", () => {
|
|
304
304
|
const base = cv({
|
|
305
305
|
variants: { color: { red: "red", blue: "blue" } },
|
|
306
|
-
|
|
306
|
+
refine: ({ setDefaultVariants }) => {
|
|
307
307
|
setDefaultVariants({ color: "blue" });
|
|
308
308
|
},
|
|
309
309
|
});
|
|
@@ -312,7 +312,7 @@ for (const config of Object.values(CONFIGS)) {
|
|
|
312
312
|
cv({
|
|
313
313
|
extend: [base],
|
|
314
314
|
variants: { color: { red: "red", blue: "blue" }, done: "" },
|
|
315
|
-
|
|
315
|
+
refine: ({ variants, setVariants }) => {
|
|
316
316
|
if (!variants.done) {
|
|
317
317
|
setVariants({ color: "red", done: true });
|
|
318
318
|
}
|
|
@@ -330,7 +330,7 @@ for (const config of Object.values(CONFIGS)) {
|
|
|
330
330
|
active: "",
|
|
331
331
|
mode: { on: "on" },
|
|
332
332
|
},
|
|
333
|
-
|
|
333
|
+
refine: ({ variants, setVariants, setDefaultVariants }) => {
|
|
334
334
|
if (variants.active) {
|
|
335
335
|
setVariants({ mode: "on" });
|
|
336
336
|
}
|
|
@@ -350,13 +350,13 @@ for (const config of Object.values(CONFIGS)) {
|
|
|
350
350
|
test("getVariants setVariants from earlier extends overrides setDefaultVariants from later extends", () => {
|
|
351
351
|
const first = cv({
|
|
352
352
|
variants: { color: { red: "first-red", blue: "first-blue" } },
|
|
353
|
-
|
|
353
|
+
refine: ({ setVariants }) => {
|
|
354
354
|
setVariants({ color: "red" });
|
|
355
355
|
},
|
|
356
356
|
});
|
|
357
357
|
const second = cv({
|
|
358
358
|
variants: { color: { red: "second-red", blue: "second-blue" } },
|
|
359
|
-
|
|
359
|
+
refine: ({ setDefaultVariants }) => {
|
|
360
360
|
setDefaultVariants({ color: "blue" });
|
|
361
361
|
},
|
|
362
362
|
});
|
|
@@ -368,13 +368,13 @@ for (const config of Object.values(CONFIGS)) {
|
|
|
368
368
|
test("getVariants setDefaultVariants from later extends overrides setDefaultVariants from earlier extends", () => {
|
|
369
369
|
const first = cv({
|
|
370
370
|
variants: { color: { red: "first-red", blue: "first-blue" } },
|
|
371
|
-
|
|
371
|
+
refine: ({ setDefaultVariants }) => {
|
|
372
372
|
setDefaultVariants({ color: "red" });
|
|
373
373
|
},
|
|
374
374
|
});
|
|
375
375
|
const second = cv({
|
|
376
376
|
variants: { color: { red: "second-red", blue: "second-blue" } },
|
|
377
|
-
|
|
377
|
+
refine: ({ setDefaultVariants }) => {
|
|
378
378
|
setDefaultVariants({ color: "blue" });
|
|
379
379
|
},
|
|
380
380
|
});
|
|
@@ -386,7 +386,7 @@ for (const config of Object.values(CONFIGS)) {
|
|
|
386
386
|
test("getVariants setDefaultVariants does not override stable setVariants on later passes", () => {
|
|
387
387
|
const base = cv({
|
|
388
388
|
variants: { color: { red: "base-red", blue: "base-blue" } },
|
|
389
|
-
|
|
389
|
+
refine: ({ setVariants }) => {
|
|
390
390
|
setVariants({ color: "red" });
|
|
391
391
|
},
|
|
392
392
|
});
|
|
@@ -395,7 +395,7 @@ for (const config of Object.values(CONFIGS)) {
|
|
|
395
395
|
cv({
|
|
396
396
|
extend: [base],
|
|
397
397
|
variants: { color: { red: "child-red", blue: "child-blue" } },
|
|
398
|
-
|
|
398
|
+
refine: ({ variants, setDefaultVariants }) => {
|
|
399
399
|
if (variants.color === "red") {
|
|
400
400
|
setDefaultVariants({ color: "blue" });
|
|
401
401
|
}
|
|
@@ -414,7 +414,7 @@ for (const config of Object.values(CONFIGS)) {
|
|
|
414
414
|
color: { red: "red", blue: "blue" },
|
|
415
415
|
done: "",
|
|
416
416
|
},
|
|
417
|
-
|
|
417
|
+
refine: ({ variants, setVariants, setDefaultVariants }) => {
|
|
418
418
|
if (!variants.done) {
|
|
419
419
|
setVariants({ color: "red", done: true });
|
|
420
420
|
}
|
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 refine setDefaultVariants", () => {
|
|
148
148
|
const base = cv({
|
|
149
149
|
variants: {
|
|
150
150
|
size: {
|
|
@@ -158,7 +158,7 @@ for (const config of Object.values(CONFIGS)) {
|
|
|
158
158
|
cv({
|
|
159
159
|
extend: [base],
|
|
160
160
|
variants: { size: { sm: null } },
|
|
161
|
-
|
|
161
|
+
refine: ({ setDefaultVariants }) => {
|
|
162
162
|
setDefaultVariants({ size: "lg" });
|
|
163
163
|
},
|
|
164
164
|
}),
|
|
@@ -173,7 +173,7 @@ for (const config of Object.values(CONFIGS)) {
|
|
|
173
173
|
cv({
|
|
174
174
|
extend: [base],
|
|
175
175
|
variants: { size: { sm: null } },
|
|
176
|
-
|
|
176
|
+
refine: ({ setDefaultVariants }) => {
|
|
177
177
|
setDefaultVariants({
|
|
178
178
|
// @ts-expect-error disabled variant value cannot be set
|
|
179
179
|
size:
|
|
@@ -186,7 +186,7 @@ for (const config of Object.values(CONFIGS)) {
|
|
|
186
186
|
expect(getStyleClass(invalidComponent())).toEqual({ class: "" });
|
|
187
187
|
});
|
|
188
188
|
|
|
189
|
-
test("extend disabled variant value with
|
|
189
|
+
test("extend disabled variant value with refine setVariants", () => {
|
|
190
190
|
const base = cv({
|
|
191
191
|
variants: {
|
|
192
192
|
size: {
|
|
@@ -200,7 +200,7 @@ for (const config of Object.values(CONFIGS)) {
|
|
|
200
200
|
cv({
|
|
201
201
|
extend: [base],
|
|
202
202
|
variants: { size: { sm: null } },
|
|
203
|
-
|
|
203
|
+
refine: ({ setVariants }) => {
|
|
204
204
|
setVariants({ size: "lg" });
|
|
205
205
|
},
|
|
206
206
|
}),
|
|
@@ -215,7 +215,7 @@ for (const config of Object.values(CONFIGS)) {
|
|
|
215
215
|
cv({
|
|
216
216
|
extend: [base],
|
|
217
217
|
variants: { size: { sm: null } },
|
|
218
|
-
|
|
218
|
+
refine: ({ setVariants }) => {
|
|
219
219
|
setVariants({
|
|
220
220
|
// @ts-expect-error disabled variant value cannot be set
|
|
221
221
|
size:
|
|
@@ -333,8 +333,13 @@ describe("non-idempotent transformClass", () => {
|
|
|
333
333
|
});
|
|
334
334
|
});
|
|
335
335
|
|
|
336
|
-
|
|
337
|
-
|
|
336
|
+
function toUpperCase(className: string) {
|
|
337
|
+
return className.toUpperCase();
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function toLowerCase(className: string) {
|
|
341
|
+
return className.toLowerCase();
|
|
342
|
+
}
|
|
338
343
|
|
|
339
344
|
describe("extend across `create()` factories", () => {
|
|
340
345
|
// The extend's own `transformClass` must apply to its own classes even when
|