FN-8232: keep task attachment picker available across tabs

Keep the task-detail attachment picker functional outside the Definition tab.

- Mount the shared hidden file input outside tab-specific content.
- Cover attachment picker access from the Activity tab.
- Add a patch changeset for the task-detail fix.

Files changed:
 .../fn-8232-task-detail-attachment-picker.md       |  7 +++++++
 .../dashboard/app/components/TaskDetailModal.tsx   | 20 ++++++++++++-------
 ...lModal.inline-editing-and-integrations.test.tsx | 23 ++++++++++++++++++++++
 3 files changed, 43 insertions(+), 7 deletions(-)

Fusion-Task-Id: FN-8232

Fusion-Task-Lineage: a425a373-f15c-4e7f-a50e-e2eba592d2c9

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
This commit is contained in:
gsxdsm
2026-07-17 13:15:58 -07:00
parent 9cdddab504
commit cdcdc32679
3 changed files with 43 additions and 7 deletions

View File

@@ -0,0 +1,7 @@
---
"@runfusion/fusion": patch
---
summary: Fix the task-detail attach-file icon when the Definition tab is not open.
category: fix
dev: Keeps the shared fileInputRef input mounted outside tab-specific content.

View File

@@ -4490,6 +4490,19 @@ export function TaskDetailContent({
<Zap size={14} aria-hidden="true" />
</button>
</div>
{/*
FNXC:TaskDetailAttachments 2026-07-17-12:30:
FN-8232: keep the hidden file input mounted independently of activeTab.
The paperclip renders on every non-editing tab while task details default
to Activity or Summary; a Definition-only input made that control a no-op.
*/}
<input
className="detail-hidden-file-input"
ref={fileInputRef}
type="file"
accept="image/*"
onChange={handleUpload}
/>
{overseerExplainOpen && (
<div className="detail-overseer-explain-panel" data-testid="detail-overseer-explain-panel" role="region" aria-live="polite">
{isLoadingOverseerExplain ? (
@@ -5892,13 +5905,6 @@ export function TaskDetailContent({
) : (
<div className="detail-empty-inline">{t("taskDetail.attachments.none", "(no attachments)")}</div>
)}
<input
className="detail-hidden-file-input"
ref={fileInputRef}
type="file"
accept="image/*"
onChange={handleUpload}
/>
<button
className="btn btn-sm"
onClick={() => fileInputRef.current?.click()}

View File

@@ -3372,6 +3372,29 @@ describe("TaskDetailModal inline action row parity (FN-8194)", () => {
expect(fileInputClick).toHaveBeenCalledOnce();
});
it("opens the shared file picker from Activity without rendering Definition", () => {
render(
<TaskDetailModal
initialTab="chat"
task={makeTask({ id: "FN-8232", column: "todo" })}
onClose={noop}
onMoveTask={noopMove}
onDeleteTask={noopDelete}
onMergeTask={noopMerge}
onOpenDetail={noopOpenDetail}
addToast={noop}
/>,
);
const fileInputs = document.querySelectorAll<HTMLInputElement>('input[type="file"]');
expect(fileInputs).toHaveLength(1);
const fileInputClick = vi.spyOn(fileInputs[0], "click");
fireEvent.click(screen.getByTestId("detail-inline-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);