diff --git a/package.json b/package.json index 20c870e..28790f9 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,8 @@ "postpack": "clean-package restore", "lint": "eslint -c .eslintrc.json ./src/**/*.{ts,tsx}", "lint:fix": "eslint --fix -c .eslintrc.json ./src/**/*.{ts,tsx}", - "test": "jest --verbose" + "test": "jest --verbose", + "test:watch": "jest --watch --no-verbose" }, "dependencies": { "tailwind-merge": "^2.2.0" diff --git a/src/__tests__/tv.test.ts b/src/__tests__/tv.test.ts index e4c4dcd..a8c98f9 100644 --- a/src/__tests__/tv.test.ts +++ b/src/__tests__/tv.test.ts @@ -2962,6 +2962,37 @@ describe("Tailwind Variants (TV) - Extends", () => { expect(base()).toHaveClass(["menuBase", "menu"]); expect(title()).toHaveClass(["title"]); }); + + it("should support multi-level extends", () => { + const themeButton = tv({ + base: "font-medium", + variants: { + color: { + primary: "text-blue-500", + }, + disabled: { + true: "opacity-50", + }, + }, + compoundVariants: [ + { + color: "primary", + disabled: true, + class: "bg-black", + }, + ], + defaultVariants: { + color: "primary", + disabled: true, + }, + }); + + const appButton = tv({extend: themeButton}); + const button = tv({extend: appButton}); + + expect(appButton()).toHaveClass("font-medium text-blue-500 opacity-50 bg-black"); + expect(button()).toHaveClass("font-medium text-blue-500 opacity-50 bg-black"); + }); }); describe("Tailwind Variants (TV) - Tailwind Merge", () => { diff --git a/src/index.js b/src/index.js index 8b0e806..62c8765 100644 --- a/src/index.js +++ b/src/index.js @@ -69,7 +69,7 @@ export const tv = (options, configProp) => { extend = null, slots: slotProps = {}, variants: variantsProps = {}, - compoundVariants = [], + compoundVariants: compoundVariantsProps = [], compoundSlots = [], defaultVariants: defaultVariantsProps = {}, } = options; @@ -109,6 +109,11 @@ export const tv = (options, configProp) => { isEmptyObject(componentSlots) ? {base: options?.base} : componentSlots, ); + // merge compoundVariants with the "extended" compoundVariants + const compoundVariants = isEmptyObject(extend?.compoundVariants) + ? compoundVariantsProps + : flatMergeArrays(extend?.compoundVariants, compoundVariantsProps); + const component = (props) => { if (isEmptyObject(variants) && isEmptyObject(slotProps) && isExtendedSlotsEmpty) { return cn(base, props?.class, props?.className)(config); @@ -322,15 +327,8 @@ export const tv = (options, configProp) => { return result; }; - const getCompoundVariantClassNames = (slotProps) => { - const cvValues = getCompoundVariantsValue(compoundVariants, slotProps); - const ecvValues = getCompoundVariantsValue(extend?.compoundVariants, slotProps); - - return flatMergeArrays(ecvValues, cvValues); - }; - const getCompoundVariantClassNamesBySlot = (slotProps) => { - const compoundClassNames = getCompoundVariantClassNames(slotProps); + const compoundClassNames = getCompoundVariantsValue(compoundVariants, slotProps); if (!Array.isArray(compoundClassNames)) { return compoundClassNames; @@ -422,7 +420,7 @@ export const tv = (options, configProp) => { return cn( base, getVariantClassNames(), - getCompoundVariantClassNames(), + getCompoundVariantsValue(compoundVariants), props?.class, props?.className, )(config);