fix(adminui): render the equipment page confirm outside the tab branches (#832)

The shared RawConfirmDialog sat inside the Alarms tab branch, so Remove
reference (Tags) and Delete virtual tag (Virtual Tags) set it visible on
a page that did not render it. Pinned by brace depth in
DeleteConfirmationWiringTests.
This commit is contained in:
Joseph Doherty
2026-11-11 01:23:33 -05:00
parent 5bbe1e3c7a
commit 25a1e61141
2 changed files with 32 additions and 5 deletions
@@ -311,12 +311,14 @@ else
<ScriptedAlarmModal Visible="_alarmModalVisible" IsNew="_alarmModalIsNew" EquipmentId="@EquipmentId"
Existing="_alarmModalExisting" Scripts="_alarmScriptOptions"
OnSaved="OnAlarmSavedAsync" OnCancel="@(() => { _alarmModalVisible = false; })" />
@* One confirm for every destructive row action on this page (R04 H1) — the same RawConfirmDialog
/raw routes its deletes through. A mis-click used to delete immediately. *@
<RawConfirmDialog @bind-Visible="_confirmVisible" Title="@_confirmTitle" Message="@_confirmMessage"
ConfirmLabel="@_confirmLabel" OnConfirm="RunConfirmedAsync" />
}
@* One confirm for every destructive row action on this page (R04 H1) — the same RawConfirmDialog
/raw routes its deletes through. A mis-click used to delete immediately. It must render OUTSIDE the
tab branches (#832): inside the Alarms branch it existed only on that tab, so the Tags tab's Remove
and the Virtual Tags tab's Delete opened a dialog that was never on the page. *@
<RawConfirmDialog @bind-Visible="_confirmVisible" Title="@_confirmTitle" Message="@_confirmMessage"
ConfirmLabel="@_confirmLabel" OnConfirm="RunConfirmedAsync" />
}
</PageErrorBoundary>
@@ -28,6 +28,31 @@ public sealed class DeleteConfirmationWiringTests
Regex.Matches(src, "btn-outline-danger").Count.ShouldBe(3);
}
/// <summary>
/// #832: the shared confirm used to sit INSIDE the Alarms tab branch, so it was on the page only while that
/// tab was showing — the Tags tab's Remove and the Virtual Tags tab's Delete set the dialog visible and
/// nothing appeared. Pinned by brace depth: the dialog must be at the depth of the tab <c>@if</c> chain,
/// not inside any branch of it.
/// </summary>
[Fact]
public void Equipment_page_confirm_renders_outside_every_tab_branch()
{
var src = AdminUiSource.Read("Components", "Pages", "Uns", "EquipmentPage.razor");
var markup = src[..src.IndexOf("@code", StringComparison.Ordinal)];
int DepthAt(int index) =>
markup[..index].Count(c => c == '{') - markup[..index].Count(c => c == '}');
var tabChain = markup.IndexOf("@if (_activeTab == \"details\")", StringComparison.Ordinal);
var dialog = markup.IndexOf("<RawConfirmDialog", StringComparison.Ordinal);
tabChain.ShouldBeGreaterThan(0);
dialog.ShouldBeGreaterThan(tabChain, "the confirm follows the tab chain");
DepthAt(dialog).ShouldBe(DepthAt(tabChain), "the confirm must not be nested inside a tab branch");
// Positive control: a tab-scoped modal really is one level deeper, so the depth probe can tell them apart.
DepthAt(markup.IndexOf("<ScriptedAlarmModal", StringComparison.Ordinal)).ShouldBe(DepthAt(tabChain) + 1);
}
[Fact]
public void Script_editor_delete_confirms()
{