Skip to content

Commit 99cb6b1

Browse files
authoredApr 6, 2021
fix(ui5-wizard): implement revised accessibility spec (#2990)
* ui5-wizard: implement revised accessibility spec * (fix)ui5-wizard: implement revised accessibility spec * fix typo * fix linter * fix tests * apply comments * apply comments
1 parent f18d59a commit 99cb6b1

File tree

8 files changed

+119
-95
lines changed

8 files changed

+119
-95
lines changed
 

‎packages/fiori/src/Wizard.hbs

+5-9
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
<div class="ui5-wiz-root" aria-label="{{ariaLabelText}}">
2-
<nav class="ui5-wiz-nav" role="navigation" aria-roledescription="{{navAriaRoleDescription}}" tabindex="-1">
3-
<ul class="ui5-wiz-nav-list" role="list">
1+
<div class="ui5-wiz-root" aria-label="{{ariaLabelText}}" role="region">
2+
<nav class="ui5-wiz-nav" aria-label="{{navAriaLabelText}}" tabindex="-1">
3+
<div class="ui5-wiz-nav-list" role="list" aria-label="{{listAriaLabelText}}" aria-controls="ui5-wiz-content" >
44
{{#each _stepsInHeader}}
55
<ui5-wizard-tab
66
heading="{{heading}}"
@@ -12,11 +12,7 @@
1212
?hide-separator="{{hideSeparator}}"
1313
?active-separator="{{activeSeparator}}"
1414
?branching-separator="{{branchingSeparator}}"
15-
role="listitem"
16-
aria-posinset="{{pos}}"
17-
aria-setsize="{{size}}"
18-
aria-roledescription="{{roleDescription}}"
19-
aria-label="{{ariaLabel}}"
15+
._wizardTabAccInfo="{{accInfo}}"
2016
data-ui5-content-ref-id="{{this.refStepId}}"
2117
data-ui5-index="{{pos}}"
2218
_tab-index="{{tabIndex}}"
@@ -26,7 +22,7 @@
2622
style={{styles}}
2723
></ui5-wizard-tab>
2824
{{/each}}
29-
</ul>
25+
</div>
3026
</nav>
3127

3228
<div class="ui5-wiz-content" @scroll="{{onScroll}}">

‎packages/fiori/src/Wizard.js

+35-6
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import UI5Element from "@ui5/webcomponents-base/dist/UI5Element.js";
22
import litRender from "@ui5/webcomponents-base/dist/renderer/LitRenderer.js";
33
import { fetchI18nBundle, getI18nBundle } from "@ui5/webcomponents-base/dist/i18nBundle.js";
4-
import { getEffectiveAriaLabelText } from "@ui5/webcomponents-base/dist/util/AriaLabelHelper.js";
54
import ItemNavigation from "@ui5/webcomponents-base/dist/delegate/ItemNavigation.js";
65
import NavigationMode from "@ui5/webcomponents-base/dist/types/NavigationMode.js";
76
import Float from "@ui5/webcomponents-base/dist/types/Float.js";
@@ -16,6 +15,11 @@ import ResponsivePopover from "@ui5/webcomponents/dist/ResponsivePopover.js";
1615
import {
1716
WIZARD_NAV_STEP_DEFAULT_HEADING,
1817
WIZARD_NAV_ARIA_ROLE_DESCRIPTION,
18+
WIZARD_NAV_ARIA_LABEL,
19+
WIZARD_LIST_ARIA_LABEL,
20+
WIZARD_ACTIONSHEET_STEPS_ARIA_LABEL,
21+
WIZARD_STEP_ARIA_LABEL,
22+
WIZARD_CURRENT_STEP_ARIA_LABEL,
1923
} from "./generated/i18n/i18n-defaults.js";
2024

2125
// Step in header and content
@@ -678,6 +682,14 @@ class Wizard extends UI5Element {
678682
return contentHeight;
679683
}
680684

685+
getStepAriaLabelText(step, ariaLabel) {
686+
if (step.selected) {
687+
return this.i18nBundle.getText(WIZARD_CURRENT_STEP_ARIA_LABEL, ariaLabel);
688+
}
689+
690+
return this.i18nBundle.getText(WIZARD_STEP_ARIA_LABEL, ariaLabel);
691+
}
692+
681693
get stepsDOM() {
682694
return Array.from(this.shadowRoot.querySelectorAll(".ui5-wiz-content-item"));
683695
}
@@ -755,6 +767,18 @@ class Wizard extends UI5Element {
755767
return this.i18nBundle.getText(WIZARD_NAV_ARIA_ROLE_DESCRIPTION);
756768
}
757769

770+
get navAriaLabelText() {
771+
return this.i18nBundle.getText(WIZARD_NAV_ARIA_LABEL);
772+
}
773+
774+
get listAriaLabelText() {
775+
return this.i18nBundle.getText(WIZARD_LIST_ARIA_LABEL);
776+
}
777+
778+
get actionSheetStepsText() {
779+
return this.i18nBundle.getText(WIZARD_ACTIONSHEET_STEPS_ARIA_LABEL);
780+
}
781+
758782
get navStepDefaultHeading() {
759783
return this.i18nBundle.getText(WIZARD_NAV_STEP_DEFAULT_HEADING);
760784
}
@@ -778,6 +802,7 @@ class Wizard extends UI5Element {
778802
const stepsCount = this.stepsCount;
779803
const selectedStepIndex = this.getSelectedStepIndex();
780804
let inintialZIndex = this.steps.length + 10;
805+
let accInfo;
781806

782807
this._adjustHeaderOverflow();
783808

@@ -787,10 +812,16 @@ class Wizard extends UI5Element {
787812
// Hide separator if it's the last step and it's not a branching one
788813
const hideSeparator = (idx === stepsCount - 1) && !step.branching;
789814

790-
// Calculate the step's aria-roledectioption: "1. heading" or "Step 1".
791-
const roleDescription = step.heading ? `${pos}. ${step.heading}` : `${this.navStepDefaultHeading} ${pos}`;
815+
const isOptional = step.subheading ? "Optional" : "";
816+
const ariaLabel = (step.heading ? `${pos} ${step.heading} ${isOptional}` : `${this.navStepDefaultHeading} ${pos} ${isOptional}`).trim();
792817
const isAfterCurrent = (idx > selectedStepIndex);
793818

819+
accInfo = {
820+
"ariaSetsize": stepsCount,
821+
"ariaPosinset": pos,
822+
"ariaLabel": this.getStepAriaLabelText(step, ariaLabel),
823+
};
824+
794825
return {
795826
icon: step.icon,
796827
heading: step.heading,
@@ -802,9 +833,7 @@ class Wizard extends UI5Element {
802833
activeSeparator: (idx < lastEnabledStepIndex) && !step.disabled,
803834
branchingSeparator: step.branching,
804835
pos,
805-
size: stepsCount,
806-
roleDescription,
807-
ariaLabel: getEffectiveAriaLabelText(step),
836+
accInfo,
808837
refStepId: step._id,
809838
tabIndex: this.selectedStepIndex === idx ? "0" : "-1",
810839
styles: `z-index: ${isAfterCurrent ? --inintialZIndex : 1}`,

‎packages/fiori/src/WizardPopover.hbs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<ui5-responsive-popover
22
horizontal-align="Center"
33
placement-type="Bottom"
4+
aria-label="{{actionSheetStepsText}}"
45
class="{{classes.popover}}"
56
@ui5-after-close={{_afterClosePopover}}
67
content-only-on-desktop
@@ -15,7 +16,7 @@
1516
icon="{{this.icon}}"
1617
?disabled="{{this.disabled}}"
1718
design="Transparent"
18-
data-ui5-header-tab-ref-id="{{this.ariaPosinset}}"
19+
data-ui5-header-tab-ref-id="{{pos}}"
1920
@click="{{../_onOverflowStepButtonClick}}"
2021
>
2122
{{this.heading}}

‎packages/fiori/src/WizardTab.hbs

+6-7
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
<div class="ui5-wiz-step-root"
2-
role="{{role}}"
2+
role="listitem"
33
tabindex="{{tabIndex}}"
4-
aria-current="{{ariaCurrent}}"
5-
aria-setsize="{{ariaSetsize}}"
6-
aria-posinset="{{ariaPosinset}}"
7-
aria-disabled="{{ariaDisabled}}"
8-
aria-label="{{ariaLabel}}"
9-
aria-roledescription="{{ariaRoledescription}}"
4+
aria-current="{{accInfo.ariaCurrent}}"
5+
aria-setsize="{{accInfo.ariaSetsize}}"
6+
aria-posinset="{{accInfo.ariaPosinset}}"
7+
aria-disabled="{{accInfo.ariaDisabled}}"
8+
aria-label="{{accInfo.ariaLabel}}"
109
@click="{{_onclick}}"
1110
@keydown="{{_onkeydown}}"
1211
@keyup="{{_onkeyup}}"

‎packages/fiori/src/WizardTab.js

+14-72
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import UI5Element from "@ui5/webcomponents-base/dist/UI5Element.js";
22
import litRender from "@ui5/webcomponents-base/dist/renderer/LitRenderer.js";
33
import { isSpace, isEnter } from "@ui5/webcomponents-base/dist/Keys.js";
4-
import Integer from "@ui5/webcomponents-base/dist/types/Integer.js";
54
import Icon from "@ui5/webcomponents/dist/Icon.js";
65

76
import WizardTabTemplate from "./generated/templates/WizardTabTemplate.lit.js";
@@ -102,69 +101,6 @@ const metadata = {
102101
type: Boolean,
103102
},
104103

105-
/**
106-
* Defines the role of the step.
107-
* @type {boolean}
108-
* @defaultvalue listitem
109-
* @private
110-
*/
111-
role: {
112-
type: String,
113-
defaultValue: "listitem",
114-
},
115-
116-
/**
117-
* Defines the aria-roledescription of the step.
118-
* @type {boolean}
119-
* @defaultvalue ""
120-
* @private
121-
*/
122-
ariaRoledescription: {
123-
type: String,
124-
},
125-
126-
/**
127-
* Defines the aria-label of the step.
128-
* @type {boolean}
129-
* @defaultvalue undefined
130-
* @private
131-
*/
132-
ariaLabel: {
133-
type: String,
134-
defaultValue: undefined,
135-
},
136-
137-
/**
138-
* Defines the aria-labelledby of the step.
139-
* @type {boolean}
140-
* @defaultvalue undefined
141-
* @private
142-
*/
143-
ariaLabelledby: {
144-
type: String,
145-
defaultValue: undefined,
146-
},
147-
148-
/**
149-
* Defines the aria-setsize of the step.
150-
* @type {boolean}
151-
* @defaultvalue undefined
152-
* @private
153-
*/
154-
ariaSetsize: {
155-
type: Integer,
156-
},
157-
158-
/**
159-
* Defines the aria-posinset of the step.
160-
* @type {boolean}
161-
* @defaultvalue undefined
162-
* @private
163-
*/
164-
ariaPosinset: {
165-
type: Integer,
166-
},
167-
168104
/**
169105
* Defines the tabindex of the step.
170106
* @type {String}
@@ -175,6 +111,10 @@ const metadata = {
175111
type: String,
176112
defaultValue: "-1",
177113
},
114+
115+
_wizardTabAccInfo: {
116+
type: Object,
117+
},
178118
},
179119
slots: /** @lends sap.ui.webcomponents.fiori.WizardTab.prototype */ {
180120
},
@@ -273,17 +213,19 @@ class WizardTab extends UI5Element {
273213
return this.disabled ? undefined : this._tabIndex;
274214
}
275215

276-
get ariaCurrent() {
277-
return this.selected ? "step" : undefined;
278-
}
279-
280-
get ariaDisabled() {
281-
return this.disabled ? "true" : undefined;
282-
}
283-
284216
get hasTexts() {
285217
return this.heading || this.subheading;
286218
}
219+
220+
get accInfo() {
221+
return {
222+
"ariaSetsize": this._wizardTabAccInfo && this._wizardTabAccInfo.ariaSetsize,
223+
"ariaPosinset": this._wizardTabAccInfo && this._wizardTabAccInfo.ariaPosinset,
224+
"ariaLabel": this._wizardTabAccInfo && this._wizardTabAccInfo.ariaLabel,
225+
"ariaCurrent": this.selected ? "true" : undefined,
226+
"ariaDisabled": this.disabled ? "true" : undefined,
227+
};
228+
}
287229
}
288230

289231
WizardTab.define();

‎packages/fiori/src/i18n/messagebundle.properties

+15
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,21 @@ SHELLBAR_OVERFLOW = More
132132
#XACT: ARIA announcement for the cancel button
133133
SHELLBAR_CANCEL = Cancel
134134

135+
#XACT: ARIA announcement for aria-label attribute of Wizard navigation header
136+
WIZARD_NAV_ARIA_LABEL = Wizard Progress Bar
137+
138+
#XACT: ARIA announcement for aria-label attribute of Wizard list
139+
WIZARD_LIST_ARIA_LABEL = Wizard Steps
140+
141+
#XACT: ARIA announcement for aria-label attribute of Wizard ActionSheet steps
142+
WIZARD_ACTIONSHEET_STEPS_ARIA_LABEL = Steps
143+
144+
#XACT: ARIA announcement for aria-label attribute of Wizard steps
145+
WIZARD_STEP_ARIA_LABEL=Step {0}
146+
147+
#XACT: ARIA announcement for aria-label attribute of Wizard current step
148+
WIZARD_CURRENT_STEP_ARIA_LABEL=Step {0} current
149+
135150
#XACT: ARIA announcement for roledescription attribute of Wizard navigation header
136151
WIZARD_NAV_ARIA_ROLE_DESCRIPTION = Wizard
137152

‎packages/fiori/src/themes/WizardPopover.css

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
@import "./InvisibleTextStyles.css";
2+
13
.ui5-wizard-responsive-popover {
24
box-shadow: var(--sapContent_Shadow1);
35
}

‎packages/fiori/test/specs/Wizard.spec.js

+40
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,43 @@ describe("Wizard general interaction", () => {
1818
"First step in the header is selected.");
1919
});
2020

21+
it("ARIA Attributes", () => {
22+
const wiz = browser.$("#wizTest");
23+
const wizRoot = wiz.shadow$(".ui5-wiz-root");
24+
const wizNav = wiz.shadow$(".ui5-wiz-nav");
25+
const wizList = wiz.shadow$(".ui5-wiz-nav-list");
26+
27+
const wizRootText = "Wizard";
28+
const wizNavText = "Wizard Progress Bar";
29+
const wizListText = "Wizard Steps";
30+
31+
assert.strictEqual(wizRoot.getAttribute("role"), "region",
32+
"Wizard has role set.");
33+
assert.strictEqual(wizRoot.getAttribute("aria-label"), wizRootText,
34+
"Wizard has aria-label set.");
35+
assert.strictEqual(wizNav.getAttribute("aria-label"), wizNavText,
36+
"Wizard nav has aria-label set.");
37+
assert.strictEqual(wizList.getAttribute("role"), "list",
38+
"Wizard list has role set..");
39+
assert.strictEqual(wizList.getAttribute("aria-controls"), "ui5-wiz-content",
40+
"Wizard list has aria-controls set.");
41+
assert.strictEqual(wizList.getAttribute("aria-label"), wizListText,
42+
"Wizard list has aria-label set.");
43+
});
44+
2145
it("move to next step by API", () => {
2246
const wiz = browser.$("#wizTest");
2347
const btnToStep2 = browser.$("#toStep2");
2448
const step1 = browser.$("#st1");
2549
const step2 = browser.$("#st2");
2650
const step1InHeader = wiz.shadow$(`[data-ui5-index="1"]`);
2751
const step2InHeader = wiz.shadow$(`[data-ui5-index="2"]`);
52+
const step1InHeaderRoot = step1InHeader.shadow$(`.ui5-wiz-step-root`);
53+
const step2InHeaderRoot = step2InHeader.shadow$(`.ui5-wiz-step-root`);
54+
55+
const stepText = "Step 1 Product type";
56+
const currentStepText = "Step 2 Product Information current";
57+
2858

2959
// act - the click handler calls the API
3060
btnToStep2.click();
@@ -35,6 +65,12 @@ describe("Wizard general interaction", () => {
3565
assert.strictEqual(step1InHeader.getAttribute("selected"), null,
3666
"First step in the header not is selected.");
3767

68+
// assert - check if aria-attributes are applied correctly when step is not selected
69+
assert.strictEqual(step1InHeaderRoot.getAttribute("role"), "listitem",
70+
"First step in the header has role.");
71+
assert.strictEqual(step1InHeaderRoot.getAttribute("aria-label"), stepText,
72+
"First step in the header has aria-label.");
73+
3874
// assert - that second step in the content and in the header are properly selected
3975
assert.strictEqual(step2.getAttribute("selected"), "true",
4076
"Second step in the content is selected.");
@@ -44,6 +80,10 @@ describe("Wizard general interaction", () => {
4480
"Second step is enabled.");
4581
assert.strictEqual(step2InHeader.getAttribute("disabled"), null,
4682
"Second step in header is enabled.");
83+
84+
// assert - check if aria-label is applied correctly when step is selected
85+
assert.strictEqual(step2InHeaderRoot.getAttribute("aria-label"), currentStepText,
86+
"Second step in the header has aria-label.");
4787
});
4888

4989
it("move to next step by click", () => {

0 commit comments

Comments
 (0)
Please sign in to comment.