Skip to content

Commit 8c0225c

Browse files
authoredMay 10, 2021
feat(ui5-product-switch): Implement accessibility specification (#2865)
FIXES: #2707
1 parent 95986e2 commit 8c0225c

File tree

6 files changed

+110
-7
lines changed

6 files changed

+110
-7
lines changed
 

‎packages/fiori/src/ProductSwitch.hbs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
<div class="ui5-product-switch-root" @focusin={{_onfocusin}}>
1+
<div class="ui5-product-switch-root" role="list" aria-label="{{_ariaLabelText}}" @focusin={{_onfocusin}} @keydown={{_onkeydown}}>
22
<slot></slot>
33
</div>

‎packages/fiori/src/ProductSwitch.js

+70-6
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,22 @@
1+
import { fetchI18nBundle, getI18nBundle } from "@ui5/webcomponents-base/dist/i18nBundle.js";
2+
13
import UI5Element from "@ui5/webcomponents-base/dist/UI5Element.js";
24
import ItemNavigation from "@ui5/webcomponents-base/dist/delegate/ItemNavigation.js";
3-
import ItemNavigationBehavior from "@ui5/webcomponents-base/dist/types/ItemNavigationBehavior.js";
45
import ResizeHandler from "@ui5/webcomponents-base/dist/delegate/ResizeHandler.js";
56
import Integer from "@ui5/webcomponents-base/dist/types/Integer.js";
67
import litRender from "@ui5/webcomponents-base/dist/renderer/LitRenderer.js";
8+
9+
import {
10+
isDown,
11+
isUp,
12+
} from "@ui5/webcomponents-base/dist/Keys.js";
13+
714
import ProductSwitchTemplate from "./generated/templates/ProductSwitchTemplate.lit.js";
815

16+
import {
17+
PRODUCT_SWITCH_CONTAINER_LABEL,
18+
} from "./generated/i18n/i18n-defaults.js";
19+
920
// Styles
1021
import ProductSwitchCss from "./generated/themes/ProductSwitch.css.js";
1122

@@ -46,6 +57,20 @@ const metadata = {
4657
* The <code>ui5-product-switch</code> is an SAP Fiori specific web component that is used in <code>ui5-shellbar</code>
4758
* and allows the user to easily switch between products.
4859
* <br><br>
60+
*
61+
* <h3>Keyboard Handling</h3>
62+
* The <code>ui5-product-switch</code> provides advanced keyboard handling.
63+
* When focused, the user can use the following keyboard
64+
* shortcuts in order to perform a navigation:
65+
* <br>
66+
* <ul>
67+
* <li>[TAB] - Move focus to the next interactive element after the <code>ui5-product-switch</code></li>
68+
* <li>[UP/DOWN] - Navigates up and down the items </li>
69+
* <li>[LEFT/RIGHT] - Navigates left and right the items</li>
70+
* </ul>
71+
* <br>
72+
* <br>
73+
*
4974
* <h3>ES6 Module Import</h3>
5075
* <code>import "@ui5/webcomponents-fiori/dist/ProductSwitch.js";</code>
5176
* <br>
@@ -63,11 +88,15 @@ class ProductSwitch extends UI5Element {
6388
constructor() {
6489
super();
6590

91+
this._currentIndex = 0;
92+
this._rowSize = 4;
93+
6694
this._itemNavigation = new ItemNavigation(this, {
67-
rowSize: 4,
68-
behavior: ItemNavigationBehavior.Cyclic,
95+
rowSize: this._rowSize,
6996
getItemsCallback: () => this.items,
7097
});
98+
99+
this.i18nBundle = getI18nBundle("@ui5/webcomponents");
71100
}
72101

73102
static get metadata() {
@@ -93,6 +122,14 @@ class ProductSwitch extends UI5Element {
93122
};
94123
}
95124

125+
static async onDefine() {
126+
await fetchI18nBundle("@ui5/webcomponents");
127+
}
128+
129+
get _ariaLabelText() {
130+
return this.i18nBundle.getText(PRODUCT_SWITCH_CONTAINER_LABEL);
131+
}
132+
96133
onEnterDOM() {
97134
this._handleResizeBound = this._handleResize.bind(this);
98135

@@ -111,18 +148,45 @@ class ProductSwitch extends UI5Element {
111148
const documentWidth = document.body.clientWidth;
112149

113150
if (documentWidth <= this.constructor.ROW_MIN_WIDTH.ONE_COLUMN) {
114-
this._itemNavigation.setRowSize(1);
151+
this._setRowSize(1);
115152
} else if (documentWidth <= this.constructor.ROW_MIN_WIDTH.THREE_COLUMN || this.items.length <= 6) {
116-
this._itemNavigation.setRowSize(3);
153+
this._setRowSize(3);
117154
} else {
118-
this._itemNavigation.setRowSize(4);
155+
this._setRowSize(4);
119156
}
120157
}
121158

122159
_onfocusin(event) {
123160
const target = event.target;
124161

125162
this._itemNavigation.setCurrentItem(target);
163+
this._currentIndex = this.items.indexOf(target);
164+
}
165+
166+
_setRowSize(size) {
167+
this._rowSize = size;
168+
this._itemNavigation.setRowSize(size);
169+
}
170+
171+
_onkeydown(event) {
172+
if (isDown(event)) {
173+
this._handleDown(event);
174+
} else if (isUp(event)) {
175+
this._handleUp(event);
176+
}
177+
}
178+
179+
_handleDown(event) {
180+
const itemsLength = this.items.length;
181+
if (this._currentIndex + this._rowSize > itemsLength) { // border reached, do nothing
182+
event.stopPropagation();
183+
}
184+
}
185+
186+
_handleUp(event) {
187+
if (this._currentIndex - this._rowSize < 0) { // border reached, do nothing
188+
event.stopPropagation();
189+
}
126190
}
127191
}
128192

‎packages/fiori/src/ProductSwitchItem.hbs

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
</a>
1414
{{else}}
1515
<div
16+
role="listitem"
1617
class="ui5-product-switch-item-root"
1718
data-sap-focus-ref
1819
@focusout="{{_onfocusout}}"

‎packages/fiori/src/ProductSwitchItem.js

+11
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,17 @@ const metadata = {
126126
* <br><br>
127127
* <b>Note:</b> <code>ui5-product-switch-item</code> is not supported when used outside of <code>ui5-product-switch</code>.
128128
* <br><br>
129+
*
130+
* <h3>Keyboard Handling</h3>
131+
* The <code>ui5-product-switch</code> provides advanced keyboard handling.
132+
* When focused, the user can use the following keyboard
133+
* shortcuts in order to perform a navigation:
134+
* <br>
135+
* <ul>
136+
* <li>[SPACE/ENTER/RETURN] - Trigger <code>ui5-click</code> event</li>
137+
* </ul>
138+
* <br><br>
139+
*
129140
* <h3>ES6 Module Import</h3>
130141
* <code>import "@ui5/webcomponents-fiori/dist/ProductSwitchItem.js";</code>
131142
*

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

+3
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,9 @@ SHELLBAR_PROFILE = Profile
123123
#XACT: ARIA announcement for the products button
124124
SHELLBAR_PRODUCTS = Products
125125

126+
#XACT: ARIA announcement of ProductSwitch container
127+
PRODUCT_SWITCH_CONTAINER_LABEL=Products
128+
126129
#XACT: ARIA announcement for the search button
127130
SHELLBAR_SEARCH = Search
128131

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

+24
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,27 @@ describe("ProductSwitch general interaction", () => {
1818
assert.strictEqual(threeColumnPSItemCount <= 6, threeColumnPSAttrValue === 3, "product switch should have 3 columns.");
1919
});
2020
});
21+
22+
describe("ARIA attributes", () => {
23+
before(() => {
24+
browser.url(`http://localhost:${PORT}/test-resources/pages/ProductSwitch.html`);
25+
});
26+
27+
it ("role and aria-label set correctly", () => {
28+
const productSwitch = $("#productSwitchFourColumn");
29+
const productSwitchRoot = productSwitch.shadow$(".ui5-product-switch-root");
30+
31+
assert.strictEqual(productSwitchRoot.getAttribute("role"), "list", "should have role list");
32+
assert.strictEqual(productSwitchRoot.getAttribute("aria-label"), "Products", "aria-label reference is correct");
33+
});
34+
35+
it ("items attributes set correctly", () => {
36+
let items = $$("#productSwitchThreeColumn > ui5-product-switch-item");
37+
38+
items.forEach(item => {
39+
const itemDom = item.shadow$(".ui5-product-switch-item-root");
40+
41+
assert.strictEqual(itemDom.getAttribute("role"), "listitem", "should have role list");
42+
})
43+
});
44+
});

0 commit comments

Comments
 (0)
Please sign in to comment.