FN-6940: expose New Task quick-add actions
Expose the regular New Task dialog's primary creation and quick-add controls in one visible action row. - Move the Create Task submit affordance into TaskForm's create-mode action cluster. - Promote GitHub tracking, workflow, model, node, fast mode, attach, priority, plan, and subtask controls as inline quick-add buttons. - Add responsive spacing and mobile touch-target coverage for the expanded action row. - Extend NewTaskModal and TaskForm tests around the promoted controls and submission flow. Files changed: packages/dashboard/app/components/NewTaskModal.css | 12 ++ packages/dashboard/app/components/NewTaskModal.tsx | 10 +- packages/dashboard/app/components/TaskForm.tsx | 110 +++++++++++++++- .../app/components/__tests__/NewTaskModal.test.tsx | 139 ++++++++++++++++++--- .../app/components/__tests__/TaskForm.test.tsx | 3 + .../__tests__/core-modals-mobile.test.tsx | 8 +- 6 files changed, 250 insertions(+), 32 deletions(-) Fusion-Task-Id: FN-6940 Fusion-Task-Lineage: 8329c432-5e01-40aa-a3da-c13754a74393
This commit is contained in:
@@ -187,6 +187,13 @@ Edge + corner resize handles. touch-action:none keeps the drag from being hijack
|
||||
|
||||
.task-form-description-actions .btn {
|
||||
border-color: var(--border);
|
||||
align-items: center;
|
||||
gap: var(--space-xs);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.task-form-action-icon {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.task-form-more-options-toggle {
|
||||
@@ -595,6 +602,11 @@ Edge + corner resize handles. touch-action:none keeps the drag from being hijack
|
||||
gap: var(--space-xs);
|
||||
}
|
||||
|
||||
.task-form-description-actions .btn {
|
||||
min-height: 36px;
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
.task-form-more-options-toggle {
|
||||
margin: 0 var(--space-md) var(--space-sm);
|
||||
width: calc(100% - var(--space-md) * 2);
|
||||
|
||||
@@ -886,6 +886,9 @@ export function NewTaskModal({ isOpen, onClose, projectId, tasks, onCreateTask,
|
||||
onGithubTrackingEnabledChange={setGithubTrackingEnabled}
|
||||
githubRepoOverride={githubRepoOverride}
|
||||
onGithubRepoOverrideChange={setGithubRepoOverride}
|
||||
onCreateSubmit={handleSubmit}
|
||||
createSubmitLabel={isSubmitting ? t("newTaskModal.creating", "Creating...") : t("newTaskModal.createTask", "Create Task")}
|
||||
createSubmitDisabled={!description.trim() || isSubmitting || githubRepoOverrideInvalid || hasInvalidBranchSelection}
|
||||
renderBelowPrimary={quickFields}
|
||||
hideDependencies={true}
|
||||
autoExpandMoreOptionsOnSelection={false}
|
||||
@@ -901,13 +904,6 @@ export function NewTaskModal({ isOpen, onClose, projectId, tasks, onCreateTask,
|
||||
<button className="btn btn-sm" onClick={handleClose} disabled={isSubmitting}>
|
||||
{t("actions.cancel", "Cancel")}
|
||||
</button>
|
||||
<button
|
||||
className="btn btn-primary btn-sm"
|
||||
onClick={handleSubmit}
|
||||
disabled={!description.trim() || isSubmitting || githubRepoOverrideInvalid || hasInvalidBranchSelection}
|
||||
>
|
||||
{isSubmitting ? t("newTaskModal.creating", "Creating...") : t("newTaskModal.createTask", "Create Task")}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -8,8 +8,9 @@ import { applyPresetToSelection, getRecommendedPresetForSize } from "../utils/mo
|
||||
import { CustomModelDropdown } from "./CustomModelDropdown";
|
||||
import { NodeHealthDot } from "./NodeHealthDot";
|
||||
import { LoadingSpinner } from "./LoadingSpinner";
|
||||
import { Sparkles, ChevronUp, ChevronDown, Maximize2, Minimize2, Paperclip, Flag, Zap } from "lucide-react";
|
||||
import { Sparkles, ChevronUp, ChevronDown, Maximize2, Minimize2, Paperclip, Flag, Zap, Brain, Server } from "lucide-react";
|
||||
import { REPO_OVERRIDE_RE, resolveEffectiveGithubRepoDefault } from "./githubTracking";
|
||||
import { ProviderIcon } from "./ProviderIcon";
|
||||
|
||||
function getNodeStatusLabel(status: NodeInfo["status"], t: (key: string, defaultValue: string) => string): string {
|
||||
if (status === "online") return t("taskForm.nodeStatusOnline", "Online");
|
||||
@@ -138,6 +139,12 @@ export interface TaskFormProps {
|
||||
onSubtaskBreakdown?: (description: string, workflowId?: string | null) => void;
|
||||
onClose?: () => void;
|
||||
|
||||
// Create-mode primary submission. NewTaskModal owns duplicate checks and payload shaping;
|
||||
// TaskForm only places the visible Create affordance in the quick-action row.
|
||||
onCreateSubmit?: () => void;
|
||||
createSubmitLabel?: string;
|
||||
createSubmitDisabled?: boolean;
|
||||
|
||||
/** Optional content to render between the primary section and the "More options" toggle. */
|
||||
renderBelowPrimary?: React.ReactNode;
|
||||
/** Optional content to render inside "More options" below Model Configuration. */
|
||||
@@ -204,6 +211,9 @@ export function TaskForm({
|
||||
onPlanningMode,
|
||||
onSubtaskBreakdown,
|
||||
onClose,
|
||||
onCreateSubmit,
|
||||
createSubmitLabel,
|
||||
createSubmitDisabled,
|
||||
renderBelowPrimary,
|
||||
renderBelowModelConfiguration,
|
||||
hideDependencies,
|
||||
@@ -697,6 +707,22 @@ export function TaskForm({
|
||||
|
||||
// U6/R3: the project default workflow id (preselected + "(default)" badged).
|
||||
const defaultWorkflowId = settings?.defaultWorkflowId ?? null;
|
||||
const selectedWorkflow = selectedWorkflowId === null
|
||||
? null
|
||||
: workflows.find((workflow) => workflow.id === (selectedWorkflowId ?? defaultWorkflowId));
|
||||
const workflowInlineLabel = selectedWorkflowId === null
|
||||
? t("taskForm.workflowNone", "No workflow")
|
||||
: selectedWorkflow?.name ?? t("taskForm.workflowInlineDefault", "Normal");
|
||||
const selectedNode = (nodeOptions ?? []).find((node) => node.id === nodeId);
|
||||
const nodeInlineLabel = selectedNode?.name ?? t("taskForm.nodeInlineDefault", "Node");
|
||||
const modelInlineLabel = selectedPreset?.name ?? (presetMode === "custom" ? t("taskForm.modelsCustom", "Models") : t("taskForm.modelsDefault", "Models"));
|
||||
|
||||
const revealAdvancedControl = useCallback((selector: string) => {
|
||||
if (!forceMoreOptionsOpen) setShowMoreOptions(true);
|
||||
window.setTimeout(() => {
|
||||
document.querySelector<HTMLElement>(selector)?.focus();
|
||||
}, 0);
|
||||
}, [forceMoreOptionsOpen]);
|
||||
|
||||
const availableDeps = tasks
|
||||
.filter((t) => !dependencies.includes(t.id))
|
||||
@@ -847,9 +873,23 @@ export function TaskForm({
|
||||
- Fast → toggles executionMode standard⇄fast via onExecutionModeChange (mirrors QuickEntryBox quick-entry-fast-toggle).
|
||||
- Priority → cycles through TASK_PRIORITIES via onPriorityChange (Flag affordance).
|
||||
Plan/Subtask remain gated on their handoff callbacks. Model selectors, branch/base, node, review level, and GitHub tracking stay in the Advanced disclosure.
|
||||
|
||||
FNXC:NewTaskDialogAffordances 2026-06-23-21:20:
|
||||
The regular New Task dialog must visibly expose the screenshot quick-add button contract in the immediate action cluster while Advanced remains the deep configuration editor. TaskForm hosts the cluster so create payload state has one source of truth; NewTaskModal only supplies the submit handler and its existing dependency/agent quick controls.
|
||||
*/}
|
||||
{mode === "create" && (
|
||||
<div className="task-form-description-actions" data-testid="task-form-description-actions">
|
||||
{onCreateSubmit && (
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-primary btn-sm"
|
||||
onClick={onCreateSubmit}
|
||||
disabled={disabled || createSubmitDisabled}
|
||||
data-testid="task-form-inline-create"
|
||||
>
|
||||
{createSubmitLabel ?? t("taskForm.createTask", "Create")}
|
||||
</button>
|
||||
)}
|
||||
{onPlanningMode && (
|
||||
<button
|
||||
type="button"
|
||||
@@ -898,7 +938,7 @@ export function TaskForm({
|
||||
data-testid="task-form-inline-attach"
|
||||
title={t("taskForm.attachScreenshot", "Attach Screenshot")}
|
||||
>
|
||||
<Paperclip size={12} style={{ verticalAlign: "middle", marginRight: 4 }} />
|
||||
<Paperclip size={12} className="task-form-action-icon" />
|
||||
{pendingImages.length > 0
|
||||
? t("taskForm.attachCount", "Attach ({{count}})", { count: pendingImages.length })
|
||||
: t("taskForm.attach", "Attach")}
|
||||
@@ -915,11 +955,72 @@ export function TaskForm({
|
||||
data-testid="task-form-inline-fast"
|
||||
title={t("taskForm.toggleFastMode", "Toggle fast execution mode")}
|
||||
>
|
||||
<Zap size={12} style={{ verticalAlign: "middle", marginRight: 4 }} />
|
||||
<Zap size={12} className="task-form-action-icon" />
|
||||
{t("taskForm.fast", "Fast")}
|
||||
</button>
|
||||
)}
|
||||
|
||||
{/* FNXC:NewTaskDialogAffordances 2026-06-23-21:31: GitHub/workflow/models/node are promoted as visible chips that mutate or focus the same Advanced controls instead of duplicating create-payload state. */}
|
||||
{onGithubTrackingEnabledChange && (
|
||||
<button
|
||||
type="button"
|
||||
className={`btn btn-sm ${githubTrackingEnabled ? "btn-primary" : ""}`}
|
||||
onClick={() => {
|
||||
githubTrackingDefaultAppliedRef.current = true;
|
||||
onGithubTrackingEnabledChange(!githubTrackingEnabled);
|
||||
}}
|
||||
aria-pressed={githubTrackingEnabled === true}
|
||||
disabled={disabled}
|
||||
data-testid="task-form-inline-github"
|
||||
title={t("taskForm.githubTrackingLabel", "GitHub Tracking")}
|
||||
>
|
||||
<ProviderIcon provider="github" size="sm" />
|
||||
{t("taskForm.githubInline", "GitHub")}
|
||||
</button>
|
||||
)}
|
||||
|
||||
{onWorkflowIdChange && (
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-sm"
|
||||
onClick={() => revealAdvancedControl("#task-workflow-select, [data-testid='task-workflow-cta']")}
|
||||
disabled={disabled}
|
||||
data-testid="task-form-inline-workflow"
|
||||
aria-label={t("taskForm.workflowInlineAria", "Choose workflow: {{workflow}}", { workflow: workflowInlineLabel })}
|
||||
title={t("taskForm.workflowLabel", "Workflow")}
|
||||
>
|
||||
{workflowInlineLabel}
|
||||
</button>
|
||||
)}
|
||||
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-sm"
|
||||
onClick={() => revealAdvancedControl("#model-preset, #executor-model")}
|
||||
disabled={disabled}
|
||||
data-testid="task-form-inline-models"
|
||||
aria-label={t("taskForm.modelsInlineAria", "Choose models: {{models}}", { models: modelInlineLabel })}
|
||||
title={t("taskForm.modelConfigLabel", "Model Configuration")}
|
||||
>
|
||||
<Brain size={12} className="task-form-action-icon" />
|
||||
{modelInlineLabel}
|
||||
</button>
|
||||
|
||||
{onNodeIdChange && (
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-sm"
|
||||
onClick={() => revealAdvancedControl("#task-node-select")}
|
||||
disabled={disabled || nodeOverrideDisabled}
|
||||
data-testid="task-form-inline-node"
|
||||
aria-label={t("taskForm.nodeInlineAria", "Choose execution node: {{node}}", { node: nodeInlineLabel })}
|
||||
title={nodeOverrideDisabled ? nodeOverrideDisabledReason : t("taskForm.nodeOverrideLabel", "Execution Node Override")}
|
||||
>
|
||||
{selectedNode ? <NodeHealthDot status={selectedNode.status} compact className="task-form-action-icon" /> : <Server size={12} className="task-form-action-icon" />}
|
||||
{nodeInlineLabel}
|
||||
</button>
|
||||
)}
|
||||
|
||||
{/* FNXC:NewTask 2026-06-23-00:10: Priority — cycles TASK_PRIORITIES via onPriorityChange (Flag affordance, same label shape as QuickEntryBox). */}
|
||||
{onPriorityChange && (
|
||||
<button
|
||||
@@ -935,7 +1036,7 @@ export function TaskForm({
|
||||
data-testid="task-form-inline-priority"
|
||||
title={t("taskForm.priorityLabel", "Priority")}
|
||||
>
|
||||
<Flag size={12} style={{ verticalAlign: "middle", marginRight: 4 }} />
|
||||
<Flag size={12} className="task-form-action-icon" />
|
||||
{(() => {
|
||||
const p = priority ?? DEFAULT_TASK_PRIORITY;
|
||||
return `${p[0].toUpperCase()}${p.slice(1)}`;
|
||||
@@ -1027,6 +1128,7 @@ export function TaskForm({
|
||||
<label htmlFor="task-node-select">{t("taskForm.nodeOverrideLabel", "Execution Node Override")}</label>
|
||||
<select
|
||||
id="task-node-select"
|
||||
data-testid="task-node-select"
|
||||
className="select"
|
||||
value={nodeId ?? ""}
|
||||
onChange={(e) => onNodeIdChange(e.target.value || undefined)}
|
||||
|
||||
@@ -24,6 +24,13 @@ vi.mock("lucide-react", () => ({
|
||||
Paperclip: () => null,
|
||||
Flag: () => null,
|
||||
Zap: () => null,
|
||||
Brain: () => null,
|
||||
Server: () => null,
|
||||
Cpu: () => null,
|
||||
}));
|
||||
|
||||
vi.mock("../ProviderIcon", () => ({
|
||||
ProviderIcon: ({ provider }: { provider: string }) => <span data-testid={`provider-icon-${provider}`} />,
|
||||
}));
|
||||
|
||||
// Mock the api module
|
||||
@@ -157,10 +164,15 @@ describe("NewTaskModal", () => {
|
||||
expect(screen.getByTestId("dep-trigger")).toBeInTheDocument();
|
||||
expect(screen.getByTestId("new-task-agent-button")).toBeInTheDocument();
|
||||
|
||||
// FNXC:NewTask 2026-06-23-00:10: The common quick-add buttons (Attach, Fast, Priority) are surfaced INLINE next to the actions row and visible immediately.
|
||||
expect(screen.getByTestId("task-form-inline-attach")).toBeInTheDocument();
|
||||
expect(screen.getByTestId("task-form-inline-fast")).toBeInTheDocument();
|
||||
expect(screen.getByTestId("task-form-inline-priority")).toBeInTheDocument();
|
||||
// FNXC:NewTaskDialogAffordances 2026-06-23-21:47: The regular New Task dialog exposes the screenshot quick-add buttons immediately; detailed selects stay in Advanced.
|
||||
expect(screen.getByTestId("task-form-inline-create")).toBeVisible();
|
||||
expect(screen.getByTestId("task-form-inline-attach")).toBeVisible();
|
||||
expect(screen.getByTestId("task-form-inline-fast")).toBeVisible();
|
||||
expect(screen.getByTestId("task-form-inline-github")).toBeVisible();
|
||||
expect(screen.getByTestId("task-form-inline-workflow")).toBeVisible();
|
||||
expect(screen.getByTestId("task-form-inline-models")).toBeVisible();
|
||||
expect(screen.getByTestId("task-form-inline-node")).toBeVisible();
|
||||
expect(screen.getByTestId("task-form-inline-priority")).toBeVisible();
|
||||
|
||||
// FNXC:NewTask 2026-06-23-00:10: The DEEP/advanced options now sit behind the collapsed "Advanced" disclosure. Model Configuration / Attachments are NOT shown until the toggle is expanded.
|
||||
const advancedToggle = screen.getByTestId("task-form-more-options-toggle");
|
||||
@@ -182,29 +194,54 @@ describe("NewTaskModal", () => {
|
||||
onSubtaskBreakdown: vi.fn(),
|
||||
});
|
||||
|
||||
const advancedSection = screen.getByTestId("task-form-more-options");
|
||||
expect(advancedSection).toHaveAttribute("hidden");
|
||||
|
||||
// Empty description state: description-gated actions are disabled/absent, while configuration chips stay immediately usable.
|
||||
expect(screen.getByTestId("task-form-inline-create")).toBeDisabled();
|
||||
expect(screen.getByTestId("task-form-plan-button")).toBeDisabled();
|
||||
expect(screen.queryByTestId("refine-button")).toBeNull();
|
||||
expect(screen.getByTestId("task-form-inline-fast")).toBeVisible();
|
||||
expect(screen.getByTestId("task-form-inline-github")).toBeVisible();
|
||||
expect(screen.getByTestId("task-form-inline-workflow")).toBeVisible();
|
||||
expect(screen.getByTestId("task-form-inline-models")).toBeVisible();
|
||||
expect(screen.getByTestId("task-form-inline-node")).toBeVisible();
|
||||
expect(screen.getByTestId("dep-trigger")).toBeVisible();
|
||||
expect(screen.getByTestId("task-form-inline-attach")).toBeVisible();
|
||||
expect(screen.getByTestId("new-task-agent-button")).toBeVisible();
|
||||
|
||||
fireEvent.change(screen.getByPlaceholderText("What needs to be done?"), { target: { value: "Create parity coverage" } });
|
||||
|
||||
// Canonical QuickEntryBox action row includes Plan, Subtask, Refine, Deps, Attach, Models, Node, and Agent affordances; the modal maps these to existing TaskForm/quick-field controls instead of duplicating implementations.
|
||||
// Populated description state: the complete screenshot affordance set is visible without opening Advanced.
|
||||
expect(screen.getByTestId("task-form-inline-create")).toBeEnabled();
|
||||
expect(screen.getAllByTestId("task-form-plan-button")).toHaveLength(1);
|
||||
expect(screen.getAllByTestId("task-form-subtask-button")).toHaveLength(1);
|
||||
expect(screen.getByTestId("refine-button")).toBeInTheDocument();
|
||||
expect(screen.getByTestId("dep-trigger")).toBeInTheDocument();
|
||||
expect(screen.getByTestId("new-task-agent-button")).toBeInTheDocument();
|
||||
expect(screen.getByTestId("task-form-plan-button")).toBeEnabled();
|
||||
expect(screen.getByTestId("refine-button")).toBeVisible();
|
||||
expect(screen.getByTestId("dep-trigger")).toBeVisible();
|
||||
expect(screen.getByTestId("new-task-agent-button")).toBeVisible();
|
||||
expect(screen.getByTestId("task-form-inline-attach")).toBeVisible();
|
||||
expect(screen.getByTestId("task-form-inline-fast")).toBeVisible();
|
||||
expect(screen.getByTestId("task-form-inline-github")).toBeVisible();
|
||||
expect(screen.getByTestId("task-form-inline-workflow")).toBeVisible();
|
||||
expect(screen.getByTestId("task-form-inline-models")).toBeVisible();
|
||||
expect(screen.getByTestId("task-form-inline-node")).toBeVisible();
|
||||
expect(screen.getByTestId("task-form-inline-priority")).toBeVisible();
|
||||
|
||||
|
||||
expect(screen.getByTestId("task-form-execution-mode-select")).toBeInTheDocument();
|
||||
expect(screen.getByTestId("task-form-github-tracking")).toBeInTheDocument();
|
||||
expect(screen.getByTestId("task-priority-select")).toBeInTheDocument();
|
||||
expect(screen.getByText(/Attachments/i)).toBeInTheDocument();
|
||||
expect(screen.getByText(/Node Override/i)).toBeInTheDocument();
|
||||
// Detailed editors remain present only inside Advanced, not duplicated as visible siblings.
|
||||
expect(advancedSection).toContainElement(screen.getByTestId("task-form-execution-mode-select"));
|
||||
expect(advancedSection).toContainElement(screen.getByTestId("task-form-github-tracking"));
|
||||
expect(advancedSection).toContainElement(screen.getByTestId("task-priority-select"));
|
||||
expect(advancedSection).toContainElement(screen.getByTestId("task-node-select"));
|
||||
expect(advancedSection).toHaveAttribute("hidden");
|
||||
});
|
||||
|
||||
it("renders the Fast and standard execution-mode affordance inside More options", () => {
|
||||
it("keeps the detailed Fast/standard execution-mode select inside Advanced", () => {
|
||||
renderNewTaskModal();
|
||||
|
||||
|
||||
const advancedSection = screen.getByTestId("task-form-more-options");
|
||||
const select = screen.getByTestId("task-form-execution-mode-select") as HTMLSelectElement;
|
||||
expect(select).toBeInTheDocument();
|
||||
expect(advancedSection).toContainElement(select);
|
||||
expect(advancedSection).toHaveAttribute("hidden");
|
||||
expect(select).toHaveValue("standard");
|
||||
expect(Array.from(select.options).map((option) => option.value)).toEqual(["standard", "fast"]);
|
||||
});
|
||||
@@ -212,7 +249,7 @@ describe("NewTaskModal", () => {
|
||||
it("includes executionMode fast in the create payload when Fast is selected", async () => {
|
||||
const { props } = renderNewTaskModal();
|
||||
|
||||
fireEvent.change(screen.getByTestId("task-form-execution-mode-select"), { target: { value: "fast" } });
|
||||
fireEvent.click(screen.getByTestId("task-form-inline-fast"));
|
||||
fireEvent.change(screen.getByPlaceholderText("What needs to be done?"), { target: { value: "Fast parity task" } });
|
||||
fireEvent.click(screen.getByRole("button", { name: "Create Task" }));
|
||||
|
||||
@@ -226,6 +263,68 @@ describe("NewTaskModal", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("promoted GitHub, workflow, model, node, deps, agent, attach, and create controls are functional", async () => {
|
||||
const { fetchWorkflows } = await import("../../api");
|
||||
vi.mocked(fetchWorkflows).mockResolvedValueOnce([
|
||||
{
|
||||
id: "WF-quick",
|
||||
name: "Quick Lane",
|
||||
description: "",
|
||||
kind: "workflow",
|
||||
ir: { version: "v1", name: "Quick Lane", nodes: [], edges: [] },
|
||||
layout: {},
|
||||
createdAt: "",
|
||||
updatedAt: "",
|
||||
} as any,
|
||||
]);
|
||||
const clickSpy = vi.spyOn(HTMLInputElement.prototype, "click").mockImplementation(() => undefined);
|
||||
const { props } = renderNewTaskModal({ tasks: [makeTask("FN-777")] });
|
||||
|
||||
fireEvent.click(screen.getByTestId("task-form-inline-github"));
|
||||
expect(screen.getByTestId("task-form-inline-github")).toHaveAttribute("aria-pressed", "true");
|
||||
|
||||
fireEvent.click(screen.getByTestId("task-form-inline-fast"));
|
||||
expect(screen.getByTestId("task-form-inline-fast")).toHaveAttribute("aria-pressed", "true");
|
||||
|
||||
fireEvent.click(screen.getByTestId("task-form-inline-attach"));
|
||||
expect(clickSpy).toHaveBeenCalled();
|
||||
|
||||
fireEvent.click(screen.getByTestId("dep-trigger"));
|
||||
expect(screen.getByPlaceholderText("Search tasks…")).toBeInTheDocument();
|
||||
|
||||
fireEvent.click(screen.getByTestId("new-task-agent-button"));
|
||||
await waitFor(() => expect(screen.getByText("No agents available")).toBeInTheDocument());
|
||||
|
||||
fireEvent.click(screen.getByTestId("task-form-inline-workflow"));
|
||||
await waitFor(() => expect(screen.getByTestId("task-form-more-options")).not.toHaveAttribute("hidden"));
|
||||
expect(await screen.findByTestId("task-workflow-select")).toBeInTheDocument();
|
||||
|
||||
fireEvent.click(screen.getByTestId("task-form-more-options-toggle"));
|
||||
expect(screen.getByTestId("task-form-more-options")).toHaveAttribute("hidden");
|
||||
fireEvent.click(screen.getByTestId("task-form-inline-models"));
|
||||
await waitFor(() => expect(screen.getByTestId("task-form-more-options")).not.toHaveAttribute("hidden"));
|
||||
expect(screen.getByText(/Model Configuration/i)).toBeInTheDocument();
|
||||
|
||||
fireEvent.click(screen.getByTestId("task-form-more-options-toggle"));
|
||||
expect(screen.getByTestId("task-form-more-options")).toHaveAttribute("hidden");
|
||||
fireEvent.click(screen.getByTestId("task-form-inline-node"));
|
||||
await waitFor(() => expect(screen.getByTestId("task-form-more-options")).not.toHaveAttribute("hidden"));
|
||||
expect(screen.getByTestId("task-node-select")).toBeInTheDocument();
|
||||
|
||||
fireEvent.change(screen.getByPlaceholderText("What needs to be done?"), { target: { value: "Promoted controls create task" } });
|
||||
fireEvent.click(screen.getByRole("button", { name: "Create Task" }));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(props.onCreateTask).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
executionMode: "fast",
|
||||
githubTracking: { enabled: true },
|
||||
}),
|
||||
);
|
||||
});
|
||||
clickSpy.mockRestore();
|
||||
});
|
||||
|
||||
it("omits executionMode from the create payload when Standard is selected", async () => {
|
||||
const { props } = renderNewTaskModal();
|
||||
|
||||
|
||||
@@ -15,6 +15,9 @@ vi.mock("lucide-react", () => ({
|
||||
Paperclip: () => null,
|
||||
Flag: () => null,
|
||||
Zap: () => null,
|
||||
Brain: () => null,
|
||||
Server: () => null,
|
||||
Cpu: () => null,
|
||||
}));
|
||||
|
||||
// Mock the api module
|
||||
|
||||
@@ -450,7 +450,13 @@ describe("core modals mobile css coverage", () => {
|
||||
const css = loadAllAppCss();
|
||||
const mobileBlock = getMainMobileBlock(css);
|
||||
|
||||
// Verify the quick-fields dep-trigger rule exists with min-height: 36px
|
||||
// Verify the promoted screenshot action row and quick-fields dep/agent buttons keep the mobile touch target.
|
||||
const actionButtonMatch = mobileBlock.match(
|
||||
/\.task-form-description-actions \.btn\s*\{[^}]+\}/,
|
||||
);
|
||||
expect(actionButtonMatch).not.toBeNull();
|
||||
expect(actionButtonMatch![0]).toContain("min-height: 36px");
|
||||
|
||||
const quickFieldsTriggerMatch = mobileBlock.match(
|
||||
/\.new-task-quick-fields \.dep-trigger\s*\{[^}]+\}/,
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user