clava 0.2.4 → 0.4.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.
@@ -15,11 +15,11 @@ for (const config of Object.values(CONFIGS)) {
15
15
  const cls = getConfigTransformClass(config);
16
16
 
17
17
  describe(getConfigDescription(config), () => {
18
- test("computedVariants", () => {
18
+ test("function variant", () => {
19
19
  const component = getModeComponent(
20
20
  mode,
21
21
  cv({
22
- computedVariants: {
22
+ variants: {
23
23
  size: (value: "sm" | "lg") => (value === "sm" ? "small" : "large"),
24
24
  },
25
25
  }),
@@ -28,11 +28,11 @@ for (const config of Object.values(CONFIGS)) {
28
28
  expect(getStyleClass(props)).toEqual({ class: cls("large") });
29
29
  });
30
30
 
31
- test("computedVariants with style", () => {
31
+ test("function variant with style", () => {
32
32
  const component = getModeComponent(
33
33
  mode,
34
34
  cv({
35
- computedVariants: {
35
+ variants: {
36
36
  size: (value: "sm" | "lg") => ({
37
37
  class: value === "sm" ? "small" : "large",
38
38
  style: { fontSize: value === "sm" ? "12px" : "16px" },
@@ -47,7 +47,7 @@ for (const config of Object.values(CONFIGS)) {
47
47
  });
48
48
  });
49
49
 
50
- test("computedVariants can return another component default result", () => {
50
+ test("function variant can return another component default result", () => {
51
51
  const button = cv({
52
52
  variants: {
53
53
  size: {
@@ -59,7 +59,7 @@ for (const config of Object.values(CONFIGS)) {
59
59
  const component = getModeComponent(
60
60
  mode,
61
61
  cv({
62
- computedVariants: {
62
+ variants: {
63
63
  size: (value: "sm" | "lg") => {
64
64
  return button({ size: value });
65
65
  },
@@ -73,7 +73,7 @@ for (const config of Object.values(CONFIGS)) {
73
73
  });
74
74
  });
75
75
 
76
- test("computedVariants overrides extended object variants", () => {
76
+ test("function variant overrides extended object variant", () => {
77
77
  const base = cv({
78
78
  variants: {
79
79
  size: {
@@ -86,7 +86,7 @@ for (const config of Object.values(CONFIGS)) {
86
86
  mode,
87
87
  cv({
88
88
  extend: [base],
89
- computedVariants: {
89
+ variants: {
90
90
  size: (value: "sm" | "lg") => ({
91
91
  class: value === "sm" ? "extended-sm" : "extended-lg",
92
92
  style: {
@@ -103,9 +103,9 @@ for (const config of Object.values(CONFIGS)) {
103
103
  });
104
104
  });
105
105
 
106
- test("computedVariants overrides extended computedVariants", () => {
106
+ test("function variant overrides extended function variant", () => {
107
107
  const base = cv({
108
- computedVariants: {
108
+ variants: {
109
109
  size: (value: "sm" | "lg") => ({
110
110
  class: value === "sm" ? "base-sm" : "base-lg",
111
111
  style: { fontSize: value === "sm" ? "12px" : "16px" },
@@ -116,7 +116,7 @@ for (const config of Object.values(CONFIGS)) {
116
116
  mode,
117
117
  cv({
118
118
  extend: [base],
119
- computedVariants: {
119
+ variants: {
120
120
  size: (value: "sm" | "lg") => ({
121
121
  class: value === "sm" ? "extended-sm" : "extended-lg",
122
122
  style: {
@@ -133,11 +133,11 @@ for (const config of Object.values(CONFIGS)) {
133
133
  });
134
134
  });
135
135
 
136
- test("computedVariants style does not accept numbers", () => {
136
+ test("function variant style does not accept numbers", () => {
137
137
  const component = getModeComponent(
138
138
  mode,
139
139
  cv({
140
- computedVariants: {
140
+ variants: {
141
141
  // @ts-expect-error
142
142
  size: (value: "sm" | "lg") => ({
143
143
  class: value === "sm" ? "small" : "large",
@@ -153,7 +153,7 @@ for (const config of Object.values(CONFIGS)) {
153
153
  });
154
154
  });
155
155
 
156
- test("computedVariants changes extended boolean variant to string", () => {
156
+ test("function variant changes extended boolean variant to string", () => {
157
157
  const base = cv({
158
158
  variants: { disabled: { true: "disabled", false: "enabled" } },
159
159
  });
@@ -161,7 +161,7 @@ for (const config of Object.values(CONFIGS)) {
161
161
  mode,
162
162
  cv({
163
163
  extend: [base],
164
- computedVariants: {
164
+ variants: {
165
165
  disabled: (value: "yes" | "no" | "maybe") => {
166
166
  if (value === "yes") return "state-disabled";
167
167
  if (value === "no") return "state-enabled";
@@ -178,13 +178,13 @@ for (const config of Object.values(CONFIGS)) {
178
178
  expect(getStyleClass(props)).toEqual({ class: cls("state-pending") });
179
179
  });
180
180
 
181
- test("computedVariants changes extended string variant to boolean", () => {
181
+ test("function variant changes extended string variant to boolean", () => {
182
182
  const base = cv({ variants: { size: { sm: "sm", md: "md", lg: "lg" } } });
183
183
  const component = getModeComponent(
184
184
  mode,
185
185
  cv({
186
186
  extend: [base],
187
- computedVariants: {
187
+ variants: {
188
188
  size: (value: boolean) => (value ? "size-large" : "size-small"),
189
189
  },
190
190
  }),
@@ -199,11 +199,11 @@ for (const config of Object.values(CONFIGS)) {
199
199
  expect(getStyleClass(propsFalse)).toEqual({ class: cls("size-small") });
200
200
  });
201
201
 
202
- test("computedVariants with number type", () => {
202
+ test("function variant with number type", () => {
203
203
  const component = getModeComponent(
204
204
  mode,
205
205
  cv({
206
- computedVariants: {
206
+ variants: {
207
207
  columns: (value: number) => ({
208
208
  class: `grid-cols-${value}`,
209
209
  style: { "--grid-columns": `${value}` },
@@ -218,11 +218,11 @@ for (const config of Object.values(CONFIGS)) {
218
218
  });
219
219
  });
220
220
 
221
- test("computedVariants with number type returns dynamic styles", () => {
221
+ test("function variant with number type returns dynamic styles", () => {
222
222
  const component = getModeComponent(
223
223
  mode,
224
224
  cv({
225
- computedVariants: {
225
+ variants: {
226
226
  gap: (value: number) => ({
227
227
  style: { "--gap": `${value * 4}px` },
228
228
  }),
@@ -244,11 +244,11 @@ for (const config of Object.values(CONFIGS)) {
244
244
  });
245
245
  });
246
246
 
247
- test("computedVariants with nullable type", () => {
247
+ test("function variant with nullable type", () => {
248
248
  const component = getModeComponent(
249
249
  mode,
250
250
  cv({
251
- computedVariants: {
251
+ variants: {
252
252
  color: (value: string | null) => ({
253
253
  class: value ? `color-${value}` : "color-default",
254
254
  style: { "--color": value ?? "inherit" },
@@ -268,7 +268,7 @@ for (const config of Object.values(CONFIGS)) {
268
268
  });
269
269
  });
270
270
 
271
- test("computedVariants changes extended variant from string to number", () => {
271
+ test("function variant changes extended variant from string to number", () => {
272
272
  const base = cv({
273
273
  variants: { size: { sm: "text-sm", md: "text-md", lg: "text-lg" } },
274
274
  });
@@ -276,7 +276,7 @@ for (const config of Object.values(CONFIGS)) {
276
276
  mode,
277
277
  cv({
278
278
  extend: [base],
279
- computedVariants: {
279
+ variants: {
280
280
  size: (value: number) => ({
281
281
  class: "text-custom",
282
282
  style: { fontSize: `${value}px` },
@@ -294,5 +294,85 @@ for (const config of Object.values(CONFIGS)) {
294
294
  fontSize: "18px",
295
295
  });
296
296
  });
297
+
298
+ test("static and function variants combine within the same component", () => {
299
+ const component = getModeComponent(
300
+ mode,
301
+ cv({
302
+ variants: {
303
+ size: { sm: "size-sm", lg: "size-lg" },
304
+ columns: (value: number) => `cols-${value}`,
305
+ },
306
+ }),
307
+ );
308
+ const props = component({ size: "lg", columns: 3 });
309
+ expect(getStyleClass(props)).toEqual({
310
+ class: cls("size-lg cols-3"),
311
+ });
312
+ });
313
+
314
+ test("object variant in child replaces extended function variant at runtime", () => {
315
+ const base = cv({
316
+ variants: {
317
+ // The function is typed for `number`; if it ran with a child string,
318
+ // it would emit garbage like `base-fn-sm`. The merged-variant type
319
+ // says the child's object replaces the parent function entirely, so
320
+ // the runtime must skip the parent's function for this key.
321
+ size: (value: number) => `base-fn-${value}`,
322
+ },
323
+ });
324
+ const component = getModeComponent(
325
+ mode,
326
+ cv({
327
+ extend: [base],
328
+ variants: {
329
+ size: { sm: "child-sm", lg: "child-lg" },
330
+ },
331
+ }),
332
+ );
333
+ const propsSm = component({ size: "sm" });
334
+ expect(getStyleClass(propsSm)).toEqual({ class: cls("child-sm") });
335
+ const propsLg = component({ size: "lg" });
336
+ expect(getStyleClass(propsLg)).toEqual({ class: cls("child-lg") });
337
+ });
338
+
339
+ test("object variant in grandchild replaces grandparent function variant at runtime", () => {
340
+ const base = cv({
341
+ variants: { size: (value: number) => `base-fn-${value}` },
342
+ });
343
+ const middle = cv({ extend: [base], class: "middle" });
344
+ const component = getModeComponent(
345
+ mode,
346
+ cv({
347
+ extend: [middle],
348
+ variants: { size: { sm: "gc-sm", lg: "gc-lg" } },
349
+ }),
350
+ );
351
+ const props = component({ size: "sm" });
352
+ expect(getStyleClass(props)).toEqual({ class: cls("middle gc-sm") });
353
+ });
354
+
355
+ test("intermediate object variant hides grandparent function from later extends", () => {
356
+ const base = cv({
357
+ variants: { size: (value: number) => `base-fn-${value}` },
358
+ });
359
+ const middle = cv({
360
+ extend: [base],
361
+ variants: { size: { sm: "middle-sm", md: "middle-md" } },
362
+ });
363
+ const component = getModeComponent(
364
+ mode,
365
+ cv({
366
+ extend: [middle],
367
+ variants: { size: { sm: "child-sm", lg: "child-lg" } },
368
+ }),
369
+ );
370
+ // Middle already replaced the grandparent function with an object, so
371
+ // child sees only objects in the chain — both objects merge by value.
372
+ const props = component({ size: "sm" });
373
+ expect(getStyleClass(props)).toEqual({
374
+ class: cls("middle-sm child-sm"),
375
+ });
376
+ });
297
377
  });
298
378
  }
@@ -27,16 +27,16 @@ test("render ignores keys inherited from Object.prototype", () => {
27
27
  expect(component({}).class).toBe("sm");
28
28
  });
29
29
 
30
- test("extend's resolveDefaults ignores polluted prototype on parent's computed", () => {
31
- // Base's computed branches on its own variants.size — if the polluted
32
- // "size" key leaks into resolveDefaultsFn's resolvedVariants, the computed
33
- // callback would see size = "lg" instead of the staticDefault "sm" and
34
- // emit the lg-specific class.
30
+ test("extend's resolveDefaults ignores polluted prototype on parent's refine", () => {
31
+ // Base's refine branches on its own variants.size — if the polluted "size"
32
+ // key leaks into resolveDefaultsFn's resolvedVariants, the refine callback
33
+ // would see size = "lg" instead of the staticDefault "sm" and emit the
34
+ // lg-specific class.
35
35
  proto.size = "lg";
36
36
  const base = cv({
37
37
  variants: { size: { sm: "sm", lg: "lg" } },
38
38
  defaultVariants: { size: "sm" },
39
- computed: ({ variants, addClass }) => {
39
+ refine: ({ variants, addClass }) => {
40
40
  if (variants.size === "lg") {
41
41
  addClass("base-lg-detected");
42
42
  }