Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 126 additions & 0 deletions packages/fiori/cypress/specs/UserMenu.cy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import UserMenuItemGroup from "../../src/UserMenuItemGroup.js";

import actionSettings from "@ui5/webcomponents-icons/dist/action-settings.js";
import Button from "@ui5/webcomponents/dist/Button.js";
import MessageStrip from "@ui5/webcomponents/dist/MessageStrip.js";

import {
USER_MENU_MANAGE_ACCOUNT_BUTTON_TXT,
Expand Down Expand Up @@ -1106,6 +1107,131 @@ describe("Footer configuration", () => {
});
});

describe("InfoArea slot", () => {
it("does not render the info-area wrapper when slot is empty", () => {
cy.mount(
<>
<Button id="openUserMenuBtn">Open User Menu</Button>
<UserMenu open={true} opener="openUserMenuBtn">
<UserMenuAccount slot="accounts" titleText="Alain Chevalier"></UserMenuAccount>
</UserMenu>
</>
);
cy.get("[ui5-user-menu]").shadow().find(".ui5-user-menu-info-area").should("not.exist");
});

it("renders the info-area wrapper when a child is slotted", () => {
cy.mount(
<>
<Button id="openUserMenuBtn">Open User Menu</Button>
<UserMenu open={true} opener="openUserMenuBtn">
<UserMenuAccount slot="accounts" titleText="Alain Chevalier"></UserMenuAccount>
<MessageStrip slot="infoArea" design="Information" hideCloseButton={true}>
Proxy session active for jane.doe@sap.com
</MessageStrip>
</UserMenu>
</>
);
cy.get("[ui5-user-menu]").shadow().find(".ui5-user-menu-info-area").should("exist");
cy.get("[ui5-user-menu]").find("[ui5-message-strip][slot='infoArea']").should("exist");
});

it("renders info-area between additional info and manage account button (DOM order)", () => {
cy.mount(
<>
<Button id="openUserMenuBtn">Open User Menu</Button>
<UserMenu open={true} opener="openUserMenuBtn" showManageAccount={true}>
<UserMenuAccount slot="accounts" titleText="Alain Chevalier" additionalInfo="Primary Employment"></UserMenuAccount>
<MessageStrip slot="infoArea" design="Information" hideCloseButton={true}>Proxy</MessageStrip>
</UserMenu>
</>
);
cy.get("[ui5-user-menu]").shadow().then($host => {
const all = Array.from($host[0].querySelectorAll(".ui5-user-menu-selected-account-additional-info, .ui5-user-menu-info-area, #selected-account-manage-btn"));
expect(all.map(el => el.classList.contains("ui5-user-menu-selected-account-additional-info") ? "additional" : el.classList.contains("ui5-user-menu-info-area") ? "info" : "btn"))
.to.deep.equal(["additional", "info", "btn"]);
});
});

it("multi-line strip grows and pushes the manage-account button down", () => {
const longText =
"You are working on behalf of Jane Doe (jane.doe@sap.com). " +
"All actions performed in this session are recorded under the proxy audit log. " +
"Switch back to your own account from the Other Accounts section to leave this session.";

cy.mount(
<>
<Button id="openUserMenuBtn">Open User Menu</Button>
<UserMenu open={true} opener="openUserMenuBtn" showManageAccount={true}>
<UserMenuAccount slot="accounts" titleText="Alain Chevalier"></UserMenuAccount>
<MessageStrip slot="infoArea" design="Information" hideCloseButton={true}>
{longText}
</MessageStrip>
</UserMenu>
</>
);

cy.get("[ui5-user-menu]").shadow().find(".ui5-user-menu-info-area")
.invoke("outerHeight")
.should("be.greaterThan", 60);

cy.get("[ui5-user-menu]").shadow().find(".ui5-user-menu-info-area").then($info => {
const infoBottom = $info.get(0).getBoundingClientRect().bottom;
cy.get("[ui5-user-menu]").shadow().find("#selected-account-manage-btn").then($btn => {
const btnTop = $btn.get(0).getBoundingClientRect().top;
expect(btnTop).to.be.gte(infoBottom - 1);
});
});
});

it("does not break the title-flickering observer on scroll when slot is populated", () => {
cy.mount(
<>
<Button id="openUserMenuBtn">Open User Menu</Button>
<UserMenu open={true} opener="openUserMenuBtn" showManageAccount={true}>
<UserMenuAccount slot="accounts" titleText="Alain Chevalier"></UserMenuAccount>
<MessageStrip slot="infoArea" design="Information" hideCloseButton={true}>Proxy</MessageStrip>
{Array.from({ length: 25 }, (_, i) =>
<UserMenuItem key={i} text={`Setting ${i + 1}`} data-id={`setting${i + 1}`}></UserMenuItem>
)}
</UserMenu>
</>
);

cy.get("[ui5-user-menu]")
.shadow()
.find("[ui5-responsive-popover]")
.shadow()
.find('div[part="content"]')
.scrollTo("bottom");

cy.get("[ui5-user-menu]").shadow().find("[ui5-bar]").as("headerBar");
cy.get("@headerBar").find("[ui5-title]").contains("Alain Chevalier");
});

it("renders correctly without manage-account button", () => {
cy.mount(
<>
<Button id="openUserMenuBtn">Open User Menu</Button>
<UserMenu open={true} opener="openUserMenuBtn">
<UserMenuAccount slot="accounts" titleText="Alain Chevalier" additionalInfo="Primary Employment"></UserMenuAccount>
<MessageStrip slot="infoArea" design="Information" hideCloseButton={true}>Proxy</MessageStrip>
</UserMenu>
</>
);
cy.get("[ui5-user-menu]").shadow().find(".ui5-user-menu-info-area").should("exist");
cy.get("[ui5-user-menu]").shadow().find("#selected-account-manage-btn").should("not.exist");

cy.get("[ui5-user-menu]").shadow().find(".ui5-user-menu-selected-account-additional-info").then($info => {
const additionalBottom = $info.get(0).getBoundingClientRect().bottom;
cy.get("[ui5-user-menu]").shadow().find(".ui5-user-menu-info-area").then($area => {
const areaTop = $area.get(0).getBoundingClientRect().top;
expect(areaTop).to.be.gte(additionalBottom);
});
});
});
});

