diff --git a/frontend/src/js/components/viewer/PreviewSwitcher.js b/frontend/src/js/components/viewer/PreviewSwitcher.js index 1f419e86..4782a9ff 100644 --- a/frontend/src/js/components/viewer/PreviewSwitcher.js +++ b/frontend/src/js/components/viewer/PreviewSwitcher.js @@ -3,6 +3,7 @@ import PropTypes from "prop-types"; import { resourcePropType } from "../../types/Resource"; import _ from "lodash"; +import { hasTextContent } from "../../util/resourceUtils"; import { keyboardShortcuts } from "../../util/keyboardShortcuts"; import { KeyboardShortcut } from "../UtilComponents/KeyboardShortcut"; @@ -34,7 +35,7 @@ class PreviewSwitcher extends React.Component { return false; } - if (this.props.view === "text" && !resource.text) { + if (this.props.view === "text" && !hasTextContent(resource)) { return false; } @@ -65,8 +66,8 @@ class PreviewSwitcher extends React.Component { this.props.view && !this.currentViewModeIsValid(this.props.resource) ) { - // Automatically switch to a preview if you can preview but there's no extracted text or OCR - if (this.props.resource.text) { + // Automatically switch to a valid view when the current one is not available + if (hasTextContent(this.props.resource)) { this.props.setResourceView("text"); } else if (this.props.resource.ocr) { const languages = Object.keys(this.props.resource.ocr); @@ -136,7 +137,8 @@ class PreviewSwitcher extends React.Component { shortcut={keyboardShortcuts.showPreview} func={this.showPreview} /> - {this.props.resource.text && !this.props.resource.transcript ? ( + {hasTextContent(this.props.resource) && + !this.props.resource.transcript ? ( ): Resource { + return { + uri: "test/doc.pdf", + type: "blob", + isExpandable: false, + processingStage: { type: "processed" }, + extracted: true, + mimeTypes: ["application/pdf"], + fileSize: 1024, + parents: [], + children: [], + comments: [], + previewStatus: "disabled", + text: makeHighlightableText(""), + ...overrides, + } as Resource; +} + +describe("hasTextContent", () => { + test("returns true when text has content", () => { + const resource = makeResource({ + text: makeHighlightableText("Hello world"), + }); + expect(hasTextContent(resource)).toBe(true); + }); + + test("returns false when text is empty", () => { + const resource = makeResource({ + text: makeHighlightableText(""), + }); + expect(hasTextContent(resource)).toBe(false); + }); + + test("returns false when text is only whitespace", () => { + const resource = makeResource({ + text: makeHighlightableText(" \n\t "), + }); + expect(hasTextContent(resource)).toBe(false); + }); +}); + +describe("getDefaultView", () => { + test("returns undefined for non-blob resources", () => { + const resource = makeResource({ + text: makeHighlightableText("content"), + type: "file", + }); + expect(getDefaultView(resource)).toBeUndefined(); + }); + + test("returns 'text' when document has text content", () => { + const resource = makeResource({ + text: makeHighlightableText("Hello world"), + }); + expect(getDefaultView(resource)).toBe("text"); + }); + + test("returns transcript view when transcript exists", () => { + const resource = makeResource({ + text: makeHighlightableText(""), + transcript: { + english: makeHighlightableText("transcript content"), + }, + }); + expect(getDefaultView(resource)).toBe("transcript.english"); + }); + + test("returns first OCR language when text is empty but OCR has content", () => { + const resource = makeResource({ + text: makeHighlightableText(""), + ocr: { + english: makeHighlightableText("OCR text"), + }, + }); + expect(getDefaultView(resource)).toBe("ocr.english"); + }); + + test("skips empty OCR entries and returns first non-empty one", () => { + const resource = makeResource({ + text: makeHighlightableText(""), + ocr: { + french: makeHighlightableText(""), + english: makeHighlightableText("OCR text"), + }, + }); + expect(getDefaultView(resource)).toBe("ocr.english"); + }); + + test("returns 'preview' when text is empty, no usable OCR, but preview is available", () => { + const resource = makeResource({ + text: makeHighlightableText(""), + previewStatus: "pass_through", + }); + expect(getDefaultView(resource)).toBe("preview"); + }); + + test("returns 'preview' when text is empty and all OCR entries are also empty", () => { + const resource = makeResource({ + text: makeHighlightableText(""), + ocr: { + english: makeHighlightableText(""), + }, + previewStatus: "pdf_generated", + }); + expect(getDefaultView(resource)).toBe("preview"); + }); + + test("returns 'text' as last resort when text is empty and preview is disabled", () => { + const resource = makeResource({ + text: makeHighlightableText(""), + previewStatus: "disabled", + }); + expect(getDefaultView(resource)).toBe("text"); + }); + + test("returns undefined when resource.text is undefined", () => { + const resource = makeResource({ + text: undefined as unknown as HighlightableText, + }); + expect(getDefaultView(resource)).toBeUndefined(); + }); +}); diff --git a/frontend/src/js/util/resourceUtils.ts b/frontend/src/js/util/resourceUtils.ts index 6f6efc1d..fed944f9 100644 --- a/frontend/src/js/util/resourceUtils.ts +++ b/frontend/src/js/util/resourceUtils.ts @@ -77,6 +77,10 @@ export function getCurrentResource(prefix: string): string { return window.location.pathname.split("/").slice(2).join("/"); } +export function hasTextContent(resource: Resource): boolean { + return resource.text.contents.trim() !== ""; +} + export function getDefaultView(resource: Resource): string | undefined { if (resource.type !== "blob") { return undefined; @@ -93,10 +97,10 @@ export function getDefaultView(resource: Resource): string | undefined { return undefined; } - if (resource.text.contents.trim().length === 0 && resource.ocr) { + if (!hasTextContent(resource) && resource.ocr) { const ocrEntries = Object.entries(resource.ocr); const firstNonEmptyEntry = ocrEntries.find( - ([_, { contents }]) => contents.trim().length > 0, + ([_, { contents }]) => contents.trim() !== "", ); if (firstNonEmptyEntry) { @@ -104,6 +108,11 @@ export function getDefaultView(resource: Resource): string | undefined { } } + // If text is empty and there's no usable OCR, prefer preview over an empty text view + if (!hasTextContent(resource) && resource.previewStatus !== "disabled") { + return "preview"; + } + return "text"; }