(react) implement Button with official tokens

Now that we have all the official design tokens we can use them
to build the button component in various colors matching the
design system's ones.
This commit is contained in:
Nathan Vasse
2023-01-12 17:41:14 +01:00
committed by NathanVss
parent 8f5e546f04
commit 7b6d130d7d
6 changed files with 137 additions and 18 deletions

View File

@@ -1,10 +1,72 @@
.c__button {
background-image: var(--c--theme--colors--primary-gradient);
padding: 8px 30px;
border-radius: var(--c--components--button--border-radius);
display: flex;
align-items: center;
border: none;
box-shadow: var(--c--components--button--shadow);
color: white;
font-weight: bold;
cursor: pointer;
transition: background-color var(--c--theme--transitions--duration) var(--c--theme--transitions--ease-out);
padding: 0 var(--c--theme--spacings--s);
height: var(--c--components--button--height);
border-radius: var(--c--components--button--border-radius);
font-size: var(--c--components--button--font-size);
font-weight: var(--c--components--button--font-weight);
&--primary {
background-color: var(--c--theme--colors--primary-500);
color: var(--c--theme--colors--primary-text);
&:hover {
background-color: var(--c--theme--colors--primary-600);
}
&:active {
background-color: var(--c--theme--colors--primary-500);
}
}
&--secondary {
background-color: var(--c--theme--colors--secondary-500);
color: var(--c--theme--colors--secondary-text);
&:hover {
background-color: var(--c--theme--colors--secondary-600);
}
&:active {
background-color: var(--c--theme--colors--secondary-500);
}
}
&--tertiary {
background-color: transparent;
color: var(--c--theme--colors--greyscale-800);
&:hover {
color: var(--c--theme--colors--greyscale-900);
}
&:active {
color: var(--c--theme--colors--greyscale-800);
}
}
&--danger {
background-color: var(--c--theme--colors--danger-500);
color: var(--c--theme--colors--danger-text);
&:hover {
background-color: var(--c--theme--colors--danger-600);
}
&:active {
background-color: var(--c--theme--colors--danger-500);
}
}
&[disabled] {
cursor: not-allowed;
background-color: var(--c--theme--colors--greyscale-200);
color: var(--c--theme--colors--greyscale-400);
}
}

View File

@@ -1,7 +1,8 @@
import { describe, expect, it } from "vitest";
import { render, screen } from "@testing-library/react";
import { act, render, screen, waitFor } from "@testing-library/react";
import React from "react";
import { buildTheme, loadTokens } from "tests/Theme";
import userEvent from "@testing-library/user-event";
import { Button } from "./index";
describe("<Button/>", () => {
@@ -11,10 +12,33 @@ describe("<Button/>", () => {
expect(button.classList.contains("c__button")).toBe(true);
});
it("call onClick when click occurs", async () => {
const user = userEvent.setup();
const handleClick = vi.fn();
render(<Button onClick={handleClick}>Test button</Button>);
const button = screen.getByRole("button", { name: "Test button" });
expect(handleClick).not.toBeCalled();
user.click(button);
await waitFor(() => expect(handleClick).toHaveBeenCalled());
});
it("does not call onClick when click occurs on a disabled button", async () => {
const user = userEvent.setup();
const handleClick = vi.fn();
render(
<Button onClick={handleClick} disabled={true}>
Test button
</Button>
);
const button = screen.getByRole("button", { name: "Test button" });
expect(handleClick).not.toBeCalled();
await act(async () => user.click(button));
expect(handleClick).not.toHaveBeenCalled();
});
it("uses custom token", async () => {
await buildTheme();
const tokens = await loadTokens();
expect(tokens.components.button["border-radius"]).toBeDefined();
expect(tokens.components.button.shadow).toBeDefined();
});
});

View File

@@ -9,7 +9,27 @@ export default {
const Template: ComponentStory<typeof Button> = (args) => <Button {...args} />;
export const Default = Template.bind({});
Default.args = {
children: "Amazing button",
export const Primary = Template.bind({});
Primary.args = {
children: "Label",
color: "primary",
};
export const Secondary = Template.bind({});
Secondary.args = {
children: "Label",
color: "secondary",
};
export const Tertiary = Template.bind({});
Tertiary.args = {
children: "Label",
color: "tertiary",
};
export const Disabled = Template.bind({});
Disabled.args = {
children: "Label",
color: "primary",
disabled: true,
};

View File

@@ -1,7 +1,13 @@
import React, { PropsWithChildren } from "react";
import React, { ButtonHTMLAttributes } from "react";
interface Props extends PropsWithChildren {}
interface Props extends ButtonHTMLAttributes<HTMLButtonElement> {
color?: "primary" | "secondary" | "tertiary";
}
export const Button = ({ children }: Props) => {
return <button className="c__button">{children}</button>;
export const Button = ({ children, color = "primary", ...props }: Props) => {
return (
<button className={"c__button c__button--" + color} {...props}>
{children}
</button>
);
};

View File

@@ -2,7 +2,9 @@ import { DefaultTokens } from "@openfun/cunningham-tokens";
export const tokens = (defaults: DefaultTokens) => {
return {
"border-radius": "5px",
shadow: "0px 0px 10px 1px " + defaults.theme.colors.primary + ";",
"border-radius": "2px",
height: "48px",
"font-size": defaults.theme.typo.l,
"font-weight": defaults.theme.typo.medium,
};
};

View File

@@ -1,8 +1,13 @@
@import "@fontsource/roboto/100";
@import "@fontsource/roboto/300";
@import "@fontsource/roboto/400";
@import "@fontsource/roboto/500";
@import "@fontsource/roboto/700";
@import "@fontsource/roboto/900";
@import "cunningham-tokens";
@import '@openfun/cunningham-tokens/default-tokens';
@import './components/Button';
@import './components/Button';
* {
font-family: var(--c--theme--typo--font-base);
}