Skip to content

Commit db6511b

Browse files
authored
feat(material-expeirmental/mdc-form-field): separate out text and icon prefixes/suffixes (#21956)
* feat(material-experimental/mdc-form-field): separate out text and icon prefixes/suffixes * fixup! feat(material-experimental/mdc-form-field): separate out text and icon prefixes/suffixes * fixup! feat(material-experimental/mdc-form-field): separate out text and icon prefixes/suffixes * fixup! feat(material-experimental/mdc-form-field): separate out text and icon prefixes/suffixes
1 parent 67bd9d6 commit db6511b

File tree

11 files changed

+102
-53
lines changed

11 files changed

+102
-53
lines changed

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

Lines changed: 38 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -124,32 +124,50 @@ <h4>Text</h4>
124124
<mat-form-field [appearance]="prefixSuffixAppearance" class="demo-text-align-end">
125125
<mat-label>Amount</mat-label>
126126
<input matInput>
127-
<span matPrefix *ngIf="showPrefix">$&nbsp;</span>
128-
<span matSuffix>.00</span>
127+
<span matTextPrefix *ngIf="showPrefix">$</span>
128+
<span matTextSuffix>.00</span>
129129
</mat-form-field>
130130

131131
<h4>Text (always outline)</h4>
132132
<mat-form-field appearance="outline" class="demo-text-align-end">
133133
<mat-label>Amount</mat-label>
134134
<input matInput>
135-
<span matPrefix>$&nbsp;</span>
136-
<span matSuffix>.00</span>
135+
<span matTextPrefix>$</span>
136+
<span matTextSuffix>.00</span>
137137
</mat-form-field>
138138

139139
<h4>Icons</h4>
140140
<mat-form-field [appearance]="prefixSuffixAppearance">
141141
<mat-label>Amount</mat-label>
142142
<input matInput>
143-
<mat-icon matPrefix *ngIf="showPrefix">calendar_today</mat-icon>
144-
<mat-icon matSuffix>mode_edit</mat-icon>
143+
<mat-icon matIconPrefix *ngIf="showPrefix">calendar_today</mat-icon>
144+
<mat-icon matIconSuffix>mode_edit</mat-icon>
145145
</mat-form-field>
146146

147147
<h4>Icon buttons</h4>
148148
<mat-form-field [appearance]="prefixSuffixAppearance">
149149
<mat-label>Amount</mat-label>
150150
<input matInput>
151-
<button mat-icon-button matPrefix *ngIf="showPrefix"><mat-icon>calendar_today</mat-icon></button>
152-
<button mat-icon-button matSuffix><mat-icon>mode_edit</mat-icon></button>
151+
<button mat-icon-button matIconPrefix *ngIf="showPrefix">
152+
<mat-icon>calendar_today</mat-icon>
153+
</button>
154+
<button mat-icon-button matIconSuffix>
155+
<mat-icon>mode_edit</mat-icon>
156+
</button>
157+
</mat-form-field>
158+
159+
<h4>Text & Icons</h4>
160+
<mat-form-field [appearance]="prefixSuffixAppearance">
161+
<mat-label>Amount</mat-label>
162+
<input matInput>
163+
<span matTextPrefix>$</span>
164+
<span matTextSuffix>.00</span>
165+
<button mat-icon-button matIconPrefix *ngIf="showPrefix">
166+
<mat-icon>calendar_today</mat-icon>
167+
</button>
168+
<button mat-icon-button matIconSuffix>
169+
<mat-icon>mode_edit</mat-icon>
170+
</button>
153171
</mat-form-field>
154172

155173
<p>
@@ -371,20 +389,22 @@ <h4>Textarea</h4>
371389
<mat-form-field>
372390
<mat-label>Prefixed</mat-label>
373391
<input matInput value="example">
374-
<div matPrefix>Example:&nbsp;</div>
392+
<span matTextPrefix>Example:&nbsp;</span>
375393
</mat-form-field>
376394
<mat-form-field class="demo-text-align-end">
377395
<mat-label>Suffixed</mat-label>
378396
<input matInput value="123">
379-
<span matSuffix>.00 €</span>
397+
<span matTextSuffix>.00 €</span>
380398
</mat-form-field>
381399
<br/>
382400
Both:
383401
<mat-form-field class="demo-text-align-end">
384402
<mat-label>Email address</mat-label>
385403
<input matInput #email="matInput" value="angular-core">
386-
<span matPrefix><mat-icon [class.primary]="email.focused">email</mat-icon>&nbsp;</span>
387-
<span matSuffix [class.primary]="email.focused">&nbsp;@gmail.com</span>
404+
<span matIconPrefix>
405+
<mat-icon [class.primary]="email.focused">email</mat-icon>&nbsp;
406+
</span>
407+
<span matTextSuffix [class.primary]="email.focused">&nbsp;@gmail.com</span>
388408
</mat-form-field>
389409
</p>
390410

@@ -679,8 +699,8 @@ <h3>&lt;textarea&gt; with bindable autosize </h3>
679699
<mat-form-field appearance="outline" class="demo-text-align-end">
680700
<mat-label>Amount</mat-label>
681701
<input matInput>
682-
<span matPrefix>$&nbsp;</span>
683-
<span matSuffix>.00</span>
702+
<span matTextPrefix>$&nbsp;</span>
703+
<span matTextSuffix>.00</span>
684704
</mat-form-field>
685705
</mat-tab>
686706
<mat-tab label="Tab 2">
@@ -692,9 +712,9 @@ <h3>&lt;textarea&gt; with bindable autosize </h3>
692712
<mat-form-field appearance="outline" class="demo-text-align-end">
693713
<mat-label>Amount</mat-label>
694714
<input matInput>
695-
<span matPrefix>$&nbsp;</span>
696-
<span matPrefix *ngIf="showSecondPrefix">!&nbsp;</span>
697-
<span matSuffix>.00</span>
715+
<span matTextPrefix>$&nbsp;</span>
716+
<span matTextPrefix *ngIf="showSecondPrefix">!&nbsp;</span>
717+
<span matTextSuffix>.00</span>
698718
</mat-form-field>
699719
</mat-tab>
700720
</mat-tab-group>
@@ -724,7 +744,7 @@ <h4>Custom control</h4>
724744
<mat-form-field appearance="outline">
725745
<mat-label>Phone number</mat-label>
726746
<example-tel-input required></example-tel-input>
727-
<mat-icon matSuffix>phone</mat-icon>
747+
<mat-icon matIconSuffix>phone</mat-icon>
728748
<mat-hint>Include area code</mat-hint>
729749
</mat-form-field>
730750
</p>

src/material-experimental/mdc-form-field/_form-field-theme.scss

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,8 @@
7575
// the correct level.
7676
.mat-mdc-input-element,
7777
.mat-mdc-form-field label,
78-
.mat-mdc-form-field-prefix,
79-
.mat-mdc-form-field-suffix {
78+
.mat-mdc-form-field-text-prefix,
79+
.mat-mdc-form-field-text-suffix {
8080
@include mdc-typography(body1, $query: mdc-helpers.$mat-typography-styles-query);
8181
}
8282
}

src/material-experimental/mdc-form-field/directives/prefix.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ export const MAT_PREFIX = new InjectionToken<MatPrefix>('MatPrefix');
1717

1818
/** Prefix to be placed in front of the form field. */
1919
@Directive({
20-
selector: '[matPrefix]',
20+
selector: '[matPrefix], [matIconPrefix], [matTextPrefix]',
2121
providers: [{provide: MAT_PREFIX, useExisting: MatPrefix}],
2222
})
2323
export class MatPrefix {}

src/material-experimental/mdc-form-field/directives/suffix.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ export const MAT_SUFFIX = new InjectionToken<MatSuffix>('MatSuffix');
1717

1818
/** Suffix to be placed at the end of the form field. */
1919
@Directive({
20-
selector: '[matSuffix]',
20+
selector: '[matSuffix], [matIconSuffix], [matTextSuffix]',
2121
providers: [{provide: MAT_SUFFIX, useExisting: MatSuffix}],
2222
})
2323
export class MatSuffix {}

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

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,11 @@
4444
</ng-template>
4545
</div>
4646

47-
<div class="mat-mdc-form-field-prefix" *ngIf="_prefixChildren.length" #prefixContainer>
48-
<ng-content select="[matPrefix]"></ng-content>
47+
<div class="mat-mdc-form-field-icon-prefix" *ngIf="_prefixChildren.length" #iconPrefixContainer>
48+
<ng-content select="[matPrefix], [matIconPrefix]"></ng-content>
49+
</div>
50+
<div class="mat-mdc-form-field-text-prefix" *ngIf="_prefixChildren.length" #textPrefixContainer>
51+
<ng-content select="[matTextPrefix]"></ng-content>
4952
</div>
5053

5154
<div class="mat-mdc-form-field-infix">
@@ -56,8 +59,11 @@
5659
<ng-content></ng-content>
5760
</div>
5861

59-
<div class="mat-mdc-form-field-suffix" *ngIf="_suffixChildren.length">
60-
<ng-content select="[matSuffix]"></ng-content>
62+
<div class="mat-mdc-form-field-text-suffix" *ngIf="_suffixChildren.length">
63+
<ng-content select="[matTextSuffix]"></ng-content>
64+
</div>
65+
<div class="mat-mdc-form-field-icon-suffix" *ngIf="_suffixChildren.length">
66+
<ng-content select="[matSuffix], [matIconSuffix]"></ng-content>
6167
</div>
6268
</div>
6369

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,11 @@
5252
width: 100%;
5353
}
5454

