diff --git a/packages/fiori/cypress/specs/UserMenu.cy.tsx b/packages/fiori/cypress/specs/UserMenu.cy.tsx index a9f0ae62ea6ed..c0b4f03e2602d 100644 --- a/packages/fiori/cypress/specs/UserMenu.cy.tsx +++ b/packages/fiori/cypress/specs/UserMenu.cy.tsx @@ -877,6 +877,22 @@ describe("Events", () => { cy.get("@opened").should("have.been.calledOnce"); }); + it("focuses first menu item after open", () => { + cy.mount( + <> + + + + + + + + ); + + cy.get("[ui5-user-menu-item][text='Setting']") + .should("be.focused"); + }); + it("tests close event", () => { cy.mount( <> @@ -970,7 +986,74 @@ describe("Responsiveness", () => { .scrollTo("bottom"); cy.get("[ui5-user-menu]").shadow().find("[ui5-bar]").as("headerBar"); cy.get("@headerBar").find("[ui5-title]").contains("Alain Chevalier 1"); - cy.get("@headerBar").find("[ui5-button]").should("have.length", 1); + cy.get("@headerBar").find("[ui5-button][slot='endContent']").should("have.length", 1); + }); + + it("submenu header on phone has close button in UserMenuItem", () => { + cy.ui5SimulateDevice("phone"); + cy.mount( + <> + + + + + + + + + ); + + cy.get("[ui5-user-menu-item][text='Settings']") + .ui5MenuItemClick(); + + cy.get("[ui5-user-menu-item][text='Settings']") + .shadow() + .find("[ui5-responsive-popover]") + .should("have.attr", "open"); + + cy.get("[ui5-user-menu-item][text='Settings']") + .shadow() + .find(".ui5-menu-close-button") + .should("exist"); + }); + + it("submenu header on desktop has no close button in UserMenuItem", () => { + cy.mount( + <> + + + + + + + + + ); + + cy.get("[ui5-user-menu-item][text='Settings']") + .shadow() + .find(".ui5-menu-close-button") + .should("not.exist"); + }); + + it("popover header has no divider line (::before pseudo-element hidden)", () => { + cy.mount( + <> + + + + + + ); + + cy.get("[ui5-user-menu]").shadow() + .find("[ui5-responsive-popover]") + .shadow() + .find(".ui5-popup-header-root") + .then($el => { + const before = window.getComputedStyle($el[0], "::before"); + expect(before.display).to.equal("none"); + }); }); it("Event firing - 'ui5-check' after 'click' on user menu item", () => { @@ -1315,6 +1398,26 @@ describe("InfoArea slot", () => { }); }); }); + + it("info-area has 8px padding on all sides", () => { + cy.mount( + <> + + + + + All actions are recorded under the proxy audit log. + + + + ); + + cy.get("[ui5-user-menu]").shadow().find(".ui5-user-menu-info-area") + .should("have.css", "padding-top", "8px") + .and("have.css", "padding-bottom", "8px") + .and("have.css", "padding-left", "8px") + .and("have.css", "padding-right", "8px"); + }); }); describe("UserMenuItem", () => { @@ -1580,7 +1683,7 @@ describe("UserMenuItem", () => { .should("not.have.attr", "show-selection"); }); - it("selection text has correct styling", () => { + it("selection text wraps instead of truncating", () => { cy.mount( <> @@ -1598,9 +1701,8 @@ describe("UserMenuItem", () => { .shadow() .find(".ui5-user-menu-item-selection-text") .should("have.css", "font-weight", "400") - .and("have.css", "white-space", "nowrap") - .and("have.css", "overflow", "hidden") - .and("have.css", "text-overflow", "ellipsis"); + .and("not.have.css", "white-space", "nowrap") + .and("not.have.css", "text-overflow", "ellipsis"); }); it("text wrapper has column layout with gap", () => { diff --git a/packages/fiori/src/UserMenu.ts b/packages/fiori/src/UserMenu.ts index e8b26d0be842a..ef89551a20364 100644 --- a/packages/fiori/src/UserMenu.ts +++ b/packages/fiori/src/UserMenu.ts @@ -433,6 +433,7 @@ class UserMenu extends UI5Element { this._titleMovedToHeader = false; this._isScrolled = false; this._setupObserver(); + this._menuItems[0]?.getFocusDomRef()?.focus(); this.fireDecoratorEvent("open"); } diff --git a/packages/fiori/src/UserMenuItemTemplate.tsx b/packages/fiori/src/UserMenuItemTemplate.tsx index ff561b1bb1627..bb4813c5e53da 100644 --- a/packages/fiori/src/UserMenuItemTemplate.tsx +++ b/packages/fiori/src/UserMenuItemTemplate.tsx @@ -1,17 +1,163 @@ import type UserMenuItem from "./UserMenuItem.js"; import MenuItemTemplate from "@ui5/webcomponents/dist/MenuItemTemplate.js"; import type { MenuItemHooks } from "@ui5/webcomponents/dist/MenuItemTemplate.js"; +import ListItemTemplate from "@ui5/webcomponents/dist/ListItemTemplate.js"; +import type { ListItemHooks } from "@ui5/webcomponents/dist/ListItemTemplate.js"; +import ResponsivePopover from "@ui5/webcomponents/dist/ResponsivePopover.js"; +import Button from "@ui5/webcomponents/dist/Button.js"; +import List from "@ui5/webcomponents/dist/List.js"; +import BusyIndicator from "@ui5/webcomponents/dist/BusyIndicator.js"; +import Icon from "@ui5/webcomponents/dist/Icon.js"; +import PopoverPlacement from "@ui5/webcomponents/dist/types/PopoverPlacement.js"; +import navBackIcon from "@ui5/webcomponents-icons/dist/nav-back.js"; +import declineIcon from "@ui5/webcomponents-icons/dist/decline.js"; +import checkIcon from "@ui5/webcomponents-icons/dist/accept.js"; +import slimArrowRight from "@ui5/webcomponents-icons/dist/slim-arrow-right.js"; export default function UserMenuItemTemplate(this: UserMenuItem) { - const hooks: Partial = {}; + if (this.isPhone) { + return phoneTemplate.call(this); + } + const hooks: Partial = {}; if (this.showSelection) { hooks.menuItemTextContent = userMenuItemTextContent; } - return [MenuItemTemplate.call(this, hooks)]; } +function phoneTemplate(this: UserMenuItem) { + const hooks: Partial = { + iconBegin(this: UserMenuItem) { + if (this.hasIcon) { + return ; + } + if (this._siblingsWithIcon) { + return
; + } + }, + listItemContent(this: UserMenuItem) { + return (<> + {this.showSelection ? ( +
+ {this.text &&
{this.text}
} + {this._selectedSubItemText && +
{this._selectedSubItemText}
+ } +
+ ) : ( + <>{this.text &&
{this.text}
} + )} + {rightContent.call(this)} + {checkmarkContent.call(this)} + ); + }, + }; + + return [ + ListItemTemplate.call(this, hooks), + phoneSubmenuPopover.call(this), + ]; +} + +function checkmarkContent(this: UserMenuItem) { + return !this._markChecked ? "" : ( +
+ +
+ ); +} + +function rightContent(this: UserMenuItem) { + switch (true) { + case this.hasSubmenu: + return ( +
+ +
+ ); + case this.hasEndContent: + return ( +
+ +
+ ); + case !!this.additionalText: + return ( + + {this.additionalText} + + ); + } +} + +function phoneSubmenuPopover(this: UserMenuItem) { + return this.hasSubmenu && +
+
+ +
+ {this.items.length ? ( + + + + ) : this.loading && } +
+
; +} + function userMenuItemTextContent(this: UserMenuItem) { return (
diff --git a/packages/fiori/src/themes/UserMenu.css b/packages/fiori/src/themes/UserMenu.css index f70d5066a0711..add0f4a0c5ed2 100644 --- a/packages/fiori/src/themes/UserMenu.css +++ b/packages/fiori/src/themes/UserMenu.css @@ -12,6 +12,14 @@ padding: 0; } +.ui5-user-menu-rp::part(header)::before { + display: none; +} + +.ui5-user-menu-rp { + --_ui5_popup_header_shadow: none; +} + .ui5-user-menu-header { display: flex; flex-direction: column; @@ -25,11 +33,23 @@ box-shadow: none; } +.ui5-user-menu-fixed-header::part(bar) { + position: relative; +} + .ui5-user-menu-fixed-header::part(startContent), .ui5-user-menu-fixed-header::part(endContent) { padding: 0; } +.ui5-user-menu-fixed-header::part(midContent) { + position: absolute; + left: 50%; + transform: translateX(-50%); + justify-content: center; + pointer-events: none; +} + .ui5-user-menu-fixed-header [ui5-button] { margin-inline: 0.5rem; font-family: var(--sapFontSemiboldDuplexFamily); @@ -178,8 +198,7 @@ flex-direction: column; align-self: stretch; margin-block: 0.5rem; - padding-block: 0.5rem; - padding-inline: 0.5rem; + padding: 0.5rem; margin-inline: -0.5rem; } diff --git a/packages/fiori/src/themes/UserMenuItem.css b/packages/fiori/src/themes/UserMenuItem.css index e06fb4b8a7a04..bdea891428b24 100644 --- a/packages/fiori/src/themes/UserMenuItem.css +++ b/packages/fiori/src/themes/UserMenuItem.css @@ -40,7 +40,10 @@ font-size: var(--sapFontSize); font-weight: normal; color: var(--sapContent_LabelColor); - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; + white-space: normal; + text-overflow: initial; +} + +.ui5-menu-close-button { + margin-left: auto; } \ No newline at end of file diff --git a/packages/fiori/test/pages/UXCIntegration.html b/packages/fiori/test/pages/UXCIntegration.html index 183b5c3b68289..788341f94a0f3 100644 --- a/packages/fiori/test/pages/UXCIntegration.html +++ b/packages/fiori/test/pages/UXCIntegration.html @@ -155,18 +155,19 @@ subtitle-text="david.wilson@sap.com" description="Project Manager"> - + + + + + + + + - - - - - - diff --git a/packages/fiori/test/pages/UserMenu.html b/packages/fiori/test/pages/UserMenu.html index cd24e1553222e..341cbf5b4a4a3 100644 --- a/packages/fiori/test/pages/UserMenu.html +++ b/packages/fiori/test/pages/UserMenu.html @@ -55,12 +55,19 @@ subtitle-text="alian.chevalier@ariba.com" description="Account Manager"> - + + + + + + + + @@ -87,7 +94,7 @@ description="Delivery Manager, SAP SE" selected> - + @@ -107,7 +114,7 @@ description="Delivery Manager, SAP SE" selected> - + @@ -146,7 +153,7 @@ subtitle-text="david.wilson@sap.com" description="Account Manager"> - + Text Direction diff --git a/packages/fiori/test/pages/UserSettingsDialog.html b/packages/fiori/test/pages/UserSettingsDialog.html index 888b602a220b8..416d6b9610393 100644 --- a/packages/fiori/test/pages/UserSettingsDialog.html +++ b/packages/fiori/test/pages/UserSettingsDialog.html @@ -74,7 +74,14 @@ description="Delivery Manager, SAP SE" selected> - + + + + + + + +
diff --git a/packages/website/docs/_samples/fiori/UserMenu/Advanced/main.js b/packages/website/docs/_samples/fiori/UserMenu/Advanced/main.js index 53da0eb45df3f..b430284532032 100644 --- a/packages/website/docs/_samples/fiori/UserMenu/Advanced/main.js +++ b/packages/website/docs/_samples/fiori/UserMenu/Advanced/main.js @@ -14,6 +14,7 @@ import "@ui5/webcomponents-icons/dist/official-service.js"; import "@ui5/webcomponents-icons/dist/private.js"; import "@ui5/webcomponents-icons/dist/accelerated.js"; import "@ui5/webcomponents-icons/dist/message-information.js"; +import "@ui5/webcomponents-icons/dist/home.js"; const shellbar = document.getElementById("shellbar"); const menu = document.getElementById("userMenu"); diff --git a/packages/website/docs/_samples/fiori/UserMenu/Advanced/sample.html b/packages/website/docs/_samples/fiori/UserMenu/Advanced/sample.html index be3af70dfc0c4..eada630c0cd08 100644 --- a/packages/website/docs/_samples/fiori/UserMenu/Advanced/sample.html +++ b/packages/website/docs/_samples/fiori/UserMenu/Advanced/sample.html @@ -55,19 +55,20 @@ subtitle-text="davud,wilson@sap.com" description="Project Manager"> - + - - - + - - + + + + + diff --git a/packages/website/docs/_samples/fiori/UserMenu/Advanced/sample.tsx b/packages/website/docs/_samples/fiori/UserMenu/Advanced/sample.tsx index 1d736c4a16536..87db04082695b 100644 --- a/packages/website/docs/_samples/fiori/UserMenu/Advanced/sample.tsx +++ b/packages/website/docs/_samples/fiori/UserMenu/Advanced/sample.tsx @@ -15,6 +15,7 @@ import "@ui5/webcomponents-icons/dist/collaborate.js"; import "@ui5/webcomponents-icons/dist/official-service.js"; import "@ui5/webcomponents-icons/dist/private.js"; import "@ui5/webcomponents-icons/dist/accelerated.js"; +import "@ui5/webcomponents-icons/dist/home.js"; const ShellBar = createReactComponent(ShellBarClass); const ShellBarBranding = createReactComponent(ShellBarBrandingClass); @@ -149,7 +150,7 @@ function App() { subtitleText="davud,wilson@sap.com" description="Project Manager" /> - + - - - + - - + + + + + ); diff --git a/packages/website/docs/_samples/fiori/UserMenu/Basic/sample.html b/packages/website/docs/_samples/fiori/UserMenu/Basic/sample.html index 19990da21f7ce..416fd2ea92a2b 100644 --- a/packages/website/docs/_samples/fiori/UserMenu/Basic/sample.html +++ b/packages/website/docs/_samples/fiori/UserMenu/Basic/sample.html @@ -39,7 +39,7 @@ description="Delivery Manager" selected> - + diff --git a/packages/website/docs/_samples/fiori/UserMenu/Basic/sample.tsx b/packages/website/docs/_samples/fiori/UserMenu/Basic/sample.tsx index bc89116b4e8aa..15193d00261d0 100644 --- a/packages/website/docs/_samples/fiori/UserMenu/Basic/sample.tsx +++ b/packages/website/docs/_samples/fiori/UserMenu/Basic/sample.tsx @@ -102,7 +102,7 @@ function App() { description="Delivery Manager" selected={true} /> - + - + + + + + + + + diff --git a/packages/website/docs/_samples/fiori/UserSettingsDialog/Basic/sample.tsx b/packages/website/docs/_samples/fiori/UserSettingsDialog/Basic/sample.tsx index 6aa4908a07e84..86da48102f6c6 100644 --- a/packages/website/docs/_samples/fiori/UserSettingsDialog/Basic/sample.tsx +++ b/packages/website/docs/_samples/fiori/UserSettingsDialog/Basic/sample.tsx @@ -8,6 +8,7 @@ import ShellBarBrandingClass from "@ui5/webcomponents-fiori/dist/ShellBarBrandin import UserMenuClass from "@ui5/webcomponents-fiori/dist/UserMenu.js"; import UserMenuAccountClass from "@ui5/webcomponents-fiori/dist/UserMenuAccount.js"; import UserMenuItemClass from "@ui5/webcomponents-fiori/dist/UserMenuItem.js"; +import UserMenuItemGroupClass from "@ui5/webcomponents-fiori/dist/UserMenuItemGroup.js"; import UserSettingsAccountViewClass from "@ui5/webcomponents-fiori/dist/UserSettingsAccountView.js"; import UserSettingsAppearanceViewClass from "@ui5/webcomponents-fiori/dist/UserSettingsAppearanceView.js"; import UserSettingsAppearanceViewGroupClass from "@ui5/webcomponents-fiori/dist/UserSettingsAppearanceViewGroup.js"; @@ -35,12 +36,14 @@ import "@ui5/webcomponents-icons/dist/iphone.js"; import "@ui5/webcomponents-icons/dist/qr-code.js"; import "@ui5/webcomponents-icons/dist/bell.js"; import "@ui5/webcomponents-icons/dist/reset.js"; +import "@ui5/webcomponents-icons/dist/home.js"; const ShellBar = createReactComponent(ShellBarClass); const ShellBarBranding = createReactComponent(ShellBarBrandingClass); const UserMenu = createReactComponent(UserMenuClass); const UserMenuAccount = createReactComponent(UserMenuAccountClass); const UserMenuItem = createReactComponent(UserMenuItemClass); +const UserMenuItemGroup = createReactComponent(UserMenuItemGroupClass); const UserSettingsAccountView = createReactComponent(UserSettingsAccountViewClass); const UserSettingsAppearanceView = createReactComponent( UserSettingsAppearanceViewClass, @@ -272,7 +275,14 @@ function App() { description="Delivery Manager, SAP SE" selected={true} /> - + + + + + + + + - + + + + + + + + diff --git a/packages/website/docs/_samples/fiori/UserSettingsDialog/SaveMode/sample.tsx b/packages/website/docs/_samples/fiori/UserSettingsDialog/SaveMode/sample.tsx index 72d39a00d5281..bf31a776e4b3f 100644 --- a/packages/website/docs/_samples/fiori/UserSettingsDialog/SaveMode/sample.tsx +++ b/packages/website/docs/_samples/fiori/UserSettingsDialog/SaveMode/sample.tsx @@ -8,6 +8,7 @@ import ShellBarBrandingClass from "@ui5/webcomponents-fiori/dist/ShellBarBrandin import UserMenuClass from "@ui5/webcomponents-fiori/dist/UserMenu.js"; import UserMenuAccountClass from "@ui5/webcomponents-fiori/dist/UserMenuAccount.js"; import UserMenuItemClass from "@ui5/webcomponents-fiori/dist/UserMenuItem.js"; +import UserMenuItemGroupClass from "@ui5/webcomponents-fiori/dist/UserMenuItemGroup.js"; import UserSettingsAccountViewClass from "@ui5/webcomponents-fiori/dist/UserSettingsAccountView.js"; import UserSettingsAppearanceViewClass from "@ui5/webcomponents-fiori/dist/UserSettingsAppearanceView.js"; import UserSettingsAppearanceViewGroupClass from "@ui5/webcomponents-fiori/dist/UserSettingsAppearanceViewGroup.js"; @@ -32,12 +33,14 @@ import "@ui5/webcomponents-icons/dist/user-settings.js"; import "@ui5/webcomponents-icons/dist/person-placeholder.js"; import "@ui5/webcomponents-icons/dist/palette.js"; import "@ui5/webcomponents-icons/dist/reset.js"; +import "@ui5/webcomponents-icons/dist/home.js"; const ShellBar = createReactComponent(ShellBarClass); const ShellBarBranding = createReactComponent(ShellBarBrandingClass); const UserMenu = createReactComponent(UserMenuClass); const UserMenuAccount = createReactComponent(UserMenuAccountClass); const UserMenuItem = createReactComponent(UserMenuItemClass); +const UserMenuItemGroup = createReactComponent(UserMenuItemGroupClass); const UserSettingsAccountView = createReactComponent(UserSettingsAccountViewClass); const UserSettingsAppearanceView = createReactComponent( UserSettingsAppearanceViewClass, @@ -236,7 +239,14 @@ function App() { description="Delivery Manager, SAP SE" selected={true} /> - + + + + + + + +