From d8200f0c2813937b1d7538ed642630e37eaa8c7f Mon Sep 17 00:00:00 2001 From: Nikola Anachkov Date: Tue, 28 Apr 2026 16:38:52 +0300 Subject: [PATCH 1/3] feat(ui5-list): add live region announcement for item selection (POC) Announces "Selected" or "Not Selected" via InvisibleMessage when a list item's selection state changes in any selectionMode other than "None". Covers Single, SingleStart, SingleEnd, SingleAuto, and Multiple modes. --- packages/main/cypress/specs/List.cy.tsx | 58 +++++++++++++++++++++++++ packages/main/src/List.ts | 10 +++++ 2 files changed, 68 insertions(+) diff --git a/packages/main/cypress/specs/List.cy.tsx b/packages/main/cypress/specs/List.cy.tsx index 0e3acd85315d..529cca25887b 100644 --- a/packages/main/cypress/specs/List.cy.tsx +++ b/packages/main/cypress/specs/List.cy.tsx @@ -457,6 +457,64 @@ describe("List - Accessibility", () => { }); }); }); + + it("announces 'Selected' when an item is selected in Single mode", () => { + cy.mount( + + Argentina + Bulgaria + + ); + + cy.get(".ui5-invisiblemessage-polite").as("liveRegion"); + + cy.get("#item1").realClick(); + cy.get("@liveRegion").should("contain.text", "Selected"); + }); + + it("announces 'Selected' and 'Not Selected' when items are toggled in Multiple mode", () => { + cy.mount( + + Argentina + Bulgaria + + ); + + cy.get(".ui5-invisiblemessage-polite").as("liveRegion"); + + cy.get("#item1").realClick(); + cy.get("@liveRegion").should("contain.text", "Selected"); + + cy.get("#item1").realClick(); + cy.get("@liveRegion").should("contain.text", "Not Selected"); + }); + + it("does not announce selection when selectionMode is None", () => { + cy.mount( + + Argentina + + ); + + cy.get(".ui5-invisiblemessage-polite").as("liveRegion"); + + cy.get("#item1").realClick(); + cy.get("@liveRegion").should("have.text", ""); + }); + + it("does not announce selection when selection-change event is prevented", () => { + cy.mount( + e.preventDefault()}> + Argentina + Bulgaria + + ); + + cy.get(".ui5-invisiblemessage-polite").as("liveRegion"); + + cy.get("#item1").realClick(); + cy.get("@liveRegion").should("have.text", ""); + }); }); describe("List - Wrapping Behavior", () => { diff --git a/packages/main/src/List.ts b/packages/main/src/List.ts index 4ce0a29f3332..893db6d51fc8 100644 --- a/packages/main/src/List.ts +++ b/packages/main/src/List.ts @@ -38,6 +38,8 @@ import { getAllAccessibleNameRefTexts, } from "@ui5/webcomponents-base/dist/util/AccessibilityTextsHelper.js"; import getNormalizedTarget from "@ui5/webcomponents-base/dist/util/getNormalizedTarget.js"; +import announce from "@ui5/webcomponents-base/dist/util/InvisibleMessage.js"; +import InvisibleMessageMode from "@ui5/webcomponents-base/dist/types/InvisibleMessageMode.js"; import type I18nBundle from "@ui5/webcomponents-base/dist/i18nBundle.js"; import debounce from "@ui5/webcomponents-base/dist/util/debounce.js"; import isElementInView from "@ui5/webcomponents-base/dist/util/isElementInView.js"; @@ -70,6 +72,8 @@ import { LOAD_MORE_TEXT, ARIA_LABEL_LIST_SELECTABLE, ARIA_LABEL_LIST_MULTISELECTABLE, ARIA_LABEL_LIST_DELETABLE, + LIST_ITEM_SELECTED, + LIST_ITEM_NOT_SELECTED, } from "./generated/i18n/i18n-defaults.js"; import type CheckBox from "./CheckBox.js"; import type RadioButton from "./RadioButton.js"; @@ -917,6 +921,12 @@ class List extends UI5Element { }); if (changePrevented) { this._revertSelection(previouslySelectedItems); + } else { + const item = e.detail.item; + const selectedText = item.selected + ? List.i18nBundle.getText(LIST_ITEM_SELECTED) + : List.i18nBundle.getText(LIST_ITEM_NOT_SELECTED); + announce(selectedText, InvisibleMessageMode.Polite); } } } From c644673c0f1858e123184c6956541d5dda03b486 Mon Sep 17 00:00:00 2001 From: Nikola Anachkov Date: Tue, 5 May 2026 11:58:29 +0300 Subject: [PATCH 2/3] feat(ui5-list): add live region announcement for item selection --- packages/main/cypress/specs/List.cy.tsx | 82 ++++++++++++++++++++----- packages/main/src/List.ts | 5 +- 2 files changed, 68 insertions(+), 19 deletions(-) diff --git a/packages/main/cypress/specs/List.cy.tsx b/packages/main/cypress/specs/List.cy.tsx index 529cca25887b..bbb95e9d0494 100644 --- a/packages/main/cypress/specs/List.cy.tsx +++ b/packages/main/cypress/specs/List.cy.tsx @@ -458,61 +458,111 @@ describe("List - Accessibility", () => { }); }); - it("announces 'Selected' when an item is selected in Single mode", () => { + it("announces 'Selected' when an item is selected in Single mode via mouse click", () => { cy.mount( - Argentina - Bulgaria + Argentina + Bulgaria + + ); + + cy.get(".ui5-invisiblemessage-polite").as("liveRegion"); + + cy.get("[ui5-list]").find("[ui5-li]").first().realClick(); + cy.get("@liveRegion").should("contain.text", "Selected"); + }); + + it("announces 'Selected' when an item is selected in Single mode via Space key", () => { + cy.mount( + + Argentina + Bulgaria ); cy.get(".ui5-invisiblemessage-polite").as("liveRegion"); - cy.get("#item1").realClick(); + cy.get("[ui5-list]").find("[ui5-li]").first().realClick(); + cy.get("@liveRegion").should("contain.text", "Selected"); + + cy.get("[ui5-list]").find("[ui5-li]").eq(1).click(); + cy.realPress("Space"); cy.get("@liveRegion").should("contain.text", "Selected"); }); - it("announces 'Selected' and 'Not Selected' when items are toggled in Multiple mode", () => { + it("announces 'Selected' and 'Not Selected' when items are toggled in Multiple mode via mouse click", () => { cy.mount( - Argentina - Bulgaria + Argentina + Bulgaria ); cy.get(".ui5-invisiblemessage-polite").as("liveRegion"); - cy.get("#item1").realClick(); + cy.get("[ui5-list]").find("[ui5-li]").first().realClick(); cy.get("@liveRegion").should("contain.text", "Selected"); - cy.get("#item1").realClick(); + cy.get("[ui5-list]").find("[ui5-li]").first().realClick(); + cy.get("@liveRegion").should("contain.text", "Not Selected"); + }); + + it("announces 'Selected' and 'Not Selected' when items are toggled in Multiple mode via Space key", () => { + cy.mount( + + Argentina + Bulgaria + + ); + + cy.get(".ui5-invisiblemessage-polite").as("liveRegion"); + + cy.get("[ui5-list]").find("[ui5-li]").first().click(); + cy.realPress("Space"); + cy.get("@liveRegion").should("contain.text", "Selected"); + + cy.realPress("Space"); cy.get("@liveRegion").should("contain.text", "Not Selected"); }); it("does not announce selection when selectionMode is None", () => { cy.mount( - Argentina + Argentina ); - cy.get(".ui5-invisiblemessage-polite").as("liveRegion"); + cy.get(".ui5-invisiblemessage-polite").as("liveRegion").should("have.text", ""); - cy.get("#item1").realClick(); + cy.get("[ui5-list]").find("[ui5-li]").first().realClick(); + cy.get("@liveRegion").should("have.text", ""); + }); + + it("does not announce selection when selectionMode is Delete", () => { + cy.mount( + + Argentina + + ); + + cy.get(".ui5-invisiblemessage-polite").as("liveRegion").should("have.text", ""); + + cy.get("[ui5-list]").find("[ui5-li]").first().click(); + cy.realPress("Delete"); cy.get("@liveRegion").should("have.text", ""); }); it("does not announce selection when selection-change event is prevented", () => { cy.mount( e.preventDefault()}> - Argentina - Bulgaria + Argentina + Bulgaria ); - cy.get(".ui5-invisiblemessage-polite").as("liveRegion"); + cy.get(".ui5-invisiblemessage-polite").as("liveRegion").should("have.text", ""); - cy.get("#item1").realClick(); + cy.get("[ui5-list]").find("[ui5-li]").first().realClick(); cy.get("@liveRegion").should("have.text", ""); }); }); diff --git a/packages/main/src/List.ts b/packages/main/src/List.ts index 893db6d51fc8..15b0ca86a9fd 100644 --- a/packages/main/src/List.ts +++ b/packages/main/src/List.ts @@ -39,7 +39,6 @@ import { } from "@ui5/webcomponents-base/dist/util/AccessibilityTextsHelper.js"; import getNormalizedTarget from "@ui5/webcomponents-base/dist/util/getNormalizedTarget.js"; import announce from "@ui5/webcomponents-base/dist/util/InvisibleMessage.js"; -import InvisibleMessageMode from "@ui5/webcomponents-base/dist/types/InvisibleMessageMode.js"; import type I18nBundle from "@ui5/webcomponents-base/dist/i18nBundle.js"; import debounce from "@ui5/webcomponents-base/dist/util/debounce.js"; import isElementInView from "@ui5/webcomponents-base/dist/util/isElementInView.js"; @@ -921,12 +920,12 @@ class List extends UI5Element { }); if (changePrevented) { this._revertSelection(previouslySelectedItems); - } else { + } else if (this.selectionMode !== ListSelectionMode.Delete) { const item = e.detail.item; const selectedText = item.selected ? List.i18nBundle.getText(LIST_ITEM_SELECTED) : List.i18nBundle.getText(LIST_ITEM_NOT_SELECTED); - announce(selectedText, InvisibleMessageMode.Polite); + announce(selectedText, "Polite"); } } } From d975417349bf0636a8c36a3bfdc6ac5cedfb7d6a Mon Sep 17 00:00:00 2001 From: Nikola Anachkov Date: Tue, 5 May 2026 13:13:25 +0300 Subject: [PATCH 3/3] chore: fix failing test --- packages/main/cypress/specs/List.cy.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/main/cypress/specs/List.cy.tsx b/packages/main/cypress/specs/List.cy.tsx index bbb95e9d0494..03297965deb5 100644 --- a/packages/main/cypress/specs/List.cy.tsx +++ b/packages/main/cypress/specs/List.cy.tsx @@ -482,10 +482,11 @@ describe("List - Accessibility", () => { cy.get(".ui5-invisiblemessage-polite").as("liveRegion"); - cy.get("[ui5-list]").find("[ui5-li]").first().realClick(); + cy.get("[ui5-list]").find("[ui5-li]").first().shadow().find("li").focus(); + cy.realPress("Space"); cy.get("@liveRegion").should("contain.text", "Selected"); - cy.get("[ui5-list]").find("[ui5-li]").eq(1).click(); + cy.get("[ui5-list]").find("[ui5-li]").eq(1).shadow().find("li").focus(); cy.realPress("Space"); cy.get("@liveRegion").should("contain.text", "Selected"); }); @@ -517,7 +518,7 @@ describe("List - Accessibility", () => { cy.get(".ui5-invisiblemessage-polite").as("liveRegion"); - cy.get("[ui5-list]").find("[ui5-li]").first().click(); + cy.get("[ui5-list]").find("[ui5-li]").first().shadow().find("li").focus(); cy.realPress("Space"); cy.get("@liveRegion").should("contain.text", "Selected");