Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ButtonIcon): adding support for labelLeft #326

Merged
merged 1 commit into from
Feb 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 45 additions & 15 deletions packages/documentation/src/Components/ButtonIcon.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import type { Story } from "@ladle/react";
import { ButtonIcon, IconEdit, IconSettings } from "@versini/ui-components";
import {
ButtonIcon,
IconEdit,
IconNext,
IconPrevious,
IconSettings,
} from "@versini/ui-components";

export const Basic: Story<any> = (args) => (
<div className="flex flex-wrap">
Expand All @@ -19,20 +25,44 @@ export const Basic: Story<any> = (args) => (
);

export const WithLabel: Story<any> = (args) => (
<div className="flex flex-wrap">
<ButtonIcon labelRight="Settings" {...args}>
<IconSettings decorative />
</ButtonIcon>
<ButtonIcon labelRight="Settings" {...args}>
<IconSettings decorative />
</ButtonIcon>
<ButtonIcon labelRight="Edit" {...args}>
<IconEdit decorative className="h-3 w-3" />
</ButtonIcon>
<ButtonIcon labelRight="Edit" {...args}>
<IconEdit decorative />
</ButtonIcon>
</div>
<>
<div className="flex flex-wrap">
<ButtonIcon labelRight="Settings" {...args}>
<IconSettings decorative />
</ButtonIcon>
<ButtonIcon labelRight="Settings" {...args}>
<IconSettings decorative />
</ButtonIcon>
<ButtonIcon labelRight="Edit" {...args}>
<IconEdit decorative className="h-3 w-3" />
</ButtonIcon>
<ButtonIcon labelRight="Edit" {...args}>
<IconEdit decorative />
</ButtonIcon>
</div>
<div className="mt-2 flex flex-wrap">
<ButtonIcon labelLeft="Settings" {...args}>
<IconSettings decorative />
</ButtonIcon>
<ButtonIcon labelLeft="Settings" {...args}>
<IconSettings decorative />
</ButtonIcon>
<ButtonIcon labelLeft="Edit" {...args}>
<IconEdit decorative className="h-3 w-3" />
</ButtonIcon>
<ButtonIcon labelLeft="Edit" {...args}>
<IconEdit decorative />
</ButtonIcon>
</div>
<div className="mt-2 flex flex-wrap">
<ButtonIcon labelRight="Previous page" {...args}>
<IconPrevious decorative monotone />
</ButtonIcon>
<ButtonIcon labelLeft="Next page" {...args}>
<IconNext decorative monotone />
</ButtonIcon>
</div>
</>
);

export default {
Expand Down
3 changes: 3 additions & 0 deletions packages/ui-components/src/components/Button/ButtonIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export const ButtonIcon = React.forwardRef<HTMLButtonElement, ButtonIconProps>(
label,
size = "medium",
labelRight,
labelLeft,
spacing,

...otherProps
Expand All @@ -36,6 +37,7 @@ export const ButtonIcon = React.forwardRef<HTMLButtonElement, ButtonIconProps>(
noBorder,
size,
labelRight,
labelLeft,
spacing,
});

Expand All @@ -48,6 +50,7 @@ export const ButtonIcon = React.forwardRef<HTMLButtonElement, ButtonIconProps>(
aria-label={ariaLabel || label}
{...otherProps}
>
{labelLeft && <span className="pr-2">{labelLeft}</span>}
aversini marked this conversation as resolved.
Show resolved Hide resolved
{children}
{labelRight && <span className="pl-2">{labelRight}</span>}
</button>
Expand Down
4 changes: 4 additions & 0 deletions packages/ui-components/src/components/Button/ButtonTypes.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@ export type ButtonIconProps = {
* The label to show next to the icon.
*/
label?: string;
/**
* The label to show to the left of the icon.
*/
labelLeft?: string;
/**
* The label to show to the right of the icon.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ describe("ButtonIcon (exceptions)", () => {
});

describe("ButtonIcon modifiers", () => {
it("should render a default button", async () => {
it("should render a default button icon", async () => {
render(
<ButtonIcon>
<IconSettings />
Expand All @@ -28,7 +28,7 @@ describe("ButtonIcon modifiers", () => {
]);
});

it("should render a dark button", async () => {
it("should render a dark button icon", async () => {
render(
<ButtonIcon kind="dark">
<IconSettings />
Expand All @@ -40,7 +40,7 @@ describe("ButtonIcon modifiers", () => {
expect(buttonClass).toContain("bg-action-dark");
});

it("should render a light button", async () => {
it("should render a light button icon", async () => {
render(
<ButtonIcon kind="light">
<IconSettings />
Expand All @@ -52,7 +52,7 @@ describe("ButtonIcon modifiers", () => {
expect(buttonClass).toContain("bg-action-light");
});

it("should render a disabled dark button", async () => {
it("should render a disabled dark button icon", async () => {
render(
<ButtonIcon kind="dark" disabled>
<IconSettings />
Expand All @@ -64,7 +64,7 @@ describe("ButtonIcon modifiers", () => {
expect(buttonClass).toContain("disabled:cursor-not-allowed");
});

it("should render a disabled light button", async () => {
it("should render a disabled light button icon", async () => {
render(
<ButtonIcon kind="light" disabled>
<IconSettings />
Expand All @@ -76,7 +76,7 @@ describe("ButtonIcon modifiers", () => {
expect(buttonClass).toContain("disabled:cursor-not-allowed");
});

it("should render a fullWidth button", async () => {
it("should render a fullWidth button icon", async () => {
render(
<ButtonIcon fullWidth>
<IconSettings />
Expand All @@ -86,7 +86,7 @@ describe("ButtonIcon modifiers", () => {
expect(button.className).toContain("w-full");
});

it("should render a size small button", async () => {
it("should render a size small button icon", async () => {
render(
<ButtonIcon size="small">
<IconSettings />
Expand All @@ -96,7 +96,7 @@ describe("ButtonIcon modifiers", () => {
expectToHaveClasses(button, ["h-6", "w-6", "p-0"]);
});

it("should render a size small button with a label on the right", async () => {
it("should render a size small button icon with a label on the right", async () => {
render(
<ButtonIcon size="small" labelRight="Settings">
<IconSettings decorative />
Expand All @@ -105,16 +105,22 @@ describe("ButtonIcon modifiers", () => {
const button = await screen.findByRole("button");
const label = await screen.findByText("Settings");
expect(label).toBeDefined();
expectToHaveClasses(button, [
"h-6",
"pl-2",
"pr-4",
"text-sm",
"font-medium",
]);
expectToHaveClasses(button, ["h-6", "px-4", "text-sm", "font-medium"]);
});

it("should render a size medium button", async () => {
it("should render a size small button icon with a label on the left", async () => {
render(
<ButtonIcon size="small" labelLeft="Settings">
<IconSettings decorative />
</ButtonIcon>,
);
const button = await screen.findByRole("button");
const label = await screen.findByText("Settings");
expect(label).toBeDefined();
expectToHaveClasses(button, ["h-6", "px-4", "text-sm", "font-medium"]);
});

it("should render a size medium button icon", async () => {
render(
<ButtonIcon size="medium">
<IconSettings />
Expand All @@ -124,7 +130,7 @@ describe("ButtonIcon modifiers", () => {
expectToHaveClasses(button, ["h-8", "w-8", "p-1"]);
});

it("should render a size medium button with a label on the right", async () => {
it("should render a size medium button icon with a label on the right", async () => {
render(
<ButtonIcon size="medium" labelRight="Settings">
<IconSettings decorative />
Expand All @@ -133,16 +139,22 @@ describe("ButtonIcon modifiers", () => {
const button = await screen.findByRole("button");
const label = await screen.findByText("Settings");
expect(label).toBeDefined();
expectToHaveClasses(button, [
"h-8",
"pl-2",
"pr-4",
"text-base",
"font-medium",
]);
expectToHaveClasses(button, ["h-8", "px-4", "text-base", "font-medium"]);
});

it("should render a size large button", async () => {
it("should render a size medium button icon with a label on the left", async () => {
render(
<ButtonIcon size="medium" labelLeft="Settings">
<IconSettings decorative />
</ButtonIcon>,
);
const button = await screen.findByRole("button");
const label = await screen.findByText("Settings");
expect(label).toBeDefined();
expectToHaveClasses(button, ["h-8", "px-4", "text-base", "font-medium"]);
});

it("should render a size large button icon", async () => {
render(
<ButtonIcon size="large">
<IconSettings />
Expand All @@ -152,7 +164,7 @@ describe("ButtonIcon modifiers", () => {
expectToHaveClasses(button, ["h-12", "w-12", "p-2"]);
});

it("should render a size large button with a label on the right", async () => {
it("should render a size large button icon with a label on the right", async () => {
render(
<ButtonIcon size="large" labelRight="Settings">
<IconSettings decorative />
Expand All @@ -161,12 +173,18 @@ describe("ButtonIcon modifiers", () => {
const button = await screen.findByRole("button");
const label = await screen.findByText("Settings");
expect(label).toBeDefined();
expectToHaveClasses(button, [
"h-12",
"pl-2",
"pr-4",
"text-lg",
"font-medium",
]);
expectToHaveClasses(button, ["h-12", "px-4", "text-lg", "font-medium"]);
});

it("should render a size large button icon with a label on the left", async () => {
render(
<ButtonIcon size="large" labelLeft="Settings">
<IconSettings decorative />
</ButtonIcon>,
);
const button = await screen.findByRole("button");
const label = await screen.findByText("Settings");
expect(label).toBeDefined();
expectToHaveClasses(button, ["h-12", "px-4", "text-lg", "font-medium"]);
});
});
27 changes: 15 additions & 12 deletions packages/ui-components/src/components/Button/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ type getButtonClassesProps = {
type: typeof TYPE_BUTTON | typeof TYPE_ICON | typeof TYPE_LINK;

className?: string;
labelLeft?: string;
labelRight?: string;
slim?: boolean;
} & SpacingProps;
Expand All @@ -28,9 +29,11 @@ const getButtonSizesClasses = ({
slim,
size,
labelRight,
labelLeft,
}: {
size: string;
type: string;
labelLeft?: string;
labelRight?: string;
slim?: boolean;
}) => {
Expand All @@ -55,17 +58,16 @@ const getButtonSizesClasses = ({

case TYPE_ICON:
return clsx("inline-flex items-center justify-center", {
"h-6 w-6 p-0": (size === "small" || slim) && !labelRight,
"h-6 pl-2 pr-4 text-sm font-medium":
(size === "small" || slim) && labelRight,

"h-8 w-8 p-1": size === "medium" && !slim && !labelRight,
"h-8 pl-2 pr-4 text-base font-medium":
size === "medium" && !slim && labelRight,

"h-12 w-12 p-2": size === "large" && !slim && !labelRight,
"h-12 pl-2 pr-4 text-lg font-medium":
size === "large" && !slim && labelRight,
"h-6 w-6 p-0": (size === "small" || slim) && !(labelRight || labelLeft),
"h-6 px-4 text-sm font-medium":
(size === "small" || slim) && (labelRight || labelLeft),
"h-8 w-8 p-1": size === "medium" && !slim && !(labelRight || labelLeft),
"h-8 px-4 text-base font-medium":
size === "medium" && !slim && (labelRight || labelLeft),
"h-12 w-12 p-2":
size === "large" && !slim && !(labelRight || labelLeft),
"h-12 px-4 text-lg font-medium":
size === "large" && !slim && (labelRight || labelLeft),
});
}
};
Expand Down Expand Up @@ -128,6 +130,7 @@ export const getButtonClasses = ({
size,
noBorder,
labelRight,
labelLeft,
spacing,
}: getButtonClassesProps) => {
return raw
Expand All @@ -137,7 +140,7 @@ export const getButtonClasses = ({
className,
getSpacing(spacing),
getButtonBaseClasses({ kind }),
getButtonSizesClasses({ type, slim, size, labelRight }),
getButtonSizesClasses({ type, slim, size, labelRight, labelLeft }),
getButtonBorderClasses({ kind, noBorder }),
getButtonFocusClasses({ focus }),
getButtonHoverClasses({ kind, disabled }),
Expand Down
Loading