Skip to content

Commit

Permalink
Add All Variants story, fix active styles
Browse files Browse the repository at this point in the history
  • Loading branch information
jandrade committed Dec 19, 2024
1 parent bcb6ced commit b32a14a
Show file tree
Hide file tree
Showing 3 changed files with 213 additions and 71 deletions.
188 changes: 188 additions & 0 deletions __docs__/wonder-blocks-button/button-variants.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
import * as React from "react";
import {StyleSheet} from "aphrodite";
import {action} from "@storybook/addon-actions";
import type {Meta, StoryObj} from "@storybook/react";

import paperPlaneIcon from "@phosphor-icons/core/fill/paper-plane-tilt-fill.svg";
import {PropsFor, View} from "@khanacademy/wonder-blocks-core";
import {ThemeSwitcherContext} from "@khanacademy/wonder-blocks-theming";
import {color, semanticColor, spacing} from "@khanacademy/wonder-blocks-tokens";
import {HeadingLarge, LabelMedium} from "@khanacademy/wonder-blocks-typography";
import Button from "@khanacademy/wonder-blocks-button";

/**
* The following stories are used to generate the pseudo states for the
* Button component. This is only used for visual testing in Chromatic.
*/
export default {
title: "Packages / Button / All Variants",
parameters: {
docs: {
autodocs: false,
},
},
} as Meta;

type StoryComponentType = StoryObj<typeof Button>;

type ButtonProps = PropsFor<typeof Button>;

const sizes: Array<ButtonProps["size"]> = ["medium", "small", "large"];
const kinds: Array<ButtonProps["kind"]> = ["primary", "secondary", "tertiary"];

const colors: Array<ButtonProps["color"]> = ["default", "destructive"];

function VariantsGroup({
color = "default",
disabled = false,
label = "Button",
light,
size,
}: {
color?: ButtonProps["color"];
disabled?: ButtonProps["disabled"];
label?: string;
light: boolean;
size: ButtonProps["size"];
}) {
const theme = React.useContext(ThemeSwitcherContext);
const category = disabled ? "disabled" : color;

return (
<View
style={[
styles.variants,
light &&
(theme === "khanmigo"
? styles.darkKhanmigo
: styles.darkDefault),
]}
>
<LabelMedium style={[styles.label, light && styles.inverseLabel]}>
{size} / {category}
</LabelMedium>
{kinds.map((kind) => (
<React.Fragment key={kind}>
<Button
onClick={action("clicked")}
disabled={disabled}
kind={kind}
light={light}
color={color}
size={size}
>
{label}
</Button>
{/* startIcon */}
<Button
onClick={action("clicked")}
disabled={disabled}
kind={kind}
light={light}
color={color}
size={size}
startIcon={paperPlaneIcon}
>
{label}
</Button>
{/* endIcon */}
<Button
onClick={action("clicked")}
disabled={disabled}
kind={kind}
light={light}
color={color}
size={size}
endIcon={paperPlaneIcon}
>
{label}
</Button>
</React.Fragment>
))}
</View>
);
}

const KindVariants = ({light}: {light: boolean}) => {
return (
<>
{sizes.map((size) => (
<>
{colors.map((color) => (
<VariantsGroup
size={size}
color={color}
light={light}
/>
))}
<VariantsGroup size={size} disabled={true} light={light} />
</>
))}
</>
);
};

const VariantsByTheme = ({themeName = "Default"}: {themeName?: string}) => (
<View style={{marginBottom: spacing.large_24}}>
<HeadingLarge>{themeName} theme</HeadingLarge>
<KindVariants light={false} />
<KindVariants light={true} />
</View>
);

const AllVariants = () => (
<>
<VariantsByTheme />
<ThemeSwitcherContext.Provider value="khanmigo">
<VariantsByTheme themeName="Khanmigo" />
</ThemeSwitcherContext.Provider>
</>
);

export const Default: StoryComponentType = {
render: AllVariants,
};

export const Hover: StoryComponentType = {
render: AllVariants,
parameters: {pseudo: {hover: true}},
};

export const Focus: StoryComponentType = {
render: AllVariants,
parameters: {pseudo: {focusVisible: true}},
};

export const HoverFocus: StoryComponentType = {
name: "Hover + Focus",
render: AllVariants,
parameters: {pseudo: {hover: true, focusVisible: true}},
};

export const Active: StoryComponentType = {
render: AllVariants,
parameters: {pseudo: {active: true}},
};

const styles = StyleSheet.create({
darkDefault: {
backgroundColor: color.darkBlue,
},
darkKhanmigo: {
backgroundColor: color.eggplant,
},
variants: {
justifyContent: "flex-start",
padding: spacing.medium_16,
display: "flex",
flexDirection: "row",
alignItems: "center",
gap: spacing.xLarge_32,
},
label: {
minWidth: 150,
},
inverseLabel: {
color: semanticColor.text.inverse,
},
});
94 changes: 24 additions & 70 deletions __docs__/wonder-blocks-button/button.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import * as React from "react";
import {StyleSheet} from "aphrodite";
import type {Meta, StoryObj} from "@storybook/react";
import {expect, userEvent, within} from "@storybook/test";

import {MemoryRouter, Route, Switch} from "react-router-dom";

Expand All @@ -27,26 +26,6 @@ import ComponentInfo from "../../.storybook/components/component-info";
import ButtonArgTypes from "./button.argtypes";
import {ThemeSwitcherContext} from "@khanacademy/wonder-blocks-theming";

/**
* Reusable button component.
*
* Consisting of a [`ClickableBehavior`](#clickablebehavior) surrounding a
* `ButtonCore`. `ClickableBehavior` handles interactions and state changes.
* `ButtonCore` is a stateless component which displays the different states the
* `Button` can take.
*
* ### Usage
*
* ```tsx
* import Button from "@khanacademy/wonder-blocks-button";
*
* <Button
* onClick={(e) => console.log("Hello, world!")}
* >
* Hello, world!
* </Button>
* ```
*/
export default {
title: "Packages / Button",
component: Button,
Expand Down Expand Up @@ -80,61 +59,14 @@ export const Default: StoryComponentType = {
},
},
parameters: {
design: {
type: "figma",
url: "https://www.figma.com/file/VbVu3h2BpBhH80niq101MHHE/%F0%9F%92%A0-Main-Components?type=design&node-id=389-0&mode=design",
},
chromatic: {
// We already have screenshots of other stories that cover more of the button states
// We already have screenshots of other stories that cover more of
// the button states
disableSnapshot: true,
},
},
};

export const Tertiary: StoryComponentType = {
args: {
onClick: () => {},
kind: "tertiary",
testId: "test-button",
children: "Hello, world!",
},
play: async ({canvasElement}) => {
const canvas = within(canvasElement);

// Get HTML elements
const button = canvas.getByRole("button");
const innerLabel = canvas.getByTestId("test-button-inner-label");
const computedStyleLabel = getComputedStyle(innerLabel, ":after");

// Resting style
await expect(button).toHaveStyle(`color: ${color.blue}`);
await expect(button).toHaveTextContent("Hello, world!");

// Hover style
await userEvent.hover(button);
await expect(computedStyleLabel.height).toBe("2px");
await expect(computedStyleLabel.color).toBe("rgb(24, 101, 242)");

// TODO(WB-1808, somewhatabstract): This isn't working. I got it passing
// locally by calling `button.focus()` as well, but it was super flaky
// and never passed first time.
// Focus style
// const computedStyleButton = getComputedStyle(button);
// await fireEvent.focus(button);
// await expect(computedStyleButton.outlineColor).toBe(
// "rgb(24, 101, 242)",
// );
// await expect(computedStyleButton.outlineWidth).toBe("2px");

// // Active (mouse down) style
// // eslint-disable-next-line testing-library/prefer-user-event
// await fireEvent.mouseDown(button);
// await expect(innerLabel).toHaveStyle("color: rgb(27, 80, 179)");
// await expect(computedStyleLabel.color).toBe("rgb(27, 80, 179)");
// await expect(computedStyleLabel.height).toBe("2px");
},
};

export const styles: StyleDeclaration = StyleSheet.create({
row: {
flexDirection: "row",
Expand Down Expand Up @@ -203,6 +135,11 @@ Variants.parameters = {
story: "There are three kinds of buttons: `primary` (default), `secondary`, and `tertiary`.",
},
},
chromatic: {
// We already have screenshots of other stories that cover more of
// the button states
disableSnapshot: true,
},
};

export const WithColor: StoryComponentType = {
Expand Down Expand Up @@ -324,6 +261,11 @@ Dark.parameters = {
story: "Buttons on a `darkBlue` background should set `light` to `true`.",
},
},
chromatic: {
// We already have screenshots of other stories that cover more of
// the button states
disableSnapshot: true,
},
};

const kinds = ["primary", "secondary", "tertiary"] as const;
Expand Down Expand Up @@ -539,6 +481,11 @@ Size.parameters = {
story: "Buttons have a size that's either `medium` (default), `small`, or `large`.",
},
},
chromatic: {
// We already have screenshots of other stories that cover more of
// the button states
disableSnapshot: true,
},
};

export const Spinner: StoryComponentType = () => (
Expand Down Expand Up @@ -789,4 +736,11 @@ export const KhanmigoTheme: StoryComponentType = {
</ThemeSwitcherContext.Provider>
);
},
parameters: {
chromatic: {
// We already have screenshots of other stories that cover more of
// the button states
disableSnapshot: true,
},
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -376,7 +376,7 @@ export const _generateStyles = (
};

const activePressedStyling = {
backgroundColor: activeColor,
background: light ? fadedColor : activeColor,
outlineColor: light ? fadedColor : activeColor,
};

Expand Down

0 comments on commit b32a14a

Please sign in to comment.