Repository navigation
[DataGrid] Only handle character key shortcuts when the grid has focus - #5331
sebguischr wants to merge 5 commits into
Conversation
…focus The grid receives key presses through the document-wide FluentKeyCodeProvider listener and did not check where the focus was. Typing an uppercase S or R in any field on the page (including the filter box in a column's ColumnOptions) cleared the sort or reset the column widths of every grid on the page. Shift+R, Shift+S, + and - now only act while the focus is in the grid and not in a field the key types into (WCAG 2.1.4, Character Key Shortcuts). The grid's JavaScript controller answers that, walking shadow roots so that the input of a fluent-text-input counts. Other keys no longer cost a JS interop call. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Copilot review overview
🟢 Approval recommended
The focus gating is implemented end-to-end (TS + C#) and is covered by targeted bUnit tests that validate both the fixed scenarios and the “no extra interop for non-shortcuts” behavior.
Review effort: Lite
Findings: None
What changed in this PR
This PR fixes DataGrid character-key shortcuts so they only run when focus is inside the grid (and not in text-entry controls), preventing unintended Shift+S / Shift+R / +/- actions triggered by typing elsewhere on the page and aligning behavior with WCAG 2.1.4 character key shortcut guidance.
Changes:
- Added a JS controller check (
canHandleCharacterKeyShortcut) that detects whether focus is within the grid across shadow roots and not in a text-entry element. - Updated
FluentDataGrid.OnKeyDownAsyncto gate Shift+R / Shift+S / +/- handling on that focus check, avoiding unnecessary JS interop for non-shortcut keys. - Added/updated bUnit tests to cover “focus in grid vs outside” behavior and ensure non-shortcut keys do not trigger the focus query.
| File | Description |
|---|---|
| tests/Core/Components/DataGrid/FluentDataGridTests.razor | Adds new bUnit coverage for focus-gated Shift+S/Shift+R behavior and verifies non-shortcut keys don’t query focus. |
| tests/Core/Components/DataGrid/FluentDataGridMultiSortTests.razor | Updates existing Shift+S multi-sort tests to set up the new controller focus contract. |
| src/Core/Components/DataGrid/FluentDataGrid.razor.ts | Implements canHandleCharacterKeyShortcut() with shadow-root aware containment + text-entry detection. |
| src/Core/Components/DataGrid/FluentDataGrid.razor.cs | Gates character-key shortcuts behind IsCharacterKeyShortcut() + CanHandleCharacterKeyShortcutAsync() focus check. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| /// is reported by a listener on the whole document, so without this every grid on the page would reset its | ||
| /// sort or column widths when an uppercase S or R is typed anywhere (WCAG 2.1.4, Character Key Shortcuts). | ||
| /// </summary> | ||
| private async Task<bool> CanHandleCharacterKeyShortcutAsync() |
There was a problem hiding this comment.
If I understand correctly, every time a user presses a key, the JavaScript canHandleCharacterKeyShortcut function will be executed from Blazor Server to the browser (and for all the grids on the page).
In terms of performance, this is not acceptable.
There was a problem hiding this comment.
Thanks, good catch. To be precise, the round trip only happened for the four shortcut keys (Shift+R, Shift+S, +, -), not for every key, but that was still one Blazor Server → browser call per grid on the page each time one of them was typed anywhere, which is wasteful.
Reworked in 2c1fd7f: the shortcuts are now handled by the grid's own keydown listener in FluentDataGrid.razor.ts, which only receives key presses made in the grid (and skips grids nested in row details and text fields):
+,-and Shift+R run entirely in the browser (ResizeColumnDiscrete/ResetColumnWidths), so they never reach .NET;- Shift+S calls
RemoveSortByColumnAsynconce, through the grid reference now passed toInitialize; OnKeyDownAsync(the document-wide listener) no longer handles these shortcuts, so nothing is sent back to the browser anymore.
Checked in the demo, including nested grids on the Master/Detail page. I updated the PR description accordingly.
Review feedback: asking the browser where the focus is, from .NET, cost a round trip per grid on the page for every Shift+R, Shift+S, + and - pressed anywhere, which is too much with Blazor Server. The shortcuts are now handled by the grid's own keydown listener, which only receives key presses made in the grid. It skips key presses made in a grid nested in the row details and in fields the key types into (WCAG 2.1.4). +, - and Shift+R run in the browser (ResizeColumnDiscrete, ResetColumnWidths), so they no longer reach .NET at all; Shift+S calls RemoveSortByColumnAsync once through the grid reference given to Initialize. The document-wide key listener no longer handles these shortcuts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Hi sebguischr, We discussed it and we definitely see value in adding this. But we do want to take some more time to properly analyze the code and solution. We will not merge it in for the final v5 release just yet. We find the mechanism where a list of elements is checked on whether a keycode needs to go through is too limited and restrictive. What if we add a new component in the grid that also needs to not pickup a shortcut? Or what if someone adds a date picker (or other type not in your list). Keeping a list in the script updated seems not a good way forward for that. |
|
Thanks Vincent Baaij (@vnbaaij), no problem at all, it makes sense to take the time after the v5 release. Fair point about the list, it can never cover every component a cell may hold. What do you think of turning the check around: the grid would only act on a shortcut when the focus is on the grid's own elements (a cell or a column header button), and ignore it whenever the focus is on anything inside a cell, whatever its type (input, date picker, custom component…). No list to maintain, and it is the rule the grid's script already applies to arrow key navigation, which ignores keys whose target is inside a cell rather than the cell itself. If that direction suits you, I'll update the PR; otherwise I'm happy to follow whatever approach you prefer. |
|
Yes, turning the check around is a better approach I think |
Review feedback: a list of text-entry element types to skip can never cover every component a cell may hold (date pickers, custom components...). The check is turned around: the character key shortcuts only act when the key press is aimed at the grid itself, i.e. the table, one of its cells or one of its column header buttons, which is also how arrow key navigation tells the grid from the content of its cells. Anything a cell template renders is left alone, whatever its type, and so is a grid nested in the row details, whose cells belong to another table. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Done in 4431182: the check is turned around. The shortcuts only act when the key press is aimed at the grid itself (the table, one of its cells or one of its column header buttons), so anything a cell holds is left alone whatever its type, and the list is gone. A grid nested in the row details is left alone too, since its cells belong to another table. Checked in the demo: typing "Spain" in the Typical filter keeps the sort; Shift+S on a cell or on a column header button resets it; on the Master/Detail page, Shift+S in a nested grid leaves the parent grid's sort alone. |
There was a problem hiding this comment.
I tested the latest commit (4431182), merged locally with the current dev (no conflicts), .NET 10 SDK, Windows.
- All 505 DataGrid tests pass.
- Demo
/DataGrid/Typical: sorted by "Name of the country", opened its column options and typed "Spain" in the filter: the sort stays on "Name of the country" (ascending) and the grid is filtered to Spain. (I didn't re-check thedevbehavior myself; the PR description shows it.) - With the focus on a grid cell,
Shift+Sstill removes the sort (back to "Rank").
So both the bug and the existing shortcut behave as described. The branch is a few weeks behind dev, but it merges cleanly.
Pull Request
📖 Description
The DataGrid receives key presses through the document-wide
FluentKeyCodeProviderlistener (part of<FluentProviders />), butOnKeyDownAsyncnever checked where the focus was. As a result, typing an uppercase S or R in any text field on the page — including the filter box rendered in a column'sColumnOptions— cleared the sort (Shift+S) or reset the column widths (Shift+R) of every grid on the page.Repro on the demo site (
/DataGrid/Typical): sort by "Name of the country", open its column options and type "Spain" → the sort jumps back to "Rank" as soon as the "S" is typed.This also conflicts with WCAG 2.1.4 (Character Key Shortcuts): a shortcut made of a single character must only be active while the component has focus.
Fix
keydownlistener (on its<table>), and only act when the key press is aimed at the grid itself: the table, one of its cells (td/th) or one of its column header buttons. Anything a cell holds (a text field, a date picker, a custom component…) is left alone whatever its type, which is also how arrow key navigation tells the grid from the content of its cells. A grid nested in the row details is left alone too, since its cells belong to another table.+,-and Shift+R run entirely in the browser (ResizeColumnDiscrete,ResetColumnWidths, as before), so they never reach .NET. Shift+S callsRemoveSortByColumnAsynconce, through the grid reference now passed toInitialize(the method is marked[JSInvokable]).OnKeyDownAsync) no longer handles these four shortcuts, so key presses made anywhere else on the page cost nothing more than before. The Alt+F/P/N/L column reorder shortcuts are unchanged (they use a modifier and only apply while the reorder UI is open).Behavior change: Shift+R / Shift+S no longer act when the focus is outside the grid (e.g. on the page body) or on something inside a cell. They keep working when a cell, a column header button or the grid itself has focus.
🎫 Issues
None found.
👩💻 Reviewer Notes
Smoke test on the demo site,
/DataGrid/Typical:📑 Test Plan
FluentDataGridTests:+and-(theory: sort unchanged, no resize/reset call);Initializereceives a reference to the grid, andRemoveSortByColumnAsync()is[JSInvokable].FluentDataGridMultiSortTestsnow callRemoveSortByColumnAsync(), which is what the script calls./DataGrid/MasterDetail, Shift+S in a nested grid leaves the parent grid's sort alone, and Shift+S on a parent cell resets it.FluentNumber_Culture_FRfails, ondevas well — unrelated.)✅ Checklist
General
Component-specific
⏭ Next Steps
None.
🤖 Generated with Claude Code