Skip to content

Commit

Permalink
feat(ui5-menu-item): add endContent slot
Browse files Browse the repository at this point in the history
New slot `endContent` is introduced in `ui5-menu-item` component. This slot accepts components that are displayed at the end of the Menu Item if it does not have sub-menu. If there are components slotted and also `additionalText` is being set, the `additionalText` will not be shown as well. 

It is strongly recommended  to slot only icon-only `ui5-button`, `ui5-icon` or `ui5-link` components in order to preserve the intended design.

BGSOFUIBALKAN-8160
#6350
  • Loading branch information
NHristov-sap committed Jun 3, 2024
1 parent f9c04d3 commit dc3cfde
Show file tree
Hide file tree
Showing 12 changed files with 205 additions and 4 deletions.
1 change: 1 addition & 0 deletions packages/main/src/Button.ts
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,7 @@ class Button extends UI5Element implements IButton, IFormElement {
if (this._cancelAction) {
e.preventDefault();
}
markEvent(e, "button");

if (isSpace(e) || isEnter(e)) {
if (this.active) {
Expand Down
6 changes: 6 additions & 0 deletions packages/main/src/ListItemBase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,9 @@ class ListItemBase extends UI5Element implements ITabbable {
}

_onkeydown(e: KeyboardEvent) {
if (getEventMark(e) === "button") {
return;
}
if (isTabNext(e)) {
return this._handleTabNext(e);
}
Expand All @@ -132,6 +135,9 @@ class ListItemBase extends UI5Element implements ITabbable {
}

_onkeyup(e: KeyboardEvent) {
if (getEventMark(e) === "button") {
return;
}
if (isSpace(e)) {
this.fireItemPress(e);
}
Expand Down
4 changes: 4 additions & 0 deletions packages/main/src/Menu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,10 @@ class Menu extends UI5Element {
}

_itemKeyDown(e: KeyboardEvent) {
if (!isLeft(e) && !isRight(e)) {
return;
}

const shouldCloseMenu = this.isRtl ? isRight(e) : isLeft(e);
const shouldOpenMenu = this.isRtl ? isLeft(e) : isRight(e);
const item = e.target as MenuItem;
Expand Down
2 changes: 2 additions & 0 deletions packages/main/src/MenuItem.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
>
</ui5-icon>
</div>
{{else if hasEndContent}}
<slot name="endContent"></slot>
{{else if additionalText}}
<span part="additional-text" class="ui5-li-additional-text">{{additionalText}}</span>
{{/if}}
Expand Down
33 changes: 32 additions & 1 deletion packages/main/src/MenuItem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,11 @@ class MenuItem extends ListItem {
/**
* Defines the `additionalText`, displayed in the end of the menu item.
*
* **Note:** The additional text would not be displayed if the item has a submenu.
* **Note:** The additional text will not be displayed if there are items added in `items` slot or there are
* components added to `endContent` slot.
*
* The priority of what will be displayed at the end of the menu item is as follows:
* sub-menu arrow (if there are items added in `items` slot) -> components added in `endContent` -> text set to `additionalText`.
* @default ""
* @public
* @since 1.8.0
Expand Down Expand Up @@ -153,11 +157,34 @@ class MenuItem extends ListItem {

/**
* Defines the items of this component.
*
* **Note:** If there are items added to this slot, an arrow will be displayed at the end
* of the item in order to indicate that there are items added. In that case components added
* to `endContent` slot or `additionalText` content will not be displayed.
*
* The priority of what will be displayed at the end of the menu item is as follows:
* sub-menu arrow (if there are items added in `items` slot) -> components added in `endContent` -> text set to `additionalText`.
* @public
*/
@slot({ "default": true, type: HTMLElement, invalidateOnChildChange: true })
items!: Array<MenuItem>;

/**
* Defines the components that should be displayed at the end of the menu item.
*
* **Note:** It is highly recommended to slot only components of type `ui5-button`,`ui5-link`
* or `ui5-icon` in order to preserve the intended design. If there are components added to this slot,
* and there is text set in `additionalText`, it will not be displayed. If there are items added to `items` slot,
* nether `additionalText` nor components added to this slot would be displayed.
*
* The priority of what will be displayed at the end of the menu item is as follows:
* sub-menu arrow (if there are items added in `items` slot) -> components added in `endContent` -> text set to `additionalText`.
* @public
* @since 2.0.0
*/
@slot({ type: HTMLElement })
endContent!: Array<HTMLElement>;

get placement(): `${PopoverPlacement}` {
return this.isRtl ? "Start" : "End";
}
Expand All @@ -170,6 +197,10 @@ class MenuItem extends ListItem {
return !!(this.items.length || this.loading);
}

get hasEndContent() {
return !!(this.endContent.length);
}

get hasIcon() {
return !!this.icon;
}
Expand Down
2 changes: 1 addition & 1 deletion packages/main/src/themes/MenuItem.css
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@
}

:host::part(content) {
padding-inline-end: 0.5rem;
padding-inline-end: 0.25rem;
}

.ui5-menu-item-submenu-icon {
Expand Down
57 changes: 56 additions & 1 deletion packages/main/test/pages/Menu.html
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@
<ui5-title level="H5" class="header-title">Event logger</ui5-title>
<ui5-input id="eventLogger" style="width: 100%;"></ui5-input>

<ui5-button id="btnOpen2">Open Menu</ui5-button> <br/>
<ui5-title level="H5" class="header-title">Menu with disabled items that have popovers</ui5-title>
<ui5-button id="btnOpen2">Open Menu</ui5-button> <br/><br/>
<ui5-menu id="menu2" header-text="My ui5-menu">
<ui5-menu-item id="newFolder" text="New Folder" additional-text="Ctrl+F" icon="add-folder" disabled></ui5-menu-item>
<ui5-menu-item text="Open" icon="open-folder" starts-section>
Expand All @@ -70,6 +71,37 @@
<ui5-menu-item text="Exit" icon="journey-arrive"></ui5-menu-item>
</ui5-menu>

<ui5-title level="H5" class="header-title">Menu with items that have endContent</ui5-title>
<ui5-button id="btnOpenEndContent">Open Menu</ui5-button>
<ui5-menu id="menuEndContent" header-text="My ui5-menu">
<ui5-menu-item text="New File" accessible-name="Opens a file explorer" additional-text="Ctrl+Alt+Shift+N" tooltip="Select a file - prevent default" icon="add-document">
<ui5-button id="newLock" slot="endContent" icon="locked" design="Transparent"></ui5-button>
<ui5-button id="newUnlock" slot="endContent" icon="unlocked" design="Transparent"></ui5-button>
<ui5-button id="newFavorite" slot="endContent" icon="favorite" design="Transparent"></ui5-button>
</ui5-menu-item>
<ui5-menu-item text="New Folder with very long title for a menu item" additional-text="Ctrl+F" icon="add-folder" disabled></ui5-menu-item>
<ui5-menu-item text="Open" icon="open-folder" accessible-name="Choose platform" starts-section>
<ui5-menu-item text="Open Locally" icon="open-folder" additional-text="Ctrl+K">
<ui5-menu-item text="Open from C"></ui5-menu-item>
<ui5-menu-item text="Open from D"></ui5-menu-item>
<ui5-menu-item text="Open from E" disabled></ui5-menu-item>
</ui5-menu-item>
<ui5-menu-item text="Open from SAP Cloud" additional-text="Ctrl+L"></ui5-menu-item>
</ui5-menu-item>
<ui5-menu-item text="Save with very long title for a menu item text inside" icon="save">
<ui5-menu-item text="Save Locally" icon="save">
<ui5-menu-item text="Save on C" icon="save"></ui5-menu-item>
<ui5-menu-item text="Save on D" icon="save"></ui5-menu-item>
<ui5-menu-item text="Save on E" icon="save"></ui5-menu-item>
</ui5-menu-item>
<ui5-menu-item text="Save on Cloud" icon="upload-to-cloud"></ui5-menu-item>
</ui5-menu-item>
<ui5-menu-item text="Close" additional-text="Ctrl+W"></ui5-menu-item>
<ui5-menu-item text="Preferences" icon="action-settings" starts-section disabled></ui5-menu-item>
<ui5-menu-item text="Exit" icon="journey-arrive"></ui5-menu-item>
</ui5-menu>


<ui5-popover id="detailsPopover" initialFocus="detailsLink">
<div>
<ui5-label for="detailsLink">For more information:</ui5-label>
Expand All @@ -87,6 +119,11 @@
menu.open = !menu.open;
});

btnOpenEndContent.addEventListener("click", function() {
menuEndContent.opener = "btnOpenEndContent";
menuEndContent.open = !menu.open;
});

btnAddOpener.addEventListener("click", function() {
menu.opener="btnToggleOpen";
});
Expand Down Expand Up @@ -178,6 +215,24 @@
});
});

newLock.addEventListener("click", function(event) {
console.warn("Lock button clicked");
event.target.parentElement.disabled = true;
});

newUnlock.addEventListener("click", function(event) {
console.warn("Unlock button clicked");
event.target.parentElement.disabled = false;
});

newFavorite.addEventListener("click", function(event) {
console.warn("Favorite button clicked");
});

menuEndContent.addEventListener("ui5-item-click", function(event) {
console.warn("Item clicked: " + event.detail.item.text);
});

</script>
</body>
</html>
16 changes: 16 additions & 0 deletions packages/main/test/specs/Menu.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,22 @@ describe("Menu interaction", () => {
assert.ok(await menuItem.getProperty("disabled"), "The menu item is disabled");
assert.ok(await menuItem.matches(":focus"), "The menu item is focused");
});

it("Add endContent to a menu item", async () => {
await browser.url(`test/pages/Menu.html`);
const openButton = await browser.$("#btnOpenEndContent");
await openButton.click();

const menu = await browser.$("#menuEndContent");
const menuItem = await browser.$("#menuEndContent > ui5-menu-item[text='New File']");
const endContent = await menuItem.$$("[ui5-button]");
const lockButton = await endContent[0];
await lockButton.click();

assert.equal(await endContent.length, 3, "The menu item has 3 components in the 'endContent' slot");
assert.ok(await menuItem.getProperty("disabled"), "The menu item is disabled");
assert.ok(await menu.getProperty("open"), "The menu remains open");
});
});

