diff --git a/src/editor/core/cm/setup.ts b/src/editor/core/cm/setup.ts index e029468c0..1c01eac6b 100644 --- a/src/editor/core/cm/setup.ts +++ b/src/editor/core/cm/setup.ts @@ -324,6 +324,13 @@ export interface SetupOptions { * below its top edge. WebKit positions them `absolute` within `.cm-editor`, so * a window-wide right bound makes CodeMirror skip the shift left and the * browser squeezes the box into the space left of the editor's edge. + * + * They are parented to the body (#2310): tooltips are `position: fixed` but + * appended inside the editor by default, whose `.content{isolation:isolate}` + * stacking context paints as one unit below the later bottom toolwindow — so + * completion popups got covered where they overflowed the editor's bottom + * edge. At the body level their z-index (500) sits above panels + * (`--z-panel: 10`) while staying below dialogs (`--z-dialog: 900`). */ export function tooltipBounds(view: { scrollDOM: Element; dom: Element }) { const { top } = view.scrollDOM.getBoundingClientRect(); @@ -336,7 +343,10 @@ export function tooltipBounds(view: { scrollDOM: Element; dom: Element }) { }; } -const tooltipSpace = tooltips({ tooltipSpace: tooltipBounds }); +const tooltipSpace = tooltips({ + parent: typeof document === "undefined" ? undefined : document.body, + tooltipSpace: tooltipBounds, +}); export function baseExtensions(o: SetupOptions): Extension { return [ diff --git a/src/editor/lsp/hoverPrecedence.test.ts b/src/editor/lsp/hoverPrecedence.test.ts index 1c42150b8..5f36ea285 100644 --- a/src/editor/lsp/hoverPrecedence.test.ts +++ b/src/editor/lsp/hoverPrecedence.test.ts @@ -110,7 +110,8 @@ describe.each([ view = editor(); activateHover(view, 2, 1); await settle(); - const tooltip = view.dom.querySelector(".cm-tooltip")!; + // Tooltips are parented to the body (#2310), not to the editor element. + const tooltip = document.querySelector(".cm-tooltip")!; tooltip.dispatchEvent(new MouseEvent("mousemove", { bubbles: true })); expect(pointerTarget()).toBe(tooltip); key("keydown", mod, { [flag]: true });