Skip to content

Commit

Permalink
fix(a11y): screen reader support for view only file preview (#1495)
Browse files Browse the repository at this point in the history
* fix(a11y): screen reader support for view only file preview

* fix(a11y): resolve comments

* fix(a11y): resolve comments
  • Loading branch information
arturfrombox authored Aug 3, 2023
1 parent bac8d65 commit 9a0b4ab
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 11 deletions.
13 changes: 11 additions & 2 deletions src/lib/viewers/doc/DocBaseViewer.js
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ class DocBaseViewer extends BaseViewer {

this.viewerEl = this.docEl.appendChild(document.createElement('div'));
this.viewerEl.classList.add('pdfViewer');

this.loadTimeout = LOAD_TIMEOUT_MS;

this.startPageNum = this.getStartPage(this.startAt);
Expand Down Expand Up @@ -795,7 +796,15 @@ class DocBaseViewer extends BaseViewer {
const { AnnotationMode: PDFAnnotationMode = {} } = this.pdfjsLib;
const assetUrlCreator = createAssetUrlCreator(location);
const hasDownload = checkPermission(file, PERMISSION_DOWNLOAD);
const hasTextLayer = hasDownload && !this.getViewerOption('disableTextLayer');
const hasTextLayer = !this.getViewerOption('disableTextLayer');
const enabledTextLayerMode = hasDownload
? PDFJS_TEXT_LAYER_MODE.ENABLE
: PDFJS_TEXT_LAYER_MODE.ENABLE_PERMISSIONS; // This mode will prevent default behavior for copy events in the TextLayerBuilder

// Text layer should be rendered for a11y reasons thats why we will block user from selecting content when no download permissions was granted
if (!hasDownload) {
this.viewerEl.classList.add('pdfViewer--viewOnly');
}

return new PdfViewerClass({
annotationMode: PDFAnnotationMode.ENABLE, // Show annotations, but not forms
Expand All @@ -806,7 +815,7 @@ class DocBaseViewer extends BaseViewer {
linkService: this.pdfLinkService,
maxCanvasPixels: this.isMobile ? MOBILE_MAX_CANVAS_SIZE : -1,
renderInteractiveForms: false, // Enabling prevents unverified signatures from being displayed
textLayerMode: hasTextLayer ? PDFJS_TEXT_LAYER_MODE.ENABLE : PDFJS_TEXT_LAYER_MODE.DISABLE,
textLayerMode: hasTextLayer ? enabledTextLayerMode : PDFJS_TEXT_LAYER_MODE.DISABLE,
});
}

Expand Down
40 changes: 31 additions & 9 deletions src/lib/viewers/doc/__tests__/DocBaseViewer-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1140,43 +1140,65 @@ describe('src/lib/viewers/doc/DocBaseViewer', () => {
});
});

test('should enable the text layer based on download permissions', () => {
test('should enable the text layer if the user is on mobile', () => {
docBase.isMobile = true;
stubs.checkPermission.mockReturnValueOnce(true);

return docBase.initViewer('').then(() => {
expect(stubs.checkPermission).toBeCalledWith(docBase.options.file, PERMISSION_DOWNLOAD);
expect(stubs.getViewerOption).toBeCalledWith('disableTextLayer');
// Text Layer mode 1 = enabled
expect(stubs.pdfViewerClass).toBeCalledWith(expect.objectContaining({ textLayerMode: 1 }));
});
});

test('should enable the text layer if the user is on mobile', () => {
docBase.isMobile = true;
test('should add view only class if user does not have download permissions', () => {
docBase.containerEl = containerEl;
stubs.checkPermission.mockReturnValueOnce(false);

return docBase.initViewer('').then(() => {
expect(stubs.checkPermission).toBeCalledWith(docBase.options.file, PERMISSION_DOWNLOAD);
expect(docBase.viewerEl).toHaveClass('pdfViewer--viewOnly');
});
});

test('should not add view only class if user has download permissions', () => {
docBase.containerEl = containerEl;
stubs.checkPermission.mockReturnValueOnce(true);

return docBase.initViewer('').then(() => {
expect(stubs.checkPermission).toBeCalledWith(docBase.options.file, PERMISSION_DOWNLOAD);
expect(docBase.viewerEl).not.toHaveClass('pdfViewer--viewOnly');
});
});

test('should use proper text layer mode if user has download permissions and disableTextLayer viewer option is not set', () => {
stubs.getViewerOption.mockReturnValue(false);
stubs.checkPermission.mockReturnValueOnce(true);

return docBase.initViewer('').then(() => {
expect(stubs.checkPermission).toBeCalledWith(docBase.options.file, PERMISSION_DOWNLOAD);
expect(stubs.getViewerOption).toBeCalledWith('disableTextLayer');
// Text Layer mode 1 = enabled
expect(stubs.pdfViewerClass).toBeCalledWith(expect.objectContaining({ textLayerMode: 1 }));
});
});

test('should disable the text layer based on download permissions', () => {
test('should use proper text layer mode if user does not have download permissions and disableTextLayer viewer option is not set', () => {
stubs.getViewerOption.mockReturnValue(false);
stubs.checkPermission.mockReturnValueOnce(false);

return docBase.initViewer('').then(() => {
expect(stubs.checkPermission).toBeCalledWith(docBase.options.file, PERMISSION_DOWNLOAD);
// Text Layer mode 0 = disabled
expect(stubs.pdfViewerClass).toBeCalledWith(expect.objectContaining({ textLayerMode: 0 }));
expect(stubs.getViewerOption).toBeCalledWith('disableTextLayer');
// Text Layer mode 2 = enabled permissions
expect(stubs.pdfViewerClass).toBeCalledWith(expect.objectContaining({ textLayerMode: 2 }));
});
});

test('should disable the text layer if disableTextLayer viewer option is set', () => {
stubs.checkPermission.mockReturnValueOnce(true);
stubs.getViewerOption.mockReturnValue(true);

return docBase.initViewer('').then(() => {
expect(stubs.checkPermission).toBeCalledWith(docBase.options.file, PERMISSION_DOWNLOAD);
expect(stubs.getViewerOption).toBeCalledWith('disableTextLayer');
// Text Layer mode 0 = disabled
expect(stubs.pdfViewerClass).toBeCalledWith(expect.objectContaining({ textLayerMode: 0 }));
Expand Down
11 changes: 11 additions & 0 deletions src/lib/viewers/doc/_docBase.scss
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,17 @@ $thumbnail-sidebar-width: 191px; // Extra pixel to account for sidebar border
}
}

.pdfViewer {
&.pdfViewer--viewOnly {
.textLayer {
span {
cursor: unset;
user-select: none;
}
}
}
}

.textLayer {
caret-color: black; // Required for caret navigation on transparent text
top: $pdfjs-page-padding;
Expand Down

0 comments on commit 9a0b4ab

Please sign in to comment.