diff --git a/packages/shell/src/dom/islands.test.ts b/packages/shell/src/dom/islands.test.ts index 7e907efa..1fb9f6d0 100644 --- a/packages/shell/src/dom/islands.test.ts +++ b/packages/shell/src/dom/islands.test.ts @@ -341,7 +341,10 @@ describe("palette registration", () => { const second = fakePalette(); const unregisterFirst = shell.registerPalette(first); shell.registerPalette(second); - const observer = FakeResizeObserver.instances[0]!; + const observer = FakeResizeObserver.instances.find((candidate) => + candidate.observed.has(second.element), + )!; + expect(observer).toBeDefined(); expect(observer.observed.has(first.element)).toBe(false); expect(observer.observed.has(second.element)).toBe(true); unregisterFirst(); diff --git a/packages/shell/src/dom/mount.test.ts b/packages/shell/src/dom/mount.test.ts index 17d2a407..cca3c110 100644 --- a/packages/shell/src/dom/mount.test.ts +++ b/packages/shell/src/dom/mount.test.ts @@ -190,7 +190,7 @@ describe("destroy", () => { shell.inspector?.open(); shell.setStatus({ severity: "danger", text: "Invalid" }, { delayMs: 800 }); shell.menu.open(); - const observer = FakeResizeObserver.instances[0]; + const observers = [...FakeResizeObserver.instances]; shell.destroy(); shell.destroy(); @@ -200,7 +200,8 @@ describe("destroy", () => { expect(root.getAttributeNames()).toEqual(["id"]); expect(root.className).toBe(""); expect(document.documentElement.className).toBe(""); - expect(observer?.disconnected).toBe(true); + expect(observers.length).toBeGreaterThan(1); + expect(observers.every((observer) => observer.disconnected)).toBe(true); expect(target.listeners.size).toBe(0); vi.advanceTimersByTime(2000); diff --git a/packages/shell/src/dom/tooltip.test.ts b/packages/shell/src/dom/tooltip.test.ts index 4813a5bc..1fdcb382 100644 --- a/packages/shell/src/dom/tooltip.test.ts +++ b/packages/shell/src/dom/tooltip.test.ts @@ -2,6 +2,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { TOOLTIP_DELAY_MS } from "../core/index.js"; import { + FakeResizeObserver, chrome, cleanup, fakePalette, @@ -149,6 +150,22 @@ describe("chrome tooltip (guide ยง5.6)", () => { expect(tooltip.style.top).toBe(`${748 - 8 - 28}px`); }); + it("is placed again when it grows after showing, for example once a web font arrives", () => { + const { root } = mount({}, { width: 1000, height: 800 }); + const zoom = chrome(root, "zoom"); + setBox(zoom, { left: 815, top: 748, width: 173, height: 40 }); + const zoomIn = zoomButton(root, "in"); + setBox(zoomIn, { left: 950, top: 752, width: 32, height: 32 }); + const tooltip = tooltipOf(root); + Object.defineProperty(tooltip, "offsetWidth", { configurable: true, value: 80 }); + Object.defineProperty(tooltip, "offsetHeight", { configurable: true, value: 28 }); + zoomIn.focus(); + expect(tooltip.style.left).toBe(`${1000 - 12 - 80}px`); + Object.defineProperty(tooltip, "offsetWidth", { configurable: true, value: 82 }); + FakeResizeObserver.resize(tooltip); + expect(tooltip.style.left).toBe(`${1000 - 12 - 82}px`); + }); + it("describes a control only when it says more than the control's name", () => { const { shell, root } = mount({ problems: { onActivate() {} } }); shell.setProblems({ count: 2, errors: 1 }); diff --git a/packages/shell/src/dom/tooltip.ts b/packages/shell/src/dom/tooltip.ts index 8514aa72..39492661 100644 --- a/packages/shell/src/dom/tooltip.ts +++ b/packages/shell/src/dom/tooltip.ts @@ -43,6 +43,15 @@ export function createTooltip({ root, layer, id }: TooltipContext): TooltipContr // A clicked control keeps its tooltip hidden until the pointer leaves it. let pressed: HTMLElement | undefined; const detachers = new Set<() => void>(); + // The tooltip grows when a web font arrives after it was placed; place it again so it keeps + // its edge distance instead of running past the container. + const resizeObserver = + typeof ResizeObserver === "function" + ? new ResizeObserver(() => { + if (shown) place(shown.control); + }) + : undefined; + resizeObserver?.observe(element); function cancelPending(): void { if (pending) clearTimeout(pending.timer); @@ -231,6 +240,7 @@ export function createTooltip({ root, layer, id }: TooltipContext): TooltipContr destroy() { hide(); for (const detach of [...detachers]) detach(); + resizeObserver?.disconnect(); element.remove(); }, };