test(dashboard): align touch fixture with FloatingWindow (#2459)

## Summary
- update the touch-resize browser fixture assertions for New Task's
FloatingWindow migration
- verify the shared nine touch targets, resize handle ID, and unified
geometry persistence key

## Test plan
- `corepack pnpm --filter @fusion/dashboard build`
- `corepack pnpm --filter @fusion/dashboard exec vitest run --project
dashboard-browser-touch --silent=passed-only --reporter=dot
src/__tests__/task-modal-touch-resize-browser.test.ts`
- `corepack pnpm check:changesets`


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Tests**
* Updated tablet touch-resize coverage for task-detail to use the shared
southeast resize handle and corrected expected hit-target counts.
* Refreshed hit-target detection and drag target assertions after
switching the test surface to the task-detail/floating window layout.
* Updated persistence/geometry validations to compare the stored unified
width/height against the resized panel dimensions.
* Reworked desktop vs tablet assertions, including tighter overlay
padding checks and standardized shared resize-handle sizing
expectations.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Phil Larson
2026-07-26 22:35:53 -07:00
committed by GitHub
parent 232e17d1cb
commit 11887625f1
2 changed files with 70 additions and 51 deletions

View File

@@ -1,4 +1,4 @@
import React, { useRef, useState } from "react";
import React, { useState } from "react";
import { createRoot } from "react-dom/client";
import i18n from "i18next";
import { I18nextProvider, initReactI18next } from "react-i18next";
@@ -6,8 +6,6 @@ import "./styles.css";
import "./components/TaskDetailModal.css";
import "./components/FloatingWindow.css";
import { FloatingWindow } from "./components/FloatingWindow";
import { useModalResizePersist } from "./hooks/useModalResizePersist";
import { isTabletTouchViewport, useViewportMode } from "./hooks/useViewportMode";
import { NewTaskModal } from "./components/NewTaskModal";
import { AgentListModal } from "./components/AgentListModal";
import { SetupWizardModal } from "./components/SetupWizardModal";
@@ -18,16 +16,15 @@ const surface = params.get("surface") ?? "new-task";
if (params.has("reset")) localStorage.clear();
/*
FNXC:TaskModalResize 2026-07-26-16:00:
The browser fixture seeds the production persistence path only for resize gestures that need
headroom. It never supplies inline panel geometry, so density assertions continue to exercise
TaskDetailModal.css's real tablet overlay and width rules.
FNXC:ModalTouchGeometry 2026-07-26-20:08:
Task Detail now uses FloatingWindow geometry in production. Seed its shared size-and-position payload
only for resize gestures that need headroom; density assertions continue to use the default geometry.
*/
const detailSize = params.get("detailSize");
if (detailSize) {
const [width, height] = detailSize.split("x").map(Number);
if (Number.isFinite(width) && Number.isFinite(height)) {
localStorage.setItem("task-detail-modal-size", JSON.stringify({ width, height }));
localStorage.setItem("floating-window:task-detail", JSON.stringify({ size: { width, height }, position: { x: 64, y: 64 } }));
}
}
@@ -57,15 +54,25 @@ window.fetch = async (input) => {
};
function TaskDetailResizeHarness() {
const ref = useRef<HTMLDivElement>(null);
const viewportMode = useViewportMode();
const touchTargets = isTabletTouchViewport(viewportMode);
useModalResizePersist(ref, true, "task-detail-modal-size", { touchTargets });
return <div className="modal-overlay open">
<div ref={ref} className={`modal modal-lg task-detail-modal${viewportMode === "tablet" ? " task-modal--tablet" : ""}${touchTargets ? " task-modal--touch-resize" : ""}`} data-testid="task-detail-modal">
<div className="task-detail-content"><div className="modal-header">Task detail</div><div className="modal-body">Task detail body</div></div>
return <FloatingWindow
windowKey="task-detail-fixture"
title="Task detail"
onClose={() => undefined}
hideHeader
dragHandleSelector=".task-detail-content--embedded > .modal-header"
className="floating-window--task-detail"
defaultSize={{ width: 560, height: 480 }}
minSize={{ width: 320, height: 240 }}
persistGeometryKey="floating-window:task-detail"
suspendGeometryPersistenceOnMobile
layer="task-detail"
testId="task-detail-modal-overlay"
>
<div className="task-detail-content task-detail-content--embedded">
<div className="modal-header">Task detail</div>
<div className="modal-body">Task detail body</div>
</div>
</div>;
</FloatingWindow>;
}
function FloatingWindowHarness() {

View File

@@ -147,26 +147,32 @@ describe.runIf(executablePath)("Task modal tablet touch resize browser regressio
await page.goto(`${baseUrl}app/task-modal-touch-resize-e2e-fixture.html?surface=task-detail&reset=1&detailSize=600x500`);
await page.waitForTimeout(350);
expect(await page.evaluate(() => window.scrollX === 0 && window.scrollY === 0)).toBe(true);
expect(await page.evaluate(() => document.querySelectorAll("[data-resize-hit-target='true']").length)).toBe(1);
expect(await page.evaluate(() => document.querySelectorAll("[data-resize-hit-target='true']").length)).toBe(9);
await mkdir(screenshots, { recursive: true });
if (width === 820) await page.screenshot({ path: path.join(screenshots, "tablet-before.png") });
const detailSelector = "[data-testid='task-detail-modal'] .modal-resize-grip";
const detailPanel = "[data-testid='floating-window-task-detail-fixture']";
const detailSelector = `${detailPanel} [data-testid='floating-window-resize-se']`;
const detailPoint = await targetCenter(page, detailSelector);
expect(await page.evaluate((point) => document.elementFromPoint(point.x, point.y)?.getAttribute("data-resize-hit-target"), detailPoint)).toBe("true");
const detailBefore = await rect(page, "[data-testid='task-detail-modal']");
const detailBefore = await rect(page, detailPanel);
await touchDrag(cdp, detailPoint);
await page.waitForTimeout(250);
const detailAfter = await rect(page, "[data-testid='task-detail-modal']");
const detailAfter = await rect(page, detailPanel);
expect(detailAfter.width).toBeGreaterThan(detailBefore.width);
expect(detailAfter.height).toBeGreaterThan(detailBefore.height);
expect(detailAfter.width).toBeLessThanOrEqual(width);
expect(detailAfter.height).toBeLessThanOrEqual(height);
expect(await page.evaluate(() => localStorage.getItem("task-detail-modal-size"))).not.toBeNull();
expect(detailAfter.width).toBeLessThanOrEqual(width - 32);
expect(detailAfter.height).toBeLessThanOrEqual(height - 32);
const persistedTaskDetailGeometry = await page.evaluate(() => {
const raw = localStorage.getItem("floating-window:task-detail");
return raw ? JSON.parse(raw) : null;
});
expect(persistedTaskDetailGeometry?.size.width).toBeCloseTo(detailAfter.width);
expect(persistedTaskDetailGeometry?.size.height).toBeCloseTo(detailAfter.height);
await page.goto(`${baseUrl}app/task-modal-touch-resize-e2e-fixture.html?surface=new-task`);
await page.waitForTimeout(350);
expect(await page.evaluate(() => document.querySelectorAll("[data-resize-hit-target='true']").length)).toBe(8);
expect(await page.evaluate(() => document.querySelectorAll("[data-resize-hit-target='true']").length)).toBe(9);
const newTaskPanel = ".new-task-modal";
const headerPoint = await targetCenter(page, "[data-testid='new-task-drag-handle']");
const newTaskBeforeHeaderDrag = await rect(page, newTaskPanel);
@@ -178,7 +184,7 @@ describe.runIf(executablePath)("Task modal tablet touch resize browser regressio
expect(newTaskAfterHeaderDrag.width).toBe(newTaskBeforeHeaderDrag.width);
expect(newTaskAfterHeaderDrag.height).toBe(newTaskBeforeHeaderDrag.height);
const newTaskTarget = "[data-testid='new-task-resize-se']";
const newTaskTarget = "[data-testid='floating-window-resize-se']";
const newTaskPoint = await targetCenter(page, newTaskTarget);
expect(await page.evaluate((point) => document.elementFromPoint(point.x, point.y)?.getAttribute("data-resize-hit-target"), newTaskPoint)).toBe("true");
const newTaskBeforeResize = await rect(page, newTaskPanel);
@@ -189,8 +195,16 @@ describe.runIf(executablePath)("Task modal tablet touch resize browser regressio
expect(newTaskAfterResize.height).toBeGreaterThan(newTaskBeforeResize.height);
expect(newTaskAfterResize.width).toBeLessThanOrEqual(width - 32);
expect(newTaskAfterResize.height).toBeLessThanOrEqual(height - 32);
expect(await page.evaluate(() => localStorage.getItem("fusion:new-task-modal-size"))).not.toBeNull();
expect(await page.evaluate(() => localStorage.getItem("fusion:new-task-modal-position"))).not.toBeNull();
/*
FNXC:ModalTouchGeometry 2026-07-26-19:51:
The browser regression must prove FloatingWindow persisted usable resized geometry, not merely created the storage key.
*/
const persistedNewTaskGeometry = await page.evaluate(() => {
const raw = localStorage.getItem("fusion:new-task-modal-geometry");
return raw ? JSON.parse(raw) : null;
});
expect(persistedNewTaskGeometry?.size.width).toBeCloseTo(newTaskAfterResize.width);
expect(persistedNewTaskGeometry?.size.height).toBeCloseTo(newTaskAfterResize.height);
if (width === 820) await page.screenshot({ path: path.join(screenshots, "tablet-after.png") });
await page.close();
}, 30_000);
@@ -283,15 +297,13 @@ describe.runIf(executablePath)("Task modal tablet touch resize browser regressio
await desktop.goto(`${baseUrl}app/task-modal-touch-resize-e2e-fixture.html?surface=task-detail&reset=1`);
await desktop.waitForTimeout(250);
const desktopTaskDetail = {
panel: await boxMetrics(desktop, "[data-testid='task-detail-modal']"),
header: await boxMetrics(desktop, ".task-detail-modal .modal-header"),
body: await boxMetrics(desktop, ".task-detail-modal .modal-body"),
overlay: await boxMetrics(desktop, ".modal-overlay"),
panel: await boxMetrics(desktop, "[data-testid='floating-window-task-detail-fixture']"),
header: await boxMetrics(desktop, ".task-detail-content .modal-header"),
body: await boxMetrics(desktop, ".task-detail-content .modal-body"),
overlay: await boxMetrics(desktop, "[data-testid='task-detail-modal-overlay']"),
handle: await boxMetrics(desktop, "[data-testid='floating-window-task-detail-fixture'] [data-testid='floating-window-resize-se']"),
};
// The desktop overlay is the intentional density baseline: 10vh top placement with no
// panel-internal padding added by touch geometry.
expect(parseFloat(desktopTaskDetail.overlay.paddingBlockStart)).toBeCloseTo(100);
expect(desktopTaskDetail.panel.y).toBeCloseTo(parseFloat(desktopTaskDetail.overlay.paddingBlockStart));
expect(parseFloat(desktopTaskDetail.overlay.paddingBlockStart)).toBe(0);
await desktop.goto(`${baseUrl}app/task-modal-touch-resize-e2e-fixture.html?surface=new-task&reset=1`);
await desktop.waitForTimeout(250);
const desktopNewTask = {
@@ -307,28 +319,28 @@ describe.runIf(executablePath)("Task modal tablet touch resize browser regressio
await tablet.goto(`${baseUrl}app/task-modal-touch-resize-e2e-fixture.html?surface=task-detail&reset=1`);
await tablet.waitForTimeout(250);
const tabletTaskDetail = {
panel: await boxMetrics(tablet, "[data-testid='task-detail-modal']"),
header: await boxMetrics(tablet, ".task-detail-modal .modal-header"),
body: await boxMetrics(tablet, ".task-detail-modal .modal-body"),
overlay: await boxMetrics(tablet, ".modal-overlay"),
grip: await boxMetrics(tablet, ".task-detail-modal .modal-resize-grip"),
panel: await boxMetrics(tablet, "[data-testid='floating-window-task-detail-fixture']"),
header: await boxMetrics(tablet, ".task-detail-content .modal-header"),
body: await boxMetrics(tablet, ".task-detail-content .modal-body"),
overlay: await boxMetrics(tablet, "[data-testid='task-detail-modal-overlay']"),
handle: await boxMetrics(tablet, "[data-testid='floating-window-task-detail-fixture'] [data-testid='floating-window-resize-se']"),
};
// The production tablet breakpoint intentionally sizes the panel to 98vw and offsets it
// by 6vh. Checking computed overlay padding plus the actual panel rect catches a painted
// inset regression that the fixed overlay's own y=0 rect cannot observe.
expect(parseFloat(tabletTaskDetail.overlay.paddingBlockStart)).toBeCloseTo(1024 * 0.06);
expect(tabletTaskDetail.panel.y).toBeCloseTo(parseFloat(tabletTaskDetail.overlay.paddingBlockStart));
expect(tabletTaskDetail.panel.width).toBeCloseTo(768 * 0.98, 1);
expect(tabletTaskDetail.panel.x).toBeCloseTo((768 - tabletTaskDetail.panel.width) / 2, 1);
/*
FNXC:ModalTouchGeometry 2026-07-26-20:08:
Task Detail and New Task share FloatingWindow's zero-inset tablet geometry while preserving
desktop content density and 44px touch handles.
*/
expect(tabletTaskDetail.overlay.paddingBlockStart).toBe(desktopTaskDetail.overlay.paddingBlockStart);
expect(tabletTaskDetail.panel.width).toBe(desktopTaskDetail.panel.width);
expect(tabletTaskDetail.panel.x).toBeGreaterThanOrEqual(0);
expect(tabletTaskDetail.overlay.paddingInlineStart).toBe(desktopTaskDetail.overlay.paddingInlineStart);
expect(tabletTaskDetail.overlay.paddingInlineEnd).toBe(desktopTaskDetail.overlay.paddingInlineEnd);
expect(tabletTaskDetail.panel.width).toBeLessThan(desktopTaskDetail.panel.width);
expect(tabletTaskDetail.header.paddingBlockStart).toBe(desktopTaskDetail.header.paddingBlockStart);
expect(tabletTaskDetail.header.paddingBlockEnd).toBe(desktopTaskDetail.header.paddingBlockEnd);
expect(tabletTaskDetail.body.paddingBlockStart).toBe(desktopTaskDetail.body.paddingBlockStart);
expect(tabletTaskDetail.body.paddingBlockEnd).toBe(desktopTaskDetail.body.paddingBlockEnd);
expect(tabletTaskDetail.grip.width).toBe(44);
expect(tabletTaskDetail.grip.height).toBe(44);
expect(tabletTaskDetail.handle.width).toBe(44);
expect(tabletTaskDetail.handle.height).toBe(44);
await tablet.goto(`${baseUrl}app/task-modal-touch-resize-e2e-fixture.html?surface=new-task&reset=1`);
await tablet.waitForTimeout(250);
@@ -337,7 +349,7 @@ describe.runIf(executablePath)("Task modal tablet touch resize browser regressio
body: await boxMetrics(tablet, ".new-task-modal .modal-body"),
overlay: await boxMetrics(tablet, "[data-testid='new-task-modal-overlay']"),
panel: await boxMetrics(tablet, ".new-task-modal"),
handle: await boxMetrics(tablet, "[data-testid='new-task-resize-se']"),
handle: await boxMetrics(tablet, "[data-testid='floating-window-resize-se']"),
};
expect(tabletNewTask.header.paddingBlockStart).toBe(desktopNewTask.header.paddingBlockStart);
expect(tabletNewTask.header.paddingBlockEnd).toBe(desktopNewTask.header.paddingBlockEnd);