From 5434fbaf4b2cc5b8f039680f42ce8b5ce24f8475 Mon Sep 17 00:00:00 2001 From: Plamen Ivanov Date: Wed, 5 Nov 2025 21:00:22 +0200 Subject: [PATCH 1/2] refactor(ui5-illustrated-message): aligned with ACC guidelines - Improved accessibility attributes for screen readers - Ensured proper role and aria-label usage --- .../cypress/specs/IllustratedMessage.cy.tsx | 129 +++++++++++++++++- packages/fiori/src/IllustratedMessage.ts | 28 ---- .../fiori/src/IllustratedMessageTemplate.tsx | 13 +- 3 files changed, 136 insertions(+), 34 deletions(-) diff --git a/packages/fiori/cypress/specs/IllustratedMessage.cy.tsx b/packages/fiori/cypress/specs/IllustratedMessage.cy.tsx index df9a91b81f8a..2ca9da895f90 100644 --- a/packages/fiori/cypress/specs/IllustratedMessage.cy.tsx +++ b/packages/fiori/cypress/specs/IllustratedMessage.cy.tsx @@ -12,12 +12,12 @@ describe("Accessibility", () => { cy.get("[ui5-illustrated-message]") .shadow() - .find("svg") + .find(".ui5-illustrated-message-illustration") .should("have.attr", "role", "presentation"); cy.get("[ui5-illustrated-message]") .shadow() - .find("svg") + .find(".ui5-illustrated-message-illustration") .should("have.attr", "aria-hidden", "true"); }); @@ -30,10 +30,133 @@ describe("Accessibility", () => { cy.get("[ui5-illustrated-message]") .shadow() - .find("svg") + .find(".ui5-illustrated-message-illustration") .should("not.have.attr", "aria-label"); }); + + it("should have role=img and aria-label with illustration name when decorative is false", () => { + cy.mount( + + + ); + + cy.get("[ui5-illustrated-message]") + .shadow() + .find(".ui5-illustrated-message-illustration") + .should("have.attr", "role", "img"); + + cy.get("[ui5-illustrated-message]") + .shadow() + .find(".ui5-illustrated-message-illustration") + .should("have.attr", "aria-label", "UnableToUpload"); + }); + + it("should have role=img and aria-label with illustration name by default (when decorative is not set)", () => { + cy.mount( + + + ); + + cy.get("[ui5-illustrated-message]") + .shadow() + .find(".ui5-illustrated-message-illustration") + .should("have.attr", "role", "img"); + + cy.get("[ui5-illustrated-message]") + .shadow() + .find(".ui5-illustrated-message-illustration") + .should("have.attr", "aria-label", "NoData"); + }); + + it("should have proper role and aria-label on the root container", () => { + cy.mount( + + + ); + + cy.get("[ui5-illustrated-message]") + .shadow() + .find(".ui5-illustrated-message-root") + .should("have.attr", "role", "region"); + + cy.get("[ui5-illustrated-message]") + .shadow() + .find(".ui5-illustrated-message-root") + .should("have.attr", "aria-label", "Illustrated Message"); + }); + + it("should have aria-describedby pointing to the title element", () => { + cy.mount( + + + ); + + cy.get("[ui5-illustrated-message]") + .shadow() + .find(".ui5-illustrated-message-root") + .should("have.attr", "aria-describedby") + .and("match", /-im-title$/); + + cy.get("[ui5-illustrated-message]") + .shadow() + .find(".ui5-illustrated-message-title") + .should("have.attr", "id") + .and("match", /-im-title$/); + + // Verify that aria-describedby points to the correct title element + cy.get("[ui5-illustrated-message]") + .shadow() + .find(".ui5-illustrated-message-root") + .invoke("attr", "aria-describedby") + .then((ariaDescribedBy) => { + cy.get("[ui5-illustrated-message]") + .shadow() + .find(".ui5-illustrated-message-title") + .should("have.attr", "id", ariaDescribedBy); + }); + }); + + it("should maintain accessibility attributes when title is present", () => { + cy.mount( + + + ); + + cy.get("[ui5-illustrated-message]") + .shadow() + .find(".ui5-illustrated-message-root") + .should("have.attr", "role", "region") + .and("have.attr", "aria-label", "Illustrated Message") + .and("have.attr", "aria-describedby"); + + cy.get("[ui5-illustrated-message]") + .shadow() + .find(".ui5-illustrated-message-title") + .should("exist") + .and("have.attr", "id"); + }); + + it("should maintain accessibility attributes when title is slotted", () => { + cy.mount( + +
Slotted Title
+
+ ); + + cy.get("[ui5-illustrated-message]") + .shadow() + .find(".ui5-illustrated-message-root") + .should("have.attr", "role", "region") + .and("have.attr", "aria-label", "Illustrated Message") + .and("have.attr", "aria-describedby"); + + cy.get("[ui5-illustrated-message]") + .shadow() + .find(".ui5-illustrated-message-title") + .should("exist") + .and("have.attr", "id"); + }); }); describe("design", () => { diff --git a/packages/fiori/src/IllustratedMessage.ts b/packages/fiori/src/IllustratedMessage.ts index a27c445f4b26..9a204ae97d48 100644 --- a/packages/fiori/src/IllustratedMessage.ts +++ b/packages/fiori/src/IllustratedMessage.ts @@ -365,30 +365,6 @@ class IllustratedMessage extends UI5Element { } } - _setSVGAccAttrs() { - const svg = this.shadowRoot!.querySelector(".ui5-illustrated-message-illustration svg"); - - if (!svg) { - return; - } - - if (this.decorative) { - svg.setAttribute("role", "presentation"); - svg.setAttribute("aria-hidden", "true"); - svg.removeAttribute("aria-label"); - } else { - svg.removeAttribute("role"); - svg.removeAttribute("aria-hidden"); - - // Set aria-label only when not decorative and text exists - if (this.ariaLabelText) { - svg.setAttribute("aria-label", this.ariaLabelText); - } else { - svg.removeAttribute("aria-label"); - } - } - } - _adjustHeightToFitContainer() { const illustrationWrapper = this.shadowRoot!.querySelector(".ui5-illustrated-message-illustration"), illustration = illustrationWrapper.querySelector("svg"); @@ -402,10 +378,6 @@ class IllustratedMessage extends UI5Element { } } - onAfterRendering() { - this._setSVGAccAttrs(); - } - /** * Modifies the IM styles in accordance to the `size` property's value. * Note: The resize handler has no effect when size is different than "Auto". diff --git a/packages/fiori/src/IllustratedMessageTemplate.tsx b/packages/fiori/src/IllustratedMessageTemplate.tsx index 03859b735aa0..a038d9287a9d 100644 --- a/packages/fiori/src/IllustratedMessageTemplate.tsx +++ b/packages/fiori/src/IllustratedMessageTemplate.tsx @@ -3,12 +3,19 @@ import type IllustratedMessage from "./IllustratedMessage.js"; export default function IllustratedMessageTemplate(this: IllustratedMessage) { return ( -
+
-
+
{this.hasTitle && -
+
{this.hasFormattedTitle ? : From e05d4f792fa40541aeee5626722fe73d3162197b Mon Sep 17 00:00:00 2001 From: Plamen Ivanov Date: Thu, 6 Nov 2025 17:44:59 +0200 Subject: [PATCH 2/2] Merge branch 'main' into im-acc-alin -fixed merge conflicts in IllustratedMessageTemplate.tsx --- packages/fiori/src/IllustratedMessageTemplate.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/fiori/src/IllustratedMessageTemplate.tsx b/packages/fiori/src/IllustratedMessageTemplate.tsx index dd70df27456c..1ebced333629 100644 --- a/packages/fiori/src/IllustratedMessageTemplate.tsx +++ b/packages/fiori/src/IllustratedMessageTemplate.tsx @@ -11,9 +11,7 @@ export default function IllustratedMessageTemplate(this: IllustratedMessage) {
-
+ aria-label={!this.decorative ? this.name : undefined}> {renderIllustration.call(this)}