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:
5
.changeset/fn-4147-github-tracking-enable-button.md
Normal file
5
.changeset/fn-4147-github-tracking-enable-button.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
Add a one-click inline "Enable GitHub tracking" button to the task detail GitHub tracking header when tracking is disabled.
|
||||
@@ -427,7 +427,7 @@ Tracking behavior is controlled per task:
|
||||
|
||||
- `task.githubTracking.enabled` turns tracking on for that task.
|
||||
- `task.githubTracking.repoOverride` optionally forces a specific target repo (`owner/repo`).
|
||||
- In the dashboard **Task Detail** modal, eligible existing tasks (`triage`, `todo`, `in-progress`, `in-review`) always show a compact GitHub tracking summary row; linked-issue details and tracking controls are behind a disclosure arrow so tracking can still be enabled, disabled, or retargeted without reopening the task in a creation flow.
|
||||
- In the dashboard **Task Detail** modal, eligible existing tasks (`triage`, `todo`, `in-progress`, `in-review`) always show a compact GitHub tracking summary row. When tracking is currently disabled and editable, the header exposes a one-click **Enable GitHub tracking** button; linked-issue details and the rest of the tracking controls remain behind the disclosure arrow for disable/retarget flows.
|
||||
- When a task is already tracking-enabled but still unlinked, Task Detail exposes a **Create tracking issue** action in the disclosure content (including non-editable columns like `done`) so "Issue not yet created" is not a dead-end state.
|
||||
- Clearing the Task Detail repo override stores `null`, which reverts repo resolution to project/global defaults.
|
||||
- Explicit task-level enablement is honored even when project/global GitHub tracking defaults are unset. If `enabled: true` and the repo resolves at task scope (for example via `repoOverride`), Fusion attempts tracking-issue creation on both create-time and eligible edit-time flows.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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", () => {
|
||||
|
||||
Reference in New Issue
Block a user