Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(material-expeirmental/mdc-form-field): separate out text and icon prefixes/suffixes #21956

Merged
merged 4 commits into from
Feb 26, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 38 additions & 18 deletions src/dev-app/mdc-input/mdc-input-demo.html
Original file line number Diff line number Diff line change
Expand Up @@ -124,32 +124,50 @@ <h4>Text</h4>
<mat-form-field [appearance]="prefixSuffixAppearance" class="demo-text-align-end">
<mat-label>Amount</mat-label>
<input matInput>
<span matPrefix *ngIf="showPrefix">$&nbsp;</span>
<span matSuffix>.00</span>
<span matTextPrefix *ngIf="showPrefix">$</span>
<span matTextSuffix>.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>
<span matTextPrefix>$</span>
<span matTextSuffix>.00</span>
</mat-form-field>

<h4>Icons</h4>
<mat-form-field [appearance]="prefixSuffixAppearance">
<mat-label>Amount</mat-label>
<input matInput>
<mat-icon matPrefix *ngIf="showPrefix">calendar_today</mat-icon>
<mat-icon matSuffix>mode_edit</mat-icon>
<mat-icon matIconPrefix *ngIf="showPrefix">calendar_today</mat-icon>
<mat-icon matIconSuffix>mode_edit</mat-icon>
</mat-form-field>

<h4>Icon buttons</h4>
<mat-form-field [appearance]="prefixSuffixAppearance">
<mat-label>Amount</mat-label>
<input matInput>
<button mat-icon-button matPrefix *ngIf="showPrefix"><mat-icon>calendar_today</mat-icon></button>
<button mat-icon-button matSuffix><mat-icon>mode_edit</mat-icon></button>
<button mat-icon-button matIconPrefix *ngIf="showPrefix">
<mat-icon>calendar_today</mat-icon>
</button>
<button mat-icon-button matIconSuffix>
<mat-icon>mode_edit</mat-icon>
</button>
</mat-form-field>

<h4>Text & Icons</h4>
<mat-form-field [appearance]="prefixSuffixAppearance">
<mat-label>Amount</mat-label>
<input matInput>
<span matTextPrefix>$</span>
<span matTextSuffix>.00</span>
<button mat-icon-button matIconPrefix *ngIf="showPrefix">
<mat-icon>calendar_today</mat-icon>
</button>
<button mat-icon-button matIconSuffix>
<mat-icon>mode_edit</mat-icon>
</button>
</mat-form-field>

<p>
Expand Down Expand Up @@ -371,20 +389,22 @@ <h4>Textarea</h4>
<mat-form-field>
<mat-label>Prefixed</mat-label>
<input matInput value="example">
<div matPrefix>Example:&nbsp;</div>
<span matTextPrefix>Example:&nbsp;</span>
</mat-form-field>
<mat-form-field class="demo-text-align-end">
<mat-label>Suffixed</mat-label>
<input matInput value="123">
<span matSuffix>.00 €</span>
<span matTextSuffix>.00 €</span>
</mat-form-field>
<br/>
Both:
<mat-form-field class="demo-text-align-end">
<mat-label>Email address</mat-label>
<input matInput #email="matInput" value="angular-core">
<span matPrefix><mat-icon [class.primary]="email.focused">email</mat-icon>&nbsp;</span>
<span matSuffix [class.primary]="email.focused">&nbsp;@gmail.com</span>
<span matIconPrefix>
<mat-icon [class.primary]="email.focused">email</mat-icon>&nbsp;
</span>
<span matTextSuffix [class.primary]="email.focused">&nbsp;@gmail.com</span>
</mat-form-field>
</p>

Expand Down Expand Up @@ -679,8 +699,8 @@ <h3>&lt;textarea&gt; with bindable autosize </h3>
<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>
<span matTextPrefix>$&nbsp;</span>
<span matTextSuffix>.00</span>
</mat-form-field>
</mat-tab>
<mat-tab label="Tab 2">
Expand All @@ -692,9 +712,9 @@ <h3>&lt;textarea&gt; with bindable autosize </h3>
<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>
<span matTextPrefix>$&nbsp;</span>
<span matTextPrefix *ngIf="showSecondPrefix">!&nbsp;</span>
<span matTextSuffix>.00</span>
</mat-form-field>
</mat-tab>
</mat-tab-group>
Expand Down Expand Up @@ -724,7 +744,7 @@ <h4>Custom control</h4>
<mat-form-field appearance="outline">
<mat-label>Phone number</mat-label>
<example-tel-input required></example-tel-input>
<mat-icon matSuffix>phone</mat-icon>
<mat-icon matIconSuffix>phone</mat-icon>
<mat-hint>Include area code</mat-hint>
</mat-form-field>
</p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,8 @@
// the correct level.
.mat-mdc-input-element,
.mat-mdc-form-field label,
.mat-mdc-form-field-prefix,
.mat-mdc-form-field-suffix {
.mat-mdc-form-field-text-prefix,
.mat-mdc-form-field-text-suffix {
@include mdc-typography(body1, $query: mdc-helpers.$mat-typography-styles-query);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export const MAT_PREFIX = new InjectionToken<MatPrefix>('MatPrefix');

/** Prefix to be placed in front of the form field. */
@Directive({
selector: '[matPrefix]',
selector: '[matPrefix], [matIconPrefix], [matTextPrefix]',
providers: [{provide: MAT_PREFIX, useExisting: MatPrefix}],
})
export class MatPrefix {}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export const MAT_SUFFIX = new InjectionToken<MatSuffix>('MatSuffix');

/** Suffix to be placed at the end of the form field. */
@Directive({
selector: '[matSuffix]',
selector: '[matSuffix], [matIconSuffix], [matTextSuffix]',
providers: [{provide: MAT_SUFFIX, useExisting: MatSuffix}],
})
export class MatSuffix {}
14 changes: 10 additions & 4 deletions src/material-experimental/mdc-form-field/form-field.html
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,11 @@
</ng-template>
</div>

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

<div class="mat-mdc-form-field-infix">
Expand All @@ -56,8 +59,11 @@
<ng-content></ng-content>
</div>

<div class="mat-mdc-form-field-suffix" *ngIf="_suffixChildren.length">
<ng-content select="[matSuffix]"></ng-content>
<div class="mat-mdc-form-field-text-suffix" *ngIf="_suffixChildren.length">
<ng-content select="[matTextSuffix]"></ng-content>
</div>
<div class="mat-mdc-form-field-icon-suffix" *ngIf="_suffixChildren.length">
<ng-content select="[matSuffix], [matIconSuffix]"></ng-content>
</div>
</div>

Expand Down
5 changes: 5 additions & 0 deletions src/material-experimental/mdc-form-field/form-field.scss
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@
width: 100%;
}

.mat-mdc-form-field-icon-prefix,
.mat-mdc-form-field-icon-suffix {
align-self: center;
}

// Infix that contains the projected content (usually an input or a textarea). We ensure
// that the projected form-field control and content can stretch as needed, but we also
// apply a default infix width to make the form-field's look natural.
Expand Down
13 changes: 9 additions & 4 deletions src/material-experimental/mdc-form-field/form-field.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,8 @@ const FLOATING_LABEL_DEFAULT_DOCKED_TRANSFORM = `translateY(-50%)`;
export class MatFormField implements AfterViewInit, OnDestroy, AfterContentChecked,
AfterContentInit {
@ViewChild('textField') _textField: ElementRef<HTMLElement>;
@ViewChild('prefixContainer') _prefixContainer: ElementRef<HTMLElement>;
@ViewChild('iconPrefixContainer') _iconPrefixContainer: ElementRef<HTMLElement>;
@ViewChild('textPrefixContainer') _textPrefixContainer: ElementRef<HTMLElement>;
@ViewChild(MatFormFieldFloatingLabel) _floatingLabel: MatFormFieldFloatingLabel|undefined;
@ViewChild(MatFormFieldNotchedOutline) _notchedOutline: MatFormFieldNotchedOutline|undefined;
@ViewChild(MatFormFieldLineRipple) _lineRipple: MatFormFieldLineRipple|undefined;
Expand Down Expand Up @@ -654,7 +655,7 @@ export class MatFormField implements AfterViewInit, OnDestroy, AfterContentCheck
const floatingLabel = this._floatingLabel.element;
// If no prefix is displayed, reset the outline label offset from potential
// previous label offset updates.
if (!this._prefixContainer) {
if (!(this._iconPrefixContainer || this._textPrefixContainer)) {
floatingLabel.style.transform = '';
return;
}
Expand All @@ -664,11 +665,15 @@ export class MatFormField implements AfterViewInit, OnDestroy, AfterContentCheck
this._needsOutlineLabelOffsetUpdateOnStable = true;
return;
}
const prefixContainer = this._prefixContainer.nativeElement as HTMLElement;
const iconPrefixContainer = this._iconPrefixContainer.nativeElement as HTMLElement;
const textPrefixContainer = this._textPrefixContainer.nativeElement as HTMLElement;
// If the directionality is RTL, the x-axis transform needs to be inverted. This
// is because `transformX` does not change based on the page directionality.
const labelHorizontalOffset =
(this._dir.value === 'rtl' ? -1 : 1) * prefixContainer.getBoundingClientRect().width;
(this._dir.value === 'rtl' ? -1 : 1) * (
iconPrefixContainer.getBoundingClientRect().width +
textPrefixContainer.getBoundingClientRect().width
);

// Update the transform the floating label to account for the prefix container. Note
// that we do not want to overwrite the default transform for docked floating labels.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ export class MatFormFieldHarness extends _MatFormFieldHarnessBase<FormFieldContr
async (harness, hasErrors) => await harness.hasErrors() === hasErrors);
}

