From 888bd1d2af8807adcd3854de6459302c2d1da28e Mon Sep 17 00:00:00 2001 From: Nayden Naydenov Date: Wed, 27 May 2026 16:00:32 +0300 Subject: [PATCH 1/2] chore: tests --- .../base/src/asset-registries/LocaleData.ts | 7 + .../fiori/cypress/specs/SideNavigation.cy.tsx | 33 +- .../src/sap/base/util/LoaderExtensions.ts | 18 +- .../cypress/specs/OpenUI5andWebCPopups.cy.tsx | 778 ------------------ .../specs/OpenUI5andWebCPopups.mixed.cy.tsx | 92 +++ .../OpenUI5andWebCPopups.openUI5Dialog.cy.tsx | 23 + ...dWebCPopups.openUI5DialogWebCDialog.cy.tsx | 37 + ...ups.openUI5DialogWebCPopoverNoFocus.cy.tsx | 43 + ...dWebCPopups.openUI5DialogWebCSelect.cy.tsx | 43 + .../specs/OpenUI5andWebCPopups.utils.tsx | 322 ++++++++ .../OpenUI5andWebCPopups.webCDialog.cy.tsx | 26 + ...bCPopups.webCDialogComboBoxWithHint.cy.tsx | 62 ++ ...ebCPopups.webCDialogOpenUI5ComboBox.cy.tsx | 46 ++ ...dWebCPopups.webCDialogOpenUI5Dialog.cy.tsx | 41 + ...ups.webCDialogOpenUI5PopoverNoFocus.cy.tsx | 43 + ...dWebCPopups.webCDialogOpenUI5Select.cy.tsx | 38 + 16 files changed, 860 insertions(+), 792 deletions(-) delete mode 100644 packages/main/cypress/specs/OpenUI5andWebCPopups.cy.tsx create mode 100644 packages/main/cypress/specs/OpenUI5andWebCPopups.mixed.cy.tsx create mode 100644 packages/main/cypress/specs/OpenUI5andWebCPopups.openUI5Dialog.cy.tsx create mode 100644 packages/main/cypress/specs/OpenUI5andWebCPopups.openUI5DialogWebCDialog.cy.tsx create mode 100644 packages/main/cypress/specs/OpenUI5andWebCPopups.openUI5DialogWebCPopoverNoFocus.cy.tsx create mode 100644 packages/main/cypress/specs/OpenUI5andWebCPopups.openUI5DialogWebCSelect.cy.tsx create mode 100644 packages/main/cypress/specs/OpenUI5andWebCPopups.utils.tsx create mode 100644 packages/main/cypress/specs/OpenUI5andWebCPopups.webCDialog.cy.tsx create mode 100644 packages/main/cypress/specs/OpenUI5andWebCPopups.webCDialogComboBoxWithHint.cy.tsx create mode 100644 packages/main/cypress/specs/OpenUI5andWebCPopups.webCDialogOpenUI5ComboBox.cy.tsx create mode 100644 packages/main/cypress/specs/OpenUI5andWebCPopups.webCDialogOpenUI5Dialog.cy.tsx create mode 100644 packages/main/cypress/specs/OpenUI5andWebCPopups.webCDialogOpenUI5PopoverNoFocus.cy.tsx create mode 100644 packages/main/cypress/specs/OpenUI5andWebCPopups.webCDialogOpenUI5Select.cy.tsx diff --git a/packages/base/src/asset-registries/LocaleData.ts b/packages/base/src/asset-registries/LocaleData.ts index 0245147742d3..821f0e14e107 100644 --- a/packages/base/src/asset-registries/LocaleData.ts +++ b/packages/base/src/asset-registries/LocaleData.ts @@ -101,6 +101,11 @@ const getLocaleData = (localeId: string) => { return content; }; +// resolves the effective locale ID that getLocaleData / _loadCldrOnce would use +const getEffectiveLocaleId = (localeId: string) => { + return loaders.has(localeId) ? localeId : DEFAULT_LOCALE; +}; + // load bundle over the network once const _loadCldrOnce = (localeId: string) => { if (!cldrPromises.get(localeId)) { @@ -167,6 +172,8 @@ export { registerLocaleDataLoader, fetchCldr, getLocaleData, + getEffectiveLocaleId, + _loadCldrOnce, }; export type { diff --git a/packages/fiori/cypress/specs/SideNavigation.cy.tsx b/packages/fiori/cypress/specs/SideNavigation.cy.tsx index e46aafbeeef0..db912d8f0a3d 100644 --- a/packages/fiori/cypress/specs/SideNavigation.cy.tsx +++ b/packages/fiori/cypress/specs/SideNavigation.cy.tsx @@ -939,33 +939,39 @@ describe("Side Navigation interaction", () => { ); - cy.get("#parentItem").realClick(); - [ + const cases: Array<{ selector: () => Cypress.Chainable, expectedCallCount: number }> = [ { - element: cy.get("#sideNav").shadow().find("[ui5-responsive-popover] [ui5-side-navigation-item][text='2']").shadow() - .find(".ui5-sn-item"), + selector: () => cy.get("#sideNav").shadow().find("[ui5-responsive-popover] [ui5-side-navigation-item][text='2']").shadow().find(".ui5-sn-item"), expectedCallCount: 1, }, { - element: cy.get("#sideNav").shadow().find("[ui5-responsive-popover] [ui5-side-navigation-sub-item][text='2.1']"), + selector: () => cy.get("#sideNav").shadow().find("[ui5-responsive-popover] [ui5-side-navigation-sub-item][text='2.1']"), expectedCallCount: 1, }, { - element: cy.get("#sideNav").shadow().find("[ui5-responsive-popover] [ui5-side-navigation-sub-item][text='2.2']"), + selector: () => cy.get("#sideNav").shadow().find("[ui5-responsive-popover] [ui5-side-navigation-sub-item][text='2.2']"), expectedCallCount: 0, }, - ].forEach(({ element, expectedCallCount }) => { + ]; + + cases.forEach(({ selector, expectedCallCount }) => { cy.get("#sideNav") .then(sideNav => { sideNav.get(0).addEventListener("ui5-selection-change", cy.stub().as("selectionChangeHandler")); }); - // act + // open the popover fresh each iteration cy.get("#parentItem").realClick(); - element.realClick(); + cy.get("#sideNav").shadow().find("[ui5-responsive-popover]").should("be.visible"); + + // act + selector().realClick(); // assert - cy.get("@selectionChangeHandler", { timeout: 1000 }).should("have.callCount", expectedCallCount); + cy.get("@selectionChangeHandler").should("have.callCount", expectedCallCount); + + // close popover before next iteration (click outside) + cy.get("body").click(0, 0); }); }); @@ -978,6 +984,8 @@ describe("Side Navigation interaction", () => { ); cy.get("#item").realClick(); + // wait for first selection to settle before clicking again + cy.get("@selectionChangeHandler").should("have.been.calledOnce"); cy.get("#item").realClick(); cy.get("@selectionChangeHandler").should("have.been.calledOnce"); @@ -1021,7 +1029,7 @@ describe("Side Navigation interaction", () => { .realClick(); cy.get("@overflowMenu") - .should("be.not.visible"); + .should("not.be.visible"); cy.get("[ui5-side-navigation-item][text='Home 6']") .should("be.focused"); @@ -1029,6 +1037,9 @@ describe("Side Navigation interaction", () => { cy.get("@itemOverflow") .realClick(); + cy.get("@overflowMenu") + .should("be.visible"); + cy.get("@overflowMenu") .find("[ui5-navigation-menu-item][text='Home 7']") .realClick(); diff --git a/packages/localization/src/sap/base/util/LoaderExtensions.ts b/packages/localization/src/sap/base/util/LoaderExtensions.ts index cd556402b8ed..2fa35635c370 100644 --- a/packages/localization/src/sap/base/util/LoaderExtensions.ts +++ b/packages/localization/src/sap/base/util/LoaderExtensions.ts @@ -1,4 +1,4 @@ -import { getLocaleData } from "@ui5/webcomponents-base/dist/asset-registries/LocaleData.js"; +import { getLocaleData, getEffectiveLocaleId, _loadCldrOnce } from "@ui5/webcomponents-base/dist/asset-registries/LocaleData.js"; const loadResource = (moduleName: string) => { const moduleFormat = moduleName.match(/sap\/ui\/core\/cldr\/(\w+)\.json/); @@ -6,8 +6,20 @@ const loadResource = (moduleName: string) => { throw new Error(`Unknown module "${moduleName}"`); } - const localeId = moduleFormat[1]; - return getLocaleData(localeId); + // OpenUI5's loadData builds locale IDs as "_" which can produce + // trailing underscores (e.g. "bg_" for Bulgarian with no region). Strip the trailing + // underscore so the ID matches what fetchCldr stores in localeDataMap (e.g. "bg"). + const localeId = moduleFormat[1].replace(/_$/, ""); + + // If the data is already in localeDataMap (fetchCldr resolved), return it synchronously + // so SyncPromise resolves immediately and mData is set in the LocaleData constructor. + // If fetchCldr is still in-flight, return its in-progress Promise so SyncPromise can + // resolve asynchronously — instead of throwing and leaving mData permanently unset. + try { + return getLocaleData(localeId); + } catch { + return _loadCldrOnce(getEffectiveLocaleId(localeId)); + } }; const LoaderExtensions = { diff --git a/packages/main/cypress/specs/OpenUI5andWebCPopups.cy.tsx b/packages/main/cypress/specs/OpenUI5andWebCPopups.cy.tsx deleted file mode 100644 index 60dc10a1f7a5..000000000000 --- a/packages/main/cypress/specs/OpenUI5andWebCPopups.cy.tsx +++ /dev/null @@ -1,778 +0,0 @@ -import OpenUI5Support from "@ui5/webcomponents-base/dist/features/OpenUI5Support.js"; -import Button from "../../src/Button.js"; -import Dialog from "../../src/Dialog.js"; -import Select from "../../src/Select.js"; -import Option from "../../src/Option.js"; -import ComboBox from "../../src/ComboBox.js"; -import ComboBoxItem from "../../src/ComboBoxItem.js"; -import ResponsivePopover from "../../src/ResponsivePopover.js"; - -let OpenUI5Element; - -function onOpenUI5InitMethod(win) { - (win as any).sap.ui.require([ - "sap/ui/core/Element", - "sap/ui/core/HTML", - "sap/m/Button", - "sap/m/Dialog", - "sap/m/Popover", - "sap/m/Input" - ], async (Element, HTML, Button, Dialog, Popover, Input) => { - - await OpenUI5Support.init(); - OpenUI5Element = Element; - new Button("openUI5Button", { - text: "Open OpenUI5 Dialog", - press: function () { - new Dialog("openUI5Dialog1", { - title: "OpenUI5 Dialog", - draggable: true, - content: [ - new HTML({ - content: - ` - Option 1 - Option 2 - Option 3 - Option 4 - Option 5 - - - - - - -

