Skip to content
Draft
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
88 changes: 88 additions & 0 deletions packages/main/cypress/specs/Menu.cy.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import Button from "../../src/Button.js";
import SplitButton from "../../src/SplitButton.js";
import Menu from "../../src/Menu.js";
import MenuItem from "../../src/MenuItem.js";
import MenuItemGroup from "../../src/MenuItemGroup.js";
Expand Down Expand Up @@ -349,6 +350,93 @@ describe("Menu interaction", () => {
.ui5MenuItemCheckShiftClickAndPress("[text='Outside']", "not.have.attr");
});

it("should close menu with Alt+ArrowDown when opened by a SplitButton", () => {
cy.mount(
<>
<SplitButton id="btnOpen">Open Menu</SplitButton>
<Menu id="menu" opener="btnOpen">
<MenuItem text="Item 1"></MenuItem>
<MenuItem text="Item 2"></MenuItem>
</Menu>
</>
);

cy.get("[ui5-menu]")
.as("menu");

cy.get("@menu")
.ui5MenuOpen();

cy.get("[ui5-menu] > [ui5-menu-item]")
.as("items");

cy.get("@items")
.first()
.should("be.focused")
.realPress(["Alt", "ArrowDown"]);

cy.get("@menu")
.ui5MenuClosed();
});

it("should close menu with Alt+ArrowUp when opened by a SplitButton", () => {
cy.mount(
<>
<SplitButton id="btnOpen">Open Menu</SplitButton>
<Menu id="menu" opener="btnOpen">
<MenuItem text="Item 1"></MenuItem>
<MenuItem text="Item 2"></MenuItem>
</Menu>
</>
);

cy.get("[ui5-menu]")
.as("menu");

cy.get("@menu")
.ui5MenuOpen();

cy.get("[ui5-menu] > [ui5-menu-item]")
.as("items");

cy.get("@items")
.first()
.should("be.focused")
.realPress(["Alt", "ArrowUp"]);

cy.get("@menu")
.ui5MenuClosed();
});

it("should close menu with F4 when opened by a SplitButton", () => {
cy.mount(
<>
<SplitButton id="btnOpen">Open Menu</SplitButton>
<Menu id="menu" opener="btnOpen">
<MenuItem text="Item 1"></MenuItem>
<MenuItem text="Item 2"></MenuItem>
</Menu>
</>
);

cy.get("[ui5-menu]")
.as("menu");

cy.get("@menu")
.ui5MenuOpen();

cy.get("[ui5-menu] > [ui5-menu-item]")
.as("items");

cy.get("@items")
.first()
.should("be.focused")
.realPress("F4");

cy.get("@menu")
.ui5MenuClosed();
});

describe("Event firing", () => {
it("Event firing - 'ui5-item-click' after 'click' on menu item", () => {
cy.mount(
Expand Down
17 changes: 17 additions & 0 deletions packages/main/cypress/support/commands/Menu.commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,22 @@ Cypress.Commands.add("ui5MenuOpened", { prevSubject: true }, subject => {
.and("have.attr", "open");
});

Cypress.Commands.add("ui5MenuClosed", { prevSubject: true }, subject => {
cy.wrap(subject)
.as("menu");

cy.get("@menu")
.should("not.have.attr", "open");

cy.get("@menu")
.shadow()
.find("[ui5-responsive-popover]")
.should($rp => {
expect($rp.is(":popover-open")).to.be.false;
})
.and("not.have.attr", "open");
});

Cypress.Commands.add("ui5MenuItemClick", { prevSubject: true }, subject => {
cy.get(subject)
.as("item")
Expand Down Expand Up @@ -104,6 +120,7 @@ declare global {
interface Chainable {
ui5MenuOpen(options?: { opener?: string }): Chainable<void>
ui5MenuOpened(): Chainable<void>
ui5MenuClosed(): Chainable<void>
ui5MenuItemClick(): Chainable<void>
ui5MenuItemPress(key: any): Chainable<void>
ui5MenuItemCheckShiftClickAndPress(menuItem: string, shouldStatement: string): Chainable<void>
Expand Down
11 changes: 9 additions & 2 deletions packages/main/src/Menu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
isEnter,
isTabNext,
isTabPrevious,
isShow,
} from "@ui5/webcomponents-base/dist/Keys.js";
import {
isPhone,
Expand All @@ -29,6 +30,7 @@ import type MenuItem from "./MenuItem.js";
import { isInstanceOfMenuItem } from "./MenuItem.js";
import { isInstanceOfMenuItemGroup } from "./MenuItemGroup.js";
import { isInstanceOfMenuSeparator } from "./MenuSeparator.js";
import { isInstanceOfSplitButton } from "./SplitButton.js";
import type PopoverHorizontalAlign from "./types/PopoverHorizontalAlign.js";
import type PopoverPlacement from "./types/PopoverPlacement.js";
import type {
Expand Down Expand Up @@ -280,6 +282,10 @@ class Menu extends UI5Element {
return this.shadowRoot!.querySelector<List>("[ui5-list]");
}

get _opener() {
return typeof this.opener === "string" ? document.getElementById(this.opener) : this.opener;
}

/** Returns menu item groups */
get _menuItemGroups() {
return this.items.filter(isInstanceOfMenuItemGroup);
Expand Down Expand Up @@ -438,6 +444,7 @@ class Menu extends UI5Element {

_itemKeyDown(e: KeyboardEvent) {
const isTabNextPrevious = isTabNext(e) || isTabPrevious(e);
const isShowKey = isShow(e);
const item = e.target as MenuItem;

if (!isInstanceOfMenuItem(item)) {
Expand All @@ -447,7 +454,7 @@ class Menu extends UI5Element {
const isEndContentNavigation = isRight(e) || isLeft(e);
const shouldOpenMenu = this.isRtl ? isLeft(e) : isRight(e);

if (isEnter(e) || isTabNextPrevious) {
if (isEnter(e) || isTabNextPrevious || isShowKey) {
e.preventDefault();
}

Expand All @@ -457,7 +464,7 @@ class Menu extends UI5Element {

if (shouldOpenMenu) {
this._openItemSubMenu(item, false);
} else if (isTabNextPrevious) {
} else if (isTabNextPrevious || (isShowKey && this._opener && isInstanceOfSplitButton(this._opener))) {
this._close();
}
}
Expand Down
8 changes: 8 additions & 0 deletions packages/main/src/SplitButton.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import UI5Element from "@ui5/webcomponents-base/dist/UI5Element.js";
import type { DefaultSlot } from "@ui5/webcomponents-base/dist/UI5Element.js";
import createInstanceChecker from "@ui5/webcomponents-base/dist/util/createInstanceChecker.js";
import customElement from "@ui5/webcomponents-base/dist/decorators/customElement.js";
import event from "@ui5/webcomponents-base/dist/decorators/event-strict.js";
import property from "@ui5/webcomponents-base/dist/decorators/property.js";
Expand Down Expand Up @@ -491,6 +492,10 @@
return SplitButton.i18nBundle.getText(SPLIT_BUTTON_ARROW_BUTTON_TOOLTIP);
}

get isSplitButton(): boolean {
return true;
}

get ariaLabelText() {
return [SplitButton.i18nBundle.getText(SPLIT_BUTTON_DESCRIPTION), SplitButton.i18nBundle.getText(SPLIT_BUTTON_KEYBOARD_HINT)].join(" ");
}
Expand All @@ -502,3 +507,6 @@
export type {
SplitButtonAccessibilityAttributes,
};

// Todo - change it to isInstanceOfMenuButton and change the function to check for the presence of opensMenu getter?

Check warning on line 511 in packages/main/src/SplitButton.ts

View workflow job for this annotation

GitHub Actions / check

Unexpected 'todo' comment: 'Todo - change it to...'
export const isInstanceOfSplitButton = createInstanceChecker<SplitButton>("isSplitButton");
Loading