diff --git a/src/dev-app/mdc-input/mdc-input-demo.html b/src/dev-app/mdc-input/mdc-input-demo.html index 2e6a578f0119..3a3fa26c202d 100644 --- a/src/dev-app/mdc-input/mdc-input-demo.html +++ b/src/dev-app/mdc-input/mdc-input-demo.html @@ -415,6 +415,81 @@

Textarea

+ + Dynamic Subscript Sizing + +

+ One validation + + Fill appearance + + This field is required + +

+ +

+ One very long validation that wraps + + Fill appearance + + This field is extremely, very much, absolutely positively required so do not forget it! + +

+ +

+ One hint and one validation + + Fill appearance + + This field is required + Please type something here + +

+ +

+ Multiple errors + + Fill appearance + + AAA + BBB + CCC + +

+ +

+ Multiple hints + + Fill appearance + + aaa + bbb + ccc + +

+ +

+ Multiple hints with differing alignment + + Fill appearance + + aaa + bbb + ccc + +

+ +

+ No hints or errors + + Fill appearance + + +

+
+
+ Number Inputs diff --git a/src/material-experimental/mdc-form-field/_form-field-subscript.scss b/src/material-experimental/mdc-form-field/_form-field-subscript.scss index 4eaa74f2cb2d..57528e11c16f 100644 --- a/src/material-experimental/mdc-form-field/_form-field-subscript.scss +++ b/src/material-experimental/mdc-form-field/_form-field-subscript.scss @@ -23,12 +23,23 @@ padding: 0 mdc-textfield-variables.$padding-horizontal; } + .mat-mdc-form-field-subscript-dynamic-size { + .mat-mdc-form-field-hint-wrapper, + .mat-mdc-form-field-error-wrapper { + position: static; + } + } + .mat-mdc-form-field-bottom-align::before { content: ''; display: inline-block; height: 16px; } + .mat-mdc-form-field-bottom-align.mat-mdc-form-field-subscript-dynamic-size::before { + content: unset; + } + .mat-mdc-form-field-hint-end { order: 1; } diff --git a/src/material-experimental/mdc-form-field/form-field.html b/src/material-experimental/mdc-form-field/form-field.html index 8dde556e1776..b04dae1bc91e 100644 --- a/src/material-experimental/mdc-form-field/form-field.html +++ b/src/material-experimental/mdc-form-field/form-field.html @@ -71,6 +71,7 @@
diff --git a/src/material-experimental/mdc-form-field/form-field.ts b/src/material-experimental/mdc-form-field/form-field.ts index 2efd38383c73..6a3837c14820 100644 --- a/src/material-experimental/mdc-form-field/form-field.ts +++ b/src/material-experimental/mdc-form-field/form-field.ts @@ -61,6 +61,9 @@ export type FloatLabelType = 'always' | 'auto'; /** Possible appearance styles for the form field. */ export type MatFormFieldAppearance = 'fill' | 'outline'; +/** Behaviors for how the subscript height is set. */ +export type SubscriptSizing = 'fixed' | 'dynamic'; + /** * Represents the default options for the form field that can be configured * using the `MAT_FORM_FIELD_DEFAULT_OPTIONS` injection token. @@ -69,6 +72,7 @@ export interface MatFormFieldDefaultOptions { appearance?: MatFormFieldAppearance; hideRequiredMarker?: boolean; floatLabel?: FloatLabelType; + subscriptSizing?: SubscriptSizing; } /** @@ -87,6 +91,9 @@ const DEFAULT_APPEARANCE: MatFormFieldAppearance = 'fill'; /** Default appearance used by the form-field. */ const DEFAULT_FLOAT_LABEL: FloatLabelType = 'auto'; +/** Default way that the suffix element height is set. */ +const DEFAULT_SUBSCRIPT_SIZING: SubscriptSizing = 'fixed'; + /** * Default transform for docked floating labels in a MDC text-field. This value has been * extracted from the MDC text-field styles because we programmatically modify the docked @@ -206,6 +213,20 @@ export class MatFormField } private _appearance: MatFormFieldAppearance = DEFAULT_APPEARANCE; + /** + * Whether the form field should reserve space for one line of hint/error text (default) + * or to have the spacing grow from 0px as needed based on the size of the hint/error content. + * Note that when using dynamic sizing, layout shifts will occur when hint/error text changes. + */ + @Input() + get subscriptSizing(): SubscriptSizing { + return this._subscriptSizing || this._defaults?.subscriptSizing || DEFAULT_SUBSCRIPT_SIZING; + } + set subscriptSizing(value: SubscriptSizing) { + this._subscriptSizing = value || this._defaults?.subscriptSizing || DEFAULT_SUBSCRIPT_SIZING; + } + private _subscriptSizing: SubscriptSizing = DEFAULT_SUBSCRIPT_SIZING; + /** Text for the form field hint. */ @Input() get hintLabel(): string { diff --git a/src/material-experimental/mdc-input/input.spec.ts b/src/material-experimental/mdc-input/input.spec.ts index db72cfd45405..5d98f2f97898 100644 --- a/src/material-experimental/mdc-input/input.spec.ts +++ b/src/material-experimental/mdc-input/input.spec.ts @@ -32,6 +32,7 @@ import { MatFormField, MatFormFieldAppearance, MatFormFieldModule, + SubscriptSizing, } from '@angular/material-experimental/mdc-form-field'; import {MatIconModule} from '@angular/material/icon'; import {By} from '@angular/platform-browser'; @@ -1406,6 +1407,47 @@ describe('MatFormField default options', () => { expect(fixture.componentInstance.formField.hideRequiredMarker).toBe(true); expect(fixture.componentInstance.formField.appearance).toBe('outline'); }); + + it('defaults subscriptSizing to false', () => { + const fixture = createComponent(MatInputWithSubscriptSizing); + fixture.detectChanges(); + + const subscriptElement = fixture.nativeElement.querySelector( + '.mat-mdc-form-field-subscript-wrapper', + ); + + expect(fixture.componentInstance.formField.subscriptSizing).toBe('fixed'); + expect(subscriptElement.classList.contains('mat-mdc-form-field-subscript-dynamic-size')).toBe( + false, + ); + + fixture.componentInstance.sizing = 'dynamic'; + fixture.detectChanges(); + + expect(fixture.componentInstance.formField.subscriptSizing).toBe('dynamic'); + expect(subscriptElement.classList.contains('mat-mdc-form-field-subscript-dynamic-size')).toBe( + true, + ); + }); + + it('changes the default value of subscriptSizing', () => { + const fixture = createComponent(MatInputWithSubscriptSizing, [ + { + provide: MAT_FORM_FIELD_DEFAULT_OPTIONS, + useValue: { + subscriptSizing: 'dynamic', + }, + }, + ]); + + fixture.detectChanges(); + expect(fixture.componentInstance.formField.subscriptSizing).toBe('dynamic'); + expect( + fixture.nativeElement + .querySelector('.mat-mdc-form-field-subscript-wrapper') + .classList.contains('mat-mdc-form-field-subscript-dynamic-size'), + ).toBe(true); + }); }); function configureTestingModule( @@ -1815,6 +1857,19 @@ class MatInputWithAppearance { appearance: MatFormFieldAppearance; } +@Component({ + template: ` + + My Label + + + `, +}) +class MatInputWithSubscriptSizing { + @ViewChild(MatFormField) formField: MatFormField; + sizing: SubscriptSizing; +} + @Component({ template: `