Skip to content

Commit

Permalink
Adds basic implementation of Spinner component (#26392)
Browse files Browse the repository at this point in the history
* Adds basic implementation of Spinner component

* Adds inverted Story to Spinner

* Cleans up some typos

* Generates API report

* Adds change file

* Removes kebab-case for key in Spinner in favor of camelCase; Adds spinner export to package.json; Properly comments spinner options with type docs

* Address feedback in PR

* Minor typo cleanup

* Rebuilds
  • Loading branch information
procload authored Feb 14, 2023
1 parent 0b8e596 commit 4d959c0
Show file tree
Hide file tree
Showing 12 changed files with 318 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "prerelease",
"comment": "Adds Spinner as a web component",
"packageName": "@fluentui/web-components",
"email": "ryan@ryanmerrill.net",
"dependentChangeType": "patch"
}
39 changes: 39 additions & 0 deletions packages/web-components/docs/api-report.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { ElementViewTemplate } from '@microsoft/fast-element';
import { FASTElement } from '@microsoft/fast-element';
import { FASTElementDefinition } from '@microsoft/fast-element';
import { FASTProgress } from '@microsoft/fast-foundation';
import { FASTProgressRing } from '@microsoft/fast-foundation';
import { StartEnd } from '@microsoft/fast-foundation';
import { StartEndOptions } from '@microsoft/fast-foundation';
import { StaticallyComposableHTML } from '@microsoft/fast-foundation';
Expand Down Expand Up @@ -1351,6 +1352,44 @@ export const spacingVerticalXXS: CSSDesignToken<string>;
// @public (undocumented)
export const spacingVerticalXXXL: CSSDesignToken<string>;

// @public
export class Spinner extends FASTProgressRing {
appearance: SpinnerAppearance;
size: SpinnerSize;
}

// @public
export const SpinnerAppearance: {
readonly primary: "primary";
readonly inverted: "inverted";
};

// @public
export type SpinnerAppearance = ValuesOf<typeof SpinnerAppearance>;

// @public
export const SpinnerDefinition: FASTElementDefinition<typeof Spinner>;

// @public
export const SpinnerSize: {
readonly tiny: "tiny";
readonly extraSmall: "extra-small";
readonly small: "small";
readonly medium: "medium";
readonly large: "large";
readonly extraLarge: "extra-large";
readonly huge: "huge";
};

// @public
export type SpinnerSize = ValuesOf<typeof SpinnerSize>;

// @public (undocumented)
export const SpinnerStyles: ElementStyles;

// @public (undocumented)
export const SpinnerTemplate: ElementViewTemplate<Spinner>;

// @public (undocumented)
export const strokeWidthThick: CSSDesignToken<string>;

