Skip to content

feat(ICloseable): add ICloseable interface#7644

Merged
ArgoZhang merged 11 commits intomainfrom
feat-dialog
Feb 9, 2026
Merged

feat(ICloseable): add ICloseable interface#7644
ArgoZhang merged 11 commits intomainfrom
feat-dialog

Conversation

@ArgoZhang
Copy link
Copy Markdown
Member

@ArgoZhang ArgoZhang commented Feb 9, 2026

Link issues

fixes #7643

Summary By Copilot

Regression?

  • Yes
  • No

Risk

  • High
  • Medium
  • Low

Verification

  • Manual (required)
  • Automated

Packaging changes reviewed?

  • Yes
  • No
  • N/A

☑️ Self Check before Merge

⚠️ Please check all items below before review. ⚠️

  • Doc is updated/provided or not needed
  • Demo is updated/provided or not needed
  • Merge the latest code from the main branch

Summary by Sourcery

Introduce a shared closable interface and apply it to dialog-related components while adding coverage for pre-close callbacks and simplifying test infrastructure.

New Features:

  • Add an ICloseable interface defining asynchronous close and pre-close callback hooks and implement it on DialogOption and Modal to standardize close behavior.

Enhancements:

  • Wire DialogOption.OnClosingAsync through Dialog to Modal so dialogs can veto closing based on asynchronous logic.
  • Simplify unit test infrastructure by removing the custom mock hosting environment helper and MockEnvironment type and updating test disposal to synchronously wait on DisposeAsync.

Tests:

  • Extend Dialog component tests to verify that an OnClosingAsync callback can prevent dialog closing and is invoked as expected.

Copilot AI review requested due to automatic review settings February 9, 2026 04:07
@bb-auto bb-auto bot added the enhancement New feature or request label Feb 9, 2026
@bb-auto bb-auto bot added this to the v10.3.0 milestone Feb 9, 2026
@sourcery-ai
Copy link
Copy Markdown
Contributor

sourcery-ai bot commented Feb 9, 2026

Reviewer's Guide

Introduces a new ICloseable interface to standardize close-related callbacks across dialog and modal components, wires Dialog and Modal to use the new pre-close callback, adds a regression test for the new behavior, and simplifies test infrastructure cleanup by removing a mock environment and properly awaiting async disposal.

Sequence diagram for dialog closing with new OnClosingAsync callback

sequenceDiagram
    actor User
    participant Dialog
    participant Modal
    participant ClientCode_OnClosingAsync

    User->>Dialog: Open dialog with DialogOption
    Dialog->>Dialog: Show(option)
    Dialog->>Modal: Set OnClosingAsync = option.OnClosingAsync
    Dialog->>Modal: Set OnCloseAsync = option.OnCloseAsync

    User->>Modal: Attempt to close
    Modal->>ClientCode_OnClosingAsync: OnClosingAsync()
    ClientCode_OnClosingAsync-->>Modal: bool canClose

    alt canClose is true
        Modal->>Modal: Proceed with close logic
        Modal->>ClientCode_OnClosingAsync: OnCloseAsync()
        Modal-->>User: Dialog closed
    else canClose is false
        Modal-->>User: Close cancelled, dialog remains open
    end
Loading

Class diagram for new ICloseable interface and implementing components

classDiagram
    direction LR

    class ICloseable {
        <<interface>>
        +Func~Task~ OnCloseAsync
        +Func~Task~1~bool~~ OnClosingAsync
    }

    class Modal {
        +Func~Task~ OnShownAsync
        +Func~Task~ OnCloseAsync
        +Func~Task~1~bool~~ OnClosingAsync
    }

    class DialogOption {
        +Modal? Modal
        +Func~Task~ OnCloseAsync
        +Func~Task~1~bool~~ OnClosingAsync
        +bool IsKeyboard
        +bool IsBackdrop
        +bool IsFade
    }

    class Dialog {
        -Modal _modal
        -Func~Task~ _onShownAsync
        -Func~Task~ _onCloseAsync
        -Func~Task~1~bool~~ _onClosingAsync
        +Task Show(DialogOption option)
    }

    ICloseable <|.. Modal
    ICloseable <|.. DialogOption

    Dialog --> Modal : uses
    Dialog --> DialogOption : configures_from
    DialogOption --> Modal : configuration_applied_via_Dialog
Loading

File-Level Changes

Change Details Files
Introduce ICloseable interface and implement it on DialogOption and Modal to unify close and pre-close callbacks.
  • Added new ICloseable interface exposing OnCloseAsync and OnClosingAsync callbacks.
  • Updated DialogOption to implement ICloseable and to expose an OnClosingAsync callback with XML docs inheriting from the interface.
  • Updated Modal component to implement ICloseable and use interface-based XML documentation for OnCloseAsync and OnClosingAsync parameters.