` - }), - new Input(), - new Button({ - text: "Focus stop" - }), - new Button("openResPopoverButton", { - text: "Open WebC Responsive Popover", - press: function () { - (document.getElementById("respPopover") as any).open = true; - } - }), - new Button("openResPopoverNoInitialFocusButton", { - text: "Open WebC RP with NO Initial Focus", - press: function () { - (document.getElementById("respPopoverNoInitialFocus") as any).open = true; - } - }), - new Button("openWebCDialog", { - text: "Open WebC Dialog", - press: function () { - (document.getElementById("webCDialog1") as any).open = true; - } - }) - ], - afterClose: function () { - this.destroy(); - } - }).open(); - } - }).placeAt("content"); - }); - - document.getElementById("myButton").addEventListener("click", function() { - (document.getElementById("dialog1") as any).open = true; - }); - - (win as any).sap.ui.require(["sap/m/Select", - "sap/m/ComboBox", - "sap/m/Button", - "sap/ui/core/Item", - "sap/ui/core/ShortcutHintsMixin"], - (Select, - ComboBox, - Button, - Item, - ShortcutHintsMixin) => { - new Select("openUI5Select1", { - items: [ - new Item({ text: "Item 1" }), - new Item({ text: "Item 2" }), - new Item({ text: "Item 3" }) - ], - change: function (oEvent) { - console.error("Selected item:", oEvent.getParameter("selectedItem").getText()); - } - }).placeAt("dialog1content"); - - new ComboBox("openUI5Combobox1", { - items: [ - new Item({ text: "Item 1" }), - new Item({ text: "Item 2" }), - new Item({ text: "Item 3" }) - ] - }).placeAt("dialog1content"); - - const button = new Button("openUI5ButtonWithHint", { - text: "OpenUI5 with Shortcut (Ctrl+S)", - press: function () { - openUI5Dialog(win); - } - }).placeAt("dialog1content"); - - - ShortcutHintsMixin.addConfig(button, { - event: "press", - position: "0 0", - addAccessibilityLabel: true, - message: "Save" - }, button); - }); - - document.getElementById("dialogButton").addEventListener("click", function () { - openUI5Dialog(win); - }); - - document.getElementById("openUI5DialogFromWebC").addEventListener("click", function () { - openUI5Dialog(win); - }); - - document.getElementById("popoverButtonNoFocus").addEventListener("click", function (event) { - openUI5Popover(win, event.target); - }); -} - -function openUI5Dialog(win) { - (win as any).sap.ui.require(["sap/m/Button", "sap/m/Dialog"], (Button, Dialog) => { - new Dialog("openUI5DialogWithButtons", { - title: "OpenUI5 Dialog", - draggable: true, - content: [ - new Button({ - text: "Focus stop" - }), - new Button("openUI5DialogButton", { - text: "Open WebC Dialog", - press: function () { - (document.getElementById("newDialog1") as any).open = true; - } - }), - new Button("openUI5DialogFromUi5", { - text: "Open UI5 Dialog", - press: function () { - openUI5DialogFromUi5(win) - } - }) - ], - afterClose: function () { - this.destroy(); - } - }).open(); - }); -} - -function openUI5DialogFromUi5(win) { - (win as any).sap.ui.require(["sap/m/Button", "sap/m/Dialog"], (Button, Dialog) => { - new Dialog("openUI5DialogFinal", { - title: "OpenUI5 Dialog", - draggable: true, - content: [ - new Button({ - text: "Focus stop" - }) - ], - afterClose: function () { - this.destroy(); - } - }).open(); - }); -} - -function openUI5Popover(win, opener) { - (win as any).sap.ui.require(["sap/m/Popover", "sap/m/Button"], (Popover, Button) => { - new Popover("openUI5PopoverSecond", { - title: "OpenUI5 Popover", - content: [ - new Button("someButton", { - text: "Open new OpenUI5 Popover", - press: function (oEvent) { - new Popover({ - title: "New OpenUI5 Popover", - placement: "Bottom", - content: [ - new Button({ - text: "Focus stop" - }) - ], - initialFocus: "someButton", - afterClose: function () { - this.destroy(); - } - }).openBy(oEvent.getSource()); - } - }) - ], - initialFocus: "popoverButtonNoFocus", - afterClose: function () { - this.destroy(); - } - }).openBy(opener); - }); -} - -function isOpenUI5DialogOpen($dialog) { - expect(OpenUI5Element).to.exist; - - const dialogInstance = OpenUI5Element.getElementById($dialog.attr("id")); - - expect(dialogInstance).to.exist - expect(dialogInstance.isOpen()).to.be.true; -}; - -describe("ui5 and web components integration", () => { - beforeEach(() => { - // mount the components - cy.mount( - <> -
- -
- -
-
- Web Components: -
- - - - - - -
-
- - -
- - - - - - -
- - - - - - - - ); - - // define initialization function before loading ui5 - cy.window().then((win) => { - (win as any).onOpenUI5Init = function () { - onOpenUI5InitMethod(win); - }; - }); - - // add ui5 bootstrap - cy.document().then((doc) => { - const ui5Script = doc.createElement('script'); - ui5Script.src = 'https://ui5.sap.com/resources/sap-ui-core.js'; - ui5Script.id = 'sap-ui-bootstrap'; - ui5Script.setAttribute('data-sap-ui-libs', 'sap.m'); - ui5Script.setAttribute('data-sap-ui-oninit', 'onOpenUI5Init'); - doc.head.appendChild(ui5Script); - }); - }); - - function OpenWebCDialog() { - cy.get("#openUI5Button", { timeout: 10000 }) - .should('be.visible'); - - cy.get('#myButton') - .should('be.visible') - .realClick(); - - cy.get("#dialog1").ui5DialogOpened(); - - cy.realPress("Escape"); - - cy.get('#dialog1') - .should('not.be.visible'); - - cy.get('#myButton') - .should('be.focused'); - } - - function OpenWebCDialogOpenUI5Select() { - cy.get("#openUI5Button") - .should('be.visible'); - - cy.get('#myButton') - .should('be.visible') - .realClick(); - - cy.get("#dialog1").ui5DialogOpened(); - - cy.get('#openUI5Select1') - .should('be.visible') - .realClick(); - - cy.get("#__popover0") - .should('be.visible'); - - cy.realPress("Escape"); - - cy.get("#__popover0") - .should('not.be.visible'); - - cy.realPress("Escape"); - - cy.get('#dialog1') - .should('not.be.visible'); - - cy.get('#myButton') - .should('be.focused'); - } - - function OpenWebCDialogOpenUI5ComboBox() { - cy.get("#openUI5Button") - .should('be.visible'); - - cy.get('#myButton') - .should('be.visible') - .realClick(); - - cy.get("#dialog1").ui5DialogOpened(); - - cy.get('#openUI5Combobox1') - .should('be.visible') - .realClick() - .type("I"); - - cy.get("#openUI5Combobox1-popup") - .should('be.visible'); - - cy.realPress("Escape"); - - cy.get("#openUI5Combobox1-popup") - .should('not.be.visible'); - - cy.get("#dialog1").ui5DialogOpened(); - - cy.realPress("Escape"); - - // combo box value is reset, dialog stays open - cy.get("#dialog1").ui5DialogOpened(); - - cy.realPress("Escape"); - - cy.get('#dialog1') - .should('not.be.visible'); - - cy.get('#myButton') - .should('be.focused'); - } - - function OpenWebCDialogOpenOpenUI5Dialog() { - cy.get("#openUI5Button") - .should('be.visible'); - - cy.get('#myButton') - .should('be.visible') - .realClick(); - - cy.get("#dialog1").ui5DialogOpened(); - - cy.get('#dialogButton') - .should('be.visible') - .realClick(); - - cy.get("#openUI5DialogWithButtons") - .should('be.visible'); - - cy.realPress("Escape"); - - cy.get("#openUI5DialogWithButtons") - .should('not.be.visible'); - - cy.get('#dialogButton') - .should('be.focused'); - - cy.realPress("Escape"); - - cy.get('#dialog1') - .should('not.be.visible'); - - cy.get('#myButton') - .should('be.focused'); - } - - function OpenWebCDialogOpenOpenUI5PopoverNoFocus() { - cy.get("#openUI5Button") - .should('be.visible'); - - cy.get('#myButton') - .should('be.visible') - .realClick(); - - cy.get("#dialog1").ui5DialogOpened(); - - cy.get('#popoverButtonNoFocus') - .should('be.visible') - .realClick(); - - cy.get("#openUI5PopoverSecond") - .should('be.visible'); - - cy.realPress("Escape"); - - cy.get("#openUI5PopoverSecond") - .should('not.exist'); - - cy.realPress(["Shift", "Tab"]); - - cy.get('#dialogButton') - .should('be.focused'); - - cy.realPress("Escape"); - - cy.get('#dialog1') - .should('not.be.visible'); - - cy.get('#myButton') - .should('be.focused'); - } - - function OpenWebCDialogOpenUI5ComboBoxNewOpenUI5DialogFromButtonWithHint() { - cy.get("#openUI5Button") - .should('be.visible'); - - cy.get('#myButton') - .should('be.visible') - .realClick(); - - cy.get("#dialog1").ui5DialogOpened(); - - cy.get('#openUI5Combobox1') - .should('be.visible') - .realClick() - .type("I"); - - cy.get("#openUI5Combobox1-popup") - .should('be.visible'); - - cy.get('#openUI5ButtonWithHint') - .should('be.visible') - .realClick(); - - cy.get("#openUI5Combobox1-popup") - .should('not.be.visible'); - - cy.get("#openUI5DialogWithButtons") - .should("be.visible"); - - cy.realPress("Escape"); - - cy.get("#openUI5DialogWithButtons") - .should("not.exist"); - - cy.get("#dialog1").ui5DialogOpened(); - - cy.get('#openUI5ButtonWithHint') - .should('be.focused') - - cy.get('#openUI5Combobox1') - .find('input') - .focus(); - - cy.get("#openUI5Combobox1") - .find('input') - .should('be.focused'); - - cy.realPress("Escape"); - - cy.get('#dialog1') - .should('not.be.visible'); - - cy.get('#myButton') - .should('be.focused'); - } - - function OpenUI5Dialog() { - cy.get("#openUI5Button") - .should('be.visible') - .realClick(); - - cy.get("#openUI5Dialog1") - .should('be.visible'); - - cy.realPress("Escape"); - - cy.get("#openUI5Dialog1") - .should('not.exist'); - - cy.get("#openUI5Button") - .should('be.focused'); - } - - function OpenUI5DialogWebCDialog() { - cy.get("#openUI5Button") - .should('be.visible') - .realClick(); - - cy.get("#openUI5Dialog1") - .should('be.visible'); - - cy.get("#openResPopoverButton") - .should('be.visible') - .realClick(); - - cy.get("#respPopover").ui5ResponsivePopoverOpened(); - - cy.realPress("Escape"); - - cy.get("#respPopover").ui5ResponsivePopoverClosed(); - - cy.get("#openUI5Dialog1") - .should('be.visible'); - - cy.realPress("Escape"); - - cy.get("#openUI5Dialog1") - .should('not.exist'); - - cy.get("#openUI5Button") - .should('be.focused'); - } - - function OpenUI5DialogWebCPopoverNoFocus() { - cy.get("#openUI5Button", { timeout: 10000 }) - .should('be.visible') - .realClick(); - - cy.get("#openUI5Dialog1") - .should('be.visible'); - - cy.wait(1000); - - cy.get("#openResPopoverNoInitialFocusButton") - .should('be.visible') - .realClick(); - - cy.get("#respPopoverNoInitialFocus").ui5ResponsivePopoverOpened(); - - cy.realPress("Escape"); - - cy.get("#respPopoverNoInitialFocus") - .ui5ResponsivePopoverClosed(); - - cy.get("#openResPopoverNoInitialFocusButton") - .should('be.focused'); - - cy.get("#openUI5Dialog1") - .should('be.visible'); - - cy.realPress("Escape"); - - cy.get("#openUI5Dialog1") - .should('not.be.visible'); - - cy.get("#openUI5Button") - .should('be.focused'); - } - - function OpenUI5DialogWebCSelect() { - cy.get("#openUI5Button", { timeout: 10000 }) - .should('be.visible') - .realClick(); - - cy.get("#openUI5Dialog1") - .should('be.visible'); - - cy.get("#webCSelect1") - .should('be.visible') - .realClick(); - - cy.get("#webCSelect1") - .shadow() - .find("[ui5-responsive-popover]").ui5DialogOpened(); - - cy.realPress("Escape"); - - cy.get("#webCSelect1") - .shadow() - .find("[ui5-responsive-popover]") - .ui5ResponsivePopoverClosed(); - - cy.get("#openUI5Dialog1") - .should('be.visible'); - - cy.realPress("Escape"); - - cy.get("#openUI5Dialog1") - .should('not.exist'); - - cy.get("#openUI5Button") - .should('be.focused'); - } - - function OpenUI5DialogWebCComboBox() { - cy.get("#openUI5Button") - .should('be.focused') - .realClick(); - - cy.get("#openUI5Dialog1") - .should('be.visible'); - - cy.get("#webCComboBox1") - .should('be.visible'); - - cy.get("#webCComboBox1") - .shadow() - .find('input') - .realClick() - .type("A"); - - cy.get("#webCComboBox1") - .shadow() - .find("[ui5-responsive-popover]").ui5DialogOpened(); - - cy.realPress("Escape"); - - cy.get("#webCComboBox1") - .shadow() - .find("[ui5-responsive-popover]") - .ui5ResponsivePopoverClosed(); - - cy.get("#openUI5Dialog1") - .should('be.visible'); - - cy.realPress("Escape"); - - cy.get("#openUI5Dialog1") - .should('be.visible'); - - cy.realPress("Escape"); - - cy.get("#openUI5Dialog1") - .should('not.exist'); - - cy.get("#openUI5Button") - .should('be.focused'); - } - - function OpenWebCUI5DialogMixed() { - cy.get("#openUI5Button", { timeout: 10000 }) - .should('be.visible'); - - // Open UI5 Dialog - cy.get("#openUI5Button") - .realClick(); - - cy.get("#openUI5Dialog1") - .should('be.visible'); - - // Open WebC Dialog from UI5 Dialog - cy.get("#openWebCDialog") - .should('be.visible') - .realClick(); - - cy.get("#webCDialog1") - .should('be.visible'); - - cy.get("#openUI5Dialog1") - .should('not.be.visible'); - - // Open UI5 Dialog from WebC Dialog - cy.get("#openUI5DialogFromWebC") - .should('be.visible') - .realClick(); - - cy.get("#webCDialog1") - .should('not.be.visible'); - - cy.get("#openUI5Dialog1") - .should('not.be.visible'); - - cy.get("#openUI5DialogWithButtons") - .should('be.visible'); - - // Open UI5 Dialog from UI5 Dialog - cy.get("#openUI5DialogFromUi5") - .should('be.visible') - .realClick(); - - cy.get("#openUI5DialogFinal") - .should('be.visible') - .should(isOpenUI5DialogOpen); - - cy.wait(1000); - - cy.get("#openUI5Dialog1") - .should('not.be.visible'); - - cy.get("#webCDialog1") - .should('not.be.visible'); - - cy.get("#openUI5DialogWithButtons") - .should('not.be.visible'); - - // Close all with Escape - cy.realPress("Escape"); - - cy.get("#openUI5DialogFinal") - .should('not.exist'); - - cy.get("#openUI5DialogWithButtons") - .should('be.visible') - .should(isOpenUI5DialogOpen); - cy.wait(100); - - cy.realPress("Escape"); - - cy.get("#openUI5DialogWithButtons") - .should('not.exist'); - - cy.get("#webCDialog1") - .should('be.visible'); - - cy.realPress("Escape"); - - cy.get("#openUI5Dialog1") - .should('be.visible'); - - cy.realPress("Escape"); - - cy.get("#openWebCDialog") - .should('not.exist'); - } - - it("Keyboard", () => { - OpenWebCDialog(); - OpenWebCDialogOpenOpenUI5Dialog(); - OpenWebCDialogOpenOpenUI5PopoverNoFocus(); - OpenWebCDialogOpenUI5Select(); - OpenWebCDialogOpenUI5ComboBox(); - OpenWebCDialogOpenUI5ComboBoxNewOpenUI5DialogFromButtonWithHint(); - - OpenUI5Dialog(); - OpenUI5DialogWebCDialog(); - OpenUI5DialogWebCPopoverNoFocus(); - OpenUI5DialogWebCSelect(); - OpenWebCUI5DialogMixed(); - // OpenUI5DialogWebCComboBox(); - }); -}); \ No newline at end of file diff --git a/packages/main/cypress/specs/OpenUI5andWebCPopups.mixed.cy.tsx b/packages/main/cypress/specs/OpenUI5andWebCPopups.mixed.cy.tsx new file mode 100644 index 000000000000..d186e6a6c5cd --- /dev/null +++ b/packages/main/cypress/specs/OpenUI5andWebCPopups.mixed.cy.tsx @@ -0,0 +1,92 @@ +import { mountUI5Fixtures, cleanupUI5Fixtures, isOpenUI5DialogOpen } from "./OpenUI5andWebCPopups.utils.js"; + +describe("ui5 and web components integration", () => { + beforeEach(() => { mountUI5Fixtures(); }); + afterEach(() => { cleanupUI5Fixtures(); }); + + it("Mixed: alternating OpenUI5 and WebC dialogs open in sequence and unwind correctly with Escape", () => { + cy.get("#openUI5Button", { timeout: 10000 }) + .should('be.visible'); + + // Open UI5 Dialog + cy.get("#openUI5Button") + .realClick(); + + cy.get("#openUI5Dialog1") + .should('be.visible'); + + // Open WebC Dialog from UI5 Dialog + cy.get("#openWebCDialog") + .should('be.visible') + .realClick(); + + cy.get("#webCDialog1") + .should('be.visible'); + + cy.get("#openUI5Dialog1") + .should('not.be.visible'); + + // Open UI5 Dialog from WebC Dialog + cy.get("#openUI5DialogFromWebC") + .should('be.visible') + .realClick(); + + cy.get("#webCDialog1") + .should('not.be.visible'); + + cy.get("#openUI5Dialog1") + .should('not.be.visible'); + + cy.get("#openUI5DialogWithButtons") + .should('be.visible'); + + // Open UI5 Dialog from UI5 Dialog + cy.get("#openUI5DialogFromUi5") + .should('be.visible') + .realClick(); + + cy.get("#openUI5DialogFinal") + .should('be.visible') + .should(isOpenUI5DialogOpen); + + cy.wait(1000); + + cy.get("#openUI5Dialog1") + .should('not.be.visible'); + + cy.get("#webCDialog1") + .should('not.be.visible'); + + cy.get("#openUI5DialogWithButtons") + .should('not.be.visible'); + + // Close all with Escape + cy.realPress("Escape"); + + cy.get("#openUI5DialogFinal") + .should('not.exist'); + + cy.get("#openUI5DialogWithButtons") + .should('be.visible') + .should(isOpenUI5DialogOpen); + cy.wait(100); + + cy.realPress("Escape"); + + cy.get("#openUI5DialogWithButtons") + .should('not.exist'); + + cy.get("#webCDialog1") + .should('be.visible'); + + cy.realPress("Escape"); + + cy.get("#openUI5Dialog1") + .should('be.visible'); + + cy.realPress("Escape"); + + cy.get("#openWebCDialog") + .should('not.exist'); + }); +}); diff --git a/packages/main/cypress/specs/OpenUI5andWebCPopups.openUI5Dialog.cy.tsx b/packages/main/cypress/specs/OpenUI5andWebCPopups.openUI5Dialog.cy.tsx new file mode 100644 index 000000000000..37b528ba9ddb --- /dev/null +++ b/packages/main/cypress/specs/OpenUI5andWebCPopups.openUI5Dialog.cy.tsx @@ -0,0 +1,23 @@ +import { mountUI5Fixtures, cleanupUI5Fixtures } from "./OpenUI5andWebCPopups.utils.js"; + +describe("ui5 and web components integration", () => { + beforeEach(() => { mountUI5Fixtures(); }); + afterEach(() => { cleanupUI5Fixtures(); }); + + it("OpenUI5 Dialog opens and closes with Escape, focus returns to opener", () => { + cy.get("#openUI5Button") + .should('be.visible') + .realClick(); + + cy.get("#openUI5Dialog1") + .should('be.visible'); + + cy.realPress("Escape"); + + cy.get("#openUI5Dialog1") + .should('not.exist'); + + cy.get("#openUI5Button") + .should('be.focused'); + }); +}); diff --git a/packages/main/cypress/specs/OpenUI5andWebCPopups.openUI5DialogWebCDialog.cy.tsx b/packages/main/cypress/specs/OpenUI5andWebCPopups.openUI5DialogWebCDialog.cy.tsx new file mode 100644 index 000000000000..d36cdf56e2f7 --- /dev/null +++ b/packages/main/cypress/specs/OpenUI5andWebCPopups.openUI5DialogWebCDialog.cy.tsx @@ -0,0 +1,37 @@ +import ResponsivePopover from "../../src/ResponsivePopover.js"; +import { mountUI5Fixtures, cleanupUI5Fixtures } from "./OpenUI5andWebCPopups.utils.js"; + +describe("ui5 and web components integration", () => { + beforeEach(() => { mountUI5Fixtures(); }); + afterEach(() => { cleanupUI5Fixtures(); }); + + it("OpenUI5 Dialog: opening nested WebC ResponsivePopover and closing both with Escape", () => { + cy.get("#openUI5Button") + .should('be.visible') + .realClick(); + + cy.get("#openUI5Dialog1") + .should('be.visible'); + + cy.get("#openResPopoverButton") + .should('be.visible') + .realClick(); + + cy.get("#respPopover").ui5ResponsivePopoverOpened(); + + cy.realPress("Escape"); + + cy.get("#respPopover").ui5ResponsivePopoverClosed(); + + cy.get("#openUI5Dialog1") + .should('be.visible'); + + cy.realPress("Escape"); + + cy.get("#openUI5Dialog1") + .should('not.exist'); + + cy.get("#openUI5Button") + .should('be.focused'); + }); +}); diff --git a/packages/main/cypress/specs/OpenUI5andWebCPopups.openUI5DialogWebCPopoverNoFocus.cy.tsx b/packages/main/cypress/specs/OpenUI5andWebCPopups.openUI5DialogWebCPopoverNoFocus.cy.tsx new file mode 100644 index 000000000000..99ef87e93551 --- /dev/null +++ b/packages/main/cypress/specs/OpenUI5andWebCPopups.openUI5DialogWebCPopoverNoFocus.cy.tsx @@ -0,0 +1,43 @@ +import ResponsivePopover from "../../src/ResponsivePopover.js"; +import { mountUI5Fixtures, cleanupUI5Fixtures } from "./OpenUI5andWebCPopups.utils.js"; + +describe("ui5 and web components integration", () => { + beforeEach(() => { mountUI5Fixtures(); }); + afterEach(() => { cleanupUI5Fixtures(); }); + + it("OpenUI5 Dialog: opening WebC ResponsivePopover with preventInitialFocus and closing with Escape", () => { + cy.get("#openUI5Button", { timeout: 10000 }) + .should('be.visible') + .realClick(); + + cy.get("#openUI5Dialog1") + .should('be.visible'); + + cy.wait(1000); + + cy.get("#openResPopoverNoInitialFocusButton") + .should('be.visible') + .realClick(); + + cy.get("#respPopoverNoInitialFocus").ui5ResponsivePopoverOpened(); + + cy.realPress("Escape"); + + cy.get("#respPopoverNoInitialFocus") + .ui5ResponsivePopoverClosed(); + + cy.get("#openResPopoverNoInitialFocusButton") + .should('be.focused'); + + cy.get("#openUI5Dialog1") + .should('be.visible'); + + cy.realPress("Escape"); + + cy.get("#openUI5Dialog1") + .should('not.be.visible'); + + cy.get("#openUI5Button") + .should('be.focused'); + }); +}); diff --git a/packages/main/cypress/specs/OpenUI5andWebCPopups.openUI5DialogWebCSelect.cy.tsx b/packages/main/cypress/specs/OpenUI5andWebCPopups.openUI5DialogWebCSelect.cy.tsx new file mode 100644 index 000000000000..5a74de053f30 --- /dev/null +++ b/packages/main/cypress/specs/OpenUI5andWebCPopups.openUI5DialogWebCSelect.cy.tsx @@ -0,0 +1,43 @@ +import Dialog from "../../src/Dialog.js"; +import ResponsivePopover from "../../src/ResponsivePopover.js"; +import { mountUI5Fixtures, cleanupUI5Fixtures } from "./OpenUI5andWebCPopups.utils.js"; + +describe("ui5 and web components integration", () => { + beforeEach(() => { mountUI5Fixtures(); }); + afterEach(() => { cleanupUI5Fixtures(); }); + + it("OpenUI5 Dialog: opening WebC Select dropdown and closing with Escape", () => { + cy.get("#openUI5Button", { timeout: 10000 }) + .should('be.visible') + .realClick(); + + cy.get("#openUI5Dialog1") + .should('be.visible'); + + cy.get("#webCSelect1") + .should('be.visible') + .realClick(); + + cy.get("#webCSelect1") + .shadow() + .find("[ui5-responsive-popover]").ui5DialogOpened(); + + cy.realPress("Escape"); + + cy.get("#webCSelect1") + .shadow() + .find("[ui5-responsive-popover]") + .ui5ResponsivePopoverClosed(); + + cy.get("#openUI5Dialog1") + .should('be.visible'); + + cy.realPress("Escape"); + + cy.get("#openUI5Dialog1") + .should('not.exist'); + + cy.get("#openUI5Button") + .should('be.focused'); + }); +}); diff --git a/packages/main/cypress/specs/OpenUI5andWebCPopups.utils.tsx b/packages/main/cypress/specs/OpenUI5andWebCPopups.utils.tsx new file mode 100644 index 000000000000..5328916344f3 --- /dev/null +++ b/packages/main/cypress/specs/OpenUI5andWebCPopups.utils.tsx @@ -0,0 +1,322 @@ +import OpenUI5Support from "@ui5/webcomponents-base/dist/features/OpenUI5Support.js"; +import Button from "../../src/Button.js"; +import Dialog from "../../src/Dialog.js"; +import Select from "../../src/Select.js"; +import Option from "../../src/Option.js"; +import ComboBox from "../../src/ComboBox.js"; +import ComboBoxItem from "../../src/ComboBoxItem.js"; +import ResponsivePopover from "../../src/ResponsivePopover.js"; + + +let OpenUI5Element; + +function onOpenUI5InitMethod(win) { + (win as any).sap.ui.require([ + "sap/ui/core/Element", + "sap/ui/core/HTML", + "sap/m/Button", + "sap/m/Dialog", + "sap/m/Popover", + "sap/m/Input" + ], async (Element, HTML, Button, Dialog, Popover, Input) => { + + await OpenUI5Support.init(); + OpenUI5Element = Element; + new Button("openUI5Button", { + text: "Open OpenUI5 Dialog", + press: function () { + new Dialog("openUI5Dialog1", { + title: "OpenUI5 Dialog", + draggable: true, + content: [ + new HTML({ + content: + ` + Option 1 + Option 2 + Option 3 + Option 4 + Option 5 + + + + + + +