protected _prefixContainer = this.locatorForOptional('.mat-mdc-form-field-prefix');
protected _suffixContainer = this.locatorForOptional('.mat-mdc-form-field-suffix');
protected _prefixContainer = this.locatorForOptional('.mat-mdc-form-field-text-prefix');
protected _suffixContainer = this.locatorForOptional('.mat-mdc-form-field-text-suffix');
protected _label = this.locatorForOptional('.mdc-floating-label');
protected _errors = this.locatorForAll('.mat-mdc-form-field-error');
protected _hints = this.locatorForAll('.mat-mdc-form-field-hint');
Expand Down
1 change: 1 addition & 0 deletions src/material-experimental/mdc-input/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ ng_test_library(
"//src/material-experimental/mdc-core",
"//src/material-experimental/mdc-form-field",
"//src/material/core",
"//src/material/icon",
"@npm//@angular/forms",
"@npm//@angular/platform-browser",
],
Expand Down
38 changes: 24 additions & 14 deletions src/material-experimental/mdc-input/input.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,20 @@ import {
Validators,
} from '@angular/forms';
import {
ErrorStateMatcher,
ShowOnDirtyErrorStateMatcher,
ThemePalette,
} from '@angular/material-experimental/mdc-core';
import {
FloatLabelType,
getMatFormFieldDuplicatedHintError,
getMatFormFieldMissingControlError,
MAT_FORM_FIELD_DEFAULT_OPTIONS,
MatFormField,
MatFormFieldAppearance,
MatFormFieldModule,
FloatLabelType,
} from '@angular/material-experimental/mdc-form-field';
import {
ErrorStateMatcher,
ShowOnDirtyErrorStateMatcher,
ThemePalette,
} from '@angular/material-experimental/mdc-core';
import {MatIconModule} from '@angular/material/icon';
import {By} from '@angular/platform-browser';
import {BrowserAnimationsModule, NoopAnimationsModule} from '@angular/platform-browser/animations';
import {MAT_INPUT_VALUE_ACCESSOR, MatInput, MatInputModule} from './index';
Expand Down Expand Up @@ -701,13 +702,19 @@ describe('MatMdcInput without forms', () => {
const fixture = createComponent(MatInputWithPrefixAndSuffix);
fixture.detectChanges();

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

expect(prefixEl).not.toBeNull();
expect(suffixEl).not.toBeNull();
expect(prefixEl.nativeElement.innerText.trim()).toEqual('Prefix');
expect(suffixEl.nativeElement.innerText.trim()).toEqual('Suffix');
expect(textPrefixEl).not.toBeNull();
expect(textSuffixEl).not.toBeNull();
expect(iconPrefixEl).not.toBeNull();
expect(iconSuffixEl).not.toBeNull();
expect(textPrefixEl.nativeElement.innerText.trim()).toEqual('Prefix');
expect(textSuffixEl.nativeElement.innerText.trim()).toEqual('Suffix');
expect(iconPrefixEl.nativeElement.innerText.trim()).toEqual('favorite');
expect(iconSuffixEl.nativeElement.innerText.trim()).toEqual('favorite');
}));

