Skip to content

Commit b984662

Browse files
committed
fix(material/stepper): Adjust aria tab-related roles to fix violations
1 parent 06a93da commit b984662

File tree

4 files changed

+41
-34
lines changed

4 files changed

+41
-34
lines changed

src/material/stepper/step-header.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ import {_CdkPrivateStyleLoader, _VisuallyHiddenLoader} from '@angular/cdk/privat
3636
'class': 'mat-step-header',
3737
'[class.mat-step-header-empty-label]': '_hasEmptyLabel()',
3838
'[class]': '"mat-" + (color || "primary")',
39-
'role': 'tab',
39+
'role': '', // ignore cdk role in favor of setting appropriately in html
4040
},
4141
encapsulation: ViewEncapsulation.None,
4242
changeDetection: ChangeDetectionStrategy.OnPush,

src/material/stepper/stepper.html

Lines changed: 37 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@
1111
@switch (orientation) {
1212
@case ('horizontal') {
1313
<div class="mat-horizontal-stepper-wrapper">
14-
<div class="mat-horizontal-stepper-header-container">
14+
<div
15+
aria-orientation="horizontal"
16+
class="mat-horizontal-stepper-header-container"
17+
role="tablist">
1518
@for (step of steps; track step) {
1619
<ng-container
1720
[ngTemplateOutlet]="stepTemplate"
@@ -40,28 +43,38 @@
4043
}
4144

4245
@case ('vertical') {
43-
@for (step of steps; track step) {
44-
<div class="mat-step">
45-
<ng-container
46-
[ngTemplateOutlet]="stepTemplate"
47-
[ngTemplateOutletContext]="{step}"/>
48-
<div
49-
#animatedContainer
50-
class="mat-vertical-content-container"
51-
[class.mat-stepper-vertical-line]="!$last"
52-
[class.mat-vertical-content-container-active]="selectedIndex === $index"
53-
[attr.inert]="selectedIndex === $index ? null : ''">
54-
<div class="mat-vertical-stepper-content"
55-
role="tabpanel"
56-
[id]="_getStepContentId($index)"
57-
[attr.aria-labelledby]="_getStepLabelId($index)">
58-
<div class="mat-vertical-content">
59-
<ng-container [ngTemplateOutlet]="step.content"/>
46+
<div
47+
aria-orientation="vertical"
48+
class="mat-vertical-stepper-wrapper"
49+
role="tablist">
50+
@for (step of steps; track step) {
51+
<div
52+
class="mat-step"
53+
role="tab"
54+
[attr.aria-posinset]="step.index() + 1"
55+
[attr.aria-setsize]="steps.length"
56+
[attr.aria-selected]="step.isSelected()"
57+
>
58+
<ng-container
59+
[ngTemplateOutlet]="stepTemplate"
60+
[ngTemplateOutletContext]="{step}"/>
61+
<div
62+
#animatedContainer
63+
class="mat-vertical-content-container"
64+
[class.mat-stepper-vertical-line]="!$last"
65+
[class.mat-vertical-content-container-active]="selectedIndex === $index"
66+
[attr.inert]="selectedIndex === $index ? null : ''">
67+
<div class="mat-vertical-stepper-content"
68+
[id]="_getStepContentId($index)"
69+
[attr.aria-labelledby]="_getStepLabelId($index)">
70+
<div class="mat-vertical-content">
71+
<ng-container [ngTemplateOutlet]="step.content"/>
72+
</div>
6073
</div>
6174
</div>
6275
</div>
63-
</div>
64-
}
76+
}
77+
</div>
6578
}
6679
}
6780

@@ -74,10 +87,11 @@
7487
(keydown)="_onKeydown($event)"
7588
[tabIndex]="_getFocusIndex() === step.index() ? 0 : -1"
7689
[id]="_getStepLabelId(step.index())"
77-
[attr.aria-posinset]="step.index() + 1"
78-
[attr.aria-setsize]="steps.length"
90+
[attr.role]="orientation === 'horizontal' ? 'tab' : null"
91+
[attr.aria-posinset]="orientation === 'horizontal' ? step.index() + 1 : null"
92+
[attr.aria-setsize]="orientation === 'horizontal' ? steps.length : null"
93+
[attr.aria-selected]="orientation === 'horizontal' ? step.isSelected() : null"
7994
[attr.aria-controls]="_getStepContentId(step.index())"
80-
[attr.aria-selected]="step.isSelected()"
8195
[attr.aria-label]="step.ariaLabel || null"
8296
[attr.aria-labelledby]="(!step.ariaLabel && step.ariaLabelledby) ? step.ariaLabelledby : null"
8397
[attr.aria-disabled]="step.isNavigable() ? null : true"

src/material/stepper/stepper.spec.ts

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -114,11 +114,6 @@ describe('MatStepper', () => {
114114
expect(stepper.selected instanceof MatStep).toBe(true);
115115
});
116116

117-
it('should set the "tablist" role on stepper', () => {
118-
const stepperEl = fixture.debugElement.query(By.css('mat-stepper'))!.nativeElement;
119-
expect(stepperEl.getAttribute('role')).toBe('tablist');
120-
});
121-
122117
it('should display the correct label', () => {
123118
let selectedLabel = fixture.nativeElement.querySelector('[aria-selected="true"]');
124119
expect(selectedLabel.textContent).toMatch('Step 1');
@@ -344,7 +339,7 @@ describe('MatStepper', () => {
344339

345340
it('should set the correct aria-posinset and aria-setsize', () => {
346341
const headers = Array.from<HTMLElement>(
347-
fixture.nativeElement.querySelectorAll('.mat-step-header'),
342+
fixture.nativeElement.querySelectorAll('[role="tab"]'),
348343
);
349344

350345
expect(headers.map(header => header.getAttribute('aria-posinset'))).toEqual(['1', '2', '3']);
@@ -941,7 +936,7 @@ describe('MatStepper', () => {
941936
const fixture = createComponent(SimpleMatVerticalStepperApp);
942937
fixture.detectChanges();
943938

944-
const stepperEl = fixture.debugElement.query(By.css('mat-stepper'))!.nativeElement;
939+
const stepperEl = fixture.debugElement.query(By.css('[role="tablist"]'))!.nativeElement;
945940
expect(stepperEl.getAttribute('aria-orientation')).toBe('vertical');
946941
});
947942

@@ -1045,7 +1040,7 @@ describe('MatStepper', () => {
10451040
const fixture = createComponent(SimpleMatHorizontalStepperApp);
10461041
fixture.detectChanges();
10471042

1048-
const stepperEl = fixture.debugElement.query(By.css('mat-stepper'))!.nativeElement;
1043+
const stepperEl = fixture.debugElement.query(By.css('[role="tablist"]'))!.nativeElement;
10491044
expect(stepperEl.getAttribute('aria-orientation')).toBe('horizontal');
10501045
});
10511046

src/material/stepper/stepper.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,8 +130,6 @@ export class MatStep extends CdkStep implements ErrorStateMatcher, AfterContentI
130130
'[class.mat-stepper-header-position-bottom]': 'headerPosition === "bottom"',
131131
'[class.mat-stepper-animating]': '_isAnimating()',
132132
'[style.--mat-stepper-animation-duration]': '_getAnimationDuration()',
133-
'[attr.aria-orientation]': 'orientation',
134-
'role': 'tablist',
135133
},
136134
providers: [{provide: CdkStepper, useExisting: MatStepper}],
137135
encapsulation: ViewEncapsulation.None,

0 commit comments

Comments
 (0)