Name dialogs and contain native overlay keyboard focus

This commit is contained in:
Joseph Doherty
2026-11-13 17:42:14 -05:00
parent cdd3adf6fc
commit 87c882ec0a
46 changed files with 1353 additions and 119 deletions
@@ -33,6 +33,46 @@ public class DialogHostShowAsyncTests : BunitContext
JSInterop.Mode = JSRuntimeMode.Loose;
}
/// <summary>Unsupported overlapping presentation cancels safely; disposal resolves pending callers.</summary>
[Fact]
public async Task RejectedOverlayAndDisposal_ResolvePendingResults()
{
JSInterop.Setup<bool?>("sbDialog.activate", _ => true).SetResult(false);
var cut = Render<DialogHost>();
Task<bool> pending = null!;
await cut.InvokeAsync(() => { pending = _service.ConfirmAsync("Rejected", "Local only"); });
cut.WaitForAssertion(() => Assert.Null(_service.Current));
Assert.False(await pending);
JSInterop.Setup<bool?>("sbDialog.activate", _ => true).SetResult(true);
await cut.InvokeAsync(() => { pending = _service.ConfirmAsync("Open", "Local only"); });
await cut.InvokeAsync(async () => await cut.Instance.DisposeAsync());
Assert.False(await pending);
Assert.Null(_service.Current);
}
/// <summary>Names successive dialogs and connects prompt labels without stale descriptions.</summary>
[Fact]
public async Task Dialog_NamesAndPromptAssociationsFollowCurrentDialog()
{
var cut = Render<DialogHost>();
await cut.InvokeAsync(() => { _ = _service.ConfirmAsync("First title", "First body"); });
var first = cut.Find("[role=dialog]");
var titleId = first.GetAttribute("aria-labelledby");
Assert.False(string.IsNullOrWhiteSpace(titleId));
Assert.Equal("First title", cut.Find("#" + titleId).TextContent);
var descriptionId = first.GetAttribute("aria-describedby");
Assert.Equal("First body", cut.Find("#" + descriptionId).TextContent);
await cut.InvokeAsync(() => cut.Find("button.btn-close").Click());
await cut.InvokeAsync(() => { _ = _service.PromptAsync("Second title", "Enter a value"); });
var prompt = cut.Find("[role=dialog]");
Assert.Equal("Second title", cut.Find("#" + prompt.GetAttribute("aria-labelledby")).TextContent);
var input = cut.Find("input");
Assert.Equal(input.Id, cut.Find("label").GetAttribute("for"));
Assert.False(string.IsNullOrWhiteSpace(input.Id));
Assert.DoesNotContain("First body", cut.Markup);
await cut.InvokeAsync(() => cut.Find("button.btn-close").Click());
}
/// <summary>Verifies that ShowAsync renders a custom body and resolves the typed result.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]