From f5d17cfa7935375977a66f110edda65704423a4d Mon Sep 17 00:00:00 2001 From: Dotta Date: Thu, 21 May 2026 03:37:46 +0000 Subject: [PATCH] Fix mobile new issue dialog height --- ui/src/components/NewIssueDialog.test.tsx | 18 +++++++++++++++--- ui/src/components/NewIssueDialog.tsx | 8 +++++--- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/ui/src/components/NewIssueDialog.test.tsx b/ui/src/components/NewIssueDialog.test.tsx index 12cd0324544..129c7733e4a 100644 --- a/ui/src/components/NewIssueDialog.test.tsx +++ b/ui/src/components/NewIssueDialog.test.tsx @@ -1,7 +1,7 @@ // @vitest-environment jsdom -import { act } from "react"; import type { ComponentProps, ReactNode } from "react"; +import { flushSync } from "react-dom"; import { createRoot } from "react-dom/client"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; @@ -226,6 +226,16 @@ vi.mock("@/components/ui/popover", () => ({ // eslint-disable-next-line @typescript-eslint/no-explicit-any (globalThis as any).IS_REACT_ACT_ENVIRONMENT = true; +function act(callback: () => void | Promise): void | Promise { + let result: unknown; + flushSync(() => { + result = callback(); + }); + return result && typeof (result as Promise).then === "function" + ? (result as Promise).then(() => undefined) + : undefined; +} + async function flush() { await act(async () => { await new Promise((resolve) => setTimeout(resolve, 0)); @@ -722,10 +732,12 @@ describe("NewIssueDialog", () => { await flush(); const dialogContent = Array.from(container.querySelectorAll("div")).find((element) => - typeof element.className === "string" && element.className.includes("max-h-[calc(100dvh-2rem)]"), + typeof element.className === "string" && element.className.includes("max-h-[var(--new-issue-dialog-height)]"), ); - expect(dialogContent?.className).toContain("h-[calc(100dvh-2rem)]"); + expect(dialogContent?.className).toContain("h-[var(--new-issue-dialog-height)]"); expect(dialogContent?.className).toContain("overflow-hidden"); + expect(dialogContent?.getAttribute("style")).toContain("env(safe-area-inset-top)"); + expect(dialogContent?.getAttribute("style")).toContain("env(safe-area-inset-bottom)"); const titleInput = container.querySelector('textarea[placeholder="Issue title"]'); const descriptionInput = container.querySelector('textarea[aria-label="Add description..."]'); diff --git a/ui/src/components/NewIssueDialog.tsx b/ui/src/components/NewIssueDialog.tsx index 4de2097155c..78c7e546467 100644 --- a/ui/src/components/NewIssueDialog.tsx +++ b/ui/src/components/NewIssueDialog.tsx @@ -1,4 +1,4 @@ -import { memo, useState, useEffect, useRef, useCallback, useMemo, type ChangeEvent, type DragEvent, type RefObject } from "react"; +import { memo, useState, useEffect, useRef, useCallback, useMemo, type ChangeEvent, type CSSProperties, type DragEvent, type RefObject } from "react"; import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; import type { IssueWorkMode } from "@paperclipai/shared"; import { pickTextColorForSolidBg } from "@/lib/color-contrast"; @@ -70,6 +70,7 @@ import { InlineEntitySelector, type InlineEntityOption } from "./InlineEntitySel const DRAFT_KEY = "paperclip:issue-draft"; const DEBOUNCE_MS = 800; +const MOBILE_DIALOG_HEIGHT = "calc(100dvh - max(1rem, env(safe-area-inset-top)) - max(1rem, env(safe-area-inset-bottom)))"; interface IssueDraft { @@ -1202,10 +1203,11 @@ export function NewIssueDialog() {