FN-7318: preserve planning summary spaces
Preserve editable Planning Mode summary descriptions so mobile and desktop textareas keep typed spaces. - Keep non-empty generated descriptions untrimmed while still falling back for whitespace-only payloads. - Cover mobile and desktop summary description editing with user-event typing assertions. - Add a patch changeset for the Planning Mode description input fix. Files changed: .changeset/fix-mobile-planning-summary-spaces.md | 7 +++++++ .../dashboard/app/components/PlanningModeModal.tsx | 9 +++++++-- .../PlanningModeModal.ui-interactions.test.tsx | 20 ++++++++++++++++++++ 3 files changed, 34 insertions(+), 2 deletions(-) Fusion-Task-Id: FN-7318 Fusion-Task-Lineage: e83ab078-e9a5-4bd4-88fa-72aa031af42e Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
This commit is contained in:
7
.changeset/fix-mobile-planning-summary-spaces.md
Normal file
7
.changeset/fix-mobile-planning-summary-spaces.md
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
summary: Fix mobile Planning Mode description editing so spaces can be entered.
|
||||
category: fix
|
||||
dev: Guards Planning Mode summary normalization so editable fields keep normal text input.
|
||||
@@ -142,8 +142,13 @@ function normalizePlanningSummary(summary: PlanningSummary): PlanningSummary {
|
||||
const title = typeof raw.title === "string" && raw.title.trim().length > 0
|
||||
? raw.title.trim()
|
||||
: "Untitled planning task";
|
||||
const description = typeof raw.description === "string" && raw.description.trim().length > 0
|
||||
? raw.description.trim()
|
||||
/*
|
||||
FNXC:PlanningMode 2026-06-30-12:54:
|
||||
Final summary description is an editable draft. Preserve internal and trailing whitespace during render so mobile text entry can insert a space before the next word; only whitespace-only server payloads fall back to the normalized title.
|
||||
*/
|
||||
const rawDescription = typeof raw.description === "string" ? raw.description : "";
|
||||
const description = rawDescription.trim().length > 0
|
||||
? rawDescription
|
||||
: title;
|
||||
return {
|
||||
...summary,
|
||||
|
||||
@@ -25,6 +25,7 @@ vi.mock("../../hooks/useNavigationHistory", async (importOriginal) => {
|
||||
};
|
||||
});
|
||||
import { act, render, renderHook, screen, fireEvent, waitFor, within } from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import * as api from "../../api";
|
||||
import { PlanningModeModal } from "../PlanningModeModal";
|
||||
import { TaskDetailModal } from "../TaskDetailModal";
|
||||
@@ -997,6 +998,25 @@ describe("PlanningModeModal", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it.each([
|
||||
{ viewport: "mobile" as const, typedDescription: "Mobile summary accepts spaces" },
|
||||
{ viewport: "desktop" as const, typedDescription: "Desktop summary accepts spaces" },
|
||||
])("preserves typed spaces in the $viewport summary description textarea", async ({ viewport, typedDescription }) => {
|
||||
mockViewport(viewport);
|
||||
const user = userEvent.setup();
|
||||
await renderPlanningSummary("Generated summary with existing words");
|
||||
|
||||
const textarea = screen.getByLabelText("Description") as HTMLTextAreaElement;
|
||||
textarea.focus();
|
||||
textarea.setSelectionRange(0, textarea.value.length);
|
||||
await user.type(textarea, typedDescription);
|
||||
|
||||
expect(textarea.value).toContain(typedDescription);
|
||||
expect(textarea.value).not.toContain(typedDescription.replaceAll(" ", ""));
|
||||
expect(screen.getByRole("button", { name: /Create Single Task/i })).toBeDefined();
|
||||
expect(screen.getByRole("button", { name: /Break into Tasks/i })).toBeDefined();
|
||||
});
|
||||
|
||||
it("expands and collapses the mobile summary description without breaking the adjacent markdown toggle", async () => {
|
||||
mockViewport("mobile");
|
||||
const { container } = await renderPlanningSummary(
|
||||
|
||||
Reference in New Issue
Block a user