Skip to content

refactor(file-viewer): replace react-doc-viewer with in-house renderers#95

Closed
kemuru wants to merge 2 commits into
mainfrom
fix/refactor-react-doc-viewer-library
Closed

refactor(file-viewer): replace react-doc-viewer with in-house renderers#95
kemuru wants to merge 2 commits into
mainfrom
fix/refactor-react-doc-viewer-library

Conversation

@kemuru

@kemuru kemuru commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

PR-Codex overview

This PR focuses on enhancing the file viewer component by adding new renderers for various file types, improving the handling of different file formats, and updating the component structure to ensure better security and usability.

Detailed summary

  • Renamed SvgDocRenderer to SvgViewer.
  • Added VideoViewer, TextViewer, and CsvViewer components.
  • Introduced ViewerMessage for consistent loading/error messages.
  • Updated FileViewer to utilize new renderers and improved file type detection.
  • Enhanced DownloadButton functionality for safer file downloads.
  • Updated dependencies in package.json.

The following files were skipped due to too many changes: yarn.lock

✨ Ask PR-Codex anything about this PR by commenting with /codex {your question}

Summary by CodeRabbit

  • New Features
    • Added built-in inline viewers for CSV, PDF (with zoom controls), Markdown, plain text, images, SVG, and video.
    • Implemented a safe, optimized download button for supported file links.
    • Improved file-type detection with async fallback and safer handling for special URL formats.
  • Bug Fixes
    • Added consistent loading, error, and empty states across viewers.
  • Refactor
    • Replaced the previous embedded document viewer with a streamlined in-app renderer selection flow and lazy loading.

@netlify

netlify Bot commented Jul 9, 2026

Copy link
Copy Markdown

Deploy Preview for kleros-v2-ui-storybook ready!

Name Link
🔨 Latest commit ab47d63
🔍 Latest deploy log https://app.netlify.com/projects/kleros-v2-ui-storybook/deploys/6a4fab00cdd32c000890796d
😎 Deploy Preview https://deploy-preview-95--kleros-v2-ui-storybook.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

Replaces the @cyntler/react-doc-viewer dependency with a custom in-app file viewer system. New renderer components for PDF, CSV, Markdown, text, image, SVG, and video are added along with useFileText/useFileType hooks, a shared ViewerMessage status component, a DownloadButton, and new type definitions. index.tsx is rewritten to gate rendering by URL safety and lazily select renderers.

Changes

File Viewer Rewrite

Layer / File(s) Summary
Types, dependencies, and shared hooks
src/lib/file-viewer/types.ts, src/lib/file-viewer/use-file-text.ts, src/lib/file-viewer/use-file-type.ts, package.json
Adds FileKind, FileRendererProps, FileViewerConfig types; useFileText fetches text with abort handling; useFileType detects file kind via extension/MIME/HEAD sniffing; dependencies swap @cyntler/react-doc-viewer for papaparse, pdfjs-dist, react-pdf.
Shared status component
src/lib/file-viewer/status.tsx
Adds ViewerMessage, a reusable centered message container used across renderers.
Individual format renderers
src/lib/file-viewer/csv-viewer.tsx, text-viewer.tsx, markdown-viewer.tsx, pdf-viewer.tsx, image-viewer.tsx, svg-viewer.tsx, video-viewer.tsx, src/stories/file-viewer.stories.tsx
Adds CsvViewer (Papa.parse table), TextViewer (escaped <pre> text), MarkdownViewer (react-markdown without rehype-raw), PdfViewer (react-pdf with zoom toolbar), ImageViewer/SvgViewer (<img> with checkerboard background), and VideoViewer (native <video>); updates a story comment.
Download button
src/lib/file-viewer/download-button.tsx
Adds DownloadButton, computing IPFS-aware hrefs and using credential-less fetch plus object-URL fallback for cross-origin downloads.
FileViewer orchestration and URL safety gating
src/lib/file-viewer/index.tsx
Rewrites FileViewer to gate rendering via URL scheme/MIME allowlists, select lazy-loaded renderers by detected kind, wrap in Suspense, conditionally show DownloadButton, and update NoRendererFallback to accept uri.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

  • kleros/ui-components-library#87: Overlapping changes to src/lib/file-viewer/index.tsx and markdown-viewer.tsx, altering how Markdown is rendered and how FileViewer selects/initializes renderers.

Poem

A bunny hops through viewer code,
No more doc-viewer's heavy load 🐇
CSVs, PDFs, and text unfold,
Safe URLs gate what's shown, I'm told
Download buttons hop with glee—
New file views, as clean can be! 🥕

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: replacing react-doc-viewer with custom file-viewer renderers.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/refactor-react-doc-viewer-library

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (4)
src/lib/file-viewer/svg-viewer.tsx (1)

17-20: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

No transparency checkerboard, unlike ImageViewer.

Transparent SVGs will render on plain background here instead of the checkerboard ImageViewer uses for transparent rasters, which is a minor visual inconsistency between the two renderers.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/file-viewer/svg-viewer.tsx` around lines 17 - 20, The SVG viewer
container currently renders transparent SVGs on a plain background, unlike
ImageViewer’s checkerboard treatment. Update svg-viewer.tsx in the SVG rendering
container to use the same transparency checkerboard background styling/pattern
as ImageViewer so both renderers behave consistently for transparent content.
src/lib/file-viewer/pdf-viewer.tsx (1)

62-63: 🚀 Performance & Scalability | 🔵 Trivial | 🏗️ Heavy lift

All PDF pages render simultaneously with no virtualization.

For large multi-page PDFs (common for legal/evidence documents), rendering every page unconditionally can cause heavy DOM/memory usage and a sluggish scroll experience. Consider windowing (e.g., only rendering pages near the viewport) or lazy-mounting pages as they scroll into view.

Also applies to: 95-111

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/file-viewer/pdf-viewer.tsx` around lines 62 - 63, The PDF viewer
currently renders every page at once in PdfViewer, which can cause heavy DOM and
memory usage for large documents. Update the page rendering logic to use
virtualization or lazy mounting so only pages near the viewport are rendered,
and wire it through the existing PdfViewer state and page map/rendering flow
that uses numPages and zoom. Prefer a windowing approach tied to scroll position
or intersection visibility rather than unconditional page rendering.
src/lib/file-viewer/csv-viewer.tsx (1)

14-18: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Papa.parse errors are silently ignored.

Papa.parse returns a errors array for malformed rows (e.g., inconsistent field counts), but only parsed.data is used. A CSV with parse errors will render whatever rows Papa managed to extract with no indication anything went wrong, which can be confusing when the displayed table looks incomplete or malformed.

♻️ Surface parse errors
-  const rows = useMemo<string[][]>(() => {
+  const [parseFailed, setParseFailed] = useState(false);
+  const rows = useMemo<string[][]>(() => {
     if (!text) return [];
     const parsed = Papa.parse<string[]>(text.trim(), { skipEmptyLines: true });
+    if (parsed.errors.length) setParseFailed(true);
     return parsed.data;
   }, [text]);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/file-viewer/csv-viewer.tsx` around lines 14 - 18, Papa.parse errors
are currently ignored in the CSV viewer, so malformed input can render without
any warning. Update the csv-viewer.tsx logic in the useMemo block that builds
rows from Papa.parse to inspect parsed.errors as well as parsed.data, and
surface a parse-failure state when errors are present. Use the existing CSV
viewer flow in CsvViewer to either collect and expose the error details or fall
back to an empty/error state so the UI can indicate the parse problem instead of
silently showing partial rows.
src/lib/file-viewer/use-file-text.ts (1)

22-43: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Solid abort-safe fetch hook. AbortController cleanup and guard checks (controller.signal.aborted) correctly prevent stale-state updates after unmount/uri-change.

One gap: there's no fetch timeout, so if the network hangs, loading stays true indefinitely and the renderer shows "Loading…" forever with no way to recover short of unmounting. Consider racing the fetch against a timeout that flips error: true.

♻️ Add a fetch timeout
   useEffect(() => {
     const controller = new AbortController();
     setState({ text: null, loading: true, error: false });
+    const timeoutId = setTimeout(() => controller.abort(), 15000);

     fetch(uri, { signal: controller.signal })
       .then((res) => {
         if (!res.ok) throw new Error(`HTTP ${res.status}`);
         return res.text();
       })
       .then((text) => {
         if (!controller.signal.aborted) {
           setState({ text, loading: false, error: false });
         }
       })
       .catch(() => {
         if (!controller.signal.aborted) {
           setState({ text: null, loading: false, error: true });
+        } else {
+          setState({ text: null, loading: false, error: true });
         }
       });

-    return () => controller.abort();
+    return () => {
+      clearTimeout(timeoutId);
+      controller.abort();
+    };
   }, [uri]);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/file-viewer/use-file-text.ts` around lines 22 - 43, The abort-safe
fetch logic in useFileText still has no timeout, so a hanging request can leave
loading stuck forever. Update the useEffect in useFileText to race fetch against
a timeout (using the existing AbortController cleanup) and, on timeout, abort
the request and set the state to a recoverable error state with loading false
and error true. Keep the stale-update guards based on controller.signal.aborted
so uri changes and unmounts remain safe.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/lib/file-viewer/download-button.tsx`:
- Around line 51-68: The download flow in DownloadButton still allows `fileName`
to be missing, which causes the native download attribute to be omitted and the
same-origin/blob/data branches to fall back to opening a new tab. Update the
`DownloadButton` component so it always derives a fallback name (for example via
`fileNameFromUrl(url)`) and uses that value consistently when building `href`
and when setting the `download` attribute, keeping the behavior aligned across
the `useMemo` URL handling and the button render logic.

In `@src/lib/file-viewer/image-viewer.tsx`:
- Around line 24-40: The static ids in ImageViewer are duplicated across viewer
instances and conflict with SvgViewer, so remove the hardcoded
id="image-renderer" and id="image-img" from ImageViewer or replace them with
non-DOM-unique test hooks such as data-testid. Keep the existing styling via
className/checkerboard intact, and make the same adjustment anywhere the shared
viewer pattern relies on these ids.

In `@src/lib/file-viewer/index.tsx`:
- Around line 139-140: Update the JSDoc for the file viewer component to include
video as a supported format, since FileViewer now wires VideoViewer. Keep the
existing description in sync with the actual supported types listed by the
component so the docs mention PDFs, images, SVG, markdown, plaintext, CSV, and
video.

In `@src/lib/file-viewer/pdf-viewer.tsx`:
- Around line 57-63: The initial zoom state in PdfViewer is taken directly from
defaultZoom, so it can start outside the allowed range before any user
interaction. Clamp the initial value used by the useState for zoom to MIN_ZOOM
and MAX_ZOOM, matching the behavior already enforced by changeZoom, and ensure
config.pdfDefaultZoom cannot initialize PdfViewer outside bounds.

---

Nitpick comments:
In `@src/lib/file-viewer/csv-viewer.tsx`:
- Around line 14-18: Papa.parse errors are currently ignored in the CSV viewer,
so malformed input can render without any warning. Update the csv-viewer.tsx
logic in the useMemo block that builds rows from Papa.parse to inspect
parsed.errors as well as parsed.data, and surface a parse-failure state when
errors are present. Use the existing CSV viewer flow in CsvViewer to either
collect and expose the error details or fall back to an empty/error state so the
UI can indicate the parse problem instead of silently showing partial rows.

In `@src/lib/file-viewer/pdf-viewer.tsx`:
- Around line 62-63: The PDF viewer currently renders every page at once in
PdfViewer, which can cause heavy DOM and memory usage for large documents.
Update the page rendering logic to use virtualization or lazy mounting so only
pages near the viewport are rendered, and wire it through the existing PdfViewer
state and page map/rendering flow that uses numPages and zoom. Prefer a
windowing approach tied to scroll position or intersection visibility rather
than unconditional page rendering.

In `@src/lib/file-viewer/svg-viewer.tsx`:
- Around line 17-20: The SVG viewer container currently renders transparent SVGs
on a plain background, unlike ImageViewer’s checkerboard treatment. Update
svg-viewer.tsx in the SVG rendering container to use the same transparency
checkerboard background styling/pattern as ImageViewer so both renderers behave
consistently for transparent content.

In `@src/lib/file-viewer/use-file-text.ts`:
- Around line 22-43: The abort-safe fetch logic in useFileText still has no
timeout, so a hanging request can leave loading stuck forever. Update the
useEffect in useFileText to race fetch against a timeout (using the existing
AbortController cleanup) and, on timeout, abort the request and set the state to
a recoverable error state with loading false and error true. Keep the
stale-update guards based on controller.signal.aborted so uri changes and
unmounts remain safe.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2c4eb886-b850-4f3b-9479-a34070405ab0

📥 Commits

Reviewing files that changed from the base of the PR and between b5d2b6c and 9e8ea5a.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (15)
  • package.json
  • src/lib/file-viewer/csv-viewer.tsx
  • src/lib/file-viewer/download-button.tsx
  • src/lib/file-viewer/image-viewer.tsx
  • src/lib/file-viewer/index.tsx
  • src/lib/file-viewer/markdown-viewer.tsx
  • src/lib/file-viewer/pdf-viewer.tsx
  • src/lib/file-viewer/status.tsx
  • src/lib/file-viewer/svg-viewer.tsx
  • src/lib/file-viewer/text-viewer.tsx
  • src/lib/file-viewer/types.ts
  • src/lib/file-viewer/use-file-text.ts
  • src/lib/file-viewer/use-file-type.ts
  • src/lib/file-viewer/video-viewer.tsx
  • src/stories/file-viewer.stories.tsx

Comment thread src/lib/file-viewer/download-button.tsx
Comment thread src/lib/file-viewer/image-viewer.tsx
Comment thread src/lib/file-viewer/index.tsx Outdated
Comment thread src/lib/file-viewer/pdf-viewer.tsx Outdated
@sonarqubecloud

sonarqubecloud Bot commented Jul 9, 2026

Copy link
Copy Markdown

@kemuru

kemuru commented Jul 14, 2026

Copy link
Copy Markdown
Contributor Author

turns out it's not needed

@kemuru kemuru closed this Jul 14, 2026
@kemuru
kemuru deleted the fix/refactor-react-doc-viewer-library branch July 14, 2026 18:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant