fix: fit compact store maps
This commit is contained in:
parent
bf60bd6a3f
commit
0778c1a71c
@ -1,6 +1,8 @@
|
||||
import { useCallback, useEffect, useMemo, useRef } from "react";
|
||||
import { DEFAULT_MAP_SIZE, clampMapZoom } from "../lib/locationMapUtils";
|
||||
|
||||
const COMPACT_MAP_LAYOUT_QUERY = "(max-width: 839px)";
|
||||
|
||||
function getAutoFitKey(mapState, mapSize) {
|
||||
return [
|
||||
mapState?.location?.id || "",
|
||||
@ -14,8 +16,12 @@ function getAutoFitKey(mapState, mapSize) {
|
||||
function getFitZoom(mapSize, scrollElement) {
|
||||
const fallbackWidth = typeof window === "undefined" ? DEFAULT_MAP_SIZE.width : window.innerWidth;
|
||||
const fallbackHeight = typeof window === "undefined" ? DEFAULT_MAP_SIZE.height : window.innerHeight;
|
||||
const availableWidth = Math.max(280, (scrollElement?.clientWidth || fallbackWidth) - 24);
|
||||
const availableHeight = Math.max(220, (scrollElement?.clientHeight || fallbackHeight) - 24);
|
||||
const isCompactLayout = typeof window !== "undefined"
|
||||
&& window.matchMedia(COMPACT_MAP_LAYOUT_QUERY).matches;
|
||||
const minimumWidth = isCompactLayout ? 160 : 280;
|
||||
const minimumHeight = isCompactLayout ? 120 : 220;
|
||||
const availableWidth = Math.max(minimumWidth, (scrollElement?.clientWidth || fallbackWidth) - 24);
|
||||
const availableHeight = Math.max(minimumHeight, (scrollElement?.clientHeight || fallbackHeight) - 24);
|
||||
|
||||
return Number(clampMapZoom(
|
||||
Math.min(availableWidth / mapSize.width, availableHeight / mapSize.height)
|
||||
@ -44,7 +50,7 @@ export default function useMobileMapAutoFit({
|
||||
useEffect(() => {
|
||||
if (typeof window === "undefined") return undefined;
|
||||
if (!mapState || mode === "setup") return undefined;
|
||||
if (!window.matchMedia("(max-width: 520px)").matches) return undefined;
|
||||
if (!window.matchMedia(COMPACT_MAP_LAYOUT_QUERY).matches) return undefined;
|
||||
if (lastAutoFitKeyRef.current === autoFitKey) return undefined;
|
||||
|
||||
const frameId = window.requestAnimationFrame(() => {
|
||||
|
||||
@ -958,6 +958,17 @@
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 839px) {
|
||||
.location-map-bottom-sheet {
|
||||
max-height: 34dvh;
|
||||
}
|
||||
|
||||
.location-map-svg {
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 520px) {
|
||||
.location-map-canvas-shell {
|
||||
padding: 0.5rem;
|
||||
|
||||
@ -187,6 +187,25 @@ async function mockMapShell(
|
||||
});
|
||||
}
|
||||
|
||||
async function expectMapFitsCanvas(page: Page, maxZoomPercent: number, context: string) {
|
||||
const scroll = page.locator(".location-map-scroll");
|
||||
const zoomText = page.locator(".location-map-zoom-controls span");
|
||||
await expect.poll(async () => {
|
||||
const value = await zoomText.textContent();
|
||||
return Number(value?.replace("%", "") || 0);
|
||||
}).toBeLessThan(maxZoomPercent);
|
||||
|
||||
const scrollBox = await scroll.boundingBox();
|
||||
const svgBox = await page.locator(".location-map-svg").boundingBox();
|
||||
expect(scrollBox).not.toBeNull();
|
||||
expect(svgBox).not.toBeNull();
|
||||
if (!scrollBox || !svgBox) throw new Error(`${context} fitted map was not measurable`);
|
||||
expect(svgBox.width).toBeLessThanOrEqual(scrollBox.width + 1);
|
||||
expect(svgBox.height).toBeLessThanOrEqual(scrollBox.height + 1);
|
||||
|
||||
return { scroll };
|
||||
}
|
||||
|
||||
test("admin creates a map from zones, saves a draft, and publishes it", async ({ page }) => {
|
||||
await mockMapShell(page);
|
||||
|
||||
@ -3645,20 +3664,7 @@ test("mobile starts fitted and fit returns panned maps into the canvas", async (
|
||||
});
|
||||
|
||||
await page.goto("/stores/100/locations/10/map");
|
||||
const scroll = page.locator(".location-map-scroll");
|
||||
const zoomText = page.locator(".location-map-zoom-controls span");
|
||||
await expect.poll(async () => {
|
||||
const value = await zoomText.textContent();
|
||||
return Number(value?.replace("%", "") || 0);
|
||||
}).toBeLessThan(45);
|
||||
|
||||
let scrollBox = await page.locator(".location-map-scroll").boundingBox();
|
||||
let svgBox = await page.locator(".location-map-svg").boundingBox();
|
||||
expect(scrollBox).not.toBeNull();
|
||||
expect(svgBox).not.toBeNull();
|
||||
if (!scrollBox || !svgBox) throw new Error("Initial mobile fitted map was not measurable");
|
||||
expect(svgBox.width).toBeLessThanOrEqual(scrollBox.width + 1);
|
||||
expect(svgBox.height).toBeLessThanOrEqual(scrollBox.height + 1);
|
||||
const { scroll } = await expectMapFitsCanvas(page, 45, "Initial mobile");
|
||||
|
||||
const zoomIn = page.getByRole("button", { name: "Zoom in" });
|
||||
await zoomIn.click();
|
||||
@ -3677,24 +3683,48 @@ test("mobile starts fitted and fit returns panned maps into the canvas", async (
|
||||
|
||||
await page.getByRole("button", { name: "Fit" }).click();
|
||||
|
||||
await expect.poll(async () => {
|
||||
const value = await zoomText.textContent();
|
||||
return Number(value?.replace("%", "") || 0);
|
||||
}).toBeLessThan(45);
|
||||
|
||||
scrollBox = await page.locator(".location-map-scroll").boundingBox();
|
||||
svgBox = await page.locator(".location-map-svg").boundingBox();
|
||||
expect(scrollBox).not.toBeNull();
|
||||
expect(svgBox).not.toBeNull();
|
||||
if (!scrollBox || !svgBox) throw new Error("Mobile fitted map was not measurable");
|
||||
expect(svgBox.width).toBeLessThanOrEqual(scrollBox.width + 1);
|
||||
expect(svgBox.height).toBeLessThanOrEqual(scrollBox.height + 1);
|
||||
await expectMapFitsCanvas(page, 45, "Mobile");
|
||||
await expect.poll(async () => scroll.evaluate((element) => ({
|
||||
left: element.scrollLeft,
|
||||
top: element.scrollTop,
|
||||
}))).toEqual({ left: 0, top: 0 });
|
||||
});
|
||||
|
||||
test("compact tablet map starts fitted on first load", async ({ page }) => {
|
||||
await page.setViewportSize({ width: 768, height: 720 });
|
||||
await mockMapShell(page);
|
||||
|
||||
const mapState = publishedMapState([
|
||||
{
|
||||
id: 1586,
|
||||
location_map_id: 901,
|
||||
zone_id: 501,
|
||||
zone_name: "Bakery",
|
||||
type: "zone",
|
||||
label: "Bakery",
|
||||
x: 40,
|
||||
y: 40,
|
||||
width: 260,
|
||||
height: 160,
|
||||
rotation: 0,
|
||||
locked: false,
|
||||
visible: true,
|
||||
sort_order: 1,
|
||||
},
|
||||
], true);
|
||||
|
||||
await page.route("**/households/1/locations/10/map", async (route) => {
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: "application/json",
|
||||
body: JSON.stringify(mapState),
|
||||
});
|
||||
});
|
||||
|
||||
await page.goto("/stores/100/locations/10/map");
|
||||
await expectMapFitsCanvas(page, 75, "Compact tablet");
|
||||
});
|
||||
|
||||
test("zoom controls disable at map zoom limits", async ({ page }) => {
|
||||
await mockMapShell(page);
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user