Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ACS-5566] - Add configurable columns to document list #8968

Merged
merged 8 commits into from
Oct 24, 2023
Merged
2 changes: 2 additions & 0 deletions docs/content-services/components/document-list.component.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ Displays the documents from a repository.
| ---- | ---- | ------------- | ----------- |
| additionalSorting | [`DataSorting`](../../../lib/core/src/lib/datatable/data/data-sorting.model.ts) | | 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. |
| allowDropFiles | `boolean` | false | When true, this enables you to drop files directly into subfolders shown as items in the list or into another file to trigger updating it's version. When false, the dropped file will be added to the current folder (ie, the one containing all the items shown in the list). See the [Upload directive](../../core/directives/upload.directive.md) for further details about how the file drop is handled. |
| columnsPresetKey | `string` | | Key of columns preset set in extension.json|
| contentActions | `boolean` | false | Toggles content actions for each row |
| contentActionsPosition | `string` | "right" | Position of the content actions dropdown menu. Can be set to "left" or "right". |
| contextMenuActions | `boolean` | false | Toggles context menus for each row |
Expand All @@ -72,6 +73,7 @@ Displays the documents from a repository.
| includeFields | `string[]` | | Include additional information about the node in the server request. For example: association, isLink, isLocked and others. |
| loading | `boolean` | false | Toggles the loading state and animated spinners for the component. Used in combination with `navigate=false` to perform custom navigation and loading state indication. |
| locationFormat | `string` | "/" | The default route for all the location-based columns (if declared). |
| maxColumnsVisible | `number` | | Limit of possible visible columns, including "$thumbnail" column if provided |
| maxItems | `number` | | Default value is stored in the user preference settings. Use this only if you are not using pagination. |
| multiselect | `boolean` | false | Toggles multiselect mode |
| navigate | `boolean` | true | Toggles navigation to folder content or file preview |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
[contextMenu]="contextMenuActions"
[rowStyle]="rowStyle"
[rowStyleClass]="rowStyleClass"
[showMainDatatableActions]="true"
[loading]="loading"
[display]="display"
[noPermission]="noPermission"
Expand Down Expand Up @@ -72,4 +73,16 @@
</ng-template>
</adf-loading-content-template>

<adf-main-menu-datatable-template>
<ng-template let-mainMenuTrigger>
<adf-datatable-column-selector
[columns]="data.getColumns()"
[mainMenuTrigger]="mainMenuTrigger"
[columnsSorting]="false"
[maxColumnsVisible]="maxColumnsVisible"
(submitColumnsVisibility)="onColumnsVisibilityChange($event)">
</adf-datatable-column-selector>
</ng-template>
</adf-main-menu-datatable-template>

</adf-datatable>
Original file line number Diff line number Diff line change
Expand Up @@ -132,57 +132,39 @@ describe('DocumentList', () => {
});
};

it('should load -trashcan- preset', async () => {
documentList.currentFolderId = '-trashcan-';

fixture.detectChanges();
await fixture.whenStable();

it('should load -trashcan- preset', () => {
documentList.presetColumn = '-trashcan-';
documentList.ngAfterContentInit();
DenysVuika marked this conversation as resolved.
Show resolved Hide resolved
validatePreset(['$thumbnail', 'name', 'path', 'content.sizeInBytes', 'archivedAt', 'archivedByUser.displayName']);
});

it('should load -sites- preset', async () => {
documentList.currentFolderId = '-sites-';

fixture.detectChanges();
await fixture.whenStable();

documentList.presetColumn = '-sites-';
documentList.ngAfterContentInit();
validatePreset(['$thumbnail', 'title', 'visibility']);
});

it('shuld load -mysites- preset', async () => {
documentList.currentFolderId = '-mysites-';

fixture.detectChanges();
await fixture.whenStable();

documentList.presetColumn = '-mysites-';
documentList.ngAfterContentInit();
validatePreset(['$thumbnail', 'title', 'visibility']);
});

it('should load -favorites- preset', async () => {
documentList.currentFolderId = '-favorites-';

fixture.detectChanges();
await fixture.whenStable();

documentList.presetColumn = '-favorites-';
documentList.ngAfterContentInit();
validatePreset(['$thumbnail', 'name', 'path', 'content.sizeInBytes', 'modifiedAt', 'modifiedByUser.displayName']);
});

it('should load -recent- preset', async () => {
documentList.currentFolderId = '-recent-';

fixture.detectChanges();
await fixture.whenStable();

documentList.presetColumn = '-recent-';
documentList.ngAfterContentInit();
validatePreset(['$thumbnail', 'name', 'path', 'content.sizeInBytes', 'modifiedAt']);
});

it('should load -sharedlinks- preset', async () => {
documentList.currentFolderId = '-sharedlinks-';

fixture.detectChanges();
await fixture.whenStable();

documentList.presetColumn = '-sharedlinks-';
documentList.ngAfterContentInit();
validatePreset([
'$thumbnail',
'name',
Expand All @@ -195,11 +177,8 @@ describe('DocumentList', () => {
});

it('should load default preset', async () => {
documentList.currentFolderId = 'f5dacdb9-6d07-4fe9-9f2a-dedc21bae603';

fixture.detectChanges();
await fixture.whenStable();

documentList.presetColumn = 'f5dacdb9-6d07-4fe9-9f2a-dedc21bae603';
documentList.ngAfterContentInit();
validatePreset(['$thumbnail', 'name', 'content.sizeInBytes', 'modifiedAt', 'modifiedByUser.displayName']);
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,11 @@ import { ContentService } from '../../common/services/content.service';

import {
DataCellEvent,
DataColumn,
DataRowActionEvent,
DataSorting,
DataTableComponent,
DisplayMode,
ShowHeaderMode,
ObjectDataColumn,
PaginatedComponent,
AppConfigService,
DataColumnListComponent,
Expand All @@ -58,7 +56,9 @@ import {
AlfrescoApiService,
UserPreferenceValues,
DataRow,
DataTableService
DataTableService,
DataTableSchema,
DataColumn
} from '@alfresco/adf-core';
import { NodesApiService } from '../../common/services/nodes-api.service';

Expand Down Expand Up @@ -97,7 +97,7 @@ const BYTES_TO_MB_CONVERSION_VALUE = 1048576;
encapsulation: ViewEncapsulation.None,
host: { class: 'adf-document-list' }
})
export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, AfterContentInit, PaginatedComponent, NavigableComponentInterface {
export class DocumentListComponent extends DataTableSchema implements OnInit, OnChanges, OnDestroy, AfterContentInit, PaginatedComponent, NavigableComponentInterface {
static SINGLE_CLICK_NAVIGATION: string = 'click';
static DOUBLE_CLICK_NAVIGATION: string = 'dblclick';

Expand Down Expand Up @@ -315,6 +315,14 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte
@Input()
maxItems: number = this.DEFAULT_PAGINATION.maxItems;

/** Key of columns preset set in extension.json */
@Input()
columnsPresetKey?: string;

/** Limit of possible visible columns, including "$thumbnail" column if provided */
@Input()
maxColumnsVisible?: number;

/** Emitted when the user clicks a list node */
@Output()
nodeClick = new EventEmitter<NodeEntityEvent>();
Expand Down Expand Up @@ -371,7 +379,6 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte
pagination: BehaviorSubject<PaginationModel> = new BehaviorSubject<PaginationModel>(this.DEFAULT_PAGINATION);
sortingSubject: BehaviorSubject<DataSorting[]> = new BehaviorSubject<DataSorting[]>(this.DEFAULT_SORTING);

private layoutPresets = {};
private rowMenuCache: { [key: string]: ContentActionModel[] } = {};
private loadingTimeout: any;
private onDestroy$ = new Subject<boolean>();
Expand All @@ -395,6 +402,7 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte
private lockService: LockService,
private dialog: MatDialog
) {
super(appConfig, 'default', presetsDefaultModel);
this.nodeService.nodeUpdated.pipe(takeUntil(this.onDestroy$)).subscribe((node) => {
this.dataTableService.rowUpdate.next({ id: node.id, obj: { entry: node } });
});
Expand All @@ -421,10 +429,6 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte
return null;
}

private get hasCustomLayout(): boolean {
return this.columnList?.columns?.length > 0;
}

private getDefaultSorting(): DataSorting {
let defaultSorting: DataSorting;
if (Array.isArray(this.sorting)) {
Expand All @@ -437,9 +441,6 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte
return defaultSorting;
}

private getLayoutPreset(name: string = 'default'): DataColumn[] {
return (this.layoutPresets[name] || this.layoutPresets['default']).map((col) => new ObjectDataColumn(col));
}

isMobile(): boolean {
return !!/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
Expand Down Expand Up @@ -477,34 +478,21 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte
if (this.filterValue && Object.keys(this.filterValue).length > 0) {
this.showHeader = ShowHeaderMode.Always;
}
if (this.columnsPresetKey) {
this.setPresetKey(this.columnsPresetKey);
}
}

ngAfterContentInit() {
if (this.columnList) {
this.columnList.columns.changes.pipe(takeUntil(this.onDestroy$)).subscribe(() => this.setTableSchema());
}
this.setTableSchema();
}

private setTableSchema() {
let schema: DataColumn[] = [];

if (this.hasCustomLayout) {
schema = this.columnList.columns.map((c) => c as DataColumn);
}

if (!this.data) {
this.data = new ShareDataTableAdapter(this.thumbnailService, this.contentService, schema, this.getDefaultSorting(), this.sortingMode);
} else if (schema && schema.length > 0) {
this.data.setColumns(schema);
}

const columns = this.data.getColumns();
if (!columns || columns.length === 0) {
this.setupDefaultColumns(this.currentFolderId);
this.columnList.columns.changes.pipe(takeUntil(this.onDestroy$)).subscribe(() => {
this.createColumns();
this.data.setColumns(this.columns);
});
}
this.createDatatableSchema();
this.data.setColumns(this.columns);
}

ngOnChanges(changes: SimpleChanges) {
if (!changes['preselectNodes']) {
this.resetSelection();
Expand Down Expand Up @@ -718,10 +706,6 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte
this.setLoadingState(true);
}

if (!this.hasCustomLayout) {
this.setupDefaultColumns(this.currentFolderId);
}

if (this.documentListService.isCustomSourceService(this.currentFolderId)) {
this.updateCustomSourceData(this.currentFolderId);
}
Expand Down Expand Up @@ -772,17 +756,6 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte
return [`${this.additionalSorting.key} ${this.additionalSorting.direction}`, `${currentKey} ${currentDirection}`];
}

/**
* Creates a set of predefined columns.
* @param preset preset to use
*/
private setupDefaultColumns(preset: string = 'default'): void {
if (this.data) {
const columns = this.getLayoutPreset(preset);
this.data.setColumns(columns);
}
}

onPreviewFile(node: NodeEntry) {
if (node) {
const sizeInMB = node.entry?.content?.sizeInBytes / BYTES_TO_MB_CONVERSION_VALUE;
Expand All @@ -797,7 +770,9 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte
}
}
}

onColumnsVisibilityChange(updatedColumns: Array<DataColumn>): void {
this.data.setColumns(updatedColumns);
}
onNodeClick(nodeEntry: NodeEntry) {
const domEvent = new CustomEvent('node-click', {
detail: {
Expand Down Expand Up @@ -932,10 +907,6 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte
return canNavigateFolder;
}

private loadLayoutPresets(): void {
const externalSettings = this.appConfig.get('document-list.presets', null);
this.layoutPresets = externalSettings ? Object.assign({}, presetsDefaultModel, externalSettings) : presetsDefaultModel;
}

private onDataReady(nodePaging: NodePaging) {
this.ready.emit(nodePaging);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
class="adf-columns-selector-column-checkbox"
[attr.data-automation-id]="'adf-columns-selector-column-checkbox-' + column.title"
[checked]="!column.isHidden"
[disabled]="isCheckboxDisabled(column)"
(change)="changeColumnVisibility(column)">
<div class="adf-columns-selector-list-content">{{translatedTitle}}</div>
</mat-checkbox>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,19 +156,35 @@ describe('ColumnsSelectorComponent', () => {
expect(toggledColumnItem.isHidden).toBe(true);
});

it('should set proper default state for checkboxes', async () => {
menuOpenedTrigger.next();
fixture.detectChanges();

const checkboxes = await loader.getAllHarnesses(MatCheckboxHarness);
describe('checkboxes', () => {
it('should have set proper default state', async () => {
menuOpenedTrigger.next();
fixture.detectChanges();

const checkboxes = await loader.getAllHarnesses(MatCheckboxHarness);

expect(await checkboxes[0].isChecked()).toBe(true);
expect(await checkboxes[1].isChecked()).toBe(true);
expect(await checkboxes[2].isChecked()).toBe(true);
expect(await checkboxes[3].isChecked()).toBe(false);
});

it('should be disabled when visible columns limit is reached', async () => {
component.maxColumnsVisible = 4;
menuOpenedTrigger.next();
fixture.detectChanges();

expect(await checkboxes[0].isChecked()).toBe(true);
expect(await checkboxes[1].isChecked()).toBe(true);
expect(await checkboxes[2].isChecked()).toBe(true);
expect(await checkboxes[3].isChecked()).toBe(false);
const checkboxes = await loader.getAllHarnesses(MatCheckboxHarness);

expect(await checkboxes[0].isDisabled()).toBe(false);
expect(await checkboxes[1].isDisabled()).toBe(false);
expect(await checkboxes[2].isDisabled()).toBe(false);
expect(await checkboxes[3].isDisabled()).toBe(true);
});
});

it('should show hidden columns at the end of the list', async () => {
describe('sorting', () => {
const hiddenDataColumn: DataColumn = {
id: 'hiddenDataColumn',
title: 'hiddenDataColumn',
Expand All @@ -183,14 +199,27 @@ describe('ColumnsSelectorComponent', () => {
key: 'shownDataColumn',
type: 'text'
};
it('should show hidden columns at the end of the list by default', async () => {
component.columns = [hiddenDataColumn, shownDataColumn];
menuOpenedTrigger.next();
fixture.detectChanges();

component.columns = [hiddenDataColumn, shownDataColumn];
menuOpenedTrigger.next();
fixture.detectChanges();
const checkboxes = await loader.getAllHarnesses(MatCheckboxHarness);

const checkboxes = await loader.getAllHarnesses(MatCheckboxHarness);
expect(await checkboxes[0].getLabelText()).toBe(shownDataColumn.title);
expect(await checkboxes[1].getLabelText()).toBe(hiddenDataColumn.title);
});

it('should NOT show hidden columns at the end of the list if sorting is disabled', async () => {
component.columns = [hiddenDataColumn, shownDataColumn];
component.columnsSorting = false;
menuOpenedTrigger.next();
fixture.detectChanges();

const checkboxes = await loader.getAllHarnesses(MatCheckboxHarness);

expect(await checkboxes[0].getLabelText()).toBe(shownDataColumn.title);
expect(await checkboxes[1].getLabelText()).toBe(hiddenDataColumn.title);
expect(await checkboxes[0].getLabelText()).toBe(hiddenDataColumn.title);
expect(await checkboxes[1].getLabelText()).toBe(shownDataColumn.title);
});
});
});
Loading
Loading