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.enabled` turns tracking on for that task.
|
||||||
- `task.githubTracking.repoOverride` optionally forces a specific target repo (`owner/repo`).
|
- `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.
|
- 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.
|
- 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.
|
- 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);
|
font-family: var(--font-mono);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.detail-github-tracking-enable {
|
||||||
|
margin-left: auto;
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
|
|
||||||
.detail-source-toggle {
|
.detail-source-toggle {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
min-width: 36px;
|
flex-shrink: 0;
|
||||||
min-height: 36px;
|
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);
|
padding: var(--space-xs);
|
||||||
border: 0;
|
border: 0;
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
@@ -451,6 +457,10 @@
|
|||||||
transition: background var(--transition-fast), color var(--transition-fast);
|
transition: background var(--transition-fast), color var(--transition-fast);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.detail-source-header .detail-source-toggle {
|
||||||
|
margin-left: var(--space-sm);
|
||||||
|
}
|
||||||
|
|
||||||
.detail-source-toggle:hover {
|
.detail-source-toggle:hover {
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
background: var(--card-hover);
|
background: var(--card-hover);
|
||||||
@@ -1576,12 +1586,23 @@
|
|||||||
|
|
||||||
.detail-source-header {
|
.detail-source-header {
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.detail-source-summary {
|
.detail-source-summary {
|
||||||
|
flex: 1 1 100%;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.detail-github-tracking-enable {
|
||||||
|
margin-left: auto;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-source-header .detail-source-toggle {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.detail-body {
|
.detail-body {
|
||||||
padding: calc(var(--space-md) + var(--space-xs) / 2);
|
padding: calc(var(--space-md) + var(--space-xs) / 2);
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
|
|||||||
@@ -2391,6 +2391,17 @@ export function TaskDetailContent({
|
|||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</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
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="detail-source-toggle"
|
className="detail-source-toggle"
|
||||||
|
|||||||
@@ -2203,7 +2203,90 @@ describe("TaskDetailModal", () => {
|
|||||||
|
|
||||||
expect(screen.getByText("GitHub tracking")).toBeTruthy();
|
expect(screen.getByText("GitHub tracking")).toBeTruthy();
|
||||||
expect(screen.getByText("Tracking is currently disabled")).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", () => {
|
it("hides section when tracking is disabled and task is not in an eligible column", () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user