src/BootstrapBlazor/Components/Modal/IClosable.cs
src/BootstrapBlazor/Components/Dialog/DialogOption.cs
src/BootstrapBlazor/Components/Modal/Modal.razor.cs
Wire Dialog component to pass through the new OnClosingAsync callback to its underlying Modal instance.
  • Added a private field to Dialog to store the OnClosingAsync callback from DialogOption.
  • Assigned the DialogOption.OnClosingAsync to the Dialog’s internal callback when showing a dialog.
  • Updated the Dialog’s Modal markup to bind the OnClosingAsync parameter to the stored callback.
src/BootstrapBlazor/Components/Dialog/Dialog.razor.cs
src/BootstrapBlazor/Components/Dialog/Dialog.razor
Add unit test coverage for the OnClosingAsync behavior of dialogs.
  • Extended DialogTest to show a dialog with OnClosingAsync that returns false and sets a flag.
  • Verified that calling Close is blocked when OnClosingAsync returns false, the flag is set, and the dialog can still be closed via CloseCallback.
test/UnitTest/Components/DialogTest.cs
Clean up test infrastructure by removing unused mock environment setup and properly awaiting async disposal of BunitContext.
  • Removed AddMockEnvironment extension and MockEnvironment implementation from IServiceCollectionExtensions and corresponding host/file provider usings.
  • Stopped calling AddMockEnvironment in TestBase constructor.
  • Changed BunitContext disposal to synchronously wait on DisposeAsync via AsTask().Wait(), adding a clarifying comment and removing suppression of the CA2012 warning.
test/UnitTest/Extensions/IServiceCollectionExtensions.cs
test/UnitTest/Core/TestBase.cs

Assessment against linked issues

Issue Objective Addressed Explanation
#7643 Add an ICloseable interface to the codebase that defines the standard close-related callbacks.

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Copy Markdown
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey - I've left some high level feedback:

  • The new interface is named ICloseable but the file is IClosable.cs; consider renaming the file (or the type) so the spelling is consistent and matches the type name.
  • Blocking on Context.DisposeAsync().AsTask().Wait() in TestBase.Dispose can introduce deadlock risks; if you need synchronous disposal, prefer GetAwaiter().GetResult() or making the base test type IAsyncDisposable and disposing asynchronously in the test framework hook.
  • Since ICloseable is now shared by both Modal and DialogOption, you may want to move it to a more generic/shared folder/namespace than Components/Modal to better reflect its cross-component usage.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The new interface is named `ICloseable` but the file is `IClosable.cs`; consider renaming the file (or the type) so the spelling is consistent and matches the type name.
- Blocking on `Context.DisposeAsync().AsTask().Wait()` in `TestBase.Dispose` can introduce deadlock risks; if you need synchronous disposal, prefer `GetAwaiter().GetResult()` or making the base test type `IAsyncDisposable` and disposing asynchronously in the test framework hook.
- Since `ICloseable` is now shared by both `Modal` and `DialogOption`, you may want to move it to a more generic/shared folder/namespace than `Components/Modal` to better reflect its cross-component usage.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@codecov
Copy link
Copy Markdown

codecov bot commented Feb 9, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (1700fbb) to head (e32d5d5).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff            @@
##              main     #7644   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files          750       750           
  Lines        33179     33182    +3     
  Branches      4602      4602           
=========================================
+ Hits         33179     33182    +3     
Flag Coverage Δ
BB 100.00% <100.00%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a shared close/closing callback contract (ICloseable) and wires dialog options into the underlying modal’s “before close” pipeline, so consumers can block close actions via OnClosingAsync.

Changes:

  • Introduces ICloseable with OnCloseAsync / OnClosingAsync callbacks.
  • Implements ICloseable in Modal and DialogOption, and passes OnClosingAsync from Dialog to Modal.
  • Updates unit tests and test infrastructure (removes mock host environment registration; changes bUnit context disposal).

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
test/UnitTest/Extensions/IServiceCollectionExtensions.cs Removes unused mock host environment helper/types from unit test DI extensions.
test/UnitTest/Core/TestBase.cs Adjusts bUnit context disposal approach after bUnit async-dispose changes; removes mock environment registration.
test/UnitTest/Components/DialogTest.cs Adds a test scenario for OnClosingAsync blocking close.
src/BootstrapBlazor/Components/Modal/Modal.razor.cs Makes Modal implement ICloseable and uses <inheritdoc> for callback docs.
src/BootstrapBlazor/Components/Modal/IClosable.cs Adds the new ICloseable interface definition.
src/BootstrapBlazor/Components/Dialog/DialogOption.cs Makes DialogOption implement ICloseable and adds OnClosingAsync.
src/BootstrapBlazor/Components/Dialog/Dialog.razor.cs Captures DialogOption.OnClosingAsync for forwarding to Modal.
src/BootstrapBlazor/Components/Dialog/Dialog.razor Forwards OnClosingAsync into the nested Modal component.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

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

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(ICloseable): add ICloseable interface

2 participants