Add store location map manager #20

Open
nalalangan wants to merge 206 commits from feature/location-map-manager into feature/store-selector-modal
3 changed files with 105 additions and 6 deletions
Showing only changes of commit 85061b93d1 - Show all commits

View File

@ -1,5 +1,51 @@
import { MAX_MAP_ZOOM, MIN_MAP_ZOOM, clampMapZoom } from "../../lib/locationMapUtils";
function ToolbarIcon({ name }) {
if (name === "undo") {
return (
<svg className="location-map-toolbar-icon" viewBox="0 0 24 24" aria-hidden="true">
<path d="M9 7H4v5" />
<path d="M5 11a7 7 0 1 0 2-5" />
</svg>
);
}
if (name === "redo") {
return (
<svg className="location-map-toolbar-icon" viewBox="0 0 24 24" aria-hidden="true">
<path d="M15 7h5v5" />
<path d="M19 11a7 7 0 1 1-2-5" />
</svg>
);
}
if (name === "zoom-in") {
return (
<svg className="location-map-toolbar-icon" viewBox="0 0 24 24" aria-hidden="true">
<path d="M11 5v12" />
<path d="M5 11h12" />
</svg>
);
}
if (name === "fit") {
return (
<svg className="location-map-toolbar-icon" viewBox="0 0 24 24" aria-hidden="true">
<path d="M8 4H4v4" />
<path d="M16 4h4v4" />
<path d="M20 16v4h-4" />
<path d="M8 20H4v-4" />
</svg>
);
}
return (
<svg className="location-map-toolbar-icon" viewBox="0 0 24 24" aria-hidden="true">
<path d="M5 11h12" />
</svg>
);
}
export default function LocationMapToolbar({
mode,
editorTool,
@ -61,19 +107,23 @@ export default function LocationMapToolbar({
>
<button
type="button"
className="location-map-icon-button"
onClick={onUndo}
disabled={!canUseHistoryControls || saving || history.length === 0}
aria-label="Undo"
title="Undo"
>
<span aria-hidden="true">&#8630;</span>
<ToolbarIcon name="undo" />
</button>
<button
type="button"
className="location-map-icon-button"
onClick={onRedo}
disabled={!canUseHistoryControls || saving || future.length === 0}
aria-label="Redo"
title="Redo"
>
<span aria-hidden="true">&#8631;</span>
<ToolbarIcon name="redo" />
</button>
</div>
) : null}
@ -103,23 +153,34 @@ export default function LocationMapToolbar({
<div className="location-map-zoom-controls" aria-label="Zoom controls">
<button
type="button"
className="location-map-icon-button"
onClick={() => setZoom((value) => Number(clampMapZoom(value - 0.15).toFixed(2)))}
disabled={!hasAnyMap || isAtMinZoom}
aria-label="Zoom out"
title="Zoom out"
>
-
<ToolbarIcon name="zoom-out" />
</button>
<span>{Math.round(zoom * 100)}%</span>
<button
type="button"
className="location-map-icon-button"
onClick={() => setZoom((value) => Number(clampMapZoom(value + 0.15).toFixed(2)))}
disabled={!hasAnyMap || isAtMaxZoom}
aria-label="Zoom in"
title="Zoom in"
>
+
<ToolbarIcon name="zoom-in" />
</button>
<button type="button" onClick={onFit} disabled={!hasAnyMap}>
Fit
<button
type="button"
className="location-map-icon-button"
onClick={onFit}
disabled={!hasAnyMap}
aria-label="Fit"
title="Fit"
>
<ToolbarIcon name="fit" />
</button>
</div>
</section>

View File

@ -191,6 +191,28 @@
line-height: 1;
}
.location-map-history-buttons button.location-map-icon-button,
.location-map-zoom-controls button.location-map-icon-button {
width: 40px;
min-width: 40px;
padding: 0;
display: inline-flex;
align-items: center;
justify-content: center;
}
.location-map-toolbar-icon {
width: 18px;
height: 18px;
display: block;
fill: none;
stroke: currentColor;
stroke-width: 2.2;
stroke-linecap: round;
stroke-linejoin: round;
pointer-events: none;
}
.location-map-tool-buttons button.is-pan.active {
border-color: rgba(245, 158, 11, 0.78);
background: rgba(245, 158, 11, 0.22);

View File

@ -1488,6 +1488,11 @@ test("narrow mobile toolbar keeps map controls tappable without overflow", async
const toolbarButtons = toolbar.getByRole("button");
const buttonCount = await toolbarButtons.count();
expect(buttonCount).toBe(7);
await expect(toolbar.getByRole("button", { name: "Undo" })).toBeVisible();
await expect(toolbar.getByRole("button", { name: "Redo" })).toBeVisible();
await expect(toolbar.getByRole("button", { name: "Zoom out" })).toBeVisible();
await expect(toolbar.getByRole("button", { name: "Zoom in" })).toBeVisible();
await expect(toolbar.getByRole("button", { name: "Fit" })).toBeVisible();
for (let index = 0; index < buttonCount; index += 1) {
const buttonBox = await toolbarButtons.nth(index).boundingBox();
expect(buttonBox).not.toBeNull();
@ -1495,6 +1500,17 @@ test("narrow mobile toolbar keeps map controls tappable without overflow", async
expect(buttonBox.width).toBeGreaterThanOrEqual(40);
expect(buttonBox.height).toBeGreaterThanOrEqual(40);
}
const iconButtons = toolbar.locator(".location-map-icon-button");
await expect(iconButtons).toHaveCount(5);
for (let index = 0; index < await iconButtons.count(); index += 1) {
const iconButtonBox = await iconButtons.nth(index).boundingBox();
expect(iconButtonBox).not.toBeNull();
if (!iconButtonBox) throw new Error("Toolbar icon button was not measurable");
expect(iconButtonBox.width).toBeLessThanOrEqual(44);
expect(iconButtonBox.height).toBeLessThanOrEqual(44);
await expect(iconButtons.nth(index).locator(".location-map-toolbar-icon")).toHaveCount(1);
}
});
test("admin selecting an object does not mark a draft dirty until it changes", async ({ page }) => {