diff --git a/packages/main/cypress/specs/Select.cy.tsx b/packages/main/cypress/specs/Select.cy.tsx
index 76d26b7aed5c..88f3b34ce77f 100644
--- a/packages/main/cypress/specs/Select.cy.tsx
+++ b/packages/main/cypress/specs/Select.cy.tsx
@@ -592,6 +592,102 @@ describe("Select - Accessibility", () => {
expect(accessInfo.label).to.equal("Updated Reference"); // Updated aria label from ref
});
});
+
+ it("should have hidden span with selected option text for screen reader announcement", () => {
+ cy.mount(
+
+ );
+
+ // Verify hidden span exists with correct attributes
+ cy.get("#selectWithHiddenText")
+ .shadow()
+ .find("[id$='-selectedOptionText']")
+ .should("exist")
+ .should("have.class", "ui5-hidden-text")
+ .should("have.attr", "role", "option")
+ .should("have.attr", "aria-selected", "true")
+ .should("have.text", "Option 2");
+ });
+
+ it("should update hidden span text when selection changes", () => {
+ cy.mount(
+
+ );
+
+ // Verify initial hidden span text
+ cy.get("#selectUpdateHiddenText")
+ .shadow()
+ .find("[id$='-selectedOptionText']")
+ .should("have.text", "Second Option");
+
+ // Open select and change selection
+ cy.get("#selectUpdateHiddenText")
+ .realClick();
+
+ cy.get("#selectUpdateHiddenText")
+ .should("have.attr", "opened");
+
+ // Select third option
+ cy.get("#selectUpdateHiddenText")
+ .find("[ui5-option]")
+ .eq(2)
+ .realClick();
+
+ // Verify hidden span text is updated for screen reader announcement
+ cy.get("#selectUpdateHiddenText")
+ .shadow()
+ .find("[id$='-selectedOptionText']")
+ .should("have.text", "Third Option");
+ });
+
+ it("should set ariaActiveDescendantElement on focus ref when popover closes after selection change (NVDA fix)", () => {
+ cy.mount(
+
+ );
+
+ // Open select
+ cy.get("#selectNvdaFix")
+ .realClick();
+
+ cy.get("#selectNvdaFix")
+ .should("have.attr", "opened");
+
+ // Select a different option
+ cy.get("#selectNvdaFix")
+ .find("[ui5-option]")
+ .eq(2)
+ .realClick();
+
+ // After popover closes, verify the hidden span contains the new selection
+ cy.get("#selectNvdaFix")
+ .should("not.have.attr", "opened");
+
+ cy.get("#selectNvdaFix")
+ .shadow()
+ .find("[id$='-selectedOptionText']")
+ .should("have.text", "Gamma");
+
+ // The ariaActiveDescendantElement is set temporarily and cleared after 100ms
+ // We verify the mechanism works by checking the hidden span has the correct content
+ // which is what screen readers like NVDA will announce
+ cy.get("#selectNvdaFix")
+ .shadow()
+ .find("[id$='-selectedOptionText']")
+ .should("have.attr", "role", "option")
+ .should("have.attr", "aria-selected", "true");
+ });
});
describe("Select - Popover", () => {
diff --git a/packages/main/src/Select.ts b/packages/main/src/Select.ts
index 2aa8a642ae2c..56c1ec690d76 100644
--- a/packages/main/src/Select.ts
+++ b/packages/main/src/Select.ts
@@ -953,13 +953,29 @@ class Select extends UI5Element implements IFormInputElement {
this._iconPressed = false;
this._listWidth = 0;
+ const selectionChanged = this._lastSelectedOption !== this.options[this._selectedIndex];
+
if (this._escapePressed) {
this._select(this._selectedIndexBeforeOpen);
this._escapePressed = false;
- } else if (this._lastSelectedOption !== this.options[this._selectedIndex]) {
+ } else if (selectionChanged) {
this._fireChangeEvent(this.options[this._selectedIndex]);
this._lastSelectedOption = this.options[this._selectedIndex];
}
+
+ // Use ariaActiveDescendantElement to trigger screen readers to read the updated value
+ // Reference the hidden span in our shadow root that contains the selected text
+ if (selectionChanged) {
+ const focusRef = this.getFocusDomRef() as HTMLElement;
+ const hiddenOptionSpan = this.shadowRoot?.querySelector(`#${this._id}-selectedOptionText`);
+ if (focusRef && hiddenOptionSpan && !this._isPickerOpen) {
+ (focusRef as any).ariaActiveDescendantElement = hiddenOptionSpan;
+ // Clear it after a short delay to avoid JAWS VPC mode
+ setTimeout(() => {
+ (focusRef as any).ariaActiveDescendantElement = null;
+ }, 100);
+ }
+ }
this.fireDecoratorEvent("close");
}
diff --git a/packages/main/src/SelectTemplate.tsx b/packages/main/src/SelectTemplate.tsx
index c0ed1fb24c7c..c0410eedabbe 100644
--- a/packages/main/src/SelectTemplate.tsx
+++ b/packages/main/src/SelectTemplate.tsx
@@ -91,6 +91,10 @@ export default function SelectTemplate(this: Select) {
{this.ariaDescriptionText}
}
+ {/* Hidden element for ariaActiveDescendantElement reference */}
+
+ {this.text}
+
{SelectPopoverTemplate.call(this)}
>