mirror of
https://github.com/Alfresco/alfresco-content-app.git
synced 2026-09-09 18:02:54 +00:00
[ACS-10323] a11y fix - Adding/removing favorites are not announced by SR (#5002)
* [ACS-10323] a11y fix - Adding/removing favorites are not announced by screen readers * fix unit test * [ACS-10323] cr fixes
This commit is contained in:
@@ -566,7 +566,9 @@
|
||||
"comment": "workaround for Recent Files and Search API issue",
|
||||
"type": "custom",
|
||||
"order": 400,
|
||||
"data": "['/favorites', '/favorite/libraries']",
|
||||
"data": {
|
||||
"routes": ["/favorites", "/favorite/libraries"]
|
||||
},
|
||||
"component": "app.toolbar.toggleFavorite",
|
||||
"rules": {
|
||||
"visible": [
|
||||
@@ -939,7 +941,10 @@
|
||||
"comment": "workaround for Recent Files and Search API issue",
|
||||
"type": "custom",
|
||||
"order": 802,
|
||||
"data": "['/favorites', '/favorite/libraries']",
|
||||
"data": {
|
||||
"routes": ["/favorites", "/favorite/libraries"],
|
||||
"focusAfterClosed": ".adf-context-menu-source"
|
||||
},
|
||||
"component": "app.toolbar.toggleFavorite",
|
||||
"rules": {
|
||||
"visible": [
|
||||
@@ -953,6 +958,9 @@
|
||||
"type": "custom",
|
||||
"order": 803,
|
||||
"component": "app.toolbar.toggleFavoriteLibrary",
|
||||
"data": {
|
||||
"focusAfterClosed": ".adf-context-menu-source"
|
||||
},
|
||||
"rules": {
|
||||
"visible": [
|
||||
"app.selection.library"
|
||||
|
||||
+1
-1
@@ -45,7 +45,7 @@
|
||||
(validationError)="error = $event"
|
||||
[hasLibrariesConstraint]="hasLibrariesConstraint"
|
||||
/>
|
||||
<mat-error *ngIf="error" class="app-search-error">
|
||||
<mat-error *ngIf="error" class="app-search-error" role="alert" aria-live="assertive">
|
||||
{{ error | translate }}
|
||||
</mat-error>
|
||||
<div id="search-options" class="app-search-options">
|
||||
|
||||
+18
-5
@@ -22,7 +22,7 @@
|
||||
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
|
||||
import { ToggleFavoriteLibraryComponent } from './toggle-favorite-library.component';
|
||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { Store } from '@ngrx/store';
|
||||
@@ -30,12 +30,14 @@ import { AppTestingModule } from '../../../testing/app-testing.module';
|
||||
import { of } from 'rxjs';
|
||||
import { Router } from '@angular/router';
|
||||
import { AppHookService, ContentApiService } from '@alfresco/aca-shared';
|
||||
import { UnitTestingUtils } from '@alfresco/adf-core';
|
||||
|
||||
describe('ToggleFavoriteLibraryComponent', () => {
|
||||
let fixture: ComponentFixture<ToggleFavoriteLibraryComponent>;
|
||||
let component: ToggleFavoriteLibraryComponent;
|
||||
let appHookService: AppHookService;
|
||||
let contentApiService: any;
|
||||
let unitTestingUtils: UnitTestingUtils;
|
||||
|
||||
const selection = { library: { entry: { id: 'libraryId' } } };
|
||||
const mockRouter = {
|
||||
@@ -66,6 +68,7 @@ describe('ToggleFavoriteLibraryComponent', () => {
|
||||
fixture = TestBed.createComponent(ToggleFavoriteLibraryComponent);
|
||||
component = fixture.componentInstance;
|
||||
contentApiService = TestBed.inject(ContentApiService);
|
||||
unitTestingUtils = new UnitTestingUtils(fixture.debugElement);
|
||||
|
||||
appHookService = TestBed.inject(AppHookService);
|
||||
spyOn(contentApiService['favoritesApi'], 'getFavoriteSite').and.returnValue(Promise.resolve(null));
|
||||
@@ -86,14 +89,24 @@ describe('ToggleFavoriteLibraryComponent', () => {
|
||||
expect(component.library.isFavorite).toBe(true);
|
||||
});
|
||||
|
||||
it('should emit onToggleEvent() event', async () => {
|
||||
it('should emit onToggleEvent() event', fakeAsync(() => {
|
||||
spyOn(appHookService.favoriteLibraryToggle, 'next');
|
||||
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
component.onToggleEvent();
|
||||
tick(100);
|
||||
|
||||
expect(appHookService.favoriteLibraryToggle.next).toHaveBeenCalled();
|
||||
}));
|
||||
|
||||
it('should focus element on toggle when focusAfterClosed is provided', () => {
|
||||
const mockElement = jasmine.createSpyObj<HTMLElement>('HTMLElement', ['focus']);
|
||||
spyOn(document, 'querySelector').and.returnValue(mockElement);
|
||||
|
||||
component.data = { focusAfterClosed: '.adf-context-menu-source' };
|
||||
const button = unitTestingUtils.getByCSS('button');
|
||||
button.triggerEventHandler('toggle', new CustomEvent('toggle'));
|
||||
|
||||
expect(document.querySelector).toHaveBeenCalledWith('.adf-context-menu-source');
|
||||
expect(mockElement.focus).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
+9
-2
@@ -22,7 +22,7 @@
|
||||
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Component, DestroyRef, inject, OnInit, ViewChild, ViewEncapsulation } from '@angular/core';
|
||||
import { Component, DestroyRef, inject, Input, OnInit, ViewChild, ViewEncapsulation } from '@angular/core';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { AppHookService } from '@alfresco/aca-shared';
|
||||
import { AppStore, getAppSelection } from '@alfresco/aca-shared/store';
|
||||
@@ -56,6 +56,8 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||
export class ToggleFavoriteLibraryComponent implements OnInit {
|
||||
library;
|
||||
|
||||
@Input() data: { focusAfterClosed?: string };
|
||||
|
||||
@ViewChild(MatMenuItem)
|
||||
menuItem: MatMenuItem;
|
||||
|
||||
@@ -84,6 +86,11 @@ export class ToggleFavoriteLibraryComponent implements OnInit {
|
||||
}
|
||||
|
||||
onToggleEvent() {
|
||||
this.appHookService.favoriteLibraryToggle.next();
|
||||
if (this.data?.focusAfterClosed) {
|
||||
document.querySelector<HTMLElement>('.adf-context-menu-source')?.focus();
|
||||
}
|
||||
setTimeout(() => {
|
||||
this.appHookService.favoriteLibraryToggle.next();
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
|
||||
+18
-2
@@ -29,11 +29,14 @@ import { ExtensionService } from '@alfresco/adf-extensions';
|
||||
import { Router } from '@angular/router';
|
||||
import { of } from 'rxjs';
|
||||
import { AppTestingModule } from '../../../testing/app-testing.module';
|
||||
import { UnitTestingUtils } from '@alfresco/adf-core';
|
||||
|
||||
describe('ToggleFavoriteComponent', () => {
|
||||
let component: ToggleFavoriteComponent;
|
||||
let fixture;
|
||||
let router;
|
||||
let unitTestingUtils: UnitTestingUtils;
|
||||
|
||||
const mockRouter = {
|
||||
url: 'some-url'
|
||||
};
|
||||
@@ -54,6 +57,7 @@ describe('ToggleFavoriteComponent', () => {
|
||||
|
||||
fixture = TestBed.createComponent(ToggleFavoriteComponent);
|
||||
component = fixture.componentInstance;
|
||||
unitTestingUtils = new UnitTestingUtils(fixture.debugElement);
|
||||
router = TestBed.inject(Router);
|
||||
});
|
||||
|
||||
@@ -66,7 +70,7 @@ describe('ToggleFavoriteComponent', () => {
|
||||
});
|
||||
|
||||
it('should not dispatch reload if route is not specified', () => {
|
||||
component.data = '["/reload_on_this_route"]';
|
||||
component.data = { routes: ['/reload_on_this_route'] };
|
||||
router.url = '/somewhere_over_the_rainbow';
|
||||
|
||||
fixture.detectChanges();
|
||||
@@ -76,7 +80,7 @@ describe('ToggleFavoriteComponent', () => {
|
||||
});
|
||||
|
||||
it('should dispatch reload if route is specified', () => {
|
||||
component.data = '["/reload_on_this_route"]';
|
||||
component.data = { routes: ['/reload_on_this_route'] };
|
||||
router.url = '/reload_on_this_route';
|
||||
|
||||
fixture.detectChanges();
|
||||
@@ -84,4 +88,16 @@ describe('ToggleFavoriteComponent', () => {
|
||||
|
||||
expect(mockStore.dispatch).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should focus element on toggle when focusAfterClosed is provided', () => {
|
||||
const mockElement = jasmine.createSpyObj<HTMLElement>('HTMLElement', ['focus']);
|
||||
spyOn(document, 'querySelector').and.returnValue(mockElement);
|
||||
|
||||
component.data = { routes: [], focusAfterClosed: '.adf-context-menu-source' };
|
||||
const button = unitTestingUtils.getByCSS('button');
|
||||
button.triggerEventHandler('toggle', new CustomEvent('toggle'));
|
||||
|
||||
expect(document.querySelector).toHaveBeenCalledWith('.adf-context-menu-source');
|
||||
expect(mockElement.focus).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
+5
-3
@@ -64,12 +64,14 @@ export class ToggleFavoriteComponent implements OnInit {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
if (this.data) {
|
||||
this.reloadOnRoutes = JSON.parse(this.data.replace(/'/g, '"'));
|
||||
}
|
||||
this.reloadOnRoutes = this.data?.routes ?? [];
|
||||
}
|
||||
|
||||
onToggleEvent() {
|
||||
const focusAfterClosed = this.data?.focusAfterClosed;
|
||||
if (focusAfterClosed) {
|
||||
document.querySelector<HTMLElement>(focusAfterClosed)?.focus();
|
||||
}
|
||||
if (this.reloadOnRoutes.includes(this.router.url)) {
|
||||
this.documentListService.reload();
|
||||
}
|
||||
|
||||
@@ -902,6 +902,20 @@ describe('ContentManagementService', () => {
|
||||
store.dispatch(new DeleteNodesAction(selection));
|
||||
expect(openSnackMessageActionSpy.calls.argsFor(0)[2].panelClass).toBe('adf-warning-snackbar');
|
||||
});
|
||||
|
||||
it('should focus element when deleting nodes with focusAfterClosed selector', fakeAsync(() => {
|
||||
const mockElement = jasmine.createSpyObj<HTMLElement>('HTMLElement', ['focus']);
|
||||
spyOn(document, 'querySelector').and.returnValue(mockElement);
|
||||
spyOn(contentApi, 'deleteNode').and.returnValue(of(null));
|
||||
|
||||
const nodes = [{ entry: { id: '1', name: 'name1' } } as NodeEntry];
|
||||
|
||||
contentManagementService.deleteNodes(nodes, false, '.some-button');
|
||||
tick();
|
||||
|
||||
expect(document.querySelector).toHaveBeenCalledWith('.some-button');
|
||||
expect(mockElement.focus).toHaveBeenCalled();
|
||||
}));
|
||||
});
|
||||
|
||||
describe('Permanent Delete', () => {
|
||||
@@ -927,6 +941,20 @@ describe('ContentManagementService', () => {
|
||||
expect(contentApi.purgeDeletedNode).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should focus element when purging nodes with focusAfterClosed selector', fakeAsync(() => {
|
||||
const mockElement = jasmine.createSpyObj<HTMLElement>('HTMLElement', ['focus']);
|
||||
spyOn(document, 'querySelector').and.returnValue(mockElement);
|
||||
spyOn(contentApi, 'purgeDeletedNode').and.returnValue(of({}));
|
||||
|
||||
const nodes = [{ entry: { id: '1', name: 'name1' } } as NodeEntry];
|
||||
|
||||
contentManagementService.purgeDeletedNodes(nodes, '.some-button');
|
||||
tick();
|
||||
|
||||
expect(document.querySelector).toHaveBeenCalledWith('.some-button');
|
||||
expect(mockElement.focus).toHaveBeenCalled();
|
||||
}));
|
||||
|
||||
describe('notification', () => {
|
||||
it('raises warning on multiple fail and one success', (done) => {
|
||||
spyOn(contentApi, 'purgeDeletedNode').and.callFake((id) => {
|
||||
@@ -1307,6 +1335,28 @@ describe('ContentManagementService', () => {
|
||||
store.dispatch(new RestoreDeletedNodesAction(selection));
|
||||
expect(openSnackMessageActionSpy.calls.argsFor(0)[2].panelClass).toBe('adf-info-snackbar');
|
||||
});
|
||||
|
||||
it('should focus element when restoring nodes with focusAfterClosed selector', fakeAsync(() => {
|
||||
const mockElement = jasmine.createSpyObj<HTMLElement>('HTMLElement', ['focus']);
|
||||
spyOn(document, 'querySelector').and.returnValue(mockElement);
|
||||
spyOn(contentApi, 'restoreNode').and.returnValue(of({} as NodeEntry));
|
||||
const path = {
|
||||
elements: [
|
||||
{
|
||||
id: '1-1',
|
||||
name: 'Company Home'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
const nodes = [{ entry: { name: 'node1', id: '1', path } } as NodeEntry];
|
||||
|
||||
contentManagementService.restoreDeletedNodes(nodes, '.some-button');
|
||||
tick(100);
|
||||
|
||||
expect(document.querySelector).toHaveBeenCalledWith('.some-button');
|
||||
expect(mockElement.focus).toHaveBeenCalled();
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1996,6 +2046,19 @@ describe('ContentManagementService', () => {
|
||||
expect(showInfoSpy).toHaveBeenCalledWith('APP.MESSAGES.INFO.FAVORITE_NODES_ADDED', null, { number: 2 });
|
||||
});
|
||||
|
||||
it('should focus element when adding favorite with focusAfterClosed selector', fakeAsync(() => {
|
||||
const mockElement = jasmine.createSpyObj<HTMLElement>('HTMLElement', ['focus']);
|
||||
|
||||
spyOn(document, 'querySelector').and.returnValue(mockElement);
|
||||
spyOn(contentApi, 'addFavorite').and.returnValue(of({ entry: { targetGuid: '', target: '' } }));
|
||||
|
||||
contentManagementService.addFavorite([fakeNode1, fakeNode2], '.some-button');
|
||||
tick(100);
|
||||
|
||||
expect(document.querySelector).toHaveBeenCalledWith('.some-button');
|
||||
expect(mockElement.focus).toHaveBeenCalled();
|
||||
}));
|
||||
|
||||
it('should call proper content api and display proper snackbar message if one node is provided for removeFavorite', () => {
|
||||
spyOn(contentApi, 'removeFavorite').and.returnValue(of({}));
|
||||
|
||||
@@ -2024,5 +2087,18 @@ describe('ContentManagementService', () => {
|
||||
expect(contentApi.removeFavorite).toHaveBeenCalledWith([fakeNode1, fakeNode2]);
|
||||
expect(showErrorSpy).toHaveBeenCalledWith('APP.MESSAGES.ERRORS.FAVORITE_NODE_NOT_FOUND', null, { name: 'mock-folder2-name' });
|
||||
});
|
||||
|
||||
it('should focus element when removing favorite with focusAfterClosed selector', fakeAsync(() => {
|
||||
const mockElement = jasmine.createSpyObj<HTMLElement>('HTMLElement', ['focus']);
|
||||
|
||||
spyOn(document, 'querySelector').and.returnValue(mockElement);
|
||||
spyOn(contentApi, 'removeFavorite').and.returnValue(of({}));
|
||||
|
||||
contentManagementService.removeFavorite([fakeNode1, fakeNode2], '.some-button');
|
||||
tick(100);
|
||||
|
||||
expect(document.querySelector).toHaveBeenCalledWith('.some-button');
|
||||
expect(mockElement.focus).toHaveBeenCalled();
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -93,7 +93,8 @@ export class ContentManagementService {
|
||||
private readonly documentListService = inject(DocumentListService);
|
||||
private readonly createMenuButtonSelector = 'app-toolbar-menu button[id="app.toolbar.create"]';
|
||||
|
||||
addFavorite(nodes: Array<NodeEntry>) {
|
||||
addFavorite(nodes: Array<NodeEntry>, focusedElementOnCloseSelector?: string) {
|
||||
this.focusAfterClose(focusedElementOnCloseSelector);
|
||||
if (nodes && nodes.length > 0) {
|
||||
this.contentApi.addFavorite(nodes).subscribe(() => {
|
||||
const favoriteNodes = nodes.map((node) => {
|
||||
@@ -112,7 +113,8 @@ export class ContentManagementService {
|
||||
}
|
||||
}
|
||||
|
||||
removeFavorite(nodes: Array<NodeEntry>) {
|
||||
removeFavorite(nodes: Array<NodeEntry>, focusedElementOnCloseSelector?: string) {
|
||||
this.focusAfterClose(focusedElementOnCloseSelector);
|
||||
if (nodes && nodes.length > 0) {
|
||||
this.contentApi.removeFavorite(nodes).subscribe({
|
||||
next: () => {
|
||||
@@ -373,7 +375,7 @@ export class ContentManagementService {
|
||||
return this.permission.check(folderNode, ['create']);
|
||||
}
|
||||
|
||||
purgeDeletedNodes(nodes: NodeEntry[]) {
|
||||
purgeDeletedNodes(nodes: NodeEntry[], focusedElementOnCloseSelector?: string) {
|
||||
if (!nodes || nodes.length === 0) {
|
||||
return;
|
||||
}
|
||||
@@ -389,6 +391,7 @@ export class ContentManagementService {
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe((result) => {
|
||||
this.focusAfterClose(focusedElementOnCloseSelector);
|
||||
if (result === true) {
|
||||
const nodesToDelete: NodeInfo[] = nodes.map((node) => {
|
||||
const { name } = node.entry;
|
||||
@@ -404,7 +407,7 @@ export class ContentManagementService {
|
||||
});
|
||||
}
|
||||
|
||||
restoreDeletedNodes(selection: NodeEntry[] = []) {
|
||||
restoreDeletedNodes(selection: NodeEntry[] = [], focusedElementOnCloseSelector?: string) {
|
||||
if (!selection.length) {
|
||||
return;
|
||||
}
|
||||
@@ -433,8 +436,11 @@ export class ContentManagementService {
|
||||
const remainingNodes = this.diff(selectedNodes, nodes.list.entries);
|
||||
|
||||
if (!remainingNodes.length) {
|
||||
this.focusAfterClose(focusedElementOnCloseSelector);
|
||||
this.showRestoreNotification(status);
|
||||
this.documentListService.reload();
|
||||
setTimeout(() => {
|
||||
this.documentListService.reload();
|
||||
}, 50);
|
||||
} else {
|
||||
this.restoreDeletedNodes(remainingNodes);
|
||||
}
|
||||
@@ -689,7 +695,8 @@ export class ContentManagementService {
|
||||
);
|
||||
}
|
||||
|
||||
deleteNodes(items: NodeEntry[], allowUndo = true): void {
|
||||
deleteNodes(items: NodeEntry[], allowUndo = true, focusedElementOnCloseSelector?: string): void {
|
||||
this.focusAfterClose(focusedElementOnCloseSelector);
|
||||
const batch: Observable<DeletedNodeInfo>[] = [];
|
||||
|
||||
items.forEach((node) => {
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
/*!
|
||||
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Alfresco Example Content Application
|
||||
*
|
||||
* This file is part of the Alfresco Example Content Application.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { fakeAsync, TestBed, tick } from '@angular/core/testing';
|
||||
import { FavoriteEffects } from './favorite.effects';
|
||||
import { ContentManagementService } from '../../services/content-management.service';
|
||||
import { AddFavoriteAction, AppStore, RemoveFavoriteAction, SetSelectedNodesAction } from '@alfresco/aca-shared/store';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { provideEffects } from '@ngrx/effects';
|
||||
import { AppTestingModule } from '../../testing/app-testing.module';
|
||||
import { NodeEntry } from '@alfresco/js-api';
|
||||
|
||||
describe('FavoriteEffects', () => {
|
||||
let contentService: ContentManagementService;
|
||||
let store: Store<AppStore>;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [AppTestingModule],
|
||||
providers: [provideEffects([FavoriteEffects])]
|
||||
});
|
||||
|
||||
store = TestBed.inject(Store);
|
||||
contentService = TestBed.inject(ContentManagementService);
|
||||
});
|
||||
|
||||
it('should call addFavorite with payload when AddFavoriteAction is dispatched', () => {
|
||||
spyOn(contentService, 'addFavorite').and.stub();
|
||||
const payload = [{ entry: { id: 'node1' } }] as NodeEntry[];
|
||||
store.dispatch(new AddFavoriteAction(payload, { focusedElementOnCloseSelector: 'test-selector' }));
|
||||
|
||||
expect(contentService.addFavorite).toHaveBeenCalledWith(payload, 'test-selector');
|
||||
});
|
||||
|
||||
it('should call addFavorite with selection when AddFavoriteAction is dispatched without payload', fakeAsync(() => {
|
||||
spyOn(contentService, 'addFavorite').and.stub();
|
||||
|
||||
const selection = [{ entry: { isFile: true } }] as NodeEntry[];
|
||||
store.dispatch(new SetSelectedNodesAction(selection));
|
||||
|
||||
tick(100);
|
||||
|
||||
store.dispatch(new AddFavoriteAction(null, { focusedElementOnCloseSelector: 'test-selector' }));
|
||||
|
||||
expect(contentService.addFavorite).toHaveBeenCalledWith(selection, 'test-selector');
|
||||
}));
|
||||
|
||||
it('should call removeFavorite with payload when RemoveFavoriteAction is dispatched', () => {
|
||||
spyOn(contentService, 'removeFavorite').and.stub();
|
||||
const payload = [{ entry: { id: 'node1' } }] as NodeEntry[];
|
||||
|
||||
store.dispatch(new RemoveFavoriteAction(payload, { focusedElementOnCloseSelector: 'test-selector' }));
|
||||
expect(contentService.removeFavorite).toHaveBeenCalledWith(payload, 'test-selector');
|
||||
});
|
||||
|
||||
it('should call removeFavorite with selection when RemoveFavoriteAction is dispatched without payload', fakeAsync(() => {
|
||||
spyOn(contentService, 'removeFavorite').and.stub();
|
||||
|
||||
const selection: any = [{ entry: { isFile: true } }] as NodeEntry[];
|
||||
store.dispatch(new SetSelectedNodesAction(selection));
|
||||
|
||||
tick(100);
|
||||
|
||||
store.dispatch(new RemoveFavoriteAction(null, { focusedElementOnCloseSelector: 'test-selector' }));
|
||||
expect(contentService.removeFavorite).toHaveBeenCalledWith(selection, 'test-selector');
|
||||
}));
|
||||
});
|
||||
@@ -41,14 +41,14 @@ export class FavoriteEffects {
|
||||
ofType<AddFavoriteAction>(NodeActionTypes.AddFavorite),
|
||||
map((action) => {
|
||||
if (action.payload && action.payload.length > 0) {
|
||||
this.content.addFavorite(action.payload);
|
||||
this.content.addFavorite(action.payload, action.configuration.focusedElementOnCloseSelector);
|
||||
} else {
|
||||
this.store
|
||||
.select(getAppSelection)
|
||||
.pipe(take(1))
|
||||
.subscribe((selection) => {
|
||||
if (selection && !selection.isEmpty) {
|
||||
this.content.addFavorite(selection.nodes);
|
||||
this.content.addFavorite(selection.nodes, action.configuration.focusedElementOnCloseSelector);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -63,14 +63,14 @@ export class FavoriteEffects {
|
||||
ofType<RemoveFavoriteAction>(NodeActionTypes.RemoveFavorite),
|
||||
map((action) => {
|
||||
if (action.payload && action.payload.length > 0) {
|
||||
this.content.removeFavorite(action.payload);
|
||||
this.content.removeFavorite(action.payload, action.configuration.focusedElementOnCloseSelector);
|
||||
} else {
|
||||
this.store
|
||||
.select(getAppSelection)
|
||||
.pipe(take(1))
|
||||
.subscribe((selection) => {
|
||||
if (selection && !selection.isEmpty) {
|
||||
this.content.removeFavorite(selection.nodes);
|
||||
this.content.removeFavorite(selection.nodes, action.configuration.focusedElementOnCloseSelector);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -156,7 +156,7 @@ describe('NodeEffects', () => {
|
||||
const node: any = {};
|
||||
store.dispatch(new PurgeDeletedNodesAction([node]));
|
||||
|
||||
expect(contentService.purgeDeletedNodes).toHaveBeenCalledWith([node]);
|
||||
expect(contentService.purgeDeletedNodes).toHaveBeenCalledWith([node], undefined);
|
||||
});
|
||||
|
||||
it('should purge nodes from the active selection', fakeAsync(() => {
|
||||
@@ -167,8 +167,8 @@ describe('NodeEffects', () => {
|
||||
|
||||
tick(100);
|
||||
|
||||
store.dispatch(new PurgeDeletedNodesAction(null));
|
||||
expect(contentService.purgeDeletedNodes).toHaveBeenCalledWith([node]);
|
||||
store.dispatch(new PurgeDeletedNodesAction([node], { focusedElementOnCloseSelector: '.test-selector' }));
|
||||
expect(contentService.purgeDeletedNodes).toHaveBeenCalledWith([node], '.test-selector');
|
||||
}));
|
||||
|
||||
it('should do nothing if invoking purge with no data', () => {
|
||||
@@ -187,7 +187,7 @@ describe('NodeEffects', () => {
|
||||
const node: any = {};
|
||||
store.dispatch(new RestoreDeletedNodesAction([node]));
|
||||
|
||||
expect(contentService.restoreDeletedNodes).toHaveBeenCalledWith([node]);
|
||||
expect(contentService.restoreDeletedNodes).toHaveBeenCalledWith([node], undefined);
|
||||
});
|
||||
|
||||
it('should restore deleted nodes from the active selection', fakeAsync(() => {
|
||||
@@ -198,8 +198,8 @@ describe('NodeEffects', () => {
|
||||
|
||||
tick(100);
|
||||
|
||||
store.dispatch(new RestoreDeletedNodesAction(null));
|
||||
expect(contentService.restoreDeletedNodes).toHaveBeenCalledWith([node]);
|
||||
store.dispatch(new RestoreDeletedNodesAction(null, { focusedElementOnCloseSelector: '.test-selector' }));
|
||||
expect(contentService.restoreDeletedNodes).toHaveBeenCalledWith([node], '.test-selector');
|
||||
}));
|
||||
|
||||
it('should do nothing if invoking restore with no data', () => {
|
||||
@@ -220,7 +220,7 @@ describe('NodeEffects', () => {
|
||||
|
||||
expect(store.dispatch).toHaveBeenCalledWith(jasmine.objectContaining({ ...new DeleteNodesAction([node], true) }));
|
||||
expect(store.dispatch).toHaveBeenCalledWith(jasmine.objectContaining({ ...new ShowLoaderAction(true) }));
|
||||
expect(contentService.deleteNodes).toHaveBeenCalledWith([node], true);
|
||||
expect(contentService.deleteNodes).toHaveBeenCalledWith([node], true, undefined);
|
||||
});
|
||||
|
||||
it('should delete nodes from the active selection', fakeAsync(() => {
|
||||
@@ -231,11 +231,13 @@ describe('NodeEffects', () => {
|
||||
|
||||
tick(100);
|
||||
|
||||
store.dispatch(new DeleteNodesAction(null));
|
||||
store.dispatch(new DeleteNodesAction(null, true, { focusedElementOnCloseSelector: '.test-selector' }));
|
||||
|
||||
expect(store.dispatch).toHaveBeenCalledWith(jasmine.objectContaining({ ...new DeleteNodesAction(null, true) }));
|
||||
expect(store.dispatch).toHaveBeenCalledWith(
|
||||
jasmine.objectContaining({ ...new DeleteNodesAction(null, true, { focusedElementOnCloseSelector: '.test-selector' }) })
|
||||
);
|
||||
expect(store.dispatch).toHaveBeenCalledWith(jasmine.objectContaining({ ...new ShowLoaderAction(true) }));
|
||||
expect(contentService.deleteNodes).toHaveBeenCalledWith([node], true);
|
||||
expect(contentService.deleteNodes).toHaveBeenCalledWith([node], true, '.test-selector');
|
||||
}));
|
||||
|
||||
it('should do nothing if invoking delete with no data', () => {
|
||||
|
||||
@@ -119,14 +119,14 @@ export class NodeEffects {
|
||||
ofType<PurgeDeletedNodesAction>(NodeActionTypes.PurgeDeleted),
|
||||
map((action) => {
|
||||
if (action?.payload?.length > 0) {
|
||||
this.contentService.purgeDeletedNodes(action.payload);
|
||||
this.contentService.purgeDeletedNodes(action.payload, action.configuration?.focusedElementOnCloseSelector);
|
||||
} else {
|
||||
this.store
|
||||
.select(getAppSelection)
|
||||
.pipe(take(1))
|
||||
.subscribe((selection) => {
|
||||
if (selection && selection.count > 0) {
|
||||
this.contentService.purgeDeletedNodes(selection.nodes);
|
||||
this.contentService.purgeDeletedNodes(selection.nodes, action.configuration?.focusedElementOnCloseSelector);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -141,14 +141,14 @@ export class NodeEffects {
|
||||
ofType<RestoreDeletedNodesAction>(NodeActionTypes.RestoreDeleted),
|
||||
map((action) => {
|
||||
if (action?.payload?.length > 0) {
|
||||
this.contentService.restoreDeletedNodes(action.payload);
|
||||
this.contentService.restoreDeletedNodes(action.payload, action.configuration?.focusedElementOnCloseSelector);
|
||||
} else {
|
||||
this.store
|
||||
.select(getAppSelection)
|
||||
.pipe(take(1))
|
||||
.subscribe((selection) => {
|
||||
if (selection && selection.count > 0) {
|
||||
this.contentService.restoreDeletedNodes(selection.nodes);
|
||||
this.contentService.restoreDeletedNodes(selection.nodes, action.configuration?.focusedElementOnCloseSelector);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -164,14 +164,14 @@ export class NodeEffects {
|
||||
map((action) => {
|
||||
this.store.dispatch(new ShowLoaderAction(true));
|
||||
if (action?.payload?.length > 0) {
|
||||
this.contentService.deleteNodes(action.payload, action.allowUndo);
|
||||
this.contentService.deleteNodes(action.payload, action.allowUndo, action.configuration?.focusedElementOnCloseSelector);
|
||||
} else {
|
||||
this.store
|
||||
.select(getAppSelection)
|
||||
.pipe(take(1))
|
||||
.subscribe((selection) => {
|
||||
if (selection && selection.count > 0) {
|
||||
this.contentService.deleteNodes(selection.nodes, action.allowUndo);
|
||||
this.contentService.deleteNodes(selection.nodes, action.allowUndo, action.configuration?.focusedElementOnCloseSelector);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -63,7 +63,8 @@ export class DeleteNodesAction implements Action {
|
||||
|
||||
constructor(
|
||||
public payload: NodeEntry[] = [],
|
||||
public allowUndo = true
|
||||
public allowUndo = true,
|
||||
public configuration?: ModalConfiguration
|
||||
) {}
|
||||
}
|
||||
|
||||
@@ -76,13 +77,19 @@ export class UndoDeleteNodesAction implements Action {
|
||||
export class RestoreDeletedNodesAction implements Action {
|
||||
readonly type = NodeActionTypes.RestoreDeleted;
|
||||
|
||||
constructor(public payload: Array<NodeEntry>) {}
|
||||
constructor(
|
||||
public payload: Array<NodeEntry>,
|
||||
public configuration?: ModalConfiguration
|
||||
) {}
|
||||
}
|
||||
|
||||
export class PurgeDeletedNodesAction implements Action {
|
||||
readonly type = NodeActionTypes.PurgeDeleted;
|
||||
|
||||
constructor(public payload: Array<NodeEntry>) {}
|
||||
constructor(
|
||||
public payload: Array<NodeEntry>,
|
||||
public configuration?: ModalConfiguration
|
||||
) {}
|
||||
}
|
||||
|
||||
export class DownloadNodesAction implements Action {
|
||||
@@ -183,13 +190,19 @@ export class UnlockWriteAction implements Action {
|
||||
export class AddFavoriteAction implements Action {
|
||||
readonly type = NodeActionTypes.AddFavorite;
|
||||
|
||||
constructor(public payload: Array<NodeEntry>) {}
|
||||
constructor(
|
||||
public payload: Array<NodeEntry>,
|
||||
public configuration?: ModalConfiguration
|
||||
) {}
|
||||
}
|
||||
|
||||
export class RemoveFavoriteAction implements Action {
|
||||
readonly type = NodeActionTypes.RemoveFavorite;
|
||||
|
||||
constructor(public payload: Array<NodeEntry>) {}
|
||||
constructor(
|
||||
public payload: Array<NodeEntry>,
|
||||
public configuration?: ModalConfiguration
|
||||
) {}
|
||||
}
|
||||
export class ManageAspectsAction implements Action {
|
||||
readonly type = NodeActionTypes.ChangeAspects;
|
||||
|
||||
Reference in New Issue
Block a user