Expand Down
4 changes: 4 additions & 0 deletions packages/web-components/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@
"./progress-bar": {
"types": "./dist/esm/progress-bar/define.d.ts",
"default": "./dist/esm/progress-bar/define.js"
},
"./spinner": {
"types": "./dist/esm/spinner/define.d.ts",
"default": "./dist/esm/spinner/define.js"
}
},
"scripts": {
Expand Down
1 change: 1 addition & 0 deletions packages/web-components/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export * from './badge/index.js';
export * from './counter-badge/index.js';
export * from './progress-bar/index.js';
export * from './spinner/index.js';
export * from './text/index.js';

export * from './theme/index.js';
4 changes: 4 additions & 0 deletions packages/web-components/src/spinner/define.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { FluentDesignSystem } from '../fluent-design-system.js';
import { definition } from './spinner.definition.js';

definition.define(FluentDesignSystem.registry);
5 changes: 5 additions & 0 deletions packages/web-components/src/spinner/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export * from './spinner.js';
export * from './spinner.options.js';
export { template as SpinnerTemplate } from './spinner.template.js';
export { styles as SpinnerStyles } from './spinner.styles.js';
export { definition as SpinnerDefinition } from './spinner.definition.js';
19 changes: 19 additions & 0 deletions packages/web-components/src/spinner/spinner.definition.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { FluentDesignSystem } from '../fluent-design-system.js';
import { Spinner } from './spinner.js';
import { styles } from './spinner.styles.js';
import { template } from './spinner.template.js';

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

/**
* SpinnerAppearance constants
* @public
*/
export const SpinnerAppearance = {
primary: 'primary',
inverted: 'inverted',
} as const;

/**
* A Spinner's appearance can be either primary or inverted
* @public
*/
export type SpinnerAppearance = ValuesOf<typeof SpinnerAppearance>;

/**
* SpinnerSize constants
* @public
*/
export const SpinnerSize = {
tiny: 'tiny',
extraSmall: 'extra-small',
small: 'small',
medium: 'medium',
large: 'large',
extraLarge: 'extra-large',
huge: 'huge',
} as const;

/**
* A Spinner's size can be either small, tiny, extra-small, medium, large, extra-large, or huge
* @public
*/
export type SpinnerSize = ValuesOf<typeof SpinnerSize>;
53 changes: 53 additions & 0 deletions packages/web-components/src/spinner/spinner.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { html } from '@microsoft/fast-element';
import type { Args, Meta } from '@storybook/html';
import { renderComponent } from '../__test__/helpers.js';
import { SpinnerAppearance, SpinnerSize } from './spinner.options.js';
import './define.js';

type SpinnerStoryArgs = Args;
type SpinnerStoryMeta = Meta<SpinnerStoryArgs>;

const storyTemplate = html<SpinnerStoryArgs>`
<fluent-spinner appearance=${x => x.appearance} size=${x => x.size}></fluent-spinner>
`;

export default {
title: 'Components/Spinner',
argTypes: {
appearance: {
description: 'The appearance of the spinner',
table: {
defaultValue: { summary: 'primary' },
},
control: {
type: 'select',
options: Object.values(SpinnerAppearance),
},
defaultValue: 'primary',
},
size: {
description: 'The size of the spinner',
table: {
defaultValue: { summary: 'medium' },
},
control: {
type: 'select',
options: Object.values(SpinnerSize),
},
defaultValue: 'medium',
},
},
parameters: {
status: {
type: 'experimental',
},
},
} as SpinnerStoryMeta;

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

export const SpinnerInverted = renderComponent(html<SpinnerStoryArgs>`
<div style="background-color: #0f6cbd; padding: 20px;">
<fluent-spinner appearance="inverted" size="medium"></fluent-spinner>
</div>
`);
96 changes: 96 additions & 0 deletions packages/web-components/src/spinner/spinner.styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import { css } from '@microsoft/fast-element';
import { display } from '@microsoft/fast-foundation';
import { colorBrandStroke1, colorBrandStroke2, colorNeutralStrokeOnBrand2 } from '../theme/design-tokens.js';

export const styles = css`
${display('flex')}
:host {
display: flex;
align-items: center;
height: 32px;
width: 32px;
}
:host([size='tiny']) {
height: 20px;
width: 20px;
}
:host([size='extra-small']) {
height: 24px;
width: 24px;
}
:host([size='small']) {
height: 28px;
width: 28px;
}
:host([size='large']) {
height: 36px;
width: 36px;
}
:host([size='extra-large']) {
height: 40px;
width: 40px;
}
:host([size='huge']) {
height: 44px;
width: 44px;
}
.progress {
height: 100%;
width: 100%;
}
.background {
fill: none;
stroke: ${colorBrandStroke2};
stroke-width: 1.5px;
}
:host([appearance='inverted']) .background {
stroke: rgba(255, 255, 255, 0.2);
}
.determinate {
stroke: ${colorBrandStroke1};
fill: none;
stroke-width: 1.5px;
stroke-linecap: round;
transform-origin: 50% 50%;
transform: rotate(-90deg);
transition: all 0.2s ease-in-out;
}
:host([appearance='inverted']) .determinite {
stroke: ${colorNeutralStrokeOnBrand2};
}
.indeterminate-indicator-1 {
stroke: ${colorBrandStroke1};
fill: none;
stroke-width: 1.5px;
stroke-linecap: round;
transform-origin: 50% 50%;
transform: rotate(-90deg);
transition: all 0.2s ease-in-out;
animation: spin-infinite 3s cubic-bezier(0.53, 0.21, 0.29, 0.67) infinite;
}
:host([appearance='inverted']) .indeterminate-indicator-1 {
stroke: ${colorNeutralStrokeOnBrand2};
}
@keyframes spin-infinite {
0% {
stroke-dasharray: 0.01px 43.97px;
transform: rotate(0deg);
}
50% {
stroke-dasharray: 21.99px 21.99px;
transform: rotate(450deg);
}
100% {
stroke-dasharray: 0.01px 43.97px;
transform: rotate(1080deg);
}
}
`;
24 changes: 24 additions & 0 deletions packages/web-components/src/spinner/spinner.template.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import type { ElementViewTemplate } from '@microsoft/fast-element';
import { progressRingTemplate } from '@microsoft/fast-foundation';
import { Spinner } from './spinner.js';

export const template: ElementViewTemplate<Spinner> = progressRingTemplate({
indeterminateIndicator: `
<svg class="progress" part="progress" viewBox="0 0 16 16">
<circle
class="background"
part="background"
cx="8px"
cy="8px"
r="7px"
></circle>
<circle
class="indeterminate-indicator-1"
part="indeterminate-indicator-1"
cx="8px"
cy="8px"
r="7px"
></circle>
</svg>
`,
});
30 changes: 30 additions & 0 deletions packages/web-components/src/spinner/spinner.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { attr } from '@microsoft/fast-element';
import { FASTProgressRing } from '@microsoft/fast-foundation';
import type { SpinnerAppearance, SpinnerSize } from './spinner.options.js';

/**
* The base class used for constructing a fluent-spinner custom element
* @public
*/
export class Spinner extends FASTProgressRing {
/**
* The size of the spinner
*
* @public
* @default 'medium'
* @remarks
* HTML Attribute: size
*/
@attr
public size: SpinnerSize;

/**
* The appearance of the spinner
* @public
* @default 'primary'
* @remarks
* HTML Attribute: appearance
*/
@attr
public appearance: SpinnerAppearance;
}

0 comments on commit 4d959c0

Please sign in to comment.