[ACS-5137] Fixed navigation between images (#8534)

* [ACS-5137] fixed navigation between images

* [ACS-5137] disable navigation while file is saving

* [ACS-5137] moved mocked tested components

* [ACS-5137] code improvements

* [ACS-5137] small fix

* Empty commit

* [ACS-5137] linting

* Empty commit
This commit is contained in:
Nikita Maliarchuk
2023-05-17 18:47:04 +02:00
committed by GitHub
parent 4301cb0933
commit b5d410b75c
12 changed files with 253 additions and 98 deletions

View File

@@ -126,7 +126,7 @@ describe('Test Img viewer component ', () => {
it('If no url or blob are passed should thrown an error', () => {
const change = new SimpleChange(null, null, true);
expect(() => {
component.ngOnChanges({ blobFile: change });
component.ngOnChanges({ blobFile: change, urlFile: change });
}).toThrow(new Error('Attribute urlFile or blobFile is required'));
});
@@ -143,6 +143,19 @@ describe('Test Img viewer component ', () => {
expect(element.querySelector('#viewer-image').getAttribute('alt')).toEqual('fake-name');
});
it('should call replace on cropper with new url if blobFile is null', () => {
component.fileName = 'fake-name';
component.urlFile = 'fake-url';
spyOn(component.cropper, 'replace').and.stub();
const fileName = new SimpleChange('val', 'val2', false);
const urlFile = new SimpleChange('fake-url', 'fake-url-2', false);
fixture.detectChanges();
component.ngOnChanges({ fileName, urlFile });
expect(component.cropper.replace).toHaveBeenCalledWith('fake-url-2');
});
it('If blob is passed should not thrown an error', () => {
const blob = createFakeBlob();
@@ -341,15 +354,21 @@ describe('Test Img viewer component ', () => {
component.readOnly = false;
component.isEditing = true;
spyOn(component, 'save');
const canvasMock = document.createElement('canvas');
spyOn(component.isSaving, 'emit');
spyOn(component, 'save').and.callThrough();
spyOn(component.cropper, 'getCroppedCanvas').and.returnValue(canvasMock);
spyOn(component.cropper.getCroppedCanvas(), 'toBlob').and.callFake(() => component.isSaving.emit(false));
fixture.detectChanges();
const saveButtonElement = fixture.debugElement.query(By.css('#viewer-save-button'));
saveButtonElement.triggerEventHandler('click', null);
tick();
expect(component.save).toHaveBeenCalled();
expect(component.isSaving.emit).toHaveBeenCalledWith(true);
expect(component.isSaving.emit).toHaveBeenCalledWith(false);
}));
});
});

View File

