Skip to content

feat(ContextMenu): make the context menu appearance speed configurable when touched#7521

Merged
ArgoZhang merged 7 commits intomainfrom
issue-7512
Jan 16, 2026
Merged

feat(ContextMenu): make the context menu appearance speed configurable when touched#7521
ArgoZhang merged 7 commits intomainfrom
issue-7512

Conversation

@peereflits
Copy link
Copy Markdown
Collaborator

@peereflits peereflits commented Jan 15, 2026

The context menu appearance speed is made configurable when touched.

Link issues

fixes #7512

Summary By Copilot

Added parameter OnTouchDelay to ContextMenuTrigger, improve touch logic.

ContextMenuTrigger now supports configurable OnTouchDelay for touch event handling, using options from BootstrapBlazorOptions. Replaces hardcoded delays with parameter value, renames TouchStart to IsTouchStarted for clarity, and exposes it for testing. Adds a new test to verify touch delay behavior and state transitions.

Note that ContextMenuOptions are already used in Table and TreeViewRow.

Note that the default timeout is changed from 200ms (hardcoded) to 500ms (configurable and used in Table and TreeViewRow).

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

Make the context menu touch-trigger delay configurable and align it with shared ContextMenu options.

New Features:

  • Add an OnTouchDelay parameter to ContextMenuTrigger to control the timeout for opening the context menu on touch devices, defaulting to the value from BootstrapBlazorOptions.ContextMenuOptions.

Enhancements:

  • Expose touch state via the IsTouchStarted property on ContextMenuTrigger and use it in touch handling logic.

Tests:

  • Add a unit test to verify touch-triggered context menu behavior respects the configured delay and updates IsTouchStarted correctly.

…ouched

Added parameter OnTouchDelay to ContextMenuTrigger, improve touch logic

ContextMenuTrigger now supports configurable OnTouchDelay for touch event handling, using options from BootstrapBlazorOptions. Replaces
hardcoded delays with parameter value, renames TouchStart to
IsTouchStarted for clarity, and exposes it for testing. Adds a new
test to verify touch delay behavior and state transitions.

Note that ContextMenuOptions are already used in Table and TreeViewRow.
@bb-auto
Copy link
Copy Markdown

bb-auto bot commented Jan 15, 2026

Thanks for your PR, @peereflits. Someone from the team will get assigned to your PR shortly and we'll get it reviewed.

@sourcery-ai
Copy link
Copy Markdown
Contributor

sourcery-ai bot commented Jan 15, 2026

Reviewer's Guide

Makes the touch-triggered context menu appearance delay configurable via a new OnTouchDelay parameter on ContextMenuTrigger, wired to BootstrapBlazorOptions.ContextMenuOptions, and updates touch handling/state plus tests to reflect the new behavior and default delay.

Sequence diagram for touch-triggered context menu with configurable delay

sequenceDiagram
    actor User
    participant Trigger as ContextMenuTrigger
    participant Zone as ContextMenuZone
    participant Menu as ContextMenu

    User->>Trigger: TouchStart
    alt Trigger not busy
        Trigger->>Trigger: IsBusy = true
        Trigger->>Trigger: IsTouchStarted = true
        Trigger->>Trigger: Task.Delay(OnTouchDelay)
        alt IsTouchStarted still true
            Trigger->>Trigger: Create MouseEventArgs
            Trigger->>Zone: OnContextMenu(MouseEventArgs)
            Zone->>Menu: Show
            Trigger->>Trigger: Task.Delay(OnTouchDelay)
        end
        Trigger->>Trigger: IsBusy = false
    end

    User->>Trigger: TouchEnd
    Trigger->>Trigger: IsTouchStarted = false
Loading

Class diagram for updated ContextMenuTrigger touch configuration

classDiagram
    class BootstrapComponentBase

    class ContextMenuTrigger {
        +ContextMenuTrigger(options IOptionsMonitor_BootstrapBlazorOptions)
        +int OnTouchDelay
        +bool IsTouchStarted
        -bool IsBusy
        -ContextMenuZone ContextMenuZone
        -Task OnTouchStart(e TouchEventArgs)
        -void OnTouchEnd()
        -Task OnContextMenu(e MouseEventArgs)
    }

    class IOptionsMonitor_BootstrapBlazorOptions {
        +BootstrapBlazorOptions CurrentValue
    }

    class BootstrapBlazorOptions {
        +ContextMenuOptions ContextMenuOptions
    }

    class ContextMenuOptions {
        +int OnTouchDelay
    }

    BootstrapComponentBase <|-- ContextMenuTrigger
    ContextMenuTrigger --> IOptionsMonitor_BootstrapBlazorOptions : constructor_injection
    IOptionsMonitor_BootstrapBlazorOptions --> BootstrapBlazorOptions : provides
    BootstrapBlazorOptions --> ContextMenuOptions : has
    ContextMenuTrigger --> ContextMenuOptions : reads_default_OnTouchDelay
