obsidian/wiki/concepts/react-state-playwright-css-hover.md
2026-05-10 21:26:56 +01:00

2 KiB

title source updated tags
React useState Dropdown — CSS group-hover vs useState for Playwright daily/2026-05-10.md 2026-05-10
react
playwright
testing
css
dropdown
hover

React useState Dropdown — CSS group-hover: vs useState for Playwright

Problem

Playwright's hover() on a trigger element does not reliably open a React dropdown that uses useState to toggle visibility:

// React component
const [open, setOpen] = useState(false)

return (
  <div onMouseEnter={() => setOpen(true)} onMouseLeave={() => setOpen(false)}>
    <button>Menu</button>
    {open && <div className="dropdown">...</div>}  {/* conditional render */}
  </div>
)

page.hover('[data-testid="menu-trigger"]') causes the dropdown to appear and immediately disappear before the next Playwright action can interact with it. The issue is a React render-cycle lag: hover() fires mouseenter, React schedules a re-render, but mouseleave may fire before the re-render commits (especially in headless Chrome).

Fix: Pure CSS Hover

Replace useState toggle with CSS group-hover: (Tailwind) or :hover pseudo-class:

// No useState needed
return (
  <div className="group relative">
    <button>Menu</button>
    <div className="hidden group-hover:block absolute ...">
      ...dropdown content...
    </div>
  </div>
)

CSS hover is handled by the browser's rendering engine synchronously — no React render cycle involved. Playwright hover() reliably keeps the element in the hover state for subsequent actions.

Tradeoff

CSS group-hover: doesn't support keyboard navigation (:focus-within can supplement this). For production components that need full accessibility, use a headless UI library (Radix UI, Headless UI) which manages hover state internally with proper ARIA.

When useState Is Required

If the dropdown must stay open after a click (not just hover), useState is appropriate. In Playwright, use click() instead of hover() in that case, and add a small waitFor after click if needed.