snapshot of https://github.com/shadcn-ui/ui.git (main, 2026-09-15; blobs > 131072B omitted)
This commit is contained in:
293
apps/v4/registry/config.test.ts
Normal file
293
apps/v4/registry/config.test.ts
Normal file
@@ -0,0 +1,293 @@
|
||||
import { describe, expect, it } from "vitest"
|
||||
|
||||
import publicSchema from "../public/schema.json"
|
||||
import {
|
||||
BASES,
|
||||
buildPartialRegistryBase,
|
||||
buildRegistryBase,
|
||||
buildThemeForPreset,
|
||||
DEFAULT_CONFIG,
|
||||
designSystemConfigSchema,
|
||||
parseRegistryBaseParts,
|
||||
POINTER_CURSOR_SELECTOR,
|
||||
PRESETS,
|
||||
STYLES,
|
||||
} from "./config"
|
||||
|
||||
const legacyPublicSchemaStyles = ["default", "new-york"] as const
|
||||
|
||||
describe("buildRegistryBase", () => {
|
||||
it("seeds a font-heading fallback when heading inherits the body font", () => {
|
||||
const result = buildRegistryBase(DEFAULT_CONFIG)
|
||||
|
||||
expect(result.registryDependencies).toContain("font-inter")
|
||||
expect(result.registryDependencies).not.toContain("font-heading-inter")
|
||||
expect(result.cssVars?.theme?.["--font-heading"]).toBe("var(--font-sans)")
|
||||
})
|
||||
|
||||
it("adds a heading font dependency when a distinct heading font is selected", () => {
|
||||
const result = buildRegistryBase({
|
||||
...DEFAULT_CONFIG,
|
||||
fontHeading: "playfair-display",
|
||||
})
|
||||
|
||||
expect(result.registryDependencies).toContain("font-inter")
|
||||
expect(result.registryDependencies).toContain(
|
||||
"font-heading-playfair-display"
|
||||
)
|
||||
expect(result.cssVars?.theme?.["--font-heading"]).toBeUndefined()
|
||||
})
|
||||
|
||||
it("normalizes a matching heading font back to inherit", () => {
|
||||
const result = buildRegistryBase({
|
||||
...DEFAULT_CONFIG,
|
||||
fontHeading: "inter",
|
||||
})
|
||||
|
||||
expect(result.registryDependencies).not.toContain("font-heading-inter")
|
||||
expect(result.cssVars?.theme?.["--font-heading"]).toBe("var(--font-sans)")
|
||||
})
|
||||
|
||||
it("inherits the selected body font variable for non-sans body fonts", () => {
|
||||
const result = buildRegistryBase({
|
||||
...DEFAULT_CONFIG,
|
||||
font: "jetbrains-mono",
|
||||
fontHeading: "inherit",
|
||||
})
|
||||
|
||||
expect(result.registryDependencies).toContain("font-jetbrains-mono")
|
||||
expect(result.cssVars?.theme?.["--font-heading"]).toBe("var(--font-mono)")
|
||||
})
|
||||
|
||||
it("defaults chartColor to neutral when omitted", () => {
|
||||
const result = designSystemConfigSchema.parse({
|
||||
base: "radix",
|
||||
style: "nova",
|
||||
iconLibrary: "lucide",
|
||||
theme: "neutral",
|
||||
font: "inter",
|
||||
fontHeading: "inherit",
|
||||
menuAccent: "subtle",
|
||||
menuColor: "default",
|
||||
radius: "default",
|
||||
})
|
||||
|
||||
expect(result.chartColor).toBe("neutral")
|
||||
})
|
||||
|
||||
it("defaults pointer to false when omitted", () => {
|
||||
const result = designSystemConfigSchema.parse({
|
||||
base: "radix",
|
||||
style: "nova",
|
||||
iconLibrary: "lucide",
|
||||
theme: "neutral",
|
||||
font: "inter",
|
||||
fontHeading: "inherit",
|
||||
menuAccent: "subtle",
|
||||
menuColor: "default",
|
||||
radius: "default",
|
||||
})
|
||||
|
||||
expect(result.pointer).toBe(false)
|
||||
})
|
||||
|
||||
it("does not include pointer cursor css by default", () => {
|
||||
const result = buildRegistryBase(DEFAULT_CONFIG)
|
||||
|
||||
expect(
|
||||
result.css?.["@layer base"]?.[POINTER_CURSOR_SELECTOR]
|
||||
).toBeUndefined()
|
||||
})
|
||||
|
||||
it("includes pointer cursor css when pointer is enabled", () => {
|
||||
const result = buildRegistryBase({
|
||||
...DEFAULT_CONFIG,
|
||||
pointer: true,
|
||||
})
|
||||
|
||||
expect(result.css?.["@layer base"]?.[POINTER_CURSOR_SELECTOR]).toEqual({
|
||||
cursor: "pointer",
|
||||
})
|
||||
})
|
||||
|
||||
it("defaults chartColor to the selected theme when omitted", () => {
|
||||
const result = designSystemConfigSchema.parse({
|
||||
base: "base",
|
||||
style: "sera",
|
||||
iconLibrary: "lucide",
|
||||
baseColor: "taupe",
|
||||
theme: "taupe",
|
||||
font: "noto-sans",
|
||||
fontHeading: "playfair-display",
|
||||
menuAccent: "subtle",
|
||||
menuColor: "default",
|
||||
radius: "default",
|
||||
})
|
||||
|
||||
expect(result.chartColor).toBe("taupe")
|
||||
})
|
||||
|
||||
it("keeps the public schema style enum in sync with presets", () => {
|
||||
expect([...publicSchema.properties.style.enum].sort()).toEqual(
|
||||
[
|
||||
...legacyPublicSchemaStyles,
|
||||
...PRESETS.map((preset) => preset.name),
|
||||
].sort()
|
||||
)
|
||||
})
|
||||
|
||||
it("defines every base and style preset exactly once", () => {
|
||||
const presetNames = PRESETS.map((preset) => preset.name)
|
||||
const expectedPresetNames = BASES.flatMap((base) =>
|
||||
STYLES.map((style) => `${base.name}-${style.name}`)
|
||||
)
|
||||
|
||||
expect([...presetNames].sort()).toEqual([...expectedPresetNames].sort())
|
||||
expect(new Set(presetNames).size).toBe(presetNames.length)
|
||||
})
|
||||
|
||||
it("rejects chartColor values that are unavailable for the selected base color", () => {
|
||||
const result = designSystemConfigSchema.safeParse({
|
||||
base: "radix",
|
||||
style: "nova",
|
||||
iconLibrary: "lucide",
|
||||
baseColor: "neutral",
|
||||
theme: "neutral",
|
||||
chartColor: "stone",
|
||||
font: "inter",
|
||||
fontHeading: "inherit",
|
||||
menuAccent: "subtle",
|
||||
menuColor: "default",
|
||||
radius: "default",
|
||||
})
|
||||
|
||||
expect(result.success).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe("buildThemeForPreset", () => {
|
||||
it("builds a copyable registry theme item", () => {
|
||||
const result = buildThemeForPreset({
|
||||
...DEFAULT_CONFIG,
|
||||
baseColor: "taupe",
|
||||
theme: "taupe",
|
||||
chartColor: "taupe",
|
||||
menuAccent: "bold",
|
||||
radius: "large",
|
||||
})
|
||||
|
||||
expect(result).toMatchObject({
|
||||
$schema: "https://ui.shadcn.com/schema/registry-item.json",
|
||||
name: "taupe-taupe",
|
||||
type: "registry:theme",
|
||||
})
|
||||
expect(result.cssVars?.light?.radius).toBe("0.875rem")
|
||||
expect(result.cssVars?.light?.accent).toBe(result.cssVars?.light?.primary)
|
||||
expect(result.cssVars?.dark?.accent).toBe(result.cssVars?.dark?.primary)
|
||||
expect(result.cssVars?.light?.background).toBeDefined()
|
||||
expect(result.cssVars?.dark?.background).toBeDefined()
|
||||
expect(result.css).toBeUndefined()
|
||||
})
|
||||
})
|
||||
|
||||
describe("parseRegistryBaseParts", () => {
|
||||
it("returns undefined parts when only is omitted", () => {
|
||||
expect(parseRegistryBaseParts(null)).toEqual({
|
||||
success: true,
|
||||
parts: undefined,
|
||||
})
|
||||
})
|
||||
|
||||
it("normalizes and dedupes only values", () => {
|
||||
expect(parseRegistryBaseParts("theme,fonts,font")).toEqual({
|
||||
success: true,
|
||||
parts: ["theme", "font"],
|
||||
})
|
||||
})
|
||||
|
||||
it("rejects invalid only values", () => {
|
||||
const result = parseRegistryBaseParts("theme,colors")
|
||||
|
||||
expect(result.success).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe("buildPartialRegistryBase", () => {
|
||||
it("builds a sparse theme payload", () => {
|
||||
const result = buildPartialRegistryBase(
|
||||
{
|
||||
...DEFAULT_CONFIG,
|
||||
baseColor: "taupe",
|
||||
theme: "taupe",
|
||||
chartColor: "taupe",
|
||||
menuAccent: "bold",
|
||||
menuColor: "inverted",
|
||||
radius: "large",
|
||||
},
|
||||
["theme"]
|
||||
)
|
||||
|
||||
expect(result.type).toBe("registry:base")
|
||||
expect(result.extends).toBe("none")
|
||||
expect(result.config).toEqual({
|
||||
menuColor: "inverted",
|
||||
menuAccent: "bold",
|
||||
tailwind: {
|
||||
baseColor: "taupe",
|
||||
},
|
||||
})
|
||||
expect(result.cssVars?.light?.radius).toBe("0.875rem")
|
||||
expect(result.cssVars?.light).toBeDefined()
|
||||
expect(result.cssVars?.dark).toBeDefined()
|
||||
expect(result.registryDependencies).toBeUndefined()
|
||||
expect("dependencies" in result).toBe(false)
|
||||
})
|
||||
|
||||
it("builds a sparse font payload", () => {
|
||||
const result = buildPartialRegistryBase(
|
||||
{
|
||||
...DEFAULT_CONFIG,
|
||||
font: "noto-sans",
|
||||
fontHeading: "playfair-display",
|
||||
},
|
||||
["font"]
|
||||
)
|
||||
|
||||
expect(result.registryDependencies).toEqual([
|
||||
"font-noto-sans",
|
||||
"font-heading-playfair-display",
|
||||
])
|
||||
expect(result.cssVars).toBeUndefined()
|
||||
expect(result.config).toBeUndefined()
|
||||
})
|
||||
|
||||
it("adds a heading fallback when font heading inherits", () => {
|
||||
const result = buildPartialRegistryBase(
|
||||
{
|
||||
...DEFAULT_CONFIG,
|
||||
font: "jetbrains-mono",
|
||||
fontHeading: "inherit",
|
||||
},
|
||||
["font"]
|
||||
)
|
||||
|
||||
expect(result.registryDependencies).toEqual(["font-jetbrains-mono"])
|
||||
expect(result.cssVars?.theme?.["--font-heading"]).toBe("var(--font-mono)")
|
||||
})
|
||||
|
||||
it("merges combined partial payloads", () => {
|
||||
const result = buildPartialRegistryBase(
|
||||
{
|
||||
...DEFAULT_CONFIG,
|
||||
font: "figtree",
|
||||
},
|
||||
["theme", "font"]
|
||||
)
|
||||
|
||||
expect(result.config?.tailwind?.baseColor).toBe("neutral")
|
||||
expect(result.registryDependencies).toEqual(["font-figtree"])
|
||||
expect(result.cssVars?.light?.radius).toBe("0.625rem")
|
||||
expect(result.cssVars?.light).toBeDefined()
|
||||
expect(result.cssVars?.theme?.["--font-heading"]).toBe("var(--font-sans)")
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user