refactor(ui/shared): introduce IDialogService + DialogHost

Eliminates the per-page <ConfirmDialog @ref="_confirmDialog"
ConfirmButtonClass="btn-danger" /> boilerplate. Pages now inject
IDialogService and call ConfirmAsync(title, message, danger: true)
programmatically.

New scoped service holds a single active dialog (throws on nested
calls), with a global DialogHost mounted once in MainLayout that
renders the modal markup, owns body scroll-lock via Bootstrap's
modal-open class, traps focus on the modal element, and handles
Escape-to-cancel.

Same service also exposes PromptAsync, used to replace the bespoke
NewFolderDialog. Both ConfirmDialog and NewFolderDialog components
are deleted — their callers (~13 pages across Admin/Design/Deployment
/Monitoring) now go through the service.

DiffDialog stays as-is — different use case (before/after content).

bUnit tests in TopologyPageTests, DataConnectionsPageTests, and
TemplatesPageTests register IDialogService in their service
collection.

Also: a top-of-file Razor comment on Sites.razor pointing future
implementers at it as the reference list-page pattern.
This commit is contained in:
Joseph Doherty
2026-05-12 03:57:37 -04:00
parent e21791adb0
commit 8038aa7cb5
21 changed files with 351 additions and 260 deletions

View File

@@ -3,6 +3,7 @@ using Bunit;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.Extensions.DependencyInjection;
using NSubstitute;
using ScadaLink.CentralUI.Components.Shared;
using ScadaLink.Commons.Entities.Sites;
using ScadaLink.Commons.Interfaces.Repositories;
using DataConnectionsPage = ScadaLink.CentralUI.Components.Pages.Admin.DataConnections;
@@ -21,6 +22,9 @@ public class DataConnectionsPageTests : BunitContext
public DataConnectionsPageTests()
{
Services.AddSingleton(_siteRepo);
// Satisfy the page's [Inject] IDialogService — the host that actually
// renders the dialog lives in MainLayout, not in bUnit's render scope.
Services.AddScoped<IDialogService, DialogService>();
AddTestAuth();
JSInterop.Setup<string?>("treeviewStorage.load", _ => true).SetResult(null);