fix(FN-4228): handle unresolved github tracking detail as loading
Fusion-Task-Id: FN-4228 Fusion-Task-Lineage: 86a2aac1-a45f-4cb0-b004-a95db3676355
This commit is contained in:
@@ -812,10 +812,21 @@ export function TaskDetailContent({
|
||||
const canEditGithubTracking = GITHUB_TRACKING_EDITABLE_COLUMNS.has(task.column) && !isSaving;
|
||||
const githubTrackingEnabled = githubTrackingEnabledDraft ?? (workingTask.githubTracking?.enabled === true);
|
||||
const githubTrackedIssue = workingTask.githubTracking?.issue;
|
||||
const githubTrackingDetailPending = detailLoading && typeof task.githubTracking === "undefined";
|
||||
const showInlineGithubTrackingEnableButton =
|
||||
canEditGithubTracking && !githubTrackedIssue && (!githubTrackingEnabled || (isSavingGithubTracking && workingTask.githubTracking?.enabled !== true));
|
||||
canEditGithubTracking
|
||||
&& !githubTrackedIssue
|
||||
&& !githubTrackingDetailPending
|
||||
&& (!githubTrackingEnabled || (isSavingGithubTracking && workingTask.githubTracking?.enabled !== true));
|
||||
const showGithubTrackingSection = canEditGithubTracking || githubTrackingEnabled || Boolean(githubTrackedIssue);
|
||||
const githubTrackingStatus = githubTrackedIssue ? "Linked" : githubTrackingEnabled ? "Enabled" : "Disabled";
|
||||
const githubTrackingStatus = githubTrackingDetailPending
|
||||
? "Loading"
|
||||
: githubTrackedIssue
|
||||
? "Linked"
|
||||
: githubTrackingEnabled
|
||||
? "Enabled"
|
||||
: "Disabled";
|
||||
const showGithubTrackingSpinner = !githubTrackedIssue && (isSavingGithubTracking || githubTrackingDetailPending);
|
||||
const effectiveGithubRepoDefault = resolveEffectiveGithubRepoDefault(settings ?? null, globalSettings);
|
||||
const githubRepoOverrideTrimmed = githubRepoOverrideDraft.trim();
|
||||
|
||||
@@ -2411,7 +2422,11 @@ export function TaskDetailContent({
|
||||
</span>
|
||||
{!githubTrackedIssue && (
|
||||
<span className="detail-source-empty">
|
||||
{githubTrackingEnabled ? "Issue not yet created" : "Tracking is currently disabled"}
|
||||
{githubTrackingDetailPending
|
||||
? "Checking tracking status"
|
||||
: githubTrackingEnabled
|
||||
? "Issue not yet created"
|
||||
: "Tracking is currently disabled"}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
@@ -2425,15 +2440,17 @@ export function TaskDetailContent({
|
||||
Enable
|
||||
</button>
|
||||
)}
|
||||
{isSavingGithubTracking && !githubTrackedIssue && (
|
||||
{showGithubTrackingSpinner && (
|
||||
<span
|
||||
className="detail-github-tracking-spinner"
|
||||
role="status"
|
||||
aria-live="polite"
|
||||
aria-label="Enabling GitHub tracking"
|
||||
aria-label={isSavingGithubTracking ? "Enabling GitHub tracking" : "Loading GitHub tracking status"}
|
||||
>
|
||||
<Loader2 size={16} className="spin" aria-hidden="true" />
|
||||
<span className="visually-hidden">Enabling GitHub tracking…</span>
|
||||
<span className="visually-hidden">
|
||||
{isSavingGithubTracking ? "Enabling GitHub tracking…" : "Loading GitHub tracking status…"}
|
||||
</span>
|
||||
</span>
|
||||
)}
|
||||
<button
|
||||
|
||||
@@ -2301,6 +2301,54 @@ describe("TaskDetailModal", () => {
|
||||
expect(screen.getByRole("button", { name: "Enable GitHub tracking" })).toHaveTextContent("Enable");
|
||||
});
|
||||
|
||||
it("FN-4228 shows loading state instead of disabled CTA while detail fetch is unresolved", async () => {
|
||||
const { fetchTaskDetail } = await import("../../api");
|
||||
const mockFetch = vi.mocked(fetchTaskDetail);
|
||||
let resolveFetch: ((value: TaskDetail) => void) | undefined;
|
||||
mockFetch.mockImplementationOnce(
|
||||
() =>
|
||||
new Promise((resolve) => {
|
||||
resolveFetch = resolve;
|
||||
}),
|
||||
);
|
||||
|
||||
const optimisticTask = makeTask({ id: "FN-001", column: "todo" });
|
||||
delete (optimisticTask as Partial<Task>).prompt;
|
||||
delete (optimisticTask as Partial<Task>).githubTracking;
|
||||
|
||||
render(
|
||||
<TaskDetailModal
|
||||
task={optimisticTask}
|
||||
onClose={noop}
|
||||
onOpenDetail={noopOpenDetail}
|
||||
onMoveTask={noopMove}
|
||||
onDeleteTask={noopDelete}
|
||||
onMergeTask={noopMerge}
|
||||
addToast={noop}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(screen.getByLabelText("GitHub tracking status")).toHaveTextContent("Loading");
|
||||
expect(screen.getByText("Checking tracking status")).toBeInTheDocument();
|
||||
expect(screen.queryByRole("button", { name: "Enable GitHub tracking" })).toBeNull();
|
||||
expect(screen.getByRole("status", { name: "Loading GitHub tracking status" })).toBeInTheDocument();
|
||||
|
||||
await act(async () => {
|
||||
resolveFetch?.({
|
||||
...(makeTask({ id: "FN-001", column: "todo" }) as TaskDetail),
|
||||
prompt: "# Spec",
|
||||
githubTracking: { enabled: false },
|
||||
});
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByLabelText("GitHub tracking status")).toHaveTextContent("Disabled");
|
||||
});
|
||||
expect(screen.getByText("Tracking is currently disabled")).toBeInTheDocument();
|
||||
expect(screen.queryByRole("status", { name: "Loading GitHub tracking status" })).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);
|
||||
|
||||
Reference in New Issue
Block a user