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);