diff --git a/packages/fiori/cypress/specs/SideNavigation.cy.tsx b/packages/fiori/cypress/specs/SideNavigation.cy.tsx index 9b85c2a090660..6ced9d1f2bc6d 100644 --- a/packages/fiori/cypress/specs/SideNavigation.cy.tsx +++ b/packages/fiori/cypress/specs/SideNavigation.cy.tsx @@ -76,6 +76,64 @@ describe("Side Navigation Rendering", () => { .should("have.attr", "design", "Action"); }); + it.only("Tests overflow item visibility and items in overflow", () => { + cy.mount( + + + + + + + + + + + + + + + + + + + ); + + cy.get("#sideNav") + .should("be.visible"); + + // the overflow item should be visible + cy.get("#sideNav") + .shadow() + .find(".ui5-sn-item-overflow:not(.ui5-sn-item-hidden)") + .should("be.visible") + .realClick(); + + // exactly 2 items should be in the overflow menu + cy.get("#sideNav") + .shadow() + .find(".ui5-side-navigation-overflow-menu [ui5-navigation-menu-item]") + .should("have.length", 2); + + + // check the last separator calculations + // when the height is 440px, also 2 items should go to the overflow + cy.get("#sideNav") + .invoke("attr", "style", "height:440px"); + + // the overflow item should be visible + cy.get("#sideNav") + .shadow() + .find(".ui5-sn-item-overflow:not(.ui5-sn-item-hidden)") + .should("be.visible") + .realClick(); + + // exactly 2 items should be in the overflow menu + cy.get("#sideNav") + .shadow() + .find(".ui5-side-navigation-overflow-menu [ui5-navigation-menu-item]") + .should("have.length", 2); + }); + it("Tests accessibility", () => { cy.mount( @@ -1369,7 +1427,7 @@ describe("Side Navigation Accessibility", () => { .shadow() .find(".ui5-sn-item-overflow") .realClick(); - + // Assert cy.get("#sideNav") .shadow() diff --git a/packages/fiori/src/SideNavigation.ts b/packages/fiori/src/SideNavigation.ts index cc636aa9bc187..2bb0db35dc261 100644 --- a/packages/fiori/src/SideNavigation.ts +++ b/packages/fiori/src/SideNavigation.ts @@ -313,8 +313,6 @@ class SideNavigation extends UI5Element { (popover?.opener as HTMLElement)?.classList.remove("ui5-sn-item-active"); } - _bn?: SideNavigationSelectableItemBase; - _onMenuClose() { const menu = this.getOverflowPopover(); if (!menu._popover.preventFocusRestore) { @@ -543,8 +541,7 @@ class SideNavigation extends UI5Element { return null; } - const overflowItem = this._overflowItem!; - const flexibleContentDomRef: HTMLElement = domRef.querySelector(".ui5-sn-flexible")!; + const overflowItem = this._overflowItem; if (!overflowItem) { return null; } @@ -553,18 +550,13 @@ class SideNavigation extends UI5Element { const overflowItems = this.overflowItems; - let itemsHeight = overflowItems.reduce((sum, itemRef) => { - if (!itemRef) { - return sum; - } - itemRef.classList.remove("ui5-sn-item-hidden"); - return sum + itemRef.offsetHeight; - }, 0); + let itemsHeight = this._calculateItemsHeight(overflowItems); + const flexibleContentDomRef: HTMLElement = domRef.querySelector(".ui5-sn-flexible")!; const { paddingTop, paddingBottom } = window.getComputedStyle(flexibleContentDomRef); const listHeight = flexibleContentDomRef?.offsetHeight - parseInt(paddingTop) - parseInt(paddingBottom); - if (itemsHeight <= listHeight) { + if (itemsHeight < listHeight) { return; } @@ -572,41 +564,115 @@ class SideNavigation extends UI5Element { itemsHeight = overflowItem.offsetHeight; - const selectedItem = overflowItems.filter(isInstanceOfSideNavigationSelectableItemBase).find(item => item._selected); + const navItems = overflowItems.filter(isInstanceOfSideNavigationSelectableItemBase); + const selectedItem = navItems.find(item => item._selected); + + itemsHeight += this._getSelectedItemHeight(overflowItems, selectedItem) + 1; // +1 for sub-pixel rounding + itemsHeight += this._getLastSeparatorHeight(navItems, overflowItems); + + this._updateItemsVisibility(overflowItems, selectedItem, itemsHeight, listHeight); + + this._flexibleItemNavigation._init(); + } + + _calculateItemsHeight(overflowItems: Array) { + return overflowItems.reduce((sum, itemRef) => { + if (!itemRef) { + return sum; + } + itemRef.classList.remove("ui5-sn-item-hidden"); + + let itemDomRef = itemRef; + + if (isInstanceOfSideNavigationItemBase(itemRef) && itemRef.getDomRef()) { + itemDomRef = itemRef.getDomRef()!; + } + + const { marginTop, marginBottom } = window.getComputedStyle(itemDomRef); + + return sum + itemDomRef.offsetHeight + parseFloat(marginTop) + parseFloat(marginBottom); + }, 0); + } + + _getSelectedItemHeight(overflowItems: Array, selectedItem: SideNavigationSelectableItemBase | undefined) { + if (!selectedItem) { + return 0; + } + + let height = 0; if (selectedItem) { const selectedItemDomRef = selectedItem.getDomRef(); if (selectedItemDomRef) { const { marginTop, marginBottom } = window.getComputedStyle(selectedItemDomRef); - itemsHeight += selectedItemDomRef.offsetHeight + parseFloat(marginTop) + parseFloat(marginBottom); + height += selectedItemDomRef.offsetHeight + parseFloat(marginTop) + parseFloat(marginBottom); + } + + const indexOf = overflowItems.indexOf(selectedItem); + const itemAfterSelected = overflowItems[indexOf + 1]; + if (itemAfterSelected && !isInstanceOfSideNavigationItemBase(itemAfterSelected)) { + height += itemAfterSelected.offsetHeight; } } - overflowItems.forEach(item => { + return height; + } + + _getLastSeparatorHeight(navItems: Array, overflowItems: Array) { + const lastNonSelectedItem = navItems.findLast(item => !item._selected); + if (!lastNonSelectedItem) { + return 0; + } + + const indexOf = overflowItems.indexOf(lastNonSelectedItem); + const nextSeparator = overflowItems[indexOf + 1]; + + if (nextSeparator && !isInstanceOfSideNavigationItemBase(nextSeparator)) { + return nextSeparator.offsetHeight; + } + + return 0; + } + + _updateItemsVisibility(overflowItems: Array, selectedItem: SideNavigationSelectableItemBase | undefined, itemsHeight: number, listHeight: number) { + for (let i = 0; i < overflowItems.length; i++) { + const item = overflowItems[i]; + if (!item || item === selectedItem) { - return; + // eslint-disable-next-line no-continue + continue; } let itemDomRef; - if (isInstanceOfSideNavigationItemBase(item) && item.getDomRef()) { + if (isInstanceOfSideNavigationItemBase(item)) { itemDomRef = item.getDomRef(); - } else { - itemDomRef = item; } - if (itemDomRef) { - const { marginTop, marginBottom } = window.getComputedStyle(itemDomRef); - itemsHeight += itemDomRef.offsetHeight + parseFloat(marginTop) + parseFloat(marginBottom); + if (!itemDomRef) { + // eslint-disable-next-line no-continue + continue; + } - if (itemsHeight > listHeight) { - item.classList.add("ui5-sn-item-hidden"); - } + const { marginTop, marginBottom } = window.getComputedStyle(itemDomRef); + itemsHeight += itemDomRef.offsetHeight + parseFloat(marginTop) + parseFloat(marginBottom); + + // if the next item is a separator, the item and the separator + // should be hidden together, so we need to add the separator height to the itemsHeight + const nextItem = overflowItems[i + 1]; + let nextItemDomRef; + if (nextItem && !isInstanceOfSideNavigationItemBase(nextItem)) { + nextItemDomRef = nextItem; + itemsHeight += nextItemDomRef.offsetHeight; + i++; } - }); - this._flexibleItemNavigation._init(); + if (itemsHeight > listHeight) { + item.classList.add("ui5-sn-item-hidden"); + nextItemDomRef?.classList.add("ui5-sn-item-hidden"); + } + } } _findFocusedItem(items: Array): SideNavigationItemBase | undefined { diff --git a/packages/fiori/src/themes/SideNavigation.css b/packages/fiori/src/themes/SideNavigation.css index c2379289e2a10..3f5c159c31288 100644 --- a/packages/fiori/src/themes/SideNavigation.css +++ b/packages/fiori/src/themes/SideNavigation.css @@ -85,4 +85,12 @@ .ui5-sn-item-overflow { margin-top: auto; +} + +.ui5-sn-spacer { + margin: var(--_ui5_side_navigation_navigation_separator_margin); + height: var(--_ui5_side_navigation_navigation_separator_height); + min-height: var(--_ui5_side_navigation_navigation_separator_height); + background-color: var(--_ui5_side_navigation_navigation_separator_background_color); + border-radius: var(--_ui5_side_navigation_navigation_separator_radius); } \ No newline at end of file diff --git a/packages/fiori/src/themes/SideNavigationGroup.css b/packages/fiori/src/themes/SideNavigationGroup.css index 00833c2f23d2b..c28a6eb844165 100644 --- a/packages/fiori/src/themes/SideNavigationGroup.css +++ b/packages/fiori/src/themes/SideNavigationGroup.css @@ -22,12 +22,4 @@ .ui5-sn-item-group-below-group.ui5-sn-item-separator, .ui5-sn-item-group-below-group .ui5-sn-item-separator:first-child { display: none; -} - -.ui5-sn-spacer { - margin: var(--_ui5_side_navigation_navigation_separator_margin); - height: var(--_ui5_side_navigation_navigation_separator_height); - min-height: var(--_ui5_side_navigation_navigation_separator_height); - background-color: var(--_ui5_side_navigation_navigation_separator_background_color); - border-radius: var(--_ui5_side_navigation_navigation_separator_radius); -} +} \ No newline at end of file diff --git a/packages/fiori/test/pages/SideNavigationOverflowOverlap.html b/packages/fiori/test/pages/SideNavigationOverflowOverlap.html new file mode 100644 index 0000000000000..69e962bd55d21 --- /dev/null +++ b/packages/fiori/test/pages/SideNavigationOverflowOverlap.html @@ -0,0 +1,58 @@ + + + + + Side Navigation Only + + + + + + + + + + + + + + + + + + + + + + + + + + +