Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ui5-side-navigation): add actions and unselectable items #10482

Merged
merged 32 commits into from
Jan 27, 2025
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
ef74d27
feat(ui5-side-navigation): add actions and option to select leaves only
dimovpetar Jan 7, 2025
a27775e
refactor: use unselectable instead of interaction mode
dimovpetar Jan 13, 2025
22257c0
test: isSelectable
dimovpetar Jan 13, 2025
4adbd21
feat: all parts of unselectable items now can toggle them
dimovpetar Jan 13, 2025
1503d1d
test: reorder tests
dimovpetar Jan 13, 2025
d8fc7b7
test: proper assertion
dimovpetar Jan 13, 2025
a9116a3
feat: add ariaAttributes with hasPopup
dimovpetar Jan 13, 2025
9cdeaa9
test: correct indexes
dimovpetar Jan 13, 2025
1afccc5
docs: annotate new properties
dimovpetar Jan 13, 2025
4f21684
Merge branch 'main' of github.com:SAP/ui5-webcomponents into side_nav…
dimovpetar Jan 13, 2025
fb131e1
fix: export SideNavigationItemAccessibilityAttributes
dimovpetar Jan 13, 2025
c944047
test: add tests for selection-change event
dimovpetar Jan 14, 2025
6d86a32
docs: add sample
dimovpetar Jan 14, 2025
f926cc8
fix: toggle unselectable items with keyboard
dimovpetar Jan 14, 2025
f50bac7
docs: document restriction not to use subitems with actions
dimovpetar Jan 14, 2025
1112cf5
Merge branch 'main' of github.com:SAP/ui5-webcomponents into side_nav…
dimovpetar Jan 14, 2025
31fb11a
docs: improve documentation
dimovpetar Jan 20, 2025
6477752
fix: properly fire selection-change from in collapsed mode
dimovpetar Jan 21, 2025
8adc31a
fix: forward design attribute to items in collapsed mode
dimovpetar Jan 21, 2025
e3e99f1
fix: aria-haspopup is now added in collapsed mode
dimovpetar Jan 21, 2025
4cc9f56
fix: don't toggle expanded property on leaf nodes
dimovpetar Jan 21, 2025
1d15c74
fix: fire click event on Enter
dimovpetar Jan 21, 2025
564a6df
fix: set role to menuitem for unselectable items
dimovpetar Jan 21, 2025
3865169
docs: improve unselectable description
dimovpetar Jan 21, 2025
216db08
docs: make all external links unselectable
dimovpetar Jan 21, 2025
a4993ef
docs: replace alert with ui5-dialog
dimovpetar Jan 21, 2025
df8e6ef
docs: improve documentation
alexandar-mitsev Jan 24, 2025
667f1f8
Merge branch 'main' into side_navigation_actions_leaves
alexandar-mitsev Jan 24, 2025
c6c4004
Merge branch 'main' into side_navigation_actions_leaves
alexandar-mitsev Jan 27, 2025
4abeacb
test: fix focusing for navigation items
alexandar-mitsev Jan 27, 2025
bf899bc
fix: revert fix of focusing items
alexandar-mitsev Jan 27, 2025
7fe12de
fix: fix commands and selection for focus
alexandar-mitsev Jan 27, 2025
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
281 changes: 255 additions & 26 deletions packages/fiori/cypress/specs/SideNavigation.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,36 +3,265 @@ import "../../src/SideNavigation.js";
import "../../src/SideNavigationItem.js";
import "../../src/SideNavigationSubItem.js";

const sample = html` <ui5-side-navigation>
<ui5-side-navigation-item
id="item1"
text="Home"
icon="home"
tooltip="Home tooltip"
></ui5-side-navigation-item>
<ui5-side-navigation-item id="item2" text="People" icon="group">
<ui5-side-navigation-sub-item
id="item21"
text="From My Team"
icon="employee-approvals"
tooltip="From My Team tooltip"
></ui5-side-navigation-sub-item>
<ui5-side-navigation-sub-item
id="item22"
text="From Other Teams"
icon="employee-rejections"
></ui5-side-navigation-sub-item>
</ui5-side-navigation-item>
</ui5-side-navigation>`;

