Skip to content

Commit f6a0504

Browse files
authored
feat(ui5-button, ui5-ai-button): extend accessibility
Extend accessibility for ui5-button and ui5-ai-button: 1.ui5-button: - ariaLabel property added for aria-label attribute - ariaKeyShortcuts property added for aria-keyshortcuts attribute 2. ui5-ai-button: - title property added for aria-label attribute - ariaKeyShortcuts property added for aria-keyshortcuts attribute
1 parent ef09e4f commit f6a0504

File tree

7 files changed

+82
-16
lines changed

7 files changed

+82
-16
lines changed

packages/ai/cypress/specs/Button.cy.tsx

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,28 @@ describe("Accessibility", () => {
5959
.ui5AIButtonCheckAttributeInArrowButton("aria-haspopup", "menu");
6060
});
6161

62+
it("should set correct aria-keyshortcuts to SplitButton root element", () => {
63+
cy.mount(
64+
<Button accessibilityAttributes={{ root: { ariaKeyShortcuts: "Alt+G" } }}>
65+
<ButtonState name="generate" text="Generate" icon="ai">Click me</ButtonState>
66+
</Button>
67+
);
68+
69+
cy.get("[ui5-ai-button]")
70+
.ui5AIButtonCheckAttributeSplitButtonRoot("aria-keyshortcuts", "Alt+G");
71+
});
72+
73+
it("should set correct aria-label to SplitButton root element", () => {
74+
cy.mount(
75+
<Button id="aiButton" accessibilityAttributes={{ root: { title: "Some title" } }}>
76+
<ButtonState name="generate" text="Generate" icon="ai" showArrowButton={false}>Click me</ButtonState>
77+
</Button>
78+
);
79+
80+
cy.get("[ui5-ai-button]")
81+
.ui5AIButtonCheckAttributeSplitButtonRoot("aria-label", "Some title");
82+
});
83+
6284
it("should set correct aria attributes with default values when not provided", () => {
6385
cy.mount(
6486
<Button>

packages/ai/src/Button.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import { i18n } from "@ui5/webcomponents-base/dist/decorators.js";
2424
import type I18nBundle from "@ui5/webcomponents-base/dist/i18nBundle.js";
2525
import type { AccessibilityAttributes } from "@ui5/webcomponents-base/dist/types.js";
2626

27-
type AIButtonRootAccessibilityAttributes = Pick<AccessibilityAttributes, "hasPopup" | "roleDescription" | "title">;
27+
type AIButtonRootAccessibilityAttributes = Pick<AccessibilityAttributes, "hasPopup" | "roleDescription" | "title" | "ariaKeyShortcuts">;
2828
type AIButtonArrowButtonAccessibilityAttributes = Pick<AccessibilityAttributes, "hasPopup" | "expanded" | "title">;
2929
type AIButtonAccessibilityAttributes = { root?: AIButtonRootAccessibilityAttributes, arrowButton?: AIButtonArrowButtonAccessibilityAttributes}
3030

@@ -142,6 +142,9 @@ class Button extends UI5Element {
142142
* Accepts string values: `"dialog"`, `"grid"`, `"listbox"`, `"menu"`, or `"tree"`.
143143
* - **roleDescription**: Defines a human-readable description for the button's role.
144144
* Accepts any string value.
145+
* - **title**: Specifies a tooltip or description for screen readers.
146+
* Accepts any string value.
147+
* - **ariaKeyShortcuts**: Defines keyboard shortcuts that activate or focus the button.
145148
*
146149
* - **arrowButton**: Accessibility attributes that will be applied to the arrow (split) button element.
147150
* - **hasPopup**: Indicates the type of popup triggered by the arrow button.
@@ -357,6 +360,7 @@ class Button extends UI5Element {
357360
hasPopup: this.accessibilityAttributes?.root?.hasPopup || "false",
358361
roleDescription: this.accessibilityAttributes?.root?.roleDescription,
359362
title: this.accessibilityAttributes?.root?.title || title,
363+
ariaKeyShortcuts: this.accessibilityAttributes?.root?.ariaKeyShortcuts,
360364
},
361365
arrowButton: {
362366
hasPopup: this.accessibilityAttributes?.arrowButton?.hasPopup,

packages/main/cypress/specs/Button.cy.tsx

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,7 @@ describe("Accessibility", () => {
343343
.shadow()
344344
.find("button")
345345
.as("button");
346-
346+
347347
cy.get("@button")
348348
.should("have.attr", "aria-label", "Action Emphasized");
349349
});
@@ -488,6 +488,31 @@ describe("Accessibility", () => {
488488
.should("have.attr", "aria-controls", "registration-dialog");
489489
});
490490

491+
it("aria-label and aria-keyshortcuts are properly applied on the button tag", () => {
492+
cy.mount(<Button accessibilityAttributes={{ariaKeyShortcuts: "Alt+G", ariaLabel: "Some text"}}>Show Registration Dialog</Button>);
493+
494+
cy.get("[ui5-button]")
495+
.as("button");
496+
497+
cy.get<Button>("@button")
498+
.then($el => {
499+
$el.get(0).accessibilityAttributes = {
500+
ariaKeyShortcuts: "Alt+G",
501+
ariaLabel: "Some text",
502+
};
503+
});
504+
505+
cy.get("@button")
506+
.shadow()
507+
.find("button")
508+
.should("have.attr", "aria-label", "Some text");
509+
510+
cy.get("@button")
511+
.shadow()
512+
.find("button")
513+
.should("have.attr", "aria-keyshortcuts", "Alt+G");
514+
});
515+
491516
it("aria-busy is properly applied on the button with busy indicator", () => {
492517
cy.mount(<Button loading></Button>);
493518

packages/main/src/Button.ts

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ interface IButton extends HTMLElement, ITabbable {
5555
let isGlobalHandlerAttached = false;
5656
let activeButton: Button | null = null;
5757

58-
type ButtonAccessibilityAttributes = Pick<AccessibilityAttributes, "expanded" | "hasPopup" | "controls">;
58+
type ButtonAccessibilityAttributes = Pick<AccessibilityAttributes, "expanded" | "hasPopup" | "controls" | "ariaKeyShortcuts" | "ariaLabel">;
5959

6060
type ButtonClickEventDetail = {
6161
originalEvent: MouseEvent,
@@ -235,6 +235,11 @@ class Button extends UI5Element implements IButton {
235235
* - **hasPopup**: Indicates the availability and type of interactive popup element, such as menu or dialog, that can be triggered by the button.
236236
* Accepts the following string values: `dialog`, `grid`, `listbox`, `menu` or `tree`.
237237
*
238+
* - **ariaLabel**: Defines the accessible ARIA name of the component.
239+
* Accepts any string value.
240+
*
241+
* - **ariaKeyShortcuts**: Defines keyboard shortcuts that activate or give focus to the button.
242+
*
238243
* - **controls**: Identifies the element (or elements) whose contents or presence are controlled by the button element.
239244
* Accepts a lowercase string value.
240245
*
@@ -573,10 +578,6 @@ class Button extends UI5Element implements IButton {
573578
this.active = active;
574579
}
575580

576-
get _hasPopup() {
577-
return this.accessibilityAttributes.hasPopup;
578-
}
579-
580581
get hasButtonType() {
581582
return this.design !== ButtonDesign.Default && this.design !== ButtonDesign.Transparent;
582583
}
@@ -638,6 +639,16 @@ class Button extends UI5Element implements IButton {
638639
return this.accessibleDescription === "" ? undefined : this.accessibleDescription;
639640
}
640641

642+
get _computedAccessibilityAttributes(): ButtonAccessibilityAttributes {
643+
return {
644+
expanded: this.accessibilityAttributes.expanded,
645+
hasPopup: this.accessibilityAttributes.hasPopup,
646+
controls: this.accessibilityAttributes.controls,
647+
ariaKeyShortcuts: this.accessibilityAttributes.ariaKeyShortcuts,
648+
ariaLabel: this.accessibilityAttributes.ariaLabel || this.ariaLabelText,
649+
};
650+
}
651+
641652
get effectiveBadgeDescriptionText() {
642653
if (!this.shouldRenderBadge) {
643654
return "";

packages/main/src/ButtonTemplate.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,11 @@ export default function ButtonTemplate(this: Button, injectedProps?: {
3434
onTouchStart={this._ontouchstart}
3535
onTouchEnd={this._ontouchend}
3636
tabindex={this.tabIndexValue}
37-
aria-expanded={this.accessibilityAttributes.expanded}
38-
aria-controls={this.accessibilityAttributes.controls}
39-
aria-haspopup={this._hasPopup}
40-
aria-label={this.ariaLabelText}
37+
aria-expanded={this._computedAccessibilityAttributes?.expanded}
38+
aria-controls={this._computedAccessibilityAttributes?.controls}
39+
aria-haspopup={this._computedAccessibilityAttributes?.hasPopup}
40+
aria-label={this._computedAccessibilityAttributes?.ariaLabel}
41+
aria-keyshortcuts={this._computedAccessibilityAttributes?.ariaKeyShortcuts}
4142
aria-description={this.ariaDescriptionText}
4243
aria-busy={this.loading ? "true" : undefined}
4344
title={this.buttonTitle}

packages/main/src/SplitButton.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ import SplitButtonTemplate from "./SplitButtonTemplate.js";
3636
// Styles
3737
import SplitButtonCss from "./generated/themes/SplitButton.css.js";
3838

39-
type SplitButtonRootAccAttributes = Pick<AccessibilityAttributes, "hasPopup" | "roleDescription" | "title">;
39+
type SplitButtonRootAccAttributes = Pick<AccessibilityAttributes, "hasPopup" | "roleDescription" | "title" | "ariaKeyShortcuts">;
4040
type SplitButtonArrowButtonAccAtributes = Pick<AccessibilityAttributes, "hasPopup" | "expanded" | "title">;
4141
type SplitButtonAccessibilityAttributes = {root?: SplitButtonRootAccAttributes, arrowButton?: SplitButtonArrowButtonAccAtributes}
4242

@@ -226,6 +226,7 @@ class SplitButton extends UI5Element {
226226
* Accepts any string value.
227227
* - **title**: Specifies a tooltip or description for screen readers.
228228
* Accepts any string value.
229+
* - **ariaKeyShortcuts**: Defines keyboard shortcuts that activate or give focus to the button.
229230
*
230231
* - **arrowButton**: Attributes applied specifically to the arrow (split) button.
231232
* - **hasPopup**: Indicates the presence and type of popup triggered by the arrow button.
@@ -443,7 +444,7 @@ class SplitButton extends UI5Element {
443444
return this.activeArrowButton || this._activeArrowButton;
444445
}
445446

446-
get textButtonAccText() {
447+
get buttonTextContent() {
447448
return this.textContent;
448449
}
449450

@@ -465,6 +466,7 @@ class SplitButton extends UI5Element {
465466
hasPopup: this.accessibilityAttributes?.root?.hasPopup,
466467
roleDescription: this.accessibilityAttributes?.root?.roleDescription || (this._hideArrowButton ? undefined : SplitButton.i18nBundle.getText(SPLIT_BUTTON_DESCRIPTION)),
467468
title: this.accessibilityAttributes?.root?.title,
469+
ariaKeyShortcuts: this.accessibilityAttributes?.root?.ariaKeyShortcuts,
468470
},
469471
arrowButton: {
470472
hasPopup: this.accessibilityAttributes?.arrowButton?.hasPopup || "menu" as AriaHasPopup,

packages/main/src/SplitButtonTemplate.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,11 @@ export default function SplitButtonTemplate(this: SplitButton) {
88
role={this._hideArrowButton ? "button" : "group"}
99
class="ui5-split-button-root"
1010
tabindex={this._tabIndex}
11-
aria-labelledby={this._hideArrowButton ? undefined : `${this._id}-invisibleTextDefault ${this._id}-invisibleText`}
11+
aria-labelledby={`${this._id}-invisibleTextDefault ${this._id}-invisibleText`}
1212
aria-haspopup={this._computedAccessibilityAttributes?.root?.hasPopup}
1313
aria-roledescription={this._computedAccessibilityAttributes?.root?.roleDescription}
14-
aria-label={this._hideArrowButton ? this._computedAccessibilityAttributes?.root?.title : undefined}
14+
aria-label={this._computedAccessibilityAttributes?.root?.title}
15+
aria-keyshortcuts={this._computedAccessibilityAttributes?.root?.ariaKeyShortcuts}
1516
onFocusOut={this._onFocusOut}
1617
onKeyDown={this._onKeyDown}
1718
onKeyUp={this._onKeyUp}
@@ -56,7 +57,7 @@ export default function SplitButtonTemplate(this: SplitButton) {
5657
>
5758
</Button>
5859
<span id={`${this._id}-invisibleText`} class="ui5-hidden-text">{this.accInfo.keyboardHint} {this.accessibleName}</span>
59-
<span id={`${this._id}-invisibleTextDefault`} class="ui5-hidden-text">{this._computedAccessibilityAttributes?.root?.title || this.textButtonAccText}</span>
60+
<span id={`${this._id}-invisibleTextDefault`} class="ui5-hidden-text">{this.buttonTextContent}</span>
6061
</>
6162
)}
6263
</div>

0 commit comments

Comments
 (0)