` + }), + new Input(), + new Button({ + text: "Focus stop" + }), + new Button("openResPopoverButton", { + text: "Open WebC Responsive Popover", + press: function () { + (document.getElementById("respPopover") as any).open = true; + } + }), + new Button("openResPopoverNoInitialFocusButton", { + text: "Open WebC RP with NO Initial Focus", + press: function () { + (document.getElementById("respPopoverNoInitialFocus") as any).open = true; + } + }), + new Button("openWebCDialog", { + text: "Open WebC Dialog", + press: function () { + (document.getElementById("webCDialog1") as any).open = true; + } + }) + ], + afterClose: function () { + this.destroy(); + } + }).open(); + } + }).placeAt("content"); + }); + + document.getElementById("myButton").addEventListener("click", function () { + (document.getElementById("dialog1") as any).open = true; + }); + + (win as any).sap.ui.require(["sap/m/Select", + "sap/m/ComboBox", + "sap/m/Button", + "sap/ui/core/Item", + "sap/ui/core/ShortcutHintsMixin"], + (Select, + ComboBox, + Button, + Item, + ShortcutHintsMixin) => { + new Select("openUI5Select1", { + items: [ + new Item({ text: "Item 1" }), + new Item({ text: "Item 2" }), + new Item({ text: "Item 3" }) + ], + change: function (oEvent) { + console.error("Selected item:", oEvent.getParameter("selectedItem").getText()); + } + }).placeAt("dialog1content"); + + new ComboBox("openUI5Combobox1", { + items: [ + new Item({ text: "Item 1" }), + new Item({ text: "Item 2" }), + new Item({ text: "Item 3" }) + ] + }).placeAt("dialog1content"); + + const button = new Button("openUI5ButtonWithHint", { + text: "OpenUI5 with Shortcut (Ctrl+S)", + press: function () { + openUI5Dialog(win); + } + }).placeAt("dialog1content"); + + + ShortcutHintsMixin.addConfig(button, { + event: "press", + position: "0 0", + addAccessibilityLabel: true, + message: "Save" + }, button); + }); + + document.getElementById("dialogButton").addEventListener("click", function () { + openUI5Dialog(win); + }); + + document.getElementById("openUI5DialogFromWebC").addEventListener("click", function () { + openUI5Dialog(win); + }); + + document.getElementById("popoverButtonNoFocus").addEventListener("click", function (event) { + openUI5Popover(win, event.target); + }); +} + +function openUI5Dialog(win) { + (win as any).sap.ui.require(["sap/m/Button", "sap/m/Dialog"], (Button, Dialog) => { + new Dialog("openUI5DialogWithButtons", { + title: "OpenUI5 Dialog", + draggable: true, + content: [ + new Button({ + text: "Focus stop" + }), + new Button("openUI5DialogButton", { + text: "Open WebC Dialog", + press: function () { + (document.getElementById("newDialog1") as any).open = true; + } + }), + new Button("openUI5DialogFromUi5", { + text: "Open UI5 Dialog", + press: function () { + openUI5DialogFromUi5(win) + } + }) + ], + afterClose: function () { + this.destroy(); + } + }).open(); + }); +} + +function openUI5DialogFromUi5(win) { + (win as any).sap.ui.require(["sap/m/Button", "sap/m/Dialog"], (Button, Dialog) => { + new Dialog("openUI5DialogFinal", { + title: "OpenUI5 Dialog", + draggable: true, + content: [ + new Button({ + text: "Focus stop" + }) + ], + afterClose: function () { + this.destroy(); + } + }).open(); + }); +} + +function openUI5Popover(win, opener) { + (win as any).sap.ui.require(["sap/m/Popover", "sap/m/Button"], (Popover, Button) => { + new Popover("openUI5PopoverSecond", { + title: "OpenUI5 Popover", + content: [ + new Button("someButton", { + text: "Open new OpenUI5 Popover", + press: function (oEvent) { + new Popover({ + title: "New OpenUI5 Popover", + placement: "Bottom", + content: [ + new Button({ + text: "Focus stop" + }) + ], + initialFocus: "someButton", + afterClose: function () { + this.destroy(); + } + }).openBy(oEvent.getSource()); + } + }) + ], + initialFocus: "popoverButtonNoFocus", + afterClose: function () { + this.destroy(); + } + }).openBy(opener); + }); +} + +export function isOpenUI5DialogOpen($dialog) { + expect(OpenUI5Element).to.exist; + + const dialogInstance = OpenUI5Element.getElementById($dialog.attr("id")); + + expect(dialogInstance).to.exist + expect(dialogInstance.isOpen()).to.be.true; +} + +export function mountUI5Fixtures() { + cy.mount( + <> +
+ +
+ +
+
+ Web Components: +
+ + + + + + +
+
+ + +
+ + + + + + +
+ + + + + + + + ); + + cy.window().then((win) => { + if (!window.sap?.ui) { + (win as any).onOpenUI5Init = function () { + onOpenUI5InitMethod(win); + }; + } else { + onOpenUI5InitMethod(win); + } + }); + + cy.document().then((doc) => { + const ui5Script = doc.createElement('script'); + ui5Script.src = 'https://ui5.sap.com/resources/sap-ui-core.js'; + ui5Script.id = 'sap-ui-bootstrap'; + ui5Script.setAttribute('data-sap-ui-libs', 'sap.m'); + ui5Script.setAttribute('data-sap-ui-oninit', 'onOpenUI5Init'); + doc.head.appendChild(ui5Script); + }); +} + +export function cleanupUI5Fixtures() { + cy.window().then((win) => { + const sap = (win as any).sap; + if (sap?.ui?.require) { + sap.ui.require(["sap/ui/core/Element"], (Element) => { + ["openUI5Button", "openUI5Select1", "openUI5Combobox1", "openUI5ButtonWithHint"].forEach((id) => { + const el = Element.getElementById(id); + if (el) { + el.destroy(); + } + }); + }); + } + }); + + cy.document().then((doc) => { + const ui5Script = doc.head.querySelector("[data-sap-ui-oninit]") as HTMLScriptElement; + if (ui5Script) { + ui5Script.remove(); + } + }); +} diff --git a/packages/main/cypress/specs/OpenUI5andWebCPopups.webCDialog.cy.tsx b/packages/main/cypress/specs/OpenUI5andWebCPopups.webCDialog.cy.tsx new file mode 100644 index 000000000000..50773646d010 --- /dev/null +++ b/packages/main/cypress/specs/OpenUI5andWebCPopups.webCDialog.cy.tsx @@ -0,0 +1,26 @@ +import Dialog from "../../src/Dialog.js"; +import { mountUI5Fixtures, cleanupUI5Fixtures } from "./OpenUI5andWebCPopups.utils.js"; + +describe("ui5 and web components integration", () => { + beforeEach(() => { mountUI5Fixtures(); }); + afterEach(() => { cleanupUI5Fixtures(); }); + + it("WebC Dialog opens and closes with Escape, focus returns to opener", () => { + cy.get("#openUI5Button", { timeout: 10000 }) + .should('be.visible'); + + cy.get('#myButton') + .should('be.visible') + .realClick(); + + cy.get("#dialog1").ui5DialogOpened(); + + cy.realPress("Escape"); + + cy.get('#dialog1') + .should('not.be.visible'); + + cy.get('#myButton') + .should('be.focused'); + }); +}); diff --git a/packages/main/cypress/specs/OpenUI5andWebCPopups.webCDialogComboBoxWithHint.cy.tsx b/packages/main/cypress/specs/OpenUI5andWebCPopups.webCDialogComboBoxWithHint.cy.tsx new file mode 100644 index 000000000000..d70afdb18337 --- /dev/null +++ b/packages/main/cypress/specs/OpenUI5andWebCPopups.webCDialogComboBoxWithHint.cy.tsx @@ -0,0 +1,62 @@ +import Dialog from "../../src/Dialog.js"; +import { mountUI5Fixtures, cleanupUI5Fixtures } from "./OpenUI5andWebCPopups.utils.js"; + +describe("ui5 and web components integration", () => { + beforeEach(() => { mountUI5Fixtures(); }); + afterEach(() => { cleanupUI5Fixtures(); }); + + it("WebC Dialog: opening OpenUI5 Dialog from button with shortcut hint while ComboBox popup is open", () => { + cy.get("#openUI5Button") + .should('be.visible'); + + cy.get('#myButton') + .should('be.visible') + .realClick(); + + cy.get("#dialog1").ui5DialogOpened(); + + cy.get('#openUI5Combobox1') + .should('be.visible') + .realClick() + .type("I"); + + cy.get("#openUI5Combobox1-popup") + .should('be.visible'); + + cy.get('#openUI5ButtonWithHint') + .should('be.visible') + .realClick(); + + cy.get("#openUI5Combobox1-popup") + .should('not.be.visible'); + + cy.get("#openUI5DialogWithButtons") + .should("be.visible"); + + cy.realPress("Escape"); + + cy.get("#openUI5DialogWithButtons") + .should("not.exist"); + + cy.get("#dialog1").ui5DialogOpened(); + + cy.get('#openUI5ButtonWithHint') + .should('be.focused') + + cy.get('#openUI5Combobox1') + .find('input') + .focus(); + + cy.get("#openUI5Combobox1") + .find('input') + .should('be.focused'); + + cy.realPress("Escape"); + + cy.get('#dialog1') + .should('not.be.visible'); + + cy.get('#myButton') + .should('be.focused'); + }); +}); diff --git a/packages/main/cypress/specs/OpenUI5andWebCPopups.webCDialogOpenUI5ComboBox.cy.tsx b/packages/main/cypress/specs/OpenUI5andWebCPopups.webCDialogOpenUI5ComboBox.cy.tsx new file mode 100644 index 000000000000..a94779725131 --- /dev/null +++ b/packages/main/cypress/specs/OpenUI5andWebCPopups.webCDialogOpenUI5ComboBox.cy.tsx @@ -0,0 +1,46 @@ +import Dialog from "../../src/Dialog.js"; +import { mountUI5Fixtures, cleanupUI5Fixtures } from "./OpenUI5andWebCPopups.utils.js"; + +describe("ui5 and web components integration", () => { + beforeEach(() => { mountUI5Fixtures(); }); + afterEach(() => { cleanupUI5Fixtures(); }); + + it("WebC Dialog: opening OpenUI5 ComboBox suggestion popup and closing with Escape", () => { + cy.get("#openUI5Button") + .should('be.visible'); + + cy.get('#myButton') + .should('be.visible') + .realClick(); + + cy.get("#dialog1").ui5DialogOpened(); + + cy.get('#openUI5Combobox1') + .should('be.visible') + .realClick() + .type("I"); + + cy.get("#openUI5Combobox1-popup") + .should('be.visible'); + + cy.realPress("Escape"); + + cy.get("#openUI5Combobox1-popup") + .should('not.be.visible'); + + cy.get("#dialog1").ui5DialogOpened(); + + cy.realPress("Escape"); + + // combo box value is reset, dialog stays open + cy.get("#dialog1").ui5DialogOpened(); + + cy.realPress("Escape"); + + cy.get('#dialog1') + .should('not.be.visible'); + + cy.get('#myButton') + .should('be.focused'); + }); +}); diff --git a/packages/main/cypress/specs/OpenUI5andWebCPopups.webCDialogOpenUI5Dialog.cy.tsx b/packages/main/cypress/specs/OpenUI5andWebCPopups.webCDialogOpenUI5Dialog.cy.tsx new file mode 100644 index 000000000000..7253f916f961 --- /dev/null +++ b/packages/main/cypress/specs/OpenUI5andWebCPopups.webCDialogOpenUI5Dialog.cy.tsx @@ -0,0 +1,41 @@ +import Dialog from "../../src/Dialog.js"; +import { mountUI5Fixtures, cleanupUI5Fixtures } from "./OpenUI5andWebCPopups.utils.js"; + +describe("ui5 and web components integration", () => { + beforeEach(() => { mountUI5Fixtures(); }); + afterEach(() => { cleanupUI5Fixtures(); }); + + it("WebC Dialog: opening nested OpenUI5 Dialog and closing both with Escape", () => { + cy.get("#openUI5Button") + .should('be.visible'); + + cy.get('#myButton') + .should('be.visible') + .realClick(); + + cy.get("#dialog1").ui5DialogOpened(); + + cy.get('#dialogButton') + .should('be.visible') + .realClick(); + + cy.get("#openUI5DialogWithButtons") + .should('be.visible'); + + cy.realPress("Escape"); + + cy.get("#openUI5DialogWithButtons") + .should('not.be.visible'); + + cy.get('#dialogButton') + .should('be.focused'); + + cy.realPress("Escape"); + + cy.get('#dialog1') + .should('not.be.visible'); + + cy.get('#myButton') + .should('be.focused'); + }); +}); diff --git a/packages/main/cypress/specs/OpenUI5andWebCPopups.webCDialogOpenUI5PopoverNoFocus.cy.tsx b/packages/main/cypress/specs/OpenUI5andWebCPopups.webCDialogOpenUI5PopoverNoFocus.cy.tsx new file mode 100644 index 000000000000..32820723e9d7 --- /dev/null +++ b/packages/main/cypress/specs/OpenUI5andWebCPopups.webCDialogOpenUI5PopoverNoFocus.cy.tsx @@ -0,0 +1,43 @@ +import Dialog from "../../src/Dialog.js"; +import { mountUI5Fixtures, cleanupUI5Fixtures } from "./OpenUI5andWebCPopups.utils.js"; + +describe("ui5 and web components integration", () => { + beforeEach(() => { mountUI5Fixtures(); }); + afterEach(() => { cleanupUI5Fixtures(); }); + + it("WebC Dialog: opening OpenUI5 Popover with no initial focus and closing with Escape", () => { + cy.get("#openUI5Button") + .should('be.visible'); + + cy.get('#myButton') + .should('be.visible') + .realClick(); + + cy.get("#dialog1").ui5DialogOpened(); + + cy.get('#popoverButtonNoFocus') + .should('be.visible') + .realClick(); + + cy.get("#openUI5PopoverSecond") + .should('be.visible'); + + cy.realPress("Escape"); + + cy.get("#openUI5PopoverSecond") + .should('not.exist'); + + cy.realPress(["Shift", "Tab"]); + + cy.get('#dialogButton') + .should('be.focused'); + + cy.realPress("Escape"); + + cy.get('#dialog1') + .should('not.be.visible'); + + cy.get('#myButton') + .should('be.focused'); + }); +}); diff --git a/packages/main/cypress/specs/OpenUI5andWebCPopups.webCDialogOpenUI5Select.cy.tsx b/packages/main/cypress/specs/OpenUI5andWebCPopups.webCDialogOpenUI5Select.cy.tsx new file mode 100644 index 000000000000..8a4a87793764 --- /dev/null +++ b/packages/main/cypress/specs/OpenUI5andWebCPopups.webCDialogOpenUI5Select.cy.tsx @@ -0,0 +1,38 @@ +import Dialog from "../../src/Dialog.js"; +import { mountUI5Fixtures, cleanupUI5Fixtures } from "./OpenUI5andWebCPopups.utils.js"; + +describe("ui5 and web components integration", () => { + beforeEach(() => { mountUI5Fixtures(); }); + afterEach(() => { cleanupUI5Fixtures(); }); + + it("WebC Dialog: opening OpenUI5 Select dropdown and closing with Escape", () => { + cy.get("#openUI5Button") + .should('be.visible'); + + cy.get('#myButton') + .should('be.visible') + .realClick(); + + cy.get("#dialog1").ui5DialogOpened(); + + cy.get('#openUI5Select1') + .should('be.visible') + .realClick(); + + cy.get("#__popover0") + .should('be.visible'); + + cy.realPress("Escape"); + + cy.get("#__popover0") + .should('not.be.visible'); + + cy.realPress("Escape"); + + cy.get('#dialog1') + .should('not.be.visible'); + + cy.get('#myButton') + .should('be.focused'); + }); +}); From 749da58fe105621fed92a36268f46055133b00d7 Mon Sep 17 00:00:00 2001 From: Nayden Naydenov Date: Wed, 27 May 2026 16:22:37 +0300 Subject: [PATCH 2/2] chore: restore date tests files --- .../base/src/asset-registries/LocaleData.ts | 7 ------- .../src/sap/base/util/LoaderExtensions.ts | 18 +++--------------- 2 files changed, 3 insertions(+), 22 deletions(-) diff --git a/packages/base/src/asset-registries/LocaleData.ts b/packages/base/src/asset-registries/LocaleData.ts index 821f0e14e107..0245147742d3 100644 --- a/packages/base/src/asset-registries/LocaleData.ts +++ b/packages/base/src/asset-registries/LocaleData.ts @@ -101,11 +101,6 @@ const getLocaleData = (localeId: string) => { return content; }; -// resolves the effective locale ID that getLocaleData / _loadCldrOnce would use -const getEffectiveLocaleId = (localeId: string) => { - return loaders.has(localeId) ? localeId : DEFAULT_LOCALE; -}; - // load bundle over the network once const _loadCldrOnce = (localeId: string) => { if (!cldrPromises.get(localeId)) { @@ -172,8 +167,6 @@ export { registerLocaleDataLoader, fetchCldr, getLocaleData, - getEffectiveLocaleId, - _loadCldrOnce, }; export type { diff --git a/packages/localization/src/sap/base/util/LoaderExtensions.ts b/packages/localization/src/sap/base/util/LoaderExtensions.ts index 2fa35635c370..cd556402b8ed 100644 --- a/packages/localization/src/sap/base/util/LoaderExtensions.ts +++ b/packages/localization/src/sap/base/util/LoaderExtensions.ts @@ -1,4 +1,4 @@ -import { getLocaleData, getEffectiveLocaleId, _loadCldrOnce } from "@ui5/webcomponents-base/dist/asset-registries/LocaleData.js"; +import { getLocaleData } from "@ui5/webcomponents-base/dist/asset-registries/LocaleData.js"; const loadResource = (moduleName: string) => { const moduleFormat = moduleName.match(/sap\/ui\/core\/cldr\/(\w+)\.json/); @@ -6,20 +6,8 @@ const loadResource = (moduleName: string) => { throw new Error(`Unknown module "${moduleName}"`); } - // OpenUI5's loadData builds locale IDs as "_" which can produce - // trailing underscores (e.g. "bg_" for Bulgarian with no region). Strip the trailing - // underscore so the ID matches what fetchCldr stores in localeDataMap (e.g. "bg"). - const localeId = moduleFormat[1].replace(/_$/, ""); - - // If the data is already in localeDataMap (fetchCldr resolved), return it synchronously - // so SyncPromise resolves immediately and mData is set in the LocaleData constructor. - // If fetchCldr is still in-flight, return its in-progress Promise so SyncPromise can - // resolve asynchronously — instead of throwing and leaving mData permanently unset. - try { - return getLocaleData(localeId); - } catch { - return _loadCldrOnce(getEffectiveLocaleId(localeId)); - } + const localeId = moduleFormat[1]; + return getLocaleData(localeId); }; const LoaderExtensions = {