feat(FN-4147): add mobile-friendly github tracking enable button in header

Adds a GitHub tracking toggle affordance to the task detail header with accompanying tests, documentation updates, and a changeset, plus a CSS fix for mobile header wrapping.

Fusion-Task-Id: FN-4147
This commit is contained in:
Fusion
2026-05-12 09:47:18 -07:00
committed by gsxdsm
parent 67c680c8d5
commit 04b41d8258
5 changed files with 124 additions and 4 deletions

View File

@@ -436,12 +436,18 @@
font-family: var(--font-mono);
}
.detail-github-tracking-enable {
margin-left: auto;
align-self: center;
}
.detail-source-toggle {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 36px;
min-height: 36px;
flex-shrink: 0;
min-width: calc(var(--space-lg) + var(--space-xl) - var(--space-xs));
min-height: calc(var(--space-lg) + var(--space-xl) - var(--space-xs));
padding: var(--space-xs);
border: 0;
border-radius: var(--radius-sm);
@@ -451,6 +457,10 @@
transition: background var(--transition-fast), color var(--transition-fast);
}
.detail-source-header .detail-source-toggle {
margin-left: var(--space-sm);
}
.detail-source-toggle:hover {
color: var(--text);
background: var(--card-hover);
@@ -1576,12 +1586,23 @@
.detail-source-header {
align-items: flex-start;
flex-wrap: wrap;
}
.detail-source-summary {
flex: 1 1 100%;
flex-wrap: wrap;
}
.detail-github-tracking-enable {
margin-left: auto;
justify-content: center;
}
.detail-source-header .detail-source-toggle {
margin-left: 0;
}
.detail-body {
padding: calc(var(--space-md) + var(--space-xs) / 2);
overflow-x: hidden;

View File

@@ -2391,6 +2391,17 @@ export function TaskDetailContent({
</span>
)}
</div>
{canEditGithubTracking && !githubTrackingEnabled && !githubTrackedIssue && !isSavingGithubTracking && (
<button
type="button"
className="btn btn-sm btn-primary touch-target detail-github-tracking-enable"
aria-label="Enable GitHub tracking"
onClick={() => void handleToggleGithubTracking()}
disabled={isSavingGithubTracking}
>
Enable GitHub tracking
</button>
)}
<button
type="button"
className="detail-source-toggle"

View File

@@ -2203,7 +2203,90 @@ describe("TaskDetailModal", () => {
expect(screen.getByText("GitHub tracking")).toBeTruthy();
expect(screen.getByText("Tracking is currently disabled")).toBeTruthy();
expect(screen.queryByLabelText("Enable GitHub tracking")).toBeNull();
expect(screen.getByRole("button", { name: "Enable GitHub tracking" })).toBeInTheDocument();
});
it("enables GitHub tracking via the inline header button without expanding the disclosure", async () => {
const { updateTask } = await import("../../api");
const mockUpdate = vi.mocked(updateTask);
const addToast = vi.fn();
mockUpdate.mockResolvedValueOnce({ id: "FN-001" } as Task);
render(
<TaskDetailModal
task={makeTask({
id: "FN-001",
column: "todo",
githubTracking: {
enabled: false,
},
})}
onClose={noop}
onOpenDetail={noopOpenDetail}
onMoveTask={noopMove}
onDeleteTask={noopDelete}
onMergeTask={noopMerge}
addToast={addToast}
/>,
);
const expandButton = screen.getByRole("button", { name: "Expand GitHub tracking details" });
expect(expandButton).toHaveAttribute("aria-expanded", "false");
fireEvent.click(screen.getByRole("button", { name: "Enable GitHub tracking" }));
await waitFor(() => {
expect(mockUpdate).toHaveBeenCalledWith("FN-001", { githubTracking: { enabled: true } }, undefined);
});
expect(addToast).not.toHaveBeenCalledWith(expect.stringContaining("Failed to update FN-001"), "error");
expect(screen.getByRole("button", { name: "Expand GitHub tracking details" })).toHaveAttribute("aria-expanded", "false");
expect(screen.queryByRole("button", { name: "Collapse GitHub tracking details" })).toBeNull();
});
it("hides the inline enable button when tracking is already enabled", () => {
render(
<TaskDetailModal
task={makeTask({ column: "todo", githubTracking: { enabled: true } })}
onClose={noop}
onOpenDetail={noopOpenDetail}
onMoveTask={noopMove}
onDeleteTask={noopDelete}
onMergeTask={noopMerge}
addToast={noop}
/>,
);
expect(screen.queryByRole("button", { name: "Enable GitHub tracking" })).toBeNull();
expect(screen.getByRole("button", { name: "Expand GitHub tracking details" })).toBeInTheDocument();
});
it("hides the inline enable button when an issue is already linked", () => {
render(
<TaskDetailModal
task={makeTask({
column: "todo",
githubTracking: {
enabled: false,
issue: {
owner: "runfusion",
repo: "fusion",
number: 456,
url: "https://github.com/runfusion/fusion/issues/456",
createdAt: "2026-01-01T00:00:00Z",
},
},
})}
onClose={noop}
onOpenDetail={noopOpenDetail}
onMoveTask={noopMove}
onDeleteTask={noopDelete}
onMergeTask={noopMerge}
addToast={noop}
/>,
);
expect(screen.queryByRole("button", { name: "Enable GitHub tracking" })).toBeNull();
expect(screen.getByRole("button", { name: "Expand GitHub tracking details" })).toBeInTheDocument();
});
it("hides section when tracking is disabled and task is not in an eligible column", () => {