From da4d221e572a88d14374fb05a229c85a3773eeff Mon Sep 17 00:00:00 2001 From: ilhan007 Date: Tue, 17 Feb 2026 15:39:14 +0200 Subject: [PATCH 1/2] fix(ui5-button): update aria-label when accessibleNameRef target changes Button now observes changes to elements referenced via accessibleNameRef and updates its aria-label accordingly. Previously, the aria-label would only update when the Button re-rendered for other reasons. The fix registers the Button with AccessibilityTextsHelper to observe mutations on referenced elements, matching the behavior of Input and other components. Fixes #13101 --- .../base/AccessibilityTextsHelper.cy.tsx | 39 +++++++++++++++++++ packages/main/src/Button.ts | 22 ++++++++++- 2 files changed, 59 insertions(+), 2 deletions(-) diff --git a/packages/main/cypress/specs/base/AccessibilityTextsHelper.cy.tsx b/packages/main/cypress/specs/base/AccessibilityTextsHelper.cy.tsx index 04dd22632446..bc7c98f40c8b 100644 --- a/packages/main/cypress/specs/base/AccessibilityTextsHelper.cy.tsx +++ b/packages/main/cypress/specs/base/AccessibilityTextsHelper.cy.tsx @@ -1,6 +1,7 @@ import Label from "../../../src/Label.js"; import Input from "../../../src/Input.js"; import List from "../../../src/List.js"; +import Button from "../../../src/Button.js"; describe("AccessibilityTextsHelper", () => { it("Label-for tests", () => { @@ -465,4 +466,42 @@ describe("AccessibilityTextsHelper", () => { cy.get("@list") .should("have.attr", "aria-description", "Desc3"); }); + + it("Button accessibleNameRef Tests", () => { + cy.mount( + <> + + + + + ); + + // assert - both buttons should have the same aria-label + cy.get("#btn1") + .shadow() + .find("button") + .should("have.attr", "aria-label", "Counter value is 0"); + + cy.get("#btn2") + .shadow() + .find("button") + .should("have.attr", "aria-label", "Counter value is 0"); + + // act - update text of referenced label (simulating counter increment) + cy.get("#lblCounter") + .then($el => { + $el.get(0).innerHTML = "Counter value is 3"; + }); + + // assert - both buttons should update their aria-label + cy.get("#btn1") + .shadow() + .find("button") + .should("have.attr", "aria-label", "Counter value is 3"); + + cy.get("#btn2") + .shadow() + .find("button") + .should("have.attr", "aria-label", "Counter value is 3"); + }); }); diff --git a/packages/main/src/Button.ts b/packages/main/src/Button.ts index 472add2425d4..7facab59e14c 100644 --- a/packages/main/src/Button.ts +++ b/packages/main/src/Button.ts @@ -13,7 +13,11 @@ import { isShift, isSpaceShift, } from "@ui5/webcomponents-base/dist/Keys.js"; -import { getEffectiveAriaLabelText } from "@ui5/webcomponents-base/dist/util/AccessibilityTextsHelper.js"; +import { + getEffectiveAriaLabelText, + registerUI5Element, + deregisterUI5Element, +} from "@ui5/webcomponents-base/dist/util/AccessibilityTextsHelper.js"; import type { AccessibilityAttributes, AriaRole } from "@ui5/webcomponents-base"; import type { ITabbable } from "@ui5/webcomponents-base/dist/delegate/ItemNavigation.js"; import type I18nBundle from "@ui5/webcomponents-base/dist/i18nBundle.js"; @@ -375,6 +379,12 @@ class Button extends UI5Element implements IButton { @property({ type: Boolean, noAttribute: true }) _isSpacePressed = false; + /** + * @private + */ + @property({ noAttribute: true }) + _accessibleNameText?: string; + /** * Defines the text of the component. * @@ -443,6 +453,12 @@ class Button extends UI5Element implements IButton { this.addEventListener("click", this._onclickBound); this._clickHandlerAttached = true; } + + registerUI5Element(this, this._updateAccessibleNameText.bind(this)); + } + + _updateAccessibleNameText() { + this._accessibleNameText = getEffectiveAriaLabelText(this); } onExitDOM() { @@ -454,6 +470,8 @@ class Button extends UI5Element implements IButton { if (activeButton === this) { activeButton = null; } + + deregisterUI5Element(this); } async onBeforeRendering() { @@ -655,7 +673,7 @@ class Button extends UI5Element implements IButton { } get ariaLabelText() { - const effectiveAriaLabelText = getEffectiveAriaLabelText(this) || ""; + const effectiveAriaLabelText = this._accessibleNameText || ""; const textContent = this.textContent || ""; const internalLabelText = this.effectiveBadgeDescriptionText || ""; From e39dbd40c102912ff1aa26829dfc8fee4e8f7fdb Mon Sep 17 00:00:00 2001 From: ilhan007 Date: Tue, 17 Feb 2026 18:08:49 +0200 Subject: [PATCH 2/2] fix(ui5-button): use accessibleName directly instead of caching Only cache accessibleNameRef texts (which require DOM lookups and mutation observation). Use accessibleName property directly in the getter, matching the pattern used by Input component. This fixes the issue where dynamically setting accessibleName (e.g., from Panel's useAccessibleNameForToggleButton) wasn't reflected because the cached value wasn't updated. --- packages/main/src/Button.ts | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/main/src/Button.ts b/packages/main/src/Button.ts index 7facab59e14c..277544a53b63 100644 --- a/packages/main/src/Button.ts +++ b/packages/main/src/Button.ts @@ -14,7 +14,7 @@ import { isSpaceShift, } from "@ui5/webcomponents-base/dist/Keys.js"; import { - getEffectiveAriaLabelText, + getAllAccessibleNameRefTexts, registerUI5Element, deregisterUI5Element, } from "@ui5/webcomponents-base/dist/util/AccessibilityTextsHelper.js"; @@ -380,10 +380,11 @@ class Button extends UI5Element implements IButton { _isSpacePressed = false; /** + * Constantly updated value of texts collected from the accessibleNameRef elements * @private */ @property({ noAttribute: true }) - _accessibleNameText?: string; + _accessibleNameRefTexts?: string; /** * Defines the text of the component. @@ -454,11 +455,11 @@ class Button extends UI5Element implements IButton { this._clickHandlerAttached = true; } - registerUI5Element(this, this._updateAccessibleNameText.bind(this)); + registerUI5Element(this, this._updateAccessibleNameRefTexts.bind(this)); } - _updateAccessibleNameText() { - this._accessibleNameText = getEffectiveAriaLabelText(this); + _updateAccessibleNameRefTexts() { + this._accessibleNameRefTexts = getAllAccessibleNameRefTexts(this); } onExitDOM() { @@ -673,7 +674,8 @@ class Button extends UI5Element implements IButton { } get ariaLabelText() { - const effectiveAriaLabelText = this._accessibleNameText || ""; + // Use accessibleNameRef texts (cached), then accessibleName (direct), then textContent as fallback + const effectiveAriaLabelText = this._accessibleNameRefTexts || this.accessibleName || ""; const textContent = this.textContent || ""; const internalLabelText = this.effectiveBadgeDescriptionText || "";