Skip to content
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
108 changes: 108 additions & 0 deletions components/button/stories/button.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import {
Meta,
Title,
Subtitle,
Description,
ArgTypes,
Canvas,
} from "@storybook/blocks";
import { ComponentDetails, TaggedReleases } from "@spectrum-css/preview/blocks";

import * as ButtonStories from "./button.stories";

<Meta of={ButtonStories} title="Docs" />

<Title of={ButtonStories} />
<Subtitle of={ButtonStories} />
<ComponentDetails of={ButtonStories} />

<Description of={ButtonStories} />

## Variants

There are four available variants that are used for different levels of emphasis and different
types of actions. By default, a button uses the fill style with a solid background. The primary
and secondary variants also have an outline option.

### Accent

<Description of={ButtonStories.Accent} />
<Canvas of={ButtonStories.Accent} />

### Primary

<Description of={ButtonStories.Primary} />
<Canvas of={ButtonStories.Primary} />

### Secondary

<Description of={ButtonStories.Secondary} />
<Canvas of={ButtonStories.Secondary} />

### Negative

<Description of={ButtonStories.Negative} />
<Canvas of={ButtonStories.Negative} />

## Static color

When a button needs to be placed on top of a color background or a visual, use the static color
option. Static color buttons do not change shades or values depending upon the color theme.

### Static white - primary

<Canvas
of={ButtonStories.StaticWhitePrimary}
className="spectrum-examples-static-white"
/>

### Static white - secondary

<Canvas
of={ButtonStories.StaticWhiteSecondary}
className="spectrum-examples-static-white"
/>

### Static black - primary

<Canvas
of={ButtonStories.StaticBlackPrimary}
className="spectrum-examples-static-black"
/>

### Static black - secondary

<Canvas
of={ButtonStories.StaticBlackSecondary}
className="spectrum-examples-static-black"
/>

## Sizing

<Description of={ButtonStories.Sizing} />
<Canvas of={ButtonStories.Sizing} />

## Pending state

<Description of={ButtonStories.Pending} />
<Canvas of={ButtonStories.Pending} />

## Disabled state

<Description of={ButtonStories.Disabled} />
<Canvas of={ButtonStories.Disabled} />

## Text overflow behavior

<Description of={ButtonStories.WithWrapping} />
<Canvas of={ButtonStories.WithWrapping} />

## Properties

The component accepts the following inputs (properties):

<ArgTypes of={ButtonStories} />

## Tagged releases

<TaggedReleases of={ButtonStories} />
171 changes: 171 additions & 0 deletions components/button/stories/button.stories.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { default as IconStories } from "@spectrum-css/icon/stories/icon.stories.js";
import { Sizes } from "@spectrum-css/preview/decorators";
import { disableDefaultModes } from "@spectrum-css/preview/modes";
import { isActive, isDisabled, isFocused, isHovered, isPending, size, staticColor } from "@spectrum-css/preview/types";
import pkgJson from "../package.json";
import { ButtonGroups } from "./button.test.js";
import { ButtonsWithIconOptions, TextOverflowTemplate, TreatmentTemplate } from "./template.js";

/**
* Buttons allow users to perform an action or to navigate to another page. They have multiple styles for various needs, and are ideal for calling attention to where a user needs to do something in order to move forward in a flow.
Expand Down Expand Up @@ -75,6 +77,7 @@ export default {
},
packageJson: pkgJson,
},
tags: ["!autodocs"],
};

export const Default = ButtonGroups.bind({});
Expand Down Expand Up @@ -115,3 +118,171 @@ WithForcedColors.parameters = {
WithForcedColors.args = {
iconName: "Actions",
};

// ********* DOCS ONLY ********* //

/**
* Buttons come in four different sizes: small, medium, large, and extra large. The medium size is
* the default and most frequently used option. Use the other sizes sparingly; they should be used
* to create a hierarchy of importance within the page.
*/
export const Sizing = (args, context) => Sizes({
Template: ButtonsWithIconOptions,
withHeading: false,
withBorder: false,
...args,
}, context);
Sizing.args = {};
Sizing.tags = ["!dev"];
Sizing.parameters = {
chromatic: { disableSnapshot: true },
};

/**
* The accent button communicates strong emphasis and is reserved for encouraging critical
* actions. In general, only use the emphasized option for the most important action on the page.
*/
export const Accent = ButtonsWithIconOptions.bind({});
Accent.tags = ["!dev"];
Accent.args = {
variant: "accent",
};
Accent.parameters = {
chromatic: { disableSnapshot: true },
};

/**
* The primary button is for medium emphasis. Use it in place of an accent button when the
* action requires less prominence, or if there are multiple primary actions of the same importance
* in the same view. Both the fill (default) and outline styles are demonstrated in this example.
*/
export const Primary = TreatmentTemplate.bind({});
Primary.tags = ["!dev"];
Primary.args = {
variant: "primary",
treatmentLayout: "stacked",
};
Primary.parameters = {
chromatic: { disableSnapshot: true },
};

/**
* The secondary button is for low emphasis. It’s paired with other button types to surface less
* prominent actions, and should never be the only button in a group. Both the fill (default) and
* outline styles are demonstrated in this example.
*/
export const Secondary = TreatmentTemplate.bind({});
Secondary.tags = ["!dev"];
Secondary.args = {
variant: "secondary",
treatmentLayout: "stacked",
};
Secondary.parameters = {
chromatic: { disableSnapshot: true },
};

