Skip to content

Commit 8cb7c48

Browse files
authored
feat(ui5-panel): support aria-label and aria-labelledby (#1910)
1 parent 7da7a54 commit 8cb7c48

File tree

4 files changed

+77
-10
lines changed

4 files changed

+77
-10
lines changed

packages/main/src/Panel.hbs

+5
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
role="{{accInfo.role}}"
1414
aria-expanded="{{accInfo.ariaExpanded}}"
1515
aria-controls="{{accInfo.ariaControls}}"
16+
aria-label="{{accInfo.ariaLabel}}"
17+
aria-labelledby="{{accInfo.ariaLabelledby}}"
1618
>
1719
{{#unless fixed}}
1820
<div class="ui5-panel-header-button-root">
@@ -23,6 +25,8 @@
2325
?non-focusable="{{nonFocusableButton}}"
2426
@click="{{_toggleButtonClick}}"
2527
._buttonAccInfo="{{accInfo.button}}"
28+
aria-label="{{accInfo.ariaLabelButton}}"
29+
aria-labelledby="{{accInfo.ariaLabelledbyButton}}"
2630
></ui5-button>
2731
</div>
2832
{{/unless}}
@@ -31,6 +35,7 @@
3135
<slot name="header"></slot>
3236
{{else}}
3337
<div
38+
id="{{_id}}-header-title"
3439
role="heading"
3540
aria-level="{{headerAriaLevel}}"
3641
class="ui5-panel-header-title"

packages/main/src/Panel.js

+45-8
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import slideDown from "@ui5/webcomponents-base/dist/animations/slideDown.js";
44
import slideUp from "@ui5/webcomponents-base/dist/animations/slideUp.js";
55
import { isSpace, isEnter } from "@ui5/webcomponents-base/dist/Keys.js";
66
import AnimationMode from "@ui5/webcomponents-base/dist/types/AnimationMode.js";
7+
import getEffectiveAriaLabelText from "@ui5/webcomponents-base/dist/util/getEffectiveAriaLabelText.js";
78
import { getAnimationMode } from "@ui5/webcomponents-base/dist/config/AnimationMode.js";
89
import { fetchI18nBundle, getI18nBundle } from "@ui5/webcomponents-base/dist/i18nBundle.js";
910
import "@ui5/webcomponents-icons/dist/icons/navigation-right-arrow.js";
@@ -117,7 +118,27 @@ const metadata = {
117118
type: TitleLevel,
118119
defaultValue: TitleLevel.H2,
119120
},
120-
121+
/**
122+
* @type {String}
123+
* @defaultvalue ""
124+
* @private
125+
* @since 1.0.0-rc.8
126+
*/
127+
ariaLabel: {
128+
type: String,
129+
},
130+
/**
131+
* Receives id(or many ids) of the elements that label the panel
132+
*
133+
* @type {String}
134+
* @defaultvalue ""
135+
* @private
136+
* @since 1.0.0-rc.8
137+
*/
138+
ariaLabelledby: {
139+
type: String,
140+
defaultValue: "",
141+
},
121142
/**
122143
* @private
123144
*/
@@ -342,10 +363,6 @@ class Panel extends UI5Element {
342363
return !this.collapsed;
343364
}
344365

345-
get ariaLabelledBy() {
346-
return this.header.length ? "" : `${this._id}-header`;
347-
}
348-
349366
get accRole() {
350367
return this.accessibleRole.toLowerCase();
351368
}
@@ -357,12 +374,28 @@ class Panel extends UI5Element {
357374
"ariaControls": this._hasHeader ? `${this._id}-content` : undefined,
358375
"title": this.toggleButtonTitle,
359376
},
360-
"ariaExpanded": !this._hasHeader ? this.expanded : undefined,
361-
"ariaControls": !this._hasHeader ? `${this._id}-content` : undefined,
362-
"role": !this._hasHeader ? "button" : undefined,
377+
"ariaExpanded": this.nonFixedInternalHeader ? this.expanded : undefined,
378+
"ariaControls": this.nonFixedInternalHeader ? `${this._id}-content` : undefined,
379+
"ariaLabelledby": this.nonFocusableButton ? this.ariaLabelledbyReference : undefined,
380+
"ariaLabel": this.nonFocusableButton ? this.ariaLabelTxt : undefined,
381+
"ariaLabelledbyButton": this.nonFocusableButton ? undefined : this.ariaLabelledbyReference,
382+
"ariaLabelButton": this.nonFocusableButton ? undefined : this.ariaLabelTxt,
383+
"role": this.nonFixedInternalHeader ? "button" : undefined,
363384
};
364385
}
365386

387+
get ariaLabelledbyReference() {
388+
if (this.ariaLabelledby || this.ariaLabel) {
389+
return undefined;
390+
}
391+
392+
return (this.nonFocusableButton && this.headerText) ? `${this._id}-header-title` : undefined;
393+
}
394+
395+
get ariaLabelTxt() {
396+
return getEffectiveAriaLabelText(this);
397+
}
398+
366399
get headerAriaLevel() {
367400
return this.headerLevel.slice(1);
368401
}
@@ -371,6 +404,10 @@ class Panel extends UI5Element {
371404
return (this.header.length || this.fixed) ? "-1" : "0";
372405
}
373406

407+
get nonFixedInternalHeader() {
408+
return !this._hasHeader && !this.fixed;
409+
}
410+
374411
get nonFocusableButton() {
375412
return !this.header.length;
376413
}

packages/main/test/pages/Panel.html

+2-2
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,11 @@
6161
}
6262
</style>
6363

