Skip to content

Commit

Permalink
Merge pull request #14 from busbud/fix/return-empty-when-slot-no-defi…
Browse files Browse the repository at this point in the history
…ned-but-accessed

fix: fix when slot accessed but not defined
  • Loading branch information
flozero authored Aug 27, 2024
2 parents b1e8f21 + e6eced4 commit e86d224
Show file tree
Hide file tree
Showing 14 changed files with 293 additions and 91 deletions.
68 changes: 48 additions & 20 deletions packages/core/src/tailwind-buddy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,22 @@ export function extractValue(value: any, slot: string) {
return undefined;
}

function transformConditionsToBeUsable(obj: any) {
const acc: any = {};
function buildArrayWithResponsivesFromDefault(obj: Record<string, any>): any[] {
const acc: any[] = [];
for (const [key, value] of Object.entries(obj)) {
acc[key] = typeof value === "object" ? value : { initial: value };
if (value === undefined || value === null)
throw new Error("value is missing");
else if (typeof value === "object") {
if (typeof value["initial"] === "undefined") {
throw new Error(
`initial value is missing for the variant ${key} ${value}`
);
} else {
acc.push([key, value]);
}
} else {
acc.push([key, { initial: value }]);
}
}
return acc;
}
Expand All @@ -103,35 +115,48 @@ export const compose: TB = (variantDefinition) => (): any => {
(acc, slot: string) => {
acc[slot] = (props: any) => {
const className = props?.className || "";
// remove className afterward as we will use the props for compounds evaluations
if (props?.className) delete props.className;

// final list of classes to return we are going to populate as we go
const classesToReturn: string[] = [];

const slotDefaultClass = variantDefinition.slots[slot] || "";
const mergedPropsWithDefaults = {
if (slotDefaultClass) classesToReturn.push(slotDefaultClass);

const mergedPropsWithDefaultsProperties = {
...variantDefinition.defaultVariants,
...props,
};

if (slotDefaultClass) classesToReturn.push(slotDefaultClass);

const transformed = transformConditionsToBeUsable(
mergedPropsWithDefaults
);
/**
* build the responsive object with the initial value
* if the value is not an object, we assume it's a string and we wrap it in an object
* [[mergedPropKey]: { initial: value, ...otherResponsive }]
* */
const responsiveArrayFromDefaults =
buildArrayWithResponsivesFromDefault(
mergedPropsWithDefaultsProperties
);
// final trasnformed responsive object
const transformed_breakpoints: any = { initial: {} };
const transformed_entries = Object.entries(
transformed as { [key: string]: any }
);

for (const [key, value] of transformed_entries) {
const variant = variantDefinition.variants![key];
//
for (const [variantKey, value] of responsiveArrayFromDefaults) {
// retrieve variant from definition. Continue when the key is not a variant but a props
const variant = variantDefinition.variants![variantKey];
if (!variant) continue;

const variants_decomposed = Object.entries(
value as { [key: string]: any }
);
for (const [responsiveKey, val] of variants_decomposed) {
const variantValue = variant[val];
const variantsDecomposedFromDefault: [string | "initial", string][] =
Object.entries(value);

for (const [
responsiveKey,
variantSubKey,
] of variantsDecomposedFromDefault) {
const variantValue = variant[variantSubKey];
const classStr = extractValue(variantValue, slot);

if (classStr) {
if (responsiveKey === "initial") {
classesToReturn.push(classStr);
Expand All @@ -142,11 +167,14 @@ export const compose: TB = (variantDefinition) => (): any => {
classesToReturn.push(`${responsiveKey}:${v}`);
});
}
} else {
transformed_breakpoints[responsiveKey] =
transformed_breakpoints[responsiveKey] || {};
}
}
}

for (const [key, value] of transformed_entries) {
for (const [key, value] of responsiveArrayFromDefaults) {
const isOnlyInitial =
Object.keys(value).length === 1 && value["initial"];
if (isOnlyInitial) {
Expand Down
29 changes: 29 additions & 0 deletions packages/core/tests/configs/setup-for-slot-not-used.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { describe, expect, test } from "vitest";
import { buttonVariants } from "../setup/setup-for-slot-not-used";

describe("test when slot is not defined but others are in variant", () => {
const { icon, root, label } = buttonVariants;

test("should not have error and do not have extra string", () => {
const expected = "bg-red-500 bg-purple-300";
expect(
icon({
appearance: "primary",
size: {
initial: "xs",
lg: "md",
},
})
).toBe(expected);
});

test("default root", () => {
const expected = "bg-red-100 bg-purple-100 bg-orange-100";
expect(root()).toBe(expected);
});

test("default label", () => {
const expected = "bg-red-300 bg-purple-100 bg-orange-300";
expect(label()).toBe(expected);
});
});
41 changes: 41 additions & 0 deletions packages/core/tests/setup/setup-for-slot-not-used.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { compose } from "../../src/tailwind-buddy";

export const buttonVariants = compose({
slots: {
root: /** @tw */ "bg-red-100",
label: /** @tw */ "bg-red-300",
icon: /** @tw */ "bg-red-500",
},
variants: {
appearance: {
default: /** @tw */ "bg-purple-100",
primary: /** @tw */ "bg-purple-300",
destructive: /** @tw */ "bg-purple-500",
},
size: {
xs: {
root: /** @tw */ "bg-yellow-100",
label: /** @tw */ "bg-yellow-300",
},
sm: {
root: /** @tw */ "bg-blue-100",
label: /** @tw */ "bg-blue-300",
},
md: {
root: /** @tw */ "bg-orange-100",
label: /** @tw */ "bg-orange-300",
},
},
/** Sets the button variant. */
variant: {
contained: /** @tw */ "",
text: /** @tw */ "",
},
},
defaultVariants: {
appearance: "default",
size: "md",
variant: "contained",
},
responsiveVariants: ["size"],
})();

This file was deleted.

16 changes: 14 additions & 2 deletions packages/sandbox/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Dumb, Label } from "ui-kit";
import { Dumb, Label, Button, SearchIcon } from "ui-kit";

function App() {
return (
<div className="gap-8 flex flex-col">
<div className="gap-8 grid grid-cols-3">
<Label
size="large"
disabled={true}
Expand All @@ -12,6 +12,18 @@ function App() {
Label
</Label>

<Button
className="h-full w-full rounded-bl-none rounded-tl-none xl:rounded-bl-none xl:rounded-tl-none"
appearance="primary"
iconStart={<SearchIcon />}
size={{
initial: "xs",
lg: "md",
}}
>
asdas
</Button>

<Dumb />
</div>
);
Expand Down
15 changes: 6 additions & 9 deletions packages/sandbox/tailwind.config.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
import twConfig from "ui-kit/tailwind.config"
import type { Config } from "tailwindcss";
import tailwindConfig from "ui-kit/tailwind.config";

/** @type {import('tailwindcss').Config} */
export default {
content: [
"./index.html",
"./src/**/*.{js,ts,jsx,tsx}",
"./node_modules/ui-kit/dist/uikit.js"
"./node_modules/ui-kit/dist/uikit.js",
],
theme: {
extend: twConfig.theme.extend
},
safelist: twConfig.safelist
}

theme: tailwindConfig.theme,
safelist: tailwindConfig.safelist,
} satisfies Config;
46 changes: 46 additions & 0 deletions packages/ui/src/components/Button/Button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import React, { ReactNode } from "react";
import { type ButtonProps, buttonVariants } from "./Button.variants";

const defaultVariants = buttonVariants.definition().defaultVariants;

export const Button: React.FC<ButtonProps> = ({
appearance = defaultVariants.appearance,
children,
className,
iconEnd: iconEndProp,
iconStart: iconStartProp,
size = "md",
variant = defaultVariants.variant,
...restProps
}) => {
const { root, icon, label } = buttonVariants;

const iconContainer = (children: ReactNode) => (
<span
className={icon({
appearance,
size,
variant,
})}
>
{children}
</span>
);

const iconStart = iconStartProp && iconContainer(iconStartProp);

return (
<button
className={root({
appearance,
className,
size,
variant,
})}
{...restProps}
>
{iconStart}
<span className={label({ size })}>{children}</span>
</button>
);
};
7 changes: 7 additions & 0 deletions packages/ui/src/components/Button/Button.types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { ReactNode } from "react";

export interface ButtonBaseProps {
iconEnd?: ReactNode;
iconStart?: ReactNode;
children: ReactNode;
}
45 changes: 45 additions & 0 deletions packages/ui/src/components/Button/Button.variants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { compose, VariantsProps } from "@busbud/tailwind-buddy";
import { ButtonBaseProps } from "@/components/Button/Button.types.ts";

export const buttonVariants = compose({
slots: {
root: /** @tw */ "bg-red-100",
label: /** @tw */ "bg-red-300",
icon: /** @tw */ "bg-red-500",
},
variants: {
appearance: {
default: "bg-purple-100",
primary: "bg-purple-300",
destructive: "bg-purple-500",
},
size: {
xs: {
root: "bg-yellow-100",
label: "bg-yellow-300",
},
sm: {
root: "bg-blue-100",
label: "bg-blue-300",
},
md: {
root: "bg-orange-100",
label: "bg-orange-300",
},
},
/** Sets the button variant. */
variant: {
contained: "",
text: "",
},
},
defaultVariants: {
appearance: "default",
size: "md",
variant: "contained",
},
responsiveVariants: ["size"],
})<ButtonBaseProps>();

export type ButtonProps = VariantsProps<typeof buttonVariants> &
ButtonBaseProps;
12 changes: 12 additions & 0 deletions packages/ui/src/components/Icons/Search/Search.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export const SearchIcon = () => {
return (
<svg viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M17.8 18.32a5.9 5.9 0 1 1 .51-.51l-.5.5zm1.24 2.84a8.9 8.9 0 1 1 2.12-2.12l5.74 5.74-2.12 2.12-5.74-5.74z"
fill="currentColor"
/>
</svg>
);
};
1 change: 1 addition & 0 deletions packages/ui/src/components/Icons/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { SearchIcon } from "./Search/Search";
5 changes: 5 additions & 0 deletions packages/ui/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
export { Label } from "./components/Label/Label";
export type { LabelProps } from "./components/Label/Label.variants";
export { Dumb } from "./components/Dumb";

export { Button } from "./components/Button/Button";
export { type ButtonProps } from "./components/Button/Button.variants";

export { SearchIcon } from "./components/Icons/Search/Search";
Loading

0 comments on commit e86d224

Please sign in to comment.