Skip to content

Feat: Add download button config similar to print config - #3

Open
suparthghimire wants to merge 1 commit into
mehuljariwala:mainfrom
suparthghimire:main
Open

Feat: Add download button config similar to print config#3
suparthghimire wants to merge 1 commit into
mehuljariwala:mainfrom
suparthghimire:main

Conversation

@suparthghimire

@suparthghimire suparthghimire commented Apr 10, 2026

Copy link
Copy Markdown

Adds config.download.enableDownload in IConfig to enable or disable download button

Screenshot

image

Summary by CodeRabbit

  • New Features

    • Add config option to enable/disable downloads (enabled by default); toolbar and document UIs now hide download when disabled.
    • Improved download handling to support binary file data for direct client downloads.
  • Bug Fixes

    • Toolbar now only shows download/print controls when those actions are enabled.
  • Tests

    • Added UI tests validating download button visibility behavior.
  • Docs

    • Added story examples demonstrating enabled/disabled download states.

@coderabbitai

coderabbitai Bot commented Apr 10, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

Added a configurable download flag to the viewer config and updated multiple renderers and toolbar components to respect download.enableDownload; added tests and Storybook examples covering enabled/disabled download UI states.

Changes

Cohort / File(s) Summary
Configuration & Types
src/models.ts
Added IDownloadConfig with enableDownload?: boolean and added optional download?: IDownloadConfig to IConfig.
Toolbar & Shared Components
src/components/CommonToolbar.tsx, src/components/ProxyRenderer.tsx
Read config.download.enableDownload (defaulting to enabled when omitted) and conditionally render the download button; adjusted toolbar/group visibility logic and minor JSX/typing tweaks.
PDF Renderer
src/renderers/pdf/components/PDFControls.tsx
Gated download button on enableDownload; updated handleDownload to support ArrayBuffer fileData (Blob -> object URL) and derive fallback filenames; omit toolbar group when both download/print disabled.
DOCX & MS Office Renderers
src/renderers/docx/index.tsx, src/renderers/msdoc/index.tsx
Conditionally render the "Download File" link based on enableDownload; preserved existing download behavior when enabled.
Tests
src/__tests__/index.test.tsx
Added three RTL tests verifying "Download file" UI is absent when enableDownload is false, present by default when omitted, and present when true; each test asserts react-doc-viewer renders.
Stories
src/stories/features.stories.tsx
Added DownloadButton and DownloadButtonDisabled story exports demonstrating enabled/disabled download configs; formatting/JSX layout adjustments only.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I dug a tiny toggle in the ground,
A download hop now softly found,
Toolbars whisper yes or no,
Tests hop in to steal the show,
🥕📥 Hop on—config keeps things sound.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title accurately summarizes the main objective: adding a download button config option that mirrors the existing print config functionality.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/components/CommonToolbar.tsx`:
- Around line 154-155: The download toggle from CommonToolbar (enableDownload /
enablePrint) is not respected by the DOCX/MSDOC fallback UIs; update the DOCX
and MSDOC renderers so they check the same enableDownload flag before rendering
download links. Specifically, modify the docx renderer component
(src/renderers/docx/index.tsx) and the msdoc renderer component
(src/renderers/msdoc/index.tsx) to accept/consume the enableDownload prop (or
access the same config) and wrap the fallback download link markup in a
conditional that only renders when enableDownload is true; also apply the same
conditional to the other fallback block noted (around the 260-285 area) so all
download links are gated by enableDownload.
- Around line 157-160: The download logic casts currentDocument.fileData to
string which breaks when fileData is an ArrayBuffer; update handleDownload in
CommonToolbar.tsx (and the analogous handlers in PDFControls.tsx) to detect
ArrayBuffer vs string/URI: if fileData is an ArrayBuffer, create a Blob (with
appropriate mime/type if available), call URL.createObjectURL(blob) to use as
the download href, trigger the download with the computed filename, and then
revokeObjectURL; if fileData is a string/URI, proceed with fetch or use the URI
directly; remove the unsafe type cast `(fileData as string)` and ensure filename
fallback uses currentDocument.name or derived name.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5eed4765-56a9-4946-aade-c1098fc4fa2e

📥 Commits

Reviewing files that changed from the base of the PR and between 4d584b3 and f52c487.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (5)
  • src/__tests__/index.test.tsx
  • src/components/CommonToolbar.tsx
  • src/components/ProxyRenderer.tsx
  • src/models.ts
  • src/renderers/pdf/components/PDFControls.tsx

Comment thread src/components/CommonToolbar.tsx
Comment thread src/components/CommonToolbar.tsx

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/components/ProxyRenderer.tsx`:
- Around line 149-158: The download attribute currently uses
currentDocument?.uri which causes blob/signed/query URLs to be used as the saved
filename; update the LinkButton (id "no-renderer-download") to use the resolved
filename from the document contents instead — e.g. set the download prop to
currentDocument?.contents?.fileName (or the exact resolved fileName field on
Contents) while keeping href as currentDocument?.uri so the link still points to
the resource.

In `@src/renderers/pdf/components/PDFControls.tsx`:
- Around line 58-83: handlePrint incorrectly assumes currentDocument.fileData is
a string and calls .startsWith(), which breaks when fileData is an ArrayBuffer;
update handlePrint to first branch on the type of currentDocument.fileData: if
it's an ArrayBuffer, create a Blob using currentDocument.fileType ||
"application/pdf", createObjectURL, load that URL into a hidden iframe (or new
window), call print on the iframe/window, then revokeObjectURL; otherwise, only
call .startsWith() after confirming typeof fileData === "string" (or fall back
to currentDocument.uri). Apply the same type-safe ArrayBuffer handling to the
other print handler block referenced (the second handler at the other toolbar
section).

In `@src/stories/features.stories.tsx`:
- Around line 302-324: The two stories render identical states because both
DownloadButton and DownloadButtonDisabled pass download.enableDownload: false to
DocViewer; update DownloadButton to show the enabled/default behavior by either
removing the download property from the config or setting
download.enableDownload: true in the DownloadButton story (leave
DownloadButtonDisabled as-is), ensuring the DocViewer call in DownloadButton
reflects an enabled download state.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: e7babe41-1eeb-484a-b85f-cc97f85c312d

📥 Commits

Reviewing files that changed from the base of the PR and between f52c487 and 7ac251c.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (8)
  • src/__tests__/index.test.tsx
  • src/components/CommonToolbar.tsx
  • src/components/ProxyRenderer.tsx
  • src/models.ts
  • src/renderers/docx/index.tsx
  • src/renderers/msdoc/index.tsx
  • src/renderers/pdf/components/PDFControls.tsx
  • src/stories/features.stories.tsx
✅ Files skipped from review due to trivial changes (1)
  • src/tests/index.test.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/components/CommonToolbar.tsx

Comment on lines +149 to +158
{enableDownload && (
<LinkButton
id="no-renderer-download"
className="rdv-download-btn"
href={currentDocument?.uri}
download={currentDocument?.uri}
>
{t("downloadButtonLabel")}
</LinkButton>
)}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Use the resolved filename in the no-renderer download link.

download={currentDocument?.uri} makes blob URLs, signed URLs, and query strings show up as the saved filename in this fallback path. Contents already has the resolved fileName, so this button should use that instead.

💡 Proposed fix
           {enableDownload && (
             <LinkButton
               id="no-renderer-download"
               className="rdv-download-btn"
               href={currentDocument?.uri}
-              download={currentDocument?.uri}
+              download={fileName}
             >
               {t("downloadButtonLabel")}
             </LinkButton>
           )}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{enableDownload && (
<LinkButton
id="no-renderer-download"
className="rdv-download-btn"
href={currentDocument?.uri}
download={currentDocument?.uri}
>
{t("downloadButtonLabel")}
</LinkButton>
)}
{enableDownload && (
<LinkButton
id="no-renderer-download"
className="rdv-download-btn"
href={currentDocument?.uri}
download={fileName}
>
{t("downloadButtonLabel")}
</LinkButton>
)}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/ProxyRenderer.tsx` around lines 149 - 158, The download
attribute currently uses currentDocument?.uri which causes blob/signed/query
URLs to be used as the saved filename; update the LinkButton (id
"no-renderer-download") to use the resolved filename from the document contents
instead — e.g. set the download prop to currentDocument?.contents?.fileName (or
the exact resolved fileName field on Contents) while keeping href as
currentDocument?.uri so the link still points to the resource.

Comment on lines 58 to 83
const handleDownload = useCallback(() => {
const url = currentDocument?.fileData as string;
const name = currentDocument?.fileName || currentDocument?.uri || "download";
if (!currentDocument) return;

const fileData = currentDocument.fileData;
const name =
currentDocument.fileName ||
currentDocument.uri?.split("/").pop() ||
"download";

// Handle ArrayBuffer fileData
if (fileData instanceof ArrayBuffer) {
const mimeType = currentDocument.fileType || "application/pdf";
const blob = new Blob([fileData], { type: mimeType });
const blobUrl = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = blobUrl;
a.download = name;
a.click();
URL.revokeObjectURL(blobUrl);
return;
}

// Handle string (data URL or regular URL) or fall back to URI
const url =
(typeof fileData === "string" ? fileData : null) || currentDocument.uri;
if (!url) return;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

ArrayBuffer-backed PDFs still expose a broken print action.

handleDownload now supports currentDocument.fileData as an ArrayBuffer, but Line 104 in handlePrint still treats fileData as a string and calls .startsWith(). Because this toolbar section renders whenever any truthy fileData exists, enablePrint: true on an ArrayBuffer document falls into the catch path and prints the page chrome instead of the PDF.

🛠️ Proposed fix
-  const handlePrint = useCallback(async () => {
-    const fileData = currentDocument?.fileData as string | undefined;
+  const handlePrint = useCallback(async () => {
+    const fileData = currentDocument?.fileData;
     if (!fileData) return;

     let blobUrl: string | undefined;
     try {
       let blob: Blob;

-      if (fileData.startsWith("data:")) {
-        const res = await fetch(fileData);
-        blob = await res.blob();
-      } else {
-        const res = await fetch(fileData);
-        blob = await res.blob();
-      }
+      if (fileData instanceof ArrayBuffer) {
+        blob = new Blob([fileData], {
+          type: currentDocument?.fileType || "application/pdf",
+        });
+      } else {
+        const res = await fetch(fileData);
+        blob = await res.blob();
+      }

Also applies to: 157-184

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/renderers/pdf/components/PDFControls.tsx` around lines 58 - 83,
handlePrint incorrectly assumes currentDocument.fileData is a string and calls
.startsWith(), which breaks when fileData is an ArrayBuffer; update handlePrint
to first branch on the type of currentDocument.fileData: if it's an ArrayBuffer,
create a Blob using currentDocument.fileType || "application/pdf",
createObjectURL, load that URL into a hidden iframe (or new window), call print
on the iframe/window, then revokeObjectURL; otherwise, only call .startsWith()
after confirming typeof fileData === "string" (or fall back to
currentDocument.uri). Apply the same type-safe ArrayBuffer handling to the other
print handler block referenced (the second handler at the other toolbar
section).

Comment on lines +302 to +324
export const DownloadButton = () => (
<div style={{ height: "100vh" }}>
<DocViewer
documents={[{ uri: pdfMultiplePagesFile }]}
config={{
download: { enableDownload: false },
pdfVerticalScrollByDefault: false,
}}
/>
</div>
);

export const DownloadButtonDisabled = () => (
<div style={{ height: "100vh" }}>
<DocViewer
documents={[{ uri: pdfMultiplePagesFile }]}
config={{
download: { enableDownload: false },
pdfVerticalScrollByDefault: false,
}}
/>
</div>
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

DownloadButton and DownloadButtonDisabled currently show the same state.

Both stories set download.enableDownload: false, so Storybook never exposes the enabled/default behavior introduced by this PR. DownloadButton should enable the flag or omit it entirely.

💡 Proposed fix
 export const DownloadButton = () => (
   <div style={{ height: "100vh" }}>
     <DocViewer
       documents={[{ uri: pdfMultiplePagesFile }]}
       config={{
-        download: { enableDownload: false },
         pdfVerticalScrollByDefault: false,
       }}
     />
   </div>
 );
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export const DownloadButton = () => (
<div style={{ height: "100vh" }}>
<DocViewer
documents={[{ uri: pdfMultiplePagesFile }]}
config={{
download: { enableDownload: false },
pdfVerticalScrollByDefault: false,
}}
/>
</div>
);
export const DownloadButtonDisabled = () => (
<div style={{ height: "100vh" }}>
<DocViewer
documents={[{ uri: pdfMultiplePagesFile }]}
config={{
download: { enableDownload: false },
pdfVerticalScrollByDefault: false,
}}
/>
</div>
);
export const DownloadButton = () => (
<div style={{ height: "100vh" }}>
<DocViewer
documents={[{ uri: pdfMultiplePagesFile }]}
config={{
pdfVerticalScrollByDefault: false,
}}
/>
</div>
);
export const DownloadButtonDisabled = () => (
<div style={{ height: "100vh" }}>
<DocViewer
documents={[{ uri: pdfMultiplePagesFile }]}
config={{
download: { enableDownload: false },
pdfVerticalScrollByDefault: false,
}}
/>
</div>
);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/stories/features.stories.tsx` around lines 302 - 324, The two stories
render identical states because both DownloadButton and DownloadButtonDisabled
pass download.enableDownload: false to DocViewer; update DownloadButton to show
the enabled/default behavior by either removing the download property from the
config or setting download.enableDownload: true in the DownloadButton story
(leave DownloadButtonDisabled as-is), ensuring the DocViewer call in
DownloadButton reflects an enabled download state.

@mehuljariwala mehuljariwala left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Please fix all code-rabbit comment

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.

2 participants