Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 72 additions & 0 deletions src/components/map/MapEditorPanel.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -239,4 +239,76 @@ describe("MapEditorPanel", () => {
expect(screen.getByText("Failed creating site. Check the name and try again.")).toBeInTheDocument();
expect(useAppStore.getState().mapEditor).not.toBeNull();
});

it("rejects invalid latitude values and shows error instead of crashing", async () => {
useAppStore.setState({
mapEditor: {
kind: "site",
resourceId: null,
isNew: true,
label: "New Site",
anchorRect,
siteSeed: { lat: 60.3, lon: 10.4, insertIntoSimulation: false },
},
});

render(<MapEditorPanel isMobile={false} />);

await userEvent.type(await screen.findByLabelText("Name"), "Test Site");
const latInput = await screen.findByLabelText("Latitude");
await userEvent.clear(latInput);
await userEvent.type(latInput, "956894");
await userEvent.click(screen.getByRole("button", { name: "Create Site" }));

expect(screen.getByText(/Latitude must be between -90 and 90/)).toBeInTheDocument();
expect(useAppStore.getState().mapEditor).not.toBeNull();
});

it("rejects invalid longitude values and shows error instead of crashing", async () => {
useAppStore.setState({
mapEditor: {
kind: "site",
resourceId: null,
isNew: true,
label: "New Site",
anchorRect,
siteSeed: { lat: 60.3, lon: 10.4, insertIntoSimulation: false },
},
});

render(<MapEditorPanel isMobile={false} />);

await userEvent.type(await screen.findByLabelText("Name"), "Test Site");
const lonInput = await screen.findByLabelText("Longitude");
await userEvent.clear(lonInput);
await userEvent.type(lonInput, "956894");
await userEvent.click(screen.getByRole("button", { name: "Create Site" }));

expect(screen.getByText(/Longitude must be between -180 and 180/)).toBeInTheDocument();
expect(useAppStore.getState().mapEditor).not.toBeNull();
});

it("shows error when pasting malformed latitude with leading spaces", async () => {
useAppStore.setState({
mapEditor: {
kind: "site",
resourceId: null,
isNew: true,
label: "New Site",
anchorRect,
siteSeed: { lat: 60.3, lon: 10.4, insertIntoSimulation: false },
},
});

render(<MapEditorPanel isMobile={false} />);

await userEvent.type(await screen.findByLabelText("Name"), "Test Site");
const latInput = await screen.findByLabelText("Latitude");
await userEvent.clear(latInput);
await userEvent.type(latInput, " 956894");
await userEvent.click(screen.getByRole("button", { name: "Create Site" }));

expect(screen.getByText(/Latitude must be between -90 and 90/)).toBeInTheDocument();
expect(useAppStore.getState().mapEditor).not.toBeNull();
});
});
24 changes: 22 additions & 2 deletions src/components/map/useMapEditorFormState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,16 @@ const parseNumber = (value: string | number): number => {
return Number.isFinite(parsed) ? parsed : 0;
};

const validateLatitude = (lat: number): string | null => {
if (lat < -90 || lat > 90) return `Latitude must be between -90 and 90, got ${lat}.`;
return null;
};

const validateLongitude = (lon: number): string | null => {
if (lon < -180 || lon > 180) return `Longitude must be between -180 and 180, got ${lon}.`;
return null;
};

export function useMapEditorFormState() {
const mapEditor = useAppStore((state) => state.mapEditor);
const mapEditorSiteDraft = useAppStore((state) => state.mapEditorSiteDraft);
Expand Down Expand Up @@ -511,6 +521,18 @@ export function useMapEditorFormState() {
setStatus("Click the map or use search to choose coordinates before saving.");
return false;
}
const saveLat = mapEditorSiteDraft?.lat ?? latDraft;
const saveLon = mapEditorSiteDraft?.lon ?? lonDraft;
const latError = validateLatitude(saveLat);
if (latError) {
setStatus(latError);
return false;
}
const lonError = validateLongitude(saveLon);
if (lonError) {
setStatus(lonError);
return false;
}
const normalizedVisibility: "private" | "shared" = accessVisibility;
if (collaboratorUserIds.includes(ownerUserId)) {
setStatus("Owner is implicit and cannot be added as collaborator.");
Expand All @@ -530,8 +552,6 @@ export function useMapEditorFormState() {
.map((id) => ({ userId: id, role: (collaboratorRoles[id] ?? "viewer") as "viewer" | "editor" }));

try {
const saveLat = mapEditorSiteDraft?.lat ?? latDraft;
const saveLon = mapEditorSiteDraft?.lon ?? lonDraft;
const saveGround = mapEditorSiteDraft?.groundElevationM ?? groundDraft;
if (mapEditor?.isNew) {
const createdId = addSiteLibraryEntry(
Expand Down
Loading