From 69d8cb9e5ac9dc065dc2d0a6df14cbf422bf1430 Mon Sep 17 00:00:00 2001 From: Kham Udom <37851220+khamudom@users.noreply.github.com> Date: Thu, 18 Mar 2021 16:05:11 -0700 Subject: [PATCH] feat: add number-field web component (#17494) * created and add number-field files * Change files * ran yarn build --- ...-6b5e7aec-7c00-49cf-8a0d-66fc803d4b59.json | 7 + packages/web-components/docs/api-report.md | 14 ++ packages/web-components/src/index.ts | 1 + .../number-field/fixtures/number-field.html | 113 +++++++++ .../web-components/src/number-field/index.ts | 58 +++++ .../src/number-field/number-field.stories.ts | 24 ++ .../src/number-field/number-field.styles.ts | 223 ++++++++++++++++++ 7 files changed, 440 insertions(+) create mode 100644 change/@fluentui-web-components-6b5e7aec-7c00-49cf-8a0d-66fc803d4b59.json create mode 100644 packages/web-components/src/number-field/fixtures/number-field.html create mode 100644 packages/web-components/src/number-field/index.ts create mode 100644 packages/web-components/src/number-field/number-field.stories.ts create mode 100644 packages/web-components/src/number-field/number-field.styles.ts diff --git a/change/@fluentui-web-components-6b5e7aec-7c00-49cf-8a0d-66fc803d4b59.json b/change/@fluentui-web-components-6b5e7aec-7c00-49cf-8a0d-66fc803d4b59.json new file mode 100644 index 00000000000000..941dcfc1d499b4 --- /dev/null +++ b/change/@fluentui-web-components-6b5e7aec-7c00-49cf-8a0d-66fc803d4b59.json @@ -0,0 +1,7 @@ +{ + "type": "minor", + "comment": "created and add number-field files", + "packageName": "@fluentui/web-components", + "email": "khamu@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/packages/web-components/docs/api-report.md b/packages/web-components/docs/api-report.md index f99d6eeb2927ed..f6b5021fe3fd38 100644 --- a/packages/web-components/docs/api-report.md +++ b/packages/web-components/docs/api-report.md @@ -31,6 +31,7 @@ import { Listbox } from '@microsoft/fast-foundation'; import { ListboxOption } from '@microsoft/fast-foundation'; import { Menu } from '@microsoft/fast-foundation'; import { MenuItem } from '@microsoft/fast-foundation'; +import { NumberField } from '@microsoft/fast-foundation'; import { Radio } from '@microsoft/fast-foundation'; import { RadioGroup } from '@microsoft/fast-foundation'; import { Select } from '@microsoft/fast-foundation'; @@ -696,6 +697,13 @@ export class FluentMenu extends Menu { export class FluentMenuItem extends MenuItem { } +// @public +export class FluentNumberField extends NumberField { + appearance: NumberFieldAppearance; + // @internal (undocumented) + connectedCallback(): void; +} + // @public export class FluentOption extends ListboxOption { } @@ -1222,6 +1230,12 @@ export const neutralOutlineRest: SwatchRecipe; // @public export const neutralOutlineRestBehavior: CSSCustomPropertyBehavior; +// @public +export type NumberFieldAppearance = 'filled' | 'outline'; + +// @public +export const NumberFieldStyles: import("@microsoft/fast-element").ElementStyles; + // @public export const OptionStyles: import("@microsoft/fast-element").ElementStyles; diff --git a/packages/web-components/src/index.ts b/packages/web-components/src/index.ts index ef629d167159fe..389f4cac3ce751 100644 --- a/packages/web-components/src/index.ts +++ b/packages/web-components/src/index.ts @@ -18,6 +18,7 @@ export * from './listbox'; export * from './listbox-option'; export * from './menu/'; export * from './menu-item/'; +export * from './number-field/'; export * from './progress/'; export * from './radio/'; export * from './radio-group/'; diff --git a/packages/web-components/src/number-field/fixtures/number-field.html b/packages/web-components/src/number-field/fixtures/number-field.html new file mode 100644 index 00000000000000..534b49c9832d71 --- /dev/null +++ b/packages/web-components/src/number-field/fixtures/number-field.html @@ -0,0 +1,113 @@ + +

Number field

+

Default

+ + Label + +

Hide step

+ Label + +

With value

+ + +

With min 1 and max 10

+ + +

0.1 stepping

+ + +

Full Width

+ + +

Placeholder

+ + + +

Required

+ + + +

Disabled

+ + label + + + +

Read only

+ + label + + +

Autofocus

+ autofocus + + +

Maxlength

+ maxlength + + +

Minlength

+ minlength + + +

With start

+ + + + + + + +

With end

+ + + + + + +

Filled

+
Default
+ + label + +
Placeholder
+ + + +
Required
+ + + +
Disabled
+ + label + + + +
Read only
+ + + label + + + +

Visual vs audio label

+ + Visible label + + + +

With aria-label

+ + +
+ +

In a form

+ + +
+
diff --git a/packages/web-components/src/number-field/index.ts b/packages/web-components/src/number-field/index.ts new file mode 100644 index 00000000000000..086a274cb15f07 --- /dev/null +++ b/packages/web-components/src/number-field/index.ts @@ -0,0 +1,58 @@ +import { attr, customElement } from '@microsoft/fast-element'; +import { NumberField, NumberFieldTemplate as template } from '@microsoft/fast-foundation'; +import { NumberFieldStyles as styles } from './number-field.styles'; + +/** + * Number field appearances + * @public + */ +export type NumberFieldAppearance = 'filled' | 'outline'; + +/** + * The Fluent Number Field Custom Element. Implements {@link @microsoft/fast-foundation#NumberField}, + * {@link @microsoft/fast-foundation#NumberFieldTemplate} + * + * + * @public + * @remarks + * HTML Element: \ + * + * {@link https://developer.mozilla.org/en-US/docs/Web/API/ShadowRoot/delegatesFocus | delegatesFocus} + */ +@customElement({ + name: 'fluent-number-field', + shadowOptions: { + delegatesFocus: true, + mode: 'closed', + }, + styles, + template, +}) +export class FluentNumberField extends NumberField { + /** + * The appearance of the element. + * + * @public + * @remarks + * HTML Attribute: appearance + */ + @attr + public appearance: NumberFieldAppearance; + + /** + * @internal + */ + public connectedCallback() { + super.connectedCallback(); + + if (!this.appearance) { + this.appearance = 'outline'; + } + } +} + +/** + * Styles for NumberField + * @public + */ +export const NumberFieldStyles = styles; diff --git a/packages/web-components/src/number-field/number-field.stories.ts b/packages/web-components/src/number-field/number-field.stories.ts new file mode 100644 index 00000000000000..404110f745bf87 --- /dev/null +++ b/packages/web-components/src/number-field/number-field.stories.ts @@ -0,0 +1,24 @@ +import addons from '@storybook/addons'; +import { STORY_RENDERED } from '@storybook/core-events'; +import NumberFieldTemplate from './fixtures/number-field.html'; +import './index'; + +addons.getChannel().addListener(STORY_RENDERED, (name: string) => { + if (name.toLowerCase().startsWith('number-field')) { + document.querySelectorAll('.form').forEach(el => { + if (el instanceof HTMLFormElement) { + el.onsubmit = event => { + event.preventDefault(); + const form: HTMLFormElement = document.forms['myForm']; + console.log(form.elements['fname'].value, 'value of input'); + }; + } + }); + } +}); + +export default { + title: 'Number Field', +}; + +export const NumberField = () => NumberFieldTemplate; diff --git a/packages/web-components/src/number-field/number-field.styles.ts b/packages/web-components/src/number-field/number-field.styles.ts new file mode 100644 index 00000000000000..7cf3c867c5e464 --- /dev/null +++ b/packages/web-components/src/number-field/number-field.styles.ts @@ -0,0 +1,223 @@ +import { css } from '@microsoft/fast-element'; +import { disabledCursor, display, focusVisible, forcedColorsStylesheetBehavior } from '@microsoft/fast-foundation'; +import { SystemColors } from '@microsoft/fast-web-utilities'; +import { + accentFillActiveBehavior, + accentFillHoverBehavior, + accentFillRestBehavior, + heightNumber, + neutralFillHoverBehavior, + neutralFillInputHoverBehavior, + neutralFillInputRestBehavior, + neutralFillRestBehavior, + neutralFocusBehavior, + neutralForegroundRestBehavior, + neutralOutlineRestBehavior, +} from '../styles/index'; + +export const NumberFieldStyles = css` + ${display('inline-block')} :host { + font-family: var(--body-font); + outline: none; + user-select: none; + } + + .root { + box-sizing: border-box; + position: relative; + display: flex; + flex-direction: row; + color: ${neutralForegroundRestBehavior.var}; + background: ${neutralFillInputRestBehavior.var}; + border-radius: calc(var(--corner-radius) * 1px); + border: calc(var(--outline-width) * 1px) solid ${accentFillRestBehavior.var}; + height: calc(${heightNumber} * 1px); + } + + .control { + -webkit-appearance: none; + font: inherit; + background: transparent; + border: 0; + color: inherit; + height: calc(100% - 4px); + width: 100%; + margin-top: auto; + margin-bottom: auto; + border: none; + padding: 0 calc(var(--design-unit) * 2px + 1px); + font-size: var(--type-ramp-base-font-size); + line-height: var(--type-ramp-base-line-height); + } + + .control:hover, + .control:${focusVisible}, + .control:disabled, + .control:active { + outline: none; + } + + .controls { + opacity: 0; + } + + .label { + display: block; + color: ${neutralForegroundRestBehavior.var}; + cursor: pointer; + font-size: var(--type-ramp-base-font-size); + line-height: var(--type-ramp-base-line-height); + margin-bottom: 4px; + } + + .label__hidden { + display: none; + visibility: hidden; + } + + .start, + .end { + margin: auto; + fill: currentcolor; + } + + .step-up, + .step-down { + padding: 2px 10px; + cursor: pointer; + } + + .step-up:before, + .step-down:before { + content: ''; + display: block; + border: solid transparent 6px; + } + + .step-up:before { + border-bottom-color: ${neutralForegroundRestBehavior.var}; + } + + .step-down:before { + border-top-color: ${neutralForegroundRestBehavior.var}; + } + + ::slotted(svg) { + ${ + /* Glyph size and margin-left is temporary - + replace when adaptive typography is figured out */ '' + } width: 16px; + height: 16px; + } + + .start { + display: flex; + margin-inline-start: 11px; + } + + .end { + display: flex; + margin-inline-end: 11px; + } + + :host(:hover:not([disabled])) .root { + background: ${neutralFillInputHoverBehavior.var}; + border-color: ${accentFillHoverBehavior.var}; + } + + :host(:active:not([disabled])) .root { + background: ${neutralFillInputHoverBehavior.var}; + border-color: ${accentFillActiveBehavior.var}; + } + + :host(:focus-within:not([disabled])) .root { + border-color: ${neutralFocusBehavior.var}; + box-shadow: 0 0 0 1px ${neutralFocusBehavior.var} inset; + } + + :host(:hover:not([disabled])) .controls, + :host(:focus-within:not([disabled])) .controls { + opacity: 1; + } + + :host([appearance="filled"]) .root { + background: ${neutralFillRestBehavior.var}; + } + + :host([appearance="filled"]:hover:not([disabled])) .root { + background: ${neutralFillHoverBehavior.var}; + } + + :host([disabled]) .label, + :host([readonly]) .label, + :host([readonly]) .control, + :host([disabled]) .control { + cursor: ${disabledCursor}; + } + + :host([disabled]) { + opacity: var(--disabled-opacity); + } + + :host([disabled]) .control { + border-color: ${neutralOutlineRestBehavior.var}; + } +`.withBehaviors( + accentFillActiveBehavior, + accentFillHoverBehavior, + accentFillRestBehavior, + neutralFillHoverBehavior, + neutralFillInputHoverBehavior, + neutralFillInputRestBehavior, + neutralFillRestBehavior, + neutralFocusBehavior, + neutralForegroundRestBehavior, + neutralOutlineRestBehavior, + forcedColorsStylesheetBehavior( + css` + .root, + :host([appearance='filled']) .root { + forced-color-adjust: none; + background: ${SystemColors.Field}; + border-color: ${SystemColors.FieldText}; + } + :host(:hover:not([disabled])) .root, + :host([appearance='filled']:hover:not([disabled])) .root, + :host([appearance='filled']:hover) .root { + background: ${SystemColors.Field}; + border-color: ${SystemColors.Highlight}; + } + :host(:focus-within:enabled) .root { + border-color: ${SystemColors.Highlight}; + box-shadow: 0 0 0 1px ${SystemColors.Highlight} inset; + } + .control, + ::placeholder, + ::-webkit-input-placeholder { + color: ${SystemColors.FieldText}; + } + .step-up:before { + border-bottom-color: ${SystemColors.FieldText}; + } + .step-down:before { + border-top-color: ${SystemColors.FieldText}; + } + .start, + .end { + fill: ${SystemColors.FieldText}; + } + :host([disabled]) { + opacity: 1; + } + :host([disabled]) .root, + :host([appearance='filled']:hover[disabled]) .root { + border-color: ${SystemColors.GrayText}; + background: ${SystemColors.Field}; + } + :host([disabled]) ::placeholder, + :host([disabled]) ::-webkit-input-placeholder { + color: ${SystemColors.GrayText}; + } + `, + ), +);