FN-7288: remove quick-add Plan buttons
Remove quick-add planning buttons from board create surfaces while preserving other planning entry points. - Remove the Plan button, icon imports, handlers, test id, disabled state, tooltip, and click targets from QuickEntryBox and InlineCreateCard. - Update quick-add and mobile board tests to assert the Plan affordance is absent while task creation and Subtask behavior remain available. - Refresh dashboard docs and add a patch changeset describing the quick-add Plan removal. Files changed: .changeset/fn-7288-remove-quick-add-plan-button.md | 7 + docs/dashboard-guide.md | 4 +- docs/getting-started.md | 2 +- docs/workflow-steps.md | 2 +- .../dashboard/app/components/InlineCreateCard.tsx | 38 +--- .../dashboard/app/components/QuickEntryBox.tsx | 38 +--- .../components/__tests__/InlineCreateCard.test.tsx | 56 +++--- .../components/__tests__/QuickEntryBox.test.tsx | 196 +++++---------------- .../app/components/__tests__/board-mobile.test.tsx | 6 +- 9 files changed, 97 insertions(+), 252 deletions(-) Fusion-Task-Id: FN-7288 Fusion-Task-Lineage: b4a5d6b6-83e7-4b0c-a38d-6ff36d8cd97e Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
This commit is contained in:
7
.changeset/fn-7288-remove-quick-add-plan-button.md
Normal file
7
.changeset/fn-7288-remove-quick-add-plan-button.md
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
summary: Remove the board quick-add Plan button while keeping New Task planning available.
|
||||
category: fix
|
||||
dev: QuickEntryBox and InlineCreateCard no longer render data-testid="plan-button" or Plan click targets.
|
||||
@@ -346,7 +346,7 @@ These values are sent with the Planning Mode create-task request as `branchSelec
|
||||
|
||||
<!-- FNXC:WorkflowCreateForwarding 2026-06-30-09:12: Dashboard create flows must forward the active real workflow id so planning/quick-create tasks do not briefly land on the default workflow or persist the synthetic All workflows aggregate. -->
|
||||
|
||||
When inline quick-create, Planning Mode, or Subtask Breakdown is opened from a workflow-filtered board/list lane, the create request also carries that active workflow selection. Quick-created tasks appear on the selected workflow lane immediately while board-workflows metadata refreshes, and planning saves, planning breakdown saves, and subtask-breakdown saves create their tasks directly on the selected workflow lane instead of briefly landing on the default board. When Board is showing **All workflows**, quick-create uses the real workflow intake/default column that owns the affordance; it never submits the synthetic aggregate as a workflow id.
|
||||
When quick-create task creation, Planning Mode, or Subtask Breakdown runs from a workflow-filtered board/list lane, the create request also carries that active workflow selection. Quick-created tasks appear on the selected workflow lane immediately while board-workflows metadata refreshes, and planning saves, planning breakdown saves, and subtask-breakdown saves create their tasks directly on the selected workflow lane instead of briefly landing on the default board. When Board is showing **All workflows**, quick-create uses the real workflow intake/default column that owns the affordance; it never submits the synthetic aggregate as a workflow id.
|
||||
|
||||
The **New Task** dialog's workflow selector also defaults to the current or last selected Board/List workflow lane for the current project. If no valid lane has been selected, or the remembered lane was deleted, the selector falls back to the project default workflow and task creation omits an explicit `workflowId`.
|
||||
|
||||
@@ -373,7 +373,7 @@ Rules:
|
||||
- `Merge target / base branch` stays optional for all modes and uses the same branch-dropdown + `Custom…` fallback behavior as Planning Mode.
|
||||
- In **More options → Model Configuration**, **Auto-merge** is a per-task override with three states: **Default** (follow project setting), **Enabled**, or **Disabled**.
|
||||
|
||||
The dialog also exposes the board quick-add AI handoffs: **Plan** opens Planning Mode with the current description, and **Subtask** opens Subtask Breakdown with the current description when **Settings → Experimental Features → Subtask Breakdown** is enabled. The Subtask handoff is hidden by default; visible handoff buttons remain disabled until the description has content, matching the quick-add row behavior. **Execution mode** and optional workflow-step selection are available in the New Task dialog as well as quick entry, so users can choose Fast or standard execution and opt into workflow-specific creation-time steps before creating a task from either surface.
|
||||
The dialog also exposes AI handoffs that quick-add no longer shows: **Plan** opens Planning Mode with the current description, and **Subtask** opens Subtask Breakdown with the current description when **Settings → Experimental Features → Subtask Breakdown** is enabled. The Subtask handoff is hidden by default; visible handoff buttons remain disabled until the description has content, matching the quick-add row behavior for Subtask. **Execution mode** and optional workflow-step selection are available in the New Task dialog as well as quick entry, so users can choose Fast or standard execution and opt into workflow-specific creation-time steps before creating a task from either surface.
|
||||
|
||||
The full **New Task** dialog includes a compact **GitHub issue or PR** picker near the description. It detects GitHub remotes for the current project, auto-selects a single remote or `origin`, and asks you to choose a remote when multiple non-`origin` remotes are available. Selecting an issue replaces the description with a prompt that tells the executor to fetch/read the issue and includes `Source: <issue-url>`; selecting a pull request creates a PR-focused prompt with `PR: <pr-url>` and explicit instructions to inspect the PR conversation, review comments, checks, and changed files, then resolve or address actionable review comments. If you already typed a description, Fusion asks before replacing it. This picker only seeds the prompt; it does not import, close, or comment on GitHub items. On mobile, the full-screen New Task sheet keeps the GitHub picker, dependency picker, agent picker, quick handoff buttons, and action row tappable and scrollable even when the keyboard reduces the visual viewport.
|
||||
|
||||
|
||||
@@ -114,7 +114,7 @@ Create tasks from the board or CLI.
|
||||
|
||||
### Option B: Planning
|
||||
|
||||
Open **Planning** from the left sidebar on desktop/tablet, or use the board/New Task **Plan** action to send your draft into AI planning mode:
|
||||
Open **Planning** from the left sidebar on desktop/tablet, or use the **New Task** dialog's **Plan** action to send your draft into AI planning mode:
|
||||
|
||||
- Fusion asks clarifying questions
|
||||
- Produces a structured summary
|
||||
|
||||
@@ -81,7 +81,7 @@ Use this inventory as the documentation map for current workflow behavior:
|
||||
| Agent workflow tools | Agents can list/get/create/update/delete workflows, inspect traits, read/write workflow settings, select workflows for explicit task contexts, and pass `workflow_id` when creating/delegating tasks. Prompt-injectable lanes strip approval-bypass flags on workflow writes. | [Agents](./agents.md#interactive-cli-chat) and [CLI Reference](./cli-reference.md#published-agent-extension-workflow-tools). |
|
||||
| Routing boundary | Agents may select/change a workflow only for explicit user requests or tasks they created; no-commit markers do not imply Quick fix or any other workflow. | This page, [Selecting workflows](#selecting-workflows); [Agents](./agents.md#interactive-cli-chat). |
|
||||
| Dashboard board/list/graph selection | Board/List/Header/Graph share durable per-project workflow selection; stale saved ids fall back to a valid workflow. Board adds a dashboard-only **All workflows** aggregate and task workflow-name badges; Graph uses **All workflows** for the full active graph. | [Dashboard Guide → Board View](./dashboard-guide.md#board-view), [Graph View](./dashboard-guide.md#graph-view), and [Workflow Selection and Editor](./dashboard-guide.md#workflow-selection-and-editor). |
|
||||
| Create/planning forwarding | Inline quick-create, Planning Mode, Subtask Breakdown, and the New Task dialog forward the active real workflow id when creating tasks; **All workflows** quick-create chooses a real workflow intake/default column instead of saving a synthetic aggregate id. | [Dashboard Guide → Planning Mode](./dashboard-guide.md#planning-mode). |
|
||||
| Create/planning forwarding | Quick-create task creation, Planning Mode, Subtask Breakdown, and the New Task dialog forward the active real workflow id when creating tasks; **All workflows** quick-create chooses a real workflow intake/default column instead of saving a synthetic aggregate id. | [Dashboard Guide → Planning Mode](./dashboard-guide.md#planning-mode). |
|
||||
|
||||
### Skill-backed workflow steps
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import "./InlineCreateCard.css";
|
||||
import { useState, useCallback, useEffect, useRef } from "react";
|
||||
import { createPortal } from "react-dom";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Brain, Link, Lightbulb, ListTree, Zap, ChevronDown, ChevronUp, Bot, Maximize2, Minimize2, Server } from "lucide-react";
|
||||
import { Brain, Link, ListTree, Zap, ChevronDown, ChevronUp, Bot, Maximize2, Minimize2, Server } from "lucide-react";
|
||||
import { DEFAULT_TASK_PRIORITY, TASK_PRIORITIES, type Task, type TaskPriority, type Settings, type ResolvedWorkflowOptionalStep } from "@fusion/core";
|
||||
import { getErrorMessage } from "@fusion/core";
|
||||
import type { ToastType } from "../hooks/useToast";
|
||||
@@ -39,7 +39,7 @@ interface InlineCreateCardProps {
|
||||
*/
|
||||
availableModels?: ModelInfo[];
|
||||
/**
|
||||
* Called when the user clicks the "Plan" button to open planning mode.
|
||||
* Preserved for shared create-surface prop compatibility. Inline quick-create intentionally omits Plan.
|
||||
*/
|
||||
onPlanningMode?: (initialPlan: string, workflowId?: string | null) => void;
|
||||
/**
|
||||
@@ -82,7 +82,6 @@ export function InlineCreateCard({
|
||||
addToast,
|
||||
projectId,
|
||||
availableModels,
|
||||
onPlanningMode,
|
||||
onSubtaskBreakdown,
|
||||
}: InlineCreateCardProps) {
|
||||
const { t } = useTranslation("app");
|
||||
@@ -693,23 +692,10 @@ export function InlineCreateCard({
|
||||
e.preventDefault();
|
||||
}, []);
|
||||
|
||||
const handlePlanClick = useCallback(() => {
|
||||
const trimmed = description.trim();
|
||||
if (!trimmed) {
|
||||
addToast(t("inline.enterDescriptionFirst", "Enter a description first"), "error");
|
||||
return;
|
||||
}
|
||||
if (selectedWorkflowId !== null) {
|
||||
onPlanningMode?.(trimmed, selectedWorkflowId);
|
||||
} else {
|
||||
onPlanningMode?.(trimmed);
|
||||
}
|
||||
/*
|
||||
FNXC:QuickAddPlanningPreserve 2026-06-22-00:00:
|
||||
Opening planning mode must keep the inline-create description and scoped draft available when the user exits without creating tasks. Planning completion owns the eventual draft clear.
|
||||
*/
|
||||
}, [description, onPlanningMode, selectedWorkflowId, addToast, t]);
|
||||
|
||||
/*
|
||||
FNXC:InlineCreate 2026-06-30-00:00:
|
||||
Inline quick-create intentionally omits the Plan button, icon, disabled state, tooltip, and click target while preserving Subtask and task creation controls.
|
||||
*/
|
||||
const handleSubtaskClick = useCallback(() => {
|
||||
const trimmed = description.trim();
|
||||
if (!trimmed) {
|
||||
@@ -881,18 +867,6 @@ export function InlineCreateCard({
|
||||
{isExpanded && (
|
||||
<div id="inline-create-controls" className="inline-create-footer">
|
||||
<div className="inline-create-controls">
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-sm"
|
||||
onClick={handlePlanClick}
|
||||
onMouseDown={(e) => e.preventDefault()}
|
||||
disabled={!description.trim()}
|
||||
data-testid="plan-button"
|
||||
title={t("inline.openPlanningMode", "Open planning mode with current description")}
|
||||
>
|
||||
<Lightbulb size={12} style={{ verticalAlign: "middle", marginRight: 4 }} />
|
||||
{t("inline.plan", "Plan")}
|
||||
</button>
|
||||
{/* FNXC:QuickAddSubtaskFlag 2026-06-21-00:00: Render no Subtask button or orphaned inline-create click target unless the default-off `subtaskBreakdown` experiment wires this callback. */}
|
||||
{onSubtaskBreakdown && (
|
||||
<button
|
||||
|
||||
@@ -31,7 +31,8 @@ interface QuickEntryBoxProps {
|
||||
tasks?: Task[];
|
||||
availableModels?: ModelInfo[];
|
||||
/**
|
||||
* Called when the user clicks the "Plan" button to open planning mode.
|
||||
* Preserved for callers that still pass planning handoff props through shared quick-create plumbing.
|
||||
* QuickEntryBox intentionally does not render a quick-add Plan button.
|
||||
*/
|
||||
onPlanningMode?: (initialPlan: string, workflowId?: string | null) => void;
|
||||
/**
|
||||
@@ -123,7 +124,7 @@ function resolveQuickAddWorkflowId(
|
||||
return workflowOptions[0]?.id ?? null;
|
||||
}
|
||||
|
||||
export function QuickEntryBox({ onCreate, addToast, tasks = [], availableModels, onPlanningMode, onSubtaskBreakdown, workflowId, workflowOptions, defaultWorkflowId, projectId, autoExpand = true, defaultExpanded = true, singleLine = false, favoriteProviders: parentFavoriteProviders, favoriteModels: parentFavoriteModels, onToggleFavorite: parentToggleFavorite, onToggleModelFavorite: parentToggleModelFavorite, onOpenTask }: QuickEntryBoxProps) {
|
||||
export function QuickEntryBox({ onCreate, addToast, tasks = [], availableModels, onSubtaskBreakdown, workflowId, workflowOptions, defaultWorkflowId, projectId, autoExpand = true, defaultExpanded = true, singleLine = false, favoriteProviders: parentFavoriteProviders, favoriteModels: parentFavoriteModels, onToggleFavorite: parentToggleFavorite, onToggleModelFavorite: parentToggleModelFavorite, onOpenTask }: QuickEntryBoxProps) {
|
||||
const { t } = useTranslation("app");
|
||||
const [description, setDescription] = useState(() => {
|
||||
if (typeof window !== "undefined") {
|
||||
@@ -1477,23 +1478,10 @@ export function QuickEntryBox({ onCreate, addToast, tasks = [], availableModels,
|
||||
}
|
||||
}, [favoriteModels, favoriteProviders, parentToggleModelFavorite]);
|
||||
|
||||
const handlePlanClick = useCallback(() => {
|
||||
const trimmed = description.trim();
|
||||
if (!trimmed) {
|
||||
addToast(t("tasks.enterDescriptionFirst", "Enter a description first"), "error");
|
||||
return;
|
||||
}
|
||||
if (selectedWorkflowForCreate !== undefined) {
|
||||
onPlanningMode?.(trimmed, selectedWorkflowForCreate);
|
||||
} else {
|
||||
onPlanningMode?.(trimmed);
|
||||
}
|
||||
/*
|
||||
FNXC:QuickAddPlanningPreserve 2026-06-22-00:00:
|
||||
Opening planning mode must preserve the quick-add description and scoped draft so exiting planning without creating tasks restores the user's text. The draft is cleared only by planning-completion handlers.
|
||||
*/
|
||||
}, [description, onPlanningMode, selectedWorkflowForCreate, addToast, t]);
|
||||
|
||||
/*
|
||||
FNXC:QuickEntry 2026-06-30-00:00:
|
||||
Quick-add intentionally exposes no Plan button, disabled Plan state, tooltip, test id, or click target. Keep non-quick-add planning entry points such as the New Task dialog and model-menu planning lane intact.
|
||||
*/
|
||||
const handleSubtaskClick = useCallback(() => {
|
||||
const trimmed = description.trim();
|
||||
if (!trimmed) {
|
||||
@@ -1857,18 +1845,6 @@ export function QuickEntryBox({ onCreate, addToast, tasks = [], availableModels,
|
||||
triggerTestId="quick-entry-optional-steps-trigger"
|
||||
/>
|
||||
|
||||
{/* FNXC:QuickEntry 2026-06-30-00:00: Quick Add Plan stays a normal text action without a Lightbulb icon so the action row preserves behavior while removing the extra visual affordance and icon spacing shell. */}
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-sm"
|
||||
onClick={handlePlanClick}
|
||||
onMouseDown={(e) => e.preventDefault()}
|
||||
disabled={!description.trim()}
|
||||
data-testid="plan-button"
|
||||
title={t("tasks.planButtonTitle", "Open planning mode with current description")}
|
||||
>
|
||||
{t("tasks.plan", "Plan")}
|
||||
</button>
|
||||
{/* FNXC:QuickAddSubtaskFlag 2026-06-21-00:00: Render no Subtask button or click target unless App wires the default-off `subtaskBreakdown` experiment callback. */}
|
||||
{onSubtaskBreakdown && (
|
||||
<button
|
||||
|
||||
@@ -16,7 +16,6 @@ vi.mock("lucide-react", () => ({
|
||||
Search: () => null,
|
||||
Sparkles: () => null,
|
||||
Terminal: () => null,
|
||||
Lightbulb: () => null,
|
||||
ListTree: () => null,
|
||||
Zap: () => null,
|
||||
ChevronDown: () => null,
|
||||
@@ -812,38 +811,38 @@ describe("InlineCreateCard dependency dropdown search", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("InlineCreateCard Plan and Subtask buttons", () => {
|
||||
it("renders Plan and Subtask buttons disabled when description is empty", () => {
|
||||
describe("InlineCreateCard Subtask controls without Plan", () => {
|
||||
it("omits Plan and renders Subtask disabled when description is empty", () => {
|
||||
renderCard();
|
||||
expandCard();
|
||||
const planButton = screen.getByTestId("plan-button") as HTMLButtonElement;
|
||||
const subtaskButton = screen.getByTestId("subtask-button") as HTMLButtonElement;
|
||||
expect(planButton.disabled).toBe(true);
|
||||
expect(screen.queryByTestId("plan-button")).not.toBeInTheDocument();
|
||||
expect(screen.queryByRole("button", { name: /^Plan$/i })).not.toBeInTheDocument();
|
||||
expect(subtaskButton.disabled).toBe(true);
|
||||
});
|
||||
|
||||
it("enables Plan and Subtask buttons when description is entered", () => {
|
||||
it("omits Plan and enables Subtask when description is entered", () => {
|
||||
renderCard();
|
||||
expandCard();
|
||||
const textarea = screen.getByPlaceholderText("What needs to be done?");
|
||||
fireEvent.change(textarea, { target: { value: "Test task" } });
|
||||
|
||||
const planButton = screen.getByTestId("plan-button") as HTMLButtonElement;
|
||||
const subtaskButton = screen.getByTestId("subtask-button") as HTMLButtonElement;
|
||||
expect(planButton.disabled).toBe(false);
|
||||
expect(screen.queryByTestId("plan-button")).not.toBeInTheDocument();
|
||||
expect(subtaskButton.disabled).toBe(false);
|
||||
});
|
||||
|
||||
it("calls onPlanningMode with description and preserves input draft when Plan clicked", () => {
|
||||
it("omits the Plan handoff while preserving input draft", () => {
|
||||
const onPlanningMode = vi.fn();
|
||||
renderCard([], { onPlanningMode });
|
||||
expandCard();
|
||||
const textarea = screen.getByPlaceholderText("What needs to be done?") as HTMLTextAreaElement;
|
||||
|
||||
fireEvent.change(textarea, { target: { value: " Plan this task " } });
|
||||
fireEvent.click(screen.getByTestId("plan-button"));
|
||||
|
||||
expect(onPlanningMode).toHaveBeenCalledWith("Plan this task");
|
||||
expect(screen.queryByTestId("plan-button")).not.toBeInTheDocument();
|
||||
expect(screen.queryByTitle("Open planning mode with current description")).not.toBeInTheDocument();
|
||||
expect(onPlanningMode).not.toHaveBeenCalled();
|
||||
expect(textarea.value).toBe(" Plan this task ");
|
||||
expect(localStorage.getItem(INLINE_CREATE_STORAGE_KEY)).toBe(" Plan this task ");
|
||||
});
|
||||
@@ -869,22 +868,20 @@ describe("InlineCreateCard Plan and Subtask buttons", () => {
|
||||
expect(controlsRow).toBeTruthy();
|
||||
expect(screen.queryByTestId("subtask-button")).not.toBeInTheDocument();
|
||||
expect(screen.queryByTitle("Break down into AI-generated subtasks")).not.toBeInTheDocument();
|
||||
expect(controlsRow.contains(screen.getByTestId("plan-button"))).toBe(true);
|
||||
expect(screen.queryByTestId("plan-button")).not.toBeInTheDocument();
|
||||
expect(controlsRow.querySelector(".dep-trigger")).toBeTruthy();
|
||||
});
|
||||
|
||||
it("shows toast when Plan clicked with empty description (via direct handler call)", () => {
|
||||
it("does not leave a disabled Plan button for empty descriptions", () => {
|
||||
const addToast = vi.fn();
|
||||
const onPlanningMode = vi.fn();
|
||||
renderCard([], { addToast, onPlanningMode });
|
||||
expandCard();
|
||||
|
||||
// When no description, button is disabled - verify that behavior
|
||||
const planButton = screen.getByTestId("plan-button") as HTMLButtonElement;
|
||||
expect(planButton.disabled).toBe(true);
|
||||
|
||||
// The handler validation exists but can't be triggered via click when disabled
|
||||
// The disabled state is the primary UX protection
|
||||
expect(screen.queryByTestId("plan-button")).not.toBeInTheDocument();
|
||||
expect(screen.queryByTitle("Open planning mode with current description")).not.toBeInTheDocument();
|
||||
expect(addToast).not.toHaveBeenCalled();
|
||||
expect(onPlanningMode).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("shows toast when Subtask clicked with empty description (via direct handler call)", () => {
|
||||
@@ -1018,8 +1015,8 @@ describe("InlineCreateCard button visibility when collapsed", () => {
|
||||
// Click toggle to expand
|
||||
expandCard();
|
||||
|
||||
// Now buttons should be visible
|
||||
expect(screen.getByTestId("plan-button")).toBeTruthy();
|
||||
// Now non-Plan buttons should be visible
|
||||
expect(screen.queryByTestId("plan-button")).toBeNull();
|
||||
expect(screen.getByTestId("subtask-button")).toBeTruthy();
|
||||
expect(screen.getByText(/Deps/)).toBeTruthy();
|
||||
expect(screen.getByTestId("save-button")).toBeTruthy();
|
||||
@@ -1030,7 +1027,8 @@ describe("InlineCreateCard button visibility when collapsed", () => {
|
||||
|
||||
// Expand
|
||||
expandCard();
|
||||
expect(screen.getByTestId("plan-button")).toBeTruthy();
|
||||
expect(screen.queryByTestId("plan-button")).toBeNull();
|
||||
expect(screen.getByTestId("subtask-button")).toBeTruthy();
|
||||
|
||||
// Collapse
|
||||
expandCard();
|
||||
@@ -1173,7 +1171,7 @@ describe("InlineCreateCard button visibility when collapsed", () => {
|
||||
});
|
||||
|
||||
describe("Consolidated controls layout (FN-781, FN-1292)", () => {
|
||||
it("renders Plan, Subtask, Deps, Agent, and Models together in footer controls when expanded", () => {
|
||||
it("renders Subtask, Deps, Agent, and Models together in footer controls without Plan", () => {
|
||||
renderCard();
|
||||
expandCard();
|
||||
|
||||
@@ -1181,8 +1179,8 @@ describe("InlineCreateCard button visibility when collapsed", () => {
|
||||
const controlsRow = document.querySelector(".inline-create-controls");
|
||||
expect(controlsRow).toBeTruthy();
|
||||
|
||||
// Plan, Subtask, Deps, Agent, Browser Verify, Priority, Preset, Models all in one row
|
||||
expect(controlsRow!.contains(screen.getByTestId("plan-button"))).toBe(true);
|
||||
// Subtask, Deps, Agent, Browser Verify, Priority, Preset, Models all in one row; Plan is intentionally omitted.
|
||||
expect(screen.queryByTestId("plan-button")).not.toBeInTheDocument();
|
||||
expect(controlsRow!.contains(screen.getByTestId("subtask-button"))).toBe(true);
|
||||
expect(controlsRow!.contains(screen.getByTestId("inline-create-agent-button"))).toBe(true);
|
||||
expect(controlsRow!.contains(screen.getByTestId("inline-create-priority-select"))).toBe(true);
|
||||
@@ -1210,18 +1208,16 @@ describe("InlineCreateCard button visibility when collapsed", () => {
|
||||
expect(controlsRow!.contains(saveButton)).toBe(false);
|
||||
});
|
||||
|
||||
it("Plan and Subtask disabled state still works in consolidated controls", () => {
|
||||
it("Subtask disabled state still works in consolidated controls without Plan", () => {
|
||||
renderCard();
|
||||
expandCard();
|
||||
const textarea = screen.getByPlaceholderText("What needs to be done?");
|
||||
|
||||
// Buttons should be disabled when description is empty
|
||||
expect((screen.getByTestId("plan-button") as HTMLButtonElement).disabled).toBe(true);
|
||||
expect(screen.queryByTestId("plan-button")).not.toBeInTheDocument();
|
||||
expect((screen.getByTestId("subtask-button") as HTMLButtonElement).disabled).toBe(true);
|
||||
|
||||
// Type something — buttons should become enabled
|
||||
fireEvent.change(textarea, { target: { value: "Some task" } });
|
||||
expect((screen.getByTestId("plan-button") as HTMLButtonElement).disabled).toBe(false);
|
||||
expect(screen.queryByTestId("plan-button")).not.toBeInTheDocument();
|
||||
expect((screen.getByTestId("subtask-button") as HTMLButtonElement).disabled).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -376,7 +376,6 @@ const QUICK_ENTRY_ACTION_BUTTONS = [
|
||||
["Fast", "quick-entry-fast-toggle"],
|
||||
["GitHub", "quick-entry-github-toggle"],
|
||||
["Priority", "quick-entry-priority-button"],
|
||||
["Plan", "plan-button"],
|
||||
["Subtask", "subtask-button"],
|
||||
["Refine", "refine-button"],
|
||||
["Deps", "quick-entry-deps"],
|
||||
@@ -739,19 +738,12 @@ describe("QuickEntryBox", () => {
|
||||
expect(document.activeElement).not.toBe(textarea);
|
||||
});
|
||||
|
||||
it("does not auto-focus after Plan or Subtask handoff reset the form", async () => {
|
||||
it("does not auto-focus after Subtask handoff resets the form", async () => {
|
||||
mockDesktopViewport();
|
||||
const onPlanningMode = vi.fn();
|
||||
const onSubtaskBreakdown = vi.fn();
|
||||
renderQuickEntryBox({ onPlanningMode, onSubtaskBreakdown });
|
||||
renderQuickEntryBox({ onSubtaskBreakdown });
|
||||
let textarea = screen.getByTestId("quick-entry-input") as HTMLTextAreaElement;
|
||||
|
||||
fireEvent.change(textarea, { target: { value: "Plan this" } });
|
||||
fireEvent.click(screen.getByTestId("plan-button"));
|
||||
await flushPendingTimers();
|
||||
expect(onPlanningMode).toHaveBeenCalledWith("Plan this");
|
||||
expect(document.activeElement).not.toBe(textarea);
|
||||
|
||||
fireEvent.change(textarea, { target: { value: "Break this down" } });
|
||||
fireEvent.click(screen.getByTestId("subtask-button"));
|
||||
await flushPendingTimers();
|
||||
@@ -1067,7 +1059,7 @@ describe("QuickEntryBox", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("does not fire disabled button actions via touch", async () => {
|
||||
it("does not render a disabled Plan button action via touch", async () => {
|
||||
const onPlanningMode = vi.fn();
|
||||
mockMobileViewport();
|
||||
renderQuickEntryBox({ onPlanningMode });
|
||||
@@ -1075,13 +1067,9 @@ describe("QuickEntryBox", () => {
|
||||
const textarea = screen.getByTestId("quick-entry-input") as HTMLTextAreaElement;
|
||||
textarea.focus();
|
||||
expect(document.activeElement).toBe(textarea);
|
||||
const planButton = screen.getByTestId("plan-button");
|
||||
expect(planButton).toBeDisabled();
|
||||
|
||||
const { preventDefaultSpy } = fireCancelableTouchStart(planButton);
|
||||
expect(preventDefaultSpy).not.toHaveBeenCalled();
|
||||
fireEvent(planButton, new Event("touchend", { bubbles: true, cancelable: true }));
|
||||
|
||||
expect(screen.queryByTestId("plan-button")).not.toBeInTheDocument();
|
||||
expect(screen.queryByRole("button", { name: /^Plan$/i })).not.toBeInTheDocument();
|
||||
expect(onPlanningMode).not.toHaveBeenCalled();
|
||||
expect(document.activeElement).toBe(textarea);
|
||||
});
|
||||
@@ -1110,9 +1098,8 @@ describe("QuickEntryBox", () => {
|
||||
vi.mocked(fetchSettings).mockResolvedValueOnce({
|
||||
githubTrackingEnabledByDefault: true,
|
||||
} as any);
|
||||
const onPlanningMode = vi.fn();
|
||||
const onSubtaskBreakdown = vi.fn();
|
||||
const result = renderQuickEntryBox({ onPlanningMode, onSubtaskBreakdown });
|
||||
const result = renderQuickEntryBox({ onSubtaskBreakdown });
|
||||
expandQuickEntry();
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId("quick-entry-github-toggle")).not.toBeDisabled();
|
||||
@@ -1124,7 +1111,7 @@ describe("QuickEntryBox", () => {
|
||||
textarea.blur();
|
||||
fireEvent.blur(textarea);
|
||||
expect(document.activeElement).not.toBe(textarea);
|
||||
return { ...result, textarea, onPlanningMode, onSubtaskBreakdown };
|
||||
return { ...result, textarea, onSubtaskBreakdown };
|
||||
}
|
||||
|
||||
function fireCancelableTouchStart(target: Element) {
|
||||
@@ -1180,9 +1167,6 @@ describe("QuickEntryBox", () => {
|
||||
case "refine-button":
|
||||
expect(await screen.findByTestId("refine-clarify")).toBeTruthy();
|
||||
break;
|
||||
case "plan-button":
|
||||
expect(helpers.onPlanningMode).toHaveBeenCalledWith("Adjust options without keyboard");
|
||||
break;
|
||||
case "subtask-button":
|
||||
expect(helpers.onSubtaskBreakdown).toHaveBeenCalledWith("Adjust options without keyboard");
|
||||
break;
|
||||
@@ -1622,7 +1606,7 @@ describe("QuickEntryBox", () => {
|
||||
{ id: "wf-review-copy", name: "Review", columns: [] },
|
||||
];
|
||||
|
||||
it("defaults to the provided workflow, changes selection, and passes it to Save/Plan/Subtask", async () => {
|
||||
it("defaults to the provided workflow, changes selection, and passes it to Save/Subtask while omitting Plan", async () => {
|
||||
vi.mocked(fetchWorkflowOptionalSteps).mockResolvedValue([]);
|
||||
const onCreate = vi.fn().mockResolvedValue(CREATED_TASK);
|
||||
const onPlanningMode = vi.fn();
|
||||
@@ -1646,9 +1630,8 @@ describe("QuickEntryBox", () => {
|
||||
clickSave();
|
||||
await waitFor(() => expect(onCreate).toHaveBeenCalledWith(expect.objectContaining({ workflowId: "wf-default" })));
|
||||
|
||||
fireEvent.change(screen.getByTestId("quick-entry-input"), { target: { value: "Plan in selected workflow" } });
|
||||
fireEvent.click(screen.getByTestId("plan-button"));
|
||||
expect(onPlanningMode).toHaveBeenCalledWith("Plan in selected workflow", "wf-default");
|
||||
expect(screen.queryByTestId("plan-button")).not.toBeInTheDocument();
|
||||
expect(onPlanningMode).not.toHaveBeenCalled();
|
||||
|
||||
fireEvent.change(screen.getByTestId("quick-entry-input"), { target: { value: "Subtask in selected workflow" } });
|
||||
fireEvent.click(screen.getByTestId("subtask-button"));
|
||||
@@ -1943,19 +1926,17 @@ describe("QuickEntryBox", () => {
|
||||
expect(screen.getByTestId("quick-entry-save")).toBeTruthy();
|
||||
});
|
||||
|
||||
it("shows Plan and Subtask buttons when expanded", () => {
|
||||
it("shows Subtask but no Plan button when expanded", () => {
|
||||
renderQuickEntryBox({});
|
||||
|
||||
// Controls region starts expanded/visible
|
||||
expect(document.getElementById("quick-entry-controls")?.hasAttribute("hidden")).toBe(false);
|
||||
|
||||
// Type something
|
||||
expandQuickEntry();
|
||||
const textarea = screen.getByTestId("quick-entry-input");
|
||||
fireEvent.change(textarea, { target: { value: "Task to plan" } });
|
||||
|
||||
// Now the Plan and Subtask buttons should be visible
|
||||
expect(screen.getByTestId("plan-button")).toBeTruthy();
|
||||
expect(screen.queryByTestId("plan-button")).not.toBeInTheDocument();
|
||||
expect(screen.getByTestId("subtask-button")).toBeTruthy();
|
||||
});
|
||||
|
||||
@@ -2389,24 +2370,6 @@ describe("QuickEntryBox", () => {
|
||||
expect(secondPayload.executionMode).toBeUndefined();
|
||||
});
|
||||
|
||||
it("keeps Fast state after Plan handoff preserves the quick-add draft", async () => {
|
||||
const onPlanningMode = vi.fn();
|
||||
renderQuickEntryBox({ onPlanningMode });
|
||||
|
||||
expandQuickEntry();
|
||||
const textarea = screen.getByTestId("quick-entry-input");
|
||||
fireEvent.click(screen.getByTestId("quick-entry-fast-toggle"));
|
||||
fireEvent.change(textarea, { target: { value: "plan input" } });
|
||||
fireEvent.click(screen.getByTestId("plan-button"));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(onPlanningMode).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
expandQuickEntry();
|
||||
expect(screen.getByTestId("quick-entry-fast-toggle").getAttribute("aria-pressed")).toBe("true");
|
||||
});
|
||||
|
||||
it("clears Fast state after Subtask flow reset", async () => {
|
||||
const onSubtaskBreakdown = vi.fn();
|
||||
renderQuickEntryBox({ onSubtaskBreakdown });
|
||||
@@ -2443,25 +2406,6 @@ describe("QuickEntryBox", () => {
|
||||
expect(screen.getByTestId("quick-entry-priority-button").textContent).toContain("Normal");
|
||||
});
|
||||
|
||||
it("keeps selected priority after Plan handoff preserves the quick-add draft", async () => {
|
||||
const onPlanningMode = vi.fn();
|
||||
renderQuickEntryBox({ onPlanningMode });
|
||||
|
||||
expandQuickEntry();
|
||||
const textarea = screen.getByTestId("quick-entry-input");
|
||||
fireEvent.change(textarea, { target: { value: "plan priority" } });
|
||||
openPriorityMenu();
|
||||
fireEvent.click(screen.getByTestId("quick-entry-priority-option-urgent"));
|
||||
fireEvent.click(screen.getByTestId("plan-button"));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(onPlanningMode).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
expandQuickEntry();
|
||||
expect(screen.getByTestId("quick-entry-priority-button").textContent).toContain("Urgent");
|
||||
});
|
||||
|
||||
it("resets priority to normal after Subtask flow", async () => {
|
||||
const onSubtaskBreakdown = vi.fn();
|
||||
renderQuickEntryBox({ onSubtaskBreakdown });
|
||||
@@ -2795,23 +2739,17 @@ describe("QuickEntryBox", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("renders Plan as text-only and preserves planning handoff", async () => {
|
||||
it("omits the quick-add Plan button while preserving the draft", () => {
|
||||
const onPlanningMode = vi.fn();
|
||||
renderQuickEntryBox({ onPlanningMode });
|
||||
expandQuickEntry();
|
||||
const textarea = screen.getByTestId("quick-entry-input") as HTMLTextAreaElement;
|
||||
const planButton = screen.getByTestId("plan-button");
|
||||
|
||||
expect(planButton).toHaveTextContent("Plan");
|
||||
expect(planButton.querySelector("svg")).toBeNull();
|
||||
|
||||
fireEvent.change(textarea, { target: { value: " Plan this task " } });
|
||||
fireEvent.click(planButton);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(onPlanningMode).toHaveBeenCalledWith("Plan this task");
|
||||
});
|
||||
|
||||
expect(screen.queryByTestId("plan-button")).not.toBeInTheDocument();
|
||||
expect(screen.queryByRole("button", { name: /^Plan$/i })).not.toBeInTheDocument();
|
||||
expect(onPlanningMode).not.toHaveBeenCalled();
|
||||
expect(textarea.value).toBe(" Plan this task ");
|
||||
expect(localStorage.getItem(QUICK_ENTRY_STORAGE_KEY)).toBe(" Plan this task ");
|
||||
});
|
||||
@@ -2833,10 +2771,7 @@ describe("QuickEntryBox", () => {
|
||||
expect((textarea as HTMLTextAreaElement).value).toBe("");
|
||||
});
|
||||
|
||||
it.each([
|
||||
{ label: "Plan", buttonId: "plan-button", callbackProp: "onPlanningMode" as const },
|
||||
{ label: "Subtask", buttonId: "subtask-button", callbackProp: "onSubtaskBreakdown" as const },
|
||||
])("passes selected workflow id through %s quick-entry handoff", async ({ buttonId, callbackProp }) => {
|
||||
it("passes selected workflow id through Subtask quick-entry handoff and omits Plan", async () => {
|
||||
const onPlanningMode = vi.fn();
|
||||
const onSubtaskBreakdown = vi.fn();
|
||||
renderQuickEntryBox({ onPlanningMode, onSubtaskBreakdown, workflowId: "WF-123" });
|
||||
@@ -2844,72 +2779,44 @@ describe("QuickEntryBox", () => {
|
||||
const textarea = screen.getByTestId("quick-entry-input");
|
||||
|
||||
fireEvent.change(textarea, { target: { value: "Create in custom workflow" } });
|
||||
fireEvent.click(screen.getByTestId(buttonId));
|
||||
expect(screen.queryByTestId("plan-button")).not.toBeInTheDocument();
|
||||
fireEvent.click(screen.getByTestId("subtask-button"));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(callbackProp === "onPlanningMode" ? onPlanningMode : onSubtaskBreakdown)
|
||||
.toHaveBeenCalledWith("Create in custom workflow", "WF-123");
|
||||
expect(onSubtaskBreakdown).toHaveBeenCalledWith("Create in custom workflow", "WF-123");
|
||||
});
|
||||
expect(onPlanningMode).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("omits workflow id in legacy quick-entry handoff", async () => {
|
||||
it("omits legacy quick-entry Plan handoff", () => {
|
||||
const onPlanningMode = vi.fn();
|
||||
renderQuickEntryBox({ onPlanningMode });
|
||||
expandQuickEntry();
|
||||
const textarea = screen.getByTestId("quick-entry-input");
|
||||
|
||||
fireEvent.change(textarea, { target: { value: "Create with default workflow" } });
|
||||
fireEvent.click(screen.getByTestId("plan-button"));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(onPlanningMode).toHaveBeenCalledWith("Create with default workflow");
|
||||
});
|
||||
expect(onPlanningMode.mock.calls[0]).toHaveLength(1);
|
||||
expect(screen.queryByTestId("plan-button")).not.toBeInTheDocument();
|
||||
expect(onPlanningMode).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("disables Plan and Subtask buttons when description is empty", () => {
|
||||
it("omits Plan and keeps Subtask disabled when description is empty", () => {
|
||||
renderQuickEntryBox({});
|
||||
expandQuickEntry();
|
||||
const textarea = screen.getByTestId("quick-entry-input");
|
||||
|
||||
// Type something first to make buttons appear
|
||||
fireEvent.change(textarea, { target: { value: "Some task" } });
|
||||
|
||||
const planButton = screen.getByTestId("plan-button") as HTMLButtonElement;
|
||||
expect(screen.queryByTestId("plan-button")).not.toBeInTheDocument();
|
||||
const subtaskButton = screen.getByTestId("subtask-button") as HTMLButtonElement;
|
||||
|
||||
// Buttons should be enabled when there's content
|
||||
expect(planButton.disabled).toBe(false);
|
||||
expect(subtaskButton.disabled).toBe(false);
|
||||
|
||||
// Clear the input
|
||||
fireEvent.change(textarea, { target: { value: "" } });
|
||||
|
||||
// Buttons should now be disabled (or hidden since controls collapse)
|
||||
// Since the controls might hide when empty, we check if they exist and are disabled
|
||||
const updatedPlanButton = screen.queryByTestId("plan-button") as HTMLButtonElement | null;
|
||||
if (updatedPlanButton) {
|
||||
expect(updatedPlanButton.disabled).toBe(true);
|
||||
}
|
||||
expect(screen.queryByTestId("plan-button")).not.toBeInTheDocument();
|
||||
expect((screen.getByTestId("subtask-button") as HTMLButtonElement).disabled).toBe(true);
|
||||
});
|
||||
|
||||
it("Plan button prevents textarea blur on mousedown", () => {
|
||||
renderQuickEntryBox({});
|
||||
expandQuickEntry();
|
||||
const textarea = screen.getByTestId("quick-entry-input");
|
||||
|
||||
fireEvent.change(textarea, { target: { value: "Task to plan" } });
|
||||
|
||||
// Get plan button and trigger mousedown (prevents blur)
|
||||
const planButton = screen.getByTestId("plan-button");
|
||||
fireEvent.mouseDown(planButton);
|
||||
|
||||
// Trigger blur on textarea
|
||||
fireEvent.blur(textarea);
|
||||
|
||||
// Controls should still be visible immediately after blur
|
||||
expect(screen.getByTestId("plan-button")).toBeTruthy();
|
||||
});
|
||||
|
||||
it("Subtask button prevents textarea blur on mousedown", () => {
|
||||
renderQuickEntryBox({});
|
||||
@@ -2929,24 +2836,18 @@ describe("QuickEntryBox", () => {
|
||||
expect(screen.getByTestId("subtask-button")).toBeTruthy();
|
||||
});
|
||||
|
||||
it("shows toast when Plan clicked with empty description", () => {
|
||||
it("does not leave a Plan tooltip or click target when description is empty", () => {
|
||||
const addToast = vi.fn();
|
||||
renderQuickEntryBox({ addToast });
|
||||
expandQuickEntry();
|
||||
const textarea = screen.getByTestId("quick-entry-input");
|
||||
|
||||
// Type something first to make buttons appear
|
||||
fireEvent.change(textarea, { target: { value: "Some task" } });
|
||||
|
||||
// Clear input
|
||||
fireEvent.change(textarea, { target: { value: "" } });
|
||||
|
||||
// Button should be hidden when input is empty (controls collapse)
|
||||
const planButton = screen.queryByTestId("plan-button");
|
||||
if (planButton) {
|
||||
// If somehow visible, it should be disabled
|
||||
expect((planButton as HTMLButtonElement).disabled).toBe(true);
|
||||
}
|
||||
expect(screen.queryByTestId("plan-button")).not.toBeInTheDocument();
|
||||
expect(screen.queryByTitle("Open planning mode with current description")).not.toBeInTheDocument();
|
||||
expect(addToast).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("includes all three selected model pairs in submit payload", async () => {
|
||||
@@ -3823,7 +3724,7 @@ describe("QuickEntryBox", () => {
|
||||
expandQuickEntry();
|
||||
|
||||
expect(document.getElementById("quick-entry-controls")?.hasAttribute("hidden")).toBe(false);
|
||||
expect(screen.getByTestId("plan-button")).toBeTruthy();
|
||||
expect(screen.queryByTestId("plan-button")).not.toBeInTheDocument();
|
||||
expect(screen.getByTestId("subtask-button")).toBeTruthy();
|
||||
expect(screen.getByTestId("refine-button")).toBeTruthy();
|
||||
expect(screen.getByTestId("quick-entry-deps")).toBeTruthy();
|
||||
@@ -3844,14 +3745,14 @@ describe("QuickEntryBox", () => {
|
||||
});
|
||||
|
||||
describe("Consolidated actions layout (FN-781, FN-1088)", () => {
|
||||
it("renders Plan, Subtask, and Refine in actions area inside controls panel", () => {
|
||||
it("renders Subtask and Refine in actions area without a Plan shell", () => {
|
||||
renderQuickEntryBox({});
|
||||
expandQuickEntry();
|
||||
|
||||
expect(screen.getByTestId("quick-entry-actions")).toBeTruthy();
|
||||
|
||||
const actionsContainer = screen.getByTestId("quick-entry-actions");
|
||||
expect(actionsContainer.contains(screen.getByTestId("plan-button"))).toBe(true);
|
||||
expect(screen.queryByTestId("plan-button")).not.toBeInTheDocument();
|
||||
expect(actionsContainer.contains(screen.getByTestId("subtask-button"))).toBe(true);
|
||||
expect(actionsContainer.contains(screen.getByTestId("refine-button"))).toBe(true);
|
||||
});
|
||||
@@ -3863,7 +3764,7 @@ describe("QuickEntryBox", () => {
|
||||
const actionsContainer = screen.getByTestId("quick-entry-actions");
|
||||
expect(screen.queryByTestId("subtask-button")).not.toBeInTheDocument();
|
||||
expect(screen.queryByTitle("Break down into AI-generated subtasks")).not.toBeInTheDocument();
|
||||
expect(actionsContainer.contains(screen.getByTestId("plan-button"))).toBe(true);
|
||||
expect(screen.queryByTestId("plan-button")).not.toBeInTheDocument();
|
||||
expect(actionsContainer.contains(screen.getByTestId("refine-button"))).toBe(true);
|
||||
});
|
||||
|
||||
@@ -3884,17 +3785,6 @@ describe("QuickEntryBox", () => {
|
||||
expect(screen.getByTestId("quick-entry-save")).toBeTruthy();
|
||||
});
|
||||
|
||||
it("Plan button disabled state still works in actions area", () => {
|
||||
renderQuickEntryBox({});
|
||||
expandQuickEntry();
|
||||
const textarea = screen.getByTestId("quick-entry-input");
|
||||
|
||||
expect((screen.getByTestId("plan-button") as HTMLButtonElement).disabled).toBe(true);
|
||||
|
||||
fireEvent.change(textarea, { target: { value: "Some task" } });
|
||||
expect((screen.getByTestId("plan-button") as HTMLButtonElement).disabled).toBe(false);
|
||||
});
|
||||
|
||||
it("keeps all task creation controls together when disclosure is expanded", () => {
|
||||
renderQuickEntryBox({});
|
||||
expandQuickEntry();
|
||||
@@ -3904,14 +3794,14 @@ describe("QuickEntryBox", () => {
|
||||
const controlsPanel = document.getElementById("quick-entry-controls");
|
||||
expect(controlsPanel?.hasAttribute("hidden")).toBe(false);
|
||||
|
||||
expect(screen.getByTestId("plan-button")).toBeTruthy();
|
||||
expect(screen.queryByTestId("plan-button")).not.toBeInTheDocument();
|
||||
expect(screen.getByTestId("subtask-button")).toBeTruthy();
|
||||
expect(screen.getByTestId("refine-button")).toBeTruthy();
|
||||
expect(screen.getByTestId("quick-entry-deps")).toBeTruthy();
|
||||
expect(screen.getByTestId("quick-entry-models")).toBeTruthy();
|
||||
expect(screen.getByTestId("quick-entry-save")).toBeTruthy();
|
||||
|
||||
expect(controlsPanel?.contains(screen.getByTestId("plan-button"))).toBe(true);
|
||||
expect(screen.queryByTestId("plan-button")).not.toBeInTheDocument();
|
||||
expect(controlsPanel?.contains(screen.getByTestId("subtask-button"))).toBe(true);
|
||||
expect(controlsPanel?.contains(screen.getByTestId("refine-button"))).toBe(true);
|
||||
expect(controlsPanel?.contains(screen.getByTestId("quick-entry-deps"))).toBe(true);
|
||||
@@ -4261,13 +4151,13 @@ describe("QuickEntryBox", () => {
|
||||
expect(modelsButton.className).toContain("btn");
|
||||
});
|
||||
|
||||
it("keeps Plan, Subtask, and Refine buttons in touch-target button classes", () => {
|
||||
it("keeps Subtask and Refine buttons in touch-target classes without Plan", () => {
|
||||
vi.spyOn(window, "innerWidth", "get").mockReturnValue(375);
|
||||
|
||||
renderQuickEntryBox({});
|
||||
expandQuickEntry();
|
||||
|
||||
expect(screen.getByRole("button", { name: /Plan/i }).className).toContain("btn");
|
||||
expect(screen.queryByRole("button", { name: /^Plan$/i })).not.toBeInTheDocument();
|
||||
expect(screen.getByRole("button", { name: /Subtask/i }).className).toContain("btn");
|
||||
expect(screen.getByRole("button", { name: /Refine/i }).className).toContain("btn");
|
||||
});
|
||||
@@ -4304,7 +4194,7 @@ describe("QuickEntryBox", () => {
|
||||
expect(rect.right).toBeLessThanOrEqual(viewportWidth);
|
||||
});
|
||||
|
||||
it("responds to clicks for toggle, plan, subtask, refine, and deps", async () => {
|
||||
it("responds to clicks for toggle, subtask, refine, and deps without Plan", async () => {
|
||||
const onPlanningMode = vi.fn();
|
||||
const onSubtaskBreakdown = vi.fn();
|
||||
|
||||
@@ -4322,8 +4212,8 @@ describe("QuickEntryBox", () => {
|
||||
expect(toggle).toHaveAttribute("aria-expanded", "true");
|
||||
|
||||
fireEvent.change(input, { target: { value: "Mobile interaction task" } });
|
||||
fireEvent.click(screen.getByRole("button", { name: /Plan/i }));
|
||||
expect(onPlanningMode).toHaveBeenCalledWith("Mobile interaction task");
|
||||
expect(screen.queryByRole("button", { name: /^Plan$/i })).not.toBeInTheDocument();
|
||||
expect(onPlanningMode).not.toHaveBeenCalled();
|
||||
|
||||
ensureExpanded();
|
||||
fireEvent.change(input, { target: { value: "Break this down" } });
|
||||
|
||||
@@ -607,7 +607,7 @@ describe("InlineCreateCard mobile", () => {
|
||||
expectRuleToContain(mobileSection, ".inline-create-priority-select", "min-height: 36px;");
|
||||
});
|
||||
|
||||
it("renders Plan and Subtask buttons when expanded", () => {
|
||||
it("renders Subtask but no Plan button when expanded", () => {
|
||||
render(
|
||||
<InlineCreateCard
|
||||
tasks={[]}
|
||||
@@ -615,13 +615,15 @@ describe("InlineCreateCard mobile", () => {
|
||||
onCancel={vi.fn()}
|
||||
addToast={vi.fn()}
|
||||
availableModels={[]}
|
||||
onPlanningMode={vi.fn()}
|
||||
onSubtaskBreakdown={vi.fn()}
|
||||
/>,
|
||||
);
|
||||
|
||||
fireEvent.click(screen.getByTestId("inline-create-toggle"));
|
||||
|
||||
expect(screen.getByRole("button", { name: "Plan" })).toBeTruthy();
|
||||
expect(screen.queryByTestId("plan-button")).not.toBeInTheDocument();
|
||||
expect(screen.queryByRole("button", { name: "Plan" })).not.toBeInTheDocument();
|
||||
expect(screen.getByRole("button", { name: "Subtask" })).toBeTruthy();
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user