mirror of
https://github.com/paperclipai/paperclip
synced 2026-05-12 01:46:27 +02:00
fix(ui): hide planning chip on standard issues and surface toggle via three-dot menu (PAP-3613)
Per user feedback: only show the Planning chip in the composer when planning mode is in effect. On a standard issue we no longer render the chip; instead a three-dot button next to the paperclip opens a menu with "Switch to planning" (or "Switch to standard" once toggled). The container only goes amber when pendingWorkMode === planning, matching the visible chip. Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
@@ -383,7 +383,7 @@ describe("IssueChatThread", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("toggles the composer work mode for the next submission without changing the issue immediately", () => {
|
||||
it("hides the planning chip on a standard issue and exposes the toggle through the menu", () => {
|
||||
const root = createRoot(container);
|
||||
const onWorkModeChange = vi.fn();
|
||||
|
||||
@@ -404,21 +404,41 @@ describe("IssueChatThread", () => {
|
||||
);
|
||||
});
|
||||
|
||||
const toggle = container.querySelector(
|
||||
'[data-testid="issue-chat-composer-work-mode-toggle"]',
|
||||
expect(
|
||||
container.querySelector('[data-testid="issue-chat-composer-work-mode-toggle"]'),
|
||||
).toBeNull();
|
||||
const composer = container.querySelector('[data-testid="issue-chat-composer"]');
|
||||
expect(composer?.getAttribute("data-pending-work-mode")).toBe("standard");
|
||||
expect(composer?.className).not.toContain("amber");
|
||||
|
||||
const menuTrigger = container.querySelector(
|
||||
'[data-testid="issue-chat-composer-work-mode-menu"]',
|
||||
) as HTMLButtonElement | null;
|
||||
expect(toggle).not.toBeNull();
|
||||
expect(toggle?.getAttribute("data-pending-work-mode")).toBe("standard");
|
||||
expect(menuTrigger).not.toBeNull();
|
||||
act(() => {
|
||||
menuTrigger?.click();
|
||||
});
|
||||
|
||||
const menuItem = document.querySelector(
|
||||
'[data-testid="issue-chat-composer-work-mode-menu-toggle"]',
|
||||
) as HTMLButtonElement | null;
|
||||
expect(menuItem).not.toBeNull();
|
||||
expect(menuItem?.textContent).toContain("Switch to planning");
|
||||
|
||||
act(() => {
|
||||
toggle?.click();
|
||||
menuItem?.click();
|
||||
});
|
||||
|
||||
expect(onWorkModeChange).not.toHaveBeenCalled();
|
||||
const composer = container.querySelector('[data-testid="issue-chat-composer"]');
|
||||
expect(composer?.getAttribute("data-pending-work-mode")).toBe("planning");
|
||||
expect(composer?.className).toContain("amber");
|
||||
|
||||
const visibleChip = container.querySelector(
|
||||
'[data-testid="issue-chat-composer-work-mode-toggle"]',
|
||||
);
|
||||
expect(visibleChip).not.toBeNull();
|
||||
expect(visibleChip?.textContent).toContain("Planning");
|
||||
|
||||
act(() => {
|
||||
root.unmount();
|
||||
});
|
||||
|
||||
@@ -2837,6 +2837,7 @@ const IssueChatComposer = forwardRef<IssueChatComposerHandle, IssueChatComposerP
|
||||
const [unassignedConfirmed, setUnassignedConfirmed] = useState(false);
|
||||
const resolvedIssueWorkMode: IssueWorkMode = issueWorkMode ?? "standard";
|
||||
const [pendingWorkMode, setPendingWorkMode] = useState<IssueWorkMode>(resolvedIssueWorkMode);
|
||||
const [workModeMenuOpen, setWorkModeMenuOpen] = useState(false);
|
||||
const canToggleWorkMode = typeof onWorkModeChange === "function";
|
||||
const attachInputRef = useRef<HTMLInputElement | null>(null);
|
||||
const editorRef = useRef<MarkdownEditorRef>(null);
|
||||
@@ -3221,32 +3222,53 @@ const IssueChatComposer = forwardRef<IssueChatComposerHandle, IssueChatComposerP
|
||||
</>
|
||||
) : null}
|
||||
{canToggleWorkMode ? (
|
||||
<Popover open={workModeMenuOpen} onOpenChange={setWorkModeMenuOpen}>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon-sm"
|
||||
data-testid="issue-chat-composer-work-mode-menu"
|
||||
title="More composer options"
|
||||
>
|
||||
<MoreHorizontal className="h-4 w-4" />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-44 p-1" align="start">
|
||||
<button
|
||||
type="button"
|
||||
data-testid="issue-chat-composer-work-mode-menu-toggle"
|
||||
data-pending-work-mode={pendingWorkMode}
|
||||
className={cn(
|
||||
"flex w-full items-center gap-2 rounded px-2 py-1.5 text-xs hover:bg-accent/50",
|
||||
isPlanning ? "text-amber-700 dark:text-amber-300" : "text-foreground",
|
||||
)}
|
||||
onClick={() => {
|
||||
setPendingWorkMode((prev) => (prev === "planning" ? "standard" : "planning"));
|
||||
setWorkModeMenuOpen(false);
|
||||
}}
|
||||
>
|
||||
{isPlanning ? (
|
||||
<Hammer className="h-3.5 w-3.5 shrink-0 text-muted-foreground" aria-hidden />
|
||||
) : (
|
||||
<ClipboardList className="h-3.5 w-3.5 shrink-0 text-amber-600 dark:text-amber-300" aria-hidden />
|
||||
)}
|
||||
<span>{isPlanning ? "Switch to standard" : "Switch to planning"}</span>
|
||||
</button>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
) : null}
|
||||
{canToggleWorkMode && isPlanning ? (
|
||||
<button
|
||||
type="button"
|
||||
data-testid="issue-chat-composer-work-mode-toggle"
|
||||
data-pending-work-mode={pendingWorkMode}
|
||||
aria-pressed={isPlanning}
|
||||
title={
|
||||
isPlanning
|
||||
? "Planning mode is on for this submission. Click to switch to Standard."
|
||||
: "Click to switch this submission to Planning mode."
|
||||
}
|
||||
onClick={() =>
|
||||
setPendingWorkMode((prev) => (prev === "planning" ? "standard" : "planning"))
|
||||
}
|
||||
className={cn(
|
||||
"inline-flex items-center gap-1.5 rounded-md border px-2 py-1 text-xs transition-colors",
|
||||
isPlanning
|
||||
? "border-amber-500/60 bg-amber-500/15 text-amber-800 hover:bg-amber-500/25 dark:border-amber-500/50 dark:bg-amber-500/15 dark:text-amber-200 dark:hover:bg-amber-500/25"
|
||||
: "border-border text-muted-foreground hover:bg-accent/50",
|
||||
)}
|
||||
aria-pressed
|
||||
title="Planning mode is on for this submission. Click to switch to Standard."
|
||||
onClick={() => setPendingWorkMode("standard")}
|
||||
className="inline-flex items-center gap-1.5 rounded-md border border-amber-500/60 bg-amber-500/15 px-2 py-1 text-xs text-amber-800 transition-colors hover:bg-amber-500/25 dark:border-amber-500/50 dark:bg-amber-500/15 dark:text-amber-200 dark:hover:bg-amber-500/25"
|
||||
>
|
||||
{isPlanning ? (
|
||||
<ClipboardList className="h-3.5 w-3.5" aria-hidden />
|
||||
) : (
|
||||
<Hammer className="h-3.5 w-3.5" aria-hidden />
|
||||
)}
|
||||
<span>{isPlanning ? "Planning" : "Standard"}</span>
|
||||
<ClipboardList className="h-3.5 w-3.5" aria-hidden />
|
||||
<span>Planning</span>
|
||||
</button>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user