Skip to content

Commit 3b7b439

Browse files
authored
feat(ui5-shellbar): implement accessibility spec (#1553)
1 parent 541df6a commit 3b7b439

File tree

7 files changed

+220
-20
lines changed

7 files changed

+220
-20
lines changed

packages/fiori/src/ShellBar.hbs

+35-7
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,37 @@
11
<div
22
class="{{classes.wrapper}}"
33
dir="{{rtl}}"
4+
role="banner"
5+
aria-label="{{_shellbarText}}"
46
>
57
<div class="ui5-shellbar-overflow-container ui5-shellbar-overflow-container-left">
68

79
{{#if startButton.length}}
810
<slot name="startButton"></slot>
911
{{/if}}
1012

11-
{{#unless interactiveLogo}}
12-
<img class="ui5-shellbar-logo" src="{{logo}}" @click="{{_logoPress}}" />
13-
{{/unless}}
13+
{{#if hasFocusableLogo}}
14+
<img class="ui5-shellbar-logo"
15+
src="{{logo}}"
16+
@click="{{_logoPress}}"
17+
role="button"
18+
aria-label="{{_logoText}}"
19+
title="{{_logoText}}"
20+
@keydown="{{_logoKeydown}}"
21+
@keyup="{{_logoKeyup}}"
22+
tabindex="0"
23+
/>
24+
{{/if}}
1425

1526
{{#if showArrowDown}}
16-
<button tabindex="{{menuBtnTabindex}}" class="{{classes.button}}" @click="{{_header.press}}">
17-
{{#if interactiveLogo}}
18-
<img class="ui5-shellbar-logo" src="{{logo}}" @click="{{_logoPress}}" />
27+
<button tabindex="{{menuBtnTabindex}}"
28+
class="{{classes.button}}"
29+
@click="{{_header.press}}"
30+
aria-haspopup="{{menuBtnHasPopup}}"
31+
aria-expanded="{{menuPopoverExpanded}}"
32+
>
33+
{{#if hasNonFocusableLogo}}
34+
<img class="ui5-shellbar-logo" src="{{logo}}" aria-label="{{_logoText}}" title="{{_logoText}}"/>
1935
{{/if}}
2036

2137
{{#if primaryTitle}}
@@ -35,6 +51,8 @@
3551
{{#if showCoPilot}}
3652
<div class="ui5-shellbar-copilot-wrapper"
3753
tabindex="0"
54+
aria-label="{{_copilotText}}"
55+
title="{{_copilotText}}"
3856
@keydown="{{_coPilotKeydown}}"
3957
@keyup="{{_coPilotKeyup}}"
4058
?active="{{coPilotActive}}">
@@ -66,6 +84,8 @@
6684
data-ui5-text="Search"
6785
data-ui5-notification-count="{{notificationCount}}"
6886
@click={{_handleSearchIconPress}}
87+
aria-label="{{_searchText}}"
88+
._buttonAccInfo="{{accInfo.search}}"
6989
></ui5-button>
7090
{{/if}}
7191

@@ -91,6 +111,8 @@
91111
data-ui5-text="Notifications"
92112
data-ui5-notification-count="{{notificationCount}}"
93113
@click={{_handleNotificationsPress}}
114+
aria-label="{{_notificationsText}}"
115+
._buttonAccInfo="{{accInfo.notifications}}"
94116
></ui5-button>
95117
{{/if}}
96118

@@ -100,6 +122,8 @@
100122
class="{{classes.items.overflow}} ui5-shellbar-button ui5-shellbar-overflow-button-shown ui5-shellbar-overflow-button"
101123
icon="sap-icon://overflow"
102124
@click="{{_handleOverflowPress}}"
125+
aria-label="{{_overflowText}}"
126+
._buttonAccInfo="{{accInfo.overflow}}"
103127
></ui5-button>
104128

105129
{{#if hasProfile}}
@@ -108,6 +132,8 @@
108132
id="{{this._id}}-item-3"
109133
@click={{_handleProfilePress}}
110134
style="{{styles.items.profile}}"
135+
aria-label="{{_profileText}}"
136+
._buttonAccInfo="{{accInfo.profile}}"
111137
class="ui5-shellbar-button ui5-shellbar-image-button">
112138
<slot
113139
name="profile"
@@ -123,14 +149,16 @@
123149
icon="sap-icon://grid"
124150
data-ui5-text="Product Switch"
125151
@click={{_handleProductSwitchPress}}
152+
aria-label="{{_productsText}}"
153+
._buttonAccInfo="{{accInfo.products}}"
126154
></ui5-button>
127155
{{/if}}
128156
</div>
129157
</div>
130158
</div>
131159

132160
{{#*inline "coPilot"}}
133-
<svg @click="{{_coPilotClick}}" focusable="false" width="44" height="44" viewBox="-150 -150 300 300" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="ui5-shellbar-coPilot">
161+
<svg @click="{{_coPilotClick}}" focusable="false" width="44" role="presentation" aria-hidden="true" height="44" viewBox="-150 -150 300 300" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="ui5-shellbar-coPilot">
134162
<defs>
135163
<linearGradient id="f" x1="0%" x2="100%" y1="100%" y2="0%"><stop offset="0%" stop-color="#c0d9f2" stop-opacity=".87"/><stop offset="80%" stop-color="#fff" stop-opacity=".87"/></linearGradient>
136164
<linearGradient id="e" x1="0%" x2="100%" y1="100%" y2="0%"><stop offset="0%" stop-color="#b4d2ff" stop-opacity=".16"/><stop offset="80%" stop-color="#fff" stop-opacity=".16"/></linearGradient>

packages/fiori/src/ShellBar.js

+138-7
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,23 @@ import StandardListItem from "@ui5/webcomponents/dist/StandardListItem.js";
1010
import List from "@ui5/webcomponents/dist/List.js";
1111
import Popover from "@ui5/webcomponents/dist/Popover.js";
1212
import Button from "@ui5/webcomponents/dist/Button.js";
13+
import { fetchI18nBundle, getI18nBundle } from "@ui5/webcomponents-base/dist/i18nBundle.js";
1314
import "@ui5/webcomponents-icons/dist/icons/search.js";
1415
import "@ui5/webcomponents-icons/dist/icons/bell.js";
1516
import "@ui5/webcomponents-icons/dist/icons/overflow.js";
1617
import "@ui5/webcomponents-icons/dist/icons/grid.js";
1718

19+
import {
20+
SHELLBAR_LABEL,
21+
SHELLBAR_LOGO,
22+
SHELLBAR_COPILOT,
23+
SHELLBAR_NOTIFICATIONS,
24+
SHELLBAR_PROFILE,
25+
SHELLBAR_PRODUCTS,
26+
SHELLBAR_SEARCH,
27+
SHELLBAR_OVERFLOW,
28+
} from "./generated/i18n/i18n-defaults.js";
29+
1830
// Templates
1931
import ShellBarTemplate from "./generated/templates/ShellBarTemplate.lit.js";
2032
import ShellBarPopoverTemplate from "./generated/templates/ShellBarPopoverTemplate.lit.js";
@@ -144,6 +156,14 @@ const metadata = {
144156
type: String,
145157
multiple: true,
146158
},
159+
_menuPopoverExpanded: {
160+
type: Boolean,
161+
noAttribute: true,
162+
},
163+
_overflowPopoverExpanded: {
164+
type: Boolean,
165+
noAttribute: true,
166+
},
147167
},
148168
managedSlots: true,
149169
slots: /** @lends sap.ui.webcomponents.fiori.ShellBar.prototype */ {
@@ -389,7 +409,7 @@ class ShellBar extends UI5Element {
389409
press: async () => {
390410
this._updateClonedMenuItems();
391411

392-
if (this.menuItems.length) {
412+
if (this.hasMenuItems) {
393413
this.updateStaticAreaItemContentDensity();
394414
const menuPopover = await this._getMenuPopover();
395415
menuPopover.openBy(this.shadowRoot.querySelector(".ui5-shellbar-menu-button"));
@@ -406,6 +426,8 @@ class ShellBar extends UI5Element {
406426
this.overflowPopover.close();
407427
this._overflowActions();
408428
};
429+
430+
this.i18nBundle = getI18nBundle("@ui5/webcomponents-fiori");
409431
}
410432

411433
_menuItemPress(event) {
@@ -414,12 +436,45 @@ class ShellBar extends UI5Element {
414436
}, true);
415437
}
416438

417-
_logoPress(event) {
439+
_logoPress() {
418440
this.fireEvent("logoClick", {
419441
targetRef: this.shadowRoot.querySelector(".ui5-shellbar-logo"),
420442
});
421443
}
422444

445+
_menuPopoverBeforeOpen() {
446+
this._menuPopoverExpanded = true;
447+
}
448+
449+
_menuPopoverAfterClose() {
450+
this._menuPopoverExpanded = false;
451+
}
452+
453+
_overflowPopoverBeforeOpen() {
454+
this._overflowPopoverExpanded = true;
455+
}
456+
457+
_overflowPopoverAfterClose() {
458+
this._overflowPopoverExpanded = false;
459+
}
460+
461+
_logoKeyup(event) {
462+
if (isSpace(event)) {
463+
this._logoPress();
464+
}
465+
}
466+
467+
_logoKeydown(event) {
468+
if (isSpace(event)) {
469+
event.preventDefault();
470+
return;
471+
}
472+
473+
if (isEnter(event)) {
474+
this._logoPress();
475+
}
476+
}
477+
423478
_fireCoPilotClick() {
424479
this.fireEvent("coPilotClick", {
425480
targetRef: this.shadowRoot.querySelector(".ui5-shellbar-coPilot"),
@@ -792,7 +847,7 @@ class ShellBar extends UI5Element {
792847
"ui5-shellbar-with-searchfield": this.searchField.length,
793848
},
794849
button: {
795-
"ui5-shellbar-menu-button--interactive": !!this.menuItems.length,
850+
"ui5-shellbar-menu-button--interactive": this.hasMenuItems,
796851
"ui5-shellbar-menu-button": true,
797852
},
798853
items: {
@@ -839,12 +894,20 @@ class ShellBar extends UI5Element {
839894
return this._itemsInfo.filter(itemInfo => !!itemInfo.custom);
840895
}
841896

842-
get interactiveLogo() {
843-
return this.breakpointSize === "S";
897+
get nonFocusableLogo() {
898+
return this.breakpointSize === "S" && this.hasMenuItems;
899+
}
900+
901+
get hasFocusableLogo() {
902+
return this.logo && !this.nonFocusableLogo;
903+
}
904+
905+
get hasNonFocusableLogo() {
906+
return this.logo && this.nonFocusableLogo;
844907
}
845908

846909
get showArrowDown() {
847-
return this.primaryTitle || (this.logo && this.interactiveLogo);
910+
return this.primaryTitle || this.hasInteractvieLogo;
848911
}
849912

850913
get popoverHorizontalAlign() {
@@ -863,12 +926,80 @@ class ShellBar extends UI5Element {
863926
return !!this.profile.length;
864927
}
865928

929+
get hasMenuItems() {
930+
return this.menuItems.length > 0;
931+
}
932+
933+
get menuBtnHasPopup() {
934+
return this.hasMenuItems ? true : undefined;
935+
}
936+
866937
get menuBtnTabindex() {
867-
return this.menuItems.length > 0 ? "0" : "-1";
938+
return this.hasMenuItems ? "0" : "-1";
939+
}
940+
941+
get menuPopoverExpanded() {
942+
return this.hasMenuItems ? this._menuPopoverExpanded : undefined;
943+
}
944+
945+
get _shellbarText() {
946+
return this.i18nBundle.getText(SHELLBAR_LABEL);
947+
}
948+
949+
get _logoText() {
950+
return this.i18nBundle.getText(SHELLBAR_LOGO);
951+
}
952+
953+
get _copilotText() {
954+
return this.i18nBundle.getText(SHELLBAR_COPILOT);
955+
}
956+
957+
get _notificationsText() {
958+
return this.i18nBundle.getText(SHELLBAR_NOTIFICATIONS, this.notificationCount);
959+
}
960+
961+
get _profileText() {
962+
return this.i18nBundle.getText(SHELLBAR_PROFILE);
963+
}
964+
965+
get _productsText() {
966+
return this.i18nBundle.getText(SHELLBAR_PRODUCTS);
967+
}
968+
969+
get _searchText() {
970+
return this.i18nBundle.getText(SHELLBAR_SEARCH);
971+
}
972+
973+
get _overflowText() {
974+
return this.i18nBundle.getText(SHELLBAR_OVERFLOW);
975+
}
976+
977+
get accInfo() {
978+
return {
979+
notifications: {
980+
"title": this._notificationsText,
981+
},
982+
profile: {
983+
"title": this._profileText,
984+
},
985+
products: {
986+
"title": this._productsText,
987+
},
988+
search: {
989+
"ariaExpanded": this.showSearchField,
990+
"title": this._searchText,
991+
},
992+
overflow: {
993+
"title": this._overflowText,
994+
"ariaHaspopup": true,
995+
"ariaExpanded": this._overflowPopoverExpanded,
996+
},
997+
};
868998
}
869999

8701000
static async onDefine() {
8711001
await Promise.all([
1002+
fetchI18nBundle("@ui5/webcomponents-fiori"),
8721003
Button.define(),
8731004
List.define(),
8741005
Popover.define(),

packages/fiori/src/ShellBarPopover.hbs

+12-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,22 @@
1-
<ui5-popover class="ui5-shellbar-menu-popover" placement-type="Bottom">
1+
<ui5-popover class="ui5-shellbar-menu-popover"
2+
placement-type="Bottom"
3+
@ui5-beforeOpen={{_menuPopoverBeforeOpen}}
4+
@ui5-afterClose={{_menuPopoverAfterClose}}
5+
>
26
<ui5-list separators="None" mode="SingleSelect" @ui5-itemPress={{_menuItemPress}}>
37
{{#each _menuPopoverItems}}
48
{{ this }}
59
{{/each}}
610
</ui5-list>
711
</ui5-popover>
812

9-
<ui5-popover class="ui5-shellbar-overflow-popover" placement-type="Bottom" horizontal-align="{{popoverHorizontalAlign}}" no-arrow>
13+
<ui5-popover class="ui5-shellbar-overflow-popover"
14+
placement-type="Bottom"
15+
horizontal-align="{{popoverHorizontalAlign}}"
16+
no-arrow
17+
@ui5-beforeOpen={{_overflowPopoverBeforeOpen}}
18+
@ui5-afterClose={{_overflowPopoverAfterClose}}
19+
>
1020
<ui5-list separators="None" @ui5-itemPress="{{_actionList.itemPress}}">
1121
{{#each _hiddenIcons}}
1222
<ui5-li

packages/fiori/src/i18n/messagebundle.properties

+25-1
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,28 @@ UPLOADCOLLECTION_NO_DATA_DESCRIPTION=Drop files to upload, or use the Upload but
3232
UPLOADCOLLECTION_DRAG_FILE_INDICATOR=Drag files here
3333

3434
#XMSG: Message text indicating where to drop file and upload
35-
UPLOADCOLLECTION_DROP_FILE_INDICATOR=Drop files to upload
35+
UPLOADCOLLECTION_DROP_FILE_INDICATOR=Drop files to upload
36+
37+
#XACT: ARIA announcement for the ShellBar container
38+
SHELLBAR_LABEL = Shell Bar
39+
40+
#XACT: ARIA announcement for the logo button
41+
SHELLBAR_LOGO = Logo
42+
43+
#XACT: ARIA announcement for the CoPilot button
44+
SHELLBAR_COPILOT = CoPilot
45+
46+
#XACT: ARIA announcement for the notifications button
47+
SHELLBAR_NOTIFICATIONS = {0} Notifications
48+
49+
#XACT: ARIA announcement for the profile button
50+
SHELLBAR_PROFILE = Profile
51+
52+
#XACT: ARIA announcement for the products button
53+
SHELLBAR_PRODUCTS = Products
54+
55+
#XACT: ARIA announcement for the search button
56+
SHELLBAR_SEARCH = Search
57+
58+
#XACT: ARIA announcement for the more button
59+
SHELLBAR_OVERFLOW = More

0 commit comments

Comments
 (0)