Skip to content

[DataGrid] Only handle character key shortcuts when the grid has focus - #5331

Draft
sebguischr wants to merge 5 commits into
microsoft:devfrom
sebguischr:fix/datagrid-character-shortcuts
Draft

sebguischr wants to merge 5 commits into
microsoft:devfrom
sebguischr:fix/datagrid-character-shortcuts

Conversation

@sebguischr

@sebguischr sebguischr commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

Pull Request

📖 Description

The DataGrid receives key presses through the document-wide FluentKeyCodeProvider listener (part of <FluentProviders />), but OnKeyDownAsync never 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's ColumnOptions — 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

  • The shortcuts are handled by the grid's own keydown listener (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 calls RemoveSortByColumnAsync once, through the grid reference now passed to Initialize (the method is marked [JSInvokable]).
  • The document-wide listener (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:

  1. Sort by "Name of the country", open its column options, type "Spain" → the sort stays on "Name of the country".
  2. Press Escape, click a cell, press Shift+S → the sort goes back to "Rank" (the shortcut still works inside the grid).

📑 Test Plan

  • New bUnit tests in FluentDataGridTests:
    • the document-wide listener no longer acts on Shift+S, Shift+R, + and - (theory: sort unchanged, no resize/reset call);
    • Initialize receives a reference to the grid, and RemoveSortByColumnAsync() is [JSInvokable].
  • The two existing Shift+S tests in FluentDataGridMultiSortTests now call RemoveSortByColumnAsync(), which is what the script calls.
  • The key handling itself is JavaScript; verified manually in the demo (Chrome): typing "Spain" in the Typical filter keeps the sort; Shift+S on a cell or on a column header button resets it; on /DataGrid/MasterDetail, Shift+S in a nested grid leaves the parent grid's sort alone, and Shift+S on a parent cell resets it.
  • All 457 DataGrid tests pass. (On a French-culture machine FluentNumber_Culture_FR fails, on dev as well — unrelated.)

✅ Checklist

General

  • I have added tests for my changes.
  • I have tested my changes.
  • I have updated the project documentation to reflect my changes.
  • I have read the CONTRIBUTING documentation and followed the standards for this project.

Component-specific

  • I have added a new component
  • I have added Unit Tests for my new component
  • I have modified an existing component
  • I have validated the Unit Tests for an existing component

⏭ Next Steps

None.

🤖 Generated with Claude Code

…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>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.OnKeyDownAsync to 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()

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 RemoveSortByColumnAsync once, through the grid reference now passed to Initialize;
  • 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.

sebguischr and others added 2 commits September 24, 2026 17:59
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>
@vnbaaij Vincent Baaij (vnbaaij) added this to the v5.0 milestone Sep 24, 2026
@vnbaaij Vincent Baaij (vnbaaij) changed the title fix(DataGrid): only handle character key shortcuts when the grid has focus [DataGrid] Only handle character key shortcuts when the grid has focus Sep 24, 2026
@vnbaaij

Copy link
Copy Markdown
Collaborator

Hi sebguischr,
Thanks for your contribution.

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.

@vnbaaij Vincent Baaij (vnbaaij) modified the milestones: v5.0, v5.0.x Sep 24, 2026
@vnbaaij Vincent Baaij (vnbaaij) added the status:needs-investigation Needs additional investigation label Sep 24, 2026
@sebguischr

Copy link
Copy Markdown
Contributor Author

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.

@vnbaaij

Copy link
Copy Markdown
Collaborator

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>
@sebguischr

Copy link
Copy Markdown
Contributor Author

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.

@vnbaaij
Vincent Baaij (vnbaaij) marked this pull request as draft September 25, 2026 09:32

@oONoobMasterOo NoobMaster (oONoobMasterOo) left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 the dev behavior myself; the PR description shows it.)
  • With the focus on a grid cell, Shift+S still 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.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

status:needs-investigation Needs additional investigation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants