Skip to content
Permalink
Browse files
feat(document): document viewer for non-pdf documents (DEV-1303) (#812)
* feat(document): add default document viewer if document is not a pdf

* fix(document): call correct function for downloading document file

* chore(document): remove console log
  • Loading branch information
mdelez committed Sep 7, 2022
1 parent ffe00c0 commit 36008c6f8d5883e92492bd39e7e85564310fae36
Show file tree
Hide file tree
Showing 6 changed files with 269 additions and 75 deletions.
@@ -1,63 +1,100 @@
<div class="pdf-container">
<div *ngIf="fileType === 'pdf'" class="pdf-container">
<!-- in case of an error -->
<app-status [status]="404" [url]="src.fileValue.fileUrl" [representation]="'document'" *ngIf="failedToLoad">
</app-status>

<pdf-viewer class="pdf-viewer" [src]="src.fileValue.fileUrl" [original-size]="false" [autoresize]="true"
[show-all]="true" [show-borders]="true" [zoom]="zoomFactor" [zoom-scale]="'page-width'">
</pdf-viewer>

<div class="toolbar">
<div class="action horizontal bottom">
<!-- caption -->
<span>
<button mat-icon-button [matMenuTriggerFor]="more">
<mat-icon>more_vert</mat-icon>
</button>
<mat-menu #more="matMenu" class="mat-menu-custom-black">
<button class="menu-content" mat-menu-item (click)="downloadDocument(src.fileValue.fileUrl)">
Download file
</button>
<button class="menu-content" mat-menu-item (click)="openReplaceFileDialog()">
Replace file
</button>
</mat-menu>
</span>

<!-- input field for searching through document -->
<input matInput #queryInp type="text" id="searchbox" name="searchbox"
class="pdf-searchbox fill-remaining-space" placeholder="Search in pdf..." [value]="pdfQuery"
[disabled]="failedToLoad" (input)="searchQueryChanged($event.target.value)"
(keyup.enter)="searchQueryChanged(queryInp.value)" />

<!-- image action tools e.g. zoom, rotate and flip -->
<span>
<!-- zoom buttons -->
<button mat-icon-button id="DSP_PDF_ZOOM_OUT" matTooltip="Zoom out"
(click)="zoomFactor = zoomFactor - 0.2" [disabled]="failedToLoad">
<mat-icon>remove_circle_outline</mat-icon>
</button>
<button mat-icon-button id="DSP_PDF_HOME" matTooltip="Reset zoom" (click)="zoomFactor = 1.0"
[disabled]="failedToLoad">
<mat-icon>adjust</mat-icon>
</button>
<button mat-icon-button id="DSP_PDF_ZOOM_IN" matTooltip="Zoom in"
(click)="zoomFactor = zoomFactor + 0.2" [disabled]="failedToLoad">
<mat-icon>add_circle_outline</mat-icon>
</button>
</span>

<!-- empty placeholders to center the zoom buttons -->
<span class="fill-remaining-space"></span>
<span class="empty-space"></span>
<span class="empty-space"></span>
<span class="empty-space"></span>

<!-- full screen button -->
<span>
<button mat-icon-button id="DSP_PDF_FULL_SCREEN" matTooltip="Fullscreen" (click)="openFullscreen()"
[disabled]="failedToLoad">
<mat-icon>fullscreen</mat-icon>
</button>
</span>
</div>
</div>
</div>
<div class="toolbar">
<div class="action horizontal bottom">
<!-- caption -->
<span>
<!-- default document viewer -->
<div *ngIf="fileType !== 'pdf'">
<div class="file-representation">
<div class="container">
<div class="contents">
<div class="icon">
<mat-icon>
text_snippet
</mat-icon>
</div>
<div class="file">
<p>{{originalFilename}}</p>
</div>
</div>
</div>
</div>
<div class="toolbar">
<!-- toolbar -->
<div class="action horizontal bottom">
<!-- three dot menu to download and replace file -->
<button mat-icon-button [matMenuTriggerFor]="more">
<mat-icon>more_vert</mat-icon>
</button>
<mat-menu #more="matMenu" class="mat-menu-custom-black">
<button class="menu-content" mat-menu-item (click)="downloadDocument(src.fileValue.fileUrl)">
<button class="menu-content" mat-menu-item (click)="downloadDocument(src.fileValue.fileUrl)"
[disabled]="failedToLoad">
Download file
</button>
<button class="menu-content" mat-menu-item (click)="openReplaceFileDialog()">
Replace file
</button>
</mat-menu>
</span>

<!-- input field for searching through document -->
<input matInput #queryInp type="text" id="searchbox" name="searchbox" class="pdf-searchbox fill-remaining-space"
placeholder="Search in pdf..." [value]="pdfQuery" [disabled]="failedToLoad"
(input)="searchQueryChanged($event.target.value)" (keyup.enter)="searchQueryChanged(queryInp.value)" />

<!-- image action tools e.g. zoom, rotate and flip -->
<span>
<!-- zoom buttons -->
<button mat-icon-button id="DSP_PDF_ZOOM_OUT" matTooltip="Zoom out" (click)="zoomFactor = zoomFactor - 0.2"
[disabled]="failedToLoad">
<mat-icon>remove_circle_outline</mat-icon>
</button>
<button mat-icon-button id="DSP_PDF_HOME" matTooltip="Reset zoom" (click)="zoomFactor = 1.0"
[disabled]="failedToLoad">
<mat-icon>adjust</mat-icon>
</button>
<button mat-icon-button id="DSP_PDF_ZOOM_IN" matTooltip="Zoom in" (click)="zoomFactor = zoomFactor + 0.2"
[disabled]="failedToLoad">
<mat-icon>add_circle_outline</mat-icon>
</button>
</span>

<!-- empty placeholders to center the zoom buttons -->
<span class="fill-remaining-space"></span>
<span class="empty-space"></span>
<span class="empty-space"></span>
<span class="empty-space"></span>

<!-- full screen button -->
<span>
<button mat-icon-button id="DSP_PDF_FULL_SCREEN" matTooltip="Fullscreen" (click)="openFullscreen()"
[disabled]="failedToLoad">
<mat-icon>fullscreen</mat-icon>
</button>
</span>
</div>
</div>
</div>
@@ -13,7 +13,7 @@ $osd-height: 460px;
}

