diff --git a/apps/web/src/components/__tests__/combatant-row.test.tsx b/apps/web/src/components/__tests__/combatant-row.test.tsx index fcac89e..933990d 100644 --- a/apps/web/src/components/__tests__/combatant-row.test.tsx +++ b/apps/web/src/components/__tests__/combatant-row.test.tsx @@ -373,6 +373,26 @@ describe("CombatantRow", () => { ).toBeInTheDocument(); }); + it("popover is not dimmed when the combatant is downed", async () => { + const user = userEvent.setup(); + renderRow({ + combatant: { + id: combatantId("1"), + name: "Goblin", + maxHp: 10, + currentHp: 0, + }, + }); + + await user.click(screen.getByLabelText(CURRENT_HP_REGEX)); + // The row dims downed combatants with opacity-50, which would cascade + // to the popover if it rendered inside the dimmed subtree. + const popover = screen + .getByRole("button", { name: "Apply damage" }) + .closest(".opacity-50"); + expect(popover).toBeNull(); + }); + it("HP section is absent when maxHp is undefined", () => { renderRow({ combatant: { diff --git a/apps/web/src/components/__tests__/hp-adjust-popover.test.tsx b/apps/web/src/components/__tests__/hp-adjust-popover.test.tsx index 387a935..d8d8c8d 100644 --- a/apps/web/src/components/__tests__/hp-adjust-popover.test.tsx +++ b/apps/web/src/components/__tests__/hp-adjust-popover.test.tsx @@ -3,11 +3,41 @@ import "@testing-library/jest-dom/vitest"; import { cleanup, render, screen } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; +import { useEffect, useRef, useState } from "react"; import { afterEach, describe, expect, it, vi } from "vitest"; import { HpAdjustPopover } from "../hp-adjust-popover"; afterEach(cleanup); +function AnchoredPopover({ + onAdjust, + onSetTempHp, + onClose, +}: Readonly<{ + onAdjust: (delta: number) => void; + onSetTempHp: (value: number) => void; + onClose: () => void; +}>) { + const anchorRef = useRef(null); + // The popover opens on click in the app, so its anchor is always mounted + // first. Mirror that here — otherwise the anchor ref is still null when the + // popover measures its position and it renders hidden. + const [open, setOpen] = useState(false); + useEffect(() => setOpen(true), []); + return ( +
+ {!!open && ( + + )} +
+ ); +} + function renderPopover( overrides: Partial<{ onAdjust: (delta: number) => void; @@ -19,7 +49,7 @@ function renderPopover( const onSetTempHp = overrides.onSetTempHp ?? vi.fn(); const onClose = overrides.onClose ?? vi.fn(); const result = render( - void; }>) { const [popoverOpen, setPopoverOpen] = useState(false); + const anchorRef = useRef(null); const status = deriveHpStatus(currentHp, maxHp); if (maxHp === undefined) { @@ -209,7 +210,7 @@ function ClickableHp({ } return ( -
+
-
+ , + document.body, ); }