Skip to content

Fix MudStaticNavGroup Visibility and Animation#70

Merged
Anu6is merged 5 commits intomasterfrom
fix/nav-group-visibility-and-animation-16877831955550158464
Mar 11, 2026
Merged

Fix MudStaticNavGroup Visibility and Animation#70
Anu6is merged 5 commits intomasterfrom
fix/nav-group-visibility-and-animation-16877831955550158464

Conversation

@Anu6is
Copy link
Contributor

@Anu6is Anu6is commented Mar 11, 2026

This change fixes a bug in MudStaticNavGroup where nested items remained invisible after expanding the group from an initially collapsed state in Static SSR.

The fix involves:

  1. Enhancing initNavGroups in Extensions.MudBlazor.StaticInput.lib.module.js to explicitly remove the invisible class upon expansion and re-add it upon collapse.
  2. Implementing a manual height animation using the scrollHeight of the content wrapper and a 250ms CSS transition, mimicking standard MudBlazor behavior without requiring an interactive lifecycle.
  3. Updating the existing unit tests in NavMenuTests.cs to include necessary delays for these animations and verify the presence/absence of the invisible class.

Visual verification was performed using the TestViewer project, confirming that items appear correctly and the expansion is smooth.


PR created automatically by Jules for task 16877831955550158464 started by @Anu6is

Summary by CodeRabbit

  • New Features

    • Per-element initialization guards and broader initialization to avoid repeated setup.
    • Initial-load handling and a MutationObserver to auto-run initialization for newly added nodes.
    • Improved navigation group expand/collapse with staged height animations, icon/ARIA/focus synchronization.
  • Bug Fixes

    • Reset transient animation states and timeouts to prevent visual glitches and inconsistent expansion.
  • Tests

    • Updated tests to validate staged expand/collapse timing and states.

- Updated JavaScript initialization for NavGroups to correctly manage the 'invisible' class on expansion.
- Implemented smooth height transitions using scrollHeight for a native feel in Static SSR.
- Updated NavMenu unit tests to accommodate animation timing and class changes.

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
@google-labs-jules
Copy link
Contributor

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

@qodo-code-review
Copy link
Contributor

Review Summary by Qodo

Fix MudStaticNavGroup visibility and add smooth animations

🐞 Bug fix ✨ Enhancement

Grey Divider

Walkthroughs

Description
• Fix MudStaticNavGroup visibility issue in Static SSR mode
• Implement smooth height-based animations for expand/collapse
• Add invisible class management for proper state handling
• Update unit tests with animation timing and class assertions
Diagram
flowchart LR
  A["NavGroup Click Event"] --> B["Check Expansion State"]
  B --> C{Will Expand?}
  C -->|Yes| D["Remove invisible class"]
  D --> E["Animate height to scrollHeight"]
  E --> F["Add mud-collapse-entered"]
  C -->|No| G["Animate height to 0"]
  G --> H["Add invisible class"]
  H --> I["Update aria-hidden"]
  F --> I
  I --> J["Update expandIcon transform"]
Loading

Grey Divider

File Changes

1. src/wwwroot/Extensions.MudBlazor.StaticInput.lib.module.js 🐞 Bug fix +39/-8

Implement smooth height animations and visibility management

• Refactored initNavGroups to properly manage the invisible class on expand/collapse
• Implemented smooth height-based CSS transitions using scrollHeight for 250ms duration
• Added separate logic paths for expansion and collapse with proper class toggling
• Introduced reflow forcing and timeout-based state transitions to sync with CSS animations

src/wwwroot/Extensions.MudBlazor.StaticInput.lib.module.js


2. tests/StaticInput.UnitTests/Components/NavMenuTests.cs 🧪 Tests +8/-1

Add animation timing and visibility class assertions

• Added 500ms delays after button clicks to wait for JavaScript animations
• Updated assertions to verify invisible class is added when collapsed
• Updated assertions to verify invisible class is removed when expanded
• Added clarifying comments about static mode animation behavior

