diff --git a/demo-shell/src/app/components/content-node-selector/content-node-selector.component.ts b/demo-shell/src/app/components/content-node-selector/content-node-selector.component.ts index b6208cbfc1..3039cf7eb6 100644 --- a/demo-shell/src/app/components/content-node-selector/content-node-selector.component.ts +++ b/demo-shell/src/app/components/content-node-selector/content-node-selector.component.ts @@ -17,7 +17,7 @@ import { Component, ViewEncapsulation } from '@angular/core'; import { SitePaging, SiteEntry, MinimalNodeEntryEntity } from '@alfresco/js-api'; -import { ContentNodeDialogService, ShareDataRow } from '@alfresco/adf-content-services'; +import { ContentNodeDialogService, ShareDataRow, RowFilter } from '@alfresco/adf-content-services'; import { DataRow, DataColumn, ThumbnailService } from '@alfresco/adf-core'; @Component({ @@ -40,7 +40,7 @@ export class ContentNodeSelectorComponent { customSideTitle = ''; actualPageSize = 2; - rowFilterFunction: any = null; + rowFilterFunction: RowFilter = null; excludeSiteContentList: string[] = ContentNodeDialogService.nonDocumentSiteContent; customImageResolver: any = null; diff --git a/e2e/pages/adf/contentServicesPage.ts b/e2e/pages/adf/contentServicesPage.ts index 76841fdffb..b027f9fc4c 100644 --- a/e2e/pages/adf/contentServicesPage.ts +++ b/e2e/pages/adf/contentServicesPage.ts @@ -190,8 +190,15 @@ export class ContentServicesPage { checkElementsSortedAsc(elements) { let sorted = true; let i = 0; + let compareNumbers = false; + + if (elements && elements[0] && typeof elements[0] === 'number') { + compareNumbers = true; + } while (elements.length > 1 && sorted === true && i < (elements.length - 1)) { - if (JSON.stringify(elements[i]) > JSON.stringify(elements[i + 1])) { + const left = compareNumbers ? elements[i] : JSON.stringify(elements[i]); + const right = compareNumbers ? elements[i + 1] : JSON.stringify(elements[i + 1]); + if (left > right) { sorted = false; } i++; diff --git a/lib/content-services/content-node-selector/content-node-selector-panel.component.html b/lib/content-services/content-node-selector/content-node-selector-panel.component.html index 12a3516adc..a59a12b285 100644 --- a/lib/content-services/content-node-selector/content-node-selector-panel.component.html +++ b/lib/content-services/content-node-selector/content-node-selector-panel.component.html @@ -58,7 +58,7 @@ [showHeader]="false" [node]="nodes" [maxItems]="pageSize" - [rowFilter]="rowFilter" + [rowFilter]="_rowFilter" [imageResolver]="imageResolver" [currentFolderId]="folderIdToShow" selectionMode="single" diff --git a/lib/content-services/content-node-selector/content-node-selector-panel.component.spec.ts b/lib/content-services/content-node-selector/content-node-selector-panel.component.spec.ts index 1f80e93def..128183d372 100644 --- a/lib/content-services/content-node-selector/content-node-selector-panel.component.spec.ts +++ b/lib/content-services/content-node-selector/content-node-selector-panel.component.spec.ts @@ -29,6 +29,7 @@ import { ContentTestingModule } from '../testing/content.testing.module'; import { DocumentListService } from '../document-list/services/document-list.service'; import { DocumentListComponent } from '../document-list/components/document-list.component'; import { CustomResourcesService } from '../document-list/services/custom-resources.service'; +import { ShareDataRow } from '../document-list'; const ONE_FOLDER_RESULT = { list: { @@ -97,6 +98,24 @@ describe('ContentNodeSelectorComponent', () => { describe('Parameters', () => { + let documentListService, + sitesService; + + beforeEach(() => { + documentListService = TestBed.get(DocumentListService); + sitesService = TestBed.get(SitesService); + spyOn(documentListService, 'getFolderNode').and.returnValue(of( { entry: { path: { elements: [] } } })); + spyOn(documentListService, 'getFolder').and.returnValue(throwError('No results for test')); + spyOn(sitesService, 'getSites').and.returnValue(of({ + list: { + entries: [ { entry: { guid: 'namek', id: 'namek' } }, + { entry: { guid: 'blog', id: 'blog' } }] + } + })); + component.currentFolderId = 'cat-girl-nuku-nuku'; + fixture.detectChanges(); + }); + it('should trigger the select event when selection has been made', (done) => { const expectedNode = {}; component.select.subscribe((nodes) => { @@ -113,7 +132,35 @@ describe('ContentNodeSelectorComponent', () => { fixture.detectChanges(); const testSiteContent = new Node({ id: 'blog-id', properties: { 'st:componentId': 'blog' } }); - expect(component.rowFilter( { node: { entry: testSiteContent } }, null, null)).toBe(false); + expect(component.rowFilter( { node: { entry: testSiteContent } }, null, null)) + .toBe(false, 'did not filter out blog'); + }); + + it('should still be able to filter out the exclude site content after rowFilter changes', () => { + const filterFunction1 = () => { + return true; + }; + const filterFunction2 = (row: ShareDataRow) => { + const node: Node = row.node.entry; + return node.isFile; + }; + + component.excludeSiteContent = ['blog']; + component.rowFilter = filterFunction1; + fixture.detectChanges(); + + const testSiteContent = new Node({ + id: 'blog-id', + properties: { 'st:componentId': 'blog' }, + isFile: true + }); + expect(component.rowFilter( { node: { entry: testSiteContent } }, null, null)) + .toBe(false, 'did not filter out blog with filterFunction1'); + + component.rowFilter = filterFunction2; + fixture.detectChanges(); + expect(component.rowFilter( { node: { entry: testSiteContent } }, null, null)) + .toBe(false, 'did not filter out blog with filterFunction2'); }); it('should NOT filter out any site content by default', () => { @@ -548,15 +595,48 @@ describe('ContentNodeSelectorComponent', () => { }); it('should pass through the rowFilter to the documentList', () => { - const filter = () => { + let filter = (shareDataRow: ShareDataRow) => { + if (shareDataRow.node.entry.name === 'impossible-name') { + return true; + } }; + component.rowFilter = filter; fixture.detectChanges(); let documentList = fixture.debugElement.query(By.directive(DocumentListComponent)); expect(documentList).not.toBeNull('Document list should be shown'); - expect(documentList.componentInstance.rowFilter).toBe(filter); + expect(documentList.componentInstance.rowFilter({ + node: { + entry: new Node({ + name: 'impossible-name', + id: 'name' + }) + } + })) + .toBe(filter( { + node: { + entry: new Node({ + name: 'impossible-name', + id: 'name' + }) + } + })); + }); + + it('should pass through the excludeSiteContent to the rowFilter of the documentList', () => { + component.excludeSiteContent = ['blog']; + + fixture.detectChanges(); + + let documentList = fixture.debugElement.query(By.directive(DocumentListComponent)); + expect(documentList).not.toBeNull('Document list should be shown'); + expect(documentList.componentInstance.rowFilter).toBeTruthy('Document list should have had a rowFilter'); + + const testSiteContent = new Node({ id: 'blog-id', properties: { 'st:componentId': 'blog' } }); + expect(documentList.componentInstance.rowFilter( { node: { entry: testSiteContent } }, null, null)) + .toBe(false); }); it('should pass through the imageResolver to the documentList', () => { diff --git a/lib/content-services/content-node-selector/content-node-selector-panel.component.ts b/lib/content-services/content-node-selector/content-node-selector-panel.component.ts index 789e6c0ccb..3e5dd6a26d 100644 --- a/lib/content-services/content-node-selector/content-node-selector-panel.component.ts +++ b/lib/content-services/content-node-selector/content-node-selector-panel.component.ts @@ -60,18 +60,35 @@ export class ContentNodeSelectorPanelComponent implements OnInit { @Input() dropdownSiteList: SitePaging = null; + _rowFilter: RowFilter = defaultValidation; + /** Custom row filter function. See the * [Document List component](document-list.component.md#custom-row-filter) * for more information. */ @Input() - rowFilter: RowFilter = null; + set rowFilter(rowFilter: RowFilter) { + this.createRowFilter(rowFilter); + } + + get rowFilter(): RowFilter { + return this._rowFilter; + } + + _excludeSiteContent: string[] = []; /** Custom list of site content componentIds. * Used to filter out the corresponding items from the displayed nodes */ @Input() - excludeSiteContent: string[] = []; + set excludeSiteContent(excludeSiteContent: string[]) { + this._excludeSiteContent = excludeSiteContent; + this.createRowFilter(this._rowFilter); + } + + get excludeSiteContent(): string[] { + return this._excludeSiteContent; + } /** Custom image resolver function. See the * [Document List component](document-list.component.md#custom-row-filter) @@ -164,27 +181,25 @@ export class ContentNodeSelectorPanelComponent implements OnInit { this.breadcrumbTransform = this.breadcrumbTransform ? this.breadcrumbTransform : null; this.isSelectionValid = this.isSelectionValid ? this.isSelectionValid : defaultValidation; - this.excludeSiteContent = this.excludeSiteContent ? this.excludeSiteContent : []; - this.rowFilter = this.getRowFilter(this.rowFilter); } - private getRowFilter(initialFilterFunction): RowFilter { - if (!initialFilterFunction) { - initialFilterFunction = () => true; + private createRowFilter(filter?: RowFilter) { + if (!filter) { + filter = () => true; } - return (value: ShareDataRow, index: number, array: ShareDataRow[]) => { - return initialFilterFunction(value, index, array) && + this._rowFilter = (value: ShareDataRow, index: number, array: ShareDataRow[]) => { + return filter(value, index, array) && !this.isExcludedSiteContent(value); }; } private isExcludedSiteContent(row: ShareDataRow): boolean { const entry = row.node.entry; - if (this.excludeSiteContent.length && + if (this._excludeSiteContent.length && entry && entry.properties && entry.properties['st:componentId']) { - const excludedItem = this.excludeSiteContent.find( + const excludedItem = this._excludeSiteContent.find( (id: string) => entry.properties['st:componentId'] === id ); return !!excludedItem;