Skip to content

[toast] Fix timers not resuming after window regains focus - #4438

Merged
atomiks merged 4 commits into
mui:masterfrom
kyrregjerstad:fix/toast-timer-resume-on-window-focus
Mar 27, 2026
Merged

atomiks merged 4 commits into
mui:masterfrom
kyrregjerstad:fix/toast-timer-resume-on-window-focus

Conversation

@kyrregjerstad

@kyrregjerstad kyrregjerstad commented Mar 25, 2026 •

Copy link
Copy Markdown
Contributor

Closes #4439

Summary

  • The handleWindowFocus handler in ToastViewport had a guard (getTarget(event) === win) that caused an early return when the window itself was the focus target — which is exactly the case when alt-tabbing back to the browser.
  • This prevented resumeTimers() and setIsWindowFocused(true) from being called, causing toasts to remain visible indefinitely after switching away and back.
  • The event.relatedTarget check alone is sufficient to filter intra-page focus moves without blocking window-level refocus.

Steps to reproduce

  1. Open the Toast demo
  2. Trigger a toast
  3. Before it auto-dismisses, switch to another window (alt-tab)
  4. Switch back
  5. Before fix: toast never auto-dismisses
  6. After fix: toast dismisses after its remaining timeout

Test plan

  • Existing toast tests pass (pnpm test:jsdom toast --no-watch — 206 passed)
  • Manual verification: trigger toast → alt-tab away → alt-tab back → toast dismisses

The handleWindowFocus guard `getTarget(event) === win` caused an early
return when the window itself was the focus target, which is exactly the
alt-tab-back scenario. The `event.relatedTarget` check alone is
sufficient to filter intra-page focus moves.
@pkg-pr-new

pkg-pr-new Bot commented Mar 25, 2026 •

Copy link
Copy Markdown

commit: 8478ca3

@mui-bot

mui-bot commented Mar 25, 2026 •

Copy link
Copy Markdown

Bundle size report

Bundle Parsed size Gzip size
@base-ui/react ▼-4B(0.00%) ▼-4B(0.00%)

Details of bundle changes


Check out the code infra dashboard for more information about this PR.

@flaviendelangle flaviendelangle left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

PR #4438 Review: [toast] Fix timers not resuming after window regains focus

Author: kyrregjerstad | Diff: +1 / -1 in 1 file | Closes: #4439


Overview

A one-line fix that removes an overly aggressive guard in handleWindowFocus. The original condition:

if (event.relatedTarget || getTarget(event) === win) {
  return;
}

was short-circuiting the exact scenario it needed to handle — when the user alt-tabs back into the browser, event.target is the window object, so the guard caused an early return, preventing resumeTimers() and setIsWindowFocused(true) from ever being called.

Analysis

Correctness — looks right. There's a clear symmetry issue in the current code:

  • handleWindowBlur (line 83–90) acts when getTarget(event) === win (pauses timers)
  • handleWindowFocus (line 92) was skipping when getTarget(event) === win (blocking resume)

The fix restores logical symmetry. The remaining event.relatedTarget check is sufficient: when focus moves within the page, relatedTarget is non-null (the element that lost focus), so intra-page focus moves are still filtered out. When focus arrives from outside the page (alt-tab back), relatedTarget is null, allowing the handler to proceed.

Scope of change — narrow. The only behavioral difference is for events where getTarget(event) === win && !event.relatedTarget, which is specifically the window-regains-focus scenario. All other paths are unchanged.

Concerns

  1. No regression test. The PR author acknowledges this is a manual-only verification. This is understandable — synthesizing a true window-level focus/blur event in JSDOM is not realistic, and even Chromium tests would need tricky alt-tab simulation. Still, it would be nice to have a unit test that directly calls handleWindowFocus with a synthetic event where relatedTarget = null and target = window, if possible via the store/timer APIs.

  2. Original intent is unclear. The getTarget(event) === win guard was presumably added deliberately. It would be helpful if a maintainer who wrote the original logic confirms this was indeed a mistake and not protecting against a different edge case. The git blame would be informative here.

