Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(button): add button to web components v3 #27278

Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "prerelease",
"comment": "feat(button): add button web component",
"packageName": "@fluentui/web-components",
"email": "[email protected]",
"dependentChangeType": "patch"
}
4 changes: 4 additions & 0 deletions packages/web-components/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@
"types": "./dist/esm/badge/define.d.ts",
"default": "./dist/esm/badge/define.js"
},
"./button": {
"types": "./dist/esm/button/define.d.ts",
"default": "./dist/esm/button/define.js"
},
"./counter-badge": {
"types": "./dist/esm/counter-badge/define.d.ts",
"default": "./dist/esm/counter-badge/define.js"
Expand Down
21 changes: 21 additions & 0 deletions packages/web-components/src/button/button.definition.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { FluentDesignSystem } from '../fluent-design-system.js';
import { Button } from './button.js';
import { styles } from './button.styles.js';
import { template } from './button.template.js';

/**
* The Fluent Button Element. Implements {@link @microsoft/fast-foundation#Button },
* {@link @microsoft/fast-foundation#buttonTemplate}
*
* @public
* @remarks
* HTML Element: \<fluent-button\>
*/
export const definition = Button.compose({
name: `${FluentDesignSystem.prefix}-button`,
template,
styles,
shadowOptions: {
delegatesFocus: true,
},
});
53 changes: 53 additions & 0 deletions packages/web-components/src/button/button.options.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { ButtonOptions, ValuesOf } from '@microsoft/fast-foundation';

/**
* ButtonAppearance constants
* @public
*/
export const ButtonAppearance = {
chrisdholt marked this conversation as resolved.
Show resolved Hide resolved
primary: 'primary',
outline: 'outline',
subtle: 'subtle',
secondary: 'secondary',
transparent: 'transparent',
} as const;

/**
* A Button can be secondary, primary, outline, subtle, transparent
* @public
*/
export type ButtonAppearance = ValuesOf<typeof ButtonAppearance>;

/**
* A Button can be square, circular or rounded.
* @public
*/
export const ButtonShape = {
circular: 'circular',
rounded: 'rounded',
square: 'square',
} as const;

/**
* A Button can be square, circular or rounded
* @public
*/
export type ButtonShape = ValuesOf<typeof ButtonShape>;

/**
* A Button can be a size of small, medium or large.
* @public
*/
export const ButtonSize = {
small: 'small',
medium: 'medium',
large: 'large',
} as const;

/**
* A Button can be on of several preset sizes.
* @public
*/
export type ButtonSize = ValuesOf<typeof ButtonSize>;

export { ButtonOptions };
208 changes: 208 additions & 0 deletions packages/web-components/src/button/button.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
import { html } from '@microsoft/fast-element';
import type { Args, Meta } from '@storybook/html';
import { renderComponent } from '../helpers.stories.js';
import type { Button as FluentButton } from './button.js';
import { ButtonAppearance, ButtonShape, ButtonSize } from './button.options.js';
import './define.js';

type ButtonStoryArgs = Args & FluentButton;
type ButtonStoryMeta = Meta<ButtonStoryArgs>;

const storyTemplate = html<ButtonStoryArgs>`
<fluent-button
appearance="${x => x.appearance}"
shape="${x => x.shape}"
size="${x => x.size}"
?disabled="${x => x.disabled}"
?disabled-focusable="${x => x.disabledFocusable}"
?icon-only="${x => x.iconOnly}"
?icon="${x => x.icon}"
>
${x => x.content}
</fluent-button>
`;

export default {
title: 'Components/Button/Button',
args: {
content: 'Button',
disabled: false,
disabledFocusable: false,
},
argTypes: {
appearance: {
options: Object.values(ButtonAppearance),
control: {
type: 'select',
},
},
shape: {
options: Object.values(ButtonShape),
control: {
type: 'select',
},
},
size: {
options: Object.values(ButtonSize),
control: {
type: 'select',
},
},
disabled: {
control: 'boolean',
table: {
type: {
summary: 'Sets the disabled state of the component',
},
defaultValue: {
summary: 'false',
},
},
},
disabledFocusable: {
control: 'boolean',
table: {
type: {
summary: 'The component is disabled but still focusable',
},
defaultValue: {
summary: 'false',
},
},
},
content: {
control: 'Button text',
},
},
} as ButtonStoryMeta;

export const Button = renderComponent(storyTemplate).bind({});

export const Appearance = renderComponent(html<ButtonStoryArgs>`
<fluent-button>Default</fluent-button>
<fluent-button appearance="primary">Primary</fluent-button>
<fluent-button appearance="outline">Outline</fluent-button>
<fluent-button appearance="subtle">Subtle</fluent-button>
<fluent-button appearance="transparent">Transparent</fluent-button>
`);

export const Shape = renderComponent(html<ButtonStoryArgs>`
<fluent-button shape="rounded">Rounded</fluent-button>
<fluent-button shape="circular">Circular</fluent-button>
<fluent-button shape="square">Square</fluent-button>
`);

export const Size = renderComponent(html<ButtonStoryArgs>`
<fluent-button size="small">Small</fluent-button>
<fluent-button size="small" icon
><svg
fill="currentColor"
slot="start"
aria-hidden="true"
width="1em"
height="1em"
viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M14.5 3A2.5 2.5 0 0117 5.5v9a2.5 2.5 0 01-2.5 2.5h-9A2.5 2.5 0 013 14.5v-9A2.5 2.5 0 015.5 3h9zm0 1h-9C4.67 4 4 4.67 4 5.5v9c0 .83.67 1.5 1.5 1.5h9c.83 0 1.5-.67 1.5-1.5v-9c0-.83-.67-1.5-1.5-1.5zM7 11a1 1 0 110 2 1 1 0 010-2zm3 0a1 1 0 110 2 1 1 0 010-2zM7 7a1 1 0 110 2 1 1 0 010-2zm3 0a1 1 0 110 2 1 1 0 010-2zm3 0a1 1 0 110 2 1 1 0 010-2z"
fill="currentColor"
></path></svg
>Small with calendar icon</fluent-button
>
<fluent-button size="small" icon-only aria-label="Small icon only button"
><svg
fill="currentColor"
aria-hidden="true"
width="1em"
height="1em"
viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M14.5 3A2.5 2.5 0 0117 5.5v9a2.5 2.5 0 01-2.5 2.5h-9A2.5 2.5 0 013 14.5v-9A2.5 2.5 0 015.5 3h9zm0 1h-9C4.67 4 4 4.67 4 5.5v9c0 .83.67 1.5 1.5 1.5h9c.83 0 1.5-.67 1.5-1.5v-9c0-.83-.67-1.5-1.5-1.5zM7 11a1 1 0 110 2 1 1 0 010-2zm3 0a1 1 0 110 2 1 1 0 010-2zM7 7a1 1 0 110 2 1 1 0 010-2zm3 0a1 1 0 110 2 1 1 0 010-2zm3 0a1 1 0 110 2 1 1 0 010-2z"
fill="currentColor"
></path></svg
></fluent-button>
<fluent-button size="medium">Medium</fluent-button>
<fluent-button size="medium" icon
><svg
fill="currentColor"
slot="start"
aria-hidden="true"
width="1em"
height="1em"
viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M14.5 3A2.5 2.5 0 0117 5.5v9a2.5 2.5 0 01-2.5 2.5h-9A2.5 2.5 0 013 14.5v-9A2.5 2.5 0 015.5 3h9zm0 1h-9C4.67 4 4 4.67 4 5.5v9c0 .83.67 1.5 1.5 1.5h9c.83 0 1.5-.67 1.5-1.5v-9c0-.83-.67-1.5-1.5-1.5zM7 11a1 1 0 110 2 1 1 0 010-2zm3 0a1 1 0 110 2 1 1 0 010-2zM7 7a1 1 0 110 2 1 1 0 010-2zm3 0a1 1 0 110 2 1 1 0 010-2zm3 0a1 1 0 110 2 1 1 0 010-2z"
fill="currentColor"
></path></svg
>Medium with calendar icon</fluent-button
>
<fluent-button size="medium" icon-only aria-label="Medium icon only button"
><svg
fill="currentColor"
aria-hidden="true"
width="1em"
height="1em"
viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M14.5 3A2.5 2.5 0 0117 5.5v9a2.5 2.5 0 01-2.5 2.5h-9A2.5 2.5 0 013 14.5v-9A2.5 2.5 0 015.5 3h9zm0 1h-9C4.67 4 4 4.67 4 5.5v9c0 .83.67 1.5 1.5 1.5h9c.83 0 1.5-.67 1.5-1.5v-9c0-.83-.67-1.5-1.5-1.5zM7 11a1 1 0 110 2 1 1 0 010-2zm3 0a1 1 0 110 2 1 1 0 010-2zM7 7a1 1 0 110 2 1 1 0 010-2zm3 0a1 1 0 110 2 1 1 0 010-2zm3 0a1 1 0 110 2 1 1 0 010-2z"
fill="currentColor"
></path></svg
></fluent-button>
<fluent-button size="large">Large</fluent-button>
<fluent-button size="large" icon
><svg
fill="currentColor"
slot="start"
aria-hidden="true"
width="1em"
height="1em"
viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M14.5 3A2.5 2.5 0 0117 5.5v9a2.5 2.5 0 01-2.5 2.5h-9A2.5 2.5 0 013 14.5v-9A2.5 2.5 0 015.5 3h9zm0 1h-9C4.67 4 4 4.67 4 5.5v9c0 .83.67 1.5 1.5 1.5h9c.83 0 1.5-.67 1.5-1.5v-9c0-.83-.67-1.5-1.5-1.5zM7 11a1 1 0 110 2 1 1 0 010-2zm3 0a1 1 0 110 2 1 1 0 010-2zM7 7a1 1 0 110 2 1 1 0 010-2zm3 0a1 1 0 110 2 1 1 0 010-2zm3 0a1 1 0 110 2 1 1 0 010-2z"
fill="currentColor"
></path></svg
>Large with calendar icon</fluent-button
>
<fluent-button size="large" icon-only aria-label="Large icon only button"
><svg
fill="currentColor"
aria-hidden="true"
width="1em"
height="1em"
viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M14.5 3A2.5 2.5 0 0117 5.5v9a2.5 2.5 0 01-2.5 2.5h-9A2.5 2.5 0 013 14.5v-9A2.5 2.5 0 015.5 3h9zm0 1h-9C4.67 4 4 4.67 4 5.5v9c0 .83.67 1.5 1.5 1.5h9c.83 0 1.5-.67 1.5-1.5v-9c0-.83-.67-1.5-1.5-1.5zM7 11a1 1 0 110 2 1 1 0 010-2zm3 0a1 1 0 110 2 1 1 0 010-2zM7 7a1 1 0 110 2 1 1 0 010-2zm3 0a1 1 0 110 2 1 1 0 010-2zm3 0a1 1 0 110 2 1 1 0 010-2z"
fill="currentColor"
></path></svg
></fluent-button>
`);

export const Disabled = renderComponent(html<ButtonStoryArgs>`
<fluent-button>Enabled state</fluent-button>
<fluent-button disabled>Disabled state</fluent-button>
<fluent-button disabled-focusable>Disabled focusable state</fluent-button>
<fluent-button appearance="primary">Enabled state</fluent-button>
<fluent-button appearance="primary" disabled>Disabled state</fluent-button>
<fluent-button appearance="primary" disabled-focusable>Disabled focusable state</fluent-button>
`);

export const WithLongText = renderComponent(html<ButtonStoryArgs>`
<style>
.max-width {
width: 280px;
}
</style>
<fluent-button>Short text</fluent-button>
<fluent-button class="max-width">Long text wraps after it hits the max width of the component</fluent-button>
`);
Loading