tests/StaticInput.UnitTests/Components/NavMenuTests.cs


Grey Divider

Qodo Logo

@qodo-code-review
Copy link
Contributor

qodo-code-review bot commented Mar 11, 2026

Code Review by Qodo

🐞 Bugs (2) 📘 Rule violations (0) 📎 Requirement gaps (0)

Grey Divider


Action required

1. Navgroup toggle race🐞 Bug ✓ Correctness
Description
initNavGroups uses 250ms setTimeout callbacks to finalize expand/collapse without cancelling
prior timers, so rapid clicks can apply stale callbacks and leave the group
invisible/aria-hidden=true while aria-expanded=true. This produces a broken visual state and
inconsistent accessibility state.
Code

src/wwwroot/Extensions.MudBlazor.StaticInput.lib.module.js[R520-555]

+                // Clear any existing timeouts or styles that might interfere
+                collapseContainer.style.transition = 'height 250ms cubic-bezier(0.4, 0, 0.2, 1)';
-                expandIcon.classList.toggle('mud-transform', !isExpanded);
-                button.setAttribute('aria-expanded', !isExpanded);
+                if (willExpand) {
+                    collapseContainer.classList.remove('invisible');
+                    collapseContainer.setAttribute('aria-hidden', 'false');
+                    collapseContainer.classList.add('mud-collapse-entering');
+
+                    const height = wrapper.scrollHeight;
+                    collapseContainer.style.height = height + 'px';
+
+                    setTimeout(() => {
+                        collapseContainer.classList.remove('mud-collapse-entering');
+                        collapseContainer.classList.add('mud-collapse-entered');
+                        collapseContainer.style.height = 'auto';
+                    }, 250);
+                } else {
+                    const height = wrapper.scrollHeight;
+                    collapseContainer.style.height = height + 'px';
+
+                    // Force reflow
+                    collapseContainer.offsetHeight;
+
+                    collapseContainer.classList.remove('mud-collapse-entered');
+                    collapseContainer.classList.add('mud-collapse-exiting');
+                    collapseContainer.style.height = '0px';
+
+                    setTimeout(() => {
+                        collapseContainer.classList.remove('mud-collapse-exiting');
+                        collapseContainer.classList.add('invisible');
+                        collapseContainer.setAttribute('aria-hidden', 'true');
+                    }, 250);
+                }
+
+                expandIcon.classList.toggle('mud-transform', willExpand);
+                button.setAttribute('aria-expanded', willExpand);
Evidence
Two independent delayed callbacks mutate the same DOM node state (classes + aria-hidden) after
250ms, but no timeout handle is stored or cleared, so earlier callbacks can still run after a
subsequent click has already changed aria-expanded and begun the opposite animation.

src/wwwroot/Extensions.MudBlazor.StaticInput.lib.module.js[517-555]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The nav group toggle schedules delayed DOM mutations via `setTimeout` but never cancels previous timeouts. Rapid user clicks can cause earlier callbacks to run after a later click, overwriting the latest visual/ARIA state.
### Issue Context
The handler updates `aria-expanded` immediately but updates `aria-hidden` and some classes later in the timeout callbacks.
### Fix Focus Areas
- src/wwwroot/Extensions.MudBlazor.StaticInput.lib.module.js[517-555]
### Implementation notes
- Store timeout IDs on the element (e.g., `collapseContainer._mudStaticNavGroupTimeout`) and call `clearTimeout(...)` at the start of the click handler.
- Consider clearing both possible pending timers (expand/collapse) if you split them.
- At the start of each click, remove stale classes (`mud-collapse-entering`, `mud-collapse-exiting`) and reset inline `height` as appropriate before applying the new transition.
- Optionally prefer `transitionend` event over fixed `setTimeout(250)` to avoid drift if the CSS duration changes.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. Fixed-delay animation waits 🐞 Bug ⛯ Reliability
Description
NavMenuTests uses Task.Delay(500) to wait for the JS animation, which makes the test slower and
can still be timing-flaky under load if the JS timer/transition completion is delayed. The suite
already uses deterministic waits elsewhere, so this is an avoidable reliability risk in CI.
Code

tests/StaticInput.UnitTests/Components/NavMenuTests.cs[R30-47]

       await button.ClickAsync();
+            // Wait for JS animation
+            await Task.Delay(500);
       menuClasses = await menuGroup.GetAttributeAsync("class");
-            menuClasses.Should().NotContain("mud-collapse-entered").And.NotContain("mud-collapse-entering");
+            menuClasses.Should().NotContain("mud-collapse-entered");
+            menuClasses.Should().Contain("invisible");
       ariaValue = await menuGroup.GetAttributeAsync("aria-hidden");
       ariaValue.Should().Be("true");
       await button.ClickAsync();
+            // Wait for JS animation
+            await Task.Delay(500);
       menuClasses = await menuGroup.GetAttributeAsync("class");
       menuClasses.Should().Contain("mud-collapse-entered");
+            menuClasses.Should().NotContain("invisible");
Evidence
The updated test synchronizes by sleeping a fixed duration rather than waiting for the specific DOM
condition it later asserts. In the same test file, other tests already use WaitForFunctionAsync to
deterministically wait for a class change after async UI work.

tests/StaticInput.UnitTests/Components/NavMenuTests.cs[30-47]
tests/StaticInput.UnitTests/Components/NavMenuTests.cs[110-116]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The nav menu collapse/expand test relies on `Task.Delay(500)` to wait for animations, which slows the test and can be flaky if the environment delays timers.
### Issue Context
The test already asserts on `class` and `aria-hidden`, so it can wait directly for those conditions instead of sleeping.
### Fix Focus Areas
- tests/StaticInput.UnitTests/Components/NavMenuTests.cs[30-47]
### Implementation notes
- After clicking to collapse, wait until the target element has `aria-hidden === &amp;amp;amp;amp;amp;#x27;true&amp;amp;amp;amp;amp;#x27;` and/or `classList.contains(&amp;amp;amp;amp;amp;#x27;invisible&amp;amp;amp;amp;amp;#x27;)`.
- After clicking to expand, wait until it has `aria-hidden === &amp;amp;amp;amp;amp;#x27;false&amp;amp;amp;amp;amp;#x27;` and `classList.contains(&amp;amp;amp;amp;amp;#x27;mud-collapse-entered&amp;amp;amp;amp;amp;#x27;)` and not `invisible`.
- Use `Page.WaitForFunctionAsync` or `Locator.WaitForAsync` (if available) rather than fixed delays.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

@coderabbitai
Copy link

coderabbitai bot commented Mar 11, 2026

Caution

Review failed

Pull request was closed or merged during review

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a per-element WeakSet initialization tracker, guards re-initialization across static inputs, wires an exported afterWebStarted(blazor) initializer with enhancedload and a MutationObserver, and reworks nav-group expand/collapse into staged height-based animations with updated ARIA, focus, and transient-state handling.

Changes

Cohort / File(s) Summary
StaticInput JS module
src/wwwroot/Extensions.MudBlazor.StaticInput.lib.module.js
Adds a global initializedElements WeakSet and per-element guards; replaces selective selectors with general selection plus guards; exports afterWebStarted(blazor) and hooks enhancedload + runs initialize on startup; installs a MutationObserver to re-run initialization for added nodes; refactors all initXxx paths (text fields, checkboxes, switches, radios, drawers, nav groups) to mark elements as initialized and avoid double-init; rewrites nav-group flow to staged height-based animation states (mud-collapse-entering, mud-collapse-entered, mud-collapse-exiting, invisible), manages aria-hidden/aria-expanded, focus adjustments, icon transforms, and clears transient timeouts to prevent cross-interaction interference.
Animation-Aware Tests
tests/StaticInput.UnitTests/Components/NavMenuTests.cs
Updates nav menu test to account for JS animation timing (~600ms) and asserts staged animation classes and aria-hidden values across initial state, first click (collapsed/invisible), and second click (expanded).

Sequence Diagram(s)

sequenceDiagram
    participant Blazor as Blazor (.NET)
    participant JS as StaticInput JS
    participant DOM as Browser DOM
    Blazor->>JS: call exported afterWebStarted(blazor)
    JS->>JS: attach enhancedload handler and run initialize()
    JS->>DOM: query static input elements
    loop for each new element
        JS->>DOM: initXxx(element) [guard: initializedElements]
        DOM-->>JS: element marked initialized
    end
    DOM->>JS: MutationObserver notices added nodes
    JS->>DOM: re-run initialization for added nodes
    Note over JS,DOM: Nav-group interactions: staged height changes, aria updates, icon transforms, timeouts cleared
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • 0phois/MudBlazor.StaticInput#59 — modifies the same JS initializer, adding per-element WeakSet guards, nav-group animation/ARIA changes, MutationObserver handling, and exporting the startup initializer.

Suggested labels

Review effort 3/5

Poem

🐰
I hop through DOMs with careful cheer,
Mark each node so it runs just once, not twice,
Heights bloom and fold in staged delight,
ARIA and icons find their place,
A rabbit twitches, pleased with animated spice.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Fix MudStaticNavGroup Visibility and Animation' directly and clearly summarizes the main changes: fixing visibility and animation issues in MudStaticNavGroup components, which is the primary focus of the changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/nav-group-visibility-and-animation-16877831955550158464

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist
Copy link

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request resolves a visibility issue in the MudStaticNavGroup component related to static server-side rendering (SSR). It introduces a JavaScript-based animation to ensure smooth expansion and collapse, and updates unit tests to reflect these changes.

Highlights

  • Bug Fix: Addresses an issue where nested items in MudStaticNavGroup remained invisible after expanding from a collapsed state in Static SSR.
  • Animation Enhancement: Implements a manual height animation using scrollHeight and CSS transitions to mimic MudBlazor's standard behavior without requiring an interactive lifecycle.
  • Test Update: Updates unit tests to include delays for animations and verifies the presence/absence of the invisible class.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • src/wwwroot/Extensions.MudBlazor.StaticInput.lib.module.js
    • Modified JavaScript to handle the expansion and collapse of MudStaticNavGroup items, including adding and removing the invisible class and implementing a manual height animation.
  • tests/StaticInput.UnitTests/Components/NavMenuTests.cs
    • Updated unit tests to include delays for animations and verify the presence/absence of the invisible class on MudStaticNavGroup items.
Activity
  • The pull request addresses issue where nested items remained invisible after expanding the group from an initially collapsed state in Static SSR.
  • The fix involves enhancing initNavGroups in Extensions.MudBlazor.StaticInput.lib.module.js to explicitly remove the invisible class upon expansion and re-add it upon collapse.
  • A manual height animation was implemented using the scrollHeight of the content wrapper and a 250ms CSS transition.
  • Unit tests in NavMenuTests.cs were updated to include necessary delays for these animations and verify the presence/absence of the invisible class.
  • Visual verification was performed using the TestViewer project.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request successfully addresses the visibility issue with MudStaticNavGroup in Static SSR by implementing a JavaScript-based animation and visibility toggle. The logic is sound and the accompanying test updates validate the fix. My review includes a suggestion to refactor a hardcoded animation duration in the JavaScript file for better maintainability. Additionally, I've recommended replacing Task.Delay in the unit tests with a more robust waiting mechanism provided by Playwright to prevent flaky tests. Overall, this is a good fix.

@github-actions
Copy link

github-actions bot commented Mar 11, 2026

Test Results

50 tests   50 ✅  13s ⏱️
 1 suites   0 💤
 1 files     0 ❌

Results for commit 058a8f9.

♻️ This comment has been updated with latest results.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
tests/StaticInput.UnitTests/Components/NavMenuTests.cs (1)

30-32: Replace hard-coded Task.Delay(500) sleeps with condition-based waits on aria-hidden/invisible state.

The JS animation uses a 250ms transition, so the 500ms sleep is arbitrary and makes tests timing-sensitive. This file already uses Page.WaitForFunctionAsync(...) for drawers; apply the same pattern here. The test checks both aria-hidden and invisible class, so polling for either would make the nav-group test deterministic and robust to CI slowness or animation-duration changes.

Locations Lines 30–32 and 41–43
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/StaticInput.UnitTests/Components/NavMenuTests.cs` around lines 30 - 32,
Replace the hard-coded Task.Delay(500) calls after await button.ClickAsync() in
NavMenuTests (both occurrences) with a condition-based wait using
Page.WaitForFunctionAsync that polls until the target nav-group element has
either aria-hidden="true" or contains the "invisible" class; locate the element
being toggled in the test (the same selector used later to assert visibility)
and wait for the expression that checks element.getAttribute('aria-hidden') ===
'true' || element.classList.contains('invisible') before proceeding to
assertions, mirroring the pattern used for drawers in other tests.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/wwwroot/Extensions.MudBlazor.StaticInput.lib.module.js`:
- Around line 520-551: The transition timers are never cleared, causing stale
callbacks to corrupt classes/ARIA; modify the logic around collapseContainer
(and where wrapper is used) to store the pending timer ID on the element (e.g.,
collapseContainer._transitionTimer), call
clearTimeout(collapseContainer._transitionTimer) before scheduling a new
setTimeout, and then assign the new timer ID back to that property; also
remove/reset transient animation classes ('mud-collapse-entering',
'mud-collapse-exiting') and ensure aria-hidden is set consistently before
starting the new transition so previous state cannot conflict with the new one.

---

Nitpick comments:
In `@tests/StaticInput.UnitTests/Components/NavMenuTests.cs`:
- Around line 30-32: Replace the hard-coded Task.Delay(500) calls after await
button.ClickAsync() in NavMenuTests (both occurrences) with a condition-based
wait using Page.WaitForFunctionAsync that polls until the target nav-group
element has either aria-hidden="true" or contains the "invisible" class; locate
the element being toggled in the test (the same selector used later to assert
visibility) and wait for the expression that checks
element.getAttribute('aria-hidden') === 'true' ||
element.classList.contains('invisible') before proceeding to assertions,
mirroring the pattern used for drawers in other tests.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f294f05a-2d1e-48c4-b740-2784ee6a4f0d

📥 Commits

Reviewing files that changed from the base of the PR and between dc26ec0 and c51705c.

📒 Files selected for processing (2)
  • src/wwwroot/Extensions.MudBlazor.StaticInput.lib.module.js
  • tests/StaticInput.UnitTests/Components/NavMenuTests.cs

…on issues

- Fixed visibility of NavGroup items by explicitly managing the 'invisible' class and 'display' style.
- Implemented smooth height transitions using scrollHeight for expansion and collapse.
- Added 'mud-nav-group-expanded' class to fix background highlighting when expanded.
- Introduced WeakSet-based element tracking in JS initializer to prevent double-binding and fix visibility issues after enhanced navigation.
- Updated unit tests to support animation timing and state changes.

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
src/wwwroot/Extensions.MudBlazor.StaticInput.lib.module.js (1)

540-575: ⚠️ Potential issue | 🟠 Major

Cancel the previous nav-group transition before starting the next one.

A second click inside the 250ms window leaves both callbacks live. The stale one can re-apply invisible or mud-collapse-entered after the state has already flipped, so the group can end up visually and semantically out of sync again.

🛠️ Suggested fix
-                // Clear any existing timeouts or styles that might interfere
-                collapseContainer.style.transition = 'height 250ms cubic-bezier(0.4, 0, 0.2, 1)';
+                clearTimeout(collapseContainer._transitionTimer);
+                collapseContainer.classList.remove('mud-collapse-entering', 'mud-collapse-exiting');
+                collapseContainer.style.transition = 'height 250ms cubic-bezier(0.4, 0, 0.2, 1)';

                 if (willExpand) {
                     navElement.classList.add('mud-nav-group-expanded');
                     collapseContainer.classList.remove('invisible');
                     collapseContainer.style.display = 'block'; // Ensure it's not display: none
                     collapseContainer.setAttribute('aria-hidden', 'false');
                     collapseContainer.classList.add('mud-collapse-entering');

                     const height = wrapper.scrollHeight;
                     collapseContainer.style.height = height + 'px';

-                    setTimeout(() => {
+                    collapseContainer._transitionTimer = setTimeout(() => {
                         collapseContainer.classList.remove('mud-collapse-entering');
                         collapseContainer.classList.add('mud-collapse-entered');
                         collapseContainer.style.height = 'auto';
+                        collapseContainer._transitionTimer = null;
                     }, 250);
                 } else {
                     navElement.classList.remove('mud-nav-group-expanded');
                     const height = wrapper.scrollHeight;
                     collapseContainer.style.height = height + 'px';

                     // Force reflow
                     collapseContainer.offsetHeight;

                     collapseContainer.classList.remove('mud-collapse-entered');
                     collapseContainer.classList.add('mud-collapse-exiting');
                     collapseContainer.style.height = '0px';

-                    setTimeout(() => {
+                    collapseContainer._transitionTimer = setTimeout(() => {
                         collapseContainer.classList.remove('mud-collapse-exiting');
                         collapseContainer.classList.add('invisible');
                         collapseContainer.style.display = ''; // Reset display
                         collapseContainer.setAttribute('aria-hidden', 'true');
+                        collapseContainer._transitionTimer = null;
                     }, 250);
                 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/wwwroot/Extensions.MudBlazor.StaticInput.lib.module.js` around lines 540
- 575, Before starting a new toggle transition, cancel any previous pending
transition callback by storing the setTimeout id on the collapse container
(e.g., collapseContainer._mudTransitionId) and calling clearTimeout if present;
also remove transient transition classes (mud-collapse-entering,
mud-collapse-exiting) and any stale inline height/aria/display state that could
be reapplied, then proceed with the current logic that adds/removes
mud-nav-group-expanded, mud-collapse-entering/mud-collapse-exiting, sets
collapseContainer.style.height and schedules the new timeout—ensure both
branches (willExpand true/false) set collapseContainer._mudTransitionId =
setTimeout(...) so future toggles can clear it.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/wwwroot/Extensions.MudBlazor.StaticInput.lib.module.js`:
- Around line 540-575: Before starting a new toggle transition, cancel any
previous pending transition callback by storing the setTimeout id on the
collapse container (e.g., collapseContainer._mudTransitionId) and calling
clearTimeout if present; also remove transient transition classes
(mud-collapse-entering, mud-collapse-exiting) and any stale inline
height/aria/display state that could be reapplied, then proceed with the current
logic that adds/removes mud-nav-group-expanded,
mud-collapse-entering/mud-collapse-exiting, sets collapseContainer.style.height
and schedules the new timeout—ensure both branches (willExpand true/false) set
collapseContainer._mudTransitionId = setTimeout(...) so future toggles can clear
it.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 56af86da-f94c-4263-b607-26dde63656d3

📥 Commits

Reviewing files that changed from the base of the PR and between c51705c and 7ea3487.

📒 Files selected for processing (1)
  • src/wwwroot/Extensions.MudBlazor.StaticInput.lib.module.js

…g issues

- Fixed visibility of NavGroup items by explicitly managing 'invisible' class and 'display' style.
- Implemented smooth height transitions using scrollHeight for expansion and collapse.
- Added robust timeout management to prevent race conditions during rapid clicks.
- Addressed persistent highlight issue by using 'button.blur()' and optimizing class usage.
- Introduced WeakSet-based element tracking in JS initializer to support enhanced navigation.
- Updated unit tests to support animation timing and state changes.

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/wwwroot/Extensions.MudBlazor.StaticInput.lib.module.js (1)

547-547: Extract the nav-group animation duration into one constant.

250ms is duplicated in the transition string and both timeout paths here, and the tests already wait on a different value in tests/StaticInput.UnitTests/Components/NavMenuTests.cs:30-43. Centralizing it will keep the JS behavior and test timing from drifting again.

♻️ Proposed refactor
+const NAV_GROUP_ANIMATION_MS = 250;
+const NAV_GROUP_ANIMATION_EASING = 'cubic-bezier(0.4, 0, 0.2, 1)';
+
 function initNavGroups() {
     const navGroups = document.querySelectorAll('[data-mud-static-type="nav-group"]');
     navGroups.forEach(navGroup => {
@@
-                collapseContainer.style.transition = 'height 250ms cubic-bezier(0.4, 0, 0.2, 1)';
+                collapseContainer.style.transition = `height ${NAV_GROUP_ANIMATION_MS}ms ${NAV_GROUP_ANIMATION_EASING}`;
@@
-                    }, 250);
+                    }, NAV_GROUP_ANIMATION_MS);
                 } else {
@@
-                    }, 250);
+                    }, NAV_GROUP_ANIMATION_MS);
                 }

Also applies to: 559-564, 575-581

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/wwwroot/Extensions.MudBlazor.StaticInput.lib.module.js` at line 547,
Extract the duplicated "250ms" into a single constant (e.g.
NAV_GROUP_ANIMATION_DURATION or NAV_GROUP_ANIMATION_MS) and use that constant
wherever the nav-group animation duration appears: in the
collapseContainer.style.transition assignment (compose the transition string
using the constant) and in the setTimeout calls that currently use numeric
timeouts; replace both string and numeric occurrences so the transition string
and timeout values are driven by the same constant, and update the three
affected blocks around the existing collapse/expand logic (the
collapseContainer.style.transition usage and the two timeout branches) to
reference that constant.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/wwwroot/Extensions.MudBlazor.StaticInput.lib.module.js`:
- Around line 587-588: The code currently calls button.blur() unconditionally
after activation which steals focus from keyboard users; remove the
unconditional call to button.blur() and instead only remove focus for pointer
interactions — e.g., detect pointer-initiated activation by checking
event.pointerType (mouse/touch) or track a pointerdown flag set on pointerdown
and cleared on keydown, then call button.blur() only when the activation was
pointer-driven; if detecting pointer input isn't feasible, resolve the
persistent hover highlight by toggling a CSS class on the button rather than
blurring it. Ensure you update the click/activation handler that currently
invokes button.blur() so it uses the pointer-only check or CSS class approach.

---

Nitpick comments:
In `@src/wwwroot/Extensions.MudBlazor.StaticInput.lib.module.js`:
- Line 547: Extract the duplicated "250ms" into a single constant (e.g.
NAV_GROUP_ANIMATION_DURATION or NAV_GROUP_ANIMATION_MS) and use that constant
wherever the nav-group animation duration appears: in the
collapseContainer.style.transition assignment (compose the transition string
using the constant) and in the setTimeout calls that currently use numeric
timeouts; replace both string and numeric occurrences so the transition string
and timeout values are driven by the same constant, and update the three
affected blocks around the existing collapse/expand logic (the
collapseContainer.style.transition usage and the two timeout branches) to
reference that constant.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 20e5d8c3-61b8-45c8-ab89-4aa8b46e70e4

📥 Commits

Reviewing files that changed from the base of the PR and between 7ea3487 and d32f1e9.

📒 Files selected for processing (2)
  • src/wwwroot/Extensions.MudBlazor.StaticInput.lib.module.js
  • tests/StaticInput.UnitTests/Components/NavMenuTests.cs
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/StaticInput.UnitTests/Components/NavMenuTests.cs

- Fixed visibility of NavGroup items by managing 'invisible' class and 'display' style.
- Implemented smooth, reliable height transitions (250ms) using scrollHeight.
- Guaranteed smooth first-time expansion by forcing reflow from height 0px.
- Resolved persistent background highlight by using 'button.blur()' and avoiding 'mud-nav-group-expanded'.
- Added robust timeout management and WeakSet tracking to prevent state corruption and double-initialization.
- Updated unit tests to match new animation timings.

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
src/wwwroot/Extensions.MudBlazor.StaticInput.lib.module.js (1)

592-593: ⚠️ Potential issue | 🟠 Major

Unconditional blur() breaks keyboard navigation.

This call removes focus after every activation, causing keyboard users to lose their position in the nav menu. They cannot immediately re-toggle the same group without re-navigating to it.

Consider detecting the interaction type and only blurring for pointer events:

🔧 Suggested approach
-            button.addEventListener('click', (event) => {
+            let lastInteractionWasPointer = false;
+            button.addEventListener('pointerdown', () => {
+                lastInteractionWasPointer = true;
+            });
+            button.addEventListener('keydown', () => {
+                lastInteractionWasPointer = false;
+            });
+            button.addEventListener('click', (event) => {

Then at the end of the click handler:

-                // Remove focus to fix the persistent "hover" highlight issue
-                button.blur();
+                // Only blur for pointer interactions to fix hover highlight
+                // while preserving keyboard navigation
+                if (lastInteractionWasPointer) {
+                    button.blur();
+                    lastInteractionWasPointer = false;
+                }

Alternatively, resolve the persistent highlight via CSS using :focus-visible instead of :focus styling, which inherently distinguishes keyboard from pointer focus.

,

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/wwwroot/Extensions.MudBlazor.StaticInput.lib.module.js` around lines 592
- 593, The unconditional call to button.blur() inside the click handler removes
keyboard focus and breaks keyboard navigation; change the handler so it only
blurs for pointer interactions (e.g., check the event type or event.pointerType
/ event.detail to detect mouse/touch) and call button.blur() only when a pointer
initiated the activation, leaving keyboard activations focused, or alternatively
remove JavaScript blur and fix the visual by switching CSS focus styles to
:focus-visible instead of :focus; locate the click handler that invokes
button.blur() and implement the conditional logic around that call (or remove it
and update CSS).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/wwwroot/Extensions.MudBlazor.StaticInput.lib.module.js`:
- Around line 592-593: The unconditional call to button.blur() inside the click
handler removes keyboard focus and breaks keyboard navigation; change the
handler so it only blurs for pointer interactions (e.g., check the event type or
event.pointerType / event.detail to detect mouse/touch) and call button.blur()
only when a pointer initiated the activation, leaving keyboard activations
focused, or alternatively remove JavaScript blur and fix the visual by switching
CSS focus styles to :focus-visible instead of :focus; locate the click handler
that invokes button.blur() and implement the conditional logic around that call
(or remove it and update CSS).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a544da74-7424-4bd6-934e-0b7629d64c53

📥 Commits

Reviewing files that changed from the base of the PR and between d32f1e9 and bc87bad.

📒 Files selected for processing (1)
  • src/wwwroot/Extensions.MudBlazor.StaticInput.lib.module.js

@0phois 0phois deleted a comment from what-the-diff bot Mar 11, 2026
@Anu6is Anu6is merged commit b8d8511 into master Mar 11, 2026
2 of 3 checks passed
@Anu6is Anu6is deleted the fix/nav-group-visibility-and-animation-16877831955550158464 branch March 11, 2026 17:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant