diff --git a/change/@fluentui-web-components-cd236262-0e75-4b91-a9b2-b7b574f558b9.json b/change/@fluentui-web-components-cd236262-0e75-4b91-a9b2-b7b574f558b9.json new file mode 100644 index 0000000000000..3a8da4c1040e5 --- /dev/null +++ b/change/@fluentui-web-components-cd236262-0e75-4b91-a9b2-b7b574f558b9.json @@ -0,0 +1,7 @@ +{ + "type": "prerelease", + "comment": "feat(menu-button): add menu button as new component", + "packageName": "@fluentui/web-components", + "email": "chhol@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/packages/web-components/package.json b/packages/web-components/package.json index 70eebc210dcce..e3680a287a4c5 100644 --- a/packages/web-components/package.json +++ b/packages/web-components/package.json @@ -60,6 +60,10 @@ "types": "./dist/esm/text/define.d.ts", "default": "./dist/esm/text/define.js" }, + "./menu-button": { + "types": "./dist/esm/menu-button/define.d.ts", + "default": "./dist/esm/menu-button/define.js" + }, "./progress-bar": { "types": "./dist/esm/progress-bar/define.d.ts", "default": "./dist/esm/progress-bar/define.js" diff --git a/packages/web-components/src/button/button.styles.ts b/packages/web-components/src/button/button.styles.ts index 93f90d0e7e860..449ce868bb47e 100644 --- a/packages/web-components/src/button/button.styles.ts +++ b/packages/web-components/src/button/button.styles.ts @@ -125,10 +125,12 @@ export const styles = css` fill: currentColor; } + [slot='start'], ::slotted([slot='start']) { margin-inline-end: var(--icon-spacing); } + [slot='end'], ::slotted([slot='end']) { margin-inline-start: var(--icon-spacing); } diff --git a/packages/web-components/src/index.ts b/packages/web-components/src/index.ts index e254cb04a98b6..35095ddf314d5 100644 --- a/packages/web-components/src/index.ts +++ b/packages/web-components/src/index.ts @@ -6,6 +6,7 @@ export * from './button/index.js'; export * from './counter-badge/index.js'; export * from './divider/index.js'; export * from './image/index.js'; +export * from './menu-button/index.js'; export * from './progress-bar/index.js'; export * from './slider/index.js'; export * from './spinner/index.js'; diff --git a/packages/web-components/src/menu-button/define.ts b/packages/web-components/src/menu-button/define.ts new file mode 100644 index 0000000000000..d18d09513254f --- /dev/null +++ b/packages/web-components/src/menu-button/define.ts @@ -0,0 +1,4 @@ +import { FluentDesignSystem } from '../fluent-design-system.js'; +import { definition } from './menu-button.definition.js'; + +definition.define(FluentDesignSystem.registry); diff --git a/packages/web-components/src/menu-button/index.ts b/packages/web-components/src/menu-button/index.ts new file mode 100644 index 0000000000000..cf17a408fbe99 --- /dev/null +++ b/packages/web-components/src/menu-button/index.ts @@ -0,0 +1,5 @@ +export * from './menu-button.js'; +export * from './menu-button.options.js'; +export { template as MenuButtonTemplate } from './menu-button.template.js'; +export { styles as MenuButtonStyles } from '../button/button.styles.js'; +export { definition as MenuButtonDefinition } from './menu-button.definition.js'; diff --git a/packages/web-components/src/menu-button/menu-button.definition.ts b/packages/web-components/src/menu-button/menu-button.definition.ts new file mode 100644 index 0000000000000..b8dbdffe82241 --- /dev/null +++ b/packages/web-components/src/menu-button/menu-button.definition.ts @@ -0,0 +1,21 @@ +import { FluentDesignSystem } from '../fluent-design-system.js'; +import { styles } from '../button/button.styles.js'; +import { MenuButton } from './menu-button.js'; +import { template } from './menu-button.template.js'; + +/** + * The Fluent Menu Button Element. Implements {@link @microsoft/fast-foundation#Button }, + * {@link @microsoft/fast-foundation#buttonTemplate} + * + * @public + * @remarks + * HTML Element: \ + */ +export const definition = MenuButton.compose({ + name: `${FluentDesignSystem.prefix}-menu-button`, + template, + styles, + shadowOptions: { + delegatesFocus: true, + }, +}); diff --git a/packages/web-components/src/menu-button/menu-button.options.ts b/packages/web-components/src/menu-button/menu-button.options.ts new file mode 100644 index 0000000000000..36cf62ac4f489 --- /dev/null +++ b/packages/web-components/src/menu-button/menu-button.options.ts @@ -0,0 +1,40 @@ +import { ButtonOptions, ValuesOf } from '@microsoft/fast-foundation'; +import { ButtonAppearance, ButtonShape, ButtonSize } from '../button/button.options.js'; + +/** + * Menu Button Appearance constants + * @public + */ +export const MenuButtonAppearance = ButtonAppearance; + +/** + * A Menu Button can be secondary, primary, outline, subtle, transparent + * @public + */ +export type MenuButtonAppearance = ValuesOf; + +/** + * A Menu Button can be square, circular or rounded. + * @public + */ +export const MenuButtonShape = ButtonShape; + +/** + * A Menu Button can be square, circular or rounded + * @public + */ +export type MenuButtonShape = ValuesOf; + +/** + * A Menu Button can be a size of small, medium or large. + * @public + */ +export const MenuButtonSize = ButtonSize; + +/** + * A Menu Button can be on of several preset sizes. + * @public + */ +export type MenuButtonSize = ValuesOf; + +export { ButtonOptions as MenuButtonOptions }; diff --git a/packages/web-components/src/menu-button/menu-button.stories.ts b/packages/web-components/src/menu-button/menu-button.stories.ts new file mode 100644 index 0000000000000..911b6ff280dfa --- /dev/null +++ b/packages/web-components/src/menu-button/menu-button.stories.ts @@ -0,0 +1,261 @@ +import { html } from '@microsoft/fast-element'; +import type { Args, Meta } from '@storybook/html'; +import { renderComponent } from '../helpers.stories.js'; +import type { MenuButton as FluentMenuButton } from './menu-button.js'; +import { MenuButtonAppearance, MenuButtonShape, MenuButtonSize } from './menu-button.options.js'; +import './define.js'; + +type MenuButtonStoryArgs = Args & FluentMenuButton; +type MenuButtonStoryMeta = Meta; + +const storyTemplate = html` + + ${x => x.content} + +`; + +export default { + title: 'Components/Button/Menu Button', + args: { + content: 'Menu Button', + disabled: false, + disabledFocusable: false, + }, + argTypes: { + appearance: { + options: Object.values(MenuButtonAppearance), + control: { + type: 'select', + }, + }, + shape: { + options: Object.values(MenuButtonShape), + control: { + type: 'select', + }, + }, + size: { + options: Object.values(MenuButtonSize), + 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 MenuButtonStoryMeta; + +export const Button = renderComponent(storyTemplate).bind({}); + +export const Appearance = renderComponent(html` + Default + Primary + Outline + Subtle + Transparent +`); + +export const Shape = renderComponent(html` + Rounded + Circular + Square +`); + +export const Size = renderComponent(html` + Small + Small with calendar icon + + Medium + Medium with calendar icon + + Large + Large with calendar icon + +`); + +export const CustomIcon = renderComponent(html` + + Small + + + Medium + + Large + +`); + +export const Disabled = renderComponent(html` + Enabled state + Disabled state + Disabled focusable state + Enabled state + Disabled state + Disabled focusable state +`); + +export const WithLongText = renderComponent(html` + + Short text + Long text wraps after it hits the max width of the component +`); diff --git a/packages/web-components/src/menu-button/menu-button.template.ts b/packages/web-components/src/menu-button/menu-button.template.ts new file mode 100644 index 0000000000000..1ffe1754fe292 --- /dev/null +++ b/packages/web-components/src/menu-button/menu-button.template.ts @@ -0,0 +1,13 @@ +import { ElementViewTemplate, html } from '@microsoft/fast-element'; +import { buttonTemplate } from '@microsoft/fast-foundation'; +import type { MenuButton } from './menu-button.js'; + +/** + * The template for the Button component. + * @public + */ +export const template: ElementViewTemplate = buttonTemplate({ + end: html.partial( + ``, + ), +}); diff --git a/packages/web-components/src/menu-button/menu-button.ts b/packages/web-components/src/menu-button/menu-button.ts new file mode 100644 index 0000000000000..d708740acfd03 --- /dev/null +++ b/packages/web-components/src/menu-button/menu-button.ts @@ -0,0 +1,7 @@ +import { Button } from '../button/button.js'; + +/** + * The base class used for constructing a fluent-menu-button custom element + * @public + */ +export class MenuButton extends Button {}