/**
* The negative button is for emphasizing actions that can be destructive or have negative
* consequences if taken. Use it sparingly.
*/
export const Negative = ButtonsWithIconOptions.bind({});
Negative.tags = ["!dev"];
Negative.args = {
variant: "negative",
};
Negative.parameters = {
chromatic: { disableSnapshot: true },
};

export const StaticWhitePrimary = TreatmentTemplate.bind({});
StaticWhitePrimary.tags = ["!dev"];
StaticWhitePrimary.args = {
variant: "primary",
treatmentLayout: "stacked",
staticColor: "white",
};
StaticWhitePrimary.parameters = {
chromatic: { disableSnapshot: true },
};

export const StaticWhiteSecondary = TreatmentTemplate.bind({});
StaticWhiteSecondary.tags = ["!dev"];
StaticWhiteSecondary.args = {
variant: "secondary",
treatmentLayout: "stacked",
staticColor: "white",
};
StaticWhiteSecondary.parameters = {
chromatic: { disableSnapshot: true },
};

export const StaticBlackPrimary = TreatmentTemplate.bind({});
StaticBlackPrimary.tags = ["!dev"];
StaticBlackPrimary.args = {
variant: "primary",
treatmentLayout: "stacked",
staticColor: "black",
};
StaticBlackPrimary.parameters = {
chromatic: { disableSnapshot: true },
};

export const StaticBlackSecondary = TreatmentTemplate.bind({});
StaticBlackSecondary.tags = ["!dev"];
StaticBlackSecondary.args = {
variant: "secondary",
treatmentLayout: "stacked",
staticColor: "black",
};
StaticBlackSecondary.parameters = {
chromatic: { disableSnapshot: true },
};

/**
* The pending button is for indicating that a quick progress action is taking place. In this case, the
* label and optional icon disappear and a progress circle appears. The progress circle always shows an
* indeterminate progress. We recommend the use of the `.is-pending` class on the component’s parent
* container, but there is also an option to use an attribute of `pending` instead. Buttons should have
* the disabled attribute when the pending state is applied.
*/
export const Pending = TreatmentTemplate.bind({});
Pending.tags = ["!dev"];
Pending.args = {
variant: "accent",
isPending: true,
onclick: () => {},
};
Pending.parameters = {
chromatic: { disableSnapshot: true },
};

/**
* A button in a disabled state shows that an action exists, but is not available in that circumstance.
* This state can be used to maintain layout continuity and to communicate that an action may become
* available later.
*/
export const Disabled = TreatmentTemplate.bind({});
Disabled.tags = ["!dev"];
Disabled.args = {
variant: "accent",
isDisabled: true,
};
Disabled.parameters = {
chromatic: { disableSnapshot: true },
};

/**
* When the button text is too long for the horizontal space available, it wraps to form another line.
* When there is no icon present, the text is aligned center. When there is an icon present, the text is
* aligned `start` (left with a writing direction of left-to-right) and the icon remains vertically aligned
* at the top.
*/
export const WithWrapping = TextOverflowTemplate.bind({});
WithWrapping.tags = ["!dev"];
WithWrapping.storyName = "Text overflow behavior";
WithWrapping.args = {
variant: "primary",
};
WithWrapping.parameters = {
chromatic: { disableSnapshot: true },
};
72 changes: 71 additions & 1 deletion components/button/stories/template.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Template as Icon } from "@spectrum-css/icon/stories/template.js";
import { getRandomId } from "@spectrum-css/preview/decorators";
import { Container, getRandomId } from "@spectrum-css/preview/decorators";
import { Template as ProgressCircle } from "@spectrum-css/progresscircle/stories/template.js";
import { html } from "lit";
import { classMap } from "lit/directives/class-map.js";
Expand Down Expand Up @@ -100,3 +100,73 @@ export const Template = ({
</button>
`;
};

/**
* Displays multiple buttons with text label, icon + text label, and icon only.
* Used in the display of some docs-only stories.
*/
export const ButtonsWithIconOptions = ({
iconName,
...args
}) => Container({
withBorder: false,
direction: "row",
wrapperStyles: {
columnGap: "12px",
},
content: html`
${Template({
...args,
iconName: undefined,
})}
${Template({
...args,
iconName: iconName ?? "Edit",
})}
${Template({
...args,
hideLabel: true,
iconName: iconName ?? "Edit",
})}
`,
});

/**
* Display the buttons with icon options for each treatment option.
*/
export const TreatmentTemplate = (args) => Container({
withBorder: false,
direction: "column",
wrapperStyles: {
rowGap: "12px",
},
content: html`${["fill", "outline"].map((treatment) => ButtonsWithIconOptions({ ...args, treatment }))}`,
});

/**
* Display the text overflow behavior of buttons.
*/
export const TextOverflowTemplate = (args) => Container({
withBorder: false,
direction: "column",
wrapperStyles: {
rowGap: "12px",
},
content: html`
${Template({
...args,
customStyles: {
"max-inline-size": "480px",
},
label: "An example of text overflow behavior when there is no icon. When the button text is too long for the horizontal space available, it wraps to form another line.",
})}
${Template({
...args,
customStyles: {
"max-inline-size": "480px",
},
iconName: "Edit",
label: "An example of text overflow behavior when the button has an icon. When the button text is too long for the horizontal space available, it wraps to form another line.",
})}
`,
});