mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2026-09-09 18:03:21 +00:00
ACS-11154 Viewer stabilisation, lint fixes (#11660)
* refactor: clean up code in SuperagentHttpClient and upload.spec - Removed unnecessary eslint-disable comment in SuperagentHttpClient for improved code clarity. - Refactored promise handling in upload.spec to utilize an array for better management of multiple promises during file upload error handling. * chore: update cspell and ESLint configurations - Added "webscript" to the cspell dictionary for improved spell checking. - Updated ESLint configuration to disable the "@typescript-eslint/no-explicit-any" rule, allowing more flexibility in type definitions. * fix: enhance ImgViewerComponent to handle cleanup and prevent errors after destruction - Added a `destroyed` flag to manage component lifecycle and prevent operations on a destroyed instance. - Implemented `afterEach` hooks in tests to ensure proper fixture cleanup. - Updated key event handlers and methods to check for the `destroyed` state before executing actions, improving stability and preventing errors. Co-authored-by: Cursor <cursoragent@cursor.com> * fix: add aria-hidden attribute to notification history button for accessibility - Updated the notification history button to include the `aria-hidden` attribute, improving accessibility for screen readers and enhancing user experience. * fix: improve key event handling in ImgViewerComponent - Updated key event handlers to check for the presence of the cropper before executing actions, enhancing stability and preventing errors when the component is destroyed. - Removed redundant checks from individual arrow key handlers, streamlining the code. --------- Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -8,7 +8,7 @@
|
||||
id="adf-notification-history-open-button"
|
||||
(menuOpened)="onMenuOpened()"
|
||||
>
|
||||
<mat-icon matBadge="⁠" [matBadgeHidden]="!notifications.length" class="adf-notification-history-menu_button-icon" matBadgeColor="accent" matBadgeSize="small" adf-icon="notifications" />
|
||||
<mat-icon aria-hidden="false" matBadge="⁠" [matBadgeHidden]="!notifications.length" class="adf-notification-history-menu_button-icon" matBadgeColor="accent" matBadgeSize="small" adf-icon="notifications" />
|
||||
</button>
|
||||
|
||||
<mat-menu #menu="matMenu"
|
||||
|
||||
@@ -64,6 +64,10 @@ describe('Test Img viewer component ', () => {
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fixture.destroy();
|
||||
});
|
||||
|
||||
describe('default value', () => {
|
||||
it('should use default zoom if is not present a custom zoom in the app.config', () => {
|
||||
fixture.detectChanges();
|
||||
@@ -103,6 +107,10 @@ describe('Test Img viewer component ', () => {
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fixture.destroy();
|
||||
});
|
||||
|
||||
it('should display current scale as percent string', () => {
|
||||
component.scale = 0.5;
|
||||
expect(component.currentScaleText).toBe('50%');
|
||||
@@ -127,6 +135,10 @@ describe('Test Img viewer component ', () => {
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fixture.destroy();
|
||||
});
|
||||
|
||||
it('should thrown an error if no url or blob are passed', () => {
|
||||
const change = new SimpleChange(null, null, true);
|
||||
expect(() => {
|
||||
@@ -181,6 +193,10 @@ describe('Test Img viewer component ', () => {
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fixture.destroy();
|
||||
});
|
||||
|
||||
it('should update scales on zoom in', fakeAsync(() => {
|
||||
spyOn(component, 'zoomIn').and.callThrough();
|
||||
spyOn(component.cropper, 'zoom');
|
||||
@@ -377,6 +393,10 @@ describe('Test Img viewer component ', () => {
|
||||
component = fixture.componentInstance;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fixture.destroy();
|
||||
});
|
||||
|
||||
it('should conditionally display rotate and crop buttons based on allowedEditActions', () => {
|
||||
component.readOnly = false;
|
||||
component.allowedEditActions = { rotate: true, crop: true };
|
||||
@@ -401,7 +421,14 @@ describe('Test Img viewer component ', () => {
|
||||
fixture = TestBed.createComponent(ImgViewerComponent);
|
||||
testingUtils = new UnitTestingUtils(fixture.debugElement);
|
||||
component = fixture.componentInstance;
|
||||
component.urlFile =
|
||||
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==';
|
||||
fixture.detectChanges();
|
||||
component.ngAfterViewInit();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fixture.destroy();
|
||||
});
|
||||
|
||||
it('should rotate the image when r key is pressed', () => {
|
||||
@@ -428,101 +455,119 @@ describe('Test Img viewer component ', () => {
|
||||
expect(component.zoomIn).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should move the cropper when arrow keys are pressed', () => {
|
||||
it('should move the cropper when arrow keys are pressed', fakeAsync(() => {
|
||||
spyOn(component.cropper, 'move');
|
||||
dispatchKeyboardEvent('ArrowLeft');
|
||||
fixture.detectChanges();
|
||||
tick();
|
||||
|
||||
expect(component.cropper.move).toHaveBeenCalledWith(-3, 0);
|
||||
|
||||
dispatchKeyboardEvent('ArrowRight');
|
||||
fixture.detectChanges();
|
||||
tick();
|
||||
|
||||
expect(component.cropper.move).toHaveBeenCalledWith(3, 0);
|
||||
|
||||
dispatchKeyboardEvent('ArrowUp');
|
||||
fixture.detectChanges();
|
||||
tick();
|
||||
|
||||
expect(component.cropper.move).toHaveBeenCalledWith(0, -3);
|
||||
|
||||
dispatchKeyboardEvent('ArrowDown');
|
||||
fixture.detectChanges();
|
||||
tick();
|
||||
|
||||
expect(component.cropper.move).toHaveBeenCalledWith(0, 3);
|
||||
});
|
||||
}));
|
||||
|
||||
it('should increase crop box area when arrow keys with shift are pressed', () => {
|
||||
it('should increase crop box area when arrow keys with shift are pressed', fakeAsync(() => {
|
||||
component.cropImage();
|
||||
spyOn(component.cropper, 'setCropBoxData');
|
||||
let expectedCropBoxData = getExpectedCropBoxData(component.cropper, -3, 3, 0, 0);
|
||||
dispatchKeyboardEvent('ArrowLeft', true);
|
||||
fixture.detectChanges();
|
||||
tick();
|
||||
|
||||
expect(component.cropper.setCropBoxData).toHaveBeenCalledWith(expectedCropBoxData);
|
||||
|
||||
expectedCropBoxData = getExpectedCropBoxData(component.cropper, 0, 3, 0, 0);
|
||||
dispatchKeyboardEvent('ArrowRight', true);
|
||||
fixture.detectChanges();
|
||||
tick();
|
||||
|
||||
expect(component.cropper.setCropBoxData).toHaveBeenCalledWith(expectedCropBoxData);
|
||||
|
||||
expectedCropBoxData = getExpectedCropBoxData(component.cropper, 0, 0, -3, 3);
|
||||
dispatchKeyboardEvent('ArrowUp', true);
|
||||
fixture.detectChanges();
|
||||
tick();
|
||||
|
||||
expect(component.cropper.setCropBoxData).toHaveBeenCalledWith(expectedCropBoxData);
|
||||
|
||||
expectedCropBoxData = getExpectedCropBoxData(component.cropper, 0, 0, 0, 3);
|
||||
dispatchKeyboardEvent('ArrowDown', true);
|
||||
fixture.detectChanges();
|
||||
tick();
|
||||
|
||||
expect(component.cropper.setCropBoxData).toHaveBeenCalledWith(expectedCropBoxData);
|
||||
});
|
||||
}));
|
||||
|
||||
it('should decrease crop box area when arrow keys with alt are pressed', () => {
|
||||
it('should decrease crop box area when arrow keys with alt are pressed', fakeAsync(() => {
|
||||
component.cropImage();
|
||||
spyOn(component.cropper, 'setCropBoxData');
|
||||
let expectedCropBoxData = getExpectedCropBoxData(component.cropper, 3, -3, 0, 0);
|
||||
dispatchKeyboardEvent('ArrowLeft', false, true);
|
||||
fixture.detectChanges();
|
||||
tick();
|
||||
|
||||
expect(component.cropper.setCropBoxData).toHaveBeenCalledWith(expectedCropBoxData);
|
||||
|
||||
expectedCropBoxData = getExpectedCropBoxData(component.cropper, 0, -3, 0, 0);
|
||||
dispatchKeyboardEvent('ArrowRight', false, true);
|
||||
fixture.detectChanges();
|
||||
tick();
|
||||
|
||||
expect(component.cropper.setCropBoxData).toHaveBeenCalledWith(expectedCropBoxData);
|
||||
|
||||
expectedCropBoxData = getExpectedCropBoxData(component.cropper, 0, 0, 3, -3);
|
||||
dispatchKeyboardEvent('ArrowUp', false, true);
|
||||
fixture.detectChanges();
|
||||
tick();
|
||||
|
||||
expect(component.cropper.setCropBoxData).toHaveBeenCalledWith(expectedCropBoxData);
|
||||
|
||||
expectedCropBoxData = getExpectedCropBoxData(component.cropper, 0, 0, 0, -3);
|
||||
dispatchKeyboardEvent('ArrowDown', false, true);
|
||||
fixture.detectChanges();
|
||||
tick();
|
||||
|
||||
expect(component.cropper.setCropBoxData).toHaveBeenCalledWith(expectedCropBoxData);
|
||||
});
|
||||
}));
|
||||
|
||||
it('should prevent default for all arrow keys events', fakeAsync(() => {
|
||||
spyOn(component.cropper, 'move');
|
||||
|
||||
it('should prevent default for all arrow keys events', () => {
|
||||
const leftEvent = dispatchKeyboardEvent('ArrowLeft');
|
||||
fixture.detectChanges();
|
||||
tick();
|
||||
expect(leftEvent.preventDefault).toHaveBeenCalled();
|
||||
|
||||
const rightEvent = dispatchKeyboardEvent('ArrowRight');
|
||||
fixture.detectChanges();
|
||||
tick();
|
||||
expect(rightEvent.preventDefault).toHaveBeenCalled();
|
||||
|
||||
const upEvent = dispatchKeyboardEvent('ArrowUp');
|
||||
fixture.detectChanges();
|
||||
tick();
|
||||
expect(upEvent.preventDefault).toHaveBeenCalled();
|
||||
|
||||
const downEvent = dispatchKeyboardEvent('ArrowDown');
|
||||
fixture.detectChanges();
|
||||
tick();
|
||||
expect(downEvent.preventDefault).toHaveBeenCalled();
|
||||
});
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -87,6 +87,9 @@ export class ImgViewerComponent implements AfterViewInit, OnChanges, OnDestroy {
|
||||
|
||||
@HostListener('document:keyup', ['$event'])
|
||||
onKeyDown(event: KeyboardEvent) {
|
||||
if (this.destroyed || !this.cropper) {
|
||||
return;
|
||||
}
|
||||
switch (event.key) {
|
||||
case 'ArrowLeft': {
|
||||
this.handleArrowLeftKey(event);
|
||||
@@ -130,6 +133,7 @@ export class ImgViewerComponent implements AfterViewInit, OnChanges, OnDestroy {
|
||||
scale: number = 1.0;
|
||||
cropper: Cropper;
|
||||
isEditing: boolean = false;
|
||||
private destroyed: boolean = false;
|
||||
|
||||
get currentScaleText(): string {
|
||||
return Math.round(this.scale * 100) + '%';
|
||||
@@ -178,7 +182,11 @@ export class ImgViewerComponent implements AfterViewInit, OnChanges, OnDestroy {
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.cropper.destroy();
|
||||
this.destroyed = true;
|
||||
if (this.cropper) {
|
||||
this.cropper.destroy();
|
||||
this.cropper = null;
|
||||
}
|
||||
}
|
||||
|
||||
initializeScaling() {
|
||||
@@ -189,11 +197,17 @@ export class ImgViewerComponent implements AfterViewInit, OnChanges, OnDestroy {
|
||||
}
|
||||
|
||||
zoomIn() {
|
||||
if (this.destroyed || !this.cropper) {
|
||||
return;
|
||||
}
|
||||
this.cropper.zoom(0.2);
|
||||
this.scale = +(this.scale + 0.2).toFixed(1);
|
||||
}
|
||||
|
||||
zoomOut() {
|
||||
if (this.destroyed || !this.cropper) {
|
||||
return;
|
||||
}
|
||||
if (this.scale > 0.2) {
|
||||
this.cropper.zoom(-0.2);
|
||||
this.scale = +(this.scale - 0.2).toFixed(1);
|
||||
@@ -201,6 +215,9 @@ export class ImgViewerComponent implements AfterViewInit, OnChanges, OnDestroy {
|
||||
}
|
||||
|
||||
rotateImage() {
|
||||
if (this.destroyed || !this.cropper) {
|
||||
return;
|
||||
}
|
||||
this.isEditing = true;
|
||||
this.cropper.rotate(-90);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user