Skip to content
132 changes: 132 additions & 0 deletions packages/fiori/cypress/specs/SideNavigation.cy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1052,6 +1052,138 @@ describe("Side Navigation interaction", () => {
});
});

describe("item-toggle event", () => {
it("fires on user toggle-icon click", () => {
cy.mount(
<SideNavigation id="sideNav">
<SideNavigationItem id="item1" text="1" icon={group}>
<SideNavigationSubItem id="subItem1" text="1.1" />
<SideNavigationSubItem id="subItem2" text="1.2" />
</SideNavigationItem>
</SideNavigation>
);

cy.get("#sideNav").then($el => {
$el[0].addEventListener("item-toggle", cy.stub().as("toggle"));
});

cy.get("#item1").shadow().find(".ui5-sn-item-toggle-icon").realClick();

cy.get("@toggle").should("have.been.calledOnce");
cy.get("@toggle").its("firstCall.args.0.detail.item").should(item => {
expect(item.id).to.equal("item1");
});
cy.get("#item1").should("have.attr", "expanded");
});

it("fires on keyboard Plus/Minus", () => {
cy.mount(
<SideNavigation id="sideNav">
<SideNavigationItem id="focusStart" text="focus start"></SideNavigationItem>
<SideNavigationItem id="item1" text="1" icon={group}>
<SideNavigationSubItem id="subItem1" text="1.1" />
</SideNavigationItem>
</SideNavigation>
);

cy.get("#sideNav").then($el => {
$el[0].addEventListener("item-toggle", cy.stub().as("toggle"));
});

cy.get("#focusStart").realClick();
cy.realPress("ArrowDown");
cy.realPress("+");

cy.get("@toggle").its("lastCall.args.0.detail.item").should(item => {
expect(item.id).to.equal("item1");
});
cy.get("#item1").should("have.attr", "expanded");

cy.realPress("-");

cy.get("@toggle").should("have.been.calledTwice");
cy.get("#item1").should("not.have.attr", "expanded");
});

it("does not fire on programmatic change", () => {
cy.mount(
<SideNavigation id="sideNav">
<SideNavigationItem id="item1" text="1" icon={group}>
<SideNavigationSubItem id="subItem1" text="1.1" />
</SideNavigationItem>
</SideNavigation>
);

cy.get("#sideNav").then($el => {
$el[0].addEventListener("item-toggle", cy.stub().as("toggle"));
});

cy.get("#item1").invoke("prop", "expanded", true);

cy.get("#item1").should("have.prop", "expanded", true);
cy.get("@toggle").should("not.have.been.called");
});

it("preventDefault suppresses the toggle", () => {
cy.mount(
<SideNavigation id="sideNav">
<SideNavigationItem id="item1" text="1" icon={group}>
<SideNavigationSubItem id="subItem1" text="1.1" />
</SideNavigationItem>
</SideNavigation>
);

cy.get("#sideNav").then($el => {
$el[0].addEventListener("item-toggle", (e: Event) => e.preventDefault());
});

cy.get("#item1").shadow().find(".ui5-sn-item-toggle-icon").realClick();

cy.get("#item1").should("have.prop", "expanded", false);
cy.get("#item1").should("not.have.attr", "expanded");
});

it("does not fire during initial rendering", () => {
cy.mount(
<SideNavigation id="sideNav">
<SideNavigationItem id="item1" text="1" icon={group} expanded={true}>
<SideNavigationSubItem id="subItem1" text="1.1" />
</SideNavigationItem>
</SideNavigation>
);

cy.get("#sideNav").then($el => {
$el[0].addEventListener("item-toggle", cy.stub().as("toggle"));
});

cy.get("#item1").should("have.prop", "expanded", true);
cy.get("@toggle").should("not.have.been.called");
});

it("does not fire on the outer side navigation when a collapsed parent opens the picker", () => {
cy.mount(
<SideNavigation id="sideNav" collapsed={true}>
<SideNavigationItem id="item1" text="1" icon={group}>
<SideNavigationSubItem id="subItem1" text="1.1" />
</SideNavigationItem>
</SideNavigation>
);

cy.get("#sideNav").then($el => {
$el[0].addEventListener("item-toggle", cy.stub().as("toggle"));
});

cy.get("#item1").realClick();

cy.get("#sideNav")
.shadow()
.find("[ui5-responsive-popover]")
.should("be.visible");

cy.get("@toggle").should("not.have.been.called");
});
});

