From 2c65ff58629e20f900ab23b8a07329e80a356f2f Mon Sep 17 00:00:00 2001 From: mollykreis <20542556+mollykreis@users.noreply.github.com> Date: Wed, 6 Nov 2024 13:13:41 -0600 Subject: [PATCH] make ErrorPattern a mixin --- .../nimble-components/src/combobox/index.ts | 19 +----- .../src/number-field/index.ts | 21 +------ .../src/patterns/error/types.ts | 59 ++++++++++++++----- .../src/radio-group/index.ts | 15 +---- .../src/rich-text/editor/index.ts | 26 +------- .../nimble-components/src/select/index.ts | 23 +------- .../nimble-components/src/text-area/index.ts | 28 +-------- .../nimble-components/src/text-field/index.ts | 21 +------ 8 files changed, 60 insertions(+), 152 deletions(-) diff --git a/packages/nimble-components/src/combobox/index.ts b/packages/nimble-components/src/combobox/index.ts index 4bf3395a25..b0b295da25 100644 --- a/packages/nimble-components/src/combobox/index.ts +++ b/packages/nimble-components/src/combobox/index.ts @@ -32,7 +32,7 @@ import { iconArrowExpanderDownTag } from '../icons/arrow-expander-down'; import { iconExclamationMarkTag } from '../icons/exclamation-mark'; import { styles } from './styles'; -import type { ErrorPattern } from '../patterns/error/types'; +import { mixinErrorPattern } from '../patterns/error/types'; import { DropdownAppearance, type DropdownPattern @@ -52,20 +52,11 @@ declare global { * A nimble-styed HTML combobox */ export class Combobox - extends FormAssociatedCombobox - implements DropdownPattern, ErrorPattern { + extends mixinErrorPattern(FormAssociatedCombobox) + implements DropdownPattern { @attr public appearance: DropdownAppearance = DropdownAppearance.underline; - /** - * A message explaining why the value is invalid. - */ - @attr({ attribute: 'error-text' }) - public errorText?: string; - - @attr({ attribute: 'error-visible', mode: 'boolean' }) - public errorVisible = false; - /** * The autocomplete attribute. */ @@ -143,10 +134,6 @@ export class Combobox @observable public hasOverflow = false; - /* @internal */ - @observable - public errorHasOverflow = false; - public override get value(): string { Observable.track(this, 'value'); return this._value; diff --git a/packages/nimble-components/src/number-field/index.ts b/packages/nimble-components/src/number-field/index.ts index 1a22420801..9e3a285f6f 100644 --- a/packages/nimble-components/src/number-field/index.ts +++ b/packages/nimble-components/src/number-field/index.ts @@ -8,7 +8,7 @@ import { import { styles } from './styles'; import { NumberFieldAppearance } from './types'; import { errorTextTemplate } from '../patterns/error/template'; -import type { ErrorPattern } from '../patterns/error/types'; +import { mixinErrorPattern } from '../patterns/error/types'; import { buttonTag } from '../button'; import { iconMinusWideTag } from '../icons/minus-wide'; import { iconAddTag } from '../icons/add'; @@ -27,27 +27,10 @@ declare global { /** * A nimble-styled HTML number input */ -export class NumberField extends FoundationNumberField implements ErrorPattern { +export class NumberField extends mixinErrorPattern(FoundationNumberField) { @attr public appearance: NumberFieldAppearance = NumberFieldAppearance.underline; - /** - * A message explaining why the value is invalid. - * - * @public - * @remarks - * HTML Attribute: error-text - */ - @attr({ attribute: 'error-text' }) - public errorText?: string; - - @attr({ attribute: 'error-visible', mode: 'boolean' }) - public errorVisible = false; - - /* @internal */ - @observable - public errorHasOverflow = false; - public override connectedCallback(): void { super.connectedCallback(); diff --git a/packages/nimble-components/src/patterns/error/types.ts b/packages/nimble-components/src/patterns/error/types.ts index d9dfa674eb..45a47e57f5 100644 --- a/packages/nimble-components/src/patterns/error/types.ts +++ b/packages/nimble-components/src/patterns/error/types.ts @@ -1,21 +1,50 @@ -/** - * This interface should be used by components that want to leverage the error pattern. The error pattern conditionally adds error text to the template when an error is configured to be visible. - */ +import { attr, FASTElement, observable } from '@microsoft/fast-element'; + export interface ErrorPattern { - /** - * The error text that will be displayed when a component is in the error appearance - */ errorText?: string; - - /* - * Show the error appearance of the control - */ errorVisible: boolean; + errorHasOverflow: boolean; +} - /* @internal - * Indicates if the error text has overflowed its container. The value should not be - * set directly. Instead, it is used with the `overflow` directive. When declared in an - * implementation of `ErrorPattern`, it must be declared as `@observable`. +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type FASTElementConstructor = abstract new (...args: any[]) => FASTElement; + +// As the returned class is internal to the function, we can't write a signature that uses is directly, so rely on inference +// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/explicit-function-return-type +export function mixinErrorPattern(base: TBase) { + /** + * The Mixin that provides a concrete column with the API to support being resized + * proportionally within a Table. */ - errorHasOverflow: boolean; + abstract class ErrorPatternElement extends base implements ErrorPattern { + /** + * The error text that will be displayed when a component is in the error appearance + */ + public errorText?: string; + + /* + * Show the error appearance of the control + */ + public errorVisible = false; + + /* @internal + * Indicates if the error text has overflowed its container. The value should not be + * set directly. Instead, it is used with the `overflow` directive. + */ + public errorHasOverflow = false; + } + + attr({ attribute: 'error-text' })( + ErrorPatternElement.prototype, + 'errorText' + ); + attr({ attribute: 'error-visible', mode: 'boolean' })( + ErrorPatternElement.prototype, + 'errorVisible' + ); + observable( + ErrorPatternElement.prototype, + 'errorHasOverflow' + ); + return ErrorPatternElement; } diff --git a/packages/nimble-components/src/radio-group/index.ts b/packages/nimble-components/src/radio-group/index.ts index 04d894a6c5..d55f3f7087 100644 --- a/packages/nimble-components/src/radio-group/index.ts +++ b/packages/nimble-components/src/radio-group/index.ts @@ -3,10 +3,9 @@ import { DesignSystem } from '@microsoft/fast-foundation'; import { Orientation } from '@microsoft/fast-web-utilities'; -import { attr, observable } from '@microsoft/fast-element'; import { styles } from './styles'; import { template } from './template'; -import type { ErrorPattern } from '../patterns/error/types'; +import { mixinErrorPattern } from '../patterns/error/types'; declare global { interface HTMLElementTagNameMap { @@ -19,17 +18,7 @@ export { Orientation }; /** * A nimble-styled grouping element for radio buttons */ -export class RadioGroup extends FoundationRadioGroup implements ErrorPattern { - @attr({ attribute: 'error-text' }) - public errorText?: string; - - @attr({ attribute: 'error-visible', mode: 'boolean' }) - public errorVisible = false; - - /* @internal */ - @observable - public errorHasOverflow = false; -} +export class RadioGroup extends mixinErrorPattern(FoundationRadioGroup) { } const nimbleRadioGroup = RadioGroup.compose({ baseName: 'radio-group', diff --git a/packages/nimble-components/src/rich-text/editor/index.ts b/packages/nimble-components/src/rich-text/editor/index.ts index ea8d245658..c828fbaf82 100644 --- a/packages/nimble-components/src/rich-text/editor/index.ts +++ b/packages/nimble-components/src/rich-text/editor/index.ts @@ -25,7 +25,7 @@ import { MentionDetail, FormatButtonsState } from './types'; -import type { ErrorPattern } from '../../patterns/error/types'; +import { mixinErrorPattern } from '../../patterns/error/types'; import { RichTextMarkdownParser } from '../models/markdown-parser'; import { RichTextMarkdownSerializer } from '../models/markdown-serializer'; import { RichText } from '../base'; @@ -44,7 +44,7 @@ declare global { /** * A nimble styled rich text editor */ -export class RichTextEditor extends RichText implements ErrorPattern { +export class RichTextEditor extends mixinErrorPattern(RichText) { /** * @internal */ @@ -90,24 +90,6 @@ export class RichTextEditor extends RichText implements ErrorPattern { @attr({ attribute: 'footer-hidden', mode: 'boolean' }) public footerHidden = false; - /** - * Whether to display the error state. - * - * @public - * HTML Attribute: error-visible - */ - @attr({ attribute: 'error-visible', mode: 'boolean' }) - public errorVisible = false; - - /** - * A message explaining why the value is invalid. - * - * @public - * HTML Attribute: error-text - */ - @attr({ attribute: 'error-text' }) - public errorText?: string; - /** * @public * HTML Attribute: placeholder @@ -196,10 +178,6 @@ export class RichTextEditor extends RichText implements ErrorPattern { */ public editorContainer!: HTMLDivElement; - /* @internal */ - @observable - public errorHasOverflow = false; - private resizeObserver?: ResizeObserver; private updateScrollbarWidthQueued = false; diff --git a/packages/nimble-components/src/select/index.ts b/packages/nimble-components/src/select/index.ts index 08d3a70cd5..f8a5b9deaf 100644 --- a/packages/nimble-components/src/select/index.ts +++ b/packages/nimble-components/src/select/index.ts @@ -35,7 +35,7 @@ import { ListOptionOwner } from '../patterns/dropdown/types'; import { errorTextTemplate } from '../patterns/error/template'; -import type { ErrorPattern } from '../patterns/error/types'; +import { mixinErrorPattern } from '../patterns/error/types'; import { iconExclamationMarkTag } from '../icons/exclamation-mark'; import { isListOption, isListOptionGroup, template } from './template'; import type { ListOption } from '../list-option'; @@ -71,8 +71,8 @@ const isOptionOrGroupVisible = (el: ListOption | ListOptionGroup): boolean => { * A nimble-styled HTML select. */ export class Select - extends FormAssociatedSelect - implements ErrorPattern, ListOptionOwner { + extends mixinErrorPattern(FormAssociatedSelect) + implements ListOptionOwner { @attr public appearance: DropdownAppearance = DropdownAppearance.underline; @@ -84,19 +84,6 @@ export class Select @attr({ attribute: 'position' }) public positionAttribute?: SelectPosition; - /** - * A message explaining why the value is invalid. - * - * @public - * @remarks - * HTML Attribute: error-text - */ - @attr({ attribute: 'error-text' }) - public errorText: string | undefined; - - @attr({ attribute: 'error-visible', mode: 'boolean' }) - public errorVisible = false; - @attr({ attribute: 'filter-mode' }) public filterMode: FilterMode = FilterMode.none; @@ -213,10 +200,6 @@ export class Select return slotTextContent(this.labelSlot); } - /* @internal */ - @observable - public errorHasOverflow = false; - private _value = ''; private forcedPosition = false; private openActiveIndex?: number; diff --git a/packages/nimble-components/src/text-area/index.ts b/packages/nimble-components/src/text-area/index.ts index eb6c5b32ac..e661444202 100644 --- a/packages/nimble-components/src/text-area/index.ts +++ b/packages/nimble-components/src/text-area/index.ts @@ -3,7 +3,7 @@ import { DesignSystem, TextArea as FoundationTextArea } from '@microsoft/fast-foundation'; -import type { ErrorPattern } from '../patterns/error/types'; +import { mixinErrorPattern } from '../patterns/error/types'; import { styles } from './styles'; import { template } from './template'; import { TextAreaAppearance } from './types'; @@ -17,7 +17,7 @@ declare global { /** * A nimble-styed HTML text area */ -export class TextArea extends FoundationTextArea implements ErrorPattern { +export class TextArea extends mixinErrorPattern(FoundationTextArea) { /** * The appearance the text area should have. * @@ -28,26 +28,6 @@ export class TextArea extends FoundationTextArea implements ErrorPattern { @attr public appearance: TextAreaAppearance = TextAreaAppearance.outline; - /** - * A message explaining why the value is invalid. - * - * @public - * @remarks - * HTML Attribute: error-text - */ - @attr({ attribute: 'error-text' }) - public errorText?: string; - - /** - * Whether to display the error state. - * - * @public - * @remarks - * HTML Attribute: error-visible - */ - @attr({ attribute: 'error-visible', mode: 'boolean' }) - public errorVisible = false; - /** * The width of the vertical scrollbar, if displayed. * @internal @@ -55,10 +35,6 @@ export class TextArea extends FoundationTextArea implements ErrorPattern { @observable public scrollbarWidth = -1; - /* @internal */ - @observable - public errorHasOverflow = false; - private resizeObserver?: ResizeObserver; private updateScrollbarWidthQueued = false; diff --git a/packages/nimble-components/src/text-field/index.ts b/packages/nimble-components/src/text-field/index.ts index fc7df01ffd..5fbc96cdb8 100644 --- a/packages/nimble-components/src/text-field/index.ts +++ b/packages/nimble-components/src/text-field/index.ts @@ -8,7 +8,7 @@ import { import { styles } from './styles'; import { TextFieldAppearance } from './types'; import { errorTextTemplate } from '../patterns/error/template'; -import type { ErrorPattern } from '../patterns/error/types'; +import { mixinErrorPattern } from '../patterns/error/types'; import { iconExclamationMarkTag } from '../icons/exclamation-mark'; declare global { @@ -20,7 +20,7 @@ declare global { /** * A nimble-styed HTML text input */ -export class TextField extends FoundationTextField implements ErrorPattern { +export class TextField extends mixinErrorPattern(FoundationTextField) { /** * The appearance the text field should have. * @@ -31,25 +31,8 @@ export class TextField extends FoundationTextField implements ErrorPattern { @attr public appearance: TextFieldAppearance = TextFieldAppearance.underline; - /** - * A message explaining why the value is invalid. - * - * @public - * @remarks - * HTML Attribute: error-text - */ - @attr({ attribute: 'error-text' }) - public errorText?: string; - - @attr({ attribute: 'error-visible', mode: 'boolean' }) - public errorVisible = false; - @attr({ attribute: 'full-bleed', mode: 'boolean' }) public fullBleed = false; - - /* @internal */ - @observable - public errorHasOverflow = false; } const nimbleTextField = TextField.compose({