From ae03f4c07e69d7591d0fc5d6cfadcd116824c65c Mon Sep 17 00:00:00 2001 From: Dobrin Dimchev Date: Fri, 4 Jul 2025 09:05:12 +0300 Subject: [PATCH 1/9] fix(ui5-select): announce header text on mobile --- packages/main/cypress/specs/Select.cy.tsx | 80 +++++++++++++++++++ .../main/cypress/specs/Select.mobile.cy.tsx | 72 +++++++++++++++++ packages/main/src/Select.ts | 7 +- packages/main/src/SelectPopoverTemplate.tsx | 1 + 4 files changed, 159 insertions(+), 1 deletion(-) create mode 100644 packages/main/cypress/specs/Select.mobile.cy.tsx diff --git a/packages/main/cypress/specs/Select.cy.tsx b/packages/main/cypress/specs/Select.cy.tsx index 5340302f3738..d82a80535587 100644 --- a/packages/main/cypress/specs/Select.cy.tsx +++ b/packages/main/cypress/specs/Select.cy.tsx @@ -221,6 +221,86 @@ describe("Select - Popover", () => { .should("be.visible") .should("have.text", "Custom message"); }); + + it("ResponsivePopover should not have accessible name on desktop", () => { + cy.mount( + + ); + + // Open the popover + cy.get("#desktopSelect").realClick(); + + // Check that the ResponsivePopover does not have an accessible name on desktop + cy.get("#desktopSelect") + .shadow() + .find("[ui5-responsive-popover]") + .should("not.have.attr", "accessible-name"); + }); + + it("should focus the selected option when popover opens", () => { + cy.mount( + + ); + + // Open the popover + cy.get("#focusSelect").realClick(); + + // The selected option should be focused + cy.get("#opt2").should("have.attr", "focused"); + }); + + it("should apply focus to the correct option when selection changes", () => { + cy.mount( + + ); + + // Programmatically change the value and check focus + cy.get("#changeFocusSelect").then(($select) => { + const select = $select[0] as any; + select.value = "option2"; + }); + + // Open the popover + cy.get("#changeFocusSelect").realClick(); + + // The newly selected option should be focused + cy.get("#opt2").should("have.attr", "focused"); + }); + + it("should maintain focus on keyboard navigation", () => { + cy.mount( + + ); + + // Open the popover + cy.get("#keyboardFocusSelect").realClick(); + + // Initially, option 1 should be focused + cy.get("#kopt1").should("have.attr", "focused"); + + // Navigate down with arrow key + cy.get("#keyboardFocusSelect").realPress("ArrowDown"); + + // Now option 2 should be focused + cy.get("#kopt2").should("have.attr", "focused"); + cy.get("#kopt1").should("not.have.attr", "focused"); + }); }); describe("Select - Properties", () => { diff --git a/packages/main/cypress/specs/Select.mobile.cy.tsx b/packages/main/cypress/specs/Select.mobile.cy.tsx new file mode 100644 index 000000000000..8b924f8e2d55 --- /dev/null +++ b/packages/main/cypress/specs/Select.mobile.cy.tsx @@ -0,0 +1,72 @@ +import Option from "../../src/Option.js"; +import Select from "../../src/Select.js"; + +describe("Select - Mobile Accessibility", () => { + beforeEach(() => { + cy.ui5SimulateDevice("phone"); + }); + + it("ResponsivePopover should have accessible name when opened on mobile", () => { + cy.mount( + + ); + + // Open the popover + cy.get("#select").realClick(); + + // Check that the ResponsivePopover has an accessible name on mobile + cy.get("#select") + .shadow() + .find("[ui5-responsive-popover]") + .should("have.attr", "accessible-name") + .and("not.be.empty"); + }); + + it("ResponsivePopover accessible name should match the expected title text on mobile", () => { + cy.mount( + + ); + + // Open the popover + cy.get("#select").realClick(); + + // check if accessible-name is equal to select._headerTitleText + cy.get("#select").invoke("prop", "_headerTitleText").then(_headerTitleText => { + cy.get("#select") + .shadow() + .find("[ui5-responsive-popover]") + .should("have.attr", "accessible-name") + .and("equal", _headerTitleText); + }); + }); +}); + +describe("Select - Mobile Focus Management", () => { + beforeEach(() => { + cy.ui5SimulateDevice("phone"); + }); + + it("should focus the selected option when popover opens on mobile", () => { + cy.mount( + + ); + + cy.get("#select").realClick(); + + cy.get("#opt2").should("have.attr", "focused"); + cy.get("#opt2").shadow() + .find(".ui5-li-root") + .should("be.focused"); + }); +}); diff --git a/packages/main/src/Select.ts b/packages/main/src/Select.ts index 97e8724f6200..b8632cbc61c7 100644 --- a/packages/main/src/Select.ts +++ b/packages/main/src/Select.ts @@ -816,7 +816,12 @@ class Select extends UI5Element implements IFormInputElement { _applyFocusToSelectedItem() { this.options.forEach(option => { - option.focused = option.selected; + if (option.selected) { + option.focused = true; + option.focus(); + } else { + option.focused = false; + } }); } diff --git a/packages/main/src/SelectPopoverTemplate.tsx b/packages/main/src/SelectPopoverTemplate.tsx index 45ea56a2d29e..0e80ea9be4ac 100644 --- a/packages/main/src/SelectPopoverTemplate.tsx +++ b/packages/main/src/SelectPopoverTemplate.tsx @@ -25,6 +25,7 @@ export default function SelectPopoverTemplate(this: Select) { onBeforeOpen={this._beforeOpen} onClose={this._afterClose} onKeyDown={this._onkeydown} + accessibleName={this._isPhone ? this._headerTitleText : undefined} > {this._isPhone &&
From 4f1f5ab66189f323e15c3408f038b644c2e396c3 Mon Sep 17 00:00:00 2001 From: Dobrin Dimchev Date: Fri, 4 Jul 2025 09:31:44 +0300 Subject: [PATCH 2/9] test: improve tests --- packages/main/cypress/specs/Select.cy.tsx | 54 ++----------------- .../main/cypress/specs/Select.mobile.cy.tsx | 44 +-------------- 2 files changed, 6 insertions(+), 92 deletions(-) diff --git a/packages/main/cypress/specs/Select.cy.tsx b/packages/main/cypress/specs/Select.cy.tsx index d82a80535587..31210c909fe4 100644 --- a/packages/main/cypress/specs/Select.cy.tsx +++ b/packages/main/cypress/specs/Select.cy.tsx @@ -243,63 +243,19 @@ describe("Select - Popover", () => { it("should focus the selected option when popover opens", () => { cy.mount( - ); - // Open the popover - cy.get("#focusSelect").realClick(); + cy.get("#select").realClick(); - // The selected option should be focused cy.get("#opt2").should("have.attr", "focused"); - }); - - it("should apply focus to the correct option when selection changes", () => { - cy.mount( - - ); - - // Programmatically change the value and check focus - cy.get("#changeFocusSelect").then(($select) => { - const select = $select[0] as any; - select.value = "option2"; - }); - - // Open the popover - cy.get("#changeFocusSelect").realClick(); - - // The newly selected option should be focused - cy.get("#opt2").should("have.attr", "focused"); - }); - - it("should maintain focus on keyboard navigation", () => { - cy.mount( - - ); - - // Open the popover - cy.get("#keyboardFocusSelect").realClick(); - - // Initially, option 1 should be focused - cy.get("#kopt1").should("have.attr", "focused"); - - // Navigate down with arrow key - cy.get("#keyboardFocusSelect").realPress("ArrowDown"); - - // Now option 2 should be focused - cy.get("#kopt2").should("have.attr", "focused"); - cy.get("#kopt1").should("not.have.attr", "focused"); + cy.get("#opt2").shadow() + .find(".ui5-li-root") + .should("be.focused"); }); }); diff --git a/packages/main/cypress/specs/Select.mobile.cy.tsx b/packages/main/cypress/specs/Select.mobile.cy.tsx index 8b924f8e2d55..7e25b30da865 100644 --- a/packages/main/cypress/specs/Select.mobile.cy.tsx +++ b/packages/main/cypress/specs/Select.mobile.cy.tsx @@ -6,27 +6,7 @@ describe("Select - Mobile Accessibility", () => { cy.ui5SimulateDevice("phone"); }); - it("ResponsivePopover should have accessible name when opened on mobile", () => { - cy.mount( - - ); - - // Open the popover - cy.get("#select").realClick(); - - // Check that the ResponsivePopover has an accessible name on mobile - cy.get("#select") - .shadow() - .find("[ui5-responsive-popover]") - .should("have.attr", "accessible-name") - .and("not.be.empty"); - }); - - it("ResponsivePopover accessible name should match the expected title text on mobile", () => { + it("ResponsivePopover accessible name should match the expected title text", () => { cy.mount( - - - - - ); - - cy.get("#select").realClick(); - - cy.get("#opt2").should("have.attr", "focused"); - cy.get("#opt2").shadow() - .find(".ui5-li-root") - .should("be.focused"); - }); -}); From ebdd733d4b0a2b48a1a6dc9461870cc544399248 Mon Sep 17 00:00:00 2001 From: Dobrin Dimchev Date: Thu, 11 Sep 2025 12:59:44 +0300 Subject: [PATCH 3/9] chore: samples --- packages/fiori/src/ShellBar.ts | 9 +- .../fiori/src/ShellBarPopoverTemplate.tsx | 1 + packages/fiori/src/ShellBarTemplate.tsx | 2 +- .../ShellBar_content_button_overflow.html | 288 ++++++++++++++++++ .../ShellBar_shellbar_item_overflow.html | 267 ++++++++++++++++ 5 files changed, 565 insertions(+), 2 deletions(-) create mode 100644 packages/fiori/test/pages/ShellBar_content_button_overflow.html create mode 100644 packages/fiori/test/pages/ShellBar_shellbar_item_overflow.html diff --git a/packages/fiori/src/ShellBar.ts b/packages/fiori/src/ShellBar.ts index 8de6a9e99188..4e1568119aa3 100644 --- a/packages/fiori/src/ShellBar.ts +++ b/packages/fiori/src/ShellBar.ts @@ -987,6 +987,13 @@ class ShellBar extends UI5Element { item.classList.add("ui5-shellbar-hidden-button"); } + if (hiddenItems.length === 1) { + return { + itemsInfo: this._itemsInfo, + contentInfo: this._contentInfo, + }; + } + if (hiddenItems.length === 1 && !this.showSearchField) { const nextItemToHide = hidableDomElements[++lastHiddenIndex]; if (nextItemToHide) { @@ -1272,7 +1279,7 @@ class ShellBar extends UI5Element { const bIndex = PREDEFINED_PLACE_ACTIONS.indexOf(b.icon || ""); return aIndex - bIndex; }).map((item: ShellBarItem) => { - item._getRealDomRef = () => this.getDomRef()!.querySelector(`*[data-ui5-stable=${item.stableDomRef}]`)!; + item._getRealDomRef = () => this.shadowRoot!.querySelector(`*[data-ui5-stable=${item.stableDomRef}]`)!; // check if included for lean mode const show = !!item.icon || false; return { diff --git a/packages/fiori/src/ShellBarPopoverTemplate.tsx b/packages/fiori/src/ShellBarPopoverTemplate.tsx index a97604a13117..b32f7491d225 100644 --- a/packages/fiori/src/ShellBarPopoverTemplate.tsx +++ b/packages/fiori/src/ShellBarPopoverTemplate.tsx @@ -32,6 +32,7 @@ export default function PopoversTemplate(this: ShellBar) { key={index} data-count={icon.count} data-ui5-external-action-item-id={icon.refItemid} + data-ui5-stable={icon.stableDomRef} icon={icon.icon ? icon.icon : ""} type="Active" onui5-_press={icon.press} diff --git a/packages/fiori/src/ShellBarTemplate.tsx b/packages/fiori/src/ShellBarTemplate.tsx index 914146bce097..a70676e1d85f 100644 --- a/packages/fiori/src/ShellBarTemplate.tsx +++ b/packages/fiori/src/ShellBarTemplate.tsx @@ -215,7 +215,7 @@ export default function ShellBarTemplate(this: ShellBar) { tooltip={item.tooltip} data-ui5-notifications-count={this.notificationsCount} data-ui5-external-action-item-id={item.refItemid} - data-ui5-stable={item.stableDomRef} + data-ui5-stable={item.icon && !this.isIconHidden(item.icon) ? item.stableDomRef : undefined} onClick={item.press} accessibilityAttributes={item.accessibilityAttributes} > diff --git a/packages/fiori/test/pages/ShellBar_content_button_overflow.html b/packages/fiori/test/pages/ShellBar_content_button_overflow.html new file mode 100644 index 000000000000..9795b40b9082 --- /dev/null +++ b/packages/fiori/test/pages/ShellBar_content_button_overflow.html @@ -0,0 +1,288 @@ + + + + + + Shell Bar - Content Button Overflow + + + + + + + + + + + + + +
+ Hidden Items +
+ +
+
+
+ + + + + + + + + + + + + + + Analytics Dashboard + + + + + + + + North + America + + Refresh + + Filters + +
+ Live Updates + +
+ + Actions + + Export + + + +
+ Report Date: + +
+ + + + + + + + + +
+ +
+
+ ShellBar Content Button Overflow Management + Uses a button in the content area that appears when items are hidden. Resize the browser + window to see how overflow management works. +
+ All items visible +
+
+
+ + + + + + diff --git a/packages/fiori/test/pages/ShellBar_shellbar_item_overflow.html b/packages/fiori/test/pages/ShellBar_shellbar_item_overflow.html new file mode 100644 index 000000000000..8c19c4607cdc --- /dev/null +++ b/packages/fiori/test/pages/ShellBar_shellbar_item_overflow.html @@ -0,0 +1,267 @@ + + + + + + Shell Bar - ShellBar Item Overflow + + + + + + + + + + + + + +
+ Hidden Items +
+ +
+
+
+ + + + + + + + + + + + Project Management + + + + + + + + Project + Alpha + + New Task + + Kanban View + +
+ High Priority + 5 +
+ + Team + + Reports + + + +
+ Deadline: + +
+ + + + + +
+ +
+
+ ShellBar Item Overflow Management + Uses a ui5-shellbar-item that appears when content items are hidden. This provides a more + integrated look in the actions area. +
+ All items visible +
+
+
+ + + + + + From c03c4da1dec2f054fadba725f78e97316137e41d Mon Sep 17 00:00:00 2001 From: Dobrin Dimchev Date: Thu, 18 Sep 2025 14:47:24 +0300 Subject: [PATCH 4/9] fix: move focus on dialog when isPhone === true --- packages/main/src/Select.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/main/src/Select.ts b/packages/main/src/Select.ts index c704c721783a..c310e5f538e9 100644 --- a/packages/main/src/Select.ts +++ b/packages/main/src/Select.ts @@ -868,11 +868,11 @@ class Select extends UI5Element implements IFormInputElement { _applyFocusToSelectedItem() { this.options.forEach(option => { - if (option.selected) { - option.focused = true; + option.focused = option.selected; + if (option.focused && isPhone()) { + // on phone, the popover opens full screen (dialog) + // move focus to option to read out dialog header option.focus(); - } else { - option.focused = false; } }); } From b14daeb7f367ae93f66ed4c125c1a11f97620757 Mon Sep 17 00:00:00 2001 From: Dobrin Dimchev Date: Thu, 18 Sep 2025 14:48:56 +0300 Subject: [PATCH 5/9] Update packages/main/cypress/specs/Select.mobile.cy.tsx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- packages/main/cypress/specs/Select.mobile.cy.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/main/cypress/specs/Select.mobile.cy.tsx b/packages/main/cypress/specs/Select.mobile.cy.tsx index 0ac7de824151..940c018b22f5 100644 --- a/packages/main/cypress/specs/Select.mobile.cy.tsx +++ b/packages/main/cypress/specs/Select.mobile.cy.tsx @@ -17,7 +17,7 @@ describe("Select mobile general interaction", () => { // Open the popover cy.get("#select").realClick(); - // check if accessible-name is equal to select._headerTitleText + // Check if accessible-name is equal to select._headerTitleText cy.get("#select").invoke("prop", "_headerTitleText").then(_headerTitleText => { cy.get("#select") .shadow() From 54fb638d92c041a8f3f9e8a0d05a2f311c9ddc77 Mon Sep 17 00:00:00 2001 From: Dobrin Dimchev Date: Thu, 18 Sep 2025 14:49:03 +0300 Subject: [PATCH 6/9] Update packages/main/cypress/specs/Select.cy.tsx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- packages/main/cypress/specs/Select.cy.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/main/cypress/specs/Select.cy.tsx b/packages/main/cypress/specs/Select.cy.tsx index 89f1b528f878..06b08610b563 100644 --- a/packages/main/cypress/specs/Select.cy.tsx +++ b/packages/main/cypress/specs/Select.cy.tsx @@ -489,7 +489,6 @@ describe("Select - Popover", () => { ); - cy.get("#select").realClick(); cy.get("#opt2").should("have.attr", "focused"); From 5702dc64b73223b98f84c16f7203c1f64eaa17ee Mon Sep 17 00:00:00 2001 From: Dobrin Dimchev Date: Thu, 18 Sep 2025 14:54:27 +0300 Subject: [PATCH 7/9] chore: remove shellbar changes --- packages/fiori/src/ShellBar.ts | 9 +- .../fiori/src/ShellBarPopoverTemplate.tsx | 1 - packages/fiori/src/ShellBarTemplate.tsx | 2 +- .../ShellBar_content_button_overflow.html | 288 ------------------ .../ShellBar_shellbar_item_overflow.html | 267 ---------------- 5 files changed, 2 insertions(+), 565 deletions(-) delete mode 100644 packages/fiori/test/pages/ShellBar_content_button_overflow.html delete mode 100644 packages/fiori/test/pages/ShellBar_shellbar_item_overflow.html diff --git a/packages/fiori/src/ShellBar.ts b/packages/fiori/src/ShellBar.ts index 4e1568119aa3..8de6a9e99188 100644 --- a/packages/fiori/src/ShellBar.ts +++ b/packages/fiori/src/ShellBar.ts @@ -987,13 +987,6 @@ class ShellBar extends UI5Element { item.classList.add("ui5-shellbar-hidden-button"); } - if (hiddenItems.length === 1) { - return { - itemsInfo: this._itemsInfo, - contentInfo: this._contentInfo, - }; - } - if (hiddenItems.length === 1 && !this.showSearchField) { const nextItemToHide = hidableDomElements[++lastHiddenIndex]; if (nextItemToHide) { @@ -1279,7 +1272,7 @@ class ShellBar extends UI5Element { const bIndex = PREDEFINED_PLACE_ACTIONS.indexOf(b.icon || ""); return aIndex - bIndex; }).map((item: ShellBarItem) => { - item._getRealDomRef = () => this.shadowRoot!.querySelector(`*[data-ui5-stable=${item.stableDomRef}]`)!; + item._getRealDomRef = () => this.getDomRef()!.querySelector(`*[data-ui5-stable=${item.stableDomRef}]`)!; // check if included for lean mode const show = !!item.icon || false; return { diff --git a/packages/fiori/src/ShellBarPopoverTemplate.tsx b/packages/fiori/src/ShellBarPopoverTemplate.tsx index b32f7491d225..a97604a13117 100644 --- a/packages/fiori/src/ShellBarPopoverTemplate.tsx +++ b/packages/fiori/src/ShellBarPopoverTemplate.tsx @@ -32,7 +32,6 @@ export default function PopoversTemplate(this: ShellBar) { key={index} data-count={icon.count} data-ui5-external-action-item-id={icon.refItemid} - data-ui5-stable={icon.stableDomRef} icon={icon.icon ? icon.icon : ""} type="Active" onui5-_press={icon.press} diff --git a/packages/fiori/src/ShellBarTemplate.tsx b/packages/fiori/src/ShellBarTemplate.tsx index a70676e1d85f..914146bce097 100644 --- a/packages/fiori/src/ShellBarTemplate.tsx +++ b/packages/fiori/src/ShellBarTemplate.tsx @@ -215,7 +215,7 @@ export default function ShellBarTemplate(this: ShellBar) { tooltip={item.tooltip} data-ui5-notifications-count={this.notificationsCount} data-ui5-external-action-item-id={item.refItemid} - data-ui5-stable={item.icon && !this.isIconHidden(item.icon) ? item.stableDomRef : undefined} + data-ui5-stable={item.stableDomRef} onClick={item.press} accessibilityAttributes={item.accessibilityAttributes} > diff --git a/packages/fiori/test/pages/ShellBar_content_button_overflow.html b/packages/fiori/test/pages/ShellBar_content_button_overflow.html deleted file mode 100644 index 9795b40b9082..000000000000 --- a/packages/fiori/test/pages/ShellBar_content_button_overflow.html +++ /dev/null @@ -1,288 +0,0 @@ - - - - - - Shell Bar - Content Button Overflow - - - - - - - - - - - - - -
- Hidden Items -
- -
-
-
- - - - - - - - - - - - - - - Analytics Dashboard - - - - - - - - North - America - - Refresh - - Filters - -
- Live Updates - -
- - Actions - - Export - - - -
- Report Date: - -
- - - - - - - - - -
- -
-
- ShellBar Content Button Overflow Management - Uses a button in the content area that appears when items are hidden. Resize the browser - window to see how overflow management works. -
- All items visible -
-
-
- - - - - - diff --git a/packages/fiori/test/pages/ShellBar_shellbar_item_overflow.html b/packages/fiori/test/pages/ShellBar_shellbar_item_overflow.html deleted file mode 100644 index 8c19c4607cdc..000000000000 --- a/packages/fiori/test/pages/ShellBar_shellbar_item_overflow.html +++ /dev/null @@ -1,267 +0,0 @@ - - - - - - Shell Bar - ShellBar Item Overflow - - - - - - - - - - - - - -
- Hidden Items -
- -
-
-
- - - - - - - - - - - - Project Management - - - - - - - - Project - Alpha - - New Task - - Kanban View - -
- High Priority - 5 -
- - Team - - Reports - - - -
- Deadline: - -
- - - - - -
- -
-
- ShellBar Item Overflow Management - Uses a ui5-shellbar-item that appears when content items are hidden. This provides a more - integrated look in the actions area. -
- All items visible -
-
-
- - - - - - From 8025a4f89beabd2056e08b54f805b92475cc7026 Mon Sep 17 00:00:00 2001 From: Dobrin Dimchev Date: Thu, 18 Sep 2025 14:55:12 +0300 Subject: [PATCH 8/9] chore: whitespace --- packages/main/cypress/specs/Select.cy.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/main/cypress/specs/Select.cy.tsx b/packages/main/cypress/specs/Select.cy.tsx index 06b08610b563..a99e9663a383 100644 --- a/packages/main/cypress/specs/Select.cy.tsx +++ b/packages/main/cypress/specs/Select.cy.tsx @@ -462,7 +462,7 @@ describe("Select - Popover", () => { .should("have.text", "Custom message"); }); - it("ResponsivePopover should not have accessible name on desktop", () => { + it("ResponsivePopover should not have accessible name on desktop", () => { cy.mount( - - - - - ); - cy.get("#select").realClick(); - - cy.get("#opt2").should("have.attr", "focused"); - cy.get("#opt2").shadow() - .find(".ui5-li-root") - .should("be.focused"); - }); - it("Value state message popover can extend beyond select width", () => { cy.mount( + + + + + ); + cy.get("#select").realClick(); + + cy.get("#opt2").should("have.attr", "focused"); + cy.get("#opt2").shadow() + .find(".ui5-li-root") + .should("be.focused"); + }); + it("Changes selection in Dialog", () => { cy.mount(