mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2026-09-09 18:03:21 +00:00
AAE-43707 Add storybook stories for pdf viewer family
Adds Storybook coverage for pdf-viewer, pdf-viewer-thumb and pdf-viewer-thumbnails. The thumbnail stories use lightweight mocks that emulate the pdf.js PDFViewer page descriptor surface so the panels can be rendered in isolation.
This commit is contained in:
+119
@@ -0,0 +1,119 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { applicationConfig, Meta, StoryObj, moduleMetadata } from '@storybook/angular';
|
||||||
|
import { PdfThumbComponent } from './pdf-viewer-thumb.component';
|
||||||
|
import { provideStoryCore } from '../../../stories/core-story.providers';
|
||||||
|
|
||||||
|
const buildMockPage = (id: number, color: string) => ({
|
||||||
|
id,
|
||||||
|
getWidth: () => 91,
|
||||||
|
getHeight: () => 114,
|
||||||
|
getPage: () =>
|
||||||
|
Promise.resolve({
|
||||||
|
getViewport: ({ scale }: { scale: number }) => ({ width: 91 * scale, height: 114 * scale }),
|
||||||
|
render: ({ canvasContext, viewport }: { canvasContext: CanvasRenderingContext2D; viewport: { width: number; height: number } }) => {
|
||||||
|
canvasContext.fillStyle = color;
|
||||||
|
canvasContext.fillRect(0, 0, viewport.width, viewport.height);
|
||||||
|
canvasContext.fillStyle = '#333';
|
||||||
|
canvasContext.font = '14px sans-serif';
|
||||||
|
canvasContext.fillText(`Page ${id}`, 16, 24);
|
||||||
|
return { promise: Promise.resolve() };
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
const EMPTY_PAGE = {
|
||||||
|
id: 0,
|
||||||
|
getWidth: () => 91,
|
||||||
|
getHeight: () => 114,
|
||||||
|
getPage: () => new Promise(() => undefined)
|
||||||
|
};
|
||||||
|
|
||||||
|
type PdfThumbStoryArgs = PdfThumbComponent & {
|
||||||
|
selected?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
const meta: Meta<PdfThumbStoryArgs> = {
|
||||||
|
component: PdfThumbComponent,
|
||||||
|
title: 'Core/Viewer/PDF Viewer Thumb',
|
||||||
|
decorators: [
|
||||||
|
moduleMetadata({
|
||||||
|
imports: [PdfThumbComponent]
|
||||||
|
}),
|
||||||
|
applicationConfig({
|
||||||
|
providers: [...provideStoryCore()]
|
||||||
|
})
|
||||||
|
],
|
||||||
|
parameters: {
|
||||||
|
docs: {
|
||||||
|
description: {
|
||||||
|
component: `Single thumbnail tile rendered for a page in the PDF Viewer thumbnails panel. Renders a small canvas preview from the supplied \`page\` descriptor.`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
argTypes: {
|
||||||
|
page: {
|
||||||
|
control: false,
|
||||||
|
description: 'Page descriptor providing `getPage()`, `getWidth()` and `getHeight()` used to render the thumbnail.',
|
||||||
|
table: { type: { summary: 'PdfThumbnailPage' } }
|
||||||
|
},
|
||||||
|
selected: {
|
||||||
|
control: 'boolean',
|
||||||
|
description: 'Visual flag toggled by the parent thumbnails list when the page is the active one.',
|
||||||
|
table: { category: 'Storybook Helpers' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default meta;
|
||||||
|
type Story = StoryObj<PdfThumbStoryArgs>;
|
||||||
|
|
||||||
|
export const Empty: Story = {
|
||||||
|
args: {
|
||||||
|
page: EMPTY_PAGE,
|
||||||
|
selected: false
|
||||||
|
},
|
||||||
|
render: (args) => ({
|
||||||
|
props: args,
|
||||||
|
template: `<adf-pdf-thumb [page]="page" style="display:inline-block; padding:8px;" />`
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Populated: Story = {
|
||||||
|
args: {
|
||||||
|
page: buildMockPage(1, '#e3f2fd'),
|
||||||
|
selected: false
|
||||||
|
},
|
||||||
|
render: (args) => ({
|
||||||
|
props: args,
|
||||||
|
template: `<adf-pdf-thumb [page]="page" style="display:inline-block; padding:8px;" />`
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Selected: Story = {
|
||||||
|
args: {
|
||||||
|
page: buildMockPage(2, '#bbdefb'),
|
||||||
|
selected: true
|
||||||
|
},
|
||||||
|
render: (args) => ({
|
||||||
|
props: args,
|
||||||
|
template: `<div [style.outline]="selected ? '2px solid #1976d2' : 'none'" style="display:inline-block; padding:8px;">
|
||||||
|
<adf-pdf-thumb [page]="page" />
|
||||||
|
</div>`
|
||||||
|
})
|
||||||
|
};
|
||||||
+127
@@ -0,0 +1,127 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { applicationConfig, Meta, StoryObj, moduleMetadata } from '@storybook/angular';
|
||||||
|
import { PdfThumbListComponent } from './pdf-viewer-thumbnails.component';
|
||||||
|
import { provideStoryCore } from '../../../stories/core-story.providers';
|
||||||
|
|
||||||
|
const buildPdfViewerMock = (totalPages: number) => {
|
||||||
|
const pageHandlers: Array<(event: { pageNumber: number }) => void> = [];
|
||||||
|
|
||||||
|
const buildMockPage = (id: number) => ({
|
||||||
|
id,
|
||||||
|
width: 91,
|
||||||
|
height: 114
|
||||||
|
});
|
||||||
|
|
||||||
|
const buildPageProxy = (id: number) => ({
|
||||||
|
getViewport: ({ scale }: { scale: number }) => ({ width: 91 * scale, height: 114 * scale }),
|
||||||
|
render: ({ canvasContext, viewport }: { canvasContext: CanvasRenderingContext2D; viewport: { width: number; height: number } }) => {
|
||||||
|
canvasContext.fillStyle = id % 2 ? '#e3f2fd' : '#f3e5f5';
|
||||||
|
canvasContext.fillRect(0, 0, viewport.width, viewport.height);
|
||||||
|
canvasContext.fillStyle = '#333';
|
||||||
|
canvasContext.font = '12px sans-serif';
|
||||||
|
canvasContext.fillText(`Page ${id}`, 12, 20);
|
||||||
|
return { promise: Promise.resolve() };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
currentPageNumber: 1,
|
||||||
|
pagesCount: totalPages,
|
||||||
|
_pages: Array.from({ length: totalPages }, (_, i) => buildMockPage(i + 1)),
|
||||||
|
pdfDocument: {
|
||||||
|
getPage: (id: number) => Promise.resolve(buildPageProxy(id))
|
||||||
|
},
|
||||||
|
eventBus: {
|
||||||
|
on: (event: string, handler: (e: { pageNumber: number }) => void) => {
|
||||||
|
if (event === 'pagechanging') {
|
||||||
|
pageHandlers.push(handler);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
off: () => undefined
|
||||||
|
}
|
||||||
|
} as any;
|
||||||
|
};
|
||||||
|
|
||||||
|
type PdfThumbnailsStoryArgs = PdfThumbListComponent & {
|
||||||
|
pageCount?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
const meta: Meta<PdfThumbnailsStoryArgs> = {
|
||||||
|
component: PdfThumbListComponent,
|
||||||
|
title: 'Core/Viewer/PDF Viewer Thumbnails',
|
||||||
|
decorators: [
|
||||||
|
moduleMetadata({
|
||||||
|
imports: [PdfThumbListComponent]
|
||||||
|
}),
|
||||||
|
applicationConfig({
|
||||||
|
providers: [...provideStoryCore()]
|
||||||
|
})
|
||||||
|
],
|
||||||
|
parameters: {
|
||||||
|
docs: {
|
||||||
|
description: {
|
||||||
|
component: `Side panel listing thumbnails for every page of the PDF currently rendered by the \`adf-pdf-viewer\`. Stories use a lightweight mock of the underlying \`PDFViewer\`.`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
argTypes: {
|
||||||
|
pdfViewer: {
|
||||||
|
control: false,
|
||||||
|
description: 'Reference to the underlying pdf.js `PDFViewer` instance. Required input.',
|
||||||
|
table: { type: { summary: 'PDFViewer' } }
|
||||||
|
},
|
||||||
|
pageCount: {
|
||||||
|
control: { type: 'number', min: 0, max: 50 },
|
||||||
|
description: 'Number of pages used to build the mock `PDFViewer`.',
|
||||||
|
table: { category: 'Storybook Helpers' }
|
||||||
|
},
|
||||||
|
close: {
|
||||||
|
action: 'close',
|
||||||
|
description: 'Emitted when the user requests to close the thumbnails panel (e.g. via Escape).',
|
||||||
|
table: { type: { summary: 'EventEmitter <void>' }, category: 'Actions' }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
args: {
|
||||||
|
pageCount: 5
|
||||||
|
},
|
||||||
|
render: (args) => ({
|
||||||
|
props: {
|
||||||
|
...args,
|
||||||
|
pdfViewer: buildPdfViewerMock(args.pageCount ?? 0)
|
||||||
|
},
|
||||||
|
template: `<div style="height:480px; width:140px; border:1px solid #ddd; overflow:hidden;">
|
||||||
|
<adf-pdf-thumbnails [pdfViewer]="pdfViewer" (close)="close($event)" style="display:block; height:100%;" />
|
||||||
|
</div>`
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
export default meta;
|
||||||
|
type Story = StoryObj<PdfThumbnailsStoryArgs>;
|
||||||
|
|
||||||
|
export const Empty: Story = {
|
||||||
|
args: { pageCount: 0 }
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Populated: Story = {
|
||||||
|
args: { pageCount: 8 }
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Selected: Story = {
|
||||||
|
args: { pageCount: 4 }
|
||||||
|
};
|
||||||
@@ -0,0 +1,142 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { applicationConfig, Meta, StoryObj, moduleMetadata } from '@storybook/angular';
|
||||||
|
import { PdfViewerComponent } from './pdf-viewer.component';
|
||||||
|
import { provideStoryCore } from '../../../stories/core-story.providers';
|
||||||
|
|
||||||
|
const SAMPLE_PDF_URL = 'https://mozilla.github.io/pdf.js/web/compressed.tracemonkey-pldi-09.pdf';
|
||||||
|
|
||||||
|
const meta: Meta<PdfViewerComponent> = {
|
||||||
|
component: PdfViewerComponent,
|
||||||
|
title: 'Core/Viewer/PDF Viewer',
|
||||||
|
decorators: [
|
||||||
|
moduleMetadata({
|
||||||
|
imports: [PdfViewerComponent]
|
||||||
|
}),
|
||||||
|
applicationConfig({
|
||||||
|
providers: [...provideStoryCore()]
|
||||||
|
})
|
||||||
|
],
|
||||||
|
parameters: {
|
||||||
|
docs: {
|
||||||
|
description: {
|
||||||
|
component: `Renders PDF documents using \`pdfjs-dist\`. Stories require the pdf.js worker assets to be served by Storybook; see the Viewer documentation for details.`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
argTypes: {
|
||||||
|
urlFile: {
|
||||||
|
control: 'text',
|
||||||
|
description: 'External URL of the PDF file to load.',
|
||||||
|
table: { type: { summary: 'string' } }
|
||||||
|
},
|
||||||
|
blobFile: {
|
||||||
|
control: false,
|
||||||
|
description: 'In-memory Blob containing the PDF binary content.',
|
||||||
|
table: { type: { summary: 'Blob' } }
|
||||||
|
},
|
||||||
|
fileName: {
|
||||||
|
control: 'text',
|
||||||
|
description: 'Optional file name displayed by the toolbar.',
|
||||||
|
table: { type: { summary: 'string' } }
|
||||||
|
},
|
||||||
|
showToolbar: {
|
||||||
|
control: 'boolean',
|
||||||
|
description: 'Whether to show the PDF viewer toolbar (page navigation, zoom, ...).',
|
||||||
|
table: { type: { summary: 'boolean' }, defaultValue: { summary: 'true' } }
|
||||||
|
},
|
||||||
|
allowThumbnails: {
|
||||||
|
control: 'boolean',
|
||||||
|
description: 'Whether the thumbnails side panel can be opened.',
|
||||||
|
table: { type: { summary: 'boolean' }, defaultValue: { summary: 'false' } }
|
||||||
|
},
|
||||||
|
cacheType: {
|
||||||
|
control: 'text',
|
||||||
|
description: 'Optional `Cache-Control` header value used when loading via URL.',
|
||||||
|
table: { type: { summary: 'string' } }
|
||||||
|
},
|
||||||
|
rendered: {
|
||||||
|
action: 'rendered',
|
||||||
|
description: 'Emitted after a page is rendered.',
|
||||||
|
table: { type: { summary: 'EventEmitter <void>' }, category: 'Actions' }
|
||||||
|
},
|
||||||
|
error: {
|
||||||
|
action: 'error',
|
||||||
|
description: 'Emitted when the PDF fails to load.',
|
||||||
|
table: { type: { summary: 'EventEmitter <void>' }, category: 'Actions' }
|
||||||
|
},
|
||||||
|
close: {
|
||||||
|
action: 'close',
|
||||||
|
description: 'Emitted when the password dialog is dismissed without supplying a password.',
|
||||||
|
table: { type: { summary: 'EventEmitter <void>' }, category: 'Actions' }
|
||||||
|
},
|
||||||
|
pagesLoaded: {
|
||||||
|
action: 'pagesLoaded',
|
||||||
|
description: 'Emitted when all pages of the document finish loading.',
|
||||||
|
table: { type: { summary: 'EventEmitter <void>' }, category: 'Actions' }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
args: {
|
||||||
|
urlFile: SAMPLE_PDF_URL,
|
||||||
|
fileName: 'sample.pdf',
|
||||||
|
showToolbar: true,
|
||||||
|
allowThumbnails: false
|
||||||
|
},
|
||||||
|
render: (args) => ({
|
||||||
|
props: args,
|
||||||
|
template: `<div style="height:600px; border:1px solid #ddd;">
|
||||||
|
<adf-pdf-viewer
|
||||||
|
[urlFile]="urlFile"
|
||||||
|
[fileName]="fileName"
|
||||||
|
[showToolbar]="showToolbar"
|
||||||
|
[allowThumbnails]="allowThumbnails"
|
||||||
|
[cacheType]="cacheType"
|
||||||
|
(rendered)="rendered($event)"
|
||||||
|
(error)="error($event)"
|
||||||
|
(close)="close($event)"
|
||||||
|
(pagesLoaded)="pagesLoaded($event)" />
|
||||||
|
</div>`
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
export default meta;
|
||||||
|
type Story = StoryObj<PdfViewerComponent>;
|
||||||
|
|
||||||
|
export const WithSamplePdf: Story = {
|
||||||
|
args: {
|
||||||
|
urlFile: SAMPLE_PDF_URL,
|
||||||
|
showToolbar: true,
|
||||||
|
allowThumbnails: false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const WithThumbnailsEnabled: Story = {
|
||||||
|
args: {
|
||||||
|
urlFile: SAMPLE_PDF_URL,
|
||||||
|
showToolbar: true,
|
||||||
|
allowThumbnails: true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ToolbarHidden: Story = {
|
||||||
|
args: {
|
||||||
|
urlFile: SAMPLE_PDF_URL,
|
||||||
|
showToolbar: false,
|
||||||
|
allowThumbnails: false
|
||||||
|
}
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user