Skip to content

Commit

Permalink
fix: implement updates for usePDF hook (#2247)
Browse files Browse the repository at this point in the history
* implement updates for usePDF hooks

* setup simple test

* setup first test for usePDF hook

* deduplicate deps

* implement tests for update

* use custom environment

* remove mocks

* add changeset

* add no args version

* update `BlobProvider` component

* add tests for components

* fix typos
  • Loading branch information
jeetiss committed Jun 5, 2023
1 parent 373aac2 commit 6d408c8
Show file tree
Hide file tree
Showing 12 changed files with 850 additions and 161 deletions.
22 changes: 22 additions & 0 deletions .changeset/two-dodos-roll.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
'@react-pdf/renderer': patch
---

### updates for `usePDF` hook

`update` function takes the new document and renders it:

```jsx
const PdfView = () => {
const [pdf, update] = usePdf();

useEffect(() => {
update(<PDFDocument />);
}, []);

if (pdf.loading) return null;

// use your PDF here
return <>{pdf.url}</>;
};
```
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"@rollup/plugin-json": "^4.1.0",
"@rollup/plugin-node-resolve": "^13.0.6",
"@rollup/plugin-replace": "^3.0.0",
"@testing-library/react": "^14.0.0",
"babel-eslint": "^10.0.1",
"babel-jest": "^29.5.0",
"babel-plugin-add-module-exports": "^1.0.0",
Expand All @@ -56,15 +57,14 @@
"eslint-plugin-react-hooks": "^2.5.1",
"husky": "^7.0.4",
"jest": "^29.5.0",
"jest-environment-jsdom": "^29.5.0",
"jest-image-snapshot": "^6.1.0",
"lerna": "^3.21.0",
"lint-staged": "^10.5.4",
"mock-fs": "^4.8.0",
"mockdate": "^2.0.2",
"pdfjs-dist": "3.1.81",
"prettier": "^1.16.4",
"react": "^16.8.6",
"react-dom": "^16.8.6",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"rimraf": "^2.6.3",
"rollup": "^2.60.1",
"rollup-plugin-ignore": "^1.0.10",
Expand Down
4 changes: 2 additions & 2 deletions packages/renderer/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -489,8 +489,8 @@ declare namespace ReactPDF {
* @platform web
*/
function usePDF(options: {
document: React.ReactElement<DocumentProps>;
}): [UsePDFInstance, () => void];
document?: React.ReactElement<DocumentProps>;
}): [UsePDFInstance, (newDocument: React.ReactElement<DocumentProps>) => void];

const Font: FontStore;

Expand Down
4 changes: 4 additions & 0 deletions packages/renderer/jest.config.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
module.exports = {
testRegex: 'tests/.*?(test)\\.js$',
setupFilesAfterEnv: ['<rootDir>/setup.jest.js'],
transformIgnorePatterns: [
'/node_modules/',
'<rootDir>/tests/environment/jsdom.js',
],
};
4 changes: 2 additions & 2 deletions packages/renderer/src/dom/BlobProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import { useEffect } from 'react';
import usePDF from './usePDF';

export const BlobProvider = ({ document: doc, children }) => {
const [instance, updateInstance] = usePDF({ document: doc });
const [instance, updateInstance] = usePDF();

useEffect(updateInstance, [doc]);
useEffect(() => updateInstance(doc), [doc]);

if (!doc) {
console.warn('You should pass a valid document to BlobProvider');
Expand Down
4 changes: 2 additions & 2 deletions packages/renderer/src/dom/PDFDownloadLink.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ export const PDFDownloadLink = ({
fileName = 'document.pdf',
onClick,
}) => {
const [instance, updateInstance] = usePDF({ document: doc });
const [instance, updateInstance] = usePDF();

useEffect(updateInstance, [children]);
useEffect(() => updateInstance(doc), [doc]);

if (!doc) {
console.warn('You should pass a valid document to PDFDownloadLink');
Expand Down
4 changes: 2 additions & 2 deletions packages/renderer/src/dom/PDFViewer.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ export const PDFViewer = ({
showToolbar = true,
...props
}) => {
const [instance, updateInstance] = usePDF({ document: children });
const [instance, updateInstance] = usePDF();

useEffect(updateInstance, [children]);
useEffect(() => updateInstance(children), [children]);

const src = instance.url
? `${instance.url}#toolbar=${showToolbar ? 1 : 0}`
Expand Down
18 changes: 10 additions & 8 deletions packages/renderer/src/dom/usePDF.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
/* eslint-disable no-console */

import queue from 'queue';
import { useState, useRef, useEffect } from 'react';
import { useState, useRef, useEffect, useCallback } from 'react';

import { pdf } from '../index';

export const usePDF = ({ document }) => {
export const usePDF = ({ document } = {}) => {
const pdfInstance = useRef(null);

const [state, setState] = useState({
url: null,
blob: null,
error: null,
loading: false,
loading: !!document,
});

// Setup rendering queue
Expand All @@ -29,7 +29,7 @@ export const usePDF = ({ document }) => {

const onRenderFailed = error => {
console.error(error);
setState(prev => ({ ...prev, error }));
setState(prev => ({ ...prev, loading: false, error }));
};

const onRenderSuccessful = blob => {
Expand All @@ -43,7 +43,9 @@ export const usePDF = ({ document }) => {

pdfInstance.current = pdf();
pdfInstance.current.on('change', queueDocumentRender);
pdfInstance.current.updateContainer(document);
if (document) {
pdfInstance.current.updateContainer(document);
}

renderQueue.on('error', onRenderFailed);
renderQueue.on('success', onRenderSuccessful);
Expand All @@ -63,9 +65,9 @@ export const usePDF = ({ document }) => {
};
}, [state.url]);

const update = () => {
pdfInstance.current.updateContainer(document);
};
const update = useCallback(newDoc => {
pdfInstance.current.updateContainer(newDoc);
}, []);

return [state, update];
};
Expand Down
85 changes: 85 additions & 0 deletions packages/renderer/tests/components.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/**
* @jest-environment ./tests/environment/jsdom.js
*/

/* eslint-disable import/no-extraneous-dependencies */
import { render, waitFor, screen } from '@testing-library/react';
import {
BlobProvider,
PDFViewer,
PDFDownloadLink,
Document,
Page,
Text,
} from '../src/dom';

const TestDocument = ({ title = 'Default' }) => (
<Document title={title}>
<Page>
<Text>Hello tests</Text>
</Page>
</Document>
);

describe('BlobProvider', () => {
it('works', async () => {
const renderFunction = jest.fn();

render(
<BlobProvider document={<TestDocument />}>{renderFunction}</BlobProvider>,
);

await waitFor(() => expect(renderFunction).toBeCalledTimes(3));

expect(renderFunction).toHaveBeenCalledWith(
expect.objectContaining({
blob: expect.anything(),
url: expect.anything(),
error: null,
loading: false,
}),
);
});
});

describe('BlobProvider', () => {
it('works', async () => {
const renderFunction = jest.fn();

render(
<BlobProvider document={<TestDocument />}>{renderFunction}</BlobProvider>,
);

await waitFor(() => expect(renderFunction).toBeCalledTimes(3));

expect(renderFunction).toHaveBeenCalledWith(
expect.objectContaining({
blob: expect.anything(),
url: expect.anything(),
error: null,
loading: false,
}),
);
});
});

describe('BlobProvider', () => {
it('works', async () => {
const { container } = render(
<PDFViewer>
<TestDocument />
</PDFViewer>,
);

await waitFor(() => expect(container.querySelector('iframe')));
});
});

describe('PDFDownloadLink', () => {
it('works', async () => {
render(<PDFDownloadLink document={<TestDocument />}>test</PDFDownloadLink>);

const link = await screen.findByText('test');
expect(link.getAttribute('download')).toBe('document.pdf');
});
});
28 changes: 28 additions & 0 deletions packages/renderer/tests/environment/jsdom.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/* eslint-disable import/no-extraneous-dependencies */
const Environment = require('jest-environment-jsdom').default;
const { TextEncoder, TextDecoder } = require('util');

class CustomEnvironment extends Environment {
async setup() {
await super.setup();

if (typeof this.global.TextEncoder === 'undefined') {
this.global.TextEncoder = TextEncoder;
this.global.TextDecoder = TextDecoder;
}

if (typeof this.global.TextDecoder === 'undefined') {
this.global.TextDecoder = TextDecoder;
}

if (typeof this.global.URL.createObjectURL === 'undefined') {
this.global.URL.createObjectURL = blob => `[Blob - ${blob.size}]`;
}

if (typeof this.global.URL.revokeObjectURL === 'undefined') {
this.global.URL.revokeObjectURL = () => undefined;
}
}
}

module.exports = CustomEnvironment;
59 changes: 59 additions & 0 deletions packages/renderer/tests/usePDF.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/**
* @jest-environment ./tests/environment/jsdom.js
*/

/* eslint-disable import/no-extraneous-dependencies */
import { renderHook, waitFor, act } from '@testing-library/react';
import { usePDF, Document, Page, Text } from '../src/dom';

const TestDocument = ({ title = 'Default' }) => (
<Document title={title}>
<Page>
<Text>Hello tests</Text>
</Page>
</Document>
);

it('returns value, updater tuple', () => {
const { result } = renderHook(() => usePDF({ document: undefined }));

expect(Array.isArray(result.current)).toBeTruthy();
expect(result.current[0]).toMatchObject(
expect.objectContaining({
url: null,
blob: null,
error: null,
loading: false,
}),
);

expect(typeof result.current[1]).toBe('function');
});

it('works with no args', () => {
const { result } = renderHook(() => usePDF());

expect(Array.isArray(result.current)).toBeTruthy();
expect(typeof result.current[0]).toBe('object');
expect(typeof result.current[1]).toBe('function');
});

it('renders document', async () => {
const { result } = renderHook(() => usePDF({ document: <TestDocument /> }));

await waitFor(() => expect(result.current[0].loading).toBeFalsy());
});

it('updates document', async () => {
const { result } = renderHook(() => usePDF({ document: <TestDocument /> }));

await waitFor(() => expect(result.current[0].loading).toBeFalsy());

const pdfSize = result.current[0].blob.size;

act(() => result.current[1](<TestDocument title="Long long long title" />));

await waitFor(() => expect(result.current[0].loading).toBeFalsy());

expect(result.current[0].blob.size).not.toEqual(pdfSize);
});

0 comments on commit 6d408c8

Please sign in to comment.