FN-8194: align task detail inline controls
Align task-detail actions with Quick Add while preserving existing integrations. - Add inline attachment and eligible GitHub tracking controls - Replace the Oversight menu dots with an Eye icon and reorder metadata actions - Cover action order, integration behavior, icon rendering, and mobile wrapping Files changed: .changeset/fn-8194-task-detail-inline-controls.md | 7 ++ docs/dashboard-guide.md | 6 +- .../dashboard/app/components/TaskDetailModal.tsx | 99 +++++++++++++++------- ...lModal.inline-editing-and-integrations.test.tsx | 80 ++++++++++++++++- .../TaskDetailModal.mock-coverage.test.ts | 1 + .../TaskDetailModal.oversight-controls.test.tsx | 19 +++++ ...etailModal.responsive-and-dependencies.test.tsx | 5 +- .../__tests__/TaskDetailModal.test-helpers.ts | 10 +-- 8 files changed, 187 insertions(+), 40 deletions(-) Fusion-Task-Id: FN-8194 Fusion-Task-Lineage: 8de0e36f-b428-401c-98aa-35964c556a39 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
This commit is contained in:
7
.changeset/fn-8194-task-detail-inline-controls.md
Normal file
7
.changeset/fn-8194-task-detail-inline-controls.md
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
summary: Task detail action row now matches Quick Add — Eye icon for oversight, plus attach and GitHub-tracking buttons.
|
||||
category: feature
|
||||
dev: TaskDetailModal inline controls reordered to attach → GitHub → oversight(Eye) → priority → Fast; reuses existing upload and GitHub-tracking handlers. New test ids: detail-inline-attach, detail-inline-github-toggle.
|
||||
@@ -232,8 +232,10 @@ Features:
|
||||
- Task cards show a compact **planner-overseer eye badge** (`data-testid="planner-overseer-state-badge"`) when the engine reports a non-idle transient `plannerOverseerState`. The eye badge is an active-overseer state marker, not a human-read/view indicator: `watching` means passive monitoring, `steering`/`recovering` mean active guidance or recovery is underway, and `awaiting-confirmation` means a human decision is required before the overseer can continue. Hover exposes the composed tooltip with the overseer's reason, watched stage/signal, and pending-confirmation note when present.
|
||||
<!-- FNXC:PlannerOversight 2026-07-04-17:00: FN-7517 adds interactive task-detail planner-overseer controls (quick level change, manual nudge, stop oversight, explain current action) alongside the FN-7516 read-only card badges above. These controls live ONLY in TaskDetailModal, not TaskCard.
|
||||
|
||||
FNXC:PlannerOversight 2026-07-05-00:00: FN-7604 collapses the desktop inline cluster (documented below through FN-7545/FN-7546) into the single universal overflow-menu dropdown that FN-7545 originally built for mobile only — the dropdown is now the ONE canonical surface on every viewport, desktop included. -->
|
||||
- The task detail modal's inline meta-controls cluster (next to Priority/Execution mode) exposes planner-overseer controls behind a single compact **"Oversight" overflow-menu button** (`data-testid="detail-oversight-menu-trigger"`, `MoreVertical` icon, `aria-haspopup="menu"`) on every viewport, desktop and mobile alike. Clicking the trigger opens a `role="menu"` popover (mirroring the existing move-action dropdown pattern) containing: a **quick oversight-level select** (`data-testid="detail-oversight-level-select"`) that writes the per-task `plannerOversightLevel` override (Off/Observe/Steer/Autonomous recovery) or clears it back to the inherited workflow/project default via an "Inherit" option; a **manual nudge** button (`data-testid="detail-overseer-nudge"`) that asks the overseer to inject one guidance-only steering comment into the currently watched stage right now (never a merge/PR/destructive action), disabled when the overseer is off/inactive or the task is user-paused/done/archived/`autoMerge:false` in-review; a **stop oversight** button (`data-testid="detail-overseer-stop"`) that disables active oversight for the task (confirmation-gated), hidden once oversight is already off; and an **explain current action** button (`data-testid="detail-overseer-explain"`) that toggles a small read-only panel (`data-testid="detail-overseer-explain-panel"`) showing the overseer's watched stage, reason, last action, and attempt count/limit, with a non-empty-shell inactive state when the overseer is not currently watching. All three action controls call the `POST /tasks/:id/overseer/nudge`, `POST /tasks/:id/overseer/stop`, and `GET /tasks/:id/overseer/explain` routes. The trigger itself is withheld entirely when none of the menu's controls would render (no empty-shell overflow button for the unresolved-default case).
|
||||
FNXC:PlannerOversight 2026-07-05-00:00: FN-7604 collapses the desktop inline cluster (documented below through FN-7545/FN-7546) into the single universal overflow-menu dropdown that FN-7545 originally built for mobile only — the dropdown is now the ONE canonical surface on every viewport, desktop included.
|
||||
|
||||
FNXC:QuickAddActionRow 2026-07-16-16:00: FN-8194 aligns task-detail metadata actions with Quick Add: attach, GitHub tracking, Oversight (Eye), Priority, then Fast. -->
|
||||
- The task detail modal's inline meta-controls cluster follows Quick Add order: compact **Attach file** (`data-testid="detail-inline-attach"`), eligible-task **GitHub tracking** toggle (`data-testid="detail-inline-github-toggle"`), **Oversight**, Priority, and Execution mode. Attach opens the existing task attachment picker; GitHub toggles the existing tracking setting and is omitted for GitLab-tracked or non-editable tasks. The Oversight control exposes planner-overseer actions behind a compact **"Oversight" overflow-menu button** (`data-testid="detail-oversight-menu-trigger"`, `Eye` icon, `aria-haspopup="menu"`) on every viewport, desktop and mobile alike. Clicking the trigger opens a `role="menu"` popover (mirroring the existing move-action dropdown pattern) containing: a **quick oversight-level select** (`data-testid="detail-oversight-level-select"`) that writes the per-task `plannerOversightLevel` override (Off/Observe/Steer/Autonomous recovery) or clears it back to the inherited workflow/project default via an "Inherit" option; a **manual nudge** button (`data-testid="detail-overseer-nudge"`) that asks the overseer to inject one guidance-only steering comment into the currently watched stage right now (never a merge/PR/destructive action), disabled when the overseer is off/inactive or the task is user-paused/done/archived/`autoMerge:false` in-review; a **stop oversight** button (`data-testid="detail-overseer-stop"`) that disables active oversight for the task (confirmation-gated), hidden once oversight is already off; and an **explain current action** button (`data-testid="detail-overseer-explain"`) that toggles a small read-only panel (`data-testid="detail-overseer-explain-panel"`) showing the overseer's watched stage, reason, last action, and attempt count/limit, with a non-empty-shell inactive state when the overseer is not currently watching. All three action controls call the `POST /tasks/:id/overseer/nudge`, `POST /tasks/:id/overseer/stop`, and `GET /tasks/:id/overseer/explain` routes. The trigger itself is withheld entirely when none of the menu's controls would render (no empty-shell overflow button for the unresolved-default case).
|
||||
<!-- FNXC:PlannerOversight 2026-07-04-20:30: FN-7546 clarifies the cluster above — operators reported the buttons were unlabeled and looked inert, with only a hover title explaining why. Adds a visible group label and an always-visible disabled-reason line, and makes Explain always openable since it never mutates anything. -->
|
||||
- The controls inside the Oversight menu carry a visible, non-interactive **`"Overseer controls"` group label** (`data-testid="detail-oversight-controls-label"`) so Nudge/Stop/Explain read as an identifiable cluster rather than unlabeled entries; the label is gated by the same `(hasTaskOversightOverride || workflowOversightResolved) && !oversightIsOff` condition as the buttons, so it never renders when oversight is Off/unresolved (opening the menu in that state shows only the level select). When **Nudge** is disabled, an always-visible helper line (`data-testid="detail-overseer-nudge-disabled-reason"`) states the reason in-DOM (mirroring the existing hover `title`) instead of relying on a mouse-hover tooltip alone. **Explain** is read-only and non-mutating, so it is never disabled purely because the overseer is inactive — clicking it always opens/closes the panel, which shows the overseer's live state when watching or an informative "not currently watching this task" message otherwise. Nudge's mutating enablement rule (`canNudgeOverseer`, including the human-control suppression cases) and Stop's confirmation dialog are unchanged.
|
||||
<!-- FNXC:PlannerOversight 2026-07-04-19:00: FN-7545 originally introduced this overflow menu for mobile only, collapsing the level-select/nudge/stop/explain controls behind the trigger below the 768px breakpoint while desktop kept an inline cluster.
|
||||
|
||||
@@ -2,7 +2,7 @@ import "./TaskDetailModal.css";
|
||||
import React, { Suspense, lazy, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
|
||||
import { createPortal } from "react-dom";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Pencil, Bot, X, ChevronDown, ChevronRight, GitBranch, ArrowLeft, Zap, Loader2, AlertTriangle, Sparkles, Maximize2, Minimize2, Send, Square, Info, MoreVertical, Eye, EyeOff } from "lucide-react";
|
||||
import { Pencil, Bot, X, ChevronDown, ChevronRight, GitBranch, ArrowLeft, Zap, Loader2, AlertTriangle, Sparkles, Maximize2, Minimize2, Send, Square, Info, Paperclip, Eye, EyeOff } from "lucide-react";
|
||||
import { useModalResizePersist } from "../hooks/useModalResizePersist";
|
||||
import { useMobileScrollLock } from "../hooks/useMobileScrollLock";
|
||||
import { useOverlayDismiss } from "../hooks/useOverlayDismiss";
|
||||
@@ -4181,40 +4181,39 @@ export function TaskDetailContent({
|
||||
</div>
|
||||
)}
|
||||
<div className="detail-meta">
|
||||
{/*
|
||||
FNXC:QuickAddActionRow 2026-07-16-16:00:
|
||||
FN-8194: task-detail metadata mirrors Quick Add's action order:
|
||||
attach, GitHub tracking, Oversight, Priority, then Fast. The compact
|
||||
controls delegate to the existing single file-input upload and
|
||||
GitHub-tracking handlers so this row never forks persistence paths.
|
||||
*/}
|
||||
<div className="detail-meta-inline-controls" data-testid="detail-meta-inline-controls">
|
||||
<label
|
||||
className={`card-priority-badge card-priority-badge--${inlinePriority} detail-priority-chip ${isSavingInlinePriority ? "detail-priority-chip--saving" : ""}`}
|
||||
>
|
||||
<span>{t("taskDetail.priority.label", "Priority:")}</span>
|
||||
<select
|
||||
className="detail-priority-select"
|
||||
value={inlinePriority}
|
||||
onChange={(event) => {
|
||||
void handleInlinePriorityChange(event.target.value);
|
||||
}}
|
||||
disabled={isSavingInlinePriority}
|
||||
aria-label={t("taskDetail.priority.ariaLabel", "Task priority")}
|
||||
>
|
||||
{TASK_PRIORITIES.map((priorityOption) => (
|
||||
<option key={priorityOption} value={priorityOption}>
|
||||
{priorityOption}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
<button
|
||||
type="button"
|
||||
className={`btn btn-sm detail-execution-mode-toggle ${inlineExecutionMode === "fast" ? "detail-execution-mode-toggle--fast" : ""} ${isSavingInlineExecutionMode ? "detail-execution-mode-toggle--saving" : ""}`}
|
||||
onClick={() => {
|
||||
void handleInlineExecutionModeToggle();
|
||||
}}
|
||||
disabled={isSavingInlineExecutionMode}
|
||||
aria-label={t("taskDetail.executionMode.ariaLabel", "Execution mode: {{mode}}", { mode: inlineExecutionMode })}
|
||||
aria-pressed={inlineExecutionMode === "fast"}
|
||||
className="btn btn-icon btn-sm"
|
||||
data-testid="detail-inline-attach"
|
||||
onClick={() => fileInputRef.current?.click()}
|
||||
disabled={uploading}
|
||||
aria-label={t("taskDetail.attachments.attachInline", "Attach file")}
|
||||
title={t("taskDetail.attachments.attachInline", "Attach file")}
|
||||
>
|
||||
<Zap aria-hidden="true" />
|
||||
<span>{inlineExecutionMode === "fast" ? t("taskDetail.executionMode.fast", "Fast") : t("taskDetail.executionMode.standard", "Standard")}</span>
|
||||
<Paperclip aria-hidden="true" />
|
||||
</button>
|
||||
{canEditGithubTracking && !gitlabTrackedItem && (
|
||||
<button
|
||||
type="button"
|
||||
className={`btn btn-icon btn-sm ${githubTrackingEnabled ? "btn-primary" : ""}`}
|
||||
data-testid="detail-inline-github-toggle"
|
||||
onClick={() => void handleToggleGithubTracking()}
|
||||
disabled={isSavingGithubTracking}
|
||||
aria-pressed={githubTrackingEnabled}
|
||||
aria-label={t("taskDetail.githubTracking.toggleInline", "Toggle GitHub tracking")}
|
||||
title={t("taskDetail.githubTracking.toggleInline", "Toggle GitHub tracking")}
|
||||
>
|
||||
<ProviderIcon provider="github" size="sm" />
|
||||
</button>
|
||||
)}
|
||||
{/*
|
||||
FNXC:PlannerOversight 2026-07-04-17:00:
|
||||
FN-7517 quick oversight-level-change control. Shows the current
|
||||
@@ -4240,6 +4239,11 @@ export function TaskDetailContent({
|
||||
stop / explain) render ONLY behind the "Oversight" overflow-menu
|
||||
trigger on every surface (desktop and mobile); the former desktop
|
||||
inline cluster was removed for a consistent, simpler control bar.
|
||||
|
||||
FNXC:PlannerOversight 2026-07-16-16:00:
|
||||
FN-8194: use Eye for the Oversight overflow trigger so task detail
|
||||
matches Quick Add's planner-advisor affordance without changing
|
||||
the labeled menu's accessibility or behavior.
|
||||
*/}
|
||||
{(hasTaskOversightOverride || workflowOversightResolved) && (
|
||||
<div className="detail-oversight-menu-dropdown" ref={oversightMenuRef}>
|
||||
@@ -4254,7 +4258,7 @@ export function TaskDetailContent({
|
||||
aria-expanded={showOversightMenu}
|
||||
aria-label={t("taskDetail.oversight.menuAriaLabel", "Oversight actions")}
|
||||
>
|
||||
<MoreVertical aria-hidden="true" />
|
||||
<Eye aria-hidden="true" />
|
||||
<span>{t("taskDetail.oversight.menuLabel", "Oversight")}</span>
|
||||
</button>
|
||||
{showOversightMenu && (
|
||||
@@ -4404,6 +4408,39 @@ export function TaskDetailContent({
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<label
|
||||
className={`card-priority-badge card-priority-badge--${inlinePriority} detail-priority-chip ${isSavingInlinePriority ? "detail-priority-chip--saving" : ""}`}
|
||||
>
|
||||
<span>{t("taskDetail.priority.label", "Priority:")}</span>
|
||||
<select
|
||||
className="detail-priority-select"
|
||||
value={inlinePriority}
|
||||
onChange={(event) => {
|
||||
void handleInlinePriorityChange(event.target.value);
|
||||
}}
|
||||
disabled={isSavingInlinePriority}
|
||||
aria-label={t("taskDetail.priority.ariaLabel", "Task priority")}
|
||||
>
|
||||
{TASK_PRIORITIES.map((priorityOption) => (
|
||||
<option key={priorityOption} value={priorityOption}>
|
||||
{priorityOption}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
<button
|
||||
type="button"
|
||||
className={`btn btn-sm detail-execution-mode-toggle ${inlineExecutionMode === "fast" ? "detail-execution-mode-toggle--fast" : ""} ${isSavingInlineExecutionMode ? "detail-execution-mode-toggle--saving" : ""}`}
|
||||
onClick={() => {
|
||||
void handleInlineExecutionModeToggle();
|
||||
}}
|
||||
disabled={isSavingInlineExecutionMode}
|
||||
aria-label={t("taskDetail.executionMode.ariaLabel", "Execution mode: {{mode}}", { mode: inlineExecutionMode })}
|
||||
aria-pressed={inlineExecutionMode === "fast"}
|
||||
>
|
||||
<Zap aria-hidden="true" />
|
||||
<span>{inlineExecutionMode === "fast" ? t("taskDetail.executionMode.fast", "Fast") : t("taskDetail.executionMode.standard", "Standard")}</span>
|
||||
</button>
|
||||
</div>
|
||||
{overseerExplainOpen && (
|
||||
<div className="detail-overseer-explain-panel" data-testid="detail-overseer-explain-panel" role="region" aria-live="polite">
|
||||
|
||||
@@ -1207,7 +1207,12 @@ describe("TaskDetailModal", () => {
|
||||
const prioritySelect = screen.getByRole("combobox", { name: "Task priority" });
|
||||
const executionModeToggle = screen.getByRole("button", { name: "Execution mode: standard" });
|
||||
|
||||
expect(prioritySelect.parentElement).toBe(controls.firstElementChild);
|
||||
/*
|
||||
FNXC:QuickAddActionRow 2026-07-16-16:00:
|
||||
FN-8194: attach, GitHub, and Oversight precede the Quick Add-matched
|
||||
Priority/Fast controls; both controls remain direct interactive children.
|
||||
*/
|
||||
expect(controls).toContainElement(prioritySelect.parentElement);
|
||||
expect(executionModeToggle.parentElement).toBe(controls);
|
||||
|
||||
fireEvent.change(prioritySelect, {
|
||||
@@ -3338,3 +3343,76 @@ describe("TaskDetailModal", () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("TaskDetailModal inline action row parity (FN-8194)", () => {
|
||||
const renderDetail = (task: Task) => render(
|
||||
<TaskDetailModal
|
||||
initialTab="definition"
|
||||
task={task}
|
||||
onClose={noop}
|
||||
onMoveTask={noopMove}
|
||||
onDeleteTask={noopDelete}
|
||||
onMergeTask={noopMerge}
|
||||
onOpenDetail={noopOpenDetail}
|
||||
addToast={noop}
|
||||
/>,
|
||||
);
|
||||
|
||||
it("uses the existing file input and preserves Quick Add action order", async () => {
|
||||
renderDetail(makeTask({ id: "FN-8194", column: "todo", plannerOversightLevel: "observe" }));
|
||||
|
||||
const controls = screen.getByTestId("detail-meta-inline-controls");
|
||||
const attach = screen.getByTestId("detail-inline-attach");
|
||||
const github = screen.getByTestId("detail-inline-github-toggle");
|
||||
const oversight = await screen.findByTestId("detail-oversight-menu-trigger");
|
||||
const priority = screen.getByRole("combobox", { name: "Task priority" }).parentElement!;
|
||||
const fast = screen.getByRole("button", { name: "Execution mode: standard" });
|
||||
const fileInput = document.querySelector<HTMLInputElement>('input[type="file"]')!;
|
||||
const fileInputClick = vi.spyOn(fileInput, "click");
|
||||
|
||||
expect([...controls.children]).toEqual([attach, github, oversight.parentElement, priority, fast]);
|
||||
fireEvent.click(attach);
|
||||
expect(fileInputClick).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it("toggles GitHub tracking through the existing update path and reflects enabled state", async () => {
|
||||
const { updateTask } = await import("../../api");
|
||||
const mockUpdate = vi.mocked(updateTask);
|
||||
mockUpdate.mockResolvedValueOnce(makeTask({ id: "FN-8194", column: "todo", githubTracking: { enabled: true } }) as Task);
|
||||
|
||||
renderDetail(makeTask({ id: "FN-8194", column: "todo", githubTracking: { enabled: false } }));
|
||||
|
||||
const toggle = screen.getByTestId("detail-inline-github-toggle");
|
||||
expect(toggle).toHaveAttribute("aria-pressed", "false");
|
||||
expect(toggle).not.toHaveClass("btn-primary");
|
||||
fireEvent.click(toggle);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockUpdate).toHaveBeenCalledWith("FN-8194", { githubTracking: { enabled: true } }, undefined);
|
||||
});
|
||||
expect(toggle).toHaveAttribute("aria-pressed", "true");
|
||||
expect(toggle).toHaveClass("btn-primary");
|
||||
});
|
||||
|
||||
it("hides the GitHub toggle for non-editable and GitLab-tracked tasks", () => {
|
||||
const { unmount } = renderDetail(makeTask({ id: "FN-8194", column: "done", githubTracking: { enabled: true } }));
|
||||
expect(screen.queryByTestId("detail-inline-github-toggle")).not.toBeInTheDocument();
|
||||
unmount();
|
||||
|
||||
renderDetail(makeTask({
|
||||
id: "FN-8195",
|
||||
column: "todo",
|
||||
gitlabTracking: {
|
||||
item: {
|
||||
kind: "project_issue",
|
||||
projectPath: "acme/app",
|
||||
iid: 1,
|
||||
title: "GitLab issue",
|
||||
url: "https://gitlab.com/acme/app/-/issues/1",
|
||||
state: "opened",
|
||||
},
|
||||
},
|
||||
}) as Task);
|
||||
expect(screen.queryByTestId("detail-inline-github-toggle")).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -14,6 +14,7 @@ describe("TaskDetailModal lucide mock coverage", () => {
|
||||
expect(lucideMock.ChevronRight).toBeDefined();
|
||||
expect(lucideMock.ChevronUp).toBeDefined();
|
||||
expect(lucideMock.Maximize2).toBeDefined();
|
||||
expect(lucideMock.Paperclip).toBeDefined();
|
||||
expect(lucideMock.Pencil).toBeDefined();
|
||||
expect(lucideMock.Workflow).toBeDefined();
|
||||
expect(lucideMock.X).toBeDefined();
|
||||
|
||||
@@ -59,6 +59,25 @@ async function openOversightMenu() {
|
||||
}
|
||||
|
||||
describe("TaskDetailModal oversight controls", () => {
|
||||
it("uses the Eye icon for the labeled oversight overflow trigger", async () => {
|
||||
render(
|
||||
<TaskDetailModal
|
||||
task={makeTask({ id: "FN-8194", column: "in-progress", plannerOversightLevel: "observe" })}
|
||||
onClose={noop}
|
||||
onMoveTask={noopMove}
|
||||
onDeleteTask={noopDelete}
|
||||
onMergeTask={noopMerge}
|
||||
onOpenDetail={noopOpenDetail}
|
||||
addToast={noop}
|
||||
/>,
|
||||
);
|
||||
|
||||
const trigger = await screen.findByTestId("detail-oversight-menu-trigger");
|
||||
expect(trigger).toHaveTextContent("Oversight");
|
||||
expect(trigger.querySelector('[data-testid="eye-icon"]')).toBeInTheDocument();
|
||||
expect(trigger.querySelector('[data-testid="more-vertical-icon"]')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
vi.clearAllMocks();
|
||||
mockConfirm.mockResolvedValue(true);
|
||||
|
||||
@@ -242,11 +242,14 @@ describe("TaskDetailModal", () => {
|
||||
expect(css).not.toMatch(/@media[^{]*\(max-width: 768px\)[^{]*\{[\s\S]*?\.detail-timestamps\s*\{[^}]*flex-direction:\s*column;/);
|
||||
});
|
||||
|
||||
it("keeps inline metadata controls in a single row without a narrow-screen column fallback", () => {
|
||||
it("keeps inline metadata controls in a single row with a wrapping mobile fallback", () => {
|
||||
const css = readDashboardStylesSource();
|
||||
const mobileBlock = getCssAtRuleBlockContaining(css, "@media (max-width: 768px)", ".detail-meta-inline-controls");
|
||||
|
||||
expectBaseRule(css, ".detail-meta-inline-controls", "display: flex;");
|
||||
expectBaseRule(css, ".detail-meta-inline-controls", "flex-wrap: nowrap;");
|
||||
expect(mobileBlock).toMatch(/\.detail-meta-inline-controls\s*\{[^}]*flex-wrap:\s*wrap;/);
|
||||
expect(mobileBlock).not.toMatch(/\.detail-meta-inline-controls\s*\{[^}]*flex-direction:\s*column;/);
|
||||
expect(css).not.toMatch(/@media \(max-width: 640px\)\s*\{[^}]*\.detail-meta-inline-controls\s*\{[^}]*flex-direction:\s*column;/);
|
||||
});
|
||||
|
||||
|
||||
@@ -132,13 +132,13 @@ vi.mock("lucide-react", () => ({
|
||||
Code2: () => null,
|
||||
Cpu: () => null,
|
||||
Bell: () => null,
|
||||
// FNXC:PlannerOversight 2026-07-04-19:00: FN-7545 mobile oversight overflow-menu trigger icon.
|
||||
MoreVertical: (props: any) => React.createElement("svg", { "data-testid": "more-vertical-icon", ...props }),
|
||||
/*
|
||||
FNXC:DashboardTests 2026-07-15-11:55:
|
||||
Session-advisor toggle on TaskDetailContent uses Eye/EyeOff; missing exports break every
|
||||
TaskDetailModal suite that mounts the session-advisor control (including oversight-mobile).
|
||||
FNXC:DashboardTests 2026-07-16-16:00:
|
||||
FN-8194 replaces the Task Detail oversight dots with Eye and adds Paperclip
|
||||
to its inline action row. Keep both exports in the shared modal mock so every
|
||||
focused TaskDetailModal suite mounts the production affordances.
|
||||
*/
|
||||
Paperclip: (props: any) => React.createElement("svg", { "data-testid": "paperclip-icon", ...props }),
|
||||
Eye: (props: any) => React.createElement("svg", { "data-testid": "eye-icon", ...props }),
|
||||
EyeOff: (props: any) => React.createElement("svg", { "data-testid": "eye-off-icon", ...props }),
|
||||
// FNXC:Test 2026-07-05-11:20: FN-7579 added "ask-user"/"exit-gate" workflow node types to
|
||||
|
||||
Reference in New Issue
Block a user