FN-6958: stop auto-focusing Planning Mode composer
Planning Mode now opens without forcing focus into the initial text area.\n\n- Remove the open-time autofocus effect while preserving explicit textarea focus behavior.\n- Cover mobile, embedded desktop, and initialPlan handoff behavior with regression tests.\n- Add a patch changeset for the published CLI package.\n\nFiles changed:\n .changeset/FN-6958-planning-focus.md | 5 ++\n .../dashboard/app/components/PlanningModeModal.tsx | 10 ++--\n .../__tests__/PlanningModeModal.initial.test.tsx | 69 ++++++++++++++++++++++\n 3 files changed, 78 insertions(+), 6 deletions(-) Fusion-Task-Id: FN-6958 Fusion-Task-Lineage: df030f3f-c9ca-4420-8618-bc3ec44d6e12
This commit is contained in:
5
.changeset/FN-6958-planning-focus.md
Normal file
5
.changeset/FN-6958-planning-focus.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
Stop Planning Mode from automatically focusing the initial text entry when it opens, preventing mobile keyboards from appearing until the user explicitly focuses the textarea.
|
||||
@@ -821,12 +821,10 @@ export function PlanningModeModal({ isOpen, onClose, onTaskCreated, onTasksCreat
|
||||
projectId,
|
||||
]);
|
||||
|
||||
// Focus textarea when opening
|
||||
useEffect(() => {
|
||||
if (isOpen && view.type === "initial") {
|
||||
textareaRef.current?.focus();
|
||||
}
|
||||
}, [isOpen, view.type]);
|
||||
/*
|
||||
FNXC:PlanningFocus 2026-06-23-00:00:
|
||||
Viewing Planning Mode must not auto-focus the initial composer because mobile browsers open the keyboard before the user chooses to type. Keep the textarea ref for autosize and explicit user focus only; populated initialPlan handoffs still auto-start through the separate effect below.
|
||||
*/
|
||||
|
||||
useEffect(() => {
|
||||
if (!isOpen) {
|
||||
|
||||
@@ -216,6 +216,75 @@ describe("PlanningModeModal", () => {
|
||||
expect(screen.queryByText("Planning Mode")).toBeNull();
|
||||
});
|
||||
|
||||
it("does not auto-focus the initial textarea on mobile open until the user focuses it", () => {
|
||||
mockViewport("mobile");
|
||||
|
||||
render(
|
||||
<PlanningModeModal
|
||||
isOpen={true}
|
||||
onClose={mockOnClose}
|
||||
onTaskCreated={mockOnTaskCreated}
|
||||
onTasksCreated={vi.fn()}
|
||||
tasks={mockTasks}
|
||||
/>
|
||||
);
|
||||
|
||||
const textarea = screen.getByLabelText("What do you want to build?") as HTMLTextAreaElement;
|
||||
expect(document.activeElement).not.toBe(textarea);
|
||||
|
||||
act(() => {
|
||||
textarea.focus();
|
||||
});
|
||||
|
||||
expect(document.activeElement).toBe(textarea);
|
||||
});
|
||||
|
||||
it("does not auto-focus the initial textarea in embedded desktop presentation", () => {
|
||||
render(
|
||||
<PlanningModeModal
|
||||
isOpen={true}
|
||||
onClose={mockOnClose}
|
||||
onTaskCreated={mockOnTaskCreated}
|
||||
onTasksCreated={vi.fn()}
|
||||
tasks={mockTasks}
|
||||
initialPlan={undefined}
|
||||
presentation="embedded"
|
||||
/>
|
||||
);
|
||||
|
||||
const textarea = screen.getByLabelText("What do you want to build?") as HTMLTextAreaElement;
|
||||
expect(textarea.value).toBe("");
|
||||
expect(document.activeElement).not.toBe(textarea);
|
||||
});
|
||||
|
||||
it("auto-starts populated initialPlan handoffs without focusing the initial textarea", async () => {
|
||||
const focusSpy = vi.spyOn(HTMLTextAreaElement.prototype, "focus");
|
||||
|
||||
try {
|
||||
render(
|
||||
<PlanningModeModal
|
||||
isOpen={true}
|
||||
onClose={mockOnClose}
|
||||
onTaskCreated={mockOnTaskCreated}
|
||||
onTasksCreated={vi.fn()}
|
||||
tasks={mockTasks}
|
||||
initialPlan="Build a login system from handoff"
|
||||
/>
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockStartPlanningStreaming).toHaveBeenCalledWith("Build a login system from handoff", undefined, undefined, {
|
||||
planningDepth: "medium",
|
||||
customQuestionCount: undefined,
|
||||
}, undefined);
|
||||
});
|
||||
|
||||
expect(focusSpy).not.toHaveBeenCalled();
|
||||
} finally {
|
||||
focusSpy.mockRestore();
|
||||
}
|
||||
});
|
||||
|
||||
it("mobile close path blurs focused input and resets viewport scroll", () => {
|
||||
mockViewport("mobile");
|
||||
const scrollToSpy = vi.spyOn(window, "scrollTo").mockImplementation(() => undefined);
|
||||
|
||||
Reference in New Issue
Block a user