diff --git a/change/@fluentui-web-components-2500a738-206b-4f66-b5d5-c60924e40a5b.json b/change/@fluentui-web-components-2500a738-206b-4f66-b5d5-c60924e40a5b.json new file mode 100644 index 00000000000000..21d5f30077f2d5 --- /dev/null +++ b/change/@fluentui-web-components-2500a738-206b-4f66-b5d5-c60924e40a5b.json @@ -0,0 +1,7 @@ +{ + "type": "prerelease", + "comment": "feat(divider): Add divider web component", + "packageName": "@fluentui/web-components", + "email": "harankin@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/packages/web-components/package.json b/packages/web-components/package.json index 2652825beb7125..14de3ce922e06e 100644 --- a/packages/web-components/package.json +++ b/packages/web-components/package.json @@ -40,6 +40,10 @@ "types": "./dist/esm/counter-badge/define.d.ts", "default": "./dist/esm/counter-badge/define.js" }, + "./divider": { + "types": "./dist/esm/divider/define.d.ts", + "default": "./dist/esm/divider/define.js" + }, "./image": { "types": "./dist/esm/image/define.d.ts", "default": "./dist/esm/image/define.js" diff --git a/packages/web-components/src/divider/define.ts b/packages/web-components/src/divider/define.ts new file mode 100644 index 00000000000000..228db500313783 --- /dev/null +++ b/packages/web-components/src/divider/define.ts @@ -0,0 +1,4 @@ +import { FluentDesignSystem } from '../fluent-design-system.js'; +import { definition } from './divider.definition.js'; + +definition.define(FluentDesignSystem.registry); diff --git a/packages/web-components/src/divider/divider.definition.ts b/packages/web-components/src/divider/divider.definition.ts new file mode 100644 index 00000000000000..3bc3304ad02097 --- /dev/null +++ b/packages/web-components/src/divider/divider.definition.ts @@ -0,0 +1,17 @@ +import { FluentDesignSystem } from '../fluent-design-system.js'; +import { Divider } from './divider.js'; +import { template } from './divider.template.js'; +import { styles } from './divider.styles.js'; + +/** + * The Fluent Divider Element + * + * @public + * @remarks + * HTML Element: \ + */ +export const definition = Divider.compose({ + name: `${FluentDesignSystem.prefix}-divider`, + template, + styles, +}); diff --git a/packages/web-components/src/divider/divider.options.ts b/packages/web-components/src/divider/divider.options.ts new file mode 100644 index 00000000000000..9e24ccda520425 --- /dev/null +++ b/packages/web-components/src/divider/divider.options.ts @@ -0,0 +1,46 @@ +import { DividerOrientation, DividerRole, ValuesOf } from '@microsoft/fast-foundation'; + +/** + * Fast Foundation DividerRole property + * @public + */ +export { DividerRole }; + +/** + * Fast Foundation Orientation property + * @public + */ +export { DividerOrientation }; + +/** + * Align content within divider + * @public + */ +export const DividerAlignContent = { + center: 'center', + start: 'start', + end: 'end', +} as const; + +/** + * The types for DividerAlignContent + * @public + */ +export type DividerAlignContent = ValuesOf; + +/** + * DividerAppearance - divider color defined by a design token alias. + * @public + */ +export const DividerAppearance = { + strong: 'strong', + brand: 'brand', + subtle: 'subtle', + default: 'default', +} as const; + +/** + * The types for Appearance + * @public + */ +export type DividerAppearance = ValuesOf; diff --git a/packages/web-components/src/divider/divider.stories.ts b/packages/web-components/src/divider/divider.stories.ts new file mode 100644 index 00000000000000..95bb0ce140666c --- /dev/null +++ b/packages/web-components/src/divider/divider.stories.ts @@ -0,0 +1,147 @@ +import { html } from '@microsoft/fast-element'; +import type { Args, Meta } from '@storybook/html'; +import { renderComponent } from '../helpers.stories.js'; +import type { Divider as FluentDivider } from './divider.js'; +import { DividerAlignContent, DividerAppearance, DividerOrientation, DividerRole } from './divider.options.js'; +import './define.js'; + +type DividerStoryArgs = Args & FluentDivider; +type DividerStoryMeta = Meta; + +const dividerTemplate = html` +
+ x.alignContent} + appearance=${x => x.appearance} + role=${x => x.role} + ?inset=${x => x.inset} + orientation=${x => x.orientation} + > + ${x => (x.content ? html`

${x.content}

` : '')} +
+
+`; + +const dividerSvgTemplate = html` +
+ + + + + +
+`; + +const dividerSvgVerticalTemplate = html` + x.role} ?inset=${x => x.inset} orientation="vertical"> + + + + +`; + +export default { + title: 'Components/Divider', + args: { + content: 'Section One', + alignContent: undefined, + appearance: undefined, + role: DividerRole.separator, + inset: false, + orientation: undefined, + }, + argTypes: { + content: { + description: 'HTML element wrapping text (e.g. `

