Skip to content

Fix overlay event prevention issue in BlazorUI (#13565) - #13771

Open
msynk wants to merge 3 commits into
developfrom
msynk/13565-blazorui-overlay-prevents-keydown-events
Open

msynk wants to merge 3 commits into
developfrom
msynk/13565-blazorui-overlay-prevents-keydown-events

Conversation

@msynk

@msynk msynk commented Oct 11, 2026 •

Copy link
Copy Markdown
Member

closes #13565

Summary by CodeRabbit

  • Bug Fixes
    • Keyboard actions inside nested dialogs, panels, modals, and callouts no longer trigger unintended actions in surrounding data grids, rows, or carousels.
    • Pressing Escape in a nested surface now dismisses that surface without also closing an outer surface.
    • Page-level keyboard shortcuts continue to receive key events while dialogs, modals, panels, and callouts are open.
    • Escape dismissal remains responsive to the active surface’s settings, including whether it is disabled.

@msynk
msynk requested a review from yasmoradi as a code owner October 11, 2026 10:54
@coderabbitai

coderabbitai Bot commented Oct 11, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Walkthrough

Surface keydown events now propagate to ancestor Blazor handlers. Nested surfaces report Escape ownership so outer surfaces can ignore an Escape handled inside them. DataGrid and Swiper also treat nested surfaces as key consumers.

Changes

Nested keyboard and Escape handling

Layer / File(s) Summary
Shared Escape ownership and verdicts
src/BlazorUI/Bit.BlazorUI/Scripts/Utils.ts, src/BlazorUI/Bit.BlazorUI/Utils/BitEscape*, src/BlazorUI/Bit.BlazorUI/Components/Notifications/Message/BitMessage.razor.cs, src/BlazorUI/Bit.BlazorUI/Components/Lists/Swiper/BitSwiper.ts, src/BlazorUI/Bit.BlazorUI.Extras/Scripts/BitBlazorUI.d.ts
Utilities track which surface owns an Escape event, send verdicts to listeners, and detect nested surfaces. .NET verdict state is consumed once. Swiper treats nested-surface elements as key consumers.
Surface propagation and nested Escape handling
src/BlazorUI/Bit.BlazorUI/Components/Surfaces/{Dialog,Modal,Panel,Callout}/*, src/BlazorUI/Bit.BlazorUI/Scripts/Callouts.ts, src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/CalloutsJsRuntimeExtensions.cs, src/BlazorUI/Bit.BlazorUI/Utils/BitEscapeVerdict.cs, src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Surfaces/*, src/BlazorUI/Tests/Bit.BlazorUI.Tests/Utils/*, src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/{Buttons,Extras,Inputs,Navs}/*, src/BlazorUI/CLAUDE.md
Surface roots no longer stop keydown propagation. Modal, Panel, and Callout handlers consume Escape verdicts; Callout toggling registers and removes nested Escape watches. Tests cover ancestor key propagation, nested dismissal, and updated callout-toggle arguments.
Nested-surface key handling in DataGrid
src/BlazorUI/Bit.BlazorUI.Extras/Components/DataGrid/*, src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Extras/DataGrid/BitDataGridTests.cs
DataGrid tracks whether a key originated in a nested surface and skips header, cell, and row key handling in that case. Tests cover Escape and Delete during cell editing.

Priority: ➖ Normal

Estimated code review effort: 4 (Complex) | ~50 minutes

Change: Bug fix · Severity of issue fixed: Medium

Sequence Diagram(s)

sequenceDiagram
  participant KeyboardEvent
  participant Utils
  participant InnerSurface
  participant OuterSurface
  participant PageHandler
  KeyboardEvent->>Utils: Identify the Escape owner
  Utils->>InnerSurface: Send the nested-surface verdict
  Utils->>OuterSurface: Send the foreign Escape verdict
  InnerSurface->>InnerSurface: Consume verdict and handle Escape
  OuterSurface->>OuterSurface: Consume verdict and skip dismissal
  KeyboardEvent->>PageHandler: Propagate keydown
Loading

Merge Risk: ⚪ Minimal · up to dedad

The stated keyboard-propagation acceptance path is exercised, and no material merge blocker is established. Merge after normal checks.

Pre-merge checks | Passed 4 | Failed 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 28.95% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 76 functions across 29 files. (7 skipped:… Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check Passed The PR implements the coding requirements in [#13565]. BitDialog, BitModal, BitCallout, and BitPanel no longer use root @onkeydown:stopPropagation. Utils provides shared nested-Escape ownership an…
Out of Scope Changes check Passed The changes stay within [#13565]. DataGrid, Swiper, and related key guards prevent newly propagated events from triggering parent component keyboard actions when the target is inside a nested surface.…
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title clearly identifies the main change: fixing overlay event prevention in BlazorUI. It is concise and directly related to the key propagation and nested-surface behavior addressed by the pull r…

Full details: Docstring Coverage

Explanation

Docstring coverage is 28.95% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 76 functions across 29 files. (7 skipped: 7 unsupported.)


  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR






🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR



  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

A rabbit taps Escape with care
The inner surface claims its share
The outer waits; the keys pass through
The grid ignores what nested layers do
Then page-side shortcuts wake anew

Comment @coderabbitai help to get the list of available commands.

@msynk

msynk commented Oct 11, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai full-review

…into msynk/13565-blazorui-overlay-prevents-keydown-events
@coderabbitai

coderabbitai Bot commented Oct 11, 2026 •

Copy link
Copy Markdown

@msynk I will review all changes in this PR again.

✅ Action performed

Full review finished.

@coderabbitai coderabbitai Bot 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.

🔇 Additional comments (24)
src/BlazorUI/Bit.BlazorUI/Scripts/Utils.ts (1)

1096-1114: LGTM!

src/BlazorUI/Bit.BlazorUI.Extras/Scripts/BitBlazorUI.d.ts (1)

12-12: LGTM!

src/BlazorUI/Bit.BlazorUI/Components/Lists/Swiper/BitSwiper.ts (1)

749-758: LGTM!

src/BlazorUI/Bit.BlazorUI/Utils/BitEscapeVerdict.cs (1)

1-29: LGTM!

src/BlazorUI/Bit.BlazorUI/Utils/BitEscapeClaimWatch.cs (1)

10-35: LGTM!

src/BlazorUI/Bit.BlazorUI/Components/Notifications/Message/BitMessage.razor.cs (1)

1422-1429: LGTM!

src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Dialog/BitDialog.razor (1)

16-18: LGTM!

src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Modal/BitModal.razor (1)

9-25: LGTM!

src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Panel/BitPanel.razor (1)

6-21: LGTM!

src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Callout/BitCallout.razor (1)

6-19: LGTM!

src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Callout/BitCallout.razor.cs (1)

898-923: LGTM!

src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Modal/BitModal.razor.cs (1)

1038-1063: LGTM!

src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Panel/BitPanel.razor.cs (1)

891-891: LGTM!

src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/CalloutsJsRuntimeExtensions.cs (1)

78-119: LGTM!

src/BlazorUI/Bit.BlazorUI/Scripts/Callouts.ts (1)

189-195: LGTM!

src/BlazorUI/CLAUDE.md (1)

95-105: LGTM!

src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Buttons/MenuButton/BitMenuButtonTests.cs (1)

1750-1750: LGTM!

src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Extras/PhoneInput/BitPhoneInputTests.cs (1)

899-900: LGTM!

Also applies to: 920-920, 969-969, 2673-2673

src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Inputs/CircularTimePicker/BitCircularTimePickerTests.cs (1)

3196-3196: LGTM!

src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Inputs/DatePicker/BitDatePickerTests.cs (1)

4866-4866: LGTM!

src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Inputs/DateRangePicker/BitDateRangePickerTests.cs (1)

4494-4494: LGTM!

src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Inputs/Dropdown/BitDropdownTests.cs (1)

1502-1502: LGTM!

src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Inputs/SearchBox/BitSearchBoxTests.cs (1)

3891-3891: LGTM!

src/BlazorUI/Tests/Bit.BlazorUI.Tests/Utils/BitSurfaceKeyPropagationContractTests.cs-92-93 (1)

92-93: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

⚠️ Unverified finding
Verification ran but could not confirm this finding. It is shown for review, not as a verified issue.

Dispatch the shortcut from inside each surface.

PressInside sends Ctrl+S to the selected element itself. The callout case selects .bit-clo, not its anchor button or opened content. These assertions can pass without establishing the requested propagation from inside each surface. Target a descendant control in each case, and check the opened callout content separately.


ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 48230ddc-8103-4567-a09d-b36145885442
📥 Commits

Reviewing files that changed from the base of the PR and between e09d4d2 and dedadef.

📒 Files selected for processing (36)
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/DataGrid/BitDataGrid.razor.cs
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/DataGrid/BitDataGrid.ts
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/DataGrid/BitDataGridCell.razor
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/DataGrid/BitDataGridRow.razor
  • src/BlazorUI/Bit.BlazorUI.Extras/Scripts/BitBlazorUI.d.ts
  • src/BlazorUI/Bit.BlazorUI/Components/Lists/Swiper/BitSwiper.ts
  • src/BlazorUI/Bit.BlazorUI/Components/Notifications/Message/BitMessage.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Callout/BitCallout.razor
  • src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Callout/BitCallout.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Dialog/BitDialog.razor
  • src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Modal/BitModal.razor
  • src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Modal/BitModal.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Panel/BitPanel.razor
  • src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Panel/BitPanel.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/CalloutsJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Scripts/Callouts.ts
  • src/BlazorUI/Bit.BlazorUI/Scripts/Utils.ts
  • src/BlazorUI/Bit.BlazorUI/Utils/BitEscapeClaimWatch.cs
  • src/BlazorUI/Bit.BlazorUI/Utils/BitEscapeVerdict.cs
  • src/BlazorUI/CLAUDE.md
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Buttons/MenuButton/BitMenuButtonTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Extras/DataGrid/BitDataGridTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Extras/PhoneInput/BitPhoneInputTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Inputs/CircularTimePicker/BitCircularTimePickerTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Inputs/DatePicker/BitDatePickerTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Inputs/DateRangePicker/BitDateRangePickerTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Inputs/Dropdown/BitDropdownTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Inputs/SearchBox/BitSearchBoxTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Inputs/TimePicker/BitTimePickerTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Navs/Breadcrumb/BitBreadcrumbTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Navs/DropMenu/BitDropMenuTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Surfaces/Callout/BitCalloutTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Surfaces/Modal/BitModalTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Surfaces/Panel/BitPanelTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Utils/BitEscapeClaimContractTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Utils/BitSurfaceKeyPropagationContractTests.cs

Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 0 remain after this review.

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

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Overlay surfaces stop every keydown from reaching Blazor handlers on ancestors, not only Escape

1 participant