Skip to content

Commit

Permalink
feat(components): styled button (#5803)
Browse files Browse the repository at this point in the history
* feat(components): add backwards compatible button

* cleanup the story
  • Loading branch information
mikeldking authored Dec 20, 2024
1 parent 374fac4 commit e8afaa8
Show file tree
Hide file tree
Showing 7 changed files with 288 additions and 2 deletions.
42 changes: 42 additions & 0 deletions app/src/components/button/Button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import React, { ReactNode, Ref } from "react";
import {
Button as AriaButton,
ButtonProps as AriaButtonProps,
} from "react-aria-components";

import { SizingProps, VarianceProps } from "@phoenix/components/types";

import { buttonCSS } from "./styles";

interface ButtonProps extends AriaButtonProps, SizingProps, VarianceProps {
/**
* An optional prefixed icon for the button
*/
icon?: ReactNode;
}

function Button(props: ButtonProps, ref: Ref<HTMLButtonElement>) {
const {
size = "M",
variant = "default",
icon,
children,
...otherProps
} = props;
return (
<AriaButton
{...otherProps}
ref={ref}
data-size={size}
data-variant={variant}
data-childless={!children}
css={buttonCSS}
>
{icon}
<>{children}</>
</AriaButton>
);
}

const _Button = React.forwardRef(Button);
export { _Button as Button, ButtonProps };
85 changes: 85 additions & 0 deletions app/src/components/button/styles.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { css } from "@emotion/react";

export const buttonCSS = css`
border: 1px solid var(--ac-global-border-color-default);
font-size: var(--ac-global-dimension-static-font-size-100);
line-height: 20px; // TODO(mikeldking): move this into a consistent variable
margin: 0;
display: flex;
justify-content: center;
align-items: center;
box-sizing: border-box;
border-radius: var(--ac-global-rounding-small);
color: var(--ac-global-text-color-900);
cursor: pointer;
/* Disable outline since there are other mechanisms to show focus */
outline: none;
&:not([disabled]) {
transition: all 0.2s ease-in-out;
}
&[disabled] {
cursor: default;
opacity: 0.6;
}
&[data-size="M"][data-childless="false"] {
padding: var(--ac-global-dimension-static-size-100)
var(--ac-global-dimension-static-size-200);
}
&[data-size="S"][data-childless="false"] {
padding: var(--ac-global-dimension-static-size-50)
var(--ac-global-dimension-static-size-100);
}
&[data-size="M"][data-childless="true"] {
padding: var(--ac-global-dimension-static-size-100)
var(--ac-global-dimension-static-size-100);
}
&[data-size="S"][data-childless="true"] {
padding: var(--ac-global-dimension-static-size-50)
var(--ac-global-dimension-static-size-50);
}
&[data-variant="primary"] {
background-color: var(--ac-global-button-primary-background-color);
border-color: var(--ac-global-button-primary-border-color);
color: var(--ac-global-static-color-white-900);
&:hover:not([disabled]) {
background-color: var(--ac-global-button-primary-background-color-hover);
}
}
&[data-variant="danger"] {
background-color: var(--ac-global-button-danger-background-color);
border-color: var(--ac-global-button-danger-border-color);
color: var(--ac-global-static-color-white-900);
&:hover:not([disabled]) {
background-color: var(--ac-global-button-danger-background-color-hover);
}
}
&[data-variant="success"] {
background-color: var(--ac-global-button-success-background-color);
border-color: var(--ac-global-button-success-border-color);
color: var(--ac-global-static-color-white-900);
color: var(--ac-global-static-color-white-900);
&:hover:not([disabled]) {
background-color: var(--ac-global-button-success-background-color-hover);
}
}
&[data-variant="default"] {
background-color: var(--ac-global-input-field-background-color);
border-color: var(--ac-global-input-field-border-color);
&:hover:not([disabled]) {
background-color: var(--ac-global-input-field-border-color-hover);
}
}
&[data-variant="quiet"] {
background-color: transparent;
border-color: transparent;
&:hover:not([disabled]) {
border-color: transparent;
background-color: var(--ac-global-input-field-background-color-active);
}
}
&[data-childless="false"] > i,
& > .ac-spinner {
margin-right: var(--ac-global-dimension-static-size-50);
}
`;
1 change: 1 addition & 0 deletions app/src/components/types/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from "./sizing";
export * from "./variance";
File renamed without changes.
12 changes: 12 additions & 0 deletions app/src/components/types/variance.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export type BaseVariant = "primary" | "default";
export type LevelVariant = "success" | "danger" | "info";

export type Variant = BaseVariant | LevelVariant;

export interface VarianceProps {
/**
* The variant of the component
* @default 'default'
*/
variant?: Variant;
}
137 changes: 137 additions & 0 deletions app/stories/Button.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import React from "react";
import { Meta, StoryFn } from "@storybook/react";
import { css } from "@emotion/react";

import { Button as LegacyButton, Icon, Icons } from "@arizeai/components";

import { Button, ButtonProps } from "@phoenix/components/button/Button";

import { ThemeWrapper } from "./components/ThemeWrapper";

const meta: Meta = {
title: "Button",
component: Button,
argTypes: {
label: {
control: {
type: "text",
default: "Label",
},
},
isDisabled: {
type: "boolean",
},
description: {
type: "string",
control: {
type: "text",
},
},
errorMessage: {
type: "string",
control: {
type: "text",
},
},
isInvalid: {
control: {
type: "boolean",
},
},
isRequired: {
control: {
type: "boolean",
},
},
menuTrigger: {
options: ["manual", "input", "focus"],
control: {
type: "radio",
},
},
},

parameters: {
controls: { expanded: true },
},
};

export default meta;

const Template: StoryFn<ButtonProps> = (args) => (
<ThemeWrapper>
<Button {...args} />
</ThemeWrapper>
);

export const Default = Template.bind({});

Default.args = {
children: "Button",
};

const liCSS = css`
display: flex;
flex-direction: row;
gap: 8px;
align-items: center;
`;

export const Migration = () => {
return (
<>
<ThemeWrapper>
<ul
css={css`
display: flex;
flex-direction: column;
gap: 4px;
`}
>
<li css={liCSS}>
<Button key="new" icon={<Icon svg={<Icons.PlusCircleOutline />} />}>
Button
</Button>
<LegacyButton
variant="default"
key="old"
icon={<Icon svg={<Icons.PlusCircleOutline />} />}
>
Legacy
</LegacyButton>
<Button key="new-s" size="S">
Button
</Button>
<LegacyButton variant="default" key="old-s" size="compact">
Legacy
</LegacyButton>
</li>
<li css={liCSS}>
<Button key="new" variant="primary">
Button
</Button>
<LegacyButton variant="primary" key="old">
Legacy
</LegacyButton>
</li>
<li css={liCSS}>
<Button key="new" variant="danger">
Button
</Button>
<LegacyButton variant="danger" key="old">
Legacy
</LegacyButton>
</li>
<li css={liCSS}>
<Button key="new" variant="success">
Button
</Button>
<LegacyButton variant="success" key="old">
Legacy
</LegacyButton>
</li>
</ul>
</ThemeWrapper>
</>
);
};
13 changes: 11 additions & 2 deletions app/stories/Gallery.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import React from "react";
import { Meta, StoryFn } from "@storybook/react";

import { Flex, Item, Picker } from "@arizeai/components";
import {
Button as LegacyButton,
Flex,
Item,
Picker,
} from "@arizeai/components";

import { Button } from "@phoenix/components/button/Button";
import {
ComboBox,
ComboBoxItem,
Expand All @@ -23,7 +29,8 @@ export default meta;

const Template: StoryFn<ComboBoxProps<object>> = () => (
<ThemeWrapper>
<Flex direction="row" gap="size-200">
<Flex direction="row" gap="size-200" alignItems="center">
<Button size="S">Button</Button>
<ComboBox label="Ice cream flavor" description={"pick a flavor"}>
<ComboBoxItem textValue="Chocolate" key={"chocolate"}>
Chocolate
Expand All @@ -44,6 +51,8 @@ const Template: StoryFn<ComboBoxProps<object>> = () => (
<Item key="strawberry">Strawberry</Item>
<Item key="vanilla">Vanilla</Item>
</Picker>
<Button size="M">Button</Button>
<LegacyButton variant="default">Button</LegacyButton>
<ComboBox label="Ice cream flavor" description={"pick a flavor"} size="L">
<ComboBoxItem textValue="Chocolate" key={"chocolate"}>
Chocolate
Expand Down

0 comments on commit e8afaa8

Please sign in to comment.