describe("Side Navigation interaction", () => {
it("Tests that expand works", () => {
cy.mount(sample);
it("Tests expanding and collapsing of items", () => {
cy.mount(html`
<ui5-side-navigation>
<ui5-side-navigation-item id="item1" text="1" icon="group">
<ui5-side-navigation-sub-item text="1.1"></ui5-side-navigation-sub-item>
<ui5-side-navigation-sub-item text="1.2"></ui5-side-navigation-sub-item>
</ui5-side-navigation-item>
</ui5-side-navigation>
`);

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

// assert
cy.get("#item1").should("have.attr", "expanded");

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

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

it("Tests expanding and collapsing of unselectable items", () => {
cy.mount(html`
<ui5-side-navigation>
<ui5-side-navigation-item id="item1" text="1" unselectable>
<ui5-side-navigation-sub-item text="2"></ui5-side-navigation-sub-item>
</ui5-side-navigation-item>
</ui5-side-navigation>
`);

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

// assert
cy.get("#item1").should("have.attr", "expanded");

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

// assert
cy.get("#item1").should("not.have.attr", "expanded");

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

// assert
cy.get("#item2").should("have.attr", "expanded");
cy.get("#item1").should("have.attr", "expanded");

// act
cy.get("#item1").shadow().find(".ui5-sn-item-text").realClick();

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

it("Tests expanding and collapsing of unselectable items with Space and Enter", () => {
cy.mount(html`
<ui5-side-navigation>
<ui5-side-navigation-item id="unselectableItem" text="1" unselectable>
<ui5-side-navigation-sub-item text="1.2"></ui5-side-navigation-sub-item>
</ui5-side-navigation-item>
</ui5-side-navigation>
`);

// act
cy.get("#unselectableItem").shadow().find(".ui5-sn-item").focus();
cy.realPress("Space");

// assert
cy.get("#unselectableItem").should("have.attr", "expanded");

// act
cy.realPress("Space");

// assert
cy.get("#unselectableItem").should("not.have.attr", "expanded");

// act
cy.realPress("Enter");

// assert
cy.get("#unselectableItem").should("have.attr", "expanded");

// act
cy.realPress("Enter");

// assert
cy.get("#unselectableItem").should("not.have.attr", "expanded");
});

it("Tests isSelectable", () => {
cy.mount(`
<ui5-side-navigation>
<ui5-side-navigation-item id="item1" text="1"></ui5-side-navigation-item>
<ui5-side-navigation-item id="item2" text="2" disabled></ui5-side-navigation-item>
<ui5-side-navigation-item id="item3" text="3" design="Action"></ui5-side-navigation-item>
<ui5-side-navigation-item id="item4" text="4" href="https://sap.com"></ui5-side-navigation-item>
<ui5-side-navigation-item id="item5" text="5" unselectable></ui5-side-navigation-item>
<ui5-side-navigation-item>
<ui5-side-navigation-sub-item id="item6" text="6"></ui5-side-navigation-sub-item>
<ui5-side-navigation-sub-item id="item7" text="7" unselectable></ui5-side-navigation-sub-item>
</ui5-side-navigation-item>
</ui5-side-navigation>
`);

// assert
[
{ id: "item1", expectedIsSelectable: true },
{ id: "item2", expectedIsSelectable: false },
{ id: "item3", expectedIsSelectable: true },
{ id: "item4", expectedIsSelectable: true },
{ id: "item5", expectedIsSelectable: false },
{ id: "item6", expectedIsSelectable: true },
{ id: "item7", expectedIsSelectable: false },
].forEach(({ id, expectedIsSelectable }) => {
cy.get(`#${id}`)
.invoke("prop", "isSelectable")
.should("equal", expectedIsSelectable);
});
});

it("Tests 'click' event", () => {
cy.mount(html`
<ui5-side-navigation id="sideNav">
<ui5-side-navigation-item id="item" text="1"></ui5-side-navigation-item>
<ui5-side-navigation-item id="unselectableItem" text="2" unselectable></ui5-side-navigation-item>
<ui5-side-navigation-item id="parentItem" text="3">
<ui5-side-navigation-sub-item text="3.1"></ui5-side-navigation-sub-item>
</ui5-side-navigation-item>
<ui5-side-navigation-item text="4" expanded>
<ui5-side-navigation-sub-item id="childItem" text="4.1"></ui5-side-navigation-sub-item>
</ui5-side-navigation-item>
<ui5-side-navigation-item id="unselectableParentItem" text="5" unselectable>
<ui5-side-navigation-sub-item id="text9" text="5.1"></ui5-side-navigation-sub-item>
</ui5-side-navigation-item>
</ui5-side-navigation>
`);

[
{ element: cy.get("#item"), expectedCallCount: 1 },
{ element: cy.get("#unselectableItem"), expectedCallCount: 1 },
{ element: cy.get("#parentItem"), expectedCallCount: 1 },
{ element: cy.get("#childItem"), expectedCallCount: 1 },
{ element: cy.get("#unselectableParentItem"), expectedCallCount: 1 },
{ element: cy.get("#unselectableParentItem").shadow().find(".ui5-sn-item-toggle-icon"), expectedCallCount: 0 },
].forEach(({ element, expectedCallCount }) => {
cy.get("#sideNav")
.then(sideNav => {
sideNav.get(0).addEventListener("click", cy.stub().as("clickHandler"));
});
// act
element.realClick();

// assert
cy.get("@clickHandler").should("have.callCount", expectedCallCount);
});
});

it("Tests 'selection-change' event", () => {
cy.mount(html`
<ui5-side-navigation id="sideNav">
<ui5-side-navigation-item id="item" text="1"></ui5-side-navigation-item>
<ui5-side-navigation-item id="unselectableItem" text="2" unselectable></ui5-side-navigation-item>
<ui5-side-navigation-item id="parentItem" text="3">
<ui5-side-navigation-sub-item text="3.1"></ui5-side-navigation-sub-item>
</ui5-side-navigation-item>
<ui5-side-navigation-item text="4" expanded>
<ui5-side-navigation-sub-item id="childItem" text="4.1"></ui5-side-navigation-sub-item>
</ui5-side-navigation-item>
<ui5-side-navigation-item id="unselectableParentItem" text="5" unselectable>
<ui5-side-navigation-sub-item id="text9" text="5.1"></ui5-side-navigation-sub-item>
</ui5-side-navigation-item>
</ui5-side-navigation>
`);

[
{ element: cy.get("#item"), expectedCallCount: 1 },
{ element: cy.get("#unselectableItem"), expectedCallCount: 0 },
{ element: cy.get("#parentItem"), expectedCallCount: 1 },
{ element: cy.get("#childItem"), expectedCallCount: 1 },
{ element: cy.get("#unselectableParentItem"), expectedCallCount: 0 },
{ element: cy.get("#unselectableParentItem").shadow().find(".ui5-sn-item-toggle-icon"), expectedCallCount: 0 },
].forEach(({ element, expectedCallCount }) => {
cy.get("#sideNav")
.then(sideNav => {
sideNav.get(0).addEventListener("ui5-selection-change", cy.stub().as("clickHandler"));
});
// act
element.realClick();

// assert
cy.get("@clickHandler").should("have.callCount", expectedCallCount);
});
});
});

describe("Side Navigation Accessibility", () => {
it("SideNavigationItem ariaHasPopup", () => {
cy.mount(html`
<ui5-side-navigation>
<ui5-side-navigation-item id="item1" text="1"></ui5-side-navigation-item>
</ui5-side-navigation>
`);

cy.get("#item1")
.shadow()
.find(".ui5-sn-item")
.should("not.have.attr", "aria-haspopup");

cy.get("#item1")
.invoke("prop", "accessibilityAttributes", {
hasPopup: "dialog",
});

cy.get("#item1")
.shadow()
.find(".ui5-sn-item")
.should("have.attr", "aria-haspopup", "dialog");
});

it("SideNavigationItem ariaHasPopup in collapsed SideNavigation", () => {
cy.mount(html`
<ui5-side-navigation collapsed>
<ui5-side-navigation-item id="item1" text="1"></ui5-side-navigation-item>
</ui5-side-navigation>
`);

cy.get("#item1")
.shadow()
.find(".ui5-sn-item")
.should("not.have.attr", "aria-haspopup");

cy.get("#item1")
.invoke("prop", "accessibilityAttributes", {
hasPopup: "dialog",
});

cy.get("#item1")
.shadow()
.find(".ui5-sn-item")
.should("have.attr", "aria-haspopup", "dialog");
});

it("SideNavigationItem with sub items ariaHasPopup in collapsed SideNavigation", () => {
cy.mount(html`
<ui5-side-navigation collapsed>
<ui5-side-navigation-item id="item1" text="1">
<ui5-side-navigation-sub-item text="1.1"></ui5-side-navigation-sub-item>
<ui5-side-navigation-sub-item text="1.2"></ui5-side-navigation-sub-item>
</ui5-side-navigation-item>
</ui5-side-navigation>
`);

cy.get("#item1")
.shadow()
.find(".ui5-sn-item")
.should("have.attr", "aria-haspopup", "tree");
});
});
2 changes: 1 addition & 1 deletion packages/fiori/src/SideNavigation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -587,7 +587,7 @@ class SideNavigation extends UI5Element {
}

_selectItem(item: SideNavigationSelectableItemBase) {
if (item.disabled) {
if (!item.isSelectable) {
return;
}

Expand Down
30 changes: 27 additions & 3 deletions packages/fiori/src/SideNavigationItem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,18 @@ import customElement from "@ui5/webcomponents-base/dist/decorators/customElement
import jsxRender from "@ui5/webcomponents-base/dist/renderer/JsxRenderer.js";
import property from "@ui5/webcomponents-base/dist/decorators/property.js";
import slot from "@ui5/webcomponents-base/dist/decorators/slot.js";
import { isLeft, isRight } from "@ui5/webcomponents-base/dist/Keys.js";
import {
isLeft,
isRight,
isSpace,
isEnter,
} from "@ui5/webcomponents-base/dist/Keys.js";
import type SideNavigationItemBase from "./SideNavigationItemBase.js";
import SideNavigationSelectableItemBase from "./SideNavigationSelectableItemBase.js";
import type SideNavigation from "./SideNavigation.js";
import type SideNavigationSubItem from "./SideNavigationSubItem.js";

// Templates
import SideNavigationItemTemplate from "./SideNavigationItemTemplate.js";

// Styles
Expand Down Expand Up @@ -89,6 +96,10 @@ class SideNavigationItem extends SideNavigationSelectableItemBase {
}

get _ariaHasPopup() {
if (this.accessibilityAttributes?.hasPopup) {
return this.accessibilityAttributes.hasPopup;
}

if (!this.disabled && this.sideNavCollapsed && this.items.length) {
return "tree";
}
Expand Down Expand Up @@ -142,10 +153,10 @@ class SideNavigationItem extends SideNavigationSelectableItemBase {
return this.selected;
}

_onToggleClick(e: PointerEvent) {
_onToggleClick(e: CustomEvent) {
e.stopPropagation();

this.expanded = !this.expanded;
this._toggle();
}

_onkeydown(e: KeyboardEvent) {
Expand All @@ -159,6 +170,11 @@ class SideNavigationItem extends SideNavigationSelectableItemBase {
return;
}

if (this.unselectable && (isSpace(e) || isEnter(e))) {
this._toggle();
return;
}

super._onkeydown(e);
}

Expand All @@ -171,6 +187,10 @@ class SideNavigationItem extends SideNavigationSelectableItemBase {
}

_onclick(e: MouseEvent) {
if (this.unselectable) {
this._toggle();
}

alexandar-mitsev marked this conversation as resolved.
Show resolved Hide resolved
super._onclick(e);
}

Expand Down Expand Up @@ -201,6 +221,10 @@ class SideNavigationItem extends SideNavigationSelectableItemBase {
get isSideNavigationItem() {
return true;
}

_toggle() {
alexandar-mitsev marked this conversation as resolved.
Show resolved Hide resolved
this.expanded = !this.expanded;
dimovpetar marked this conversation as resolved.
Show resolved Hide resolved
}
}

SideNavigationItem.define();
Expand Down
Loading
Loading