From 963baefeb602f2b5a1c3e87ea371ce89e557d33d Mon Sep 17 00:00:00 2001 From: Nikola Anachkov Date: Fri, 8 May 2026 16:03:55 +0300 Subject: [PATCH 1/3] fix(ui5-list): suppress F2 aria description when no interactive items present --- packages/main/cypress/specs/List.cy.tsx | 85 +++++++++++++++++++++++++ packages/main/src/List.ts | 12 +++- 2 files changed, 96 insertions(+), 1 deletion(-) diff --git a/packages/main/cypress/specs/List.cy.tsx b/packages/main/cypress/specs/List.cy.tsx index 0e3acd85315de..0aff30c3dc0f3 100644 --- a/packages/main/cypress/specs/List.cy.tsx +++ b/packages/main/cypress/specs/List.cy.tsx @@ -457,6 +457,91 @@ describe("List - Accessibility", () => { }); }); }); + + it("does not announce F2 instruction for selectionMode None with standard items", () => { + cy.mount( + + Item 1 + Item 2 + + ); + + cy.get("[ui5-list]") + .shadow() + .find(".ui5-list-ul") + .invoke("attr", "aria-description") + .then((ariaDesc) => { + cy.get("[ui5-list]") + .should(($list) => { + const defaultText = $list.prop("defaultAriaDescriptionText") as string; + expect(ariaDesc ?? "").to.not.include(defaultText); + }); + }); + }); + + it("announces F2 instruction for selectionMode Delete", () => { + cy.mount( + + Item 1 + Item 2 + + ); + + cy.get("[ui5-list]") + .shadow() + .find(".ui5-list-ul") + .invoke("attr", "aria-description") + .then((ariaDesc) => { + cy.get("[ui5-list]") + .should(($list) => { + const defaultText = $list.prop("defaultAriaDescriptionText") as string; + expect(ariaDesc).to.include(defaultText); + }); + }); + }); + + it("announces F2 instruction for selectionMode None with type Detail items", () => { + cy.mount( + + Item 1 + Item 2 + + ); + + cy.get("[ui5-list]") + .shadow() + .find(".ui5-list-ul") + .invoke("attr", "aria-description") + .then((ariaDesc) => { + cy.get("[ui5-list]") + .should(($list) => { + const defaultText = $list.prop("defaultAriaDescriptionText") as string; + expect(ariaDesc).to.include(defaultText); + }); + }); + }); + + it("announces F2 instruction for selectionMode None with custom list items", () => { + cy.mount( + + + + + + ); + + cy.get("[ui5-list]") + .shadow() + .find(".ui5-list-ul") + .invoke("attr", "aria-description") + .then((ariaDesc) => { + cy.get("[ui5-list]") + .should(($list) => { + const defaultText = $list.prop("defaultAriaDescriptionText") as string; + expect(ariaDesc).to.include(defaultText); + }); + }); + }); }); describe("List - Wrapping Behavior", () => { diff --git a/packages/main/src/List.ts b/packages/main/src/List.ts index 4ce0a29f3332f..9216409e8a4e5 100644 --- a/packages/main/src/List.ts +++ b/packages/main/src/List.ts @@ -742,7 +742,7 @@ class List extends UI5Element { get ariaDescriptionText() { const parts = []; - if (this.accessibleRole === ListAccessibleRole.List) { + if (this.accessibleRole === ListAccessibleRole.List && this._hasInteractiveItems) { parts.push(this.defaultAriaDescriptionText); } const externalDescription = this._associatedDescriptionRefTexts || getEffectiveAriaDescriptionText(this); @@ -763,6 +763,16 @@ class List extends UI5Element { return List.i18nBundle.getText(LIST_ROLE_DESCRIPTION); } + get _hasInteractiveItems() { + if (this.selectionMode === ListSelectionMode.Delete) { + return true; + } + + return this.getItems().some(item => { + return item.getAttribute("type") === "Detail" || item.hasAttribute("ui5-li-custom"); + }); + } + get growingButtonAriaLabel() { return this.accessibilityAttributes.growingButton?.name; } From 7b3a7bfbbc9ccc935532c4dcbb8d1df56b690d01 Mon Sep 17 00:00:00 2001 From: Nikola Anachkov Date: Fri, 8 May 2026 18:29:46 +0300 Subject: [PATCH 2/3] chore: fix failing tests --- packages/main/cypress/specs/List.cy.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/main/cypress/specs/List.cy.tsx b/packages/main/cypress/specs/List.cy.tsx index 0aff30c3dc0f3..4a3df3d1ed45b 100644 --- a/packages/main/cypress/specs/List.cy.tsx +++ b/packages/main/cypress/specs/List.cy.tsx @@ -378,7 +378,7 @@ describe("List - Accessibility", () => { it("has default aria-description for accessibleRole List when no accessibleDescription is set", () => { cy.mount( - + Item 1 Item 2 @@ -413,7 +413,7 @@ describe("List - Accessibility", () => { const customDescription = "Custom list description"; cy.mount( - + Item 1 Item 2 From 7429cec365c84f28ec6e9bbe05cb7e4028673447 Mon Sep 17 00:00:00 2001 From: Nikola Anachkov Date: Tue, 19 May 2026 14:14:46 +0300 Subject: [PATCH 3/3] fix: address comments --- packages/main/src/List.ts | 3 ++- packages/main/src/ListItemCustom.ts | 8 ++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/main/src/List.ts b/packages/main/src/List.ts index 9216409e8a4e5..1936c20951a8f 100644 --- a/packages/main/src/List.ts +++ b/packages/main/src/List.ts @@ -75,6 +75,7 @@ import type CheckBox from "./CheckBox.js"; import type RadioButton from "./RadioButton.js"; import { isInstanceOfListItemGroup } from "./ListItemGroup.js"; import type ListItemGroup from "./ListItemGroup.js"; +import { isInstanceOfListItemCustom } from "./ListItemCustom.js"; const INFINITE_SCROLL_DEBOUNCE_RATE = 250; // ms @@ -769,7 +770,7 @@ class List extends UI5Element { } return this.getItems().some(item => { - return item.getAttribute("type") === "Detail" || item.hasAttribute("ui5-li-custom"); + return item.getAttribute("type") === "Detail" || isInstanceOfListItemCustom(item); }); } diff --git a/packages/main/src/ListItemCustom.ts b/packages/main/src/ListItemCustom.ts index 2cfae94b0c5e5..d8347ca09b935 100644 --- a/packages/main/src/ListItemCustom.ts +++ b/packages/main/src/ListItemCustom.ts @@ -7,6 +7,7 @@ import type { ClassMap, AccessibilityInfo } from "@ui5/webcomponents-base/dist/t import customElement from "@ui5/webcomponents-base/dist/decorators/customElement.js"; import property from "@ui5/webcomponents-base/dist/decorators/property.js"; import i18n from "@ui5/webcomponents-base/dist/decorators/i18n.js"; +import createInstanceChecker from "@ui5/webcomponents-base/dist/util/createInstanceChecker.js"; import ListItem from "./ListItem.js"; import ListItemCustomTemplate from "./ListItemCustomTemplate.js"; import { getCustomAnnouncement, applyCustomAnnouncement } from "./CustomAnnouncement.js"; @@ -44,6 +45,11 @@ import ListItemCustomCss from "./generated/themes/ListItemCustom.css.js"; class ListItemCustom extends ListItem { @i18n("@ui5/webcomponents") static i18nBundle: I18nBundle; + + get isCustomListItem() { + return true; + } + /** * Defines whether the item is movable. * @default false @@ -193,3 +199,5 @@ class ListItemCustom extends ListItem { ListItemCustom.define(); export default ListItemCustom; + +export const isInstanceOfListItemCustom = createInstanceChecker("isCustomListItem");