diff --git a/src/Core.Scripts/src/Components/Dialog/FluentDialog.ts b/src/Core.Scripts/src/Components/Dialog/FluentDialog.ts index df4330a058..56175ff427 100644 --- a/src/Core.Scripts/src/Components/Dialog/FluentDialog.ts +++ b/src/Core.Scripts/src/Components/Dialog/FluentDialog.ts @@ -1,5 +1,14 @@ 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. @@ -90,4 +99,25 @@ export namespace Microsoft.FluentUI.Blazor.Components.Dialog { dialog.addEventListener('keydown', handler, true); } } + + /** + * Returns whether dialog keyboard shortcuts should be handled for the current focused element. + * Shortcuts are limited to dialog action areas so interactive content inside drawers/dialogs + * can use Enter/Space/Arrow keys without being intercepted. + * @param id The id of the fluent-dialog/fluent-drawer element + */ + export function ShouldHandleShortcut(id: string): boolean { + const dialog = document.getElementById(id) as HTMLElement | null; + if (!dialog) { + return false; + } + + const activeElement = getDeepActiveElement(); + if (!activeElement || !dialog.contains(activeElement)) { + return false; + } + + // Keep shortcuts active for explicit dialog action surfaces. + return !!activeElement.closest('[slot="action"], [slot="footer"], [slot="close"], [slot="title-action"]'); + } } diff --git a/src/Core.Scripts/src/Components/Menu/FluentMenu.ts b/src/Core.Scripts/src/Components/Menu/FluentMenu.ts index fa39308977..9b1ef8d908 100644 --- a/src/Core.Scripts/src/Components/Menu/FluentMenu.ts +++ b/src/Core.Scripts/src/Components/Menu/FluentMenu.ts @@ -8,31 +8,34 @@ export namespace Microsoft.FluentUI.Blazor.Components.Menu { * @param triggerId The id of the trigger element that will open the menu when clicked. */ export function Initialize(id: string, triggerId: string) { - const trigger = document.getElementById(triggerId) as HTMLElement | null; - if (!trigger) return; + const initWithRetry = (attempt: number = 0) => { + const trigger = document.getElementById(triggerId) as HTMLElement | null; + const menu = document.getElementById(id) as Menu | null; + if (!trigger || !menu) { + if (attempt < 10) { + requestAnimationFrame(() => initWithRetry(attempt + 1)); + } + return; + } - trigger.style["anchor-name" as any] = `--anchor-${triggerId}`; - const menuElement = document.getElementById(id) as Menu | null; - if (!menuElement) return; + trigger.style["anchor-name" as any] = `--anchor-${triggerId}`; - // Set the anchor name for the menu list to position it relative to the trigger - const doInit = () => { - const menu = document.getElementById(id) as Menu | null; - if (menu && menu.slottedMenuList?.length) { - menu.slottedMenuList[0].style["position-anchor" as any] = `--anchor-${triggerId}`; - menu.slottedTriggersChanged(menu.slottedTriggers, [trigger]); + // Keep trigger wiring explicit for hosted surfaces (drawer/dialog/shadow-heavy layouts). + menu.setAttribute("trigger", triggerId); + + const menuList = menu.slottedMenuList?.[0] as MenuList | undefined; + if (!menuList) { + if (attempt < 10) { + requestAnimationFrame(() => initWithRetry(attempt + 1)); + } + return; } - }; - // already populated (e.g. hot-reload / re-render) - if (menuElement.slottedMenuList?.length) { - doInit(); - } + menuList.style["position-anchor" as any] = `--anchor-${triggerId}`; + menu.slottedTriggersChanged(menu.slottedTriggers ?? [], [trigger]); + }; - // wait for slotchange macrotask - else { - requestAnimationFrame(doInit); - } + initWithRetry(); } /** diff --git a/src/Core/Components/DataGrid/Columns/ColumnBase.razor b/src/Core/Components/DataGrid/Columns/ColumnBase.razor index fac6ae1fac..4faa302268 100644 --- a/src/Core/Components/DataGrid/Columns/ColumnBase.razor +++ b/src/Core/Components/DataGrid/Columns/ColumnBase.razor @@ -1,5 +1,4 @@ @using Microsoft.AspNetCore.Components.Rendering -@using Microsoft.FluentUI.AspNetCore.Components.DataGrid.Infrastructure @using Microsoft.FluentUI.AspNetCore.Components.Extensions @namespace Microsoft.FluentUI.AspNetCore.Components @typeparam TGridItem @@ -35,7 +34,7 @@ @if (headerCapabilities.HasAnyAction) { - +
@HeaderTitleContent
@@ -65,7 +64,7 @@ } - + @if (headerCapabilities.CanSort) { @@ -188,7 +187,7 @@ private RenderFragment OptionsButton() { return - @ + @ ; } diff --git a/src/Core/Components/DataGrid/Columns/ColumnBase.razor.cs b/src/Core/Components/DataGrid/Columns/ColumnBase.razor.cs index dff48abeb4..3276a1dd31 100644 --- a/src/Core/Components/DataGrid/Columns/ColumnBase.razor.cs +++ b/src/Core/Components/DataGrid/Columns/ColumnBase.razor.cs @@ -8,7 +8,6 @@ using Microsoft.AspNetCore.Components.Web; using Microsoft.AspNetCore.Components.Web.Virtualization; using Microsoft.FluentUI.AspNetCore.Components.DataGrid.Infrastructure; -using Microsoft.FluentUI.AspNetCore.Components.Utilities; namespace Microsoft.FluentUI.AspNetCore.Components; @@ -19,8 +18,13 @@ namespace Microsoft.FluentUI.AspNetCore.Components; public abstract partial class ColumnBase { private static readonly string[] KEYBOARD_MENU_SELECT_KEYS = ["Enter", "NumpadEnter"]; - private readonly string _columnId = Identifier.NewId(); private FluentMenu? _menu; + private bool _suppressNextHeaderSyntheticClick; + private bool _openHeaderMenuAfterRender; + + private string HeaderButtonId => $"{Grid.Id}-col-{Index}"; + + private string HeaderMenuId => $"{HeaderButtonId}-menu"; /// [Inject] @@ -345,7 +349,29 @@ protected internal virtual Task OnCellKeyDownAsync(FluentDataGridCell internal bool CanSortFromHeader() => Sortable ?? IsSortableByDefault(); - private async Task HandleColumnHeaderClickedAsync() + /// + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (_openHeaderMenuAfterRender && _menu is not null) + { + _openHeaderMenuAfterRender = false; + await _menu.OpenMenuAsync(); + } + } + + private async Task HandleColumnHeaderClickedAsync(MouseEventArgs args) + { + if (_suppressNextHeaderSyntheticClick && args.Detail == 0) + { + _suppressNextHeaderSyntheticClick = false; + return; + } + + _suppressNextHeaderSyntheticClick = false; + await HandleColumnHeaderActivatedAsync(); + } + + private async Task HandleColumnHeaderActivatedAsync() { var headerCapabilities = HeaderCapabilities; var hasSorting = headerCapabilities.CanSort; @@ -362,8 +388,8 @@ private async Task HandleColumnHeaderClickedAsync() if (hasMultiple) { + _openHeaderMenuAfterRender = true; return; - //StateHasChanged(); } if (hasSorting) @@ -384,6 +410,33 @@ private async Task HandleColumnHeaderClickedAsync() } } + private async Task HandleHeaderButtonKeyDownAsync(KeyboardEventArgs args) + { + if (!string.Equals(args.Code, "Enter", StringComparison.OrdinalIgnoreCase) && + !string.Equals(args.Code, "Space", StringComparison.OrdinalIgnoreCase)) + { + return; + } + + if (HeaderCapabilities.HasAnyAction && Grid.HeaderCellAsButtonWithMenu) + { + _suppressNextHeaderSyntheticClick = true; + _openHeaderMenuAfterRender = true; + return; + } + + await HandleColumnHeaderActivatedAsync(); + } + + private async Task HandleOptionsButtonKeyDownAsync(KeyboardEventArgs args) + { + if (string.Equals(args.Code, "Enter", StringComparison.OrdinalIgnoreCase) || + string.Equals(args.Code, "Space", StringComparison.OrdinalIgnoreCase)) + { + await Grid.ShowAllHeaderUIAsync(this); + } + } + private async Task HandleSortMenuKeyDownAsync(KeyboardEventArgs args) { if (KEYBOARD_MENU_SELECT_KEYS.Contains(args.Key, StringComparer.OrdinalIgnoreCase)) diff --git a/src/Core/Components/DataGrid/FluentDataGrid.razor.ts b/src/Core/Components/DataGrid/FluentDataGrid.razor.ts index b8fda6af1a..375c7fec69 100644 --- a/src/Core/Components/DataGrid/FluentDataGrid.razor.ts +++ b/src/Core/Components/DataGrid/FluentDataGrid.razor.ts @@ -12,6 +12,52 @@ export namespace Microsoft.FluentUI.Blazor.DataGrid { element.style.visibility = 'hidden'; } }; + + 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; + }; + + const getFocusedGridElement = (gridElement: HTMLElement, event: KeyboardEvent): HTMLElement | null => { + const composedPath = event.composedPath(); + + for (const entry of composedPath) { + if (!(entry instanceof HTMLElement)) { + continue; + } + + const tableCell = entry.closest('td,th'); + if (tableCell instanceof HTMLElement && gridElement.contains(tableCell)) { + return tableCell; + } + + if (entry === gridElement) { + return gridElement; + } + } + + const activeElement = getDeepActiveElement(); + if (activeElement) { + const tableCell = activeElement.closest('td,th'); + if (tableCell instanceof HTMLElement && gridElement.contains(tableCell)) { + return tableCell; + } + + const table = activeElement.closest('table'); + if (table instanceof HTMLElement && table === gridElement) { + return table; + } + } + + return null; + }; + + const handledArrowNavigationEventFlag = '__fluentDataGridArrowNavigationHandled'; + interface Grid { id: string; columns: Column[]; // or a more specific type if you have one @@ -77,49 +123,173 @@ export namespace Microsoft.FluentUI.Blazor.DataGrid { }; const keyboardNavigation = (sibling: HTMLElement | null) => { if (sibling !== null) { - if (start) start.focus(); - sibling.focus(); - start = sibling; + // th elements are not focusable; transfer focus to the inner header button instead. + if (sibling.matches('th')) { + const headerButton = sibling.querySelector('.col-sort-button, .col-options-button'); + if (headerButton) { + sibling = headerButton; + } + } + + const focusSibling = () => { + sibling?.focus({ preventScroll: true }); + start = sibling; + }; + + // Defer focusing until after the current key event completes so focus traps + // or inner component key handlers cannot immediately override DataGrid focus. + setTimeout(() => { + focusSibling(); + + // Some host components can move focus again on the next frame. + // Re-apply focus once when focus escaped the current grid. + requestAnimationFrame(() => { + const activeElement = getDeepActiveElement(); + if (!activeElement || !gridElement.contains(activeElement)) { + focusSibling(); + } + }); + }, 0); } } + const getHeaderButtons = () => { + return Array.from( + gridElement.querySelectorAll('.column-header .col-sort-button, .column-header .col-options-button') + ).filter(button => button.tabIndex >= 0 && !button.hasAttribute('disabled')); + }; + const moveHeaderFocus = (current: HTMLElement, shiftKey: boolean) => { + const headerButtons = getHeaderButtons(); + const currentIndex = headerButtons.indexOf(current); + if (currentIndex < 0) { + return false; + } + + const nextIndex = shiftKey ? currentIndex - 1 : currentIndex + 1; + if (nextIndex < 0 || nextIndex >= headerButtons.length) { + return false; + } + + headerButtons[nextIndex].focus({ preventScroll: true }); + return true; + }; + const moveFocusIntoHeader = () => { + const headerButtons = getHeaderButtons(); + const firstHeaderButton = headerButtons[0]; + if (!firstHeaderButton) { + return false; + } + + firstHeaderButton.focus({ preventScroll: true }); + return true; + }; + const getAdjacentRowCell = (cell: HTMLTableCellElement, direction: 'up' | 'down') => { + const row = cell.parentElement as HTMLTableRowElement | null; + if (!row) { + return null; + } + + const rowGroupName = row.parentElement?.tagName.toLowerCase(); + let targetRow = direction === 'up' + ? row.previousElementSibling as HTMLTableRowElement | null + : row.nextElementSibling as HTMLTableRowElement | null; + + if (!targetRow) { + const table = row.closest('table'); + if (direction === 'down' && rowGroupName === 'thead') { + targetRow = table?.querySelector('tbody tr') as HTMLTableRowElement | null; + } else if (direction === 'up' && rowGroupName === 'tbody') { + targetRow = table?.querySelector('thead tr:last-child') as HTMLTableRowElement | null; + } + } + + if (!targetRow) { + return null; + } + + return targetRow.cells[cell.cellIndex] as HTMLTableCellElement | null; + }; const keyDownHandler = (event: KeyboardEvent) => { + if ((event as any)[handledArrowNavigationEventFlag]) { + return; + } + + const isArrowKey = event.key === "ArrowRight" || event.key === "ArrowLeft" || event.key === "ArrowDown" || event.key === "ArrowUp"; + const targetElement = event.target instanceof HTMLElement ? event.target : null; + const isMenuInteraction = event.composedPath().some((entry) => + entry instanceof HTMLElement && + (entry.matches('fluent-menu, fluent-menu-list, fluent-menu-item, [role="menu"], [role="menuitem"]') || + !!entry.closest('fluent-menu, fluent-menu-list, fluent-menu-item, [role="menu"], [role="menuitem"]')) + ); + + if (isArrowKey && isMenuInteraction) { + return; + } + + if (event.key === "Tab" && !event.shiftKey) { + const activeElement = getDeepActiveElement(); + if (activeElement && !activeElement.matches('.col-sort-button, .col-options-button')) { + const focusableElements = Array.from( + document.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])') + ).filter(element => element.tabIndex >= 0 && !element.hasAttribute('disabled') && element.getClientRects().length > 0); + const currentIndex = focusableElements.indexOf(activeElement); + if (currentIndex >= 0) { + const nextFocusable = focusableElements[currentIndex + 1] ?? null; + if (nextFocusable && getHeaderButtons().includes(nextFocusable) && moveFocusIntoHeader()) { + event.preventDefault(); + event.stopPropagation(); + return; + } + } + } + } + + if (event.key === "Tab" && targetElement && (targetElement.matches('.col-sort-button') || targetElement.matches('.col-options-button'))) { + if (moveHeaderFocus(targetElement, event.shiftKey)) { + event.preventDefault(); + event.stopPropagation(); + return; + } + } + const headerUiElement = gridElement?.querySelector(headerUiSelector); if (headerUiElement && headerUiElement.contains(event.target as HTMLElement)) { - if (event.key === "ArrowRight" || event.key === "ArrowLeft" || event.key === "ArrowDown" || event.key === "ArrowUp") { + if (isArrowKey) { event.stopPropagation(); return; } } - if (document.activeElement?.tagName.toLowerCase() != 'table' && document.activeElement?.tagName.toLowerCase() != 'td' && document.activeElement?.tagName.toLowerCase() != 'th') { + if (!isArrowKey) { return; } - if ((event.target as HTMLElement).getAttribute('role') !== "gridcell" && (event.key === "ArrowRight" || event.key === "ArrowLeft" || event.key === "ArrowDown" || event.key === "ArrowUp")) { + const focusedGridElement = getFocusedGridElement(gridElement, event); + if (!(focusedGridElement instanceof HTMLTableCellElement)) { return; } - // check if start is a child of gridElement - if (start !== null && (gridElement.contains(start) || gridElement === start) && document.activeElement === start && document.activeElement.tagName.toLowerCase() !== 'fluent-text-field' && document.activeElement.tagName.toLowerCase() !== 'fluent-menu-item') { - const idx = (start as HTMLTableCellElement).cellIndex; + if (targetElement && targetElement !== focusedGridElement && targetElement.closest('[role="gridcell"]') === focusedGridElement) { + return; + } + + if (start !== focusedGridElement) { + start = focusedGridElement; + } + + if (start !== null && (gridElement.contains(start) || gridElement === start)) { + (event as any)[handledArrowNavigationEventFlag] = true; const isRTL = getComputedStyle(gridElement).direction === 'rtl'; if (event.key === "ArrowUp") { - // up arrow - const previousRow = start.parentElement?.previousElementSibling as HTMLTableRowElement | null; - if (previousRow !== null) { - event.preventDefault(); - const previousSibling = previousRow.cells[idx]; - keyboardNavigation(previousSibling); - } + event.preventDefault(); + const previousSibling = getAdjacentRowCell(start as HTMLTableCellElement, 'up'); + keyboardNavigation(previousSibling); + event.stopPropagation(); } else if (event.key === "ArrowDown") { - // down arrow - const nextRow = start.parentElement?.nextElementSibling as HTMLTableRowElement | null; - if (nextRow !== null) { - event.preventDefault(); - const nextSibling = nextRow.cells[idx]; - keyboardNavigation(nextSibling); - } + event.preventDefault(); + const nextSibling = getAdjacentRowCell(start as HTMLTableCellElement, 'down'); + keyboardNavigation(nextSibling); + event.stopPropagation(); } else if (event.key === "ArrowLeft") { // left arrow event.preventDefault(); @@ -134,10 +304,6 @@ export namespace Microsoft.FluentUI.Blazor.DataGrid { event.stopPropagation(); } } - else { - start = document.activeElement as HTMLElement; - } - }; const cells = gridElement.querySelectorAll('[role="gridcell"]'); @@ -151,7 +317,7 @@ export namespace Microsoft.FluentUI.Blazor.DataGrid { } cell.addEventListener( "keydown", - (event: KeyboardEvent ) => { + (event: KeyboardEvent) => { if ((event.target as HTMLElement).role !== "gridcell" && (event.key === "ArrowRight" || event.key === "ArrowLeft")) { event.stopPropagation(); } @@ -163,7 +329,8 @@ export namespace Microsoft.FluentUI.Blazor.DataGrid { document.body.addEventListener('click', bodyClickHandler, { signal }); document.body.addEventListener('mousedown', bodyClickHandler, { signal }); document.body.addEventListener('keydown', bodyKeyDownHandler, { signal }); - gridElement.addEventListener('keydown', keyDownHandler, { signal }); + + gridElement.addEventListener('keydown', keyDownHandler, { signal, capture: true }); return { stop: () => { @@ -195,10 +362,20 @@ export namespace Microsoft.FluentUI.Blazor.DataGrid { colPopup.style.visibility = 'visible'; (colPopup as any).scrollIntoViewIfNeeded?.(); - const autoFocusElem = colPopup.querySelector('[autofocus]') as HTMLElement | null; - if (autoFocusElem) { - autoFocusElem.focus(); - } + requestAnimationFrame(() => { + const autoFocusElem = colPopup.querySelector('[autofocus]'); + if (autoFocusElem) { + autoFocusElem.focus({ preventScroll: true }); + return; + } + + const firstFocusable = colPopup.querySelector( + 'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])' + ); + if (firstFocusable && firstFocusable.getClientRects().length > 0) { + firstFocusable.focus({ preventScroll: true }); + } + }); } } @@ -588,7 +765,7 @@ export namespace Microsoft.FluentUI.Blazor.DataGrid { let headerBeingResized: HTMLElement | null | undefined; if (!column) { - const targetElement = (document.activeElement as HTMLElement)?.parentElement?.parentElement?.parentElement?.parentElement; + const targetElement = (document.activeElement as HTMLElement)?.parentElement; if (!(targetElement && targetElement.classList.contains('column-header') && targetElement.classList.contains('resizable'))) { return; } diff --git a/src/Core/Components/Dialog/FluentDialog.razor.cs b/src/Core/Components/Dialog/FluentDialog.razor.cs index ca64bdadaf..ada4482581 100644 --- a/src/Core/Components/Dialog/FluentDialog.razor.cs +++ b/src/Core/Components/Dialog/FluentDialog.razor.cs @@ -14,7 +14,7 @@ namespace Microsoft.FluentUI.AspNetCore.Components; /// The dialog component is a window overlaid on either the primary window or another dialog window. /// Windows under a modal dialog are inert. /// -public partial class FluentDialog : FluentComponentBase +public partial class FluentDialog : FluentComponentBase, IHandleEvent { private string? _shownInstanceId; @@ -109,6 +109,12 @@ protected override Task OnAfterRenderAsync(bool firstRender) /// internal async Task OnToggleAsync(DialogToggleEventArgs args) { + // The 'beforetoggle'/'toggle' DOM events are shared by the native element and the + // Popover API. Any popover rendered inside the dialog/drawer content (e.g. fluent-menu-list, + // select listbox, tooltip) also raises these events. Blazor's event delegation attributes + // them to this dialog's @ondialogtoggle handler. We must ignore events that don't target + // this dialog instance; otherwise the IHandleEvent implementation below would re-render the + // whole dialog subtree and detach any open popover content. if (string.CompareOrdinal(args.Id, Instance?.Id) != 0) { return; @@ -134,6 +140,29 @@ internal async Task OnToggleAsync(DialogToggleEventArgs args) } } + /// + /// Handles UI events for this component. + /// + /// + /// The dialog's content is supplied by the consumer (declaratively or through the + /// ) and is re-rendered on its own. The dialog's own event handlers + /// ( and ) only forward to dialog + /// actions/state callbacks that already request their own renders, so they don't need the + /// automatic StateHasChanged that the default implementation + /// performs after every callback. + /// + /// Suppressing that automatic render is important because the 'beforetoggle'/'toggle' and + /// 'keydown' DOM events also bubble from content rendered inside the dialog/drawer (for example a + /// fluent-menu-list popover, a select listbox or a DataGrid header). Blazor's event + /// delegation attributes those to this dialog's handlers, and an unnecessary re-render of the + /// dialog subtree would recreate keyed child content (e.g. DataGrid header cells) and detach any + /// open popover. + /// + /// + [ExcludeFromCodeCoverage(Justification = "Tested in aspnetcore code")] + Task IHandleEvent.HandleEventAsync(EventCallbackWorkItem callback, object? arg) + => callback.InvokeAsync(arg); + /// private async Task RaiseOnStateChangeAsync(DialogEventArgs args) { @@ -185,6 +214,12 @@ private async Task OnKeyDownHandlerAsync(Microsoft.AspNetCore.Components.Web.Key return; } + var shouldHandleShortcut = await JSRuntime.InvokeAsync("Microsoft.FluentUI.Blazor.Components.Dialog.ShouldHandleShortcut", Id); + if (!shouldHandleShortcut) + { + return; + } + var shortCut = $"{(e.CtrlKey ? "Ctrl+" : string.Empty)}{(e.AltKey ? "Alt+" : string.Empty)}{(e.ShiftKey ? "Shift+" : string.Empty)}{e.Key}"; // OK button diff --git a/tests/Core/Components/DataGrid/FluentDataGridTests.razor b/tests/Core/Components/DataGrid/FluentDataGridTests.razor index 64c09d430f..ac5fd34609 100644 --- a/tests/Core/Components/DataGrid/FluentDataGridTests.razor +++ b/tests/Core/Components/DataGrid/FluentDataGridTests.razor @@ -1735,6 +1735,33 @@ Assert.Equal(3, items.Count); // Only one item in the menu } + [Fact] + public void FluentDataGrid_HeaderCellAsButtonWithMenu_MenuKeyDown_KeyboardOpensMenu() + { + // Arrange && Act + FluentDataGrid? grid = default!; + var cut = Render>( + @ + + +
Hello!
+
+
+
+ ); + + var row = cut.FindComponent>(); + + row.Find(".col-sort-button").KeyDown(new KeyboardEventArgs() { Code = "Enter", Key = "Enter" }); + + var items = cut.FindAll("fluent-menu-item"); + + // Assert + Assert.NotEmpty(items); + Assert.Equal(3, items.Count); + Assert.Empty(cut.FindAll(".col-header-ui")); + } + [Fact] public void FluentDataGrid_HeaderCellAsButtonWithMenu_OnlySort() { diff --git a/tests/Core/Components/Dialog/FluentDialogTests.razor b/tests/Core/Components/Dialog/FluentDialogTests.razor index 60c7edf48f..a91e1341f1 100644 --- a/tests/Core/Components/Dialog/FluentDialogTests.razor +++ b/tests/Core/Components/Dialog/FluentDialogTests.razor @@ -3,11 +3,12 @@ @code { // A timeout can be set when you open a dialog box and do not close it. - private const int TEST_TIMEOUT = 3000; + private const int TEST_TIMEOUT = 13000; public FluentDialogTests() { JSInterop.Mode = JSRuntimeMode.Loose; + JSInterop.Setup("Microsoft.FluentUI.Blazor.Components.Dialog.ShouldHandleShortcut", _ => true).SetResult(true); Services.AddFluentUIComponents(options => options.UseGlobalOverlay = false); DialogService = Services.GetRequiredService();