Skip to content

Fix item order issues in multi-api components (#12653)#12659

Open
msynk wants to merge 5 commits into
bitfoundation:developfrom
msynk:12653-blazorui-multiapi-issues
Open

Fix item order issues in multi-api components (#12653)#12659
msynk wants to merge 5 commits into
bitfoundation:developfrom
msynk:12653-blazorui-multiapi-issues

Conversation

@msynk

@msynk msynk commented Jul 14, 2026

Copy link
Copy Markdown
Member

closes #12653

Summary by CodeRabbit

  • Bug Fixes
    • Improved UI rendering consistency across accordion lists, button groups, menu buttons, choice groups, dropdowns, timelines, breadcrumbs, navs, and nav bars—particularly when options are conditionally added/removed.
    • Conditional items now preserve their expected order and selection/expanded/toggled state across re-renders.
    • Option UIs refresh more reliably after expand/collapse, selection changes, toggling, and dropdown search/filter updates.
    • Breadcrumb option ordering stays synchronized with what’s displayed.
  • Tests
    • Added/expanded bUnit/MSTest coverage validating conditional option ordering and refreshed UI behavior across the affected components.

@msynk msynk requested a review from yasmoradi July 14, 2026 04:33
@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 0df9285c-156c-453e-8ad6-392864b32d09

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Walkthrough

Several Blazor components now delegate option rendering to dedicated item components, explicitly refresh registered options after state changes, and preserve conditional option order. Breadcrumb adds DOM markers and JavaScript-based ordering synchronization. New bUnit tests cover ordering and interactions.

Changes

Option rendering and state refresh

Layer / File(s) Summary
Dedicated item rendering
src/BlazorUI/.../AccordionList/*, src/BlazorUI/.../ButtonGroup/*, src/BlazorUI/.../MenuButton/*, src/BlazorUI/.../Timeline/*
Per-item markup moved into internal generic components; option components render through those components and parent helpers are now internally accessible.
Input and navigation updates
src/BlazorUI/.../ChoiceGroup/*, src/BlazorUI/.../Dropdown/*, src/BlazorUI/.../Nav/*, src/BlazorUI/.../NavBar/*
Conditional Options/ChildContent branches were added, option refresh paths were introduced, and nested navigation content remains rendered while collapsed.
Breadcrumb ordering synchronization
src/BlazorUI/.../Breadcrumb/*, src/BlazorUI/Bit.BlazorUI/Scripts/Utils.ts, src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/*
Breadcrumb options emit marker attributes, and post-render JavaScript interop reorders registered items from rendered DOM order.
Validation coverage
src/BlazorUI/Tests/...
New tests verify conditional option ordering across components, button-group behavior, breadcrumb ordering, and navigation-bar selection state.

Estimated code review effort: 4 (Complex) | ~60 minutes

Suggested reviewers: yasmoradi

Sequence Diagram(s)

sequenceDiagram
  participant ParentComponent
  participant OptionComponent
  participant ItemRenderer
  ParentComponent->>OptionComponent: register option
  OptionComponent->>ItemRenderer: render item with parent and option
  ParentComponent->>OptionComponent: refresh option state
  OptionComponent-->>ItemRenderer: rerender item
Loading

Poem

I’m a rabbit hopping through the render-row,
Options line up where the DOM winds flow.
A hidden marker, a tiny refresh,
Keeps First, Middle, Last in mesh.
Buttons bloom and navs align—
Carrots applaud the ordered design!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 8.52% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: fixing item order issues in multi-API components.
Linked Issues check ✅ Passed The changes align with #12653 by preserving item order and handling conditional Option subcomponents without corrupting order.
Out of Scope Changes check ✅ Passed The added component refactors, ordering sync, and tests all support the stated order-preservation fix and appear in scope.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@msynk

msynk commented Jul 14, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai full-review

@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown

@msynk I'll perform a full review of all changes in this PR.

✅ Action performed

Full review finished.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/BlazorUI/Bit.BlazorUI/Components/Inputs/ChoiceGroup/BitChoiceGroup.razor (1)

38-41: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Preserve state and focus with a @key attribute.

When rendering a list of Blazor components dynamically, it is highly recommended to bind a @key attribute. This helps Blazor's diffing algorithm preserve DOM elements, component internal state, and user focus when list items are added, removed, or reordered.

♻️ Proposed fix
             `@foreach` (var item in _items)
             {
-                <_BitChoiceGroupItem Item="item" ChoiceGroup="this" />
+                <_BitChoiceGroupItem `@key`="item" Item="item" ChoiceGroup="this" />
             }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/BlazorUI/Bit.BlazorUI/Components/Inputs/ChoiceGroup/BitChoiceGroup.razor`
around lines 38 - 41, Update the foreach rendering in BitChoiceGroup to add a
stable `@key` for each _BitChoiceGroupItem, using the item’s unique identity,
while preserving the existing Item and ChoiceGroup parameters.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@src/BlazorUI/Bit.BlazorUI/Components/Buttons/ButtonGroup/BitButtonGroupOption.cs`:
- Around line 150-155: Guard the Parent.RegisterOption call in
OnInitializedAsync with a null check, matching BuildRenderTree’s behavior when
no BitButtonGroup parent exists. Update Dispose to call Parent.UnregisterOption
only when Parent is not null, while preserving the existing disposal state
handling.

In `@src/BlazorUI/Bit.BlazorUI/Components/Navs/NavBar/BitNavBar.razor.cs`:
- Around line 211-222: The internal selection update paths do not refresh navbar
options, leaving active styling stale. Add RefreshOptions() after
SetSelectedItemByCurrentUrl() in OnLocationChanged, and after
OnSelectItem.InvokeAsync(item) in SetSelectedItem, before StateHasChanged(),
while preserving the existing selection guards and assignment flow.

---

Nitpick comments:
In
`@src/BlazorUI/Bit.BlazorUI/Components/Inputs/ChoiceGroup/BitChoiceGroup.razor`:
- Around line 38-41: Update the foreach rendering in BitChoiceGroup to add a
stable `@key` for each _BitChoiceGroupItem, using the item’s unique identity,
while preserving the existing Item and ChoiceGroup parameters.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: d957eb77-e5ec-486f-be74-324e0b55dd3d

📥 Commits

Reviewing files that changed from the base of the PR and between 6d91524 and 85accf6.

📒 Files selected for processing (64)
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/AccordionList/BitAccordionList.razor
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/AccordionList/BitAccordionList.razor.cs
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/AccordionList/BitAccordionListOption.cs
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/AccordionList/_BitAccordionListItem.razor
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/AccordionList/_BitAccordionListItem.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Buttons/ButtonGroup/BitButtonGroup.razor
  • src/BlazorUI/Bit.BlazorUI/Components/Buttons/ButtonGroup/BitButtonGroup.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Buttons/ButtonGroup/BitButtonGroupOption.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Buttons/ButtonGroup/_BitButtonGroupItem.razor
  • src/BlazorUI/Bit.BlazorUI/Components/Buttons/ButtonGroup/_BitButtonGroupItem.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Buttons/MenuButton/BitMenuButton.razor
  • src/BlazorUI/Bit.BlazorUI/Components/Buttons/MenuButton/BitMenuButton.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Buttons/MenuButton/BitMenuButtonOption.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Buttons/MenuButton/_BitMenuButtonItem.razor
  • src/BlazorUI/Bit.BlazorUI/Components/Buttons/MenuButton/_BitMenuButtonItem.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/ChoiceGroup/BitChoiceGroup.razor
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/ChoiceGroup/BitChoiceGroup.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/ChoiceGroup/BitChoiceGroupOption.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/Dropdown/BitDropdown.razor
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/Dropdown/BitDropdown.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/Dropdown/BitDropdownOption.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Lists/Timeline/BitTimeline.razor
  • src/BlazorUI/Bit.BlazorUI/Components/Lists/Timeline/BitTimeline.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Lists/Timeline/BitTimelineOption.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Lists/Timeline/_BitTimelineItem.razor
  • src/BlazorUI/Bit.BlazorUI/Components/Lists/Timeline/_BitTimelineItem.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Navs/Breadcrumb/BitBreadcrumb.razor
  • src/BlazorUI/Bit.BlazorUI/Components/Navs/Breadcrumb/BitBreadcrumb.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Navs/Breadcrumb/BitBreadcrumbOption.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Navs/Nav/BitNav.razor
  • src/BlazorUI/Bit.BlazorUI/Components/Navs/Nav/BitNav.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Navs/Nav/BitNavOption.razor
  • src/BlazorUI/Bit.BlazorUI/Components/Navs/Nav/BitNavOption.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Navs/Nav/_BitNavChild.razor
  • src/BlazorUI/Bit.BlazorUI/Components/Navs/Nav/_BitNavChild.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Navs/NavBar/BitNavBar.razor
  • src/BlazorUI/Bit.BlazorUI/Components/Navs/NavBar/BitNavBar.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Navs/NavBar/BitNavBarOption.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Navs/NavBar/_BitNavBarItem.razor
  • src/BlazorUI/Bit.BlazorUI/Components/Navs/NavBar/_BitNavBarItem.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/UtilsJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Scripts/Utils.ts
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Buttons/Button/BitButtonTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Buttons/ButtonGroup/BitButtonGroupOptionsOrderTest.razor
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Buttons/ButtonGroup/BitButtonGroupOptionsOrderTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Buttons/ButtonGroup/BitButtonGroupTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Buttons/MenuButton/BitMenuButtonOptionsOrderTest.razor
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Buttons/MenuButton/BitMenuButtonOptionsOrderTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Buttons/MenuButton/BitMenuButtonTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Buttons/ToggleButton/BitToggleButtonTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Extras/AccordionList/BitAccordionListOptionsOrderTest.razor
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Extras/AccordionList/BitAccordionListOptionsOrderTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Inputs/ChoiceGroup/BitChoiceGroupOptionsOrderTest.razor
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Inputs/ChoiceGroup/BitChoiceGroupOptionsOrderTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Inputs/Dropdown/BitDropdownOptionsOrderTest.razor
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Inputs/Dropdown/BitDropdownOptionsOrderTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Lists/Timeline/BitTimelineOptionsOrderTest.razor
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Lists/Timeline/BitTimelineOptionsOrderTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Navs/Breadcrumb/BitBreadcrumbOptionsOrderTest.razor
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Navs/Breadcrumb/BitBreadcrumbOptionsOrderTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Navs/Nav/BitNavOptionsOrderTest.razor
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Navs/Nav/BitNavOptionsOrderTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Navs/NavBar/BitNavBarOptionsTest.razor
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Navs/NavBar/BitNavBarTests.cs

Comment thread src/BlazorUI/Bit.BlazorUI/Components/Navs/NavBar/BitNavBar.razor.cs
@msynk

msynk commented Jul 14, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai full-review

@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown

@msynk I'll perform a full review of all the changes in this PR again.

✅ Action performed

Full review finished.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/BlazorUI/Bit.BlazorUI/Components/Navs/NavBar/BitNavBar.razor.cs (1)

224-229: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Call RefreshOptions() when the selection is updated internally.

The internal selection update paths do not explicitly refresh the navbar options, leaving the active styling stale. When a user clicks an item or the URL changes in automatic mode, the selection state updates internally, but the child option components are not re-rendered (similar to how BitNav handles it).

  • src/BlazorUI/Bit.BlazorUI/Components/Navs/NavBar/BitNavBar.razor.cs#L224-L229: Add a call to RefreshOptions(); right before StateHasChanged(); in OnLocationChanged.
  • src/BlazorUI/Bit.BlazorUI/Components/Navs/NavBar/BitNavBar.razor.cs#L257-L266: Add a call to RefreshOptions(); right before StateHasChanged(); in SetSelectedItem.
🛠️ Proposed fixes
     private void OnLocationChanged(object? sender, LocationChangedEventArgs args)
     {
         SetSelectedItemByCurrentUrl();
 
+        RefreshOptions();
         StateHasChanged();
     }
     private async Task SetSelectedItem(TItem item)
     {
         if (item == SelectedItem && Reselectable is false) return;
 
         if (await AssignSelectedItem(item) is false) return;
 
         await OnSelectItem.InvokeAsync(item);
 
+        RefreshOptions();
         StateHasChanged();
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/BlazorUI/Bit.BlazorUI/Components/Navs/NavBar/BitNavBar.razor.cs` around
lines 224 - 229, Refresh navbar option components after internal selection
updates: in src/BlazorUI/Bit.BlazorUI/Components/Navs/NavBar/BitNavBar.razor.cs
lines 224-229, add RefreshOptions() before StateHasChanged() in
OnLocationChanged; make the same change in SetSelectedItem at lines 257-266.
src/BlazorUI/Bit.BlazorUI/Components/Buttons/MenuButton/BitMenuButton.razor.cs (1)

339-342: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Check Options as an alias for ChildContent.

The Options parameter is documented as an alias for ChildContent. If a user provides Options instead of ChildContent, ChildContent will be null. Consequently, this logic will bypass the early return and overwrite the _items collection with Items, discarding any options registered via the <BitMenuButtonOption> elements.

🐛 Proposed fix
-        if (ChildContent is not null || Items.Any() is false || Items == _oldItems) return;
+        if (ChildContent is not null || Options is not null || Items.Any() is false || Items == _oldItems) return;

         _oldItems = Items;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/BlazorUI/Bit.BlazorUI/Components/Buttons/MenuButton/BitMenuButton.razor.cs`
around lines 339 - 342, Update the items-refresh guard in the relevant
menu-button lifecycle method to treat the documented Options parameter as an
alias for ChildContent. When either ChildContent or Options is provided, return
before replacing _items; preserve the existing empty-Items and
unchanged-reference checks.
🧹 Nitpick comments (1)
src/BlazorUI/Bit.BlazorUI/Components/Inputs/ChoiceGroup/BitChoiceGroup.razor.cs (1)

272-283: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

Remove async from HandleChange to resolve compiler warning.

The HandleChange method is marked as async Task but contains no await expressions, which will cause a CS1998 compiler warning. Because the method performs only synchronous state updates, consider changing the return type to void or explicitly returning Task.CompletedTask.

♻️ Proposed refactor to use void
-    internal async Task HandleChange(TItem item)
+    internal void HandleChange(TItem item)
     {
         if (IsEnabled is false || ReadOnly || GetIsEnabled(item) is false) return;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/BlazorUI/Bit.BlazorUI/Components/Inputs/ChoiceGroup/BitChoiceGroup.razor.cs`
around lines 272 - 283, Update HandleChange to remove the unnecessary async
modifier and use a synchronous return type, preserving its existing state-update
logic and call behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@src/BlazorUI/Bit.BlazorUI/Components/Buttons/MenuButton/BitMenuButtonOption.cs`:
- Around line 78-83: Guard the Parent calls in
BitMenuButtonOption.OnInitializedAsync and Dispose with null-conditional
invocation: update RegisterOption and UnregisterOption so both lifecycle methods
safely handle a missing Parent. Apply this in
src/BlazorUI/Bit.BlazorUI/Components/Buttons/MenuButton/BitMenuButtonOption.cs
at lines 78-83 and 103-110.

In `@src/BlazorUI/Bit.BlazorUI/Components/Inputs/Dropdown/BitDropdown.razor.cs`:
- Around line 570-576: Update ShouldRenderOptionItem to avoid calling
GetSearchedItems for every option. When SearchFunction is not provided, evaluate
the current item directly against _searchText using the existing item-text
comparison logic; retain the SearchFunction path for custom filtering and
preserve the no-search behavior.

In `@src/BlazorUI/Bit.BlazorUI/Components/Inputs/Dropdown/BitDropdownOption.cs`:
- Around line 95-100: Guard nullable Parent access in
BitDropdownOption.OnInitializedAsync by registering with
Parent?.RegisterOption(this), and apply the same null-safe access in Dispose
using Parent?.UnregisterOption(this). Update both affected locations in
src/BlazorUI/Bit.BlazorUI/Components/Inputs/Dropdown/BitDropdownOption.cs (lines
95-100 and 126-133).

In `@src/BlazorUI/Bit.BlazorUI/Components/Lists/Timeline/_BitTimelineItem.razor`:
- Line 21: The class attribute in _BitTimelineItem should separate the base
bit-tln-itm class from the classes returned by Timeline.GetItemClasses(Item).
Add a literal space before that interpolation while preserving the existing
Timeline.Classes?.Item concatenation.

---

Outside diff comments:
In
`@src/BlazorUI/Bit.BlazorUI/Components/Buttons/MenuButton/BitMenuButton.razor.cs`:
- Around line 339-342: Update the items-refresh guard in the relevant
menu-button lifecycle method to treat the documented Options parameter as an
alias for ChildContent. When either ChildContent or Options is provided, return
before replacing _items; preserve the existing empty-Items and
unchanged-reference checks.

In `@src/BlazorUI/Bit.BlazorUI/Components/Navs/NavBar/BitNavBar.razor.cs`:
- Around line 224-229: Refresh navbar option components after internal selection
updates: in src/BlazorUI/Bit.BlazorUI/Components/Navs/NavBar/BitNavBar.razor.cs
lines 224-229, add RefreshOptions() before StateHasChanged() in
OnLocationChanged; make the same change in SetSelectedItem at lines 257-266.

---

Nitpick comments:
In
`@src/BlazorUI/Bit.BlazorUI/Components/Inputs/ChoiceGroup/BitChoiceGroup.razor.cs`:
- Around line 272-283: Update HandleChange to remove the unnecessary async
modifier and use a synchronous return type, preserving its existing state-update
logic and call behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 1494a02e-8424-4729-8773-f6830c453d1c

📥 Commits

Reviewing files that changed from the base of the PR and between 6d91524 and 61a8334.

📒 Files selected for processing (64)
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/AccordionList/BitAccordionList.razor
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/AccordionList/BitAccordionList.razor.cs
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/AccordionList/BitAccordionListOption.cs
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/AccordionList/_BitAccordionListItem.razor
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/AccordionList/_BitAccordionListItem.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Buttons/ButtonGroup/BitButtonGroup.razor
  • src/BlazorUI/Bit.BlazorUI/Components/Buttons/ButtonGroup/BitButtonGroup.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Buttons/ButtonGroup/BitButtonGroupOption.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Buttons/ButtonGroup/_BitButtonGroupItem.razor
  • src/BlazorUI/Bit.BlazorUI/Components/Buttons/ButtonGroup/_BitButtonGroupItem.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Buttons/MenuButton/BitMenuButton.razor
  • src/BlazorUI/Bit.BlazorUI/Components/Buttons/MenuButton/BitMenuButton.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Buttons/MenuButton/BitMenuButtonOption.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Buttons/MenuButton/_BitMenuButtonItem.razor
  • src/BlazorUI/Bit.BlazorUI/Components/Buttons/MenuButton/_BitMenuButtonItem.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/ChoiceGroup/BitChoiceGroup.razor
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/ChoiceGroup/BitChoiceGroup.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/ChoiceGroup/BitChoiceGroupOption.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/Dropdown/BitDropdown.razor
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/Dropdown/BitDropdown.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/Dropdown/BitDropdownOption.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Lists/Timeline/BitTimeline.razor
  • src/BlazorUI/Bit.BlazorUI/Components/Lists/Timeline/BitTimeline.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Lists/Timeline/BitTimelineOption.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Lists/Timeline/_BitTimelineItem.razor
  • src/BlazorUI/Bit.BlazorUI/Components/Lists/Timeline/_BitTimelineItem.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Navs/Breadcrumb/BitBreadcrumb.razor
  • src/BlazorUI/Bit.BlazorUI/Components/Navs/Breadcrumb/BitBreadcrumb.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Navs/Breadcrumb/BitBreadcrumbOption.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Navs/Nav/BitNav.razor
  • src/BlazorUI/Bit.BlazorUI/Components/Navs/Nav/BitNav.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Navs/Nav/BitNavOption.razor
  • src/BlazorUI/Bit.BlazorUI/Components/Navs/Nav/BitNavOption.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Navs/Nav/_BitNavChild.razor
  • src/BlazorUI/Bit.BlazorUI/Components/Navs/Nav/_BitNavChild.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Navs/NavBar/BitNavBar.razor
  • src/BlazorUI/Bit.BlazorUI/Components/Navs/NavBar/BitNavBar.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Navs/NavBar/BitNavBarOption.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Navs/NavBar/_BitNavBarItem.razor
  • src/BlazorUI/Bit.BlazorUI/Components/Navs/NavBar/_BitNavBarItem.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/UtilsJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Scripts/Utils.ts
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Buttons/Button/BitButtonTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Buttons/ButtonGroup/BitButtonGroupOptionsOrderTest.razor
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Buttons/ButtonGroup/BitButtonGroupOptionsOrderTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Buttons/ButtonGroup/BitButtonGroupTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Buttons/MenuButton/BitMenuButtonOptionsOrderTest.razor
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Buttons/MenuButton/BitMenuButtonOptionsOrderTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Buttons/MenuButton/BitMenuButtonTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Buttons/ToggleButton/BitToggleButtonTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Extras/AccordionList/BitAccordionListOptionsOrderTest.razor
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Extras/AccordionList/BitAccordionListOptionsOrderTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Inputs/ChoiceGroup/BitChoiceGroupOptionsOrderTest.razor
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Inputs/ChoiceGroup/BitChoiceGroupOptionsOrderTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Inputs/Dropdown/BitDropdownOptionsOrderTest.razor
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Inputs/Dropdown/BitDropdownOptionsOrderTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Lists/Timeline/BitTimelineOptionsOrderTest.razor
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Lists/Timeline/BitTimelineOptionsOrderTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Navs/Breadcrumb/BitBreadcrumbOptionsOrderTest.razor
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Navs/Breadcrumb/BitBreadcrumbOptionsOrderTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Navs/Nav/BitNavOptionsOrderTest.razor
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Navs/Nav/BitNavOptionsOrderTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Navs/NavBar/BitNavBarOptionsTest.razor
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Navs/NavBar/BitNavBarTests.cs

@msynk

msynk commented Jul 14, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai full-review

@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown

@msynk I'll perform a full review of all the changes in this PR again.

✅ Action performed

Full review finished.

@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown

@msynk I'll perform a full review of all the changes in this PR again.

✅ Action performed

Full review finished.


Your plan includes PR reviews subject to rate limits. More reviews will be available in 14 minutes.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/BlazorUI/Bit.BlazorUI/Components/Navs/NavBar/BitNavBar.razor.cs (1)

224-229: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Call RefreshOptions() when the selection is updated internally.

RefreshOptions() correctly ensures that option components re-render when the navbar state changes. However, because OnSetSelectedItem is only triggered when the SelectedItem parameter is updated by a parent component (via the [CallOnSet] attribute), internal selection updates bypass this refresh mechanism.

When a user clicks an item (triggering SetSelectedItem) or the URL changes in automatic mode (triggering OnLocationChanged), the selection state updates internally but the option components are not explicitly refreshed. As a result, the active/selected CSS class will fail to update on the rendered UI items.

Apply the following changes to ensure internal state updates also propagate to the options (similar to how BitNav.razor.cs handles it):

🐛 Proposed fix
     private void OnLocationChanged(object? sender, LocationChangedEventArgs args)
     {
         SetSelectedItemByCurrentUrl();
 
+        RefreshOptions();
         StateHasChanged();
     }
     private async Task SetSelectedItem(TItem item)
     {
         if (item == SelectedItem && Reselectable is false) return;
 
         if (await AssignSelectedItem(item) is false) return;
 
         await OnSelectItem.InvokeAsync(item);
 
+        RefreshOptions();
         StateHasChanged();
     }

Also applies to: 257-266

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/BlazorUI/Bit.BlazorUI/Components/Navs/NavBar/BitNavBar.razor.cs` around
lines 224 - 229, Update the internal selection flows in BitNavBar, specifically
SetSelectedItem and OnLocationChanged, to call RefreshOptions() after
SetSelectedItemByCurrentUrl or other selection state updates. Preserve the
existing StateHasChanged behavior and ensure both user-triggered selection and
automatic URL-driven selection refresh the option components.
♻️ Duplicate comments (1)
src/BlazorUI/Bit.BlazorUI/Components/Lists/Timeline/_BitTimelineItem.razor (1)

21-21: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Add a space before the @(Timeline.GetItemClasses(Item)) call.

Without a space, the base class bit-tln-itm will concatenate directly with the first class returned by GetItemClasses (e.g., resulting in bit-tln-itmcustom-class), breaking both the base styling and the applied class.

🐛 Proposed fix
-     class="bit-tln-itm@(Timeline.GetItemClasses(Item)) `@Timeline.Classes`?.Item">
+     class="bit-tln-itm @(Timeline.GetItemClasses(Item)) `@Timeline.Classes`?.Item">
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/BlazorUI/Bit.BlazorUI/Components/Lists/Timeline/_BitTimelineItem.razor`
at line 21, Update the class attribute in _BitTimelineItem to include a
separating space between the base class “bit-tln-itm” and the output of
Timeline.GetItemClasses(Item), preserving the existing Timeline.Classes?.Item
expression.
🧹 Nitpick comments (1)
src/BlazorUI/Bit.BlazorUI/Components/Buttons/ButtonGroup/BitButtonGroup.razor (1)

17-22: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add @key to the looped items to prevent rendering bugs.

In Blazor, looping over components without a @key can lead to state leakage or DOM patching bugs when items are added, removed, or reordered. Consider adding a @key (e.g., @key="item") to _BitButtonGroupItem, similar to how it was done safely in BitChoiceGroup.razor.

⚡ Proposed fix
     else
     {
         `@foreach` (var item in _items)
         {
-            <_BitButtonGroupItem ButtonGroup="this" Item="item" />
+            <_BitButtonGroupItem `@key`="item" ButtonGroup="this" Item="item" />
         }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/BlazorUI/Bit.BlazorUI/Components/Buttons/ButtonGroup/BitButtonGroup.razor`
around lines 17 - 22, Update the foreach rendering of _BitButtonGroupItem in the
ButtonGroup component to include a stable `@key` based on item, preserving correct
component state and DOM updates when _items changes.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/BlazorUI/Bit.BlazorUI/Components/Inputs/Dropdown/BitDropdown.razor.cs`:
- Around line 1051-1052: Remove RefreshOptions() from OnAfterRenderAsync, then
invoke it synchronously at the end of OnParametersSetAsync and immediately after
internal state mutations in SearchItems, ClearSearchBox, ClearComboBoxInput,
AddOrRemoveSelectedItem, UpdateSelectedItemsFromValues, and HandleOnClearClick,
following the established BitChoiceGroup pattern.

In `@src/BlazorUI/Bit.BlazorUI/Components/Lists/Timeline/BitTimelineOption.cs`:
- Around line 122-133: The child components receive stable parameter references
and do not refresh when parent-driven state changes. In
src/BlazorUI/Bit.BlazorUI/Components/Lists/Timeline/BitTimelineOption.cs lines
122-133, update BuildRenderTree and its _BitTimelineItem usage to provide a
changing parameter or explicit refresh trigger; in
src/BlazorUI/Bit.BlazorUI/Components/Navs/Nav/BitNavOption.razor lines 5-21,
apply the equivalent change to _BitNavChild so childItems.Any() is reevaluated
after nested options register and the display wrapper updates correctly.

---

Outside diff comments:
In `@src/BlazorUI/Bit.BlazorUI/Components/Navs/NavBar/BitNavBar.razor.cs`:
- Around line 224-229: Update the internal selection flows in BitNavBar,
specifically SetSelectedItem and OnLocationChanged, to call RefreshOptions()
after SetSelectedItemByCurrentUrl or other selection state updates. Preserve the
existing StateHasChanged behavior and ensure both user-triggered selection and
automatic URL-driven selection refresh the option components.

---

Duplicate comments:
In `@src/BlazorUI/Bit.BlazorUI/Components/Lists/Timeline/_BitTimelineItem.razor`:
- Line 21: Update the class attribute in _BitTimelineItem to include a
separating space between the base class “bit-tln-itm” and the output of
Timeline.GetItemClasses(Item), preserving the existing Timeline.Classes?.Item
expression.

---

Nitpick comments:
In
`@src/BlazorUI/Bit.BlazorUI/Components/Buttons/ButtonGroup/BitButtonGroup.razor`:
- Around line 17-22: Update the foreach rendering of _BitButtonGroupItem in the
ButtonGroup component to include a stable `@key` based on item, preserving correct
component state and DOM updates when _items changes.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: c6aa8ff5-80eb-460b-ac21-d96453e0b6b5

📥 Commits

Reviewing files that changed from the base of the PR and between 6d91524 and 968cb00.

📒 Files selected for processing (64)
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/AccordionList/BitAccordionList.razor
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/AccordionList/BitAccordionList.razor.cs
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/AccordionList/BitAccordionListOption.cs
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/AccordionList/_BitAccordionListItem.razor
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/AccordionList/_BitAccordionListItem.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Buttons/ButtonGroup/BitButtonGroup.razor
  • src/BlazorUI/Bit.BlazorUI/Components/Buttons/ButtonGroup/BitButtonGroup.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Buttons/ButtonGroup/BitButtonGroupOption.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Buttons/ButtonGroup/_BitButtonGroupItem.razor
  • src/BlazorUI/Bit.BlazorUI/Components/Buttons/ButtonGroup/_BitButtonGroupItem.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Buttons/MenuButton/BitMenuButton.razor
  • src/BlazorUI/Bit.BlazorUI/Components/Buttons/MenuButton/BitMenuButton.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Buttons/MenuButton/BitMenuButtonOption.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Buttons/MenuButton/_BitMenuButtonItem.razor
  • src/BlazorUI/Bit.BlazorUI/Components/Buttons/MenuButton/_BitMenuButtonItem.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/ChoiceGroup/BitChoiceGroup.razor
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/ChoiceGroup/BitChoiceGroup.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/ChoiceGroup/BitChoiceGroupOption.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/Dropdown/BitDropdown.razor
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/Dropdown/BitDropdown.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/Dropdown/BitDropdownOption.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Lists/Timeline/BitTimeline.razor
  • src/BlazorUI/Bit.BlazorUI/Components/Lists/Timeline/BitTimeline.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Lists/Timeline/BitTimelineOption.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Lists/Timeline/_BitTimelineItem.razor
  • src/BlazorUI/Bit.BlazorUI/Components/Lists/Timeline/_BitTimelineItem.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Navs/Breadcrumb/BitBreadcrumb.razor
  • src/BlazorUI/Bit.BlazorUI/Components/Navs/Breadcrumb/BitBreadcrumb.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Navs/Breadcrumb/BitBreadcrumbOption.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Navs/Nav/BitNav.razor
  • src/BlazorUI/Bit.BlazorUI/Components/Navs/Nav/BitNav.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Navs/Nav/BitNavOption.razor
  • src/BlazorUI/Bit.BlazorUI/Components/Navs/Nav/BitNavOption.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Navs/Nav/_BitNavChild.razor
  • src/BlazorUI/Bit.BlazorUI/Components/Navs/Nav/_BitNavChild.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Navs/NavBar/BitNavBar.razor
  • src/BlazorUI/Bit.BlazorUI/Components/Navs/NavBar/BitNavBar.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Navs/NavBar/BitNavBarOption.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Navs/NavBar/_BitNavBarItem.razor
  • src/BlazorUI/Bit.BlazorUI/Components/Navs/NavBar/_BitNavBarItem.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/UtilsJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Scripts/Utils.ts
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Buttons/Button/BitButtonTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Buttons/ButtonGroup/BitButtonGroupOptionsOrderTest.razor
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Buttons/ButtonGroup/BitButtonGroupOptionsOrderTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Buttons/ButtonGroup/BitButtonGroupTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Buttons/MenuButton/BitMenuButtonOptionsOrderTest.razor
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Buttons/MenuButton/BitMenuButtonOptionsOrderTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Buttons/MenuButton/BitMenuButtonTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Buttons/ToggleButton/BitToggleButtonTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Extras/AccordionList/BitAccordionListOptionsOrderTest.razor
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Extras/AccordionList/BitAccordionListOptionsOrderTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Inputs/ChoiceGroup/BitChoiceGroupOptionsOrderTest.razor
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Inputs/ChoiceGroup/BitChoiceGroupOptionsOrderTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Inputs/Dropdown/BitDropdownOptionsOrderTest.razor
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Inputs/Dropdown/BitDropdownOptionsOrderTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Lists/Timeline/BitTimelineOptionsOrderTest.razor
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Lists/Timeline/BitTimelineOptionsOrderTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Navs/Breadcrumb/BitBreadcrumbOptionsOrderTest.razor
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Navs/Breadcrumb/BitBreadcrumbOptionsOrderTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Navs/Nav/BitNavOptionsOrderTest.razor
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Navs/Nav/BitNavOptionsOrderTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Navs/NavBar/BitNavBarOptionsTest.razor
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Navs/NavBar/BitNavBarTests.cs

@msynk

msynk commented Jul 14, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai full-review

@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown

@msynk I'll perform a full review of all the changes in this PR again.

✅ Action performed

Full review finished.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (2)
src/BlazorUI/Bit.BlazorUI/Components/Navs/NavBar/BitNavBar.razor.cs (1)

216-229: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Call RefreshOptions() when the selection is updated internally.

RefreshOptions() correctly ensures that option components re-render when the navbar state changes. However, because OnSetSelectedItem is only triggered when the SelectedItem parameter is updated by a parent component (via the [CallOnSet] attribute), internal selection updates bypass this refresh mechanism.

When a user clicks an item (triggering SetSelectedItem) or the URL changes in automatic mode (triggering OnLocationChanged), the selection state updates internally but the option components are not explicitly refreshed. As a result, the active/selected CSS class will fail to update on the rendered UI items.

Apply the following changes to ensure internal state updates also propagate to the options (similar to how BitNav.razor.cs handles it).

🐛 Proposed fix for internal selection updates
     private void OnLocationChanged(object? sender, LocationChangedEventArgs args)
     {
         SetSelectedItemByCurrentUrl();

+        RefreshOptions();
         StateHasChanged();
     }

Also, apply a similar change to SetSelectedItem further down in the file:

    private async Task SetSelectedItem(TItem item)
    {
        if (item == SelectedItem && Reselectable is false) return;

        if (await AssignSelectedItem(item) is false) return;

        await OnSelectItem.InvokeAsync(item);

        RefreshOptions(); // <-- Add this
        StateHasChanged();
    }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/BlazorUI/Bit.BlazorUI/Components/Navs/NavBar/BitNavBar.razor.cs` around
lines 216 - 229, Update the internal selection flows to refresh navbar options
after state changes: call RefreshOptions() in SetSelectedItem after
OnSelectItem.InvokeAsync succeeds and before StateHasChanged, and call it in
OnLocationChanged after SetSelectedItemByCurrentUrl(). Preserve the existing
parent-driven OnSetSelectedItem behavior and event ordering.
src/BlazorUI/Bit.BlazorUI/Components/Inputs/Dropdown/BitDropdown.razor.cs (1)

1051-1052: 🚀 Performance & Scalability | 🟠 Major | 🏗️ Heavy lift

Move RefreshOptions() out of OnAfterRenderAsync to avoid UI flicker and double rendering.

Calling StateHasChanged() (via RefreshOptions()) on child components from within OnAfterRenderAsync forces them to re-render after the browser has already painted the parent component's updates. This causes an unnecessary double-render cycle on every parent render and will introduce a visible one-frame UI flicker when search text or selections change.

Remove this call from OnAfterRenderAsync. Instead, invoke RefreshOptions() synchronously at the end of OnParametersSetAsync() and exactly at the sites where the internal state mutates (such as inside SearchItems(), ClearSearchBox(), ClearComboBoxInput(), AddOrRemoveSelectedItem(), UpdateSelectedItemsFromValues(), and HandleOnClearClick()). This matches the pattern safely implemented in BitChoiceGroup.razor.cs.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/BlazorUI/Bit.BlazorUI/Components/Inputs/Dropdown/BitDropdown.razor.cs`
around lines 1051 - 1052, Remove RefreshOptions() from OnAfterRenderAsync, then
invoke it synchronously at the end of OnParametersSetAsync and immediately after
internal state changes in SearchItems, ClearSearchBox, ClearComboBoxInput,
AddOrRemoveSelectedItem, UpdateSelectedItemsFromValues, and HandleOnClearClick,
following the established BitChoiceGroup pattern.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@src/BlazorUI/Bit.BlazorUI/Components/Inputs/ChoiceGroup/BitChoiceGroupOption.cs`:
- Around line 122-133: Make the child item components refreshable by passing a
changing state parameter to _BitChoiceGroupItem in BitChoiceGroupOption.cs
(lines 122-133) and to _BitTimelineItem in BitTimelineOption.cs (lines 122-133).
Add or reuse a StateId value incremented by InternalStateHasChanged(), and pass
it alongside the existing Parent and Item parameters so each parent-driven state
update bypasses parameter equality checks.

---

Duplicate comments:
In `@src/BlazorUI/Bit.BlazorUI/Components/Inputs/Dropdown/BitDropdown.razor.cs`:
- Around line 1051-1052: Remove RefreshOptions() from OnAfterRenderAsync, then
invoke it synchronously at the end of OnParametersSetAsync and immediately after
internal state changes in SearchItems, ClearSearchBox, ClearComboBoxInput,
AddOrRemoveSelectedItem, UpdateSelectedItemsFromValues, and HandleOnClearClick,
following the established BitChoiceGroup pattern.

In `@src/BlazorUI/Bit.BlazorUI/Components/Navs/NavBar/BitNavBar.razor.cs`:
- Around line 216-229: Update the internal selection flows to refresh navbar
options after state changes: call RefreshOptions() in SetSelectedItem after
OnSelectItem.InvokeAsync succeeds and before StateHasChanged, and call it in
OnLocationChanged after SetSelectedItemByCurrentUrl(). Preserve the existing
parent-driven OnSetSelectedItem behavior and event ordering.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 2edcbc57-cddd-43ac-abde-9a00a9304a4b

📥 Commits

Reviewing files that changed from the base of the PR and between 6d91524 and e1f9bd8.

📒 Files selected for processing (64)
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/AccordionList/BitAccordionList.razor
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/AccordionList/BitAccordionList.razor.cs
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/AccordionList/BitAccordionListOption.cs
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/AccordionList/_BitAccordionListItem.razor
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/AccordionList/_BitAccordionListItem.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Buttons/ButtonGroup/BitButtonGroup.razor
  • src/BlazorUI/Bit.BlazorUI/Components/Buttons/ButtonGroup/BitButtonGroup.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Buttons/ButtonGroup/BitButtonGroupOption.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Buttons/ButtonGroup/_BitButtonGroupItem.razor
  • src/BlazorUI/Bit.BlazorUI/Components/Buttons/ButtonGroup/_BitButtonGroupItem.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Buttons/MenuButton/BitMenuButton.razor
  • src/BlazorUI/Bit.BlazorUI/Components/Buttons/MenuButton/BitMenuButton.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Buttons/MenuButton/BitMenuButtonOption.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Buttons/MenuButton/_BitMenuButtonItem.razor
  • src/BlazorUI/Bit.BlazorUI/Components/Buttons/MenuButton/_BitMenuButtonItem.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/ChoiceGroup/BitChoiceGroup.razor
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/ChoiceGroup/BitChoiceGroup.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/ChoiceGroup/BitChoiceGroupOption.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/Dropdown/BitDropdown.razor
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/Dropdown/BitDropdown.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/Dropdown/BitDropdownOption.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Lists/Timeline/BitTimeline.razor
  • src/BlazorUI/Bit.BlazorUI/Components/Lists/Timeline/BitTimeline.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Lists/Timeline/BitTimelineOption.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Lists/Timeline/_BitTimelineItem.razor
  • src/BlazorUI/Bit.BlazorUI/Components/Lists/Timeline/_BitTimelineItem.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Navs/Breadcrumb/BitBreadcrumb.razor
  • src/BlazorUI/Bit.BlazorUI/Components/Navs/Breadcrumb/BitBreadcrumb.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Navs/Breadcrumb/BitBreadcrumbOption.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Navs/Nav/BitNav.razor
  • src/BlazorUI/Bit.BlazorUI/Components/Navs/Nav/BitNav.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Navs/Nav/BitNavOption.razor
  • src/BlazorUI/Bit.BlazorUI/Components/Navs/Nav/BitNavOption.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Navs/Nav/_BitNavChild.razor
  • src/BlazorUI/Bit.BlazorUI/Components/Navs/Nav/_BitNavChild.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Navs/NavBar/BitNavBar.razor
  • src/BlazorUI/Bit.BlazorUI/Components/Navs/NavBar/BitNavBar.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Navs/NavBar/BitNavBarOption.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Navs/NavBar/_BitNavBarItem.razor
  • src/BlazorUI/Bit.BlazorUI/Components/Navs/NavBar/_BitNavBarItem.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/UtilsJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Scripts/Utils.ts
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Buttons/Button/BitButtonTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Buttons/ButtonGroup/BitButtonGroupOptionsOrderTest.razor
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Buttons/ButtonGroup/BitButtonGroupOptionsOrderTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Buttons/ButtonGroup/BitButtonGroupTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Buttons/MenuButton/BitMenuButtonOptionsOrderTest.razor
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Buttons/MenuButton/BitMenuButtonOptionsOrderTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Buttons/MenuButton/BitMenuButtonTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Buttons/ToggleButton/BitToggleButtonTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Extras/AccordionList/BitAccordionListOptionsOrderTest.razor
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Extras/AccordionList/BitAccordionListOptionsOrderTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Inputs/ChoiceGroup/BitChoiceGroupOptionsOrderTest.razor
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Inputs/ChoiceGroup/BitChoiceGroupOptionsOrderTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Inputs/Dropdown/BitDropdownOptionsOrderTest.razor
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Inputs/Dropdown/BitDropdownOptionsOrderTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Lists/Timeline/BitTimelineOptionsOrderTest.razor
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Lists/Timeline/BitTimelineOptionsOrderTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Navs/Breadcrumb/BitBreadcrumbOptionsOrderTest.razor
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Navs/Breadcrumb/BitBreadcrumbOptionsOrderTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Navs/Nav/BitNavOptionsOrderTest.razor
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Navs/Nav/BitNavOptionsOrderTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Navs/NavBar/BitNavBarOptionsTest.razor
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Navs/NavBar/BitNavBarTests.cs

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.

Item order issue in multi-API components

1 participant