diff --git a/src/Core.Scripts/src/Components/Dialog/FluentDialog.ts b/src/Core.Scripts/src/Components/Dialog/FluentDialog.ts index 56175ff427..b5f6010172 100644 --- a/src/Core.Scripts/src/Components/Dialog/FluentDialog.ts +++ b/src/Core.Scripts/src/Components/Dialog/FluentDialog.ts @@ -1,14 +1,5 @@ export namespace Microsoft.FluentUI.Blazor.Components.Dialog { - const getDeepActiveElement = (): HTMLElement | null => { - let activeElement: Element | null = document.activeElement; - while (activeElement instanceof HTMLElement && activeElement.shadowRoot?.activeElement) { - activeElement = activeElement.shadowRoot.activeElement; - } - - return activeElement instanceof HTMLElement ? activeElement : null; - }; - /** * Tag names of non-modal, transient elements (e.g. toasts) that reuse the * dialog toggle plumbing but must never restore focus when they open or close. @@ -112,11 +103,16 @@ export namespace Microsoft.FluentUI.Blazor.Components.Dialog { return false; } - const activeElement = getDeepActiveElement(); - if (!activeElement || !dialog.contains(activeElement)) { + const activeElement = document.activeElement; + if (!(activeElement instanceof HTMLElement) || !dialog.contains(activeElement)) { return false; } + // The dialog surface itself receives focus when no interactive content does (e.g. MessageBox). + if (activeElement === dialog) { + return true; + } + // Keep shortcuts active for explicit dialog action surfaces. return !!activeElement.closest('[slot="action"], [slot="footer"], [slot="close"], [slot="title-action"]'); } diff --git a/tests/Core/Components/Dialog/FluentMessageBoxTests.razor b/tests/Core/Components/Dialog/FluentMessageBoxTests.razor index 9d346f6e82..6a41edfdae 100644 --- a/tests/Core/Components/Dialog/FluentMessageBoxTests.razor +++ b/tests/Core/Components/Dialog/FluentMessageBoxTests.razor @@ -8,6 +8,7 @@ public FluentMessageBoxTests() { JSInterop.Mode = JSRuntimeMode.Loose; + JSInterop.Setup("Microsoft.FluentUI.Blazor.Components.Dialog.ShouldHandleShortcut", _ => true).SetResult(true); Services.AddFluentUIComponents(options => options.UseGlobalOverlay = false); DialogService = Services.GetRequiredService(); @@ -154,6 +155,22 @@ DialogProvider.Verify(); } + [Theory(Timeout = TEST_TIMEOUT)] + [InlineData("Y", false)] + [InlineData("N", true)] + public async Task ShowConfirmationAsync_ShortcutPressed_ReturnsExpectedResult(string shortcut, bool expectedCancelled) + { + // Arrange + var dialogTask = DialogService.ShowConfirmationAsync("My message"); + + // Act + DialogProvider.Find("fluent-dialog").KeyDown(shortcut); + var result = await dialogTask; + + // Assert + Assert.Equal(expectedCancelled, result.Cancelled); + } + [Fact(Timeout = TEST_TIMEOUT)] public async Task FluentMessageBox_EmptyOptions() {