From d7d287ea8ad52f4469f40269bc935d86a60b3e55 Mon Sep 17 00:00:00 2001 From: Zayooo Date: Mon, 31 Aug 2026 14:44:06 +0200 Subject: [PATCH] fix(Combobox): render the dropdown inline in the Plasmic canvas (MET-3088) Headless UI v2's `anchor` forces the options list into a portal on `document.body`. That is outside the subtree the Plasmic design tool can select or scope injected styles to, so `optionsClassName` (and every option-state style) never reached the menu in Studio, and Floating UI mispositioned it inside the scaled canvas. Gate on `useInPlasmic()`: in the canvas render `` with `portal: false` and an inline `position: absolute; top: 100%` so the menu sits under the input, fully selectable and styleable. In prod keep `anchor` + `transition` (portal, viewport flip/shift). Only the positioning differs between the two paths; the classes are identical. --- src/code-components/Combobox/Combobox.tsx | 32 ++++++++++++++++++++--- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/src/code-components/Combobox/Combobox.tsx b/src/code-components/Combobox/Combobox.tsx index c1d300fe..ef20fbf8 100644 --- a/src/code-components/Combobox/Combobox.tsx +++ b/src/code-components/Combobox/Combobox.tsx @@ -7,6 +7,7 @@ import { } from "@headlessui/react"; import { Fragment, useRef, useState, ReactNode } from "react"; +import { useInPlasmic } from "../../common/useInPlasmic"; import { MemoDataProvider } from "../MemoDataProvider/MemoDataProvider"; import styles from "./Combobox.module.css"; import { HighlightQueryValue } from "./HighlightQueryValue"; @@ -125,6 +126,14 @@ export function Combobox({ const [query, setQuery] = useState(""); const buttonRef = useRef(null); + // In the Plasmic canvas the menu must render inline: Headless UI's `anchor` + // portals it to `document.body`, which is outside the component subtree the + // design tool can select or inject styles into, so `optionsClassName` edits + // would never reach it. Inline keeps it styleable; prod keeps the portal so + // the menu escapes `overflow` ancestors and flips into the viewport. Only the + // positioning differs between the two paths — the classes are identical. + const inCanvas = useInPlasmic(); + // `width` / `min-width` are inline so they beat any leftover `width` on // `optionsClassName` (e.g. a `width: 100%` from the design tool). `max-width` // is deliberately left to CSS so it stays overridable via `optionsClassName`. @@ -217,10 +226,25 @@ export function Combobox({ {/*