it('should update empty class when value changes programmatically and OnPush', fakeAsync(() => {
Expand Down Expand Up @@ -1285,6 +1292,7 @@ function configureTestingModule(component: Type<any>, options:
imports: [
FormsModule,
MatFormFieldModule,
MatIconModule,
MatInputModule,
animations ? BrowserAnimationsModule : NoopAnimationsModule,
PlatformModule,
Expand Down Expand Up @@ -1597,9 +1605,11 @@ class MatInputWithFormGroupErrorMessages {
@Component({
template: `
<mat-form-field>
<div matPrefix>Prefix</div>
<mat-icon matIconPrefix>favorite</mat-icon>
<div matTextPrefix>Prefix</div>
<input matInput>
<div matSuffix>Suffix</div>
<div matTextSuffix>Suffix</div>
<mat-icon matIconSuffix>favorite</mat-icon>
</mat-form-field>
`
})
Expand Down
16 changes: 9 additions & 7 deletions src/material/form-field/testing/shared.spec.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import {ComponentHarness, HarnessLoader, HarnessPredicate, parallel} from '@angular/cdk/testing';
import {
createFakeEvent,
dispatchFakeEvent,
} from '@angular/cdk/testing/private';
import {createFakeEvent, dispatchFakeEvent} from '@angular/cdk/testing/private';
import {TestbedHarnessEnvironment} from '@angular/cdk/testing/testbed';
import {Component, Type} from '@angular/core';
import {ComponentFixture, TestBed} from '@angular/core/testing';
Expand Down Expand Up @@ -30,6 +27,7 @@ export function runHarnessTests(
.compileComponents();

fixture = TestBed.createComponent(FormFieldHarnessTest);
fixture.componentInstance.isMdc = isMdcImplementation;
fixture.detectChanges();
loader = TestbedHarnessEnvironment.loader(fixture);
});
Expand Down Expand Up @@ -243,11 +241,14 @@ export function runHarnessTests(
@Component({
template: `
<mat-form-field id="first-form-field" [floatLabel]="shouldLabelFloat">
<span matPrefix>prefix_text</span>
<span matPrefix>prefix_text_2</span>
<span matPrefix *ngIf="!isMdc">prefix_text</span>
<span matPrefix *ngIf="!isMdc">prefix_text_2</span>
<span matTextPrefix *ngIf="isMdc">prefix_text</span>
<span matTextPrefix *ngIf="isMdc">prefix_text_2</span>
<input matInput value="Sushi" name="favorite-food" placeholder="With placeholder"
[disabled]="isDisabled">
<span matSuffix>suffix_text</span>
<span matSuffix *ngIf="!isMdc">suffix_text</span>
<span matTextSuffix *ngIf="isMdc">suffix_text</span>
</mat-form-field>

<mat-form-field appearance="standard" color="warn" id="with-errors">
Expand Down Expand Up @@ -287,6 +288,7 @@ class FormFieldHarnessTest {
shouldLabelFloat: 'always'|'auto' = 'auto';
hasLabel = false;
isDisabled = false;
isMdc = false;

setupAsyncValidator() {
this.requiredControl.setValidators(() => null);
Expand Down