diff --git a/ui/src/components/IssueDocumentsSection.test.tsx b/ui/src/components/IssueDocumentsSection.test.tsx index 6c3ec8ab66f..9f04df9f433 100644 --- a/ui/src/components/IssueDocumentsSection.test.tsx +++ b/ui/src/components/IssueDocumentsSection.test.tsx @@ -414,6 +414,85 @@ describe("IssueDocumentsSection", () => { queryClient.clear(); }); + it("returns from a historical preview when fetched history is newer than the document summary", async () => { + const staleDocument = createIssueDocument({ + body: "Original plan body", + latestRevisionId: "revision-2", + latestRevisionNumber: 2, + updatedAt: new Date("2026-03-31T12:00:00.000Z"), + }); + const issue = createIssue(); + const root = createRoot(container); + const queryClient = new QueryClient({ + defaultOptions: { + queries: { + retry: false, + }, + mutations: { + retry: false, + }, + }, + }); + + mockIssuesApi.listDocuments.mockResolvedValue([staleDocument]); + queryClient.setQueryData( + queryKeys.issues.documentRevisions(issue.id, "plan"), + [ + createRevision({ + id: "revision-3", + revisionNumber: 3, + body: "Current plan body", + createdAt: new Date("2026-03-31T12:05:00.000Z"), + }), + createRevision({ + id: "revision-2", + revisionNumber: 2, + body: "Original plan body", + createdAt: new Date("2026-03-31T12:00:00.000Z"), + }), + ], + ); + + await act(async () => { + root.render( + + + , + ); + }); + await flush(); + await flush(); + + expect(container.textContent).toContain("Current plan body"); + + const revisionButtons = Array.from(container.querySelectorAll("button")); + const historicalRevisionButton = revisionButtons.find((button) => button.textContent?.includes("rev 2")); + expect(historicalRevisionButton).toBeTruthy(); + + await act(async () => { + historicalRevisionButton?.dispatchEvent(new MouseEvent("click", { bubbles: true })); + }); + + expect(container.textContent).toContain("Viewing revision 2"); + expect(container.textContent).toContain("Original plan body"); + + const currentRevisionButton = Array.from(container.querySelectorAll("button")) + .find((button) => button.textContent?.includes("rev 3")); + expect(currentRevisionButton).toBeTruthy(); + + await act(async () => { + currentRevisionButton?.dispatchEvent(new MouseEvent("click", { bubbles: true })); + }); + + expect(container.textContent).not.toContain("Viewing revision 2"); + expect(container.textContent).toContain("Current plan body"); + + await act(async () => { + root.unmount(); + }); + queryClient.clear(); + }); + it("ignores mount-time editor change noise before a document is actively being edited", async () => { markdownEditorMockState.emitMountEmptyChange = true; diff --git a/ui/src/components/IssueDocumentsSection.tsx b/ui/src/components/IssueDocumentsSection.tsx index fe8b65a11ad..6923befab07 100644 --- a/ui/src/components/IssueDocumentsSection.tsx +++ b/ui/src/components/IssueDocumentsSection.tsx @@ -537,13 +537,13 @@ export function IssueDocumentsSection({ }, []); const previewRevision = useCallback((doc: IssueDocument, revisionId: string) => { - if (revisionId === doc.latestRevisionId) { + const revisionState = deriveDocumentRevisionState(doc, getDocumentRevisions(doc.key)); + const selectedRevision = revisionState.revisions.find((revision) => revision.id === revisionId); + if (!selectedRevision) return; + if (selectedRevision.id === revisionState.currentRevision.id) { returnToLatestRevision(doc.key); return; } - const revisions = getDocumentRevisions(doc.key); - const selectedRevision = revisions.find((revision) => revision.id === revisionId); - if (!selectedRevision) return; if (documentConflict?.key === doc.key || documentHasUnsavedChanges(doc, draft)) { setError("Save or cancel your local changes before viewing an older revision."); return; @@ -788,7 +788,8 @@ export function IssueDocumentsSection({ const activeDraft = draft?.key === doc.key && !draft.isNew ? draft : null; const activeConflict = documentConflict?.key === doc.key ? documentConflict : null; const isFolded = foldedDocumentKeys.includes(doc.key); - const revisionState = deriveDocumentRevisionState(doc, getDocumentRevisions(doc.key)); + const rawRevisionHistory = getDocumentRevisions(doc.key); + const revisionState = deriveDocumentRevisionState(doc, rawRevisionHistory); const revisionHistory = revisionState.revisions; const currentRevision = revisionState.currentRevision; const selectedRevisionId = selectedRevisionIds[doc.key] ?? null; @@ -848,7 +849,7 @@ export function IssueDocumentsSection({ Revision history - {revisionMenuOpenKey === doc.key && isFetchingDocumentRevisions && revisionHistory.length === 0 ? ( + {revisionMenuOpenKey === doc.key && isFetchingDocumentRevisions && rawRevisionHistory.length === 0 ? ( Loading revisions... ) : revisionHistory.length > 0 ? (