Section One

`), Image or SVG', + table: { + defaultValue: { + summary: 'empty', + }, + }, + }, + alignContent: { + description: 'Align content', + table: { + type: { + summary: 'Fluent v9. Determines the alignment of the content within the divider.', + }, + defaultValue: { + summary: 'undefined', + }, + }, + options: Object.values(DividerAlignContent), + control: { + type: 'select', + }, + }, + appearance: { + description: 'Divider and text colors', + table: { + type: { + summary: 'Fluent v9. A divider can have one of the preset appearances.', + }, + defaultValue: { + summary: 'undefined', + }, + }, + options: Object.values(DividerAppearance), + control: { + type: 'select', + }, + }, + role: { + description: 'Set role attribute', + table: { + type: { + summary: 'Inherited from FASTDivider. Aria role for the divider.', + }, + defaultValue: { + summary: 'separator', + }, + }, + options: Object.values(DividerRole), + control: { + type: 'select', + }, + }, + inset: { + description: 'Pad the ends of divider', + table: { + type: { + summary: + 'Type: boolean. Fluent v9. Divider layout is block for strict distinctions between items, or inset for closer relationships with neighboring content.', + }, + defaultValue: { + summary: false, + }, + }, + control: 'boolean', + }, + orientation: { + description: 'Divider layout', + table: { + type: { + summary: + 'Inherited from FASTDivider. Layout can be horizontal or vertical. Adds aria-orientation to component.', + }, + defaultValue: { + summary: undefined, + }, + }, + options: Object.values(DividerOrientation), + control: { + type: 'select', + }, + }, + }, +} as DividerStoryMeta; + +export const Divider = renderComponent(dividerTemplate).bind({}); +export const DividerWithSvg = renderComponent(dividerSvgTemplate).bind({}); +export const VerticalDividerWithSvg = renderComponent(dividerSvgVerticalTemplate).bind({}); diff --git a/packages/web-components/src/divider/divider.styles.ts b/packages/web-components/src/divider/divider.styles.ts new file mode 100644 index 00000000000000..e302ab9271da08 --- /dev/null +++ b/packages/web-components/src/divider/divider.styles.ts @@ -0,0 +1,124 @@ +import { css } from '@microsoft/fast-element'; +import { display } from '@microsoft/fast-foundation'; +import { + colorBrandForeground1, + colorBrandStroke1, + colorNeutralForeground1, + colorNeutralForeground2, + colorNeutralForeground3, + colorNeutralStroke1, + colorNeutralStroke2, + colorNeutralStroke3, + fontFamilyBase, + fontSizeBase200, + fontWeightRegular, + strokeWidthThin, +} from '../theme/design-tokens.js'; + +/** Divider styles + * @public + */ +export const styles = css` + ${display('flex')} + + :host::after, + :host::before { + align-self: center; + background: ${colorNeutralStroke2}; + box-sizing: border-box; + content: ''; + display: flex; + flex-grow: 1; + height: ${strokeWidthThin}; + } + + :host([inset]) { + padding: 0 12px; + } + + :host ::slotted(*) { + color: ${colorNeutralForeground2}; + font-family: ${fontFamilyBase}; + font-size: ${fontSizeBase200}; + font-weight: ${fontWeightRegular}; + margin: 0; + padding: 0 12px; + } + + :host([align-content='start'])::before, + :host([align-content='end'])::after { + flex-basis: 12px; + flex-grow: 0; + flex-shrink: 0; + } + + :host([orientation='vertical']) { + height: 100%; + min-height: 84px; + } + :host([orientation='vertical']):empty { + min-height: 20px; + } + + :host([orientation='vertical']) { + flex-direction: column; + align-items: center; + } + + :host([orientation='vertical'][inset])::before { + margin-top: 12px; + } + :host([orientation='vertical'][inset])::after { + margin-bottom: 12px; + } + + :host([orientation='vertical']):empty::before, + :host([orientation='vertical']):empty::after { + height: 10px; + min-height: 10px; + flex-grow: 0; + } + + :host([orientation='vertical'])::before, + :host([orientation='vertical'])::after { + width: ${strokeWidthThin}; + min-height: 20px; + height: 100%; + } + + :host([orientation='vertical']) ::slotted(*) { + display: flex; + flex-direction: column; + padding: 12px 0; + line-height: 20px; + } + + :host([orientation='vertical'][align-content='start'])::before { + min-height: 8px; + } + :host([orientation='vertical'][align-content='end'])::after { + min-height: 8px; + } + + :host([appearance='strong'])::before, + :host([appearance='strong'])::after { + background: ${colorNeutralStroke1}; + } + :host([appearance='strong']) ::slotted(*) { + color: ${colorNeutralForeground1}; + } + :host([appearance='brand'])::before, + :host([appearance='brand'])::after { + background: ${colorBrandStroke1}; + } + :host([appearance='brand']) ::slotted(*) { + color: ${colorBrandForeground1}; + } + :host([appearance='subtle'])::before, + :host([appearance='subtle'])::after { + background: ${colorNeutralStroke3}; + } + :host([appearance='subtle']) ::slotted(*) { + color: ${colorNeutralForeground3}; + } +`; diff --git a/packages/web-components/src/divider/divider.template.ts b/packages/web-components/src/divider/divider.template.ts new file mode 100644 index 00000000000000..eecca0cb4535c8 --- /dev/null +++ b/packages/web-components/src/divider/divider.template.ts @@ -0,0 +1,9 @@ +import { ElementViewTemplate } from '@microsoft/fast-element'; +import { dividerTemplate } from '@microsoft/fast-foundation'; +import type { Divider } from './divider.js'; + +/** + * Template for the Divider component + * @public + */ +export const template: ElementViewTemplate = dividerTemplate(); diff --git a/packages/web-components/src/divider/divider.ts b/packages/web-components/src/divider/divider.ts new file mode 100644 index 00000000000000..9c208437d590a1 --- /dev/null +++ b/packages/web-components/src/divider/divider.ts @@ -0,0 +1,38 @@ +import { attr } from '@microsoft/fast-element'; +import { FASTDivider } from '@microsoft/fast-foundation'; +import { DividerAlignContent, DividerAppearance } from './divider.options.js'; + +/** + * @class Divider component + * + * @remarks + * This class extends the FASTDivider. A divider groups sections of content to create visual rhythm and hierarchy. Use dividers along with spacing and headers to organize content in your layout. + */ +export class Divider extends FASTDivider { + /** + * @property alignContent + * @default center + * @remarks + * Determines the alignment of the content within the divider. Select from start or end. When not specified, the content will be aligned to the center. + */ + @attr({ attribute: 'align-content' }) + public alignContent?: DividerAlignContent; + + /** + * @property appearance + * @default default + * @remarks + * A divider can have one of the preset appearances. Select from strong, brand, subtle. When not specified, the divider has its default appearance. + */ + @attr + public appearance?: DividerAppearance; + + /** + * @property inset + * @default false + * @remarks + * Adds padding to the beginning and end of the divider. + */ + @attr({ mode: 'boolean' }) + public inset?: boolean; +} diff --git a/packages/web-components/src/divider/index.ts b/packages/web-components/src/divider/index.ts new file mode 100644 index 00000000000000..5ee2dd0fcaf33e --- /dev/null +++ b/packages/web-components/src/divider/index.ts @@ -0,0 +1,5 @@ +export * from './divider.js'; +export * from './divider.options.js'; +export { definition as DividerDefinition } from './divider.definition.js'; +export { template as DividerTemplate } from './divider.template.js'; +export { styles as DividerStyles } from './divider.styles.js'; diff --git a/packages/web-components/src/index.ts b/packages/web-components/src/index.ts index eaf2580651e94a..f8394d0a1cf521 100644 --- a/packages/web-components/src/index.ts +++ b/packages/web-components/src/index.ts @@ -2,6 +2,7 @@ export * from './accordion/index.js'; export * from './accordion-item/index.js'; export * from './badge/index.js'; export * from './counter-badge/index.js'; +export * from './divider/index.js'; export * from './image/index.js'; export * from './progress-bar/index.js'; export * from './spinner/index.js';