fix: stabilize mobile map toolbar

This commit is contained in:
Nico 2026-06-04 12:45:17 -07:00
parent 8614c0d984
commit e0417783e9
3 changed files with 22 additions and 29 deletions

View File

@ -21,7 +21,7 @@ export default function LocationMapToolbar({
const isAtMinZoom = zoom <= MIN_MAP_ZOOM; const isAtMinZoom = zoom <= MIN_MAP_ZOOM;
const isAtMaxZoom = zoom >= MAX_MAP_ZOOM; const isAtMaxZoom = zoom >= MAX_MAP_ZOOM;
const showHistorySlot = canManage && hasAnyMap; const showHistorySlot = canManage && hasAnyMap;
const showHistoryControls = mode === "edit" && showHistorySlot; const canUseHistoryControls = mode === "edit" && showHistorySlot;
return ( return (
<section <section
@ -56,25 +56,22 @@ export default function LocationMapToolbar({
</div> </div>
{showHistorySlot ? ( {showHistorySlot ? (
<div <div
className={`location-map-history-buttons ${showHistoryControls ? "" : "is-reserved"}`} className={`location-map-history-buttons ${canUseHistoryControls ? "" : "is-inactive"}`}
aria-label={showHistoryControls ? "Edit history" : undefined} aria-label="Edit history"
aria-hidden={showHistoryControls ? undefined : "true"}
> >
<button <button
type="button" type="button"
onClick={onUndo} onClick={onUndo}
disabled={!showHistoryControls || saving || history.length === 0} disabled={!canUseHistoryControls || saving || history.length === 0}
aria-label="Undo" aria-label="Undo"
tabIndex={showHistoryControls ? undefined : -1}
> >
<span aria-hidden="true">&#8630;</span> <span aria-hidden="true">&#8630;</span>
</button> </button>
<button <button
type="button" type="button"
onClick={onRedo} onClick={onRedo}
disabled={!showHistoryControls || saving || future.length === 0} disabled={!canUseHistoryControls || saving || future.length === 0}
aria-label="Redo" aria-label="Redo"
tabIndex={showHistoryControls ? undefined : -1}
> >
<span aria-hidden="true">&#8631;</span> <span aria-hidden="true">&#8631;</span>
</button> </button>

View File

@ -180,11 +180,6 @@
gap: 0.3rem; gap: 0.3rem;
} }
.location-map-history-buttons.is-reserved {
visibility: hidden;
pointer-events: none;
}
.location-map-history-buttons button { .location-map-history-buttons button {
min-width: 40px; min-width: 40px;
padding-inline: 0.55rem; padding-inline: 0.55rem;
@ -964,7 +959,7 @@
} }
.location-map-toolbar { .location-map-toolbar {
--location-map-mode-slot: clamp(144px, 37vw, 164px); --location-map-mode-slot: 148px;
--location-map-history-slot: 80px; --location-map-history-slot: 80px;
display: grid; display: grid;
grid-template-columns: var(--location-map-mode-slot) minmax(0, 1fr); grid-template-columns: var(--location-map-mode-slot) minmax(0, 1fr);
@ -981,14 +976,17 @@
.location-map-mode-group { .location-map-mode-group {
grid-column: 1; grid-column: 1;
width: 100%; width: var(--location-map-mode-slot);
max-width: var(--location-map-mode-slot);
min-width: 0; min-width: 0;
} }
.location-map-mode-buttons { .location-map-mode-buttons {
width: 100%; width: var(--location-map-mode-slot);
max-width: var(--location-map-mode-slot);
min-width: 0; min-width: 0;
padding: 0.18rem; padding: 0.18rem;
box-sizing: border-box;
} }
.location-map-mode-buttons button { .location-map-mode-buttons button {
@ -1013,12 +1011,6 @@
justify-self: center; justify-self: center;
} }
.location-map-history-buttons.is-reserved {
display: flex;
visibility: hidden;
pointer-events: none;
}
.location-map-history-buttons button { .location-map-history-buttons button {
min-width: 40px; min-width: 40px;
min-height: 40px; min-height: 40px;

View File

@ -703,7 +703,7 @@ test("admin status follows the visible map when a saved draft exists", async ({
}); });
test("mobile keeps draft preview status compact in the topbar", async ({ page }) => { test("mobile keeps draft preview status compact in the topbar", async ({ page }) => {
await page.setViewportSize({ width: 390, height: 844 }); await page.setViewportSize({ width: 409, height: 838 });
await mockMapShell(page); await mockMapShell(page);
const publishedObjects = [ const publishedObjects = [
@ -793,10 +793,14 @@ test("mobile keeps draft preview status compact in the topbar", async ({ page })
expect(statusBox.y).toBeLessThan(titleBox.y + titleBox.height); expect(statusBox.y).toBeLessThan(titleBox.y + titleBox.height);
expect(statusBox.x).toBeGreaterThan(titleBox.x); expect(statusBox.x).toBeGreaterThan(titleBox.x);
expect(toolbarBox.height).toBeLessThanOrEqual(64); expect(toolbarBox.height).toBeLessThanOrEqual(64);
expect(modeBox.width).toBeGreaterThanOrEqual(142); expect(modeBox.width).toBeGreaterThanOrEqual(146);
expect(modeBox.width).toBeLessThanOrEqual(166); expect(modeBox.width).toBeLessThanOrEqual(150);
expect(reservedHistoryBox.width).toBeGreaterThanOrEqual(70); expect(reservedHistoryBox.width).toBeGreaterThanOrEqual(70);
expect(reservedHistoryBox.x).toBeGreaterThan(modeBox.x + modeBox.width - 1); expect(reservedHistoryBox.x).toBeGreaterThan(modeBox.x + modeBox.width - 1);
await expect(historyActions.getByRole("button", { name: "Undo" })).toBeVisible();
await expect(historyActions.getByRole("button", { name: "Redo" })).toBeVisible();
await expect(historyActions.getByRole("button", { name: "Undo" })).toBeDisabled();
await expect(historyActions.getByRole("button", { name: "Redo" })).toBeDisabled();
expect(zoomBox.y).toBeLessThan(modeBox.y + modeBox.height); expect(zoomBox.y).toBeLessThan(modeBox.y + modeBox.height);
expect(zoomBox.x).toBeGreaterThan(reservedHistoryBox.x + reservedHistoryBox.width - 1); expect(zoomBox.x).toBeGreaterThan(reservedHistoryBox.x + reservedHistoryBox.width - 1);
expect(zoomBox.x + zoomBox.width).toBeLessThanOrEqual(toolbarBox.x + toolbarBox.width + 1); expect(zoomBox.x + zoomBox.width).toBeLessThanOrEqual(toolbarBox.x + toolbarBox.width + 1);
@ -820,8 +824,8 @@ test("mobile keeps draft preview status compact in the topbar", async ({ page })
expect(Math.abs(editModeBox.x - modeBox.x)).toBeLessThanOrEqual(1); expect(Math.abs(editModeBox.x - modeBox.x)).toBeLessThanOrEqual(1);
expect(Math.abs(editModeGroupBox.width - modeGroupBox.width)).toBeLessThanOrEqual(1); expect(Math.abs(editModeGroupBox.width - modeGroupBox.width)).toBeLessThanOrEqual(1);
expect(Math.abs(editModeBox.width - modeBox.width)).toBeLessThanOrEqual(1); expect(Math.abs(editModeBox.width - modeBox.width)).toBeLessThanOrEqual(1);
expect(editModeBox.width).toBeGreaterThanOrEqual(142); expect(editModeBox.width).toBeGreaterThanOrEqual(146);
expect(editModeBox.width).toBeLessThanOrEqual(166); expect(editModeBox.width).toBeLessThanOrEqual(150);
expect(Math.abs(editHistoryBox.width - reservedHistoryBox.width)).toBeLessThanOrEqual(1); expect(Math.abs(editHistoryBox.width - reservedHistoryBox.width)).toBeLessThanOrEqual(1);
expect(editHistoryBox.x).toBeGreaterThan(editModeBox.x + editModeBox.width); expect(editHistoryBox.x).toBeGreaterThan(editModeBox.x + editModeBox.width);
expect(editZoomBox.x).toBeGreaterThan(editHistoryBox.x + editHistoryBox.width); expect(editZoomBox.x).toBeGreaterThan(editHistoryBox.x + editHistoryBox.width);
@ -880,8 +884,8 @@ test("narrow mobile toolbar keeps map controls tappable without overflow", async
expect(toolbarBox.x).toBeGreaterThanOrEqual(0); expect(toolbarBox.x).toBeGreaterThanOrEqual(0);
expect(toolbarBox.x + toolbarBox.width).toBeLessThanOrEqual(361); expect(toolbarBox.x + toolbarBox.width).toBeLessThanOrEqual(361);
expect(modeBox.width).toBeGreaterThanOrEqual(142); expect(modeBox.width).toBeGreaterThanOrEqual(146);
expect(modeBox.width).toBeLessThanOrEqual(166); expect(modeBox.width).toBeLessThanOrEqual(150);
expect(modeBox.x + modeBox.width).toBeLessThanOrEqual(historyBox.x); expect(modeBox.x + modeBox.width).toBeLessThanOrEqual(historyBox.x);
expect(zoomBox.y).toBeGreaterThan(modeBox.y + modeBox.height - 1); expect(zoomBox.y).toBeGreaterThan(modeBox.y + modeBox.height - 1);
expect(zoomBox.x).toBeGreaterThanOrEqual(toolbarBox.x); expect(zoomBox.x).toBeGreaterThanOrEqual(toolbarBox.x);