diff --git a/demo-shell/src/app/components/files/files.component.html b/demo-shell/src/app/components/files/files.component.html index ba891abda0..8db9dd3ecf 100644 --- a/demo-shell/src/app/components/files/files.component.html +++ b/demo-shell/src/app/components/files/files.component.html @@ -13,6 +13,7 @@ [currentFolderId]="'-recent-'" locationFormat="/files" [display]="'gallery'" + [preselectNodes]="selectedNodes" [showHeader]="false" [maxItems]="5" (preview)="showFile($event)" @@ -221,6 +222,7 @@ [contentActions]="true" [allowDropFiles]="allowDropFiles" [selectionMode]="selectionMode" + [preselectNodes]="selectedNodes" [multiselect]="multiselect" [display]="displayMode" [node]="nodeResult" @@ -550,7 +552,7 @@
- + {{'DOCUMENT_LIST.MULTIPLE_FILE_UPLOAD' | translate}}
@@ -643,6 +645,21 @@ +
+ + Preselect Nodes + +
+ +
+ + + +
+
Upload
diff --git a/demo-shell/src/app/components/files/files.component.scss b/demo-shell/src/app/components/files/files.component.scss index 79bb0c0db7..db5b54b3fa 100644 --- a/demo-shell/src/app/components/files/files.component.scss +++ b/demo-shell/src/app/components/files/files.component.scss @@ -88,4 +88,8 @@ justify-content: center; } } + + .adf-preselect-nodes-input { + width: 100%; + } } diff --git a/demo-shell/src/app/components/files/files.component.ts b/demo-shell/src/app/components/files/files.component.ts index 6f654785b6..6b4c4afbe2 100644 --- a/demo-shell/src/app/components/files/files.component.ts +++ b/demo-shell/src/app/components/files/files.component.ts @@ -23,13 +23,14 @@ import { Location } from '@angular/common'; import { MatDialog } from '@angular/material/dialog'; import { ActivatedRoute, Params, Router } from '@angular/router'; -import { MinimalNodeEntity, NodePaging, Pagination, MinimalNodeEntryEntity, SiteEntry, SearchEntry } from '@alfresco/js-api'; +import { MinimalNodeEntity, NodePaging, Pagination, MinimalNodeEntryEntity, SiteEntry, SearchEntry, NodeEntry } from '@alfresco/js-api'; import { AlfrescoApiService, AuthenticationService, AppConfigService, AppConfigValues, ContentService, TranslationService, FolderCreatedEvent, LogService, NotificationService, UploadService, DataRow, UserPreferencesService, PaginationComponent, FormValues, DisplayMode, ShowHeaderMode, InfinitePaginationComponent, HighlightDirective, SharedLinksApiService, - FormRenderingService + FormRenderingService, + FileUploadEvent } from '@alfresco/adf-core'; import { @@ -47,7 +48,7 @@ import { VersionManagerDialogAdapterComponent } from './version-manager-dialog-a import { MetadataDialogAdapterComponent } from './metadata-dialog-adapter.component'; import { Subject } from 'rxjs'; import { PreviewService } from '../../services/preview.service'; -import { takeUntil } from 'rxjs/operators'; +import { takeUntil, debounceTime, scan } from 'rxjs/operators'; const DEFAULT_FOLDER_TO_SHOW = '-my-'; @@ -208,6 +209,7 @@ export class FilesComponent implements OnInit, OnChanges, OnDestroy { permissionsStyle: PermissionStyleModel[] = []; infiniteScrolling: boolean; stickyHeader: boolean; + preselectNodes: boolean; warnOnMultipleUploads = false; thumbnails = false; @@ -216,6 +218,8 @@ export class FilesComponent implements OnInit, OnChanges, OnDestroy { displayEmptyMetadata = false; hyperlinkNavigation = false; + selectedNodes = []; + constructor(private notificationService: NotificationService, private uploadService: UploadService, private contentService: ContentService, @@ -277,6 +281,33 @@ export class FilesComponent implements OnInit, OnChanges, OnDestroy { }); } + this.uploadService.fileUploadComplete + .pipe( + debounceTime(300), + scan((files, currentFile) => [...files, currentFile], []), + takeUntil(this.onDestroy$) + ) + .subscribe((value: any[]) => { + let selectedNodes: NodeEntry[] = []; + + if (this.preselectNodes) { + if (value && value.length > 0 ) { + if (this.selectionMode === 'single') { + selectedNodes = [...[value[value.length - 1]].map((uploadedFile) => uploadedFile.data)]; + } else { + selectedNodes = [...value.map((uploadedFile) => uploadedFile.data)]; + } + this.selectedNodes = [...selectedNodes]; + } + } + + this.onFileUploadEvent(value[0]); + }); + + this.uploadService.fileUploadDeleted + .pipe(takeUntil(this.onDestroy$)) + .subscribe(value => this.onFileUploadEvent(value)); + this.contentService.folderCreated .pipe(takeUntil(this.onDestroy$)) .subscribe(value => this.onFolderCreated(value)); @@ -302,6 +333,12 @@ export class FilesComponent implements OnInit, OnChanges, OnDestroy { }); } + onFileUploadEvent(event: FileUploadEvent) { + if (event && event.file.options.parentId === this.documentList.currentFolderId) { + this.documentList.reload(); + } + } + ngOnDestroy() { this.onDestroy$.next(true); this.onDestroy$.complete(); @@ -669,4 +706,30 @@ export class FilesComponent implements OnInit, OnChanges, OnDestroy { this.router.navigate([], { relativeTo: this.route, queryParams: objectFromMap }); } + setPreselectNodes(nodes: string) { + this.selectedNodes = this.getArrayFromString(nodes); + this.documentList.reload(); + } + + isStringArray(str: string): boolean { + try { + const result = JSON.parse(str); + return Array.isArray(result); + } catch (e) { + return false; + } + } + + private getArrayFromString(value: string): T[] { + if (this.isStringArray(value)) { + return JSON.parse(value); + } else { + return []; + } + } + + onMultipleFilesUpload() { + this.selectedNodes = []; + } + } diff --git a/docs/content-services/components/document-list.component.md b/docs/content-services/components/document-list.component.md index d0b7af639d..16b81339e1 100644 --- a/docs/content-services/components/document-list.component.md +++ b/docs/content-services/components/document-list.component.md @@ -73,6 +73,7 @@ Displays the documents from a repository. | navigationMode | `string` | | [User](../../../lib/core/pipes/user-initial.pipe.ts) interaction for folder navigation or file preview. Valid values are "click" and "dblclick". Default value: "dblclick" | | node | [`NodePaging`](https://github.com/Alfresco/alfresco-js-api/blob/develop/src/api/content-rest-api/docs/NodePaging.md) | null | The Document list will show all the nodes contained in the [NodePaging](https://github.com/Alfresco/alfresco-js-api/blob/develop/src/api/content-rest-api/docs/NodePaging.md) entity | | permissionsStyle | [`PermissionStyleModel`](../../../lib/content-services/src/lib/document-list/models/permissions-style.model.ts)`[]` | \[] | Define a set of CSS styles to apply depending on the permission of the user on that node. See the [Permission Style model](../../../lib/content-services/src/lib/document-list/models/permissions-style.model.ts) page for further details and examples. | +| preselectNodes | [`NodeEntry`](https://github.com/Alfresco/alfresco-js-api/blob/master/src/alfresco-core-rest-api/docs/NodeEntry.md) `[]` | \[] | Array of nodes to preselect . | | rowStyle | `string` | | The inline style to apply to every row. See the Angular NgStyle docs for more details and usage examples. | | rowStyleClass | `string` | | The CSS class to apply to every row | | selectionMode | `string` | "single" | Row selection mode. Can be null, `single` or `multiple`. For `multiple` mode, you can use Cmd (macOS) or Ctrl (Win) modifier key to toggle selection for multiple rows. | diff --git a/lib/content-services/src/lib/content-node-selector/content-node-selector-panel.component.html b/lib/content-services/src/lib/content-node-selector/content-node-selector-panel.component.html index ad626731a4..23592e57e1 100644 --- a/lib/content-services/src/lib/content-node-selector/content-node-selector-panel.component.html +++ b/lib/content-services/src/lib/content-node-selector/content-node-selector-panel.component.html @@ -61,6 +61,7 @@ adf-highlight-selector=".adf-name-location-cell-name" [showHeader]="false" [node]="nodePaging" + [preselectNodes]="preselectNodes" [maxItems]="pageSize" [rowFilter]="_rowFilter" [imageResolver]="imageResolver" diff --git a/lib/content-services/src/lib/content-node-selector/content-node-selector-panel.component.ts b/lib/content-services/src/lib/content-node-selector/content-node-selector-panel.component.ts index d4fec5ac1b..4719de8554 100644 --- a/lib/content-services/src/lib/content-node-selector/content-node-selector-panel.component.ts +++ b/lib/content-services/src/lib/content-node-selector/content-node-selector-panel.component.ts @@ -23,7 +23,9 @@ import { UserPreferenceValues, InfinitePaginationComponent, PaginatedComponent, NodesApiService, - SitesService + SitesService, + UploadService, + FileUploadCompleteEvent } from '@alfresco/adf-core'; import { FormControl } from '@angular/forms'; import { Node, NodePaging, Pagination, SiteEntry, SitePaging, NodeEntry } from '@alfresco/js-api'; @@ -31,7 +33,7 @@ import { DocumentListComponent } from '../document-list/components/document-list import { RowFilter } from '../document-list/data/row-filter.model'; import { ImageResolver } from '../document-list/data/image-resolver.model'; import { ContentNodeSelectorService } from './content-node-selector.service'; -import { debounceTime, takeUntil } from 'rxjs/operators'; +import { debounceTime, takeUntil, scan } from 'rxjs/operators'; import { CustomResourcesService } from '../document-list/services/custom-resources.service'; import { NodeEntryEvent, ShareDataRow } from '../document-list'; import { Subject } from 'rxjs'; @@ -222,6 +224,7 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy { searchInput: FormControl = new FormControl(); target: PaginatedComponent; + preselectNodes: NodeEntry[] = []; private onDestroy$ = new Subject(); @@ -229,6 +232,7 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy { private customResourcesService: CustomResourcesService, private userPreferencesService: UserPreferencesService, private nodesApiService: NodesApiService, + private uploadService: UploadService, private sitesService: SitesService) { } @@ -267,6 +271,7 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy { this.breadcrumbTransform = this.breadcrumbTransform ? this.breadcrumbTransform : null; this.isSelectionValid = this.isSelectionValid ? this.isSelectionValid : defaultValidation; + this.onFileUploadEvent(); } ngOnDestroy() { @@ -274,6 +279,19 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy { this.onDestroy$.complete(); } + private onFileUploadEvent() { + this.uploadService.fileUploadComplete + .pipe( + debounceTime(300), + scan((files, currentFile) => [...files, currentFile], []), + takeUntil(this.onDestroy$) + ) + .subscribe((uploadedFiles: FileUploadCompleteEvent[]) => { + this.preselectNodes = this.getPreselectNodesBasedOnSelectionMode(uploadedFiles); + this.documentList.reload(); + }); + } + private getStartSite() { this.nodesApiService.getNode(this.currentFolderId).subscribe((startNodeEntry) => { this.startSiteGuid = this.sitesService.getSiteNameFromNodePath(startNodeEntry); @@ -363,7 +381,7 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy { this.searchTerm = ''; this.nodePaging = null; this.pagination.maxItems = this.pageSize; - this.chosenNode = null; + this.resetChosenNode(); this.showingSearchResults = false; } @@ -434,6 +452,7 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy { this.showingSearchResults = false; this.infiniteScroll = false; this.breadcrumbFolderTitle = null; + this.preselectNodes = []; this.clearSearch(); this.navigationChange.emit($event); } @@ -476,8 +495,6 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy { private attemptNodeSelection(entry: Node): void { if (entry && this.isSelectionValid(entry)) { this.chosenNode = [entry]; - } else { - this.resetChosenNode(); } } @@ -510,4 +527,26 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy { this.breadcrumbFolderTitle = null; } } + + hasPreselectNodes(): boolean { + return this.preselectNodes && this.preselectNodes.length > 0; + } + + isSingleSelectionMode(): boolean { + return this.selectionMode === 'single'; + } + + private getPreselectNodesBasedOnSelectionMode(uploadedFiles: FileUploadCompleteEvent[]): NodeEntry[] { + let selectedNodes: NodeEntry[] = []; + + if (uploadedFiles && uploadedFiles.length > 0 ) { + if (this.isSingleSelectionMode()) { + selectedNodes = [...[uploadedFiles[uploadedFiles.length - 1]].map((uploadedFile) => uploadedFile.data)]; + } else { + selectedNodes = [...uploadedFiles.map((uploadedFile) => uploadedFile.data)]; + } + } + + return selectedNodes; + } } diff --git a/lib/content-services/src/lib/document-list/components/document-list.component.spec.ts b/lib/content-services/src/lib/document-list/components/document-list.component.spec.ts index 34d4e3d9e6..61f9a5d7d2 100644 --- a/lib/content-services/src/lib/document-list/components/document-list.component.spec.ts +++ b/lib/content-services/src/lib/document-list/components/document-list.component.spec.ts @@ -26,7 +26,8 @@ import { DataTableComponent, DataTableModule, ObjectDataTableAdapter, - ShowHeaderMode + ShowHeaderMode, + ThumbnailService } from '@alfresco/adf-core'; import { Subject, of, throwError } from 'rxjs'; import { @@ -34,7 +35,9 @@ import { fakeNodeAnswerWithNOEntries, fakeNodeWithNoPermission, fakeGetSitesAnswer, - fakeGetSiteMembership + fakeGetSiteMembership, + mockPreselectedNodes, + mockNodePagingWithPreselectedNodes } from '../../mock'; import { ContentActionModel } from '../models/content-action.model'; import { NodeMinimal, NodeMinimalEntry, NodePaging } from '../models/document-library.model'; @@ -56,6 +59,7 @@ describe('DocumentList', () => { let documentListService: DocumentListService; let apiService: AlfrescoApiService; let customResourcesService: CustomResourcesService; + let thumbnailService: ThumbnailService; let fixture: ComponentFixture; let element: HTMLElement; let eventMock: any; @@ -86,6 +90,7 @@ describe('DocumentList', () => { documentListService = TestBed.inject(DocumentListService); apiService = TestBed.inject(AlfrescoApiService); customResourcesService = TestBed.inject(CustomResourcesService); + thumbnailService = TestBed.inject(ThumbnailService); spyFolder = spyOn(documentListService, 'getFolder').and.callFake(() => { return Promise.resolve({ list: {} }); @@ -1515,6 +1520,74 @@ describe('DocumentList', () => { where: undefined }), undefined); }); + + describe('Preselect nodes', () => { + + beforeEach(() => { + spyOn(thumbnailService, 'getMimeTypeIcon').and.returnValue(`assets/images/ft_ic_created.svg`); + }); + + it('should able to emit nodeSelected event with preselectNodes on the reload', async () => { + const nodeSelectedSpy = spyOn(documentList.nodeSelected, 'emit'); + + fixture.detectChanges(); + + documentList.node = mockNodePagingWithPreselectedNodes; + documentList.preselectNodes = mockPreselectedNodes; + documentList.reload(); + + fixture.detectChanges(); + await fixture.whenStable(); + + expect(documentList.preselectNodes.length).toBe(2); + expect(nodeSelectedSpy).toHaveBeenCalled(); + }); + + it('should able to select first node from the preselectNodes when selectionMode set to single', async () => { + documentList.selectionMode = 'single'; + fixture.detectChanges(); + + documentList.node = mockNodePagingWithPreselectedNodes; + documentList.preselectNodes = mockPreselectedNodes; + documentList.reload(); + + fixture.detectChanges(); + await fixture.whenStable(); + + expect(documentList.preselectNodes.length).toBe(2); + expect(documentList.getPreselectNodesBasedOnSelectionMode().length).toBe(1); + }); + + it('should able to select all preselectNodes when selectionMode set to multiple', async () => { + documentList.selectionMode = 'multiple'; + fixture.detectChanges(); + + documentList.node = mockNodePagingWithPreselectedNodes; + documentList.preselectNodes = mockPreselectedNodes; + documentList.reload(); + + fixture.detectChanges(); + await fixture.whenStable(); + + expect(documentList.preselectNodes.length).toBe(2); + expect(documentList.getPreselectNodesBasedOnSelectionMode().length).toBe(2); + }); + + it('should not emit nodeSelected event when preselectNodes undefined/empty', async () => { + const nodeSelectedSpy = spyOn(documentList.nodeSelected, 'emit'); + + fixture.detectChanges(); + + documentList.node = mockNodePagingWithPreselectedNodes; + documentList.preselectNodes = []; + documentList.reload(); + + fixture.detectChanges(); + await fixture.whenStable(); + + expect(nodeSelectedSpy).not.toHaveBeenCalled(); + }); + }); }); @Component({ diff --git a/lib/content-services/src/lib/document-list/components/document-list.component.ts b/lib/content-services/src/lib/document-list/components/document-list.component.ts index 079c7faefe..d6b50c14c2 100644 --- a/lib/content-services/src/lib/document-list/components/document-list.component.ts +++ b/lib/content-services/src/lib/document-list/components/document-list.component.ts @@ -46,7 +46,7 @@ import { AlfrescoApiService, UserPreferenceValues, LockService, - UploadService + DataRow } from '@alfresco/adf-core'; import { Node, NodeEntry, NodePaging, Pagination } from '@alfresco/js-api'; @@ -61,7 +61,7 @@ import { NavigableComponentInterface } from '../../breadcrumb/navigable-componen import { RowFilter } from '../data/row-filter.model'; import { DocumentListService } from '../services/document-list.service'; import { DocumentLoaderNode } from '../models/document-folder.model'; -import { debounceTime, takeUntil } from 'rxjs/operators'; +import { takeUntil } from 'rxjs/operators'; @Component({ selector: 'adf-document-list', @@ -259,6 +259,13 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte @Input() currentFolderId: string = null; + /** Array of nodes to be pre-selected. All nodes in the + * array are pre-selected in multi selection mode, but only the first node + * is pre-selected in single selection mode. + */ + @Input() + preselectNodes: NodeEntry[] = []; + /** The Document list will show all the nodes contained in the NodePaging entity */ @Input() node: NodePaging = null; @@ -327,7 +334,6 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte private appConfig: AppConfigService, private userPreferencesService: UserPreferencesService, private contentService: ContentService, - private uploadService: UploadService, private thumbnailService: ThumbnailService, private alfrescoApiService: AlfrescoApiService, private lockService: LockService) { @@ -337,18 +343,6 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte .subscribe(pagSize => { this.maxItems = this._pagination.maxItems = pagSize; }); - - this.uploadService.fileUploadComplete - .pipe( - debounceTime(300), - takeUntil(this.onDestroy$)) - .subscribe(() => this.reload()); - - this.uploadService.fileUploadDeleted - .pipe( - debounceTime(300), - takeUntil(this.onDestroy$)) - .subscribe(() => this.reload()); } getContextActions(node: NodeEntry) { @@ -469,7 +463,8 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte if (changes['currentFolderId']?.currentValue !== changes['currentFolderId']?.previousValue) { if (this.data) { - this.data.loadPage(null, false); + this.data.loadPage(null, false, null, this.getPreselectNodesBasedOnSelectionMode()); + this.onPreselectNodes(); this.resetNewFolderPagination(); } @@ -483,7 +478,8 @@ 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.data.loadPage(changes.node.currentValue, merge, null, this.getPreselectNodesBasedOnSelectionMode()); + this.onPreselectNodes(); this.onDataReady(changes.node.currentValue); } else if (changes.imageResolver) { this.data.setImageResolver(changes.imageResolver.currentValue); @@ -495,7 +491,8 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte this.ngZone.run(() => { this.resetSelection(); if (this.node) { - this.data.loadPage(this.node, this._pagination.merge); + this.data.loadPage(this.node, this._pagination.merge, null, this.getPreselectNodesBasedOnSelectionMode()); + this.onPreselectNodes(); this.onDataReady(this.node); } else { this.loadFolder(); @@ -685,7 +682,8 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte onPageLoaded(nodePaging: NodePaging) { if (nodePaging) { - this.data.loadPage(nodePaging, this._pagination.merge, this.allowDropFiles); + this.data.loadPage(nodePaging, this._pagination.merge, this.allowDropFiles, this.getPreselectNodesBasedOnSelectionMode()); + this.onPreselectNodes(); this.setLoadingState(false); this.onDataReady(nodePaging); } @@ -787,7 +785,7 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte this.selection = event.selection.map((entry) => entry.node); const domEvent = new CustomEvent('node-select', { detail: { - node: event.row.node, + node: event.row ? event.row.node : null, selection: this.selection }, bubbles: true @@ -898,4 +896,40 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte this.setLoadingState(false); this.error.emit(err); } + + getPreselectNodesBasedOnSelectionMode(): NodeEntry[] { + let selectedNodes: NodeEntry[] = []; + + if (this.hasPreselectNodes()) { + if (this.isSingleSelectionMode()) { + selectedNodes = [this.preselectNodes[0]]; + } else { + selectedNodes = this.preselectNodes; + } + } + + return selectedNodes; + } + + private onPreselectNodes() { + if (this.hasPreselectNodes()) { + let selectedNodes: DataRow[] = []; + + if (this.isSingleSelectionMode()) { + selectedNodes = [this.data.getPreselectRows()[0]]; + } else { + selectedNodes = this.data.getPreselectRows(); + } + + this.onNodeSelect({ row: undefined, selection: selectedNodes }); + } + } + + isSingleSelectionMode(): boolean { + return this.selectionMode === 'single'; + } + + hasPreselectNodes(): boolean { + return this.preselectNodes && this.preselectNodes.length > 0; + } } diff --git a/lib/content-services/src/lib/document-list/data/share-datatable-adapter.spec.ts b/lib/content-services/src/lib/document-list/data/share-datatable-adapter.spec.ts index e68f7130ea..b5a2ca2b29 100644 --- a/lib/content-services/src/lib/document-list/data/share-datatable-adapter.spec.ts +++ b/lib/content-services/src/lib/document-list/data/share-datatable-adapter.spec.ts @@ -16,7 +16,7 @@ */ import { DataColumn, DataRow, DataSorting, ContentService, ThumbnailService, setupTestBed } from '@alfresco/adf-core'; -import { FileNode, FolderNode, SmartFolderNode, RuleFolderNode, LinkFolderNode } from './../../mock'; +import { FileNode, FolderNode, SmartFolderNode, RuleFolderNode, LinkFolderNode, mockPreselectedNodes, mockNodePagingWithPreselectedNodes, mockNode2, fakeNodePaging } from './../../mock'; import { ShareDataRow } from './share-data-row.model'; import { ShareDataTableAdapter } from './share-datatable-adapter'; import { ContentTestingModule } from '../../testing/content.testing.module'; @@ -481,4 +481,31 @@ describe('ShareDataTableAdapter', () => { expect(row.isDropTarget).toBeFalsy(); }); }); + + describe('Preselect rows', () => { + + it('should set isSelected to be true for each preselectRow if the preselectNodes are defined', () => { + const adapter = new ShareDataTableAdapter(thumbnailService, contentService, []); + adapter.loadPage(mockNodePagingWithPreselectedNodes, null, null, mockPreselectedNodes); + + expect(adapter.getPreselectRows().length).toBe(1); + expect(adapter.getPreselectRows()[0].isSelected).toBe(true); + }); + + it('should set preselectRows empty if preselectedNodes are undefined/empty', () => { + const adapter = new ShareDataTableAdapter(thumbnailService, contentService, []); + adapter.loadPage(mockNodePagingWithPreselectedNodes, null, null, []); + + expect(adapter.getPreselectRows().length).toBe(0); + }); + + it('should set preselectRows empty if preselectedNodes are not found in the list', () => { + const adapter = new ShareDataTableAdapter(thumbnailService, contentService, []); + mockNode2.id = 'mock-file-id'; + const preselectedNode = [ { entry: mockNode2 }]; + adapter.loadPage(fakeNodePaging, null, null, preselectedNode); + + expect(adapter.getPreselectRows().length).toBe(0); + }); + }); }); diff --git a/lib/content-services/src/lib/document-list/data/share-datatable-adapter.ts b/lib/content-services/src/lib/document-list/data/share-datatable-adapter.ts index c95adb906a..d4a2e4e499 100644 --- a/lib/content-services/src/lib/document-list/data/share-datatable-adapter.ts +++ b/lib/content-services/src/lib/document-list/data/share-datatable-adapter.ts @@ -45,6 +45,7 @@ export class ShareDataTableAdapter implements DataTableAdapter { permissionsStyle: PermissionStyleModel[]; selectedRow: DataRow; allowDropFiles: boolean; + preselectRows: DataRow[] = []; set sortingMode(value: string) { let newValue = (value || 'client').toLowerCase(); @@ -81,6 +82,10 @@ export class ShareDataTableAdapter implements DataTableAdapter { this.sort(); } + getPreselectRows(): Array { + return this.preselectRows; + } + getColumns(): Array { return this.columns; } @@ -245,7 +250,7 @@ export class ShareDataTableAdapter implements DataTableAdapter { } } - public loadPage(nodePaging: NodePaging, merge: boolean = false, allowDropFiles?: boolean) { + public loadPage(nodePaging: NodePaging, merge: boolean = false, allowDropFiles?: boolean, preselectNodes: NodeEntry[] = []) { let shareDataRows: ShareDataRow[] = []; if (allowDropFiles !== undefined) { this.allowDropFiles = allowDropFiles; @@ -292,6 +297,23 @@ export class ShareDataTableAdapter implements DataTableAdapter { } else { this.rows = shareDataRows; } + + this.selectRowsBasedOnGivenNodes(preselectNodes); + } + + selectRowsBasedOnGivenNodes(preselectNodes: NodeEntry[]) { + if (preselectNodes && preselectNodes.length > 0) { + this.rows = this.rows.map((row) => { + preselectNodes.map((preselectedNode) => { + if (row.obj.entry.id === preselectedNode.entry.id) { + row.isSelected = true; + } + }); + return row; + }); + } + + this.preselectRows = [...this.rows.filter((res) => res.isSelected)]; } } diff --git a/lib/content-services/src/lib/mock/document-list.component.mock.ts b/lib/content-services/src/lib/mock/document-list.component.mock.ts index 365a9179c8..2f41038841 100644 --- a/lib/content-services/src/lib/mock/document-list.component.mock.ts +++ b/lib/content-services/src/lib/mock/document-list.component.mock.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Node, NodePaging } from '@alfresco/js-api'; +import { Node, NodePaging, NodeEntry } from '@alfresco/js-api'; export const fakeNodeWithCreatePermission = new Node({ isFile: false, @@ -215,3 +215,117 @@ export const fakeNodePaging: NodePaging = { }] } }; + +export const mockNode1 = new Node({ + 'isFile': true, + 'createdByUser': { 'id': 'admin', 'displayName': 'Administrator' }, + 'modifiedAt': '2017-05-24T15:08:55.640Z', + 'nodeType': 'cm:content', + 'content': { + 'mimeType': 'application/rtf', + 'mimeTypeName': 'Rich Text Format', + 'sizeInBytes': 14530, + 'encoding': 'UTF-8' + }, + 'parentId': 'd124de26-6ba0-4f40-8d98-4907da2d337a', + 'createdAt': '2017-05-24T15:08:55.640Z', + 'path': { + 'name': '/Company Home/Guest Home', + 'isComplete': true, + 'elements': [{ + 'id': '94acfc73-7014-4475-9bd9-93a2162f0f8c', + 'name': 'Company Home' + }, { 'id': 'd124de26-6ba0-4f40-8d98-4907da2d337a', 'name': 'Guest Home' }] + }, + 'isFolder': false, + 'modifiedByUser': { 'id': 'admin', 'displayName': 'Administrator' }, + 'name': 'b_txt_file.rtf', + 'id': '70e1cc6a-6918-468a-b84a-1048093b06fd', + 'properties': { 'cm:versionLabel': '1.0', 'cm:versionType': 'MAJOR' }, + 'allowableOperations': ['delete', 'update'] +}); + +export const mockNode2 = new Node({ + 'isFile': true, + 'createdByUser': { 'id': 'admin', 'displayName': 'Administrator' }, + 'modifiedAt': '2017-05-24T15:08:55.640Z', + 'nodeType': 'cm:content', + 'content': { + 'mimeType': 'application/rtf', + 'mimeTypeName': 'Rich Text Format', + 'sizeInBytes': 14530, + 'encoding': 'UTF-8' + }, + 'parentId': 'd124de26-6ba0-4f40-8d98-4907da2d337a', + 'createdAt': '2017-05-24T15:08:55.640Z', + 'path': { + 'name': '/Company Home/Guest Home', + 'isComplete': true, + 'elements': [{ + 'id': '94acfc73-7014-4475-9bd9-93a2162f0f8c', + 'name': 'Company Home' + }, { 'id': 'd124de26-6ba0-4f40-8d98-4907da2d337a', 'name': 'Guest Home' }] + }, + 'isFolder': false, + 'modifiedByUser': { 'id': 'admin', 'displayName': 'Administrator' }, + 'name': 'b_txt_file.rtf', + 'id': '67b80f77-dbca-4f58-be6c-71b9dd61e554', + 'properties': { 'cm:versionLabel': '1.0', 'cm:versionType': 'MAJOR' }, + 'allowableOperations': ['delete', 'update'] +}); + +export const mockNode3 = new Node({ + 'isFile': true, + 'createdByUser': { 'id': 'admin', 'displayName': 'Administrator' }, + 'modifiedAt': '2017-05-24T15:08:55.640Z', + 'nodeType': 'cm:content', + 'content': { + 'mimeType': 'application/rtf', + 'mimeTypeName': 'Rich Text Format', + 'sizeInBytes': 14530, + 'encoding': 'UTF-8' + }, + 'parentId': 'd124de26-6ba0-4f40-8d98-4907da2d337a', + 'createdAt': '2017-05-24T15:08:55.640Z', + 'path': { + 'name': '/Company Home/Guest Home', + 'isComplete': true, + 'elements': [{ + 'id': '94acfc73-7014-4475-9bd9-93a2162f0f8c', + 'name': 'Company Home' + }, { 'id': 'd124de26-6ba0-4f40-8d98-4907da2d337a', 'name': 'Guest Home' }] + }, + 'isFolder': false, + 'modifiedByUser': { 'id': 'admin', 'displayName': 'Administrator' }, + 'name': 'c_txt_file.rtf', + 'id': '67b80f77-dbca-4f58-be6c-71b9dd61e555', + 'properties': { 'cm:versionLabel': '1.0', 'cm:versionType': 'MAJOR' }, + 'allowableOperations': ['delete', 'update'] +}); + +export const mockPreselectedNodes: NodeEntry[] = [ + { + entry: mockNode1 + }, + { + entry: mockNode1 + } +]; + +export const mockNodePagingWithPreselectedNodes: NodePaging = { + list: { + pagination: { + count: 5, + hasMoreItems: false, + totalItems: 5, + skipCount: 0, + maxItems: 100 + }, entries: [{ + entry: mockNode1 + }, { + entry: mockNode2 + }, { + entry: mockNode3 + }] + } +};