Loading

File-Level Changes

Change Details Files
Make touch context menu trigger delay configurable and driven by options instead of a hardcoded value.
  • Inject IOptionsMonitor into ContextMenuTrigger via primary constructor.
  • Add OnTouchDelay component parameter defaulting to ContextMenuOptions.OnTouchDelay and enforcing values > 0.
  • Replace hardcoded 200ms Task.Delay calls in touch handling logic with the configurable OnTouchDelay value.
  • Rename the internal touch state flag to IsTouchStarted and expose it as a public read-only property for observability and testing.
  • Keep the busy flag logic but base both the pre-trigger and post-trigger delay windows on the configurable delay.
src/BootstrapBlazor/Components/ContextMenu/ContextMenuTrigger.cs
Add automated test coverage for the new touch delay behavior and touch state transitions.
  • Add a new unit test that sets a custom OnTouchDelay on ContextMenuTrigger and simulates touch start/end events.
  • Use Stopwatch to assert that the elapsed time for the touch interaction is at least twice the configured delay (pre-trigger + post-trigger).
  • Verify that IsTouchStarted toggles true on TouchStart and false on TouchEnd in the new test.
test/UnitTest/Components/ContextMenuTest.cs

Assessment against linked issues

Issue Objective Addressed Explanation
#7512 Add a configurable parameter to control the timeout before the context menu appears on touch input, instead of using a hardcoded delay.
#7512 Update the context menu touch-handling logic to use the configurable timeout parameter for both initial appearance and preventing rapid reactivation.

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

@bb-auto bb-auto bot added the enhancement New feature or request label Jan 15, 2026
@bb-auto bb-auto bot added this to the v10.2.0 milestone Jan 15, 2026
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 found 2 issues, and left some high level feedback:

  • The ContextMenu_TouchWithTimeout_Ok test relies on real elapsed wall-clock time (Stopwatch and ElapsedMilliseconds >= Delay * 2), which is likely to be flaky on slower CI or under load; consider restructuring it to assert on observable state changes (e.g., via fake timers or injected delay abstraction) instead of timing thresholds.
  • Exposing IsTouchStarted as a public property purely to support the new test leaks internal state into the public API; consider keeping it private/internal and validating behavior through the component’s visible effects (e.g., menu visibility) or using test-only access patterns instead.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The `ContextMenu_TouchWithTimeout_Ok` test relies on real elapsed wall-clock time (`Stopwatch` and `ElapsedMilliseconds >= Delay * 2`), which is likely to be flaky on slower CI or under load; consider restructuring it to assert on observable state changes (e.g., via fake timers or injected delay abstraction) instead of timing thresholds.
- Exposing `IsTouchStarted` as a public property purely to support the new test leaks internal state into the public API; consider keeping it private/internal and validating behavior through the component’s visible effects (e.g., menu visibility) or using test-only access patterns instead.

## Individual Comments

### Comment 1
<location> `src/BootstrapBlazor/Components/ContextMenu/ContextMenuTrigger.cs:43-46` </location>
<code_context>
+    /// Default is <see cref="ContextMenuOptions.OnTouchDelay"/> milliseconds. Must be greater than 0.
+    /// </summary>
+    [Parameter]
+    public int OnTouchDelay
+    {
+        get;
+        set { if(value > 0) field = value; }
+    } = options.CurrentValue.ContextMenuOptions.OnTouchDelay;
+
</code_context>

<issue_to_address>
**issue (bug_risk):** Silent rejection of non-positive OnTouchDelay values may be confusing for consumers.

The setter ignores non-positive values and keeps the previous value, which can be surprising since any `int` is accepted in markup without feedback. Consider either clamping to a minimum (e.g. `field = Math.Max(1, value);`), throwing an exception for invalid values, or validating and enforcing the minimum before setting this property so the behavior is explicit and predictable.
</issue_to_address>

### Comment 2
<location> `test/UnitTest/Components/ContextMenuTest.cs:111-120` </location>
<code_context>
+    public async Task ContextMenu_TouchWithTimeout_Ok()
</code_context>

<issue_to_address>
**suggestion (testing):** Avoid using real-time delays and Stopwatch in unit tests to reduce flakiness and improve performance.

This test uses real `Task.Delay` and `Stopwatch` and asserts `sw.ElapsedMilliseconds >= Delay * 2`, making it slow (~600ms per run for Delay=300) and prone to flakiness on slower or loaded CI. It also couples the test to the implementation detail that there are exactly two `OnTouchDelay` intervals.

Please refactor to avoid wall‑clock timing by using a much smaller delay and/or asserting on observable behavior (e.g., menu visibility or a callback invocation) rather than elapsed time. Ideally, abstract the timer behind an injectable clock/timer so tests can advance time deterministically while still verifying that `OnTouchDelay` controls when the menu appears.

Suggested implementation:

