feat(FN-779): make inline editor taller with 4-row default and mount-time resize

- Increase inline editor textarea default rows from 1 to 4 for better multi-line editing
- Add mount-time auto-resize to fit content on initial render
- Add regression tests for taller inline editor behavior
- Update documentation to reflect the new taller editor defaults
This commit is contained in:
gsxdsm
2026-04-03 11:23:53 -07:00
parent d55e6a4431
commit 18335ab2a5
4 changed files with 59 additions and 6 deletions

View File

@@ -40,7 +40,7 @@ AI-guided interactive planning for creating well-specified tasks from high-level
### Task Management
- **Kanban Board**: Drag-and-drop task management across columns (Triage, Todo, In Progress, In Review, Done)
- **Inline Editing**: Quick-edit a task's description directly on the board for Triage and Todo columns. Double-click a card or use the pencil icon — visible on hover for desktop, always visible on mobile for touch accessibility. Inline editing changes only the description; the title is preserved. To edit both title and description, use the task detail modal.
- **Inline Editing**: Quick-edit a task's description directly on the board for Triage and Todo columns. The editor opens as a taller multi-line editing area (4 visible lines) for comfortable editing of longer descriptions, and auto-grows to fit existing content. Double-click a card or use the pencil icon — visible on hover for desktop, always visible on mobile for touch accessibility. Inline editing changes only the description; the title is preserved. To edit both title and description, use the task detail modal.
- **Task Detail Editing**: Edit task title and description directly in the task detail modal. Click the pencil icon in the modal header (available for Triage and Todo tasks) to enter edit mode.
- **List View**: Alternative tabular view for tasks with sorting and filtering. The "Hide Done" toggle hides both Done and Archived tasks for an active-work-only view.
- **Model Selection at Creation**: Choose executor and validator AI models while creating tasks from the board or list view, or leave them unset to use the global defaults. Quick-add model dropdowns in both the board triage column and the list view honor saved favorite providers and pinned models, matching the rest of the dashboard model UI.

View File

@@ -161,10 +161,16 @@ function TaskCardComponent({
setEditDescription(task.description || "");
}, [task.id, task.description]);
// Auto-focus on description textarea when entering edit mode
// Auto-focus and auto-resize description textarea when entering edit mode
useEffect(() => {
if (isEditing) {
descTextareaRef.current?.focus();
if (isEditing && descTextareaRef.current) {
const el = descTextareaRef.current;
el.focus();
// Apply the same resize logic used in handleDescChange so the textarea
// opens at the correct height for existing long descriptions without
// requiring the user to type first.
el.style.height = "auto";
el.style.height = el.scrollHeight + "px";
}
}, [isEditing]);
@@ -511,7 +517,7 @@ function TaskCardComponent({
onKeyDown={handleDescKeyDown}
onBlur={handleBlur}
disabled={isSaving}
rows={1}
rows={4}
/>
{isSaving && (
<div className="card-edit-loading">

View File

@@ -1387,6 +1387,53 @@ describe("TaskCard inline editing", () => {
expect(screen.queryByPlaceholderText(/Task title/i)).toBeNull();
expect(screen.getByPlaceholderText(/Task description/i)).toBeDefined();
});
it("opens the description textarea with 4 visible rows", () => {
const task = makeEditableTask({ description: "Some text" });
render(
<TaskCard
task={task}
onOpenDetail={vi.fn()}
addToast={noopToast}
onUpdateTask={noopUpdateTask}
/>
);
// Enter edit mode
const card = document.querySelector('[data-id="FN-099"]');
fireEvent.doubleClick(card!);
const descTextarea = screen.getByPlaceholderText(/Task description/i) as HTMLTextAreaElement;
expect(descTextarea).toBeDefined();
expect(descTextarea.getAttribute("rows")).toBe("4");
});
it("applies mount-time auto-resize for existing long descriptions", () => {
// A multi-line description that would exceed the default 4-row height
const longDescription = Array(10).fill("This is a line of description text.").join("\n");
const task = makeEditableTask({ description: longDescription });
render(
<TaskCard
task={task}
onOpenDetail={vi.fn()}
addToast={noopToast}
onUpdateTask={noopUpdateTask}
/>
);
// Enter edit mode
const card = document.querySelector('[data-id="FN-099"]');
fireEvent.doubleClick(card!);
const descTextarea = screen.getByPlaceholderText(/Task description/i) as HTMLTextAreaElement;
// The mount-time resize effect sets height to scrollHeight + "px".
// In JSDOM, scrollHeight is 0 (no real layout), so the style ends up
// as "0px" — but the important thing is the effect *did* set the
// height property, proving the auto-resize logic runs on mount.
expect(descTextarea.style.height).not.toBe("");
});
});
/**

View File

@@ -3974,7 +3974,7 @@ body {
resize: none;
overflow: hidden;
line-height: 1.4;
min-height: 38px;
min-height: 80px;
transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
}