Skip to content

fix(react-tooltip): dismiss tooltip when document becomes hidden#36023

Open
Copilot wants to merge 6 commits intomasterfrom
copilot/fix-tooltip-persistence-mobile
Open

fix(react-tooltip): dismiss tooltip when document becomes hidden#36023
Copilot wants to merge 6 commits intomasterfrom
copilot/fix-tooltip-persistence-mobile

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 21, 2026

On mobile, tapping a tooltip-wrapped link that opens a new tab leaves the tooltip stuck visible after returning to the original tab, because neither pointerleave nor blur fires in that flow. Per WCAG 1.4.13 analysis in the issue thread, dismissing is correct here since the original hover/tap/focus trigger is no longer active.

Changes

  • useTooltipBase.tsx: Inside the existing visibility-gated useIsomorphicLayoutEffect (which already owns the keydown/Escape listener), register a visibilitychange listener on targetDocument from useFluent_unstable(). When targetDocument.visibilityState === 'hidden', call the same thisTooltip.hide() path used by Escape so onVisibleChange and controlled-state semantics are preserved. Cleanup is colocated with the existing keydown removal.
  • Tooltip.test.tsx: New test stubs document.visibilityState to 'hidden', dispatches a visibilitychange event, and asserts onVisibleChange fires with { visible: false }.
  • Change file: patch bump for @fluentui/react-tooltip.

Caveat

The reported bug is on iOS Safari / Mobile Edge. The Jest test verifies the dismiss path fires on visibilitychange in jsdom; the actual iOS task-switcher flow needs manual verification by the reviewer on a real device.

Copilot AI changed the title [WIP] Fix tooltip persistence on mobile touch devices after tab switch fix(react-tooltip): dismiss tooltip when document becomes hidden Apr 21, 2026
Copilot AI requested a review from tudorpopams April 21, 2026 20:28
@tudorpopams tudorpopams marked this pull request as ready for review April 22, 2026 07:29
@tudorpopams tudorpopams requested review from a team and mainframev as code owners April 22, 2026 07:29
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 22, 2026

📊 Bundle size report

Package & Exports Baseline (minified/GZIP) PR Change
react-charts
AreaChart
412.997 kB
126.642 kB
413.17 kB
126.681 kB
173 B
39 B
react-charts
DeclarativeChart
763.759 kB
220.641 kB
763.932 kB
220.685 kB
173 B
44 B
react-charts
DonutChart
323.416 kB
97.131 kB
323.589 kB
97.174 kB
173 B
43 B
react-charts
FunnelChart
314.963 kB
94.145 kB
315.136 kB
94.182 kB
173 B
37 B
react-charts
GanttChart
396.105 kB
120.154 kB
396.278 kB
120.197 kB
173 B
43 B
react-charts
GaugeChart
322.849 kB
96.554 kB
323.022 kB
96.596 kB
173 B
42 B
react-charts
GroupedVerticalBarChart
403.98 kB
122.722 kB
404.153 kB
122.766 kB
173 B
44 B
react-charts
HeatMapChart
398.177 kB
122.047 kB
398.35 kB
122.085 kB
173 B
38 B
react-charts
HorizontalBarChart
303.146 kB
89.291 kB
303.319 kB
89.336 kB
173 B
45 B
react-charts
LineChart
424.337 kB
128.724 kB
424.51 kB
128.771 kB
173 B
47 B
react-charts
SankeyChart
220.864 kB
67.971 kB
221.037 kB
68.027 kB
173 B
56 B
react-charts
ScatterChart
403.719 kB
122.848 kB
403.892 kB
122.894 kB
173 B
46 B
react-charts
VerticalBarChart
440.449 kB
128.312 kB
440.622 kB
128.359 kB
173 B
47 B
react-charts
VerticalStackedBarChart
409.991 kB
124.241 kB
410.164 kB
124.282 kB
173 B
41 B
react-components
react-components: entire library
1.302 MB
325.338 kB
1.302 MB
325.382 kB
173 B
44 B
react-tooltip
Tooltip
57.52 kB
19.808 kB
57.693 kB
19.847 kB
173 B
39 B
Unchanged fixtures
Package & Exports Size (minified/GZIP)
react-avatar
Avatar
48.492 kB
15.379 kB
react-avatar
AvatarGroup
17.468 kB
6.999 kB
react-avatar
AvatarGroupItem
61.513 kB
19.251 kB
react-charts
HorizontalBarChartWithAxis
63 B
83 B
react-charts
Legends
242.887 kB
71.756 kB
react-charts
PolarChart
351.985 kB
107.563 kB
react-charts
Sparkline
91.4 kB
28.708 kB
react-checkbox
Checkbox
33.718 kB
11.424 kB
react-combobox
Combobox (including child components)
106.709 kB
34.573 kB
react-combobox
Dropdown (including child components)
106.479 kB
34.32 kB
react-components
react-components: Button, FluentProvider & webLightTheme
70.415 kB
19.963 kB
react-components
react-components: Accordion, Button, FluentProvider, Image, Menu, Popover
237.29 kB
68.851 kB
react-components
react-components: FluentProvider & webLightTheme
43.63 kB
14.026 kB
react-datepicker-compat
DatePicker Compat
225.645 kB
63.659 kB
react-dialog
Dialog (including children components)
101.984 kB
30.317 kB
react-field
Field
22.411 kB
8.393 kB
react-headless-components-preview
react-headless-components-preview: entire library
84.122 kB
24.635 kB
react-input
Input
26.298 kB
8.705 kB
react-list
List
87.098 kB
25.756 kB
react-list
ListItem
110.887 kB
32.626 kB
react-persona
Persona
55.447 kB
17.311 kB
react-portal-compat
PortalCompatProvider
8.386 kB
2.624 kB
react-progress
ProgressBar
20.23 kB
7.866 kB
react-radio
Radio
31.105 kB
9.66 kB
react-radio
RadioGroup
14.053 kB
5.703 kB
react-select
Select
26.183 kB
9.474 kB
react-slider
Slider
36.377 kB
12.034 kB
react-spinbutton
SpinButton
33.822 kB
11.128 kB
react-swatch-picker
@fluentui/react-swatch-picker - package
104.207 kB
29.897 kB
react-switch
Switch
36.351 kB
11.07 kB
react-table
DataGrid
159.681 kB
44.965 kB
react-table
Table (Primitives only)
41.015 kB
13.174 kB
react-table
Table as DataGrid
131.023 kB
36.014 kB
react-table
Table (Selection only)
69.409 kB
19.408 kB
react-table
Table (Sort only)
68.052 kB
19.026 kB
react-tag-picker
@fluentui/react-tag-picker - package
187.014 kB
55.911 kB
react-tags
InteractionTag
13.742 kB
5.473 kB
react-tags
Tag
29.666 kB
9.433 kB
react-tags
TagGroup
82.265 kB
24.156 kB
react-textarea
Textarea
24.686 kB
8.972 kB
react-timepicker-compat
TimePicker
109.674 kB
36.193 kB
react-tree
FlatTree
148.056 kB
42.19 kB
react-tree
PersonaFlatTree
149.884 kB
42.567 kB
react-tree
PersonaTree
145.944 kB
41.374 kB
react-tree
Tree
144.122 kB
41.003 kB
🤖 This report was generated against 0392152eacf970d6d7063c12af84b20b7d45c8e1

@github-actions
Copy link
Copy Markdown

Pull request demo site: URL

@@ -0,0 +1,7 @@
{
Copy link
Copy Markdown

@github-actions github-actions Bot Apr 22, 2026

Choose a reason for hiding this comment

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

🕵🏾‍♀️ visual changes to review in the Visual Change Report

vr-tests-react-components/Charts-DonutChart 1 screenshots
Image Name Diff(in Pixels) Image Type
vr-tests-react-components/Charts-DonutChart.Dynamic - RTL.default.chromium.png 5570 Changed
vr-tests-react-components/Positioning 2 screenshots
Image Name Diff(in Pixels) Image Type
vr-tests-react-components/Positioning.Positioning end.chromium.png 963 Changed
vr-tests-react-components/Positioning.Positioning end.updated 2 times.chromium.png 735 Changed
vr-tests-react-components/ProgressBar converged 3 screenshots
Image Name Diff(in Pixels) Image Type
vr-tests-react-components/ProgressBar converged.Indeterminate + thickness - Dark Mode.default.chromium.png 25 Changed
vr-tests-react-components/ProgressBar converged.Indeterminate + thickness - High Contrast.default.chromium.png 61 Changed
vr-tests-react-components/ProgressBar converged.Indeterminate + thickness.default.chromium.png 37 Changed
vr-tests-react-components/TagPicker 3 screenshots
Image Name Diff(in Pixels) Image Type
vr-tests-react-components/TagPicker.disabled - Dark Mode.disabled input hover.chromium.png 658 Changed
vr-tests-react-components/TagPicker.disabled - RTL.chromium.png 635 Changed
vr-tests-react-components/TagPicker.disabled.disabled input hover.chromium.png 677 Changed

There were 3 duplicate changes discarded. Check the build logs for more information.

// Dismiss the tooltip when the document becomes hidden (e.g. tab backgrounded,
// app switched on mobile). The original trigger (hover/tap/focus) is no longer
// active in this case, so persisting the tooltip is a stale UI state.
// See https://github.com/microsoft/fluentui/issues/35301
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 this link takes you to a React 19 related PR. Not relevant for this.

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.

Removed the incorrect issue link from the comment in 8b5f0e7.

Agent-Logs-Url: https://github.com/microsoft/fluentui/sessions/86e33d49-003c-4190-9a61-90a69bca5bc6

Co-authored-by: tudorpopams <97875118+tudorpopams@users.noreply.github.com>
@tudorpopams tudorpopams enabled auto-merge (squash) April 23, 2026 08:42
@tudorpopams
Copy link
Copy Markdown
Contributor

PR Review: #36023 — fix(react-tooltip): dismiss tooltip when document becomes hidden

Author: app/copilot-swe-agent
Type: bug-fix
Packages affected: @fluentui/react-tooltip
CI Status: passing (all 15 checks green, including e2e, VR screenshots, bundle-size, CodeQL)

Confidence Score: 100/100

Minimal, well-scoped WCAG-1.4.13-compliant fix. New listener is colocated with the existing keydown handler inside the visibility-gated effect, so it only exists when a tooltip is actually showing and is cleaned up in the same teardown path.

Findings

Blockers (must fix before merge)

none

Warnings (should address)

none

Info (consider)

  • The PR description is refreshingly honest about what's verifiable: the jsdom test proves the dismiss path fires on visibilitychange, but the underlying iOS task-switcher behavior can only be checked on a real device. Not a flaw in the PR — just a reminder that a reviewer with an iPhone should do one manual pass before merge.
  • Independently verified in a prior session: hover → tooltip appears → stub visibilityState='hidden' + dispatch visibilitychange → tooltip dismisses (with correct React re-render timing); and dispatching visibilitychange while state remains 'visible' is correctly a no-op. Full tooltip unit suite passed 21/21 under the patched tree.

Category Breakdown

Category Status Notes
Change file PASS @fluentui-react-tooltip-5cd4f2be-...json, type patch, clear description
V9 patterns PASS Uses targetDocument from useFluent_unstable() — complies with CLAUDE.md rule #3 (no global document). Listener is registered inside the visibility-gated useIsomorphicLayoutEffect (line 149 context) so it only exists while a tooltip is open. Cleanup paired with the existing keydown removal.
Dep layers PASS No package.json or new-import changes.
SSR safety PASS targetDocument is optional-chained on both addEventListener and removeEventListener. No global browser-API access anywhere in the diff.
Testing PASS New test in Tooltip.test.tsx stubs document.visibilityState via jest.spyOn(document, 'visibilityState', 'get').mockReturnValue('hidden'), fires the visibilitychange event, and asserts onVisibleChange was called with { visible: false }. Spy is restored after. Goes through the same hide() path as Escape so controlled-state semantics and onVisibleChange firing are preserved.
API surface PASS No public API change; etc/react-tooltip.api.md untouched (correctly).
Accessibility PASS This IS the a11y fix — WCAG 1.4.13 (dismissible content on hover/focus). The original trigger is no longer active when the tab is backgrounded, so dismissing is spec-compliant. No aria-* attributes touched.
Security/Quality PASS No eval/dangerouslySetInnerHTML/console.log/@ts-ignore/any.

Recommendation

APPROVE

The fix composes cleanly with existing tooltip teardown: the visibilitychange listener lives inside the same visible-gated effect that already owns the keydown handler, so it's only active when a tooltip is on screen, and it tears down through the same cleanup path. Calling thisTooltip.hide() (the Escape-path equivalent) instead of setting state directly preserves onVisibleChange firing and controlled-state semantics. Prior-session concerns about a wrong-issue-reference inline comment have been resolved — the current code has no stray issue number.


Posted via the /review-pr skill.

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.

[Bug]: Tooltip persists on mobile touch devices after returning from a link that opens in a new tab (A11y vs Touch behavior)

2 participants