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:
7
.changeset/fn-8232-task-detail-attachment-picker.md
Normal file
7
.changeset/fn-8232-task-detail-attachment-picker.md
Normal 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.
|
||||||
@@ -4490,6 +4490,19 @@ export function TaskDetailContent({
|
|||||||
<Zap size={14} aria-hidden="true" />
|
<Zap size={14} aria-hidden="true" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</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 && (
|
{overseerExplainOpen && (
|
||||||
<div className="detail-overseer-explain-panel" data-testid="detail-overseer-explain-panel" role="region" aria-live="polite">
|
<div className="detail-overseer-explain-panel" data-testid="detail-overseer-explain-panel" role="region" aria-live="polite">
|
||||||
{isLoadingOverseerExplain ? (
|
{isLoadingOverseerExplain ? (
|
||||||
@@ -5892,13 +5905,6 @@ export function TaskDetailContent({
|
|||||||
) : (
|
) : (
|
||||||
<div className="detail-empty-inline">{t("taskDetail.attachments.none", "(no attachments)")}</div>
|
<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
|
<button
|
||||||
className="btn btn-sm"
|
className="btn btn-sm"
|
||||||
onClick={() => fileInputRef.current?.click()}
|
onClick={() => fileInputRef.current?.click()}
|
||||||
|
|||||||
@@ -3372,6 +3372,29 @@ describe("TaskDetailModal inline action row parity (FN-8194)", () => {
|
|||||||
expect(fileInputClick).toHaveBeenCalledOnce();
|
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 () => {
|
it("toggles GitHub tracking through the existing update path and reflects enabled state", async () => {
|
||||||
const { updateTask } = await import("../../api");
|
const { updateTask } = await import("../../api");
|
||||||
const mockUpdate = vi.mocked(updateTask);
|
const mockUpdate = vi.mocked(updateTask);
|
||||||
|
|||||||
Reference in New Issue
Block a user