diff --git a/packages/fiori/cypress/specs/UserSettingsDialog.cy.tsx b/packages/fiori/cypress/specs/UserSettingsDialog.cy.tsx index 9db3e7df18d46..3ffd2e451b856 100644 --- a/packages/fiori/cypress/specs/UserSettingsDialog.cy.tsx +++ b/packages/fiori/cypress/specs/UserSettingsDialog.cy.tsx @@ -509,6 +509,64 @@ describe("Events", () => { cy.get("@search").should("have.attr", "placeholder", "Search"); cy.get("@search").shadow().find("input").type("test"); }); + + it("tests search results announcement - no results", () => { + cy.mount( + + + ); + + cy.get("[ui5-user-settings-dialog]").as("settings"); + cy.get("@settings").shadow().find("[ui5-dialog]").as("dialog"); + cy.get("@dialog").find("[ui5-input]").as("search"); + + cy.get(".ui5-invisiblemessage-polite") + .as("invisibleMessage") + .should("have.text", ""); + + cy.get("@search").shadow().find("input").type("zzz"); + + cy.get("@invisibleMessage").should("have.text", "No search results"); + }); + + it("tests search results announcement - one result", () => { + cy.mount( + + + ); + + cy.get("[ui5-user-settings-dialog]").as("settings"); + cy.get("@settings").shadow().find("[ui5-dialog]").as("dialog"); + cy.get("@dialog").find("[ui5-input]").as("search"); + + cy.get(".ui5-invisiblemessage-polite") + .as("invisibleMessage") + .should("have.text", ""); + + cy.get("@search").shadow().find("input").type("Payment"); + + cy.get("@invisibleMessage").should("have.text", "1 result available"); + }); + + it("tests search results announcement - multiple results", () => { + cy.mount( + + + + ); + + cy.get("[ui5-user-settings-dialog]").as("settings"); + cy.get("@settings").shadow().find("[ui5-dialog]").as("dialog"); + cy.get("@dialog").find("[ui5-input]").as("search"); + + cy.get(".ui5-invisiblemessage-polite") + .as("invisibleMessage") + .should("have.text", ""); + + cy.get("@search").shadow().find("input").type("Pay"); + + cy.get("@invisibleMessage").should("have.text", "2 results are available"); + }); }); describe("Responsiveness", () => { diff --git a/packages/fiori/src/UserSettingsDialog.ts b/packages/fiori/src/UserSettingsDialog.ts index 3edd22591a817..310de63622c6a 100644 --- a/packages/fiori/src/UserSettingsDialog.ts +++ b/packages/fiori/src/UserSettingsDialog.ts @@ -13,6 +13,8 @@ import type ListItemBase from "@ui5/webcomponents/dist/ListItemBase.js"; import type { PopupBeforeCloseEventDetail } from "@ui5/webcomponents/dist/Popup.js"; import { isPhone, isTablet, isCombi } from "@ui5/webcomponents-base/dist/Device.js"; import MediaRange from "@ui5/webcomponents-base/dist/MediaRange.js"; +import announce from "@ui5/webcomponents-base/dist/util/InvisibleMessage.js"; +import InvisibleMessageMode from "@ui5/webcomponents-base/dist/types/InvisibleMessageMode.js"; import UserSettingsDialogTemplate from "./UserSettingsDialogTemplate.js"; import type UserSettingsItem from "./UserSettingsItem.js"; import UserSettingsDialogCss from "./generated/themes/UserSettingsDialog.css.js"; @@ -25,6 +27,9 @@ import { USER_SETTINGS_DIALOG_SAVE_BUTTON_TEXT, USER_SETTINGS_DIALOG_CANCEL_BUTTON_TEXT, USER_SETTINGS_DIALOG_NO_SEARCH_RESULTS_TEXT, + USER_SETTINGS_DIALOG_SEARCH_NO_RESULTS, + USER_SETTINGS_DIALOG_SEARCH_ONE_RESULT, + USER_SETTINGS_DIALOG_SEARCH_MORE_RESULTS, } from "./generated/i18n/i18n-defaults.js"; type UserSettingsItemSelectEventDetail = { @@ -223,6 +228,13 @@ class UserSettingsDialog extends UI5Element { @property({ type: Boolean }) _showNoSearchResult = false; + /** + * Indicates that the user changed the search value and the search + * results should be announced on the next rendering. + * @private + */ + _announceSearchResults = false; + /** * Defines the current media query size. * @private @@ -269,6 +281,11 @@ class UserSettingsDialog extends UI5Element { this._showNoSearchResult = false; } + if (this._announceSearchResults) { + this._announceSearchResults = false; + announce(this._searchResultsText, InvisibleMessageMode.Polite); + } + if (!this._selectedSetting) { this._selectedSetting = this.items[0] || this.fixedItems[0]; } @@ -344,6 +361,19 @@ class UserSettingsDialog extends UI5Element { return UserSettingsDialog.i18nBundle.getText(USER_SETTINGS_DIALOG_NO_SEARCH_RESULTS_TEXT); } + get _searchResultsText() { + const resultsCount = this._filteredItems.length + this._filteredFixedItems.length; + + switch (resultsCount) { + case 0: + return UserSettingsDialog.i18nBundle.getText(USER_SETTINGS_DIALOG_SEARCH_NO_RESULTS); + case 1: + return UserSettingsDialog.i18nBundle.getText(USER_SETTINGS_DIALOG_SEARCH_ONE_RESULT); + default: + return UserSettingsDialog.i18nBundle.getText(USER_SETTINGS_DIALOG_SEARCH_MORE_RESULTS, resultsCount); + } + } + get _selectedItemSlotName() { return this._selectedSetting ? this._selectedSetting._individualSlot : ""; } @@ -374,6 +404,7 @@ class UserSettingsDialog extends UI5Element { _handleInput(e: CustomEvent) { this._searchValue = (e.target as Input).value; + this._announceSearchResults = true; } captureRef(ref: HTMLElement & { associatedSettingItem?: UI5Element} | null) { diff --git a/packages/fiori/src/i18n/messagebundle.properties b/packages/fiori/src/i18n/messagebundle.properties index 2397251013b95..eeac8c1e8d6bf 100644 --- a/packages/fiori/src/i18n/messagebundle.properties +++ b/packages/fiori/src/i18n/messagebundle.properties @@ -614,6 +614,15 @@ USER_SETTINGS_DIALOG_CANCEL_BUTTON_TEXT=Cancel #XTXT: User settings dialog not result text USER_SETTINGS_DIALOG_NO_SEARCH_RESULTS_TEXT=No search results +#XACT: ARIA announcement for the User Settings Dialog search result if no hit +USER_SETTINGS_DIALOG_SEARCH_NO_RESULTS=No search results + +#XACT: ARIA announcement for the User Settings Dialog search result if one hit +USER_SETTINGS_DIALOG_SEARCH_ONE_RESULT=1 result available + +#XACT: ARIA announcement for the User Settings Dialog search result if more than one hit ({0} is the number of hits) +USER_SETTINGS_DIALOG_SEARCH_MORE_RESULTS={0} results are available + #XTXT: User settings account view edit avatar text USER_SETTINGS_ACCOUNT_EDIT_AVATAR_TXT=Edit avatar