Skip to content

Commit 1357c16

Browse files
feat(ui5-icon): accessibility implementation (#709)
1 parent 9c6df48 commit 1357c16

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+685
-54
lines changed

packages/base/src/ResourceBundle.js

+4
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,10 @@ class ResourceBundleWrapper {
6767
}
6868

6969
getText(textObj, ...params) {
70+
if (!this._resourceBundle.hasText(textObj.key)) {
71+
return textObj.defaultText;
72+
}
73+
7074
return this._resourceBundle.getText(textObj.key, ...params);
7175
}
7276
}

packages/base/src/SVGIconRegistry.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
const registry = new Map();
22

3-
const registerIcon = (name, d) => {
4-
registry.set(name, { name, d });
3+
const registerIcon = (name, d, accData) => {
4+
registry.set(name, { d, accData });
55
};
66

77
const getIconData = name => {
+3-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { registerIcon } from "../SVGIconRegistry.js";
2+
import { ICON_MESSAGE_ERROR } from "../../../main/src/i18n/defaults.js";
23

34
const name = "sap-icon://message-error";
45
const d = "M257 334q15 0 26.5 10.5T295 372q0 15-11.5 27T257 411q-17 0-28-12t-11-27q0-17 11-27.5t28-10.5zm0-231q15 0 26.5 10.5T295 141l-18 136q-3 14-7.5 18t-12.5 4-13-3.5-7-18.5l-19-136q0-17 11-27.5t28-10.5zM256 0q53 0 100 20t81.5 55 54.5 81.5 20 99.5-20 100-54.5 81.5T356 492t-100 20-99.5-20T75 437.5 20 356 0 256t20-99.5T75 75t81.5-55T256 0zm0 480q46 0 87-17.5t71.5-48 48-71T480 256q0-46-17.5-87t-48-71.5-71.5-48T256 32t-87 17.5-71.5 48-48 71.5T32 256q0 47 17.5 87.5t48 71 71.5 48 87 17.5z";
6+
const acc = ICON_MESSAGE_ERROR;
57

6-
registerIcon(name, d);
8+
registerIcon(name, d, acc);
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { registerIcon } from "../SVGIconRegistry.js";
2+
import { ICON_MESSAGE_INFORMATION } from "../../../main/src/i18n/defaults.js";
23

34
const name = "sap-icon://message-information";
45
const d = "M448 32q13 0 22.5 9t9.5 22v384q0 14-9.5 23.5T448 480H64q-14 0-23-9.5T32 447V63q0-13 9-22t23-9h384zm0 31H64v384h384V63zM320 416H192v-33h33V255h-32v-31h95v159h32v33zm-64-225q-14 0-23-9t-9-22q0-14 9-23.5t23-9.5q13 0 22.5 9.5T288 160q0 13-9.5 22t-22.5 9z";
6+
const acc = ICON_MESSAGE_INFORMATION;
57

6-
registerIcon(name, d);
8+
registerIcon(name, d, acc);
+3-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { registerIcon } from "../SVGIconRegistry.js";
2+
import { ICON_MESSAGE_SUCCESS } from "../../../main/src/i18n/defaults.js";
23

34
const name = "sap-icon://message-success";
45
const d = "M448 32q13 0 22.5 9t9.5 23v384q0 13-9.5 22.5T448 480H64q-14 0-23-9.5T32 448V64q0-14 9-23t23-9h384zm0 32H64v384h384V64zM244 384l-113-95 33-47 74 56 103-170 41 42z";
6+
const acc = ICON_MESSAGE_SUCCESS;
57

6-
registerIcon(name, d);
8+
registerIcon(name, d, acc);

packages/base/src/icons/message-warning.js

+3-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/main/lib/i18n-transform/USED_KEYS.txt

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,5 @@ BUTTON_ARIA_TYPE_EMPHASIZED
99
LINK_SUBTLE
1010
LINK_EMPHASIZED
1111
SWITCH_ON
12-
SWITCH_OFF
12+
SWITCH_OFF
13+
TOKEN_ARIA_DELETABLE

packages/main/src/Button.hbs

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
<ui5-icon
1616
class="ui5-button-icon"
1717
src="{{icon}}"
18+
show-tooltip={{iconOnly}}
1819
></ui5-icon>
1920
{{/if}}
2021

packages/main/src/Icon.hbs

+5-1
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,13 @@
44
viewBox="0 0 512 512"
55
role="img"
66
preserveAspectRatio="xMidYMid meet"
7+
aria-label="{{accessibleNameText}}"
78
xmlns="http://www.w3.org/2000/svg"
89
>
9-
<g role="presentation" aria-hidden="true">
10+
{{#if hasIconTooltip}}
11+
<title id="{{_id}}-tooltip">{{accessibleNameText}}</title>
12+
{{/if}}
13+
<g role="presentation">
1014
<path transform="translate(0, 512) scale(1, -1)" d={{d}} />
1115
</g>
1216
</svg>

packages/main/src/Icon.js

+47
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import UI5Element from "@ui5/webcomponents-base/dist/UI5Element.js";
22
import litRender from "@ui5/webcomponents-base/dist/renderer/LitRenderer.js";
33
import { getRTL } from "@ui5/webcomponents-base/dist/config/RTL.js";
44
import { getIconData } from "@ui5/webcomponents-base/dist/SVGIconRegistry.js";
5+
import { fetchResourceBundle, getResourceBundle } from "@ui5/webcomponents-base/dist/ResourceBundle.js";
56
import IconTemplate from "./generated/templates/IconTemplate.lit.js";
67

78
// Styles
@@ -30,6 +31,31 @@ const metadata = {
3031
src: {
3132
type: String,
3233
},
34+
35+
/**
36+
* Defines the text alternative of the <code>ui5-icon</code>.
37+
* If not provided a default text alternative will be set, if present.
38+
* <br><br>
39+
* <b>Note:</b> Every icon should have a text alternative in order to
40+
* calculate its accessible name.
41+
*
42+
* @type {string}
43+
* @public
44+
*/
45+
accessibleName: {
46+
type: String,
47+
},
48+
49+
/**
50+
* Defines whether the <code>ui5-icon</code> should have a tooltip.
51+
*
52+
* @type {boolean}
53+
* @defaultvalue false
54+
* @public
55+
*/
56+
showTooltip: {
57+
type: Boolean,
58+
},
3359
},
3460
events: {
3561
},
@@ -63,6 +89,11 @@ const metadata = {
6389
* @public
6490
*/
6591
class Icon extends UI5Element {
92+
constructor() {
93+
super();
94+
this.resourceBundle = getResourceBundle("@ui5/webcomponents");
95+
}
96+
6697
static get metadata() {
6798
return metadata;
6899
}
@@ -79,6 +110,12 @@ class Icon extends UI5Element {
79110
return iconCss;
80111
}
81112

113+
static async define(...params) {
114+
await fetchResourceBundle("@ui5/webcomponents");
115+
116+
super.define(...params);
117+
}
118+
82119
_normalizeIconURI(iconURI) {
83120
return this._hasIconPrefix(iconURI) ? iconURI : `sap-icon://${iconURI}`;
84121
}
@@ -98,6 +135,16 @@ class Icon extends UI5Element {
98135
return icon.d;
99136
}
100137

138+
get hasIconTooltip() {
139+
return this.showTooltip && this.accessibleNameText;
140+
}
141+
142+
get accessibleNameText() {
143+
const icon = getIconData(this._normalizeIconURI(this.src));
144+
145+
return this.accessibleName || (icon.accData && this.resourceBundle.getText(icon.accData));
146+
}
147+
101148
get dir() {
102149
return getRTL() ? "rtl" : "ltr";
103150
}

packages/main/src/MessageStrip.hbs

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
@click={{_closeClick}}
1919
>
2020
<ui5-icon
21-
class="ui5-messagestrip-close-icon"
21+
class="ui5-messagestrip-close-icon"
2222
src="sap-icon://decline"
2323
>
2424
</ui5-icon>

packages/main/src/Panel.hbs

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
<ui5-icon
2626
src="sap-icon://navigation-right-arrow"
2727
class="ui5-panel-header-button-icon"
28+
accessible-name="{{_icon.title}}"
2829
>
2930
</ui5-icon>
3031
</ui5-button>

packages/main/src/Token.hbs

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@
22
<span class="ui5-token--text"><slot></slot></span>
33

44
{{#unless readonly}}
5-
<ui5-icon @click="{{_delete}}" src="{{iconURI}}" class="ui5-token--icon"></ui5-icon>
5+
<ui5-icon @click="{{_delete}}" src="{{iconURI}}" accessible-name="{{tokenDeletableText}}" show-tooltip="true" class="ui5-token--icon"></ui5-icon>
66
{{/unless}}
77
</div>

packages/main/src/Token.js

+16-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import {
99
} from "@ui5/webcomponents-base/dist/events/PseudoEvents.js";
1010
import "@ui5/webcomponents-base/dist/icons/decline.js";
1111
import "@ui5/webcomponents-base/dist/icons/cancel.js";
12+
import { fetchResourceBundle, getResourceBundle } from "@ui5/webcomponents-base/dist/ResourceBundle.js";
13+
import { TOKEN_ARIA_DELETABLE } from "./i18n/defaults.js";
1214

1315
import Icon from "./Icon.js";
1416
import TokenTemplate from "./generated/templates/TokenTemplate.lit.js";
@@ -117,6 +119,12 @@ class Token extends UI5Element {
117119
return styles;
118120
}
119121

122+
constructor() {
123+
super();
124+
125+
this.resourceBundle = getResourceBundle("@ui5/webcomponents");
126+
}
127+
120128
_select() {
121129
this.fireEvent("select");
122130
}
@@ -143,12 +151,19 @@ class Token extends UI5Element {
143151
}
144152
}
145153

154+
get tokenDeletableText() {
155+
return this.resourceBundle.getText(TOKEN_ARIA_DELETABLE);
156+
}
157+
146158
get iconURI() {
147159
return getTheme() === "sap_fiori_3" ? "sap-icon://decline" : "sap-icon://sys-cancel";
148160
}
149161

150162
static async define(...params) {
151-
await Icon.define();
163+
await Promise.all([
164+
Icon.define(),
165+
fetchResourceBundle("@ui5/webcomponents"),
166+
]);
152167

153168
super.define(...params);
154169
}

0 commit comments

Comments
 (0)