Skip to content

Commit fc91061

Browse files
authored
fix(material-experimental/mdc-form-field): notched-outline sho… (#18381)
* 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
1 parent 782f114 commit fc91061

File tree

9 files changed

+233
-63
lines changed

9 files changed

+233
-63
lines changed

src/dev-app/mdc-input/mdc-input-demo.html

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,14 @@ <h4>Text</h4>
128128
<span matSuffix>.00</span>
129129
</mat-form-field>
130130

131+
<h4>Text (always outline)</h4>
132+
<mat-form-field appearance="outline" class="demo-text-align-end">
133+
<mat-label>Amount</mat-label>
134+
<input matInput>
135+
<span matPrefix>$&nbsp;</span>
136+
<span matSuffix>.00</span>
137+
</mat-form-field>
138+
131139
<h4>Icons</h4>
132140
<mat-form-field [appearance]="prefixSuffixAppearance">
133141
<mat-label>Amount</mat-label>
@@ -640,14 +648,31 @@ <h3>&lt;textarea&gt; with bindable autosize </h3>
640648
<mat-label>Tab 1 input</mat-label>
641649
<input matInput value="test">
642650
</mat-form-field>
651+
652+
<mat-form-field appearance="outline" class="demo-text-align-end">
653+
<mat-label>Amount</mat-label>
654+
<input matInput>
655+
<span matPrefix>$&nbsp;</span>
656+
<span matSuffix>.00</span>
657+
</mat-form-field>
643658
</mat-tab>
644659
<mat-tab label="Tab 2">
645660
<mat-form-field appearance="outline">
646661
<mat-label>Tab 2 input</mat-label>
647662
<input matInput value="test">
648663
</mat-form-field>
664+
665+
<mat-form-field appearance="outline" class="demo-text-align-end">
666+
<mat-label>Amount</mat-label>
667+
<input matInput>
668+
<span matPrefix>$&nbsp;</span>
669+
<span matPrefix *ngIf="showSecondPrefix">!&nbsp;</span>
670+
<span matSuffix>.00</span>
671+
</mat-form-field>
649672
</mat-tab>
650673
</mat-tab-group>
674+
675+
<button (click)="showSecondPrefix = !showSecondPrefix">Toggle second prefix.</button>
651676
</mat-card-content>
652677
</mat-card>
653678

src/dev-app/mdc-input/mdc-input-demo.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ export class MdcInputDemo {
3737
prefixSuffixAppearance: MatFormFieldAppearance = 'fill';
3838
placeholderTestControl = new FormControl('', Validators.required);
3939
options: string[] = ['One', 'Two', 'Three'];
40+
showSecondPrefix = false;
4041

4142
name: string;
4243
errorMessageExample1: string;

src/material-experimental/mdc-form-field/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ ng_module(
1818
assets = [":form_field_scss"] + glob(["**/*.html"]),
1919
module_name = "@angular/material-experimental/mdc-form-field",
2020
deps = [
21+
"//src/cdk/bidi",
2122
"//src/cdk/observers",
2223
"//src/cdk/platform",
2324
"//src/material/core",

src/material-experimental/mdc-form-field/_mdc-text-field-structure-overrides.scss

Lines changed: 42 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
@import '@material/notched-outline/variables.import';
2+
@import '@material/textfield/variables.import';
13
@import 'form-field-sizing';
24

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

38-
// MDC changes the vertical spacing of the input if there is no label. Since we moved
39-
// the spacing of the input to the parent infix container (to support custom controls),
40-
// we need to replicate these styles for the infix container. The goal is that the input
41-
// placeholder vertically aligns with floating labels of other filled inputs. Note that
42-
// outline appearance currently still relies on the input spacing due to a notched-outline
43-
// limitation. TODO: https://github.com/material-components/material-components-web/issues/5326
44-
.mdc-text-field--no-label:not(.mdc-text-field--outlined):not(.mdc-text-field--textarea)
45-
.mat-mdc-form-field-infix {
46-
padding-top: $mat-form-field-no-label-padding-top;
47-
padding-bottom: $mat-form-field-no-label-padding-bottom;
48-
}
49-
5040
// MDC adds vertical spacing to inputs. We removed this spacing and intend to add it
5141
// to the infix container. This is necessary to ensure that custom form-field controls
52-
// also have the proper Material Design spacing to the label and bottom-line. Note that
53-
// outline appearance currently still relies on the input spacing due to a notched-outline
54-
// limitation. TODO: https://github.com/material-components/material-components-web/issues/5326
55-
.mat-mdc-text-field-wrapper:not(.mdc-text-field--outlined) .mat-mdc-form-field-infix {
42+
// also have the proper Material Design spacing to the label and bottom-line.
43+
.mat-mdc-text-field-wrapper .mat-mdc-form-field-infix {
5644
// Apply the default text-field input padding to the infix container. We removed the
5745
// padding from the input elements in order to support arbitrary form-field controls.
5846
padding-top: $mat-form-field-with-label-input-padding-top;
5947
padding-bottom: $mat-form-field-with-label-input-padding-bottom;
6048
}
6149

50+
// MDC changes the vertical spacing of the input if there is no label, or in the outline
51+
// appearance. This is because the input should vertically align with other inputs which use
52+
// a floating label. To achieve this, we add custom vertical spacing to the infix container
53+
// that differs from the vertical spacing for text-field's with a floating label.
54+
.mdc-text-field--no-label:not(.mdc-text-field--textarea) .mat-mdc-form-field-infix,
55+
.mat-mdc-text-field-wrapper.mdc-text-field--outlined .mat-mdc-form-field-infix {
56+
padding-top: $mat-form-field-no-label-padding-top;
57+
padding-bottom: $mat-form-field-no-label-padding-bottom;
58+
}
59+
6260
// Root element of the mdc-text-field. As explained in the height overwrites above, MDC
6361
// sets a default height on the text-field root element. This is not desired since we
6462
// want the element to be able to expand as needed.
@@ -79,22 +77,33 @@
7977
opacity: 1;
8078
}
8179

82-
// The additional nesting is a temporary until the notched-outline is decoupled from the
83-
// floating label. See https://github.com/material-components/material-components-web/issues/5326
84-
// TODO(devversion): Remove this workaround/nesting once the feature is available.
85-
.mat-mdc-text-field-wrapper:not(.mdc-text-field--outlined) {
86-
// We removed the horizontal inset on input elements, but need to re-add the spacing to
87-
// the actual form-field flex container that contains the prefixes, suffixes and infix.
88-
.mat-mdc-form-field-flex {
89-
padding: 0 $mdc-text-field-input-padding;
90-
}
80+
// We removed the horizontal inset on input elements, but need to re-add the spacing to
81+
// the actual form-field flex container that contains the prefixes, suffixes and infix.
82+
.mat-mdc-text-field-wrapper .mat-mdc-form-field-flex {
83+
padding: 0 $mdc-text-field-input-padding;
84+
}
85+
86+
// Since we moved the horizontal spacing from the input to the form-field flex container
87+
// and the MDC floating label tries to account for the horizontal spacing, we need to reset
88+
// the shifting since there is no padding the label needs to account for. Note that we do not
89+
// want do this for labels in the notched-outline since MDC keeps those labels relative to
90+
// the notched outline container, and already applies a specific horizontal spacing which
91+
// we do not want to overwrite.
92+
.mat-mdc-form-field-infix .mdc-floating-label {
93+
left: 0;
94+
right: 0;
95+
}
9196

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

100109
// MDC sets the input elements in outline appearance to "display: flex". There seems to

src/material-experimental/mdc-form-field/_mdc-text-field-textarea-overrides.scss

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -34,18 +34,4 @@
3434
.mat-mdc-text-field-wrapper .mdc-floating-label {
3535
top: $mat-form-field-baseline;
3636
}
37-
38-
// In the outline appearance, the textarea needs slightly reduced top spacing because
39-
// the label overflows the outline by 50%. Additionally, horizontal spacing needs to be
40-
// added since the outline is part of the "infix" and we need to account for the outline.
41-
// TODO(devversion): horizontal spacing and extra specificity can be removed once the
42-
// following feature is available: material-components-web#5326.
43-
.mat-mdc-form-field > .mat-mdc-text-field-wrapper.mdc-text-field--outlined
44-
.mat-mdc-form-field-infix .mat-mdc-textarea-input {
45-
margin-top: $mat-form-field-outline-top-spacing;
46-
padding: {
47-
left: $mdc-text-field-input-padding;
48-
right: $mdc-text-field-input-padding;
49-
}
50-
}
5137
}

src/material-experimental/mdc-form-field/directives/floating-label.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ import {MDCFloatingLabel} from '@material/floating-label';
2424
},
2525
})
2626
export class MatFormFieldFloatingLabel extends MDCFloatingLabel implements OnDestroy {
27-
2827
@Input()
2928
get floating() { return this._floating; }
3029
set floating(shouldFloat: boolean) {
@@ -35,11 +34,16 @@ export class MatFormFieldFloatingLabel extends MDCFloatingLabel implements OnDes
3534
}
3635
private _floating = false;
3736

38-
constructor(elementRef: ElementRef) {
39-
super(elementRef.nativeElement);
37+
constructor(private _elementRef: ElementRef) {
38+
super(_elementRef.nativeElement);
4039
}
4140

4241
ngOnDestroy() {
4342
this.destroy();
4443
}
44+
45+
/** Gets the HTML element for the floating label. */
46+
get element(): HTMLElement {
47+
return this._elementRef.nativeElement;
48+
}
4549
}

src/material-experimental/mdc-form-field/form-field.html

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,21 +26,21 @@
2626
[class.mdc-text-field--invalid]="_control.errorState"
2727
(click)="_control.onContainerClick && _control.onContainerClick($event)">
2828
<div class="mat-mdc-form-field-flex">
29-
<div class="mat-mdc-form-field-prefix" *ngIf="_prefixChildren.length">
29+
<div matFormFieldNotchedOutline *ngIf="_hasOutline()">
30+
<ng-template [ngIf]="!_forceDisplayInfixLabel()">
31+
<ng-template [ngTemplateOutlet]="labelTemplate"></ng-template>
32+
</ng-template>
33+
</div>
34+
35+
<div class="mat-mdc-form-field-prefix" *ngIf="_prefixChildren.length" #prefixContainer>
3036
<ng-content select="[matPrefix]"></ng-content>
3137
</div>
3238

3339
<div class="mat-mdc-form-field-infix">
34-
<!-- For non outline appearance. -->
35-
<ng-template [ngIf]="!_hasOutline()">
40+
<ng-template [ngIf]="!_hasOutline() || _forceDisplayInfixLabel()">
3641
<ng-template [ngTemplateOutlet]="labelTemplate"></ng-template>
3742
</ng-template>
3843

39-
<!-- For outline appearance where MDC expects the label to be wrapped. -->
40-
<div matFormFieldNotchedOutline *ngIf="_hasOutline()">
41-
<ng-template [ngTemplateOutlet]="labelTemplate"></ng-template>
42-
</div>
43-
4444
<ng-content></ng-content>
4545
</div>
4646

0 commit comments

Comments
 (0)