.mat-button-disabled {
color: $grey !important;
color: $grey !important;
}

app-status {
@@ -50,10 +50,36 @@ $osd-height: 460px;

}

.file-representation {
width: 100%;

.container {
background: #292929;
border-radius: 8px 8px 0px 0px;
display: flex;
justify-content: center;
align-items: center;
padding: 15.5% 0%;

.contents {
color: #FFFFFF;

.icon {
mat-icon {
height: 90px;
width: 90px;
font-size: 90px;
line-height: 90px;
}
}
}
}
}

::ng-deep .mat-menu-custom-black {
background: #292929;

.menu-content {
color: #FFFFFF;
}
}

}
@@ -1,13 +1,21 @@
import { Component, OnInit, ViewChild } from '@angular/core';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { Component, DebugElement, Input, OnInit, ViewChild } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { MatButtonModule } from '@angular/material/button';
import { MatDialogModule } from '@angular/material/dialog';
import { MatIconModule } from '@angular/material/icon';
import { MatMenuModule } from '@angular/material/menu';
import { MatSnackBarModule } from '@angular/material/snack-bar';
import { By } from '@angular/platform-browser';
import { KnoraApiConnection } from '@dasch-swiss/dsp-js';
import { PdfViewerModule } from 'ng2-pdf-viewer';
import { AppInitService } from 'src/app/app-init.service';
import { DspApiConfigToken, DspApiConnectionToken } from 'src/app/main/declarations/dsp-api-tokens';
import { TestConfig } from 'test.config';
import { FileRepresentation } from '../file-representation';
import { DocumentComponent } from './document.component';