describe("Menu Accessibility", () => {
Expand Down
7 changes: 6 additions & 1 deletion packages/website/docs/_components_pages/main/Menu/Menu.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ slug: ../../Menu
import Basic from "../../../_samples/main/Menu/Basic/Basic.md";
import SubMenu from "../../../_samples/main/Menu/SubMenu/SubMenu.md";
import AditionalText from "../../../_samples/main/Menu/AditionalText/AditionalText.md";
import MenuEndContent from "../../../_samples/main/Menu/MenuEndContent/MenuEndContent.md";

<%COMPONENT_OVERVIEW%>

Expand All @@ -22,4 +23,8 @@ You can nest menu items to create sub menus.

### Menu Item with Additional Text

<AditionalText />
<AditionalText />

### Menu Item with End Content

<MenuEndContent />
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import html from '!!raw-loader!./sample.html';
import js from '!!raw-loader!./main.js';

<Editor html={html} js={js} />
36 changes: 36 additions & 0 deletions packages/website/docs/_samples/main/Menu/MenuEndContent/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import "@ui5/webcomponents/dist/Menu.js";
import "@ui5/webcomponents/dist/MenuItem.js";
import "@ui5/webcomponents/dist/Button.js";

import "@ui5/webcomponents-icons/dist/add-document.js";
import "@ui5/webcomponents-icons/dist/add-folder.js";
import "@ui5/webcomponents-icons/dist/open-folder.js";
import "@ui5/webcomponents-icons/dist/action-settings.js";
import "@ui5/webcomponents-icons/dist/journey-arrive.js";
import "@ui5/webcomponents-icons/dist/slim-arrow-down.js";
import "@ui5/webcomponents-icons/dist/add.js";
import "@ui5/webcomponents-icons/dist/hint.js";
import "@ui5/webcomponents-icons/dist/favorite.js";

const btnOpenEndContent = document.getElementById("btnOpenEndContent");
const menuEndContent = document.getElementById("menuEndContent");
const btnNewAdd = document.getElementById("newAdd");
const btnNewHint = document.getElementById("newHint");
const btnNewFavorite = document.getElementById("newFavorite");

btnOpenEndContent.addEventListener("click", function(event) {
menuEndContent.opener = btnOpenEndContent;
menuEndContent.open = !menuEndContent.open;
});

btnNewAdd.addEventListener("click", function(event) {
alert("Add button pressed");
});

btnNewHint.addEventListener("click", function(event) {
alert("Hint button pressed");
});

btnNewFavorite.addEventListener("click", function(event) {
alert("Favorite button pressed");
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<!-- playground-fold -->
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Sample</title>
</head>

<body style="background-color: var(--sapBackgroundColor); height: 350px">
<!-- playground-fold-end -->

<ui5-button id="btnOpenEndContent">Open Menu</ui5-button>

<ui5-menu id="menuEndContent" header-text="My ui5-menu">
<ui5-menu-item text="New File" accessible-name="Opens a file explorer" additional-text="Ctrl+Alt+Shift+N" tooltip="Select a file - prevent default" icon="add-document">
<ui5-button id="newAdd" slot="endContent" icon="add" design="Transparent"></ui5-button>
<ui5-button id="newHint" slot="endContent" icon="hint" design="Transparent"></ui5-button>
<ui5-button id="newFavorite" slot="endContent" icon="favorite" design="Transparent"></ui5-button>
</ui5-menu-item>
<ui5-menu-item text="New Folder" additional-text="Ctrl+F" icon="add-folder"></ui5-menu-item>
<ui5-menu-item text="Open" icon="open-folder" accessible-name="Choose platform" starts-section>
<ui5-menu-item text="Open Locally" icon="open-folder" additional-text="Ctrl+K"></ui5-menu-item>
<ui5-menu-item text="Open from SAP Cloud" additional-text="Ctrl+L"></ui5-menu-item>
</ui5-menu-item>
<ui5-menu-item text="Save with very long title for a menu item text inside" icon="save">
<ui5-menu-item text="Save Locally" icon="save"></ui5-menu-item>
<ui5-menu-item text="Save on Cloud" icon="upload-to-cloud"></ui5-menu-item>
</ui5-menu-item>
<ui5-menu-item text="Close" additional-text="Ctrl+W"></ui5-menu-item>
<ui5-menu-item text="Preferences" icon="action-settings" starts-section></ui5-menu-item>
<ui5-menu-item text="Exit" icon="journey-arrive"></ui5-menu-item>
</ui5-menu>

<!-- playground-fold -->
<script type="module" src="main.js"></script>
</body>

</html>
<!-- playground-fold-end -->

0 comments on commit dc3cfde

Please sign in to comment.