From 930e4b1f3ce1cc2d3fc4a8ceb643035f7a1b413b Mon Sep 17 00:00:00 2001 From: MichalKinas <113341662+MichalKinas@users.noreply.github.com> Date: Wed, 21 Feb 2024 12:55:13 +0100 Subject: [PATCH] [ACS-6813] ACA configurable layout for search result list (#3656) * [ACS-6813] Make search results list column configurable * [ACS-6813] Documentation update * [ACS-6813] Typo fix --- docs/extending/application-features.md | 27 ++++++ extension.schema.json | 6 ++ .../aca-content/assets/app.extensions.json | 63 ++++++++++++++ .../aca-content/src/lib/aca-content.module.ts | 4 +- .../search-results.component.html | 50 ++++++++--- .../search-results.component.spec.ts | 84 +------------------ .../search-results.component.ts | 7 +- .../src/lib/services/app.extension.service.ts | 7 +- 8 files changed, 151 insertions(+), 97 deletions(-) diff --git a/docs/extending/application-features.md b/docs/extending/application-features.md index 19ef2598f1..e87c0f75d4 100644 --- a/docs/extending/application-features.md +++ b/docs/extending/application-features.md @@ -44,6 +44,33 @@ All the customizations are stored in the `features` section of the configuration Other applications or external plugins can utilise different subsets of the configuration above. Also, extra entries can be added to the configuration schema. +## Customizing list columns + +To modify default columns layout for lists in personal files, libraries, favorite libraries, shared, recent files, favorites and search results you can update the `app.extensions.json` file by modifying/inserting new entry to the corresponding section of `features.documentList`. For example modifying `search-results` section: + +```json +{ + "features": { + "documentList": { + "search-results": [ + { + "id": "app.search.type", + "key": "nodeType", + "title": "APP.DOCUMENT_LIST.COLUMNS.TYPE", + "type": "text", + "class": "adf-ellipsis-cell", + "sortable": true, + "desktopOnly": false, + "order": 102 + } + ] + } + } +} +``` + +will result in new column being displayed in search results list. Any number of columns can be added, modified or removed from any section. + ## Content Actions Most of the UI elements that operate with content, like toolbar buttons or menus, diff --git a/extension.schema.json b/extension.schema.json index 6eb3e78c10..22064d0a70 100644 --- a/extension.schema.json +++ b/extension.schema.json @@ -924,6 +924,12 @@ "type": "array", "items": { "$ref": "#/definitions/documentListPresetRef" }, "minItems": 1 + }, + "search-results": { + "description": "Search results list preset", + "type": "array", + "items": { "$ref": "#/definitions/documentListPresetRef" }, + "minItems": 1 } } }, diff --git a/projects/aca-content/assets/app.extensions.json b/projects/aca-content/assets/app.extensions.json index 57f3a3e797..773f7792b5 100644 --- a/projects/aca-content/assets/app.extensions.json +++ b/projects/aca-content/assets/app.extensions.json @@ -2754,6 +2754,69 @@ "order": 50, "draggable": true } + ], + "search-results": [ + { + "id": "app.search.name", + "key": "name", + "title": "APP.DOCUMENT_LIST.COLUMNS.NAME", + "type": "text", + "class": "adf-ellipsis-cell adf-expand-cell-5", + "sortable": false, + "template": "app.search.columns.name", + "desktopOnly": false, + "order": 20, + "draggable": true + }, + { + "id": "app.search.size", + "key": "content.sizeInBytes", + "title": "APP.DOCUMENT_LIST.COLUMNS.SIZE", + "type": "fileSize", + "class": "adf-no-grow-cell adf-ellipsis-cell", + "sortable": false, + "desktopOnly": true, + "order": 30, + "draggable": true + }, + { + "id": "app.search.modifiedOn", + "key": "modifiedAt", + "title": "APP.DOCUMENT_LIST.COLUMNS.MODIFIED_ON", + "type": "date", + "format": "timeAgo", + "class": "adf-ellipsis-cell adf-no-grow-cell", + "sortable": false, + "desktopOnly": true, + "order": 40, + "draggable": true + }, + { + "id": "app.search.modifiedBy", + "key": "modifiedByUser.displayName", + "title": "APP.DOCUMENT_LIST.COLUMNS.MODIFIED_BY", + "type": "text", + "class": "adf-ellipsis-cell adf-expand-cell-2 adf-min-width-cell", + "sortable": false, + "desktopOnly": true, + "order": 50, + "draggable": true + }, + { + "id": "app.search.tags", + "template": "app.columns.tags", + "key": "tags", + "title": "APP.DOCUMENT_LIST.COLUMNS.TAGS", + "class": "adf-full-width adf-expand-cell-4", + "type": "text", + "sortable": false, + "desktopOnly": true, + "order": 60, + "draggable": true, + "rules": { + "visible": "app.areTagsEnabled" + } + } ] } } diff --git a/projects/aca-content/src/lib/aca-content.module.ts b/projects/aca-content/src/lib/aca-content.module.ts index 0614036141..bc4db85939 100644 --- a/projects/aca-content/src/lib/aca-content.module.ts +++ b/projects/aca-content/src/lib/aca-content.module.ts @@ -86,6 +86,7 @@ import { ViewProfileComponent } from './components/view-profile/view-profile.com import { TrashcanComponent } from './components/trashcan/trashcan.component'; import { SharedLinkViewComponent } from './components/shared-link-view/shared-link-view.component'; import { MAT_DIALOG_DEFAULT_OPTIONS } from '@angular/material/dialog'; +import { SearchResultsRowComponent } from './components/search/search-results-row/search-results-row.component'; @NgModule({ imports: [ @@ -174,7 +175,8 @@ export class ContentServiceExtensionModule { 'app.logout': LogoutComponent, 'app.user': UserInfoComponent, 'app.notification-center': NotificationHistoryComponent, - 'app.user.menu': UserMenuComponent + 'app.user.menu': UserMenuComponent, + 'app.search.columns.name': SearchResultsRowComponent }); extensions.setEvaluators({ diff --git a/projects/aca-content/src/lib/components/search/search-results/search-results.component.html b/projects/aca-content/src/lib/components/search/search-results/search-results.component.html index 8990e8d73f..63a3726d34 100644 --- a/projects/aca-content/src/lib/components/search/search-results/search-results.component.html +++ b/projects/aca-content/src/lib/components/search/search-results/search-results.component.html @@ -58,20 +58,44 @@ - - - - - + + + + + + + + - - - - - - - - + + + + + diff --git a/projects/aca-content/src/lib/components/search/search-results/search-results.component.spec.ts b/projects/aca-content/src/lib/components/search/search-results/search-results.component.spec.ts index ce3c18fbbb..e8614b0a6f 100644 --- a/projects/aca-content/src/lib/components/search/search-results/search-results.component.spec.ts +++ b/projects/aca-content/src/lib/components/search/search-results/search-results.component.spec.ts @@ -22,15 +22,15 @@ * from Hyland Software. If not, see . */ -import { ComponentFixture, fakeAsync, flush, TestBed, tick } from '@angular/core/testing'; +import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing'; import { SearchResultsComponent } from './search-results.component'; import { AppConfigService, TranslationService } from '@alfresco/adf-core'; import { Store } from '@ngrx/store'; import { NavigateToFolder, SnackbarErrorAction } from '@alfresco/aca-shared/store'; -import { Pagination, ResultSetPaging, SearchRequest } from '@alfresco/js-api'; -import { SearchQueryBuilderService, TagService } from '@alfresco/adf-content-services'; +import { Pagination, SearchRequest } from '@alfresco/js-api'; +import { SearchQueryBuilderService } from '@alfresco/adf-content-services'; import { ActivatedRoute, Router } from '@angular/router'; -import { BehaviorSubject, of, Subject } from 'rxjs'; +import { BehaviorSubject, Subject } from 'rxjs'; import { AppTestingModule } from '../../../testing/app-testing.module'; import { AppService } from '@alfresco/aca-shared'; @@ -274,80 +274,4 @@ describe('SearchComponent', () => { expect(queryBuilder.userQuery).toBe(`((=cm:tag:"orange"))`); expect(queryBuilder.update).toHaveBeenCalled(); }); - - describe('Dynamic Columns', () => { - let tagsService: TagService; - - beforeEach(() => { - tagsService = TestBed.inject(TagService); - - spyOn(queryBuilder['searchApi'], 'search').and.returnValue( - Promise.resolve({ - list: { - pagination: { - count: 1, - hasMoreItems: false, - totalItems: 1, - skipCount: 0, - maxItems: 25 - }, - entries: [ - { - entry: { - isFile: true, - nodeType: 'cm:content', - isFolder: false, - name: 'test-file.txt', - id: '8dd4d319-ec9f-4ea0-8276-f3b195918477' - } - } - ] - } - } as ResultSetPaging) - ); - - spyOn(queryBuilder, 'buildQuery').and.returnValue(searchRequest); - }); - - it('should not show tags column if tags are disabled', fakeAsync(() => { - spyOn(tagsService, 'areTagsEnabled').and.returnValue(false); - fixture = TestBed.createComponent(SearchResultsComponent); - fixture.detectChanges(); - queryBuilder.execute(); - tick(); - fixture.detectChanges(); - const tagsColumnHeader = fixture.nativeElement.querySelector(`[data-automation-id='auto_id_$tags']`); - expect(tagsColumnHeader).toBeNull(); - })); - - it('should show tags column if tags are enabled', fakeAsync(() => { - spyOn(tagsService, 'areTagsEnabled').and.returnValue(true); - spyOn(tagsService, 'getTagsByNodeId').and.returnValue( - of({ - list: { - pagination: { - count: 0, - hasMoreItems: false, - totalItems: 0, - skipCount: 0, - maxItems: 100 - }, - entries: [] - } - }) - ); - fixture = TestBed.createComponent(SearchResultsComponent); - fixture.detectChanges(); - queryBuilder.execute(); - tick(); - fixture.detectChanges(); - const tagsColumnHeader = fixture.nativeElement.querySelector(`[data-automation-id='auto_id_$tags']`); - expect(tagsColumnHeader).not.toBeNull(); - flush(); - })); - - afterEach(() => { - fixture.destroy(); - }); - }); }); diff --git a/projects/aca-content/src/lib/components/search/search-results/search-results.component.ts b/projects/aca-content/src/lib/components/search/search-results/search-results.component.ts index e8f516904d..875307e36f 100644 --- a/projects/aca-content/src/lib/components/search/search-results/search-results.component.ts +++ b/projects/aca-content/src/lib/components/search/search-results/search-results.component.ts @@ -59,6 +59,7 @@ import { SearchActionMenuComponent } from '../search-action-menu/search-action-m import { TagsColumnComponent } from '../../dl-custom-components/tags-column/tags-column.component'; import { MatIconModule } from '@angular/material/icon'; import { SearchResultsRowComponent } from '../search-results-row/search-results-row.component'; +import { DocumentListPresetRef, ExtensionsModule } from '@alfresco/adf-extensions'; @Component({ standalone: true, @@ -85,7 +86,8 @@ import { SearchResultsRowComponent } from '../search-results-row/search-results- PaginationDirective, ViewerModule, PageLayoutComponent, - ToolbarComponent + ToolbarComponent, + ExtensionsModule ], selector: 'aca-search-results', templateUrl: './search-results.component.html', @@ -103,6 +105,7 @@ export class SearchResultsComponent extends PageComponent implements OnInit { isLoading = false; totalResults: number; isTagsEnabled = false; + columns: DocumentListPresetRef[] = []; constructor( tagsService: TagService, @@ -157,6 +160,8 @@ export class SearchResultsComponent extends PageComponent implements OnInit { }) ); + this.columns = this.extensions.documentListPresets.searchResults || []; + if (this.route) { this.route.params.forEach((params: Params) => { // eslint-disable-next-line no-prototype-builtins diff --git a/projects/aca-shared/src/lib/services/app.extension.service.ts b/projects/aca-shared/src/lib/services/app.extension.service.ts index 74b198ed31..dc33684ce5 100644 --- a/projects/aca-shared/src/lib/services/app.extension.service.ts +++ b/projects/aca-shared/src/lib/services/app.extension.service.ts @@ -92,6 +92,7 @@ export class AppExtensionService implements RuleContext { favorites: Array; trashcan: Array; searchLibraries: Array; + searchResults: Array; } = { libraries: [], favoriteLibraries: [], @@ -99,7 +100,8 @@ export class AppExtensionService implements RuleContext { recent: [], favorites: [], trashcan: [], - searchLibraries: [] + searchLibraries: [], + searchResults: [] }; selection: SelectionState; @@ -179,7 +181,8 @@ export class AppExtensionService implements RuleContext { recent: this.getDocumentListPreset(config, 'recent'), favorites: this.getDocumentListPreset(config, 'favorites'), trashcan: this.getDocumentListPreset(config, 'trashcan'), - searchLibraries: this.getDocumentListPreset(config, 'search-libraries') + searchLibraries: this.getDocumentListPreset(config, 'search-libraries'), + searchResults: this.getDocumentListPreset(config, 'search-results') }; this.withCredentials = this.appConfig.get('auth.withCredentials', false);