@@ -61,6 +61,9 @@ export class ImgViewerComponent implements AfterViewInit, OnChanges, OnDestroy {
@Output()
submit = new EventEmitter<any>();
@Output()
isSaving = new EventEmitter<boolean>();
@ViewChild('image', { static: false})
public imageElement: ElementRef;
@@ -74,7 +77,8 @@ export class ImgViewerComponent implements AfterViewInit, OnChanges, OnDestroy {
constructor(
private appConfigService: AppConfigService,
private urlService: UrlService) {
private urlService: UrlService
) {
this.initializeScaling();
}
@@ -143,6 +147,13 @@ export class ImgViewerComponent implements AfterViewInit, OnChanges, OnDestroy {
this.urlFile = this.urlService.createTrustedUrl(this.blobFile);
return;
}
if (!changes['urlFile'].firstChange && changes['fileName']) {
if (changes['fileName'].previousValue !== changes['fileName'].currentValue) {
this.cropper.replace(changes['urlFile'].currentValue);
}
}
if (!this.urlFile && !this.blobFile) {
throw new Error('Attribute urlFile or blobFile is required');
}
@@ -172,13 +183,14 @@ export class ImgViewerComponent implements AfterViewInit, OnChanges, OnDestroy {
}
save() {
this.isSaving.emit(true);
this.isEditing = false;
this.cropper.setDragMode('move');
this.cropper.getCroppedCanvas().toBlob((blob) => {
this.submit.emit(blob);
this.cropper.replace(this.cropper.getCroppedCanvas().toDataURL());
this.cropper.clear();
this.isSaving.emit(false);
});
}

View File

@@ -0,0 +1,42 @@
/*!
* @license
* Copyright © 2005-2023 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 { Component } from '@angular/core';
@Component({
selector: 'adf-viewer-container-more-actions',
template: `
<adf-viewer>
<adf-viewer-more-actions>
<button mat-menu-item>
<mat-icon>dialpad</mat-icon>
<span>Action One</span>
</button>
<button mat-menu-item disabled>
<mat-icon>voicemail</mat-icon>
<span>Action Two</span>
</button>
<button mat-menu-item>
<mat-icon>notifications_off</mat-icon>
<span>Action Three</span>
</button>
</adf-viewer-more-actions>
</adf-viewer>
`
})
export class ViewerWithCustomMoreActionsComponent {
}

View File

@@ -0,0 +1,42 @@
/*!
* @license
* Copyright © 2005-2023 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 { Component } from '@angular/core';
@Component({
selector: 'adf-viewer-container-open-with',
template: `
<adf-viewer>
<adf-viewer-open-with>
<button mat-menu-item>
<mat-icon>dialpad</mat-icon>
<span>Option 1</span>
</button>
<button mat-menu-item disabled>
<mat-icon>voicemail</mat-icon>
<span>Option 2</span>
</button>
<button mat-menu-item>
<mat-icon>notifications_off</mat-icon>
<span>Option 3</span>
</button>
</adf-viewer-open-with>
</adf-viewer>
`
})
export class ViewerWithCustomOpenWithComponent {
}

View File

@@ -0,0 +1,31 @@
/*!
* @license
* Copyright © 2005-2023 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 { Component } from '@angular/core';
@Component({
selector: 'adf-viewer-container-sidebar',
template: `
<adf-viewer>
<adf-viewer-sidebar>
<div class="custom-sidebar"></div>
</adf-viewer-sidebar>
</adf-viewer>
`
})
export class ViewerWithCustomSidebarComponent {
}

View File

@@ -0,0 +1,33 @@
/*!
* @license
* Copyright © 2005-2023 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 { Component } from '@angular/core';
@Component({
selector: 'adf-viewer-container-toolbar-actions',
template: `
<adf-viewer>
<adf-viewer-toolbar-actions>
<button mat-icon-button id="custom-button">
<mat-icon>alarm</mat-icon>
</button>
</adf-viewer-toolbar-actions>
</adf-viewer>
`
})
export class ViewerWithCustomToolbarActionsComponent {
}

View File

@@ -0,0 +1,31 @@
/*!
* @license
* Copyright © 2005-2023 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 { Component } from '@angular/core';
@Component({
selector: 'adf-viewer-container-toolbar',
template: `
<adf-viewer>
<adf-viewer-toolbar>
<div class="custom-toolbar-element"></div>
</adf-viewer-toolbar>
</adf-viewer>
`
})
export class ViewerWithCustomToolbarComponent {
}

View File

@@ -38,7 +38,6 @@
[cacheType]="cacheTypeForContent"
(close)="onClose()"
(error)="onUnsupportedFile()">
</adf-pdf-viewer>
</ng-container>
@@ -49,6 +48,7 @@
[blobFile]="blobFile"
(error)="onUnsupportedFile()"
(submit)="onSubmitFile($event)"
(isSaving)="isSaving.emit($event)"
></adf-img-viewer>
</ng-container>
@@ -66,7 +66,6 @@
<ng-container *ngSwitchCase="'text'">
<adf-txt-viewer [urlFile]="urlFile"
[blobFile]="blobFile">
</adf-txt-viewer>
</ng-container>

View File

@@ -30,6 +30,7 @@ import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { AppExtensionService, ViewerExtensionRef } from '@alfresco/adf-extensions';
import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
import { By } from '@angular/platform-browser';
@Component({
selector: 'adf-double-viewer',
@@ -365,6 +366,18 @@ describe('ViewerComponent', () => {
fixture.detectChanges();
});
it('should emit new value when isSaving emits new event', () => {
spyOn(component.isSaving, 'emit');
component.urlFile = 'fake-url-file.png';
component.ngOnChanges();
fixture.detectChanges();
const imgViewer = fixture.debugElement.query(By.css('adf-img-viewer'));
imgViewer.triggerEventHandler('isSaving', true);
expect(component.isSaving.emit).toHaveBeenCalledWith(true);
});
describe('Attribute', () => {
it('should urlFile present not thrown any error ', () => {

View File

@@ -90,6 +90,10 @@ export class ViewerRenderComponent implements OnChanges, OnInit, OnDestroy {
@Output()
close = new EventEmitter<boolean>();
/** Emitted when the img is saving. */
@Output()
isSaving = new EventEmitter<boolean>();
extensionTemplates: { template: TemplateRef<any>; isVisible: boolean }[] = [];
extension: string;
internalFileName: string;

View File

@@ -157,6 +157,7 @@
[readOnly]="readOnly"
(submitFile)="onSubmitFile($event)"
[urlFile]="urlFile"
(isSaving)="allowNavigate = !$event"
[tracks]="tracks">
</adf-viewer-render>

View File

@@ -31,49 +31,14 @@ import {
DownloadPromptDialogComponent,
DownloadPromptActions
} from '@alfresco/adf-core';
import { Component } from '@angular/core';
import { of } from 'rxjs';
@Component({
selector: 'adf-viewer-container-toolbar',
template: `
<adf-viewer>
<adf-viewer-toolbar>
<div class="custom-toolbar-element"></div>
</adf-viewer-toolbar>
</adf-viewer>
`
})
class ViewerWithCustomToolbarComponent {
}
@Component({
selector: 'adf-viewer-container-toolbar-actions',
template: `
<adf-viewer>
<adf-viewer-toolbar-actions>
<button mat-icon-button id="custom-button">
<mat-icon>alarm</mat-icon>
</button>
</adf-viewer-toolbar-actions>
</adf-viewer>
`
})
class ViewerWithCustomToolbarActionsComponent {
}
@Component({
selector: 'adf-viewer-container-sidebar',
template: `
<adf-viewer>
<adf-viewer-sidebar>
<div class="custom-sidebar"></div>
</adf-viewer-sidebar>
</adf-viewer>
`
})
class ViewerWithCustomSidebarComponent {
}
import { ViewerWithCustomMoreActionsComponent } from './mock/adf-viewer-container-more-actions.component.mock';
import { ViewerWithCustomToolbarComponent } from './mock/adf-viewer-container-toolbar.component.mock';
import { ViewerWithCustomSidebarComponent } from './mock/adf-viewer-container-sidebar.component.mock';
import { ViewerWithCustomOpenWithComponent } from './mock/adf-viewer-container-open-with.component.mock';
import { ViewerWithCustomToolbarActionsComponent } from './mock/adf-viewer-container-toolbar-actions.component.mock';
import { Component } from '@angular/core';
import { By } from '@angular/platform-browser';
@Component({
selector: 'adf-dialog-dummy',
@@ -82,55 +47,6 @@ class ViewerWithCustomSidebarComponent {
class DummyDialogComponent {
}
@Component({
selector: 'adf-viewer-container-open-with',
template: `
<adf-viewer>
<adf-viewer-open-with>
<button mat-menu-item>
<mat-icon>dialpad</mat-icon>
<span>Option 1</span>
</button>
<button mat-menu-item disabled>
<mat-icon>voicemail</mat-icon>
<span>Option 2</span>
</button>
<button mat-menu-item>
<mat-icon>notifications_off</mat-icon>
<span>Option 3</span>
</button>
</adf-viewer-open-with>
</adf-viewer>
`
})
class ViewerWithCustomOpenWithComponent {
}
@Component({
selector: 'adf-viewer-container-more-actions',
template: `
<adf-viewer>
<adf-viewer-more-actions>
<button mat-menu-item>
<mat-icon>dialpad</mat-icon>
<span>Action One</span>
</button>
<button mat-menu-item disabled>
<mat-icon>voicemail</mat-icon>
<span>Action Two</span>
</button>
<button mat-menu-item>
<mat-icon>notifications_off</mat-icon>
<span>Action Three</span>
</button>
</adf-viewer-more-actions>
</adf-viewer>
`
})
class ViewerWithCustomMoreActionsComponent {
}
describe('ViewerComponent', () => {
let component: ViewerComponent<any>;
@@ -371,6 +287,18 @@ describe('ViewerComponent', () => {
expect(prevButton).toBeNull();
});
it('should not show navigation buttons if file is saving', async () => {
component.allowNavigate = true;
fixture.detectChanges();
const viewerRender = fixture.debugElement.query(By.css('adf-viewer-render'));
viewerRender.triggerEventHandler('isSaving', true);
expect(component.allowNavigate).toBeFalsy();
viewerRender.triggerEventHandler('isSaving', false);
expect(component.allowNavigate).toBeTruthy();
});
it('should now show navigation buttons even if navigation enabled', async () => {
component.allowNavigate = true;
component.canNavigateBefore = false;