[ACA-3542] - added sorting for filters (#5932)

* [ACA-3542] - added sorting for filters

* [ACA-3542] - removed wrong parameter

* [ACA-3542] - fixed test with fixed sorting mode parameter

* Update content-node-selector-panel.component.html

* fix e2e

* fix delete site

Co-authored-by: Eugenio Romano <eromano@users.noreply.github.com>
Co-authored-by: Eugenio Romano <eugenio.romano@alfresco.com>
This commit is contained in:
Vito 2020-08-03 10:30:14 +01:00 committed by GitHub
parent bfbb66ea8e
commit 85183ead0d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 114 additions and 27 deletions

View File

@ -567,7 +567,7 @@
"ascending": true
},
{
"key": "createdByUser",
"key": "createdByUser.displayName",
"label": "Author",
"type": "FIELD",
"field": "cm:creator",

View File

@ -229,7 +229,7 @@
[sortingMode]="sortingMode"
[showHeader]="showHeader"
[thumbnails]="thumbnails"
[stickyHeader]="stickyHeader"
[stickyHeader]="stickyHeader"
(error)="onNavigationError($event)"
(success)="resetError()"
(ready)="emitReadyEvent($event)"
@ -242,6 +242,7 @@
<adf-search-header [col]="col"
[value]="paramValues? paramValues[col.key] : null"
[currentFolderNodeId]="currentFolderId"
[sorting]="filterSorting"
[maxItems]="pagination?.maxItems"
[skipCount]="pagination?.skipCount"
(update)="onFilterUpdate($event)"

View File

@ -88,7 +88,7 @@ export class FilesComponent implements OnInit, OnChanges, OnDestroy {
processId;
@Input()
sorting = ['nodeType', 'DESC'];
sorting = ['name', 'ASC'];
@Input()
sortingMode = 'server';
@ -163,7 +163,10 @@ export class FilesComponent implements OnInit, OnChanges, OnDestroy {
enableCustomHeaderFilter = false;
@Input()
paramValues: Map <any, any> = null;
paramValues: Map<any, any> = null;
@Input()
filterSorting: string = null;
@Output()
documentListReady: EventEmitter<any> = new EventEmitter();
@ -208,7 +211,7 @@ export class FilesComponent implements OnInit, OnChanges, OnDestroy {
enableMediumTimeFormat = false;
displayEmptyMetadata = false;
hyperlinkNavigation = false;
filtersStates: any[] = [];
filteredSorting: string[] = null;
constructor(private notificationService: NotificationService,
private uploadService: UploadService,

View File

@ -4,6 +4,8 @@
[showSettingsPanel]="false"
[navigationRoute]="navigationRoute"
[currentFolderId]="currentFolderId"
[filterSorting]="filterSorting"
[enableCustomHeaderFilter]="true"
[paramValues]="queryParams">
[paramValues]="queryParams"
(sorting-changed)="onSortingChanged($event)">
</app-files-component>

View File

@ -30,6 +30,7 @@ export class FilteredSearchComponent {
currentFolderId = '-my-';
queryParams = null;
filterSorting = null;
constructor(@Optional() private route: ActivatedRoute) {
@ -46,4 +47,8 @@ export class FilteredSearchComponent {
}
}
onSortingChanged(event) {
this.filterSorting = event.detail.column + '-' + event.detail.direction;
}
}

View File

@ -19,7 +19,8 @@ import { by, element, ElementFinder } from 'protractor';
import { BrowserVisibility, BrowserActions } from '@alfresco/adf-testing';
export class FolderDialogPage {
folderDialog = element(by.css('adf-folder-dialog'));
folderDialog = element.all(by.css('adf-folder-dialog')).first();
folderNameField = this.folderDialog.element(by.id('adf-folder-name-input'));
folderDescriptionField = this.folderDialog.element(by.id('adf-folder-description-input'));
createUpdateButton = this.folderDialog.element(by.id('adf-folder-create-button'));

View File

@ -64,6 +64,7 @@ describe('Viewer', () => {
});
afterAll(async () => {
await apiService.getInstance().login(browser.params.testConfig.admin.email, browser.params.testConfig.admin.password);
await apiService.getInstance().core.sitesApi.deleteSite(site.entry.id, { permanent: true });
await navigationBarPage.clickLogoutButton();
});

View File

@ -76,6 +76,7 @@ describe('Viewer', () => {
});
afterAll(async () => {
await apiService.getInstance().login(browser.params.testConfig.admin.email, browser.params.testConfig.admin.password);
await apiService.getInstance().core.sitesApi.deleteSite(site.entry.id, { permanent: true });
await navigationBarPage.clickLogoutButton();
});

View File

@ -64,6 +64,7 @@ describe('Viewer', () => {
});
afterAll(async () => {
await apiService.getInstance().login(browser.params.testConfig.admin.email, browser.params.testConfig.admin.password);
await apiService.getInstance().core.sitesApi.deleteSite(site.entry.id, { permanent: true });
});

View File

@ -70,6 +70,7 @@ describe('Viewer', () => {
});
afterAll(async () => {
await apiService.getInstance().login(browser.params.testConfig.admin.email, browser.params.testConfig.admin.password);
await apiService.getInstance().core.sitesApi.deleteSite(site.entry.id, { permanent: true });
});

View File

@ -66,6 +66,7 @@ describe('Viewer', () => {
});
afterAll(async () => {
await apiService.getInstance().login(browser.params.testConfig.admin.email, browser.params.testConfig.admin.password);
await apiService.getInstance().core.sitesApi.deleteSite(site.entry.id, { permanent: true });
});

View File

@ -64,6 +64,7 @@ describe('Viewer', () => {
});
afterAll(async () => {
await apiService.getInstance().login(browser.params.testConfig.admin.email, browser.params.testConfig.admin.password);
await apiService.getInstance().core.sitesApi.deleteSite(site.entry.id, { permanent: true });
});

View File

@ -66,6 +66,7 @@ describe('Viewer', () => {
});
afterAll(async () => {
await apiService.getInstance().login(browser.params.testConfig.admin.email, browser.params.testConfig.admin.password);
await apiService.getInstance().core.sitesApi.deleteSite(site.entry.id, { permanent: true });
await navigationBarPage.clickLogoutButton();
});

View File

@ -14,6 +14,9 @@
"paths": {
"@alfresco/adf-testing": [
"../lib/testing"
],
"@alfresco/adf-process-services-cloud": [
"../lib/process-services-cloud"
]
}
},

View File

@ -69,7 +69,7 @@
[contextMenuActions]="false"
[contentActions]="false"
[allowDropFiles]="false"
[sorting]="'server'"
sortingMode="server"
[where]="where"
(folderChange)="onFolderChange($event)"
(ready)="onFolderLoaded()"

View File

@ -131,7 +131,7 @@ describe('ContentNodeSelectorComponent', () => {
});
it('should the document list use the server ordering', () => {
expect(component.documentList.sorting).toBe('server');
expect(component.documentList.sortingMode).toBe('server');
});
it('should trigger the select event when selection has been made', (done) => {

View File

@ -13,7 +13,7 @@
[noPermission]="noPermission"
[showHeader]="showHeader"
[rowMenuCacheEnabled]="false"
[stickyHeader]="stickyHeader"
[stickyHeader]="stickyHeader"
[allowFiltering]="allowFiltering"
(showRowContextMenu)="onShowRowContextMenu($event)"
(showRowActionsMenu)="onShowRowActionsMenu($event)"

View File

@ -1451,7 +1451,7 @@ describe('DocumentList', () => {
where: undefined,
maxItems: 25,
skipCount: 0,
orderBy: [ 'name ASC' ],
orderBy: ['nodeType DESC', 'name asc' ],
rootFolderId: 'fake-id'
}, ['test-include']);
});
@ -1467,14 +1467,14 @@ describe('DocumentList', () => {
where: '(isFolder=true)',
maxItems: 25,
skipCount: 0,
orderBy: [ 'name ASC' ],
orderBy: ['nodeType DESC', 'name asc' ],
rootFolderId: 'fake-id'
}, ['test-include']);
});
it('should add orderBy in the server request', () => {
documentList.includeFields = ['test-include'];
documentList.orderBy = ['size DESC'];
documentList.sorting = ['size', 'DESC'];
documentList.currentFolderId = 'fake-id';
documentList.where = null;
@ -1484,7 +1484,7 @@ describe('DocumentList', () => {
maxItems: 25,
skipCount: 0,
where: undefined,
orderBy: ['size DESC'],
orderBy: ['nodeType DESC', 'size DESC'],
rootFolderId: 'fake-id'
}, ['test-include']);
});
@ -1502,7 +1502,7 @@ describe('DocumentList', () => {
expect(documentListService.getFolder).toHaveBeenCalledWith(null, {
maxItems: 25,
skipCount: 0,
orderBy: [ 'name ASC' ],
orderBy: ['nodeType DESC', 'name asc' ],
rootFolderId: 'folder-id',
where: undefined
}, undefined);

View File

@ -183,7 +183,14 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte
* override the default sorting detected by the component based on columns.
*/
@Input()
sorting = ['nodeType', 'DESC'];
sorting = ['name', 'asc'];
/** Defines default sorting. The format is an array of strings `[key direction, otherKey otherDirection]`
* i.e. `['name desc', 'nodeType asc']` or `['name asc']`. Set this value if you want a base
* rule to be added to the sorting apart from the one driven by the header.
*/
@Input()
additionalSorting = ['nodeType DESC'];
/** Defines sorting mode. Can be either `client` (items in the list
* are sorted client-side) or `server` (the ordering supplied by the
@ -255,6 +262,10 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte
set currentFolderId(currentFolderId: string) {
if (this._currentFolderId !== currentFolderId) {
this._currentFolderId = currentFolderId;
if (this.sorting) {
const [key, direction] = this.sorting;
this.orderBy = this.buildOrderByArray(key, direction);
}
if (this.data) {
this.data.loadPage(null, false);
this.resetNewFolderPagination();
@ -456,10 +467,16 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte
ngOnChanges(changes: SimpleChanges) {
this.resetSelection();
if (changes.sorting && changes.sorting.currentValue) {
const [key, direction] = changes.sorting.currentValue;
this.orderBy = this.buildOrderByArray(key, direction);
}
if (this.data) {
this.data.thumbnails = this.thumbnails;
}
if (changes.sortingMode && !changes.sortingMode.firstChange && this.data) {
this.data.sortingMode = changes.sortingMode.currentValue;
}
@ -475,7 +492,6 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte
if (this.data) {
if (changes.node && changes.node.currentValue) {
const merge = this._pagination ? this._pagination.merge : false;
this.data.loadPage(changes.node.currentValue, merge);
this.onDataReady(changes.node.currentValue);
} else if (changes.imageResolver) {
@ -646,6 +662,7 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte
}
loadFolder() {
if (!this._pagination.merge) {
this.setLoadingState(true);
}
@ -685,13 +702,16 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte
}
onSortingChanged(event: CustomEvent) {
const flattenExtraSortingOption = this.sorting.join(' ');
const orderArray = [flattenExtraSortingOption];
orderArray.push(''.concat(event.detail.key, ' ', event.detail.direction));
this.orderBy = orderArray;
this.orderBy = this.buildOrderByArray(event.detail.key, event.detail.direction);
this.reload();
}
private buildOrderByArray(currentKey: string, currentDirection: string ): string[] {
const orderArray = [...this.additionalSorting];
orderArray.push(''.concat(currentKey, ' ', currentDirection));
return orderArray;
}
/**
* Creates a set of predefined columns.
*/

View File

@ -51,6 +51,7 @@ export abstract class BaseQueryBuilderService {
filterQueries: FilterQuery[] = [];
paging: { maxItems?: number; skipCount?: number } = null;
sorting: Array<SearchSortingDefinition> = [];
sortingOptions: Array<SearchSortingDefinition> = [];
protected userFacetBuckets: { [key: string]: Array<FacetFieldBucket> } = {};
@ -93,6 +94,7 @@ export abstract class BaseQueryBuilderService {
this.userFacetBuckets = {};
if (this.config.sorting) {
this.sorting = this.config.sorting.defaults || [];
this.sortingOptions = this.config.sorting.options || [];
}
}
}

View File

@ -149,6 +149,20 @@ describe('SearchHeaderComponent', () => {
await fixture.whenStable();
});
it('should execute a new query when a new sorting is requested', async (done) => {
spyOn(alfrescoApiService.searchApi, 'search').and.returnValue(Promise.resolve(fakeNodePaging));
spyOn(queryBuilder, 'buildQuery').and.returnValue({});
component.update.subscribe((newNodePaging) => {
expect(newNodePaging).toBe(fakeNodePaging);
done();
});
const skipCount = new SimpleChange(null, '123-asc', false);
component.ngOnChanges({ 'sorting': skipCount });
fixture.detectChanges();
await fixture.whenStable();
});
it('should emit the clear event when no filter has been selected', async (done) => {
spyOn(queryBuilder, 'isNoFilterActive').and.returnValue(true);
spyOn(alfrescoApiService.searchApi, 'search').and.returnValue(Promise.resolve(fakeNodePaging));

View File

@ -67,6 +67,9 @@ export class SearchHeaderComponent implements OnInit, OnChanges, OnDestroy {
@Input()
skipCount: number;
@Input()
sorting: string = null;
/** Emitted when the result of the filter is received from the API. */
@Output()
update: EventEmitter<NodePaging> = new EventEmitter();
@ -136,6 +139,14 @@ export class SearchHeaderComponent implements OnInit, OnChanges, OnDestroy {
this.searchHeaderQueryBuilder.setupCurrentPagination(actualMaxItems, actualSkipCount);
}
if (changes['sorting'] && changes['sorting'].currentValue) {
const [key, value] = changes['sorting'].currentValue.split('-');
if (key === this.col.key) {
this.searchHeaderQueryBuilder.setSorting(key, value);
}
}
}
ngOnDestroy() {

View File

@ -23,6 +23,7 @@ import { SearchCategory } from './search-category.interface';
import { MinimalNode, QueryBody } from '@alfresco/js-api';
import { filter } from 'rxjs/operators';
import { Observable } from 'rxjs';
import { SearchSortingDefinition } from './search-sorting-definition.interface';
@Injectable({
providedIn: 'root'
@ -77,6 +78,22 @@ export class SearchHeaderQueryBuilderService extends BaseQueryBuilderService {
}
}
setSorting(column: string, direction: string) {
const optionAscending = direction.toLocaleLowerCase() === 'asc' ? true : false;
const fieldValue = this.getSortingFieldFromColumnName(column);
const currentSort: SearchSortingDefinition = { key: column, label: 'current', type: 'FIELD', field: fieldValue, ascending: optionAscending};
this.sorting = [currentSort];
this.execute();
}
private getSortingFieldFromColumnName(columnName: string) {
if (this.sortingOptions.length > 0) {
const sortOption: SearchSortingDefinition = this.sortingOptions.find((option: SearchSortingDefinition) => option.key === columnName);
return sortOption.field;
}
return '';
}
getCategoryForColumn(columnKey: string): SearchCategory {
let foundCategory = null;
if (this.categories !== null) {

View File

@ -543,11 +543,11 @@ export class DataTableComponent implements AfterContentInit, OnChanges, DoCheck,
const current = this.data.getSorting();
let newDirection = 'asc';
if (current && column.key === current.key) {
newDirection = current.direction === 'asc' ? 'desc' : 'asc';
newDirection = current.direction?.toLowerCase() === 'asc' ? 'desc' : 'asc';
}
this.sorting = [column.key, newDirection];
this.data.setSorting(new DataSorting(column.key, newDirection));
this.emitSortingChangedEvent(column.sortingKey, newDirection);
this.emitSortingChangedEvent(column.key, column.sortingKey, newDirection);
}
this.keyManager.updateActiveItem(0);
@ -639,7 +639,7 @@ export class DataTableComponent implements AfterContentInit, OnChanges, DoCheck,
isColumnSorted(col: DataColumn, direction: string): boolean {
if (col && direction) {
const sorting = this.data.getSorting();
return sorting && sorting.key === col.key && sorting.direction === direction;
return sorting && sorting.key === col.key && sorting.direction.toLocaleLowerCase() === direction;
}
return false;
}
@ -771,9 +771,10 @@ export class DataTableComponent implements AfterContentInit, OnChanges, DoCheck,
this.elementRef.nativeElement.dispatchEvent(domEvent);
}
private emitSortingChangedEvent(key: string, direction: string) {
private emitSortingChangedEvent(column: string, key: string, direction: string) {
const domEvent = new CustomEvent('sorting-changed', {
detail: {
column,
key,
direction
},

View File

@ -51,7 +51,7 @@ export class TaskFiltersCloudComponentPage {
}
getTaskFilterLocatorByFilterName(filterName: string): ElementFinder {
return element(by.css(`button[data-automation-id="${filterName}_filter"]`));
return element.all(by.css(`button[data-automation-id="${filterName}_filter"]`)).first();
}
}