FN-5876: prevent mobile quick-entry autofocus
Avoid restoring focus to the new-task quick entry on mobile while preserving desktop behavior. - gate quick-entry autofocus and post-submit focus restoration behind a desktop-width check - add desktop and mobile coverage for initial focus behavior - add mobile coverage for avoiding focus restoration after successful task creation Files changed: .../dashboard/app/components/QuickEntryBox.tsx | 5 ++- .../components/__tests__/QuickEntryBox.test.tsx | 42 ++++++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) Fusion-Task-Id: FN-5876 Fusion-Task-Lineage: ede08207-6506-4f5e-8a6f-f7f1cbfb37c9
This commit is contained in:
@@ -16,6 +16,7 @@ import { NodeHealthDot } from "./NodeHealthDot";
|
||||
import { ProviderIcon } from "./ProviderIcon";
|
||||
|
||||
const STORAGE_KEY = "kb-quick-entry-text";
|
||||
const MOBILE_BREAKPOINT_PX = 768;
|
||||
const ALLOWED_IMAGE_TYPES = ["image/png", "image/jpeg", "image/gif", "image/webp"];
|
||||
|
||||
interface PendingImage {
|
||||
@@ -329,7 +330,9 @@ export function QuickEntryBox({ onCreate, addToast, tasks = [], availableModels,
|
||||
if (!isSubmitting && description === "" && textareaRef.current) {
|
||||
// Use setTimeout to ensure focus happens after React re-enables the textarea
|
||||
const focusTimeout = setTimeout(() => {
|
||||
textareaRef.current?.focus();
|
||||
if (typeof window !== "undefined" && window.innerWidth > MOBILE_BREAKPOINT_PX) {
|
||||
textareaRef.current?.focus();
|
||||
}
|
||||
}, 0);
|
||||
return () => clearTimeout(focusTimeout);
|
||||
}
|
||||
|
||||
@@ -295,6 +295,28 @@ describe("QuickEntryBox", () => {
|
||||
expect((textarea as HTMLTextAreaElement).rows).toBe(2);
|
||||
});
|
||||
|
||||
it("focuses the quick-entry textarea on mount at desktop width", async () => {
|
||||
mockDesktopViewport();
|
||||
renderQuickEntryBox({});
|
||||
const textarea = screen.getByTestId("quick-entry-input");
|
||||
|
||||
await waitFor(() => {
|
||||
expect(document.activeElement).toBe(textarea);
|
||||
});
|
||||
});
|
||||
|
||||
it("does not focus the quick-entry textarea on mount at mobile width", async () => {
|
||||
const innerWidthSpy = vi.spyOn(window, "innerWidth", "get").mockReturnValue(375);
|
||||
renderQuickEntryBox({});
|
||||
const textarea = screen.getByTestId("quick-entry-input");
|
||||
|
||||
await waitFor(() => {
|
||||
expect(document.activeElement).not.toBe(textarea);
|
||||
});
|
||||
|
||||
innerWidthSpy.mockRestore();
|
||||
});
|
||||
|
||||
it("textarea spans full container width (FN-1608)", () => {
|
||||
mockDesktopViewport();
|
||||
renderQuickEntryBox({});
|
||||
@@ -643,6 +665,26 @@ describe("QuickEntryBox", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("does not restore focus after successful creation at mobile width", async () => {
|
||||
const innerWidthSpy = vi.spyOn(window, "innerWidth", "get").mockReturnValue(375);
|
||||
const { props } = renderQuickEntryBox({});
|
||||
const textarea = screen.getByTestId("quick-entry-input");
|
||||
|
||||
fireEvent.focus(textarea);
|
||||
fireEvent.change(textarea, { target: { value: "Task to create" } });
|
||||
fireEvent.keyDown(textarea, { key: "Enter" });
|
||||
|
||||
await waitFor(() => {
|
||||
expect(props.onCreate).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(document.activeElement).not.toBe(textarea);
|
||||
});
|
||||
|
||||
innerWidthSpy.mockRestore();
|
||||
});
|
||||
|
||||
describe("Rich creation features", () => {
|
||||
it("shows inline deps/models/save controls when expanded", () => {
|
||||
renderQuickEntryBox({});
|
||||
|
||||
Reference in New Issue
Block a user