From f67c478b8b92c09bfc75d492e38bc49e01121a99 Mon Sep 17 00:00:00 2001 From: Kevin <68811197+yangyezhuang@users.noreply.github.com> Date: Tue, 3 Nov 2026 15:22:26 +0800 Subject: [PATCH] fix(ui): wrap long tooltip text inside the bubble (#983) Hovering an editor tab with a long path showed a tooltip whose text ran past the bubble: tooltips never wrapped (`white-space: nowrap`) and were capped at 360px. Tooltip text now wraps inside the bubble, breaking long paths where needed, and the shortcut stays on one line next to it. The bubble grows up to 720px or the window width less 32px, so most paths still fit on one line. Short tooltips look as before (same size and line height), and newlines in tooltip text are still collapsed. Fixes #968. Co-authored-by: yangyezhuang <68811197+yangyezhuang@users.noreply.github.com> --- src/ui/Tooltip.module.css | 10 ++++-- src/ui/Tooltip.test.tsx | 67 +++++++++++++++++++++++++++++++++++++++ src/ui/Tooltip.tsx | 2 +- 3 files changed, 76 insertions(+), 3 deletions(-) create mode 100644 src/ui/Tooltip.test.tsx diff --git a/src/ui/Tooltip.module.css b/src/ui/Tooltip.module.css index 48cd3c4a..d5717f3d 100644 --- a/src/ui/Tooltip.module.css +++ b/src/ui/Tooltip.module.css @@ -5,7 +5,7 @@ display: flex; align-items: center; gap: 8px; - max-width: 360px; + max-width: min(720px, calc(100vw - 32px)); padding: 6px 10px; border-radius: var(--radius-tooltip, 6px); background: var(--bg-tooltip); @@ -15,17 +15,23 @@ font-size: var(--fs-ui); line-height: 16px; pointer-events: none; - white-space: nowrap; opacity: 0; } .tooltip[data-placed] { opacity: 1; transition: opacity var(--dur-fast) var(--ease); } +/* Long paths wrap inside the bubble; the shortcut stays on one line. */ +.text { + min-width: 0; + overflow-wrap: anywhere; +} .shortcut { font-family: var(--font-keys); font-size: var(--fs-ui); color: color-mix(in srgb, var(--fg-tooltip) 62%, transparent); + white-space: nowrap; + flex-shrink: 0; } @media (prefers-reduced-motion: reduce) { diff --git a/src/ui/Tooltip.test.tsx b/src/ui/Tooltip.test.tsx new file mode 100644 index 00000000..813bd8a8 --- /dev/null +++ b/src/ui/Tooltip.test.tsx @@ -0,0 +1,67 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { act, cleanup, fireEvent, render, screen } from "@testing-library/react"; +import { TOOLTIP_DELAY, TooltipHost, tooltipAttrs } from "./Tooltip"; +import styles from "./Tooltip.module.css"; + +describe("tooltipAttrs", () => { + it("returns empty object when text is missing", () => { + expect(tooltipAttrs(undefined)).toEqual({}); + expect(tooltipAttrs("")).toEqual({}); + }); + + it("sets data-tooltip and optional shortcut", () => { + expect(tooltipAttrs("Close Tab")).toEqual({ "data-tooltip": "Close Tab" }); + expect(tooltipAttrs("Save All", "Ctrl+S")).toEqual({ + "data-tooltip": "Save All", + "data-tooltip-shortcut": "Ctrl+S", + }); + }); +}); + +describe("TooltipHost", () => { + beforeEach(() => { + vi.useFakeTimers(); + }); + + afterEach(() => { + cleanup(); + vi.useRealTimers(); + }); + + it("shows tooltip text with styles.text class and shortcut after delay", async () => { + render( +
+ + +
, + ); + + const button = screen.getByRole("button", { name: "Tab" }); + expect(screen.queryByRole("tooltip")).toBeNull(); + + fireEvent.pointerOver(button); + expect(screen.queryByRole("tooltip")).toBeNull(); + + act(() => { + vi.advanceTimersByTime(TOOLTIP_DELAY); + }); + + const tooltip = screen.getByRole("tooltip"); + expect(tooltip).toBeInTheDocument(); + expect(tooltip).toHaveClass(styles.tooltip!); + + const textSpan = tooltip.querySelector(`.${styles.text!}`); + expect(textSpan).toBeInTheDocument(); + expect(textSpan?.textContent).toBe("d:/project/src/main.rs"); + + const shortcutSpan = tooltip.querySelector(`.${styles.shortcut!}`); + expect(shortcutSpan).toBeInTheDocument(); + expect(shortcutSpan?.textContent).toBe("Ctrl+Shift+S"); + + // Pointer out hides the tooltip + fireEvent.pointerOut(button); + expect(screen.queryByRole("tooltip")).toBeNull(); + }); +}); diff --git a/src/ui/Tooltip.tsx b/src/ui/Tooltip.tsx index 893e2b54..0b970220 100644 --- a/src/ui/Tooltip.tsx +++ b/src/ui/Tooltip.tsx @@ -121,7 +121,7 @@ export function TooltipHost() { data-placed={shown.placed || undefined} style={{ left: shown.x, top: shown.y }} > - {shown.text} + {shown.text} {shown.shortcut && {shown.shortcut}} , document.body,