```csharp
    [Fact]
    public async Task ContextMenu_TouchWithTimeout_Ok()
    {
        // Use a much smaller delay in tests to reduce runtime and flakiness
        const int Delay = 10; // ms

```

Based on your comment, more changes are needed in the same test method (and possibly in the production code) that are not visible in the snippet:

1. **Remove dependence on wallclock time in the test:**
   - Find any usage of `Stopwatch` and `Assert.True(sw.ElapsedMilliseconds >= Delay * 2)` (or similar) inside `ContextMenu_TouchWithTimeout_Ok`.
   - Replace those assertions with checks on observable behavior, such as:
     - Asserting that the context menu is not visible before the "delay period" has been simulated.
     - Asserting that the context menu becomes visible (or a callback is invoked) after simulating the appropriate number of touch delay intervals.

2. **Avoid real `Task.Delay` where possible:**
   - If the test explicitly calls `await Task.Delay(Delay * 2)` (or similar) merely to wait out the timer, refactor to:
     - Either use a much smaller delay (matching the new `Delay = 10`) and assert behavior without relying on tight timing thresholds, or
     - Preferably, abstract the timing behavior in the component under test behind an injectable timer/clock service (e.g., `ITimer` or `IClock`) and:
       - In production, use a real implementation (`System.Threading.Timer` / `Task.Delay`).
       - In tests, use a fake implementation that allows you to advance "logical" time deterministically (e.g., `AdvanceBy(Delay)`), then assert on menu visibility or callbacks without calling `Task.Delay` at all.

3. **Clean up `using System.Diagnostics;` if no longer needed:**
   - After removing `Stopwatch` from the tests, remove the `using System.Diagnostics;` directive at the top of `ContextMenuTest.cs` if nothing else in the file uses it.

4. **Keep test intent but decouple from implementation detail:**
   - The test currently couples to the implementation detail that there are exactly two `OnTouchDelay` intervals by asserting `ElapsedMilliseconds >= Delay * 2`.
   - Instead, drive the component through its public API:
     - Simulate the touch start event.
     - Simulate passage of the first delay interval using your fake timer.
     - Assert that the menu is still hidden (if thats the intended behavior).
     - Simulate passage of the second delay interval.
     - Assert that the menu is now visible or the expected callback has been invoked.

Because I cant see the rest of `ContextMenu_TouchWithTimeout_Ok` or the `ContextMenuZone` implementation, youll need to wire the above suggestions into your existing test and component abstractions (or introduce an injectable timer) to fully eliminate realtime waiting and flaky timing assertions.
</issue_to_address>

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 Jan 15, 2026

Codecov Report

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

Additional details and impacted files
@@            Coverage Diff            @@
##              main     #7521   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files          748       748           
  Lines        32986     32998   +12     
  Branches      4585      4588    +3     
=========================================
+ Hits         32986     32998   +12     
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.

@peereflits
Copy link
Copy Markdown
Collaborator Author

@microsoft-github-policy-service agree

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

This PR makes the context menu touch delay configurable by adding an OnTouchDelay parameter to ContextMenuTrigger, aligning it with the existing configuration pattern used in Table and TreeViewRow components. The default delay is changed from 200ms to 500ms, matching the global ContextMenuOptions.OnTouchDelay configuration.

Changes:

  • Added OnTouchDelay parameter to ContextMenuTrigger with dependency injection of BootstrapBlazorOptions
  • Renamed TouchStart property to IsTouchStarted and made it public for testing
  • Added unit test to verify configurable touch delay behavior

Reviewed changes

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

File Description
src/BootstrapBlazor/Components/ContextMenu/ContextMenuTrigger.cs Added constructor with DI, new OnTouchDelay parameter, renamed TouchStart to IsTouchStarted, and updated touch handlers to use configurable delay
test/UnitTest/Components/ContextMenuTest.cs Added test ContextMenu_TouchWithTimeout_Ok to verify touch delay functionality and state management

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

Comment thread src/BootstrapBlazor/Components/ContextMenu/ContextMenuTrigger.cs Outdated
Comment thread src/BootstrapBlazor/Components/ContextMenu/ContextMenuTrigger.cs Outdated
Comment thread test/UnitTest/Components/ContextMenuTest.cs
Comment thread test/UnitTest/Components/ContextMenuTest.cs Outdated
peereflits and others added 2 commits January 15, 2026 11:05
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Signed-off-by: Marcel Peereboom <40403094+peereflits@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Signed-off-by: Marcel Peereboom <40403094+peereflits@users.noreply.github.com>
@ArgoZhang ArgoZhang changed the title #7512 : Make the context menu appearance speed configurable when touched feat(ContextMenu): make the context menu appearance speed configurable when touched Jan 16, 2026
@ArgoZhang ArgoZhang merged commit 81903b6 into main Jan 16, 2026
6 checks passed
@ArgoZhang ArgoZhang deleted the issue-7512 branch January 16, 2026 02:30
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(ContextMenu): make the context menu appearance speed configurable when touched

3 participants