Skip to content

Commit 62e093c

Browse files
fix: gracefully fallback on invalid variant props instead of crashing (#469)
1 parent 100e22e commit 62e093c

28 files changed

Lines changed: 366 additions & 46 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@cloudflare/kumo": patch
3+
---
4+
5+
Gracefully fall back to default variant instead of crashing when an invalid variant prop is passed at runtime. Previously 22 of 25 components would throw `TypeError` on unknown variant values; all 25 now use a shared `resolveVariant()` utility that returns the default config and logs a dev warning.

packages/kumo/src/components/autocomplete/autocomplete.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { CheckIcon } from "@phosphor-icons/react";
33
import { type ReactNode } from "react";
44
import { inputVariants, KUMO_INPUT_VARIANTS } from "../input/input";
55
import { cn } from "../../utils/cn";
6+
import { resolveVariant } from "../../utils/resolve-variant";
67
import { Field, type FieldErrorMatch } from "../field/field";
78

89
/** Autocomplete variant definitions. */
@@ -32,7 +33,7 @@ export interface KumoAutocompleteVariantsProps {
3233
export function autocompleteVariants({
3334
size = KUMO_AUTOCOMPLETE_DEFAULT_VARIANTS.size,
3435
}: KumoAutocompleteVariantsProps = {}) {
35-
return cn(KUMO_INPUT_VARIANTS.size[size].classes);
36+
return cn(resolveVariant(KUMO_INPUT_VARIANTS.size, size, KUMO_AUTOCOMPLETE_DEFAULT_VARIANTS.size).classes);
3637
}
3738

3839
/**

packages/kumo/src/components/badge/badge.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { ReactNode } from "react";
22
import { cn } from "../../utils/cn";
3+
import { resolveVariant } from "../../utils/resolve-variant";
34

45
/** Base styles applied to all badge variants. */
56
export const KUMO_BADGE_BASE_STYLES =
@@ -98,13 +99,11 @@ export interface KumoBadgeVariantsProps {
9899
export function badgeVariants({
99100
variant = KUMO_BADGE_DEFAULT_VARIANTS.variant,
100101
}: KumoBadgeVariantsProps = {}) {
101-
const variantConfig = KUMO_BADGE_VARIANTS.variant[variant];
102102
return cn(
103103
// Base styles (exported as KUMO_BADGE_BASE_STYLES for Figma plugin)
104104
KUMO_BADGE_BASE_STYLES,
105105
// Apply variant styles from KUMO_BADGE_VARIANTS (fallback to primary if variant not found)
106-
variantConfig?.classes ??
107-
KUMO_BADGE_VARIANTS.variant[KUMO_BADGE_DEFAULT_VARIANTS.variant].classes,
106+
resolveVariant(KUMO_BADGE_VARIANTS.variant, variant, KUMO_BADGE_DEFAULT_VARIANTS.variant).classes,
108107
);
109108
}
110109

packages/kumo/src/components/banner/banner.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { type ReactNode, isValidElement, forwardRef } from "react";
22
import { cn } from "../../utils/cn";
3+
import { resolveVariant } from "../../utils/resolve-variant";
34

45
/** Base styles applied to all banner variants. */
56
export const KUMO_BANNER_BASE_STYLES =
@@ -54,7 +55,7 @@ export function bannerVariants({
5455
// Base styles (exported as KUMO_BANNER_BASE_STYLES for Figma plugin)
5556
KUMO_BANNER_BASE_STYLES,
5657
// Apply variant styles from KUMO_BANNER_VARIANTS
57-
KUMO_BANNER_VARIANTS.variant[variant].classes,
58+
resolveVariant(KUMO_BANNER_VARIANTS.variant, variant, KUMO_BANNER_DEFAULT_VARIANTS.variant).classes,
5859
);
5960
}
6061

@@ -133,7 +134,7 @@ export const Banner = forwardRef<HTMLDivElement, BannerProps>(function Banner(
133134
},
134135
ref,
135136
) {
136-
const variantConfig = KUMO_BANNER_VARIANTS.variant[variant];
137+
const variantConfig = resolveVariant(KUMO_BANNER_VARIANTS.variant, variant, KUMO_BANNER_DEFAULT_VARIANTS.variant);
137138

138139
// Structured mode: title and/or description provided
139140
if (title || description) {

packages/kumo/src/components/breadcrumbs/breadcrumbs.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { Button } from "../../components/button";
1313
import { SkeletonLine } from "../../components/loader/skeleton-line";
1414
import { useLinkComponent } from "../../utils/link-provider";
1515
import { cn } from "../../utils/cn";
16+
import { resolveVariant } from "../../utils/resolve-variant";
1617

1718
/** Breadcrumbs size variant definitions. */
1819
export const KUMO_BREADCRUMBS_VARIANTS = {
@@ -49,7 +50,7 @@ export function breadcrumbsVariants({
4950
}: KumoBreadcrumbsVariantsProps = {}) {
5051
return cn(
5152
"group mr-4 flex min-w-0 grow items-center overflow-hidden whitespace-nowrap",
52-
KUMO_BREADCRUMBS_VARIANTS.size[size].classes,
53+
resolveVariant(KUMO_BREADCRUMBS_VARIANTS.size, size, KUMO_BREADCRUMBS_DEFAULT_VARIANTS.size).classes,
5354
);
5455
}
5556

packages/kumo/src/components/button/button.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { ArrowsClockwise, type Icon } from "@phosphor-icons/react";
33
import { Loader } from "../loader/loader";
44
import { Tooltip } from "../tooltip/tooltip";
55
import { cn } from "../../utils/cn";
6+
import { resolveVariant } from "../../utils/resolve-variant";
67
import { useLinkComponent } from "../../utils/link-provider";
78

89
/** Button variant definitions mapping shape, size, and variant names to their Tailwind classes. */
@@ -135,10 +136,10 @@ export function buttonVariants({
135136
// Disabled state
136137
"disabled:cursor-not-allowed disabled:text-kumo-subtle",
137138
// Apply variant, size, shape styles from KUMO_BUTTON_VARIANTS
138-
KUMO_BUTTON_VARIANTS.variant[variant].classes,
139-
KUMO_BUTTON_VARIANTS.size[size].classes,
140-
KUMO_BUTTON_VARIANTS.shape[shape].classes,
141-
isCompactShape && KUMO_BUTTON_VARIANTS.compactSize[size].classes,
139+
resolveVariant(KUMO_BUTTON_VARIANTS.variant, variant, KUMO_BUTTON_DEFAULT_VARIANTS.variant).classes,
140+
resolveVariant(KUMO_BUTTON_VARIANTS.size, size, KUMO_BUTTON_DEFAULT_VARIANTS.size).classes,
141+
resolveVariant(KUMO_BUTTON_VARIANTS.shape, shape, KUMO_BUTTON_DEFAULT_VARIANTS.shape).classes,
142+
isCompactShape && resolveVariant(KUMO_BUTTON_VARIANTS.compactSize, size, KUMO_BUTTON_DEFAULT_VARIANTS.size).classes,
142143
);
143144
}
144145

packages/kumo/src/components/checkbox/checkbox.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { forwardRef, createContext, useContext, type ReactNode } from "react";
22
import { CheckIcon, MinusIcon } from "@phosphor-icons/react";
33
import { cn } from "../../utils/cn";
4+
import { resolveVariant } from "../../utils/resolve-variant";
45
import { Label } from "../label";
56
import { Fieldset } from "@base-ui/react/fieldset";
67
import { Field as FieldBase } from "@base-ui/react/field";
@@ -47,7 +48,7 @@ export interface KumoCheckboxVariantsProps {
4748
export function checkboxVariants({
4849
variant = KUMO_CHECKBOX_DEFAULT_VARIANTS.variant,
4950
}: KumoCheckboxVariantsProps = {}) {
50-
return cn(KUMO_CHECKBOX_VARIANTS.variant[variant].classes);
51+
return cn(resolveVariant(KUMO_CHECKBOX_VARIANTS.variant, variant, KUMO_CHECKBOX_DEFAULT_VARIANTS.variant).classes);
5152
}
5253

5354
// Legacy type alias for backwards compatibility

packages/kumo/src/components/clipboard-text/clipboard-text.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { Tooltip } from "@base-ui/react/tooltip";
55
import { Button } from "../button";
66
import { inputVariants } from "../input";
77
import { cn } from "../../utils/cn";
8+
import { resolveVariant } from "../../utils/resolve-variant";
89

910
// Create a toast manager for anchored "Copied" toasts
1011
const clipboardToastManager = Toast.createToastManager();
@@ -65,7 +66,7 @@ export function clipboardTextVariants({
6566
// Base styles
6667
"flex items-center overflow-hidden bg-kumo-base px-0 font-mono",
6768
// Apply size styles from KUMO_CLIPBOARD_TEXT_VARIANTS
68-
KUMO_CLIPBOARD_TEXT_VARIANTS.size[size].classes,
69+
resolveVariant(KUMO_CLIPBOARD_TEXT_VARIANTS.size, size, KUMO_CLIPBOARD_TEXT_DEFAULT_VARIANTS.size).classes,
6970
);
7071
}
7172

@@ -176,7 +177,7 @@ export const ClipboardText = forwardRef<HTMLDivElement, ClipboardTextProps>(
176177
) => {
177178
const [copied, setCopied] = useState(false);
178179
const buttonRef = useRef<HTMLButtonElement | null>(null);
179-
const sizeConfig = KUMO_CLIPBOARD_TEXT_VARIANTS.size[size];
180+
const sizeConfig = resolveVariant(KUMO_CLIPBOARD_TEXT_VARIANTS.size, size, KUMO_CLIPBOARD_TEXT_DEFAULT_VARIANTS.size);
180181

181182
// Destructure tooltip config with defaults
182183
const {

packages/kumo/src/components/code/code.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { type CSSProperties } from "react";
22
import { cn } from "../../utils/cn";
3+
import { resolveVariant } from "../../utils/resolve-variant";
34

45
/** Code language variant definitions. */
56
export const KUMO_CODE_VARIANTS = {
@@ -102,8 +103,7 @@ export function codeVariants({
102103
// Base styles
103104
"m-0 w-auto rounded-none border-none bg-transparent p-0 font-mono text-sm leading-[20px] text-kumo-subtle",
104105
// Apply lang-specific styles (fallback to default if lang not in map)
105-
KUMO_CODE_VARIANTS.lang[lang]?.classes ??
106-
KUMO_CODE_VARIANTS.lang[KUMO_CODE_DEFAULT_VARIANTS.lang].classes,
106+
resolveVariant(KUMO_CODE_VARIANTS.lang, lang, KUMO_CODE_DEFAULT_VARIANTS.lang).classes,
107107
);
108108
}
109109

packages/kumo/src/components/combobox/combobox.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
type KumoInputSize,
1414
} from "../input/input";
1515
import { cn } from "../../utils/cn";
16+
import { resolveVariant } from "../../utils/resolve-variant";
1617
import { Field, type FieldErrorMatch } from "../field/field";
1718
import {
1819
usePortalContainer,
@@ -69,7 +70,7 @@ export interface KumoComboboxVariantsProps {
6970
export function comboboxVariants({
7071
inputSide = KUMO_COMBOBOX_DEFAULT_VARIANTS.inputSide,
7172
}: KumoComboboxVariantsProps = {}) {
72-
return cn(KUMO_COMBOBOX_VARIANTS.inputSide[inputSide].classes);
73+
return cn(resolveVariant(KUMO_COMBOBOX_VARIANTS.inputSide, inputSide, KUMO_COMBOBOX_DEFAULT_VARIANTS.inputSide).classes);
7374
}
7475

7576
// Legacy type alias for backwards compatibility

0 commit comments

Comments
 (0)