55+
.mat-mdc-form-field-icon-prefix,
56+
.mat-mdc-form-field-icon-suffix {
57+
align-self: center;
58+
}
59+
5560
// Infix that contains the projected content (usually an input or a textarea). We ensure
5661
// that the projected form-field control and content can stretch as needed, but we also
5762
// apply a default infix width to make the form-field's look natural.

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

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,8 @@ const FLOATING_LABEL_DEFAULT_DOCKED_TRANSFORM = `translateY(-50%)`;
134134
export class MatFormField implements AfterViewInit, OnDestroy, AfterContentChecked,
135135
AfterContentInit {
136136
@ViewChild('textField') _textField: ElementRef<HTMLElement>;
137-
@ViewChild('prefixContainer') _prefixContainer: ElementRef<HTMLElement>;
137+
@ViewChild('iconPrefixContainer') _iconPrefixContainer: ElementRef<HTMLElement>;
138+
@ViewChild('textPrefixContainer') _textPrefixContainer: ElementRef<HTMLElement>;
138139
@ViewChild(MatFormFieldFloatingLabel) _floatingLabel: MatFormFieldFloatingLabel|undefined;
139140
@ViewChild(MatFormFieldNotchedOutline) _notchedOutline: MatFormFieldNotchedOutline|undefined;
140141
@ViewChild(MatFormFieldLineRipple) _lineRipple: MatFormFieldLineRipple|undefined;
@@ -654,7 +655,7 @@ export class MatFormField implements AfterViewInit, OnDestroy, AfterContentCheck
654655
const floatingLabel = this._floatingLabel.element;
655656
// If no prefix is displayed, reset the outline label offset from potential
656657
// previous label offset updates.
657-
if (!this._prefixContainer) {
658+
if (!(this._iconPrefixContainer || this._textPrefixContainer)) {
658659
floatingLabel.style.transform = '';
659660
return;
660661
}
@@ -664,11 +665,15 @@ export class MatFormField implements AfterViewInit, OnDestroy, AfterContentCheck
664665
this._needsOutlineLabelOffsetUpdateOnStable = true;
665666
return;
666667
}
667-
const prefixContainer = this._prefixContainer.nativeElement as HTMLElement;
668+
const iconPrefixContainer = this._iconPrefixContainer.nativeElement as HTMLElement;
669+
const textPrefixContainer = this._textPrefixContainer.nativeElement as HTMLElement;
668670
// If the directionality is RTL, the x-axis transform needs to be inverted. This
669671
// is because `transformX` does not change based on the page directionality.
670672
const labelHorizontalOffset =
671-
(this._dir.value === 'rtl' ? -1 : 1) * prefixContainer.getBoundingClientRect().width;
673+
(this._dir.value === 'rtl' ? -1 : 1) * (
674+
iconPrefixContainer.getBoundingClientRect().width +
675+
textPrefixContainer.getBoundingClientRect().width
676+
);
672677

673678
// Update the transform the floating label to account for the prefix container. Note
674679
// that we do not want to overwrite the default transform for docked floating labels.

src/material-experimental/mdc-form-field/testing/form-field-harness.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ export class MatFormFieldHarness extends _MatFormFieldHarnessBase<FormFieldContr
3737
async (harness, hasErrors) => await harness.hasErrors() === hasErrors);
3838
}
3939

