Skip to content

Commit 61b2982

Browse files
committed
RadzenTabs: ensure render state is restored when tab event handlers throw
Follow-up to the #2558 fix. In SelectTab the StateHasChanged call was skipped if Change/SelectedIndexChanged threw, leaving the component stale (multiple tabs visually active); in SelectTabOnClient a throw left shouldRender stuck at false, blocking all further renders. Wrap both in finally so the render state is always restored. Addresses review feedback on commit df1dfa7. Fixes #2558
1 parent 38ea391 commit 61b2982

2 files changed

Lines changed: 46 additions & 14 deletions

File tree

Radzen.Blazor.Tests/TabsTests.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,27 @@ public void Tabs_EscapeKeyDownInPanelContent_UpdatesPropagation()
230230
Assert.True(component.RenderCount > renderCountBefore);
231231
}
232232

233+
[Fact]
234+
public void Tabs_ChangeHandlerThrows_StillRendersSelectedTab()
235+
{
236+
using var ctx = new TestContext();
237+
238+
var component = ctx.RenderComponent<RadzenTabs>(parameters => parameters
239+
.Add(p => p.SelectedIndex, 0)
240+
.Add(p => p.Change, (int _) => throw new System.InvalidOperationException("boom"))
241+
.Add(p => p.Tabs, TabsFragmentWithContent(("First", "First-Content"), ("Second", "Second-Content")))
242+
);
243+
244+
Assert.Contains("First-Content", component.Markup);
245+
246+
var secondTab = component.FindAll("button[role='tab']")[1];
247+
var ex = Record.Exception(() => secondTab.Click());
248+
249+
Assert.NotNull(ex);
250+
Assert.Contains("Second-Content", component.Markup);
251+
Assert.DoesNotContain("First-Content", component.Markup);
252+
}
253+
233254
[Fact]
234255
public void Tabs_NonNavigationKeyDown_DoesNotBlockSubsequentRenders()
235256
{

Radzen.Blazor/RadzenTabs.razor.cs

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -210,22 +210,27 @@ internal async Task SelectTab(RadzenTabsItem tab, bool raiseChange = false)
210210

211211
SetFocusedIndex();
212212

213-
if (raiseChange)
213+
try
214214
{
215-
await Change.InvokeAsync(selectedIndex);
215+
if (raiseChange)
216+
{
217+
await Change.InvokeAsync(selectedIndex);
216218

217-
await SelectedIndexChanged.InvokeAsync(selectedIndex);
219+
await SelectedIndexChanged.InvokeAsync(selectedIndex);
218220

219-
try
220-
{
221-
await Element.FocusAsync(preventScroll: true);
222-
}
223-
catch (JSDisconnectedException)
224-
{
221+
try
222+
{
223+
await Element.FocusAsync(preventScroll: true);
224+
}
225+
catch (JSDisconnectedException)
226+
{
227+
}
225228
}
226229
}
227-
228-
StateHasChanged();
230+
finally
231+
{
232+
StateHasChanged();
233+
}
229234
}
230235

231236
/// <inheritdoc />
@@ -342,9 +347,15 @@ internal async System.Threading.Tasks.Task SelectTabOnClient(RadzenTabsItem tab)
342347
}
343348

344349
shouldRender = false;
345-
await Change.InvokeAsync(selectedIndex);
346-
await SelectedIndexChanged.InvokeAsync(selectedIndex);
347-
shouldRender = true;
350+
try
351+
{
352+
await Change.InvokeAsync(selectedIndex);
353+
await SelectedIndexChanged.InvokeAsync(selectedIndex);
354+
}
355+
finally
356+
{
357+
shouldRender = true;
358+
}
348359

349360
try
350361
{

0 commit comments

Comments
 (0)