fix: constrain dialog content to the viewport
- Add dynamic viewport bounds and scrollable body regions to dialogs and modals\n- Make tool-launch popups use the shared scrollable body pattern\n- Keep mobile sheets, action sheets, and notification popups scroll-contained\n- Restore the ProjectsPage test setup required for the frontend suite\n\nOpenSpec: fix-dialog-scroll-containment\nQuality gates: npm run typecheck, npm run lint, npm test (88 passed), npm run build
This commit is contained in:
@@ -578,19 +578,23 @@ export const InstanceList = ({
|
||||
{showCreate && (
|
||||
<div className="dialog-overlay" role="dialog" aria-modal="true">
|
||||
<div className="dialog">
|
||||
<h2>Launch Tool</h2>
|
||||
<CreateSessionForm
|
||||
projects={[]}
|
||||
repositories={[]}
|
||||
toolTypes={toolTypes}
|
||||
fixedProjectId={projectId}
|
||||
fixedRepoId={repoId}
|
||||
projectName={projectName}
|
||||
repoName={repoName}
|
||||
onSuccess={handleCreateSuccess}
|
||||
onCancel={() => setShowCreate(false)}
|
||||
submitLabel="Launch"
|
||||
/>
|
||||
<div className="dialog-header">
|
||||
<h2>Launch Tool</h2>
|
||||
</div>
|
||||
<div className="dialog-body">
|
||||
<CreateSessionForm
|
||||
projects={[]}
|
||||
repositories={[]}
|
||||
toolTypes={toolTypes}
|
||||
fixedProjectId={projectId}
|
||||
fixedRepoId={repoId}
|
||||
projectName={projectName}
|
||||
repoName={repoName}
|
||||
onSuccess={handleCreateSuccess}
|
||||
onCancel={() => setShowCreate(false)}
|
||||
submitLabel="Launch"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -62,6 +62,7 @@ export function StartToolFAB() {
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="modal-body">
|
||||
{workspacesLoading ? (
|
||||
<p className="muted">Loading workspaces...</p>
|
||||
) : workspaces.length === 0 ? (
|
||||
@@ -110,6 +111,7 @@ export function StartToolFAB() {
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
import "@testing-library/jest-dom/vitest";
|
||||
import { fireEvent, render, screen } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { WorkspaceToolsPanel } from "./workspace-tools-panel";
|
||||
import type { Workspace } from "../../../types/workspace";
|
||||
|
||||
vi.mock("../../../hooks/use-workspace-instances", () => ({
|
||||
useWorkspaceInstances: () => ({
|
||||
instances: [],
|
||||
loading: false,
|
||||
refresh: vi.fn(),
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock("../tool/tool-starter", () => ({
|
||||
ToolStarter: () => <div data-testid="tool-starter" />,
|
||||
}));
|
||||
|
||||
const workspace: Workspace = {
|
||||
id: "workspace-1",
|
||||
name: "Main",
|
||||
repo_id: "repo-1",
|
||||
repo_name: "repository",
|
||||
repo_ssh_key_id: null,
|
||||
project_id: "project-1",
|
||||
project_name: "Project",
|
||||
user_id: "user-1",
|
||||
branch: "main",
|
||||
path: "/workspace",
|
||||
status: "ready",
|
||||
last_sync_at: null,
|
||||
created_at: "2026-01-01T00:00:00Z",
|
||||
updated_at: "2026-01-01T00:00:00Z",
|
||||
instance_count: 0,
|
||||
};
|
||||
|
||||
describe("WorkspaceToolsPanel", () => {
|
||||
it("places the tool launcher inside the shared scrollable dialog body", () => {
|
||||
const { container } = render(<WorkspaceToolsPanel workspace={workspace} />);
|
||||
|
||||
fireEvent.click(screen.getByRole("button", { name: "Start Tool" }));
|
||||
|
||||
const dialogBody = container.querySelector(".dialog-body");
|
||||
expect(dialogBody).toContainElement(screen.getByTestId("tool-starter"));
|
||||
});
|
||||
});
|
||||
@@ -64,15 +64,19 @@ export function WorkspaceToolsPanel({ workspace }: WorkspaceToolsPanelProps) {
|
||||
{showModal && (
|
||||
<div className="dialog-overlay" onClick={() => setShowModal(false)}>
|
||||
<div className="dialog" onClick={(e) => e.stopPropagation()}>
|
||||
<h3>Start Tool</h3>
|
||||
<ToolStarter
|
||||
workspace={workspace}
|
||||
onStarted={() => {
|
||||
setShowModal(false);
|
||||
void refresh();
|
||||
}}
|
||||
onCancel={() => setShowModal(false)}
|
||||
/>
|
||||
<div className="dialog-header">
|
||||
<h3>Start Tool</h3>
|
||||
</div>
|
||||
<div className="dialog-body">
|
||||
<ToolStarter
|
||||
workspace={workspace}
|
||||
onStarted={() => {
|
||||
setShowModal(false);
|
||||
void refresh();
|
||||
}}
|
||||
onCancel={() => setShowModal(false)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user