Skip to content

Commit 88029be

Browse files
authored
refactor(material-experimental/mdc-form-field): align label setup with non-MDC version (#24339)
Aligns the floating label setup of the MDC-based form field with the non-MDC version for better accessibility and to allow for easier customization. Includes: * Moving the required asterisk into its own element. * Reducing the specificity of the label typography.
1 parent f6cdc96 commit 88029be

File tree

9 files changed

+34
-26
lines changed

9 files changed

+34
-26
lines changed

src/material-experimental/mdc-chips/chip-grid.spec.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -694,15 +694,17 @@ describe('MDC-based MatChipGrid', () => {
694694
});
695695

696696
it('should set an asterisk after the placeholder if the control is required', () => {
697-
let requiredMarker = fixture.debugElement.query(By.css('.mdc-floating-label--required'))!;
697+
let requiredMarker = fixture.debugElement.query(
698+
By.css('.mat-mdc-form-field-required-marker'),
699+
)!;
698700
expect(requiredMarker)
699701
.withContext(`Expected placeholder not to have an asterisk, as control was not required.`)
700702
.toBeNull();
701703

702704
fixture.componentInstance.chipGrid.required = true;
703705
fixture.detectChanges();
704706

705-
requiredMarker = fixture.debugElement.query(By.css('.mdc-floating-label--required'))!;
707+
requiredMarker = fixture.debugElement.query(By.css('.mat-mdc-form-field-required-marker'))!;
706708
expect(requiredMarker)
707709
.not.withContext(`Expected placeholder to have an asterisk, as control was required.`)
708710
.toBeNull();
@@ -714,7 +716,9 @@ describe('MDC-based MatChipGrid', () => {
714716
fixture.componentInstance.control = new FormControl(undefined, [Validators.required]);
715717
fixture.detectChanges();
716718

717-
expect(fixture.nativeElement.querySelector('.mdc-floating-label--required')).toBeTruthy();
719+
expect(
720+
fixture.nativeElement.querySelector('.mat-mdc-form-field-required-marker'),
721+
).toBeTruthy();
718722
});
719723

720724
it('should blur the form field when the active chip is blurred', fakeAsync(() => {

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@
7777
// on whether a textarea is used. This is not possible in our implementation of the
7878
// form-field because we do not know what type of form-field control is set up. Hence
7979
// we always use a fixed position for the label. This does not have any implications.
80-
.mat-mdc-form-field .mat-mdc-text-field-wrapper .mdc-floating-label {
80+
.mat-mdc-form-field .mat-mdc-text-field-wrapper .mat-mdc-floating-label {
8181
top: math.div($height, 2);
8282
}
8383

@@ -107,7 +107,7 @@
107107
// Update the spacing for filled form fields to account for the hidden floating label.
108108
@include _infix-vertical-spacing-filled(
109109
$no-label-padding, $no-label-padding);
110-
.mat-mdc-text-field-wrapper:not(.mdc-text-field--outlined) .mdc-floating-label {
110+
.mat-mdc-text-field-wrapper:not(.mdc-text-field--outlined) .mat-mdc-floating-label {
111111
display: none;
112112
}
113113
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@
7676
// shows `body1` for text fields though, so we manually override the typography.
7777
// Note: Form controls inherit the typography from the parent form field.
7878
.mat-mdc-form-field,
79-
.mat-mdc-form-field .mdc-floating-label {
79+
.mat-mdc-floating-label {
8080
@include mdc-typography.typography(body1, $query: mdc-helpers.$mat-typography-styles-query);
8181
}
8282

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
// In order to ensure proper alignment of the floating label, we reset its line-height.
2121
// The line-height is not important as the element is absolutely positioned and only has one line
2222
// of text.
23-
.mat-mdc-form-field .mdc-floating-label.mdc-floating-label {
23+
.mat-mdc-form-field .mat-mdc-floating-label.mdc-floating-label {
2424
line-height: normal;
2525
}
2626

@@ -95,7 +95,7 @@
9595
// the notched outline container, and already applies a specific horizontal spacing which
9696
// we do not want to overwrite. *Note*: We need to have increased specificity for this
9797
// override because the `right` property will be set with higher specificity in RTL mode.
98-
.mat-mdc-text-field-wrapper .mat-mdc-form-field-infix .mdc-floating-label {
98+
.mat-mdc-text-field-wrapper .mat-mdc-form-field-infix .mat-mdc-floating-label {
9999
left: auto;
100100
right: auto;
101101
}

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

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,13 @@ import {ponyfill} from '@material/dom';
2525
@Directive({
2626
selector: 'label[matFormFieldFloatingLabel]',
2727
host: {
28-
'class': 'mdc-floating-label',
29-
'[class.mdc-floating-label--required]': 'required',
28+
'class': 'mdc-floating-label mat-mdc-floating-label',
3029
'[class.mdc-floating-label--float-above]': 'floating',
3130
},
3231
})
3332
export class MatFormFieldFloatingLabel {
3433
/** Whether the label is floating. */
3534
@Input() floating: boolean = false;
36-
/** Whether the label is required. */
37-
@Input() required: boolean = false;
3835

3936
constructor(private _elementRef: ElementRef) {}
4037

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,22 @@
1616
-->
1717
<label matFormFieldFloatingLabel
1818
[floating]="_shouldLabelFloat()"
19-
[required]="!hideRequiredMarker && _control.required"
2019
*ngIf="_hasFloatingLabel()"
2120
(cdkObserveContent)="_refreshOutlineNotchWidth()"
2221
[cdkObserveContentDisabled]="!_hasOutline()"
2322
[id]="_labelId"
2423
[attr.for]="_control.id"
2524
[attr.aria-owns]="_control.id">
2625
<ng-content select="mat-label"></ng-content>
26+
<!--
27+
We set the required marker as a separate element, in order to make it easier to target if
28+
apps want to override it and to be able to set `aria-hidden` so that screen readers don't
29+
pick it up.
30+
-->
31+
<span
32+
*ngIf="!hideRequiredMarker && _control.required"
33+
aria-hidden="true"
34+
class="mat-mdc-form-field-required-marker mdc-floating-label--required"></span>
2735
</label>
2836
</ng-template>
2937

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,7 @@ describe('MatMdcInput without forms', () => {
342342
const label = fixture.debugElement.query(By.css('label'))!;
343343
expect(label).not.toBeNull();
344344
expect(label.nativeElement.textContent).toBe('hello');
345-
expect(label.nativeElement.classList).toContain('mdc-floating-label--required');
345+
expect(label.nativeElement.querySelector('.mat-mdc-form-field-required-marker')).toBeTruthy();
346346
}));
347347

348348
it('should show the required star when using a FormControl', fakeAsync(() => {
@@ -352,7 +352,7 @@ describe('MatMdcInput without forms', () => {
352352
const label = fixture.debugElement.query(By.css('label'))!;
353353
expect(label).not.toBeNull();
354354
expect(label.nativeElement.textContent).toBe('Hello');
355-
expect(label.nativeElement.classList).toContain('mdc-floating-label--required');
355+
expect(label.nativeElement.querySelector('.mat-mdc-form-field-required-marker')).toBeTruthy();
356356
}));
357357

358358
it('should not hide the required star if input is disabled', () => {
@@ -364,7 +364,7 @@ describe('MatMdcInput without forms', () => {
364364
const label = fixture.debugElement.query(By.css('label'))!;
365365
expect(label).not.toBeNull();
366366
expect(label.nativeElement.textContent).toBe('hello');
367-
expect(label.nativeElement.classList).toContain('mdc-floating-label--required');
367+
expect(label.nativeElement.querySelector('.mat-mdc-form-field-required-marker')).toBeTruthy();
368368
});
369369

370370
it('hide label required star when set to hide the required marker', fakeAsync(() => {
@@ -373,13 +373,13 @@ describe('MatMdcInput without forms', () => {
373373

374374
const label = fixture.debugElement.query(By.css('label'))!;
375375
expect(label).not.toBeNull();
376-
expect(label.nativeElement.classList).toContain('mdc-floating-label--required');
376+
expect(label.nativeElement.querySelector('.mat-mdc-form-field-required-marker')).toBeTruthy();
377377
expect(label.nativeElement.textContent).toBe('hello');
378378

379379
fixture.componentInstance.hideRequiredMarker = true;
380380
fixture.detectChanges();
381381

382-
expect(label.nativeElement.classList).not.toContain('mdc-floating-label--required');
382+
expect(label.nativeElement.querySelector('.mat-mdc-form-field-required-marker')).toBeFalsy();
383383
expect(label.nativeElement.textContent).toBe('hello');
384384
}));
385385

src/material-experimental/mdc-select/select.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ $scale: 0.75 !default;
142142
// container. Below are the styles to account for the select arrow icon at the end.
143143
.mat-mdc-form-field-type-mat-select {
144144
&.mat-form-field-appearance-fill {
145-
.mdc-floating-label {
145+
.mat-mdc-floating-label {
146146
max-width: calc(100% - #{$mat-select-placeholder-arrow-space});
147147
}
148148

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

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2142,17 +2142,16 @@ describe('MDC-based MatSelect', () => {
21422142
it('should set an asterisk after the label if control is required', fakeAsync(() => {
21432143
const label = fixture.nativeElement.querySelector('.mat-mdc-form-field label');
21442144

2145-
expect(label.classList).not.toContain(
2146-
'mdc-floating-label--required',
2147-
`Expected label not to have an asterisk, as control was not required.`,
2148-
);
2145+
expect(label.querySelector('.mat-mdc-form-field-required-marker'))
2146+
.withContext(`Expected label not to have an asterisk, as control was not required.`)
2147+
.toBeFalsy();
21492148

21502149
fixture.componentInstance.isRequired = true;
21512150
fixture.detectChanges();
21522151

2153-
expect(label.classList)
2152+
expect(label.querySelector('.mat-mdc-form-field-required-marker'))
21542153
.withContext(`Expected label to have an asterisk, as control was required.`)
2155-
.toContain('mdc-floating-label--required');
2154+
.toBeTruthy();
21562155
}));
21572156
});
21582157

@@ -2975,7 +2974,7 @@ describe('MDC-based MatSelect', () => {
29752974

29762975
it('should set an asterisk after the label if the FormControl is required', fakeAsync(() => {
29772976
const label = fixture.nativeElement.querySelector('.mat-mdc-form-field label');
2978-
expect(label.classList).toContain('mdc-floating-label--required');
2977+
expect(label.querySelector('.mat-mdc-form-field-required-marker')).toBeTruthy();
29792978
}));
29802979
});
29812980

0 commit comments

Comments
 (0)