fix(web): constrain ci checks popover height (#1441)
This commit is contained in:
@@ -36,6 +36,9 @@ type CountsView = {
|
||||
|
||||
const CHECK_GROUP_ORDER: CheckBucket[] = ["passed", "in_progress", "failed"];
|
||||
|
||||
export const PR_CI_DESKTOP_POPOVER_SCROLL_CLASS =
|
||||
"max-h-[min(28rem,calc(100vh-8rem))] overflow-y-auto overscroll-contain";
|
||||
|
||||
function countForBucket(counts: CountsView, kind: CheckBucket): number {
|
||||
if (kind === "passed") return counts.passed;
|
||||
if (kind === "in_progress") return counts.inProgress;
|
||||
|
||||
@@ -5,6 +5,7 @@ import { TooltipProvider } from "@kandev/ui/tooltip";
|
||||
import { StateProvider } from "@/components/state-provider";
|
||||
import { ToastProvider } from "@/components/toast-provider";
|
||||
import { PRStatusChip, aggregateChipStatus } from "./pr-status-chip";
|
||||
import { PR_CI_DESKTOP_POPOVER_SCROLL_CLASS } from "./pr-ci-popover";
|
||||
import type { AppState } from "@/lib/state/store";
|
||||
import type { TaskPR } from "@/lib/types/github";
|
||||
|
||||
@@ -90,6 +91,17 @@ function multiState(prs: TaskPR[]): Partial<AppState> {
|
||||
return { taskPRs: { byTaskId: { "task-1": prs } } };
|
||||
}
|
||||
|
||||
async function expectDesktopHoverPopoverConstrained() {
|
||||
fireEvent.pointerEnter(screen.getByTestId(CHIP_TESTID));
|
||||
const inner = await screen.findByTestId("pr-topbar-popover-inner");
|
||||
const content = inner.closest<HTMLElement>(".overflow-y-auto");
|
||||
expect(content).not.toBeNull();
|
||||
const classNames = content!.className.split(/\s+/);
|
||||
expect(classNames).toEqual(
|
||||
expect.arrayContaining(PR_CI_DESKTOP_POPOVER_SCROLL_CLASS.split(/\s+/)),
|
||||
);
|
||||
}
|
||||
|
||||
describe("PRStatusChip", () => {
|
||||
it("returns null when the task has no PR", () => {
|
||||
renderWithStore(undefined, <PRStatusChip taskId="missing" />);
|
||||
@@ -127,6 +139,11 @@ describe("PRStatusChip", () => {
|
||||
expect(document.querySelector(DRAWER_SELECTOR)).toBeNull();
|
||||
});
|
||||
|
||||
it("constrains the hover popover to the available viewport height", async () => {
|
||||
renderWithStore(seededState, <PRStatusChip taskId="task-1" />);
|
||||
await expectDesktopHoverPopoverConstrained();
|
||||
});
|
||||
|
||||
it("exposes the canonical data attributes that desktop tests rely on", () => {
|
||||
renderWithStore(seededState, <PRStatusChip taskId="task-1" />);
|
||||
const chip = screen.getByTestId(CHIP_TESTID);
|
||||
@@ -239,6 +256,11 @@ describe("PRStatusChip — multiple PRs", () => {
|
||||
expect(chip.getAttribute(ATTR_STATUS)).toBe("failed");
|
||||
});
|
||||
|
||||
it("constrains the aggregate hover popover to the available viewport height", async () => {
|
||||
renderWithStore(multiState(TWO_OPEN), <PRStatusChip taskId="task-1" />);
|
||||
await expectDesktopHoverPopoverConstrained();
|
||||
});
|
||||
|
||||
it("stays visible while at least one PR is still open", () => {
|
||||
renderWithStore(
|
||||
multiState([makePR({ id: "a", state: "merged" }), makePR({ id: "b", pr_number: 2 })]),
|
||||
|
||||
@@ -21,7 +21,7 @@ import {
|
||||
import { Button } from "@kandev/ui/button";
|
||||
import { useTaskPR } from "@/hooks/domains/github/use-task-pr";
|
||||
import { usePRFeedbackBackgroundSync } from "@/hooks/domains/github/use-pr-ci-popover";
|
||||
import { PRCIPopover } from "@/components/github/pr-ci-popover";
|
||||
import { PR_CI_DESKTOP_POPOVER_SCROLL_CLASS, PRCIPopover } from "@/components/github/pr-ci-popover";
|
||||
import { MultiPRCIPopover } from "@/components/github/multi-pr-ci-popover";
|
||||
import {
|
||||
isPRAwaitingReview,
|
||||
@@ -176,7 +176,7 @@ function PRStatusChipHoverCard({ pr }: { pr: TaskPR }) {
|
||||
side="top"
|
||||
align="start"
|
||||
sideOffset={8}
|
||||
className="w-80 p-2.5"
|
||||
className={`w-80 p-2.5 ${PR_CI_DESKTOP_POPOVER_SCROLL_CLASS}`}
|
||||
onPointerDownOutside={onPointerDownOutside}
|
||||
>
|
||||
<PRCIPopover pr={pr} enabled={true} />
|
||||
@@ -233,7 +233,7 @@ function PRStatusChipMultiHoverCard({ prs }: { prs: TaskPR[] }) {
|
||||
side="top"
|
||||
align="start"
|
||||
sideOffset={8}
|
||||
className="w-96 p-2.5"
|
||||
className={`w-96 p-2.5 ${PR_CI_DESKTOP_POPOVER_SCROLL_CLASS}`}
|
||||
onPointerDownOutside={onPointerDownOutside}
|
||||
>
|
||||
<MultiPRCIPopover prs={prs} enabled={true} />
|
||||
|
||||
@@ -29,7 +29,7 @@ import {
|
||||
isPRReadyToMerge,
|
||||
} from "@/components/github/pr-task-icon";
|
||||
import { prTaskKey } from "@/components/github/pr-detail-panel";
|
||||
import { PRCIPopover } from "@/components/github/pr-ci-popover";
|
||||
import { PR_CI_DESKTOP_POPOVER_SCROLL_CLASS, PRCIPopover } from "@/components/github/pr-ci-popover";
|
||||
import { MultiPRCIPopover } from "@/components/github/multi-pr-ci-popover";
|
||||
import { useAppStore } from "@/components/state-provider";
|
||||
import type { TaskPR } from "@/lib/types/github";
|
||||
@@ -186,7 +186,7 @@ function PRSingleButton({ pr }: { pr: TaskPR }) {
|
||||
data-testid="pr-topbar-popover"
|
||||
align="end"
|
||||
sideOffset={4}
|
||||
className="w-80"
|
||||
className={`w-80 ${PR_CI_DESKTOP_POPOVER_SCROLL_CLASS}`}
|
||||
onMouseEnter={handleEnter}
|
||||
onMouseLeave={handleLeave}
|
||||
onOpenAutoFocus={(e) => e.preventDefault()}
|
||||
@@ -258,7 +258,7 @@ function PRMultiButton({ prs }: { prs: TaskPR[] }) {
|
||||
data-testid="pr-topbar-popover"
|
||||
align="end"
|
||||
sideOffset={4}
|
||||
className="w-96"
|
||||
className={`w-96 ${PR_CI_DESKTOP_POPOVER_SCROLL_CLASS}`}
|
||||
onMouseEnter={handleEnter}
|
||||
onMouseLeave={handleLeave}
|
||||
onOpenAutoFocus={(e) => e.preventDefault()}
|
||||
|
||||
Reference in New Issue
Block a user