Add store location map manager #20
@ -1,5 +1,51 @@
|
|||||||
import { MAX_MAP_ZOOM, MIN_MAP_ZOOM, clampMapZoom } from "../../lib/locationMapUtils";
|
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({
|
export default function LocationMapToolbar({
|
||||||
mode,
|
mode,
|
||||||
editorTool,
|
editorTool,
|
||||||
@ -61,19 +107,23 @@ export default function LocationMapToolbar({
|
|||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
className="location-map-icon-button"
|
||||||
onClick={onUndo}
|
onClick={onUndo}
|
||||||
disabled={!canUseHistoryControls || saving || history.length === 0}
|
disabled={!canUseHistoryControls || saving || history.length === 0}
|
||||||
aria-label="Undo"
|
aria-label="Undo"
|
||||||
|
title="Undo"
|
||||||
>
|
>
|
||||||
<span aria-hidden="true">↶</span>
|
<ToolbarIcon name="undo" />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
className="location-map-icon-button"
|
||||||
onClick={onRedo}
|
onClick={onRedo}
|
||||||
disabled={!canUseHistoryControls || saving || future.length === 0}
|
disabled={!canUseHistoryControls || saving || future.length === 0}
|
||||||
aria-label="Redo"
|
aria-label="Redo"
|
||||||
|
title="Redo"
|
||||||
>
|
>
|
||||||
<span aria-hidden="true">↷</span>
|
<ToolbarIcon name="redo" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
@ -103,23 +153,34 @@ export default function LocationMapToolbar({
|
|||||||
<div className="location-map-zoom-controls" aria-label="Zoom controls">
|
<div className="location-map-zoom-controls" aria-label="Zoom controls">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
className="location-map-icon-button"
|
||||||
onClick={() => setZoom((value) => Number(clampMapZoom(value - 0.15).toFixed(2)))}
|
onClick={() => setZoom((value) => Number(clampMapZoom(value - 0.15).toFixed(2)))}
|
||||||
disabled={!hasAnyMap || isAtMinZoom}
|
disabled={!hasAnyMap || isAtMinZoom}
|
||||||
aria-label="Zoom out"
|
aria-label="Zoom out"
|
||||||
|
title="Zoom out"
|
||||||
>
|
>
|
||||||
-
|
<ToolbarIcon name="zoom-out" />
|
||||||
</button>
|
</button>
|
||||||
<span>{Math.round(zoom * 100)}%</span>
|
<span>{Math.round(zoom * 100)}%</span>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
className="location-map-icon-button"
|
||||||
onClick={() => setZoom((value) => Number(clampMapZoom(value + 0.15).toFixed(2)))}
|
onClick={() => setZoom((value) => Number(clampMapZoom(value + 0.15).toFixed(2)))}
|
||||||
disabled={!hasAnyMap || isAtMaxZoom}
|
disabled={!hasAnyMap || isAtMaxZoom}
|
||||||
aria-label="Zoom in"
|
aria-label="Zoom in"
|
||||||
|
title="Zoom in"
|
||||||
>
|
>
|
||||||
+
|
<ToolbarIcon name="zoom-in" />
|
||||||
</button>
|
</button>
|
||||||
<button type="button" onClick={onFit} disabled={!hasAnyMap}>
|
<button
|
||||||
Fit
|
type="button"
|
||||||
|
className="location-map-icon-button"
|
||||||
|
onClick={onFit}
|
||||||
|
disabled={!hasAnyMap}
|
||||||
|
aria-label="Fit"
|
||||||
|
title="Fit"
|
||||||
|
>
|
||||||
|
<ToolbarIcon name="fit" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@ -191,6 +191,28 @@
|
|||||||
line-height: 1;
|
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 {
|
.location-map-tool-buttons button.is-pan.active {
|
||||||
border-color: rgba(245, 158, 11, 0.78);
|
border-color: rgba(245, 158, 11, 0.78);
|
||||||
background: rgba(245, 158, 11, 0.22);
|
background: rgba(245, 158, 11, 0.22);
|
||||||
|
|||||||
@ -1488,6 +1488,11 @@ test("narrow mobile toolbar keeps map controls tappable without overflow", async
|
|||||||
const toolbarButtons = toolbar.getByRole("button");
|
const toolbarButtons = toolbar.getByRole("button");
|
||||||
const buttonCount = await toolbarButtons.count();
|
const buttonCount = await toolbarButtons.count();
|
||||||
expect(buttonCount).toBe(7);
|
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) {
|
for (let index = 0; index < buttonCount; index += 1) {
|
||||||
const buttonBox = await toolbarButtons.nth(index).boundingBox();
|
const buttonBox = await toolbarButtons.nth(index).boundingBox();
|
||||||
expect(buttonBox).not.toBeNull();
|
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.width).toBeGreaterThanOrEqual(40);
|
||||||
expect(buttonBox.height).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 }) => {
|
test("admin selecting an object does not mark a draft dirty until it changes", async ({ page }) => {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user