Skip to content

Commit 00b153b

Browse files
crisbetovictoriaaa234
authored andcommitted
fix(stepper): unable to set aria-label on step (#11989)
Along the same lines as #11898. In our stepper docs we recommend for people to add a meaningful label via `aria-label` or `aria-labelledby`, however even if they do, the attributes don't get forwarded to the underlying element that has the proper role. These changes add inputs for `aria-label` and `aria-labelledby` and correctly forward the attributes.
1 parent 837b936 commit 00b153b

File tree

4 files changed

+68
-1
lines changed

4 files changed

+68
-1
lines changed

src/cdk/stepper/stepper.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,9 +84,18 @@ export class CdkStep implements OnChanges {
8484
/** Whether user has seen the expanded step content or not. */
8585
interacted = false;
8686

87-
/** Label of the step. */
87+
/** Plain text label of the step. */
8888
@Input() label: string;
8989

90+
/** Aria label for the tab. */
91+
@Input('aria-label') ariaLabel: string;
92+
93+
/**
94+
* Reference to the element that the tab is labelled by.
95+
* Will be cleared if `aria-label` is set at the same time.
96+
*/
97+
@Input('aria-labelledby') ariaLabelledby: string;
98+
9099
/** Whether the user can return to this step once it has been marked as complted. */
91100
@Input()
92101
get editable(): boolean { return this._editable; }

src/lib/stepper/stepper-horizontal.html

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
[attr.aria-setsize]="_steps.length"
1010
[attr.aria-controls]="_getStepContentId(i)"
1111
[attr.aria-selected]="selectedIndex == i"
12+
[attr.aria-label]="step.ariaLabel || null"
13+
[attr.aria-labelledby]="(!step.ariaLabel && step.ariaLabelledby) ? step.ariaLabelledby : null"
1214
[index]="i"
1315
[state]="_getIndicatorType(i)"
1416
[label]="step.stepLabel || step.label"

src/lib/stepper/stepper-vertical.html

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
[attr.aria-setsize]="_steps.length"
99
[attr.aria-controls]="_getStepContentId(i)"
1010
[attr.aria-selected]="selectedIndex === i"
11+
[attr.aria-label]="step.ariaLabel || null"
12+
[attr.aria-labelledby]="(!step.ariaLabel && step.ariaLabelledby) ? step.ariaLabelledby : null"
1113
[index]="i"
1214
[state]="_getIndicatorType(i)"
1315
[label]="step.stepLabel || step.label"

src/lib/stepper/stepper.spec.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ describe('MatStepper', () => {
5454
SimpleStepperWithStepControlAndCompletedBinding,
5555
SimpleMatHorizontalStepperApp,
5656
LinearStepperWithValidOptionalStep,
57+
StepperWithAriaInputs,
5758
],
5859
providers: [
5960
{provide: Directionality, useFactory: () => dir}
@@ -836,6 +837,46 @@ describe('MatStepper', () => {
836837
expect(stepper.selectedIndex).toBe(2);
837838
});
838839
});
840+
841+
describe('aria labelling', () => {
842+
let fixture: ComponentFixture<StepperWithAriaInputs>;
843+
let stepHeader: HTMLElement;
844+
845+
beforeEach(() => {
846+
fixture = TestBed.createComponent(StepperWithAriaInputs);
847+
fixture.detectChanges();
848+
stepHeader = fixture.nativeElement.querySelector('.mat-step-header');
849+
});
850+
851+
it('should not set aria-label or aria-labelledby attributes if they are not passed in', () => {
852+
expect(stepHeader.hasAttribute('aria-label')).toBe(false);
853+
expect(stepHeader.hasAttribute('aria-labelledby')).toBe(false);
854+
});
855+
856+
it('should set the aria-label attribute', () => {
857+
fixture.componentInstance.ariaLabel = 'First step';
858+
fixture.detectChanges();
859+
860+
expect(stepHeader.getAttribute('aria-label')).toBe('First step');
861+
});
862+
863+
it('should set the aria-labelledby attribute', () => {
864+
fixture.componentInstance.ariaLabelledby = 'first-step-label';
865+
fixture.detectChanges();
866+
867+
expect(stepHeader.getAttribute('aria-labelledby')).toBe('first-step-label');
868+
});
869+
870+
it('should not be able to set both an aria-label and aria-labelledby', () => {
871+
fixture.componentInstance.ariaLabel = 'First step';
872+
fixture.componentInstance.ariaLabelledby = 'first-step-label';
873+
fixture.detectChanges();
874+
875+
expect(stepHeader.getAttribute('aria-label')).toBe('First step');
876+
expect(stepHeader.hasAttribute('aria-labelledby')).toBe(false);
877+
});
878+
879+
});
839880
});
840881

841882
/** Asserts that keyboard interaction works correctly. */
@@ -1158,3 +1199,16 @@ class LinearStepperWithValidOptionalStep {
11581199
controls = [0, 0, 0].map(() => new FormControl());
11591200
step2Optional = false;
11601201
}
1202+
1203+
1204+
@Component({
1205+
template: `
1206+
<mat-horizontal-stepper>
1207+
<mat-step [aria-label]="ariaLabel" [aria-labelledby]="ariaLabelledby" label="One"></mat-step>
1208+
</mat-horizontal-stepper>
1209+
`
1210+
})
1211+
class StepperWithAriaInputs {
1212+
ariaLabel: string;
1213+
ariaLabelledby: string;
1214+
}

0 commit comments

Comments
 (0)