mirror of
https://github.com/Sea-Haven-Industries/shoc-frontend-new.git
synced 2026-10-04 13:42:09 +00:00
- Sites page: Add Site modal with Site Code, Client, address, State/Area, optional Site Phone and Email, points of contact - Duplicate Site Code shows inline under the field and keeps the modal open - Row opens a non-modal detail panel; Street Address links to Google Maps; key-less map preview - Edit switches the panel in place with Site Code read-only; Save needs a valid change, Cancel makes no call - Site Phone is sent independently of the contacts; zip and title are carried through edits - Retire the separate new/edit location pages
136 lines
4.6 KiB
TypeScript
136 lines
4.6 KiB
TypeScript
import { useForm } from "react-hook-form";
|
|
import { zodResolver } from "@hookform/resolvers/zod";
|
|
import { fireEvent, screen, waitFor } from "@testing-library/react";
|
|
import { describe, expect, it, vi } from "vitest";
|
|
import { StateSelect } from "@/app/(protected)/locations/_components/state-select";
|
|
import {
|
|
locationDefaultValues,
|
|
locationSchema,
|
|
type LocationFormValues,
|
|
} from "@/domain/locations/schemas/location-schema";
|
|
import { renderWithProviders } from "@/test/test-utils";
|
|
|
|
function StateSelectHarness({
|
|
onSubmit = vi.fn(),
|
|
}: {
|
|
onSubmit?: (values: LocationFormValues) => void;
|
|
}) {
|
|
const {
|
|
control,
|
|
handleSubmit,
|
|
formState: { errors },
|
|
} = useForm<LocationFormValues>({
|
|
resolver: zodResolver(locationSchema),
|
|
defaultValues: {
|
|
...locationDefaultValues,
|
|
name: "Dallas DC",
|
|
address: "3811 Distribution Dr",
|
|
city: "Dallas",
|
|
zipCode: "70068",
|
|
contacts: [{ name: "Jane", phone: "(421) 433-0032" }],
|
|
contactEmail: "jane@example.com",
|
|
accountId: "7",
|
|
},
|
|
});
|
|
|
|
return (
|
|
<form onSubmit={handleSubmit(onSubmit)} noValidate>
|
|
<StateSelect control={control} error={errors.state} />
|
|
<button type="submit">Save</button>
|
|
</form>
|
|
);
|
|
}
|
|
|
|
function openOptions() {
|
|
const input = screen.getByRole("combobox");
|
|
input.focus();
|
|
fireEvent.mouseDown(input);
|
|
return input;
|
|
}
|
|
|
|
describe("StateSelect", () => {
|
|
it("starts empty with no implicit default state", () => {
|
|
renderWithProviders(<StateSelectHarness />, { withAuth: false });
|
|
|
|
expect(screen.getByRole("combobox")).toHaveValue("");
|
|
expect(screen.queryByText("Area: ")).not.toBeInTheDocument();
|
|
});
|
|
|
|
it("shows searchable Name (CODE) options and no free-text entries", async () => {
|
|
renderWithProviders(<StateSelectHarness />, { withAuth: false });
|
|
const input = openOptions();
|
|
|
|
const options = await screen.findAllByRole("option");
|
|
expect(options).toHaveLength(50);
|
|
expect(screen.getByRole("option", { name: "Texas (TX)" })).toBeInTheDocument();
|
|
expect(screen.getByRole("option", { name: "California (CA)" })).toBeInTheDocument();
|
|
|
|
fireEvent.change(input, { target: { value: "mass" } });
|
|
await screen.findAllByRole("option");
|
|
expect(screen.getByRole("option", { name: "Massachusetts (MA)" })).toBeInTheDocument();
|
|
expect(screen.getAllByRole("option")).toHaveLength(1);
|
|
|
|
fireEvent.change(input, { target: { value: "Republic of Texas" } });
|
|
expect(screen.queryByRole("option")).not.toBeInTheDocument();
|
|
});
|
|
|
|
it("selects by search and shows the Area helper immediately", async () => {
|
|
const onSubmit = vi.fn();
|
|
renderWithProviders(<StateSelectHarness onSubmit={onSubmit} />, { withAuth: false });
|
|
const input = openOptions();
|
|
|
|
fireEvent.change(input, { target: { value: "texas" } });
|
|
fireEvent.click(await screen.findByRole("option", { name: "Texas (TX)" }));
|
|
|
|
expect(input).toHaveValue("Texas (TX)");
|
|
expect(screen.getByText("Area: West")).toBeInTheDocument();
|
|
|
|
fireEvent.click(screen.getByRole("button", { name: "Save" }));
|
|
await waitFor(() => {
|
|
expect(onSubmit).toHaveBeenCalledWith(
|
|
expect.objectContaining({ state: "TX" }),
|
|
expect.anything(),
|
|
);
|
|
});
|
|
});
|
|
|
|
it("is keyboard accessible (arrow + enter selection)", async () => {
|
|
const onSubmit = vi.fn();
|
|
renderWithProviders(<StateSelectHarness onSubmit={onSubmit} />, { withAuth: false });
|
|
const input = screen.getByRole("combobox");
|
|
|
|
input.focus();
|
|
fireEvent.change(input, { target: { value: "ohio" } });
|
|
fireEvent.keyDown(input, { key: "ArrowDown" });
|
|
fireEvent.keyDown(input, { key: "Enter" });
|
|
|
|
expect(input).toHaveValue("Ohio (OH)");
|
|
expect(screen.getByText("Area: Central")).toBeInTheDocument();
|
|
|
|
fireEvent.click(screen.getByRole("button", { name: "Save" }));
|
|
await waitFor(() => {
|
|
expect(onSubmit).toHaveBeenCalledWith(
|
|
expect.objectContaining({ state: "OH" }),
|
|
expect.anything(),
|
|
);
|
|
});
|
|
});
|
|
|
|
it("requires a state and rejects free text on submit", async () => {
|
|
const onSubmit = vi.fn();
|
|
renderWithProviders(<StateSelectHarness onSubmit={onSubmit} />, { withAuth: false });
|
|
|
|
fireEvent.click(screen.getByRole("button", { name: "Save" }));
|
|
expect(await screen.findByText("State is required")).toBeInTheDocument();
|
|
expect(onSubmit).not.toHaveBeenCalled();
|
|
|
|
const input = screen.getByRole("combobox");
|
|
input.focus();
|
|
fireEvent.change(input, { target: { value: "NotAState" } });
|
|
fireEvent.keyDown(input, { key: "Enter" });
|
|
fireEvent.click(screen.getByRole("button", { name: "Save" }));
|
|
|
|
await screen.findByText("State is required");
|
|
expect(onSubmit).not.toHaveBeenCalled();
|
|
});
|
|
});
|