feat(sp6): bulk action bar wired into lanes
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
// @vitest-environment happy-dom
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { cleanup, render, screen, fireEvent } from "@testing-library/react";
|
||||
import { BulkBar } from "./BulkBar";
|
||||
|
||||
afterEach(() => cleanup());
|
||||
|
||||
describe("BulkBar", () => {
|
||||
it("renders action buttons appropriate for the lane", () => {
|
||||
const { container } = render(
|
||||
<BulkBar
|
||||
lane="rejected"
|
||||
count={3}
|
||||
onResubmit={vi.fn()}
|
||||
onDismiss={vi.fn()}
|
||||
onExport={vi.fn()}
|
||||
/>,
|
||||
);
|
||||
expect(container.textContent).toMatch(/re-?submit/i);
|
||||
expect(container.textContent).toMatch(/export/i);
|
||||
expect(container.textContent).not.toMatch(/dismiss/i);
|
||||
});
|
||||
|
||||
it("calls onResubmit when Resubmit clicked", () => {
|
||||
const onResubmit = vi.fn();
|
||||
const { container } = render(
|
||||
<BulkBar
|
||||
lane="rejected"
|
||||
count={2}
|
||||
onResubmit={onResubmit}
|
||||
onDismiss={() => {}}
|
||||
onExport={() => {}}
|
||||
/>,
|
||||
);
|
||||
const buttons = container.querySelectorAll("button");
|
||||
const resubmitBtn = Array.from(buttons).find((b) => /re-?submit/i.test(b.textContent || ""));
|
||||
expect(resubmitBtn).toBeDefined();
|
||||
fireEvent.click(resubmitBtn!);
|
||||
expect(onResubmit).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it("hides when count is zero", () => {
|
||||
const { container } = render(
|
||||
<BulkBar
|
||||
lane="rejected"
|
||||
count={0}
|
||||
onResubmit={() => {}}
|
||||
onDismiss={() => {}}
|
||||
onExport={() => {}}
|
||||
/>,
|
||||
);
|
||||
expect(container.firstChild).toBeNull();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,56 @@
|
||||
export function BulkBar({
|
||||
lane,
|
||||
count,
|
||||
onResubmit,
|
||||
onDismiss,
|
||||
onExport,
|
||||
}: {
|
||||
lane: "rejected" | "candidates" | "unmatched" | "done_today";
|
||||
count: number;
|
||||
onResubmit: () => void;
|
||||
onDismiss: () => void;
|
||||
onExport: () => void;
|
||||
}) {
|
||||
if (count === 0) return null;
|
||||
return (
|
||||
<div
|
||||
className="fixed bottom-4 left-1/2 px-4 py-2 font-mono flex gap-3 z-20"
|
||||
style={{
|
||||
transform: "translateX(-50%)",
|
||||
background: "var(--tt-bg-elev)",
|
||||
border: "1px solid var(--tt-amber)",
|
||||
color: "var(--tt-ink)",
|
||||
fontSize: 11,
|
||||
}}
|
||||
>
|
||||
<span className="tabular-nums" style={{ color: "var(--tt-amber)" }}>
|
||||
{count} selected
|
||||
</span>
|
||||
{lane === "rejected" && (
|
||||
<button
|
||||
onClick={onResubmit}
|
||||
className="hover:underline"
|
||||
style={{ color: "var(--tt-ink)" }}
|
||||
>
|
||||
Re-submit ({count})
|
||||
</button>
|
||||
)}
|
||||
{lane === "candidates" && (
|
||||
<button
|
||||
onClick={onDismiss}
|
||||
className="hover:underline"
|
||||
style={{ color: "var(--tt-ink)" }}
|
||||
>
|
||||
Dismiss ({count})
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
onClick={onExport}
|
||||
className="hover:underline"
|
||||
style={{ color: "var(--tt-ink)" }}
|
||||
>
|
||||
Export CSV ({count})
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user