64-
<ui5-panel id="p1" collapsed accessible-role="Complementary">
64+
<ui5-panel id="p1" collapsed accessible-role="Complementary" aria-labelledby="p1-title">
6565

6666
<!-- Panel header -->
6767
<div slot="header" class="header">
68-
<ui5-title>Expandable but not expanded</ui5-title>
68+
<ui5-title id="p1-title">Expandable but not expanded</ui5-title>
6969
<ui5-button id="b1">Add child </ui5-button>
7070
<ui5-label id="l1">No new children added yet.</ui5-label>
7171
<ui5-button design="Reject" icon="cancel">Cancel</ui5-button>

packages/main/test/specs/Panel.spec.js

+25
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,31 @@ describe("Panel general interaction", () => {
114114
assert.strictEqual(title.getAttribute("aria-level"), "3", "title aria-level is set to 3 correctly");
115115
});
116116

117+
it("tests aria-label and aria-labelledby attributes", () => {
118+
const panelWithNativeHeader = $("#panel-expandable");
119+
const nativeHeader = panelWithNativeHeader.shadow$(".ui5-panel-header");
120+
const panelWithNativeHeaderId = panelWithNativeHeader.getProperty("_id");
121+
122+
assert.strictEqual(nativeHeader.getAttribute("aria-label"), null, "aria-label is not present");
123+
assert.strictEqual(nativeHeader.getAttribute("aria-labelledby"),
124+
`${panelWithNativeHeaderId}-header-title`, "aria-labelledby is correct");
125+
126+
const panelWithCustomHeader = $("#p1");
127+
const headerButton = panelWithCustomHeader.shadow$(".ui5-panel-header-button");
128+
const expectedText = "Expandable but not expanded";
129+
130+
assert.strictEqual(headerButton.getAttribute("aria-label"), expectedText,
131+
"aria-labelledby is propagated correctly to the expand/collapse button");
132+
});
133+
134+
it("tests whether aria attributes are set correctly with fixed header", () => {
135+
const header = browser.$("#panel-fixed").shadow$(".ui5-panel-header");
136+
137+
assert.ok(!header.getAttribute("aria-expanded"), "aria-expanded shouldn't be set on the fixed header");
138+
assert.ok(!header.getAttribute("aria-controls"), "aria-controls shouldn't be set on the fixed header");
139+
assert.ok(!header.getAttribute("role"), "role shouldn't be set on the fixed header");
140+
});
141+
117142
it("tests whether aria attributes are set correctly in case of custom header", () => {
118143
const button = browser.$("#panel2").shadow$(".ui5-panel-header-button").shadow$(".ui5-button-root");
119144
const header = browser.$("#panel2").shadow$(".ui5-panel-header");

0 commit comments

Comments
 (0)