40-
protected _prefixContainer = this.locatorForOptional('.mat-mdc-form-field-prefix');
41-
protected _suffixContainer = this.locatorForOptional('.mat-mdc-form-field-suffix');
40+
protected _prefixContainer = this.locatorForOptional('.mat-mdc-form-field-text-prefix');
41+
protected _suffixContainer = this.locatorForOptional('.mat-mdc-form-field-text-suffix');
4242
protected _label = this.locatorForOptional('.mdc-floating-label');
4343
protected _errors = this.locatorForAll('.mat-mdc-form-field-error');
4444
protected _hints = this.locatorForAll('.mat-mdc-form-field-hint');

src/material-experimental/mdc-input/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ ng_test_library(
5353
"//src/material-experimental/mdc-core",
5454
"//src/material-experimental/mdc-form-field",
5555
"//src/material/core",
56+
"//src/material/icon",
5657
"@npm//@angular/forms",
5758
"@npm//@angular/platform-browser",
5859
],

src/material-experimental/mdc-input/input.spec.ts

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,19 +20,20 @@ import {
2020
Validators,
2121
} from '@angular/forms';
2222
import {
23+
ErrorStateMatcher,
24+
ShowOnDirtyErrorStateMatcher,
25+
ThemePalette,
26+
} from '@angular/material-experimental/mdc-core';
27+
import {
28+
FloatLabelType,
2329
getMatFormFieldDuplicatedHintError,
2430
getMatFormFieldMissingControlError,
2531
MAT_FORM_FIELD_DEFAULT_OPTIONS,
2632
MatFormField,
2733
MatFormFieldAppearance,
2834
MatFormFieldModule,
29-
FloatLabelType,
3035
} from '@angular/material-experimental/mdc-form-field';
31-
import {
32-
ErrorStateMatcher,
33-
ShowOnDirtyErrorStateMatcher,
34-
ThemePalette,
35-
} from '@angular/material-experimental/mdc-core';
36+
import {MatIconModule} from '@angular/material/icon';
3637
import {By} from '@angular/platform-browser';
3738
import {BrowserAnimationsModule, NoopAnimationsModule} from '@angular/platform-browser/animations';
3839
import {MAT_INPUT_VALUE_ACCESSOR, MatInput, MatInputModule} from './index';
@@ -701,13 +702,19 @@ describe('MatMdcInput without forms', () => {
701702
const fixture = createComponent(MatInputWithPrefixAndSuffix);
702703
fixture.detectChanges();
703704

704-
const prefixEl = fixture.debugElement.query(By.css('.mat-mdc-form-field-prefix'))!;
705-
const suffixEl = fixture.debugElement.query(By.css('.mat-mdc-form-field-suffix'))!;
705+
const textPrefixEl = fixture.debugElement.query(By.css('.mat-mdc-form-field-text-prefix'))!;
706+
const textSuffixEl = fixture.debugElement.query(By.css('.mat-mdc-form-field-text-suffix'))!;
707+
const iconPrefixEl = fixture.debugElement.query(By.css('.mat-mdc-form-field-icon-prefix'))!;
708+
const iconSuffixEl = fixture.debugElement.query(By.css('.mat-mdc-form-field-icon-suffix'))!;
706709

707-
expect(prefixEl).not.toBeNull();
708-
expect(suffixEl).not.toBeNull();
709-
expect(prefixEl.nativeElement.innerText.trim()).toEqual('Prefix');
710-
expect(suffixEl.nativeElement.innerText.trim()).toEqual('Suffix');
710+
expect(textPrefixEl).not.toBeNull();
711+
expect(textSuffixEl).not.toBeNull();
712+
expect(iconPrefixEl).not.toBeNull();
713+
expect(iconSuffixEl).not.toBeNull();
714+
expect(textPrefixEl.nativeElement.innerText.trim()).toEqual('Prefix');
715+
expect(textSuffixEl.nativeElement.innerText.trim()).toEqual('Suffix');
716+
expect(iconPrefixEl.nativeElement.innerText.trim()).toEqual('favorite');
717+
expect(iconSuffixEl.nativeElement.innerText.trim()).toEqual('favorite');
711718
}));
712719