describe("UserMenuItem", () => {
describe("showSelection property", () => {
it("renders two-line layout when showSelection is true and sub-item is checked", () => {
Expand Down
15 changes: 15 additions & 0 deletions packages/fiori/src/UserMenu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,17 @@ class UserMenu extends UI5Element {
@slot()
footer!: Slot<HTMLElement>;

/**
* Defines the content of the info area inside the User Menu's account block.
*
* **Note:** When empty, the User Menu renders unchanged.
*
* @public
* @since 2.24.0
*/
@slot()
infoArea!: Slot<HTMLElement>;

@i18n("@ui5/webcomponents-fiori")
static i18nBundle: I18nBundle;

Expand Down Expand Up @@ -521,6 +532,10 @@ class UserMenu extends UI5Element {
return this.footer.length === 0;
}

get _hasInfoArea(): boolean {
return this.infoArea.length > 0;
}

getAccountDescriptionText(account: UserMenuAccount) {
return `${account.titleText} ${account.subtitleText} ${account.description} ${account.selected ? UserMenu.i18nBundle.getText(USER_MENU_POPOVER_ACCESSIBLE_ACCOUNT_SELECTED_TXT) : ""}`;
}
Expand Down
6 changes: 6 additions & 0 deletions packages/fiori/src/UserMenuTemplate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,12 @@ function headerContent(this: UserMenu) {
<Text class="ui5-user-menu-selected-account-additional-info">{this._selectedAccount.additionalInfo}</Text>
}

{this._hasInfoArea &&
<div class="ui5-user-menu-info-area">
<slot name="infoArea"></slot>
</div>
}

{this.showManageAccount &&
<Button id="selected-account-manage-btn" icon={userSettings} class="ui5-user-menu-manage-account-btn" onClick={this._handleManageAccountClick}>{this._manageAccountButtonText}</Button>
}
Expand Down
20 changes: 19 additions & 1 deletion packages/fiori/src/themes/UserMenu.css
Original file line number Diff line number Diff line change
Expand Up @@ -171,4 +171,22 @@
flex: 1;
justify-content: flex-end;
align-items: center;
}
}

.ui5-user-menu-info-area {
display: flex;
flex-direction: column;
align-self: stretch;
margin-block: 0.5rem;
padding-block: 0.5rem;
padding-inline: 0.5rem;
margin-inline: -0.5rem;
}

.ui5-user-menu-selected-account-subtitleText:has(+ .ui5-user-menu-info-area) {
margin-bottom: 0;
}

.ui5-user-menu-info-area + .ui5-user-menu-manage-account-btn {
margin-block-start: 0;
}
37 changes: 37 additions & 0 deletions packages/fiori/test/pages/UserMenu.html
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
#btnOpenUserMenuCustomFooter {
float: right;
}
#btnOpenUserMenuInfoArea {
float: right;
}
</style>
</head>

Expand Down Expand Up @@ -117,6 +120,35 @@
<ui5-button design="Transparent" icon="log">Sign Out</ui5-button>
</div>
</ui5-user-menu>

<ui5-button id="btnOpenUserMenuInfoArea">User menu with proxy banner</ui5-button>
<ui5-user-menu id="userMenuInfoArea" opener="btnOpenUserMenuInfoArea" show-manage-account show-other-accounts show-edit-accounts>
<ui5-message-strip slot="infoArea" design="Information" hide-close-button>
All actions are recorded under the proxy audit log.
</ui5-message-strip>
<ui5-user-menu-account slot="accounts"
avatar-src="./img/man_avatar_1.png"
title-text="Alain Chevalier"
subtitle-text="alain.chevalier@sap.com"
description="Delivery Manager, SAP SE"
additional-info="Primary Employment"
selected>
</ui5-user-menu-account>
<ui5-user-menu-account slot="accounts"
avatar-initials="JW"
title-text="John Walker"
subtitle-text="john.walker@sap.com"
description="Project Manager">
</ui5-user-menu-account>
<ui5-user-menu-account slot="accounts"
avatar-initials="DW"
title-text="David Wilson"
subtitle-text="david.wilson@sap.com"
description="Account Manager">
</ui5-user-menu-account>
<ui5-user-menu-item icon="action-settings" text="Setting" data-id="setting"></ui5-user-menu-item>
<ui5-user-menu-item icon="official-service" text="Legal Information"></ui5-user-menu-item>
</ui5-user-menu>
<ui5-title level="H5" class="header-title">Text Direction</ui5-title>
<ui5-switch id="direction" text-on="RTL" text-off="LTR"></ui5-switch>

Expand All @@ -129,6 +161,7 @@
});
const menu = document.getElementById("userMenu");
const menuCustomFooter = document.getElementById("userMenuCustomFooter");
const menuInfoArea = document.getElementById("userMenuInfoArea");
document.getElementById("btnOpenUserMenu").addEventListener("click", function () {
if(menu.open){
menu.open = false;
Expand All @@ -145,6 +178,10 @@
}
});

document.getElementById("btnOpenUserMenuInfoArea").addEventListener("click", function () {
menuInfoArea.open = !menuInfoArea.open;
});

const shellbar = document.getElementById("shellbar");
const menuShellBar = document.getElementById("userMenuShellBar");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import "@ui5/webcomponents/dist/Avatar.js";
import "@ui5/webcomponents-fiori/dist/ShellBar.js";
import "@ui5/webcomponents-fiori/dist/ShellBarBranding.js";
import "@ui5/webcomponents/dist/Button.js";
import "@ui5/webcomponents/dist/MessageStrip.js";
import "@ui5/webcomponents-icons/dist/action-settings.js";
import "@ui5/webcomponents-icons/dist/globe.js";
import "@ui5/webcomponents-icons/dist/collaborate.js";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@
</ui5-avatar>
</ui5-shellbar>
<ui5-user-menu id="userMenu" show-manage-account show-other-accounts show-edit-accounts show-edit-button>
<ui5-message-strip slot="infoArea" design="Information" hide-close-button>
All actions are recorded under the proxy audit log.
</ui5-message-strip>
<ui5-user-menu-account slot="accounts"
avatar-src="../assets/images/avatars/man_avatar_3.png"
title-text="Alain Chevalier 1"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import UserMenuAccountClass from "@ui5/webcomponents-fiori/dist/UserMenuAccount.
import UserMenuItemClass from "@ui5/webcomponents-fiori/dist/UserMenuItem.js";
import UserMenuItemGroupClass from "@ui5/webcomponents-fiori/dist/UserMenuItemGroup.js";
import AvatarClass from "@ui5/webcomponents/dist/Avatar.js";
import MessageStripClass from "@ui5/webcomponents/dist/MessageStrip.js";
import "@ui5/webcomponents-icons/dist/action-settings.js";
import "@ui5/webcomponents-icons/dist/globe.js";
import "@ui5/webcomponents-icons/dist/collaborate.js";
Expand All @@ -22,6 +23,7 @@ const UserMenuAccount = createReactComponent(UserMenuAccountClass);
const UserMenuItem = createReactComponent(UserMenuItemClass);
const UserMenuItemGroup = createReactComponent(UserMenuItemGroupClass);
const Avatar = createReactComponent(AvatarClass);
const MessageStrip = createReactComponent(MessageStripClass);

function App() {
const userMenuRef = useRef<UserMenuClass | null>(null);
Expand Down Expand Up @@ -122,6 +124,9 @@ function App() {
onChangeAccount={handleChangeAccount}
onSignOutClick={handleSignOutClick}
>
<MessageStrip slot="infoArea" design="Information" hideCloseButton={true}>
All actions are recorded under the proxy audit log.
</MessageStrip>
<UserMenuAccount
slot="accounts"
avatarSrc="/images/avatars/man_avatar_3.png"
Expand Down
Loading