From d044f81d5c818eb28b09518e76b92e4a94b4394a Mon Sep 17 00:00:00 2001 From: Boyan Rakilovski Date: Sun, 28 Jun 2026 03:23:30 +0300 Subject: [PATCH 1/2] fix(ui5-file-uploader): announce value state message to screen readers Issue: - The value state message of ui5-file-uploader was not announced by screen readers (NVDA, JAWS) when the component received focus. - Unlike ui5-input, the native input type file had no aria-describedby link to the value state text, so AT had no way to discover the message regardless of whether the visual popover was open. Solution: - Added an ariaValueStateHiddenText getter that composes the full AT announcement string from the value state type label and the message text, mirroring the existing pattern in ui5-input. - Added ariaDescribedBy to accInfo, pointing to valueStateDesc when a value state is active. - Added a hidden span with id valueStateDesc in FileUploaderTemplate within the same shadow root as the native input, so the aria-describedby reference resolves correctly across renders. - The hidden span is intentionally placed in FileUploaderTemplate and not in FileUploaderPopoverTemplate, as aria-describedby cannot cross shadow DOM boundaries into the popover's own shadow root. Fixed: https://github.com/UI5/webcomponents/issues/13549 --- packages/main/src/FileUploader.ts | 30 ++++++++++++++++++++++ packages/main/src/FileUploaderTemplate.tsx | 4 +++ 2 files changed, 34 insertions(+) diff --git a/packages/main/src/FileUploader.ts b/packages/main/src/FileUploader.ts index 1c55c49063d3..5a8d4aad18b6 100644 --- a/packages/main/src/FileUploader.ts +++ b/packages/main/src/FileUploader.ts @@ -35,6 +35,10 @@ import { VALUE_STATE_INFORMATION, VALUE_STATE_ERROR, VALUE_STATE_WARNING, + VALUE_STATE_TYPE_SUCCESS, + VALUE_STATE_TYPE_INFORMATION, + VALUE_STATE_TYPE_ERROR, + VALUE_STATE_TYPE_WARNING, FILEUPLOADER_DEFAULT_PLACEHOLDER, FILEUPLOADER_DEFAULT_MULTIPLE_PLACEHOLDER, FILEUPLOADER_ROLE_DESCRIPTION, @@ -624,9 +628,35 @@ class FileUploader extends UI5Element implements IFormInputElement { "ariaHasPopup": "dialog", "ariaLabel": getAllAccessibleNameRefTexts(this) || getEffectiveAriaLabelText(this) || getAssociatedLabelForTexts(this) || undefined, "ariaDescription": getAllAccessibleDescriptionRefTexts(this) || getEffectiveAriaDescriptionText(this) || undefined, + "ariaDescribedBy": this.hasValueState ? "valueStateDesc" : undefined, }; } + get valueStateTypeMappings(): Record { + return { + "Positive": FileUploader.i18nBundle.getText(VALUE_STATE_TYPE_SUCCESS), + "Information": FileUploader.i18nBundle.getText(VALUE_STATE_TYPE_INFORMATION), + "Negative": FileUploader.i18nBundle.getText(VALUE_STATE_TYPE_ERROR), + "Critical": FileUploader.i18nBundle.getText(VALUE_STATE_TYPE_WARNING), + }; + } + + get ariaValueStateHiddenText(): string | undefined { + if (!this.hasValueState) { + return undefined; + } + + const valueStateType = this.valueStateTypeMappings[this.valueState]; + + if (this.shouldDisplayDefaultValueStateMessage) { + return this.valueStateText ? `${valueStateType} ${this.valueStateText}` : valueStateType; + } + + return this.valueStateMessage.length + ? `${valueStateType} ${this.valueStateMessage.map(el => el.textContent).join(" ")}` + : valueStateType; + } + get inputTitle(): string { return FileUploader.i18nBundle.getText(FILEUPLOADER_INPUT_TOOLTIP); } diff --git a/packages/main/src/FileUploaderTemplate.tsx b/packages/main/src/FileUploaderTemplate.tsx index bbceeaa01d3d..a54954d0ca88 100644 --- a/packages/main/src/FileUploaderTemplate.tsx +++ b/packages/main/src/FileUploaderTemplate.tsx @@ -35,10 +35,14 @@ export default function FileUploaderTemplate(this: FileUploader) { aria-description={this.accInfo.ariaDescription} aria-required={this.accInfo.ariaRequired} aria-invalid={this.accInfo.ariaInvalid} + aria-describedby={this.accInfo.ariaDescribedBy} onClick={this._onNativeInputClick} onChange={this._onChange} data-sap-focus-ref /> + {this.hasValueState && + {this.ariaValueStateHiddenText} + } {this.hideInput ? ( From 4b9ae7ed8ec60cccada4bc124a4c4cbfd1d0a582 Mon Sep 17 00:00:00 2001 From: Boyan Rakilovski Date: Sun, 28 Jun 2026 03:29:03 +0300 Subject: [PATCH 2/2] fix(ui5-file-uploader): announce value state message to screen readers --- .../main/cypress/specs/FileUploader.cy.tsx | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/packages/main/cypress/specs/FileUploader.cy.tsx b/packages/main/cypress/specs/FileUploader.cy.tsx index b2537c4decec..280b0611b7d0 100644 --- a/packages/main/cypress/specs/FileUploader.cy.tsx +++ b/packages/main/cypress/specs/FileUploader.cy.tsx @@ -478,6 +478,31 @@ describe("Interaction", () => { }); describe("Accessibility", () => { + it("value state hidden text contains type label and default message", () => { + cy.mount( + + ); + + cy.get("#uploader") + .shadow() + .find("#valueStateDesc") + .should("have.text", "Value State Error Invalid entry"); + }); + + it("value state hidden text contains type label and custom message", () => { + cy.mount( + +
Custom error message.
+
+ ); + + cy.get("#uploader") + .shadow() + .find("#valueStateDesc") + .should("include.text", "Value State Error") + .and("include.text", "Custom error message."); + }); + it("A11y attributes", () => { cy.mount( <>