Verdict

Dimension Assessment
Correctness The fix is logically sound and restores symmetry with the blur handler
Style / Conventions Clean, minimal change — follows project patterns
Performance No impact
Test coverage No new automated test (hard to test this scenario)
Risk Low — change is narrow and only affects the alt-tab-back path

Confidence: 4/5

The fix is correct, minimal, and addresses a real user-facing bug. The -1 is for the lack of an automated regression test and the fact that the original guard's intent should ideally be confirmed by whoever wrote it. But the logical analysis is solid and the risk is very low — this is safe to merge after a maintainer sanity-check.


TLDR: Please add a test 😆

@netlify

netlify Bot commented Mar 25, 2026 •

Copy link
Copy Markdown

✅ Deploy Preview for base-ui ready!

Name Link
🔨 Latest commit 8478ca3
🔍 Latest deploy log https://app.netlify.com/projects/base-ui/deploys/69c61a771f38630008a34250
😎 Deploy Preview https://deploy-preview-4438--base-ui.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

When the window regains focus, getTarget(event) returns the Window
object which is not a Node, causing contains() to throw. Short-circuit
with target === win since the window is never inside the viewport.
@atomiks

atomiks commented Mar 25, 2026

Copy link
Copy Markdown
Contributor

On https://base-ui.com/react/components/toast, when I switch to a new tab (pointer or cmd+tab) then come back, it resumes correctly for me currently

@oliviertassinari oliviertassinari added the component: toast Changes related to the toast component. label Mar 25, 2026
@kyrregjerstad

Copy link
Copy Markdown
Contributor Author

@atomiks ah sorry I just realized, this is an Arc bug, not a base-ui toast bug! Seems like window blur events are not sent reliably

@atomiks

atomiks commented Mar 26, 2026

Copy link
Copy Markdown
Contributor

@kyrregjerstad so this PR doesn't fix it in Arc still?

@atomiks

atomiks commented Mar 27, 2026 •

Copy link
Copy Markdown
Contributor

Codex Review (GPT-5.4)

This is a focused bug-fix PR for ToastViewport window refocus handling, with a targeted regression test added on top. The runtime fix looks correct to me, and after looking at the tradeoff more closely I think the current test coverage is acceptable for this kind of browser-specific focus bug.

1. Bugs / Issues (None)

2. Root Cause & Patch Assessment

Area Assessment Notes
Alt-tab / tab-return root cause Handled Removing the getTarget(event) === win early return lets the real window-refocus path reach resumeTimers() and setIsWindowFocused(true).
contains() safety Handled The new target === win short-circuit prevents passing a Window into contains(), which matches the Safari follow-up fix intent.
In-page focus transitions Low risk Keeping the event.relatedTarget guard means ordinary element-to-element focus movement inside the page should still bail out before resuming timers.

3. Test Coverage Assessment

The new regression is a logic-level test rather than a full browser-tab automation, but I think that is a reasonable stopping point here. A true alt-tab or tab-backgrounding test is awkward to automate reliably, and the current coverage still protects the important handler path that was broken.

I ran pnpm test:jsdom ToastViewport --no-watch, pnpm test:chromium ToastViewport --no-watch, pnpm typescript, and pnpm eslint, and they all passed on the reviewed branch.

Merge Confidence Score

Overall merge confidence is 4/5. I’m highly confident in the runtime fix and comfortable with the current regression coverage for a small browser-specific bug fix like this. This looks good to merge.

@atomiks

atomiks commented Mar 27, 2026

Copy link
Copy Markdown
Contributor

Looks like Safari has the issue as well, but Chrome and Firefox don't seem to for me. This PR fixes Safari.

@atomiks
atomiks merged commit d954866 into mui:master Mar 27, 2026
23 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

component: toast Changes related to the toast component.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[toast] Timers never resume after window regains focus

5 participants