describe("Side Navigation Accessibility", () => {
it("SideNavigationItem ariaHasPopup", () => {
cy.mount(
Expand Down
112 changes: 112 additions & 0 deletions packages/fiori/cypress/specs/SideNavigationWithGroups.cy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -317,4 +317,116 @@ describe("Component Behavior", () => {
.should("have.attr", "tabindex", "0");
});
});

describe("item-toggle event", () => {
it("fires on user click", () => {
cy.mount(
<SideNavigation id="sn1">
<SideNavigationGroup id="group1" expanded text="Group">
<SideNavigationItem text="Home 1" icon="home" />
</SideNavigationGroup>
</SideNavigation>);

cy.get("#sn1").then($el => {
$el[0].addEventListener("item-toggle", cy.stub().as("toggle"));
});

cy.get("#group1")
.shadow()
.find(".ui5-sn-item")
.realClick();

cy.get("@toggle").should("have.been.calledOnce");
cy.get("@toggle").its("firstCall.args.0.detail.item").should(item => {
expect(item.id).to.equal("group1");
});
cy.get("#group1").should("have.prop", "expanded", false);
});

it("fires on keyboard Plus/Minus", () => {
cy.mount(
<SideNavigation id="sn">
<SideNavigationItem id="focusStart" text="focus start"></SideNavigationItem>
<SideNavigationGroup id="group1" text="Group">
<SideNavigationItem text="Home 1" icon="home" />
</SideNavigationGroup>
</SideNavigation>);

cy.get("#sn").then($el => {
$el[0].addEventListener("item-toggle", cy.stub().as("toggle"));
});

cy.get("#focusStart").realClick();
cy.realPress("ArrowDown");
cy.realPress("+");

cy.get("@toggle").its("lastCall.args.0.detail.item").should(item => {
expect(item.id).to.equal("group1");
});
cy.get("#group1").should("have.attr", "expanded");

cy.realPress("-");

cy.get("@toggle").should("have.been.calledTwice");
cy.get("#group1").should("not.have.attr", "expanded");
});

it("does not fire on programmatic change", () => {
cy.mount(
<SideNavigation id="sn1">
<SideNavigationGroup id="group1" text="Group">
<SideNavigationItem text="Home 1" icon="home" />
</SideNavigationGroup>
</SideNavigation>);

cy.get("#sn1").then($el => {
$el[0].addEventListener("item-toggle", cy.stub().as("toggle"));
});

cy.get("#group1").invoke("prop", "expanded", true);

cy.get("#group1").should("have.prop", "expanded", true);
cy.get("@toggle").should("not.have.been.called");
});

it("preventDefault suppresses the toggle", () => {
cy.mount(
<SideNavigation id="sn1">
<SideNavigationGroup id="group1" expanded text="Group">
<SideNavigationItem text="Home 1" icon="home" />
</SideNavigationGroup>
</SideNavigation>);

cy.get("#sn1").then($el => {
$el[0].addEventListener("item-toggle", (e: Event) => e.preventDefault());
});

cy.get("#group1")
.shadow()
.find(".ui5-sn-item")
.realClick();

cy.get("#group1").should("have.prop", "expanded", true);
cy.get("#group1")
.shadow()
.find(".ui5-sn-item-group")
.should("have.attr", "aria-expanded", "true");
});

it("does not fire during initial rendering", () => {
cy.mount(
<SideNavigation id="sn1">
<SideNavigationGroup id="group1" expanded text="Group">
<SideNavigationItem text="Home 1" icon="home" />
</SideNavigationGroup>
</SideNavigation>);

cy.get("#sn1").then($el => {
$el[0].addEventListener("item-toggle", cy.stub().as("toggle"));
});

cy.get("#group1").should("have.prop", "expanded", true);
cy.get("@toggle").should("not.have.been.called");
});
});
});
24 changes: 23 additions & 1 deletion packages/fiori/src/SideNavigation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ type SideNavigationItemClickEventDetail = {
item: SideNavigationSelectableItemBase,
};

type SideNavigationItemToggleEventDetail = {
item: SideNavigationItemBase,
};

type PopupSideNavigationItem = SideNavigationItem & { associatedItem: SideNavigationSelectableItemBase };

/**
Expand Down Expand Up @@ -138,10 +142,27 @@ type PopupSideNavigationItem = SideNavigationItem & { associatedItem: SideNaviga
cancelable: true,
})

/**
* Fired when a `ui5-side-navigation-item` or `ui5-side-navigation-group` is expanded or collapsed by user interaction.
*
Comment thread
LidiyaGeorgieva marked this conversation as resolved.
* **Note:** You can call `preventDefault()` on the event to suppress the expand/collapse.
* The `expanded` state stays unchanged. This is handy, for example, if you want to
* dynamically load child items before allowing a parent item to expand.
*
* @param {SideNavigationItemBase} item The toggled item.
* @since 2.26.0
* @public
*/
@event("item-toggle", {
bubbles: true,
cancelable: true,
})

class SideNavigation extends UI5Element {
eventDetails!: {
"selection-change": SideNavigationSelectionChangeEventDetail,
"item-click": SideNavigationItemClickEventDetail
"item-click": SideNavigationItemClickEventDetail,
"item-toggle": SideNavigationItemToggleEventDetail
}

/**
Expand Down Expand Up @@ -854,4 +875,5 @@ export default SideNavigation;
export type {
SideNavigationSelectionChangeEventDetail,
SideNavigationItemClickEventDetail,
SideNavigationItemToggleEventDetail,
};
16 changes: 10 additions & 6 deletions packages/fiori/src/SideNavigationGroup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
} from "@ui5/webcomponents-base/dist/Keys.js";
import customElement from "@ui5/webcomponents-base/dist/decorators/customElement.js";
import type I18nBundle from "@ui5/webcomponents-base/dist/i18nBundle.js";
import SideNavigationItemBase from "./SideNavigationItemBase.js";
import SideNavigationItemBase, { toggleExpanded } from "./SideNavigationItemBase.js";
import type SideNavigationSelectableItemBase from "./SideNavigationSelectableItemBase.js";
import type SideNavigationItem from "./SideNavigationItem.js";
import SideNavigationGroupTemplate from "./SideNavigationGroupTemplate.js";
Expand Down Expand Up @@ -154,24 +154,24 @@ class SideNavigationGroup extends SideNavigationItemBase {

if (isLeft(e)) {
e.preventDefault();
this.expanded = isRTL;
this._setExpandedByUser(isRTL);
return;
}

if (isRight(e)) {
e.preventDefault();
this.expanded = !isRTL;
this._setExpandedByUser(!isRTL);
}

if (isMinus(e)) {
e.preventDefault();
this.expanded = false;
this._setExpandedByUser(false);
return;
}

if (isPlus(e)) {
e.preventDefault();
this.expanded = true;
this._setExpandedByUser(true);
}
}

Expand All @@ -187,10 +187,14 @@ class SideNavigationGroup extends SideNavigationItemBase {

_toggle() {
if (!this.disabled) {
this.expanded = !this.expanded;
this._setExpandedByUser(!this.expanded);
}
}

_setExpandedByUser(value: boolean) {
toggleExpanded(this, value);
}

get isSideNavigationGroup() {
return true;
}
Expand Down
Loading
Loading