Skip to content

ui5-side-navigation-item: collapse does not remove 'expanded' attribute after multiple leftNavData re-renders #5291

Description

@JohannesDoberer

Description

The ui5-side-navigation-item component fails to correctly remove the expanded attribute when a user clicks to collapse a category, if the LeftNav has been re-rendered multiple times in the same browser session (e.g., by navigating between different Luigi configurations/nodes).

Steps to Reproduce

  1. Run multiple navigation tests in sequence that trigger leftNavData updates (e.g., Nav sync tests with dynamic parameters)
  2. Then navigate to a page with collapsible categories (collapsible: true)
  3. Click a category to expand it → expanded attribute is correctly set
  4. Click the same category to collapse it → expanded attribute remains on the element

Expected Behavior

After clicking a collapsed category to expand it, clicking again should remove the expanded attribute and collapse the category.

Actual Behavior

The expanded attribute persists on the ui5-side-navigation-item element after the collapse click. The UI5 component does not react to the click event for collapsing.

Key Observations

  • Works in isolation: When the test runs alone (fresh browser session), expand/collapse works correctly.
  • Fails in suite: When preceded by tests that cause multiple leftNavData prop changes (triggering Svelte re-renders of the {#each} block containing the side-navigation items), the collapse no longer works.
  • Not a localStorage issue: Clearing luigi.preferences.navigation.expandedCategories from localStorage before the test does not help.
  • Not a page reload issue: Even with a full cy.visit() to a different URL before the test, the problem persists (UI5 custom element registry retains internal state).
  • Appears after core-modular update 0.0.9 → 0.0.11: The test was previously stable; the update likely causes more frequent leftNavData emissions during navigation.

Reproduction Config

const config = {
  navigation: {
    nodes: [{
      pathSegment: 'projects',
      label: 'Projects',
      viewUrl: '/multipurpose.html',
      children: [{
        pathSegment: 'collapsibles',
        label: 'Collapsibles',
        viewUrl: '/multipurpose.html',
        children: [
          {
            label: 'Open Github in new tab',
            category: { label: 'Super useful Github links', icon: 'internet-browser', collapsible: true },
            externalLink: { url: 'http://github.com', sameWindow: false },
            icon: 'internet-browser'
          },
          {
            label: 'Open Github in this tab',
            category: 'Super useful Github links',
            externalLink: { url: 'http://github.com', sameWindow: true },
            icon: 'globe'
          }
        ]
      }]
    }]
  }
};

Root Cause Analysis

In LeftNav.svelte, the expanded attribute is set via a spread:

<ui5-side-navigation-item
  {...readExpandedState(uid) ? { expanded: true } : {}}
>

When leftNavData changes (prop update), Svelte re-renders the {#each} block. The UI5 web component's internal state gets out of sync with the DOM attribute — the component believes it's expanded (internal state) even after Svelte removes the attribute on re-render, leading to the click-to-collapse no longer toggling correctly.

The MutationObserver correctly detects and persists expand/collapse changes to localStorage, but the problem is at the UI5 component level — its internal expand/collapse toggle mechanism breaks after the element has been through multiple Svelte-driven attribute updates.

Workaround

The e2e test is currently skipped (it.skip) in the test suite. The feature works correctly in production when the user interacts with a stable (non-re-rendered) side navigation.

Environment

  • @ui5/webcomponents-fiori: (side-navigation component)
  • @luigi-project/core-modular: 0.0.11
  • Svelte 5 (runes mode)
  • Cypress 15.16.0

Metadata

Metadata

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions