Skip to content

Commit

Permalink
fix(material-experimental/mdc-form-field): notched-outline sho… (#18381)
Browse files Browse the repository at this point in the history
* fix(material-experimental/mdc-form-field): notched-outline should include prefixes and suffixes

When we landed the initial MDC form-field implementation, we
intentionally left the outline appearance with prefixes and suffixes in
a visually bad state. We did this because it was unclear how to proceed
due to a limitation with the MDC notched-outline.

After chatting more with the MDC team about this, and experimenting with
more alternatives, the form-field will now follow MDC as close as
possible. Instead of requiring changes upstream to MDC which aren't
necessarily reasonable (due to us supporting arbitrary prefix/suffix
content), we just use the notched-outline as intended.

To summarize what happened before: Our form-field supports abitrary
prefixes and suffixes. This is different in MDC, which always assumes a
fixed width/size for prefixes and suffixes. Ultimately, this causes
problems for us since in the outline appearance, the label should
be relative to the infix container (not overlap prefixes), but still
be aligned vertically over the prefix container. MDC achieves this by
simply shifting the label by a fixed amount of pixels (since they only
allow a specific width for prefixes and suffixes). This won't work for
us since our prefix container can scale as much as possible.

To reasonably fix this issue in a way that leverages/follows MDC as much
as possible, we determine the prefix container width programmatically
and shift the floating label horizontally based on the measured width.
This is basically the same approach MDC takes.. just that we have a
dynamic label offset measured at runtime.

For more context on the issue, refer to the original feature request in
MDC: material-components-web#5326.

* fixup! fix(material-experimental/mdc-form-field): notched-outline should include prefixes and suffixes

Address feedback; and handle SSR better
  • Loading branch information
devversion authored Feb 7, 2020
1 parent 782f114 commit fc91061
Show file tree
Hide file tree
Showing 9 changed files with 233 additions and 63 deletions.
25 changes: 25 additions & 0 deletions src/dev-app/mdc-input/mdc-input-demo.html
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,14 @@ <h4>Text</h4>
<span matSuffix>.00</span>
</mat-form-field>

<h4>Text (always outline)</h4>
<mat-form-field appearance="outline" class="demo-text-align-end">
<mat-label>Amount</mat-label>
<input matInput>
<span matPrefix>$&nbsp;</span>
<span matSuffix>.00</span>
</mat-form-field>

<h4>Icons</h4>
<mat-form-field [appearance]="prefixSuffixAppearance">
<mat-label>Amount</mat-label>
Expand Down Expand Up @@ -640,14 +648,31 @@ <h3>&lt;textarea&gt; with bindable autosize </h3>
<mat-label>Tab 1 input</mat-label>
<input matInput value="test">
</mat-form-field>

<mat-form-field appearance="outline" class="demo-text-align-end">
<mat-label>Amount</mat-label>
<input matInput>
<span matPrefix>$&nbsp;</span>
<span matSuffix>.00</span>
</mat-form-field>
</mat-tab>
<mat-tab label="Tab 2">
<mat-form-field appearance="outline">
<mat-label>Tab 2 input</mat-label>
<input matInput value="test">
</mat-form-field>

<mat-form-field appearance="outline" class="demo-text-align-end">
<mat-label>Amount</mat-label>
<input matInput>
<span matPrefix>$&nbsp;</span>
<span matPrefix *ngIf="showSecondPrefix">!&nbsp;</span>
<span matSuffix>.00</span>
</mat-form-field>
</mat-tab>
</mat-tab-group>

<button (click)="showSecondPrefix = !showSecondPrefix">Toggle second prefix.</button>
</mat-card-content>
</mat-card>

Expand Down
1 change: 1 addition & 0 deletions src/dev-app/mdc-input/mdc-input-demo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export class MdcInputDemo {
prefixSuffixAppearance: MatFormFieldAppearance = 'fill';
placeholderTestControl = new FormControl('', Validators.required);
options: string[] = ['One', 'Two', 'Three'];
showSecondPrefix = false;

name: string;
errorMessageExample1: string;
Expand Down
1 change: 1 addition & 0 deletions src/material-experimental/mdc-form-field/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ ng_module(
assets = [":form_field_scss"] + glob(["**/*.html"]),
module_name = "@angular/material-experimental/mdc-form-field",
deps = [
"//src/cdk/bidi",
"//src/cdk/observers",
"//src/cdk/platform",
"//src/material/core",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
@import '@material/notched-outline/variables.import';
@import '@material/textfield/variables.import';
@import 'form-field-sizing';

// Mixin that can be included to override the default MDC text-field
Expand Down Expand Up @@ -29,36 +31,32 @@
// spacing as we support arbitrary form-field controls which aren't necessarily matching
// the "mdc-text-field__input" class. Note: We need the first selector to overwrite the
// default no-label MDC padding styles which are set with a very high specificity.
.mdc-text-field--no-label:not(.mdc-text-field--outlined):not(.mdc-text-field--textarea)
.mdc-text-field--no-label:not(.mdc-text-field--textarea)
.mat-mdc-input-element.mdc-text-field__input,
.mat-mdc-input-element {
.mat-mdc-text-field-wrapper .mat-mdc-input-element {
padding: 0;
}

// MDC changes the vertical spacing of the input if there is no label. Since we moved
// the spacing of the input to the parent infix container (to support custom controls),
// we need to replicate these styles for the infix container. The goal is that the input
// placeholder vertically aligns with floating labels of other filled inputs. Note that
// outline appearance currently still relies on the input spacing due to a notched-outline
// limitation. TODO: https://github.com/material-components/material-components-web/issues/5326
.mdc-text-field--no-label:not(.mdc-text-field--outlined):not(.mdc-text-field--textarea)
.mat-mdc-form-field-infix {
padding-top: $mat-form-field-no-label-padding-top;
padding-bottom: $mat-form-field-no-label-padding-bottom;
}

// MDC adds vertical spacing to inputs. We removed this spacing and intend to add it
// to the infix container. This is necessary to ensure that custom form-field controls
// also have the proper Material Design spacing to the label and bottom-line. Note that
// outline appearance currently still relies on the input spacing due to a notched-outline
// limitation. TODO: https://github.com/material-components/material-components-web/issues/5326
.mat-mdc-text-field-wrapper:not(.mdc-text-field--outlined) .mat-mdc-form-field-infix {
// also have the proper Material Design spacing to the label and bottom-line.
.mat-mdc-text-field-wrapper .mat-mdc-form-field-infix {
// Apply the default text-field input padding to the infix container. We removed the
// padding from the input elements in order to support arbitrary form-field controls.
padding-top: $mat-form-field-with-label-input-padding-top;
padding-bottom: $mat-form-field-with-label-input-padding-bottom;
}

// MDC changes the vertical spacing of the input if there is no label, or in the outline
// appearance. This is because the input should vertically align with other inputs which use
// a floating label. To achieve this, we add custom vertical spacing to the infix container
// that differs from the vertical spacing for text-field's with a floating label.
.mdc-text-field--no-label:not(.mdc-text-field--textarea) .mat-mdc-form-field-infix,
.mat-mdc-text-field-wrapper.mdc-text-field--outlined .mat-mdc-form-field-infix {
padding-top: $mat-form-field-no-label-padding-top;
padding-bottom: $mat-form-field-no-label-padding-bottom;
}

// Root element of the mdc-text-field. As explained in the height overwrites above, MDC
// sets a default height on the text-field root element. This is not desired since we
// want the element to be able to expand as needed.
Expand All @@ -79,22 +77,33 @@
opacity: 1;
}

// The additional nesting is a temporary until the notched-outline is decoupled from the
// floating label. See https://github.com/material-components/material-components-web/issues/5326
// TODO(devversion): Remove this workaround/nesting once the feature is available.
.mat-mdc-text-field-wrapper:not(.mdc-text-field--outlined) {
// We removed the horizontal inset on input elements, but need to re-add the spacing to
// the actual form-field flex container that contains the prefixes, suffixes and infix.
.mat-mdc-form-field-flex {
padding: 0 $mdc-text-field-input-padding;
}
// We removed the horizontal inset on input elements, but need to re-add the spacing to
// the actual form-field flex container that contains the prefixes, suffixes and infix.
.mat-mdc-text-field-wrapper .mat-mdc-form-field-flex {
padding: 0 $mdc-text-field-input-padding;
}

// Since we moved the horizontal spacing from the input to the form-field flex container
// and the MDC floating label tries to account for the horizontal spacing, we need to reset
// the shifting since there is no padding the label needs to account for. Note that we do not
// want do this for labels in the notched-outline since MDC keeps those labels relative to
// the notched outline container, and already applies a specific horizontal spacing which
// we do not want to overwrite.
.mat-mdc-form-field-infix .mdc-floating-label {
left: 0;
right: 0;
}

// Since we moved the horizontal spacing from the input to the form-field flex container
// and the MDC floating label tries to account for the horizontal spacing, we need to reset
// the shifting since there is no padding the label needs to account for.
.mdc-floating-label {
left: 0;
}
// For the outline appearance, we re-create the active floating label transform. This is
// necessary because the transform for docked floating labels can be updated to account for
// the width of prefix container. We need to re-create these styles with `!important` because
// the horizontal adjustment for the label is applied through inline styles, and we want to
// make sure that the label can still float as expected. It should be okay using `!important`
// because it's unlikely that developers commonly overwrite the floating label transform.
.mat-mdc-text-field-wrapper.mdc-text-field--outlined .mdc-floating-label--float-above {
// This transform has been extracted from the MDC text-field styles. We can't access it
// through a variable because MDC generates this label transform through a mixin.
transform: translateY(-$mdc-text-field-outlined-label-position-y) scale(0.75) !important;
}

// MDC sets the input elements in outline appearance to "display: flex". There seems to
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,18 +34,4 @@
.mat-mdc-text-field-wrapper .mdc-floating-label {
top: $mat-form-field-baseline;
}

// In the outline appearance, the textarea needs slightly reduced top spacing because
// the label overflows the outline by 50%. Additionally, horizontal spacing needs to be
// added since the outline is part of the "infix" and we need to account for the outline.
// TODO(devversion): horizontal spacing and extra specificity can be removed once the
// following feature is available: material-components-web#5326.
.mat-mdc-form-field > .mat-mdc-text-field-wrapper.mdc-text-field--outlined
.mat-mdc-form-field-infix .mat-mdc-textarea-input {
margin-top: $mat-form-field-outline-top-spacing;
padding: {
left: $mdc-text-field-input-padding;
right: $mdc-text-field-input-padding;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import {MDCFloatingLabel} from '@material/floating-label';
},
})
export class MatFormFieldFloatingLabel extends MDCFloatingLabel implements OnDestroy {

@Input()
get floating() { return this._floating; }
set floating(shouldFloat: boolean) {
Expand All @@ -35,11 +34,16 @@ export class MatFormFieldFloatingLabel extends MDCFloatingLabel implements OnDes
}
private _floating = false;

constructor(elementRef: ElementRef) {
super(elementRef.nativeElement);
constructor(private _elementRef: ElementRef) {
super(_elementRef.nativeElement);
}

ngOnDestroy() {
this.destroy();
}

/** Gets the HTML element for the floating label. */
get element(): HTMLElement {
return this._elementRef.nativeElement;
}
}
16 changes: 8 additions & 8 deletions src/material-experimental/mdc-form-field/form-field.html
Original file line number Diff line number Diff line change
Expand Up @@ -26,21 +26,21 @@
[class.mdc-text-field--invalid]="_control.errorState"
(click)="_control.onContainerClick && _control.onContainerClick($event)">
<div class="mat-mdc-form-field-flex">
<div class="mat-mdc-form-field-prefix" *ngIf="_prefixChildren.length">
<div matFormFieldNotchedOutline *ngIf="_hasOutline()">
<ng-template [ngIf]="!_forceDisplayInfixLabel()">
<ng-template [ngTemplateOutlet]="labelTemplate"></ng-template>
</ng-template>
</div>

<div class="mat-mdc-form-field-prefix" *ngIf="_prefixChildren.length" #prefixContainer>
<ng-content select="[matPrefix]"></ng-content>
</div>

<div class="mat-mdc-form-field-infix">
<!-- For non outline appearance. -->
<ng-template [ngIf]="!_hasOutline()">
<ng-template [ngIf]="!_hasOutline() || _forceDisplayInfixLabel()">
<ng-template [ngTemplateOutlet]="labelTemplate"></ng-template>
</ng-template>

<!-- For outline appearance where MDC expects the label to be wrapped. -->
<div matFormFieldNotchedOutline *ngIf="_hasOutline()">
<ng-template [ngTemplateOutlet]="labelTemplate"></ng-template>
</div>

<ng-content></ng-content>
</div>

Expand Down
Loading

0 comments on commit fc91061

Please sign in to comment.