From b5d410b75cf262779f97b495da2c56e592c852b9 Mon Sep 17 00:00:00 2001 From: Nikita Maliarchuk <84377976+nikita-web-ua@users.noreply.github.com> Date: Wed, 17 May 2023 18:47:04 +0200 Subject: [PATCH] [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 --- .../components/img-viewer.component.spec.ts | 25 +++- .../viewer/components/img-viewer.component.ts | 16 ++- ...r-container-more-actions.component.mock.ts | 42 +++++++ ...ewer-container-open-with.component.mock.ts | 42 +++++++ ...viewer-container-sidebar.component.mock.ts | 31 +++++ ...ontainer-toolbar-actions.component.mock.ts | 33 ++++++ ...viewer-container-toolbar.component.mock.ts | 31 +++++ .../components/viewer-render.component.html | 3 +- .../viewer-render.component.spec.ts | 13 +++ .../components/viewer-render.component.ts | 4 + .../viewer/components/viewer.component.html | 1 + .../components/viewer.component.spec.ts | 110 +++--------------- 12 files changed, 253 insertions(+), 98 deletions(-) create mode 100644 lib/core/src/lib/viewer/components/mock/adf-viewer-container-more-actions.component.mock.ts create mode 100644 lib/core/src/lib/viewer/components/mock/adf-viewer-container-open-with.component.mock.ts create mode 100644 lib/core/src/lib/viewer/components/mock/adf-viewer-container-sidebar.component.mock.ts create mode 100644 lib/core/src/lib/viewer/components/mock/adf-viewer-container-toolbar-actions.component.mock.ts create mode 100644 lib/core/src/lib/viewer/components/mock/adf-viewer-container-toolbar.component.mock.ts diff --git a/lib/core/src/lib/viewer/components/img-viewer.component.spec.ts b/lib/core/src/lib/viewer/components/img-viewer.component.spec.ts index 2491e473a0..55e72d0521 100644 --- a/lib/core/src/lib/viewer/components/img-viewer.component.spec.ts +++ b/lib/core/src/lib/viewer/components/img-viewer.component.spec.ts @@ -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); })); - }); }); diff --git a/lib/core/src/lib/viewer/components/img-viewer.component.ts b/lib/core/src/lib/viewer/components/img-viewer.component.ts index bb17e42c1b..f5d15f0b71 100644 --- a/lib/core/src/lib/viewer/components/img-viewer.component.ts +++ b/lib/core/src/lib/viewer/components/img-viewer.component.ts @@ -61,6 +61,9 @@ export class ImgViewerComponent implements AfterViewInit, OnChanges, OnDestroy { @Output() submit = new EventEmitter(); + @Output() + isSaving = new EventEmitter(); + @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); }); } diff --git a/lib/core/src/lib/viewer/components/mock/adf-viewer-container-more-actions.component.mock.ts b/lib/core/src/lib/viewer/components/mock/adf-viewer-container-more-actions.component.mock.ts new file mode 100644 index 0000000000..d45eefb63f --- /dev/null +++ b/lib/core/src/lib/viewer/components/mock/adf-viewer-container-more-actions.component.mock.ts @@ -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: ` + + + + + + + + ` +}) +export class ViewerWithCustomMoreActionsComponent { +} diff --git a/lib/core/src/lib/viewer/components/mock/adf-viewer-container-open-with.component.mock.ts b/lib/core/src/lib/viewer/components/mock/adf-viewer-container-open-with.component.mock.ts new file mode 100644 index 0000000000..dfb7957561 --- /dev/null +++ b/lib/core/src/lib/viewer/components/mock/adf-viewer-container-open-with.component.mock.ts @@ -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: ` + + + + + + + + ` +}) +export class ViewerWithCustomOpenWithComponent { +} diff --git a/lib/core/src/lib/viewer/components/mock/adf-viewer-container-sidebar.component.mock.ts b/lib/core/src/lib/viewer/components/mock/adf-viewer-container-sidebar.component.mock.ts new file mode 100644 index 0000000000..0353408f41 --- /dev/null +++ b/lib/core/src/lib/viewer/components/mock/adf-viewer-container-sidebar.component.mock.ts @@ -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: ` + + +
+
+
+ ` +}) +export class ViewerWithCustomSidebarComponent { +} diff --git a/lib/core/src/lib/viewer/components/mock/adf-viewer-container-toolbar-actions.component.mock.ts b/lib/core/src/lib/viewer/components/mock/adf-viewer-container-toolbar-actions.component.mock.ts new file mode 100644 index 0000000000..18fd220cbe --- /dev/null +++ b/lib/core/src/lib/viewer/components/mock/adf-viewer-container-toolbar-actions.component.mock.ts @@ -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: ` + + + + + + ` +}) +export class ViewerWithCustomToolbarActionsComponent { +} diff --git a/lib/core/src/lib/viewer/components/mock/adf-viewer-container-toolbar.component.mock.ts b/lib/core/src/lib/viewer/components/mock/adf-viewer-container-toolbar.component.mock.ts new file mode 100644 index 0000000000..507afbef94 --- /dev/null +++ b/lib/core/src/lib/viewer/components/mock/adf-viewer-container-toolbar.component.mock.ts @@ -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: ` + + +
+
+
+ ` +}) +export class ViewerWithCustomToolbarComponent { +} diff --git a/lib/core/src/lib/viewer/components/viewer-render.component.html b/lib/core/src/lib/viewer/components/viewer-render.component.html index afae445471..1d10891f41 100644 --- a/lib/core/src/lib/viewer/components/viewer-render.component.html +++ b/lib/core/src/lib/viewer/components/viewer-render.component.html @@ -38,7 +38,6 @@ [cacheType]="cacheTypeForContent" (close)="onClose()" (error)="onUnsupportedFile()"> - @@ -49,6 +48,7 @@ [blobFile]="blobFile" (error)="onUnsupportedFile()" (submit)="onSubmitFile($event)" + (isSaving)="isSaving.emit($event)" > @@ -66,7 +66,6 @@ - diff --git a/lib/core/src/lib/viewer/components/viewer-render.component.spec.ts b/lib/core/src/lib/viewer/components/viewer-render.component.spec.ts index 91940455e8..324bca5f46 100644 --- a/lib/core/src/lib/viewer/components/viewer-render.component.spec.ts +++ b/lib/core/src/lib/viewer/components/viewer-render.component.spec.ts @@ -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 ', () => { diff --git a/lib/core/src/lib/viewer/components/viewer-render.component.ts b/lib/core/src/lib/viewer/components/viewer-render.component.ts index adb6e527b8..63680538d2 100644 --- a/lib/core/src/lib/viewer/components/viewer-render.component.ts +++ b/lib/core/src/lib/viewer/components/viewer-render.component.ts @@ -90,6 +90,10 @@ export class ViewerRenderComponent implements OnChanges, OnInit, OnDestroy { @Output() close = new EventEmitter(); + /** Emitted when the img is saving. */ + @Output() + isSaving = new EventEmitter(); + extensionTemplates: { template: TemplateRef; isVisible: boolean }[] = []; extension: string; internalFileName: string; diff --git a/lib/core/src/lib/viewer/components/viewer.component.html b/lib/core/src/lib/viewer/components/viewer.component.html index af91e8efa9..b8fdccfd22 100644 --- a/lib/core/src/lib/viewer/components/viewer.component.html +++ b/lib/core/src/lib/viewer/components/viewer.component.html @@ -157,6 +157,7 @@ [readOnly]="readOnly" (submitFile)="onSubmitFile($event)" [urlFile]="urlFile" + (isSaving)="allowNavigate = !$event" [tracks]="tracks"> diff --git a/lib/core/src/lib/viewer/components/viewer.component.spec.ts b/lib/core/src/lib/viewer/components/viewer.component.spec.ts index 41bf38e368..7a44cf1c6c 100644 --- a/lib/core/src/lib/viewer/components/viewer.component.spec.ts +++ b/lib/core/src/lib/viewer/components/viewer.component.spec.ts @@ -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: ` - - -
-
-
- ` -}) -class ViewerWithCustomToolbarComponent { -} - -@Component({ - selector: 'adf-viewer-container-toolbar-actions', - template: ` - - - - - - ` -}) -class ViewerWithCustomToolbarActionsComponent { -} - -@Component({ - selector: 'adf-viewer-container-sidebar', - template: ` - - -
-
-
- ` -}) -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: ` - - - - - - - - ` -}) -class ViewerWithCustomOpenWithComponent { -} - -@Component({ - selector: 'adf-viewer-container-more-actions', - template: ` - - - - - - - - ` -}) -class ViewerWithCustomMoreActionsComponent { -} - - describe('ViewerComponent', () => { let component: ViewerComponent; @@ -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;