brackt/app/components/__tests__/DraftGridSection.test.tsx
Claude a2cbedbb82
Address all code review issues for draft room context menus
Layout bug: flex-1 min-w-20 was nested inside headerInner instead of the
direct flex-child wrapper in MiniDraftGrid, breaking equal column sizing.
Fixed by moving the classes to the outermost element in both the menu and
non-menu paths (matching DraftGridSection's pattern).

MiniDraftGrid improvements:
- Hoist hasHeaderMenu constant above the draftSlots.map() loop
- Add optional connectedTeams prop; disconnected teams render italic +
  muted-foreground, consistent with DraftGridSection
- connectedTeams now wired through the route's miniDraftGrid useMemo

DraftGridSection interface:
- Make onForceAutopick and onForceManualPickOpen optional (?) to match
  every other commissioner callback; add null checks at all three call
  sites (context menu, mobile MoreVertical button, mobile Sheet)
- Gate those two callbacks behind isCommissioner at the route level

Tests (new files):
- app/components/__tests__/MiniDraftGrid.test.tsx — covers layout classes,
  connected/disconnected styling, and all four context menu interactions
- app/components/__tests__/DraftGridSection.test.tsx — covers all three
  context menu surfaces for both commissioner and non-commissioner roles

Storybook: add CommissionerView and DisconnectedTeams stories to
MiniDraftGrid.stories.tsx

https://claude.ai/code/session_017JCShLVs9xZ6FZmyrUFaE1
2026-04-25 07:11:13 +00:00

198 lines
7 KiB
TypeScript

import { describe, it, expect, vi, beforeEach } from "vitest";
import { render, screen, fireEvent } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { DraftGridSection } from "~/components/draft/DraftGridSection";
const draftSlots = [
{ id: "slot-1", draftOrder: 1, team: { id: "team-1", name: "Alpha", logoUrl: null } },
{ id: "slot-2", draftOrder: 2, team: { id: "team-2", name: "Bravo", logoUrl: null } },
];
const ownerMap = { "team-1": "Alpha", "team-2": "Bravo" };
const draftGrid = [
[
{ pickNumber: 1, round: 1, pickInRound: 1, teamId: "team-1", pick: { participant: { name: "Player A" }, sport: { name: "NFL" } } },
{ pickNumber: 2, round: 1, pickInRound: 2, teamId: "team-2" },
],
];
const baseProps = {
draftSlots,
draftGrid,
currentPick: 2,
teamTimers: {} as Record<string, number | undefined>,
autodraftStatus: {} as Record<string, { isEnabled: boolean; mode: "next_pick" | "while_on"; queueOnly: boolean }>,
connectedTeams: new Set(["team-1", "team-2"]),
isCommissioner: false,
ownerMap,
seasonStatus: "draft" as const,
draftPaused: false,
};
describe("DraftGridSection", () => {
beforeEach(() => vi.clearAllMocks());
describe("Team header context menus", () => {
it("shows no header context menu for non-commissioner", () => {
render(<DraftGridSection {...baseProps} />);
fireEvent.contextMenu(screen.getAllByText("Alpha")[0]);
expect(screen.queryByText("Adjust Time Bank...")).not.toBeInTheDocument();
expect(screen.queryByText("Set Autodraft...")).not.toBeInTheDocument();
});
it("shows Adjust Time Bank and Set Autodraft for commissioner on right-click", () => {
render(
<DraftGridSection
{...baseProps}
isCommissioner
onAdjustTimeBankOpen={vi.fn()}
onSetAutodraftOpen={vi.fn()}
/>
);
fireEvent.contextMenu(screen.getAllByText("Alpha")[0]);
expect(screen.getByText("Adjust Time Bank...")).toBeInTheDocument();
expect(screen.getByText("Set Autodraft...")).toBeInTheDocument();
});
it("calls onAdjustTimeBankOpen with correct teamId", async () => {
const user = userEvent.setup();
const onAdjustTimeBankOpen = vi.fn();
render(
<DraftGridSection
{...baseProps}
isCommissioner
onAdjustTimeBankOpen={onAdjustTimeBankOpen}
/>
);
fireEvent.contextMenu(screen.getAllByText("Bravo")[0]);
await user.click(screen.getByText("Adjust Time Bank..."));
expect(onAdjustTimeBankOpen).toHaveBeenCalledWith("team-2");
});
it("calls onSetAutodraftOpen with correct teamId", async () => {
const user = userEvent.setup();
const onSetAutodraftOpen = vi.fn();
render(
<DraftGridSection
{...baseProps}
isCommissioner
onSetAutodraftOpen={onSetAutodraftOpen}
/>
);
fireEvent.contextMenu(screen.getAllByText("Alpha")[0]);
await user.click(screen.getByText("Set Autodraft..."));
expect(onSetAutodraftOpen).toHaveBeenCalledWith("team-1");
});
});
describe("Current cell context menu", () => {
it("shows no context menu for non-commissioner on current cell", () => {
render(
<DraftGridSection
{...baseProps}
onForceAutopick={vi.fn()}
onForceManualPickOpen={vi.fn()}
/>
);
fireEvent.contextMenu(screen.getByTitle("Overall Pick #2"));
expect(screen.queryByText("Force Auto Pick")).not.toBeInTheDocument();
});
it("shows Force Auto Pick and Force Manual Pick for commissioner", () => {
render(
<DraftGridSection
{...baseProps}
isCommissioner
onForceAutopick={vi.fn()}
onForceManualPickOpen={vi.fn()}
/>
);
fireEvent.contextMenu(screen.getByTitle("Overall Pick #2"));
expect(screen.getByText("Force Auto Pick")).toBeInTheDocument();
expect(screen.getByText("Force Manual Pick")).toBeInTheDocument();
});
it("shows no force-pick menu when callbacks absent even for commissioner", () => {
render(<DraftGridSection {...baseProps} isCommissioner />);
fireEvent.contextMenu(screen.getByTitle("Overall Pick #2"));
expect(screen.queryByText("Force Auto Pick")).not.toBeInTheDocument();
});
it("calls onForceAutopick with correct args", async () => {
const user = userEvent.setup();
const onForceAutopick = vi.fn();
render(
<DraftGridSection {...baseProps} isCommissioner onForceAutopick={onForceAutopick} />
);
fireEvent.contextMenu(screen.getByTitle("Overall Pick #2"));
await user.click(screen.getByText("Force Auto Pick"));
expect(onForceAutopick).toHaveBeenCalledWith(2, "team-2");
});
it("calls onForceManualPickOpen with correct args", async () => {
const user = userEvent.setup();
const onForceManualPickOpen = vi.fn();
render(
<DraftGridSection
{...baseProps}
isCommissioner
onForceManualPickOpen={onForceManualPickOpen}
/>
);
fireEvent.contextMenu(screen.getByTitle("Overall Pick #2"));
await user.click(screen.getByText("Force Manual Pick"));
expect(onForceManualPickOpen).toHaveBeenCalledWith(2, "team-2");
});
});
describe("Picked cell context menu", () => {
it("shows Replace Pick and Roll Back for commissioner on a picked cell", () => {
render(
<DraftGridSection
{...baseProps}
isCommissioner
onReplacePick={vi.fn()}
onRollbackToPick={vi.fn()}
/>
);
fireEvent.contextMenu(screen.getByTitle("Overall Pick #1"));
expect(screen.getByText("Replace Pick")).toBeInTheDocument();
expect(screen.getByText("Roll Back to This Pick")).toBeInTheDocument();
});
it("shows no context menu on picked cell for non-commissioner", () => {
render(
<DraftGridSection
{...baseProps}
onReplacePick={vi.fn()}
onRollbackToPick={vi.fn()}
/>
);
fireEvent.contextMenu(screen.getByTitle("Overall Pick #1"));
expect(screen.queryByText("Replace Pick")).not.toBeInTheDocument();
});
it("calls onReplacePick with correct args", async () => {
const user = userEvent.setup();
const onReplacePick = vi.fn();
render(
<DraftGridSection {...baseProps} isCommissioner onReplacePick={onReplacePick} />
);
fireEvent.contextMenu(screen.getByTitle("Overall Pick #1"));
await user.click(screen.getByText("Replace Pick"));
expect(onReplacePick).toHaveBeenCalledWith(1, "team-1");
});
it("calls onRollbackToPick with correct pickNumber", async () => {
const user = userEvent.setup();
const onRollbackToPick = vi.fn();
render(
<DraftGridSection {...baseProps} isCommissioner onRollbackToPick={onRollbackToPick} />
);
fireEvent.contextMenu(screen.getByTitle("Overall Pick #1"));
await user.click(screen.getByText("Roll Back to This Pick"));
expect(onRollbackToPick).toHaveBeenCalledWith(1);
});
});
});