713720
it('should update empty class when value changes programmatically and OnPush', fakeAsync(() => {
@@ -1285,6 +1292,7 @@ function configureTestingModule(component: Type<any>, options:
12851292
imports: [
12861293
FormsModule,
12871294
MatFormFieldModule,
1295+
MatIconModule,
12881296
MatInputModule,
12891297
animations ? BrowserAnimationsModule : NoopAnimationsModule,
12901298
PlatformModule,
@@ -1597,9 +1605,11 @@ class MatInputWithFormGroupErrorMessages {
15971605
@Component({
15981606
template: `
15991607
<mat-form-field>
1600-
<div matPrefix>Prefix</div>
1608+
<mat-icon matIconPrefix>favorite</mat-icon>
1609+
<div matTextPrefix>Prefix</div>
16011610
<input matInput>
1602-
<div matSuffix>Suffix</div>
1611+
<div matTextSuffix>Suffix</div>
1612+
<mat-icon matIconSuffix>favorite</mat-icon>
16031613
</mat-form-field>
16041614
`
16051615
})

src/material/form-field/testing/shared.spec.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
import {ComponentHarness, HarnessLoader, HarnessPredicate, parallel} from '@angular/cdk/testing';
2-
import {
3-
createFakeEvent,
4-
dispatchFakeEvent,
5-
} from '@angular/cdk/testing/private';
2+
import {createFakeEvent, dispatchFakeEvent} from '@angular/cdk/testing/private';
63
import {TestbedHarnessEnvironment} from '@angular/cdk/testing/testbed';
74
import {Component, Type} from '@angular/core';
85
import {ComponentFixture, TestBed} from '@angular/core/testing';
@@ -30,6 +27,7 @@ export function runHarnessTests(
3027
.compileComponents();
3128

3229
fixture = TestBed.createComponent(FormFieldHarnessTest);
30+
fixture.componentInstance.isMdc = isMdcImplementation;
3331
fixture.detectChanges();
3432
loader = TestbedHarnessEnvironment.loader(fixture);
3533
});
@@ -243,11 +241,14 @@ export function runHarnessTests(
243241
@Component({
244242
template: `
245243
<mat-form-field id="first-form-field" [floatLabel]="shouldLabelFloat">
246-
<span matPrefix>prefix_text</span>
247-
<span matPrefix>prefix_text_2</span>
244+
<span matPrefix *ngIf="!isMdc">prefix_text</span>
245+
<span matPrefix *ngIf="!isMdc">prefix_text_2</span>
246+
<span matTextPrefix *ngIf="isMdc">prefix_text</span>
247+
<span matTextPrefix *ngIf="isMdc">prefix_text_2</span>
248248
<input matInput value="Sushi" name="favorite-food" placeholder="With placeholder"
249249
[disabled]="isDisabled">
250-
<span matSuffix>suffix_text</span>
250+
<span matSuffix *ngIf="!isMdc">suffix_text</span>
251+
<span matTextSuffix *ngIf="isMdc">suffix_text</span>
251252
</mat-form-field>
252253
253254
<mat-form-field appearance="standard" color="warn" id="with-errors">
@@ -287,6 +288,7 @@ class FormFieldHarnessTest {
287288
shouldLabelFloat: 'always'|'auto' = 'auto';
288289
hasLabel = false;
289290
isDisabled = false;
291+
isMdc = false;
290292

291293
setupAsyncValidator() {
292294
this.requiredControl.setValidators(() => null);

0 commit comments

Comments
 (0)