From 2d88ab117f312f7f0acdf1c39f30bf6b1b471cee Mon Sep 17 00:00:00 2001 From: Luc Perkins Date: Wed, 11 Jun 2025 12:23:32 -0700 Subject: [PATCH 1/3] Add button group component --- src/Button/index.scss | 5 +- src/ButtonGroup/ButtonGroup.stories.tsx | 87 +++++++++++++++++++++++++ src/ButtonGroup/index.scss | 14 ++++ src/ButtonGroup/index.tsx | 32 +++++++++ src/sass/_tokens.scss | 6 ++ 5 files changed, 141 insertions(+), 3 deletions(-) create mode 100644 src/ButtonGroup/ButtonGroup.stories.tsx create mode 100644 src/ButtonGroup/index.scss create mode 100644 src/ButtonGroup/index.tsx diff --git a/src/Button/index.scss b/src/Button/index.scss index 1c49e77..a451a63 100644 --- a/src/Button/index.scss +++ b/src/Button/index.scss @@ -8,10 +8,9 @@ align-self: self-start; // Prevents the button from occupying all available space inside a div display: inline-flex; flex-direction: row; + box-sizing: border-box; - margin: 0; border: 0; - border-radius: 0.5rem; font-family: map.get(tokens.$fonts, sans); @@ -37,7 +36,7 @@ } &.button--outlined { - border: 2px solid $color; + outline: 2px solid $color; background-color: map.get(tokens.$brand, white); color: $color; diff --git a/src/ButtonGroup/ButtonGroup.stories.tsx b/src/ButtonGroup/ButtonGroup.stories.tsx new file mode 100644 index 0000000..29376ec --- /dev/null +++ b/src/ButtonGroup/ButtonGroup.stories.tsx @@ -0,0 +1,87 @@ +import type { Meta, StoryObj } from "@storybook/react"; + +import ButtonGroup from "./"; +import { Button } from ".."; +import { action } from "@storybook/addon-actions"; + +const meta = { + title: "Atoms/ButtonGroup", + component: ButtonGroup, + parameters: { + layout: "centered", + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +// Make clicks visible in the inspector +const onClick = action("click"); + +export const Small: Story = { + args: { + size: "sm", + children: ( + <> + + + + + ), + }, +}; + +export const Base: Story = { + args: { + size: "base", + children: ( + <> + + + + + ), + }, +}; + +export const Large: Story = { + args: { + size: "lg", + children: ( + <> + + + + + ), + }, +}; + +export const SmallButtonsWithLargeGap: Story = { + args: { + size: "lg", + children: ( + <> + + + + + ), + }, +}; diff --git a/src/ButtonGroup/index.scss b/src/ButtonGroup/index.scss new file mode 100644 index 0000000..1951287 --- /dev/null +++ b/src/ButtonGroup/index.scss @@ -0,0 +1,14 @@ +@use "sass:map"; + +@use "../sass/tokens"; + +.button-group { + display: flex; + align-items: center; + + @each $size in tokens.$sizes { + &.button-group--#{$size} { + gap: map.get(tokens.$gap, $size); + } + } +} diff --git a/src/ButtonGroup/index.tsx b/src/ButtonGroup/index.tsx new file mode 100644 index 0000000..2594db4 --- /dev/null +++ b/src/ButtonGroup/index.tsx @@ -0,0 +1,32 @@ +import type { FC, PropsWithChildren } from "react"; + +import "./index.scss"; +import clsx from "clsx"; + +export type ButtonGroupSize = "sm" | "base" | "lg"; + +export interface ButtonGroupProps { + /** + * The size of the button group (mostly affects spacing). + */ + size?: ButtonGroupSize; +} + +const ButtonGroup: FC> = ({ + children, + size = "base", +}) => { + return ( +
+ {children} +
+ ); +}; + +export default ButtonGroup; diff --git a/src/sass/_tokens.scss b/src/sass/_tokens.scss index 4f7a990..b1dda5b 100644 --- a/src/sass/_tokens.scss +++ b/src/sass/_tokens.scss @@ -58,6 +58,12 @@ $border-radius: ( lg: 0.75rem, ); +$gap: ( + sm: 0.25rem, + base: 0.5rem, + lg: 0.75rem, +); + $padding: ( sm: 0.2rem, base: 0.4rem, From 9c3d6dc3285c2d795c7b5114a88bc9ca8c50c976 Mon Sep 17 00:00:00 2001 From: Luc Perkins Date: Wed, 11 Jun 2025 12:42:31 -0700 Subject: [PATCH 2/3] Enable using enums as arrays --- src/Button/Button.stories.tsx | 12 +++++++++++- src/Button/index.tsx | 22 +++++++++++++--------- src/CodeBlock/CodeBlock.stories.tsx | 3 ++- src/CodeFile/CodeFile.stories.tsx | 3 ++- src/Highlight/Highlight.stories.tsx | 3 ++- src/hooks/useHighlight.ts | 3 ++- 6 files changed, 32 insertions(+), 14 deletions(-) diff --git a/src/Button/Button.stories.tsx b/src/Button/Button.stories.tsx index b52fd1a..265329b 100644 --- a/src/Button/Button.stories.tsx +++ b/src/Button/Button.stories.tsx @@ -1,11 +1,21 @@ import { action } from "@storybook/addon-actions"; import type { Meta, StoryObj } from "@storybook/react"; -import Button from "./"; +import Button, { ButtonColors, ButtonSizes } from "./"; const meta = { title: "Atoms/Button", component: Button, + argTypes: { + color: { + description: "The button's color scheme", + options: [...ButtonColors], + }, + size: { + description: "The button's size", + option: [...ButtonSizes], + }, + }, } satisfies Meta; export default meta; diff --git a/src/Button/index.tsx b/src/Button/index.tsx index 65cea08..dc8aceb 100644 --- a/src/Button/index.tsx +++ b/src/Button/index.tsx @@ -3,14 +3,18 @@ import type { FC, PropsWithChildren } from "react"; import "./index.scss"; import clsx from "clsx"; -export type ButtonSize = "sm" | "base" | "lg"; -export type ButtonColors = - | "primary" - | "secondary" - | "success" - | "info" - | "warning" - | "danger"; +export const ButtonSizes = ["sm", "base", "lg"]; +export type ButtonSize = (typeof ButtonSizes)[number]; + +export const ButtonColors = [ + "primary", + "secondary", + "success", + "info", + "warning", + "danger", +]; +export type ButtonColor = (typeof ButtonColors)[number]; export interface ButtonProps { /** @@ -26,7 +30,7 @@ export interface ButtonProps { /** * The button's color scheme. */ - color?: ButtonColors; + color?: ButtonColor; /** * Whether the button is outlined. diff --git a/src/CodeBlock/CodeBlock.stories.tsx b/src/CodeBlock/CodeBlock.stories.tsx index 57c3f10..3b5afbd 100644 --- a/src/CodeBlock/CodeBlock.stories.tsx +++ b/src/CodeBlock/CodeBlock.stories.tsx @@ -1,6 +1,7 @@ import type { Meta, StoryObj } from "@storybook/react"; import CodeBlock from "./"; +import { HighlightLanguages } from "../hooks/useHighlight"; const meta = { title: "Molecules/CodeBlock", @@ -11,7 +12,7 @@ const meta = { argTypes: { language: { control: "select", - options: ["shell", "yaml", "terraform", "text"], + options: [...HighlightLanguages], }, code: { control: "text" }, allowCopy: { control: "boolean" }, diff --git a/src/CodeFile/CodeFile.stories.tsx b/src/CodeFile/CodeFile.stories.tsx index 3d0f7b6..8af17f3 100644 --- a/src/CodeFile/CodeFile.stories.tsx +++ b/src/CodeFile/CodeFile.stories.tsx @@ -1,6 +1,7 @@ import type { Meta, StoryObj } from "@storybook/react"; import CodeFile from "./"; +import { HighlightLanguages } from "../hooks/useHighlight"; const meta = { title: "Molecules/CodeFile", @@ -11,7 +12,7 @@ const meta = { argTypes: { language: { control: "select", - options: ["shell", "yaml", "terraform", "text"], + options: [...HighlightLanguages], }, code: { control: "text" }, filename: { control: "text" }, diff --git a/src/Highlight/Highlight.stories.tsx b/src/Highlight/Highlight.stories.tsx index 9e7ed9b..0cb7e4a 100644 --- a/src/Highlight/Highlight.stories.tsx +++ b/src/Highlight/Highlight.stories.tsx @@ -1,6 +1,7 @@ import type { Meta, StoryObj } from "@storybook/react"; import Highlight from "./"; +import { HighlightLanguages } from "../hooks/useHighlight"; const meta = { title: "Atoms/Highlight", @@ -11,7 +12,7 @@ const meta = { argTypes: { language: { control: "select", - options: ["shell", "yaml", "terraform", "text"], + options: [...HighlightLanguages], }, code: { control: "text" }, inline: { control: "boolean" }, diff --git a/src/hooks/useHighlight.ts b/src/hooks/useHighlight.ts index b4a0f10..0568706 100644 --- a/src/hooks/useHighlight.ts +++ b/src/hooks/useHighlight.ts @@ -10,7 +10,8 @@ import { useMemo } from "react"; /** * Languages understood by the UI system's highlighter. The `text` option prevents highlighting. */ -export type HighlightLanguage = "shell" | "yaml" | "terraform" | "text"; +export const HighlightLanguages = ["shell", "yaml", "terraform", "text"]; +export type HighlightLanguage = (typeof HighlightLanguages)[number]; // Lazily instantiate the Shiki renderer to avoid paying (some) startp costs let shiki: ReturnType; From de2786d783dd683ccdcd6c9ee141266a1dd427d5 Mon Sep 17 00:00:00 2001 From: Luc Perkins Date: Thu, 12 Jun 2025 11:03:36 -0700 Subject: [PATCH 3/3] Fix labels on buttons --- src/ButtonGroup/ButtonGroup.stories.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ButtonGroup/ButtonGroup.stories.tsx b/src/ButtonGroup/ButtonGroup.stories.tsx index 29376ec..bf4fe0f 100644 --- a/src/ButtonGroup/ButtonGroup.stories.tsx +++ b/src/ButtonGroup/ButtonGroup.stories.tsx @@ -28,7 +28,7 @@ export const Small: Story = { ), @@ -45,7 +45,7 @@ export const Base: Story = { ), @@ -62,7 +62,7 @@ export const Large: Story = { ), @@ -79,7 +79,7 @@ export const SmallButtonsWithLargeGap: Story = { ),