Skip to content

Commit ad04a72

Browse files
authored
fix: Tooltips trigger without a delay (#4419)
Partially (or may be fully) Fixes #4406 Closes #4410 Some issues can't be fixed with current tooltips - i.e. if hover from tooltip is going onto the canvas we can't set delay on hover back. ## Description 1. Fixes focusable buttons had tooltip. 2. ## Steps for reproduction 1. click button 2. expect xyz ## Code Review - [ ] hi @kof, I need you to do - conceptual review (architecture, feature-correctness) - detailed review (read every line) - test it on preview ## Before requesting a review - [ ] made a self-review - [ ] added inline comments where things may be not obvious (the "why", not "what") ## Before merging - [ ] tested locally and on preview environment (preview dev login: 5de6) - [ ] updated [test cases](https://github.com/webstudio-is/webstudio/blob/main/apps/builder/docs/test-cases.md) document - [ ] added tests - [ ] if any new env variables are added, added them to `.env` file
1 parent 36ba02d commit ad04a72

File tree

2 files changed

+38
-5
lines changed

2 files changed

+38
-5
lines changed

apps/builder/app/builder/features/breakpoints/breakpoints-selector.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { useCallback, useEffect, useRef, useState } from "react";
22
import type { Breakpoint, Breakpoints } from "@webstudio-is/sdk";
33
import {
4-
EnhancedTooltip,
54
Flex,
65
Text,
76
Toolbar,
@@ -156,10 +155,11 @@ export const BreakpointsSelector = ({
156155
{groupBreakpoints(Array.from(breakpoints.values())).map(
157156
(breakpoint) => {
158157
return (
159-
<EnhancedTooltip
158+
<Tooltip
160159
key={breakpoint.id}
161160
content={getTooltipContent(breakpoint)}
162161
variant="wrapped"
162+
disableHoverableContent
163163
>
164164
<ToolbarToggleItem
165165
variant="subtle"
@@ -180,7 +180,7 @@ export const BreakpointsSelector = ({
180180
<BpStarOffIcon size={22} />
181181
))}
182182
</ToolbarToggleItem>
183-
</EnhancedTooltip>
183+
</Tooltip>
184184
);
185185
}
186186
)}

packages/design-system/src/components/tooltip.tsx

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ export const Tooltip = forwardRef(
6666
},
6767
ref: Ref<HTMLDivElement>
6868
) => {
69+
const triggerRef = useRef<HTMLButtonElement>(null);
6970
// We need to intercept tooltip open
7071
const [open = false, setOpen] = useControllableState({
7172
prop: openProp,
@@ -95,15 +96,47 @@ export const Tooltip = forwardRef(
9596
props.onMouseLeave?.(event);
9697
};
9798

98-
return (
99+
// There's no way to prevent a rendered trigger from opening.
100+
// This causes delay issues when an invisible tooltip forces other tooltips to show immediately.
101+
return content == null ? (
102+
children
103+
) : (
99104
<TooltipPrimitive.Root
100105
open={open}
101106
defaultOpen={defaultOpen}
102107
onOpenChange={setOpen}
103108
delayDuration={delayDuration}
104109
disableHoverableContent={disableHoverableContent}
105110
>
106-
<TooltipPrimitive.Trigger asChild {...triggerProps}>
111+
<TooltipPrimitive.Trigger
112+
asChild
113+
ref={triggerRef}
114+
{...triggerProps}
115+
onFocus={(event) => {
116+
// Prevent the tooltip from opening on focus
117+
// The main issue is that after dialogs or selects, the tooltip button is autofocused and causes the tooltip to open
118+
event.preventDefault();
119+
}}
120+
onPointerMove={(event) => {
121+
// The tooltip captures pointer events, which can be an issue when the tooltip trigger is also the popover trigger.
122+
// This is related to Popover.Anchor, but sometimes it can't be placed above the tooltip trigger.
123+
// To prevent pointer movements from affecting the tooltip, we check if there’s an element between the target and the current dialog.
124+
let currentElement =
125+
event.target instanceof Element ? event.target : null;
126+
127+
while (
128+
currentElement !== null &&
129+
currentElement !== event.currentTarget
130+
) {
131+
if (currentElement.getAttribute("role") === "dialog") {
132+
event.preventDefault();
133+
break;
134+
}
135+
136+
currentElement = currentElement.parentElement;
137+
}
138+
}}
139+
>
107140
{children}
108141
</TooltipPrimitive.Trigger>
109142
{content != null && (

0 commit comments

Comments
 (0)