// --> TODO: get test data from dsp-js or from dsp-api test data
const documentFileValue = {
const documentPdfFileValue = {
'type': 'http://api.knora.org/ontology/knora-api/v2#DocumentFileValue',
'id': 'http://rdfh.ch/1111/ay_F18DrTHeJ4zKuTt8Qzw/values/uY_qTEqVSBOW3c2vyeGXQg',
'attachedToUser': 'http://rdfh.ch/users/Iscj52QaSk-LNurRU6z3Hw',
@@ -28,55 +36,158 @@ const documentFileValue = {
'propertyComment': 'Connects a Representation to a document'
};

const documentPptFileValue = {
'type': 'http://api.knora.org/ontology/knora-api/v2#DocumentFileValue',
'id': 'http://rdfh.ch/1111/ay_F18DrTHeJ4zKuTt8Qzw/values/uY_qTEqVSBOW3c2vyeGXQg',
'attachedToUser': 'http://rdfh.ch/users/Iscj52QaSk-LNurRU6z3Hw',
'arkUrl': 'http://0.0.0.0:3336/ark:/72163/1/1111/ay_F18DrTHeJ4zKuTt8Qzws/lRExhaYnQZuWVspAnbrjAQO',
'versionArkUrl': 'http://0.0.0.0:3336/ark:/72163/1/1111/ay_F18DrTHeJ4zKuTt8Qzws/lRExhaYnQZuWVspAnbrjAQO.20210716T062258456728Z',
'valueCreationDate': '2021-07-16T06:22:58.456728Z',
'hasPermissions': 'CR knora-admin:ProjectAdmin|D knora-admin:ProjectAdmin|M knora-admin:ProjectAdmin|V knora-admin:ProjectAdmin|RV knora-admin:ProjectAdmin',
'userHasPermission': 'CR',
'uuid': 'lRExhaYnQZuWVspAnbrjAQ',
'filename': 'Bf9iaid15df-EOrcDcZEtfk.ppt',
'fileUrl': 'http://0.0.0.0:1024/1111/Bf9iaid15df-EOrcDcZEtfk.ppt/file',
'strval': 'http://0.0.0.0:1024/1111/Bf9iaid15df-EOrcDcZEtfk.ppt/file',
'property': 'http://api.knora.org/ontology/knora-api/v2#hasDocumentFileValue',
'propertyLabel': 'hat Dokument',
'propertyComment': 'Connects a Representation to a document'
};

/**
* test host component with a pdf document
*/
@Component({
template: `
<app-document [src]="documnetFileRepresentation">
<app-document [src]="documentFileRepresentation">
</app-document>`
})
class TestHostComponent implements OnInit {

@ViewChild(DocumentComponent) pdfViewerComp: DocumentComponent;
class TestPdfDocumentHostComponent implements OnInit {

documnetFileRepresentation: FileRepresentation;
caption = 'test image';
inputActivateRegion: string;
@ViewChild(DocumentComponent) documentComp: DocumentComponent;

activeRegion: string;
documentFileRepresentation: FileRepresentation;

ngOnInit() {

this.documnetFileRepresentation = new FileRepresentation(documentFileValue);
this.documentFileRepresentation = new FileRepresentation(documentPdfFileValue);
}
}

/**
* test host component with a ppt document
*/
@Component({
template: `
<app-document [src]="documentFileRepresentation">
</app-document>`
})
class TestPptDocumentHostComponent implements OnInit {

@ViewChild(DocumentComponent) documentComp: DocumentComponent;

regHovered(regIri: string) {
this.activeRegion = regIri;
documentFileRepresentation: FileRepresentation;

ngOnInit() {
this.documentFileRepresentation = new FileRepresentation(documentPptFileValue);
}
}

@Component({ selector: 'app-status', template: '' })
class MockStatusComponent {
@Input() status: number;

@Input() comment?: string;
@Input() url?: string;
@Input() representation?: 'archive' | 'audio' | 'document' | 'still-image' | 'video' | 'text';

constructor() { }
}

describe('DocumentComponent', () => {
let testHostComponent: TestHostComponent;
let testHostFixture: ComponentFixture<TestHostComponent>;

beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [DocumentComponent],
declarations: [
DocumentComponent,
TestPdfDocumentHostComponent,
TestPptDocumentHostComponent,
MockStatusComponent
],
imports: [
HttpClientTestingModule,
MatButtonModule,
MatDialogModule,
MatIconModule,
MatMenuModule,
MatSnackBarModule,
PdfViewerModule

],
providers: [
AppInitService,
{
provide: DspApiConfigToken,
useValue: TestConfig.ApiConfig
},
{
provide: DspApiConnectionToken,
useValue: new KnoraApiConnection(TestConfig.ApiConfig)
}
]
})
.compileComponents();
});

beforeEach(() => {
testHostFixture = TestBed.createComponent(TestHostComponent);
testHostComponent = testHostFixture.componentInstance;
testHostFixture.detectChanges();
describe('pdf viewer', () => {
let testHostComponent: TestPdfDocumentHostComponent;
let testHostFixture: ComponentFixture<TestPdfDocumentHostComponent>;
let documentComponentDe: DebugElement;

beforeEach(() => {
testHostFixture = TestBed.createComponent(TestPdfDocumentHostComponent);
testHostComponent = testHostFixture.componentInstance;
testHostFixture.detectChanges();

const hostCompDe = testHostFixture.debugElement;
documentComponentDe = hostCompDe.query(By.directive(DocumentComponent));

expect(testHostComponent).toBeTruthy();
});

it('should show the pdf viewer if the document is a pdf', () => {
const pdfDebugElement = documentComponentDe.query(By.css('.pdf-viewer'));
expect(pdfDebugElement).toBeTruthy();

// should not show the default document viewer if the pdf viewer is shown
const fileRepresentationDebugElement = documentComponentDe.query(By.css('.file-representation'));
expect(fileRepresentationDebugElement).toBeFalsy();
});
});

it('should create', () => {
expect(testHostComponent).toBeTruthy();
describe('default document viewer', () => {
let testHostComponent: TestPptDocumentHostComponent;
let testHostFixture: ComponentFixture<TestPptDocumentHostComponent>;
let documentComponentDe: DebugElement;

beforeEach(() => {
testHostFixture = TestBed.createComponent(TestPptDocumentHostComponent);
testHostComponent = testHostFixture.componentInstance;
testHostFixture.detectChanges();

const hostCompDe = testHostFixture.debugElement;
documentComponentDe = hostCompDe.query(By.directive(DocumentComponent));

expect(testHostComponent).toBeTruthy();
});

it('should show the default document viewer if the document is not a pdf', () => {
const fileRepresentationDebugElement = documentComponentDe.query(By.css('.file-representation'));
expect(fileRepresentationDebugElement).toBeTruthy();

// should not show the pdf viewer if the default document viewer is shown
const pdfDebugElement = documentComponentDe.query(By.css('.pdf-viewer'));
expect(pdfDebugElement).toBeFalsy();
});
});

});

0 comments on commit 36008c6

Please sign in to comment.