From 45a5a92455dc6ff0dc01f0ebad20eaab1707bc1c Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Thu, 18 Jun 2026 03:05:23 -0400 Subject: [PATCH] =?UTF-8?q?fix(centralui):=20NodeBrowserDialog=20=E2=80=94?= =?UTF-8?q?=20reset=20load/expand=20spinner=20on=20error=20+=20clear=20sta?= =?UTF-8?q?le=20failure=20on=20blank=20search=20(T15)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Dialogs/NodeBrowserDialog.razor | 26 +++++++++++++---- .../NodeBrowserDialogSearchTests.cs | 28 +++++++++++++++++++ 2 files changed, 49 insertions(+), 5 deletions(-) diff --git a/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Dialogs/NodeBrowserDialog.razor b/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Dialogs/NodeBrowserDialog.razor index 5d15697e..d3bee874 100644 --- a/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Dialogs/NodeBrowserDialog.razor +++ b/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Dialogs/NodeBrowserDialog.razor @@ -211,8 +211,15 @@ { node.Loading = true; StateHasChanged(); - var result = await BrowseService.BrowseChildrenAsync(_runtimeSiteId, _runtimeConnectionName, node.NodeId); - node.Loading = false; + BrowseNodeResult result; + try + { + result = await BrowseService.BrowseChildrenAsync(_runtimeSiteId, _runtimeConnectionName, node.NodeId); + } + finally + { + node.Loading = false; + } if (result.Failure is not null) { @@ -237,9 +244,16 @@ node.Loading = true; StateHasChanged(); - var result = await BrowseService.BrowseChildrenAsync( - _runtimeSiteId, _runtimeConnectionName, node.NodeId, node.ContinuationToken); - node.Loading = false; + BrowseNodeResult result; + try + { + result = await BrowseService.BrowseChildrenAsync( + _runtimeSiteId, _runtimeConnectionName, node.NodeId, node.ContinuationToken); + } + finally + { + node.Loading = false; + } if (result.Failure is not null) { @@ -266,6 +280,8 @@ _searchActive = false; _searchResults = new(); _searchCapReached = false; + _failure = null; + _failureMessage = ""; StateHasChanged(); return; } diff --git a/tests/ZB.MOM.WW.ScadaBridge.CentralUI.Tests/Components/NodeBrowserDialogSearchTests.cs b/tests/ZB.MOM.WW.ScadaBridge.CentralUI.Tests/Components/NodeBrowserDialogSearchTests.cs index 8c7e727b..9ee5c2d5 100644 --- a/tests/ZB.MOM.WW.ScadaBridge.CentralUI.Tests/Components/NodeBrowserDialogSearchTests.cs +++ b/tests/ZB.MOM.WW.ScadaBridge.CentralUI.Tests/Components/NodeBrowserDialogSearchTests.cs @@ -124,4 +124,32 @@ public class NodeBrowserDialogSearchTests : BunitContext cut.Find("[data-test=node-search-button]").Click(); Assert.Empty(cut.FindAll("[data-test=node-search-result]")); } + + [Fact] + public void BlankQueryAfterFailure_ClearsStaleFailureAlert() + { + // First search returns a failure so the alert banner appears. + _browse.SearchAsync( + Arg.Any(), Arg.Any(), Arg.Any(), + Arg.Any(), Arg.Any(), Arg.Any()) + .Returns(new SearchAddressSpaceResult( + Matches: Array.Empty(), + CapReached: false, + Failure: new BrowseFailure(BrowseFailureKind.Timeout, "timed out"))); + + var cut = RenderShown(out _); + + cut.Find("[data-test=node-search-input]").Input("Pump1"); + cut.Find("[data-test=node-search-button]").Click(); + + // The failure alert must be visible after the failed search. + Assert.NotEmpty(cut.FindAll(".alert-danger")); + + // User clears the query and searches again (blank). + cut.Find("[data-test=node-search-input]").Input(""); + cut.Find("[data-test=node-search-button]").Click(); + + // Stale failure alert must be gone. + Assert.Empty(cut.FindAll(".alert-danger")); + } }