diff --git a/package.json b/package.json index bf32583..b9e7dbc 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "@rolemodel/spider", "description": "Shared high level web components for RoleModel Software and beyond", "packageManager": "yarn@4.12.0", - "version": "0.0.10", + "version": "0.0.11", "author": "RoleModel Software", "license": "MIT", "type": "module", diff --git a/src/components/pdf-viewer/canvas/pdf-canvas.js b/src/components/pdf-viewer/canvas/pdf-canvas.js index 9698a18..40e1e22 100644 --- a/src/components/pdf-viewer/canvas/pdf-canvas.js +++ b/src/components/pdf-viewer/canvas/pdf-canvas.js @@ -124,7 +124,7 @@ export default class PDFCanvas extends PDFViewerComponent { .searchMatches=${this.context?.searchMatches || []} .currentMatchIndex=${this.context?.currentMatchIndex ?? -1}> - `)}) + `)} ` } diff --git a/src/components/pdf-viewer/page/pdf-page.js b/src/components/pdf-viewer/page/pdf-page.js index ba77ad5..108f451 100644 --- a/src/components/pdf-viewer/page/pdf-page.js +++ b/src/components/pdf-viewer/page/pdf-page.js @@ -65,14 +65,16 @@ export default class PDFPage extends PDFViewerComponent { const scaledViewport = this.page.getViewport({ scale: this.scale * devicePixelRatio }) + let renderTask + try { - this._renderTask = this.page.render({ + renderTask = this._renderTask = this.page.render({ canvasContext, viewport: scaledViewport }) - await this._renderTask.promise - this._renderTask = null + await renderTask.promise + if (this._renderTask === renderTask) this._renderTask = null const textLayerDiv = this.shadowRoot.querySelector('.text-layer') textLayerDiv.style.width = `${viewport.width}px` @@ -91,7 +93,7 @@ export default class PDFPage extends PDFViewerComponent { if (error.name !== 'RenderingCancelledException') { console.error('Error rendering page:', error) } - this._renderTask = null + if (this._renderTask === renderTask) this._renderTask = null } } diff --git a/test/components/pdf-page.test.js b/test/components/pdf-page.test.js new file mode 100644 index 0000000..eba2726 --- /dev/null +++ b/test/components/pdf-page.test.js @@ -0,0 +1,58 @@ +import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest' +import '../../src/components/pdf-viewer/page/pdf-page.js' +import { createMockPage } from '../helpers/test-utils.js' + +function createPendingRenderTask() { + let reject + const task = { + promise: new Promise((_, r) => { reject = r }), + cancel: vi.fn(() => { + const error = new Error('Rendering cancelled') + error.name = 'RenderingCancelledException' + reject(error) + }) + } + return task +} + +const flushPromises = () => new Promise((resolve) => setTimeout(resolve)) + +describe('PDFPage Component', () => { + let element + let renderTasks + + beforeEach(async () => { + renderTasks = [] + const page = createMockPage(1) + page.render = vi.fn(() => { + const task = createPendingRenderTask() + renderTasks.push(task) + return task + }) + + element = document.createElement('rm-pdf-page') + document.body.appendChild(element) + await element.updateComplete + + element.page = page + await flushPromises() + }) + + afterEach(() => { + element.remove() + }) + + describe('Overlapping renders', () => { + it('cancels every superseded render so only the latest one draws', async () => { + element.renderPage() + await flushPromises() + element.renderPage() + await flushPromises() + + expect(renderTasks).toHaveLength(3) + expect(renderTasks[0].cancel).toHaveBeenCalled() + expect(renderTasks[1].cancel).toHaveBeenCalled() + expect(renderTasks[2].cancel).not.toHaveBeenCalled() + }) + }) +}) diff --git a/test/components/pdf-viewer.test.js b/test/components/pdf-viewer.test.js index 8fd2325..889ffa9 100644 --- a/test/components/pdf-viewer.test.js +++ b/test/components/pdf-viewer.test.js @@ -76,6 +76,17 @@ describe('PDFViewer Component', () => { expect(element.pdfDoc).toBeDefined() expect(element.totalPages).toBe(5) }) + + it('should render only pages in the canvas container', async () => { + element = await createViewer({ src: '/test.pdf', open: true }) + await waitForCondition(() => element.pdfDoc !== null) + + const canvas = element.shadowRoot.querySelector('rm-pdf-canvas') + await waitForCondition(() => canvas?.shadowRoot?.querySelectorAll('rm-pdf-page').length === 5) + + const container = canvas.shadowRoot.querySelector('.canvas-container') + expect(container.textContent.trim()).toBe('') + }) }) describe('Navigation', () => {