diff --git a/src/app/components/favorites/favorites.component.html b/src/app/components/favorites/favorites.component.html index 8bce8ff2d..3378d3ed6 100644 --- a/src/app/components/favorites/favorites.component.html +++ b/src/app/components/favorites/favorites.component.html @@ -98,7 +98,7 @@
-
+
- - - - + +
-
+
- - - - + +
diff --git a/src/app/components/files/files.component.spec.ts b/src/app/components/files/files.component.spec.ts index 04c960c0b..edefece61 100644 --- a/src/app/components/files/files.component.spec.ts +++ b/src/app/components/files/files.component.spec.ts @@ -24,7 +24,7 @@ */ import { Observable } from 'rxjs/Rx'; -import { TestBed, async } from '@angular/core/testing'; +import { TestBed, async, fakeAsync, tick } from '@angular/core/testing'; import { NO_ERRORS_SCHEMA } from '@angular/core'; import { Router, ActivatedRoute } from '@angular/router'; import { RouterTestingModule } from '@angular/router/testing'; @@ -138,10 +138,36 @@ describe('FilesComponent', () => { pagination: {} } }; + + spyOn(component.documentList, 'loadFolder').and.callFake(() => {}); + }); + + describe('Current page is valid', () => { + it('should be a valid current page', fakeAsync(() => { + spyOn(component, 'fetchNode').and.returnValue(Observable.of(node)); + spyOn(component, 'fetchNodes').and.returnValue(Observable.throw(null)); + + component.ngOnInit(); + fixture.detectChanges(); + tick(); + + expect(component.isValidPath).toBe(false); + })); + + it('should set current page as invalid path', fakeAsync(() => { + spyOn(component, 'fetchNode').and.returnValue(Observable.of(node)); + spyOn(component, 'fetchNodes').and.returnValue(Observable.of(page)); + + component.ngOnInit(); + tick(); + fixture.detectChanges(); + + expect(component.isValidPath).toBe(true); + })); }); describe('OnInit', () => { - it('set current node', () => { + it('should set current node', () => { spyOn(component, 'fetchNode').and.returnValue(Observable.of(node)); spyOn(component, 'fetchNodes').and.returnValue(Observable.of(page)); @@ -150,13 +176,13 @@ describe('FilesComponent', () => { expect(component.node).toBe(node); }); - it('get current node children', () => { + it('should get current node children', () => { spyOn(component, 'fetchNode').and.returnValue(Observable.of(node)); spyOn(component, 'fetchNodes').and.returnValue(Observable.of(page)); fixture.detectChanges(); - expect(component.paging).toBe(page); + expect(component.fetchNodes).toHaveBeenCalled(); }); it('emits onChangeParent event', () => { @@ -170,25 +196,6 @@ describe('FilesComponent', () => { .toHaveBeenCalledWith(node); }); - it('raise error when fetchNode() fails', () => { - spyOn(component, 'fetchNode').and.returnValue(Observable.throw(null)); - spyOn(component, 'onFetchError'); - - fixture.detectChanges(); - - expect(component.onFetchError).toHaveBeenCalled(); - }); - - it('raise error when fetchNodes() fails', () => { - spyOn(component, 'fetchNode').and.returnValue(Observable.of(node)); - spyOn(component, 'fetchNodes').and.returnValue(Observable.throw(null)); - spyOn(component, 'onFetchError'); - - fixture.detectChanges(); - - expect(component.onFetchError).toHaveBeenCalled(); - }); - it('if should navigate to parent if node is not a folder', () => { node.isFolder = false; node.parentId = 'parent-id'; @@ -205,12 +212,12 @@ describe('FilesComponent', () => { beforeEach(() => { spyOn(component, 'fetchNode').and.returnValue(Observable.of(node)); spyOn(component, 'fetchNodes').and.returnValue(Observable.of(page)); - spyOn(component, 'load'); + spyOn(component.documentList, 'reload'); fixture.detectChanges(); }); - it('calls refresh onContentCopied event if parent is the same', () => { + it('should call refresh onContentCopied event if parent is the same', () => { const nodes = [ { entry: { parentId: '1' } }, { entry: { parentId: '2' } } @@ -220,10 +227,10 @@ describe('FilesComponent', () => { nodeActionsService.contentCopied.next(nodes); - expect(component.load).toHaveBeenCalled(); + expect(component.documentList.reload).toHaveBeenCalled(); }); - it('does not call refresh onContentCopied event when parent mismatch', () => { + it('should not call refresh onContentCopied event when parent mismatch', () => { const nodes = [ { entry: { parentId: '1' } }, { entry: { parentId: '2' } } @@ -233,73 +240,73 @@ describe('FilesComponent', () => { nodeActionsService.contentCopied.next(nodes); - expect(component.load).not.toHaveBeenCalled(); + expect(component.documentList.reload).not.toHaveBeenCalled(); }); - it('calls refresh onCreateFolder event', () => { + it('should call refresh onCreateFolder event', () => { alfrescoContentService.folderCreate.next(); - expect(component.load).toHaveBeenCalled(); + expect(component.documentList.reload).toHaveBeenCalled(); }); - it('calls refresh editFolder event', () => { + it('should call refresh editFolder event', () => { alfrescoContentService.folderEdit.next(); - expect(component.load).toHaveBeenCalled(); + expect(component.documentList.reload).toHaveBeenCalled(); }); - it('calls refresh deleteNode event', () => { + it('should call refresh deleteNode event', () => { contentManagementService.nodeDeleted.next(); - expect(component.load).toHaveBeenCalled(); + expect(component.documentList.reload).toHaveBeenCalled(); }); - it('calls refresh moveNode event', () => { + it('should call refresh moveNode event', () => { contentManagementService.nodeMoved.next(); - expect(component.load).toHaveBeenCalled(); + expect(component.documentList.reload).toHaveBeenCalled(); }); - it('calls refresh restoreNode event', () => { + it('should call refresh restoreNode event', () => { contentManagementService.nodeRestored.next(); - expect(component.load).toHaveBeenCalled(); + expect(component.documentList.reload).toHaveBeenCalled(); }); - it('calls refresh on fileUploadComplete event if parent node match', () => { + it('should call refresh on fileUploadComplete event if parent node match', () => { const file = { file: { options: { parentId: 'parentId' } } }; component.node = { id: 'parentId' }; uploadService.fileUploadComplete.next(file); - expect(component.load).toHaveBeenCalled(); + expect(component.documentList.reload).toHaveBeenCalled(); }); - it('does not call refresh on fileUploadComplete event if parent mismatch', () => { + it('should not call refresh on fileUploadComplete event if parent mismatch', () => { const file = { file: { options: { parentId: 'otherId' } } }; component.node = { id: 'parentId' }; uploadService.fileUploadComplete.next(file); - expect(component.load).not.toHaveBeenCalled(); + expect(component.documentList.reload).not.toHaveBeenCalled(); }); - it('calls refresh on fileUploadDeleted event if parent node match', () => { + it('should call refresh on fileUploadDeleted event if parent node match', () => { const file = { file: { options: { parentId: 'parentId' } } }; component.node = { id: 'parentId' }; uploadService.fileUploadDeleted.next(file); - expect(component.load).toHaveBeenCalled(); + expect(component.documentList.reload).toHaveBeenCalled(); }); - it('does not call refresh on fileUploadDeleted event if parent mismatch', () => { + it('should not call refresh on fileUploadDeleted event if parent mismatch', () => { const file = { file: { options: { parentId: 'otherId' } } }; component.node = { id: 'parentId' }; uploadService.fileUploadDeleted.next(file); - expect(component.load).not.toHaveBeenCalled(); + expect(component.documentList.reload).not.toHaveBeenCalled(); }); }); @@ -311,7 +318,7 @@ describe('FilesComponent', () => { fixture.detectChanges(); }); - it('calls getNode api with node id', () => { + it('should call getNode api with node id', () => { component.fetchNode('nodeId'); expect(nodesApi.getNode).toHaveBeenCalledWith('nodeId'); @@ -326,7 +333,7 @@ describe('FilesComponent', () => { fixture.detectChanges(); }); - it('calls getNode api with node id', () => { + it('should call getNode api with node id', () => { component.fetchNodes('nodeId'); expect(nodesApi.getNodeChildren).toHaveBeenCalledWith('nodeId', jasmine.any(Object)); @@ -341,7 +348,7 @@ describe('FilesComponent', () => { fixture.detectChanges(); }); - it('opens preview if node is file', () => { + it('should open preview if node is file', () => { spyOn(router, 'navigate').and.stub(); node.isFile = true; node.isFolder = false; @@ -358,7 +365,7 @@ describe('FilesComponent', () => { expect(router.navigate['calls'].argsFor(0)[0]).toEqual(['./preview', node.id]); }); - it('navigate if node is folder', () => { + it('should navigate if node is folder', () => { spyOn(component, 'navigate').and.stub(); node.isFolder = true; @@ -376,63 +383,6 @@ describe('FilesComponent', () => { }); }); - describe('load()', () => { - let fetchNodesSpy; - - beforeEach(() => { - spyOn(component, 'fetchNode').and.returnValue(Observable.of(node)); - fetchNodesSpy = spyOn(component, 'fetchNodes'); - - fetchNodesSpy.and.returnValue(Observable.of(page)); - - fixture.detectChanges(); - }); - - afterEach(() => { - fetchNodesSpy.calls.reset(); - }); - - it('shows load indicator', () => { - spyOn(component, 'onPageLoaded'); - component.node = { id: 'currentNode' }; - - expect(component.isLoading).toBe(false); - - component.load(true); - - expect(component.isLoading).toBe(true); - }); - - it('does not show load indicator', () => { - spyOn(component, 'onPageLoaded'); - component.node = { id: 'currentNode' }; - - expect(component.isLoading).toBe(false); - - component.load(); - - expect(component.isLoading).toBe(false); - }); - - it('sets data on success', () => { - component.node = { id: 'currentNode' }; - - component.load(); - - expect(component.paging).toBe(page); - expect(component.pagination).toEqual(page.list.pagination); - }); - - it('raise error on fail', () => { - fetchNodesSpy.and.returnValue(Observable.throw(null)); - spyOn(component, 'onFetchError'); - - component.load(); - - expect(component.onFetchError).toHaveBeenCalled(); - }); - }); - describe('onBreadcrumbNavigate()', () => { beforeEach(() => { spyOn(component, 'fetchNode').and.returnValue(Observable.of(node)); @@ -441,7 +391,7 @@ describe('FilesComponent', () => { fixture.detectChanges(); }); - it('navigates to node id', () => { + it('should navigates to node id', () => { const routeData = { id: 'some-where-over-the-rainbow' }; spyOn(component, 'navigate'); @@ -460,19 +410,19 @@ describe('FilesComponent', () => { fixture.detectChanges(); }); - it('navigates to node when id provided', () => { + it('should navigates to node when id provided', () => { component.navigate(node.id); expect(router.navigate).toHaveBeenCalledWith(['./', node.id], jasmine.any(Object)); }); - it('navigates to home when id not provided', () => { + it('should navigates to home when id not provided', () => { component.navigate(); expect(router.navigate).toHaveBeenCalledWith(['./'], jasmine.any(Object)); }); - it('it navigate home if node is root', () => { + it('should navigate home if node is root', () => { (component).node = { path: { elements: [ {id: 'node-id'} ] diff --git a/src/app/components/files/files.component.ts b/src/app/components/files/files.component.ts index 8eab1d700..86018017b 100644 --- a/src/app/components/files/files.component.ts +++ b/src/app/components/files/files.component.ts @@ -24,7 +24,7 @@ */ import { Observable, Subscription } from 'rxjs/Rx'; -import { Component, OnInit, OnDestroy, NgZone } from '@angular/core'; +import { Component, OnInit, OnDestroy, ViewChild } from '@angular/core'; import { Router, ActivatedRoute, Params } from '@angular/router'; import { MinimalNodeEntity, MinimalNodeEntryEntity, PathElementEntity, NodePaging, PathElement } from 'alfresco-js-api'; import { @@ -32,6 +32,8 @@ import { ContentService, AlfrescoApiService, UserPreferencesService, NotificationService } from '@alfresco/adf-core'; +import { DocumentListComponent } from '@alfresco/adf-content-services'; + import { BrowsingFilesService } from '../../common/services/browsing-files.service'; import { ContentManagementService } from '../../common/services/content-management.service'; import { NodeActionsService } from '../../common/services/node-actions.service'; @@ -43,6 +45,7 @@ import { PageComponent } from '../page.component'; templateUrl: './files.component.html' }) export class FilesComponent extends PageComponent implements OnInit, OnDestroy { + @ViewChild(DocumentListComponent) documentList: DocumentListComponent; isValidPath = true; @@ -52,7 +55,6 @@ export class FilesComponent extends PageComponent implements OnInit, OnDestroy { sorting = [ 'modifiedAt', 'desc' ]; constructor(private router: Router, - private zone: NgZone, private route: ActivatedRoute, private nodesApi: NodesApiService, private nodeActionsService: NodeActionsService, @@ -80,7 +82,6 @@ export class FilesComponent extends PageComponent implements OnInit, OnDestroy { route.params.subscribe(({ folderId }: Params) => { const nodeId = folderId || data.defaultNodeId; - this.isLoading = true; this.fetchNode(nodeId) .do((node) => { @@ -95,22 +96,20 @@ export class FilesComponent extends PageComponent implements OnInit, OnDestroy { .subscribe( (page) => { this.isValidPath = true; - this.onPageLoaded(page); }, error => { this.isValidPath = false; - this.onFetchError(error); } ); }); this.subscriptions = this.subscriptions.concat([ nodeActionsService.contentCopied.subscribe((nodes) => this.onContentCopied(nodes)), - contentService.folderCreate.subscribe(() => this.load(false, this.pagination)), - contentService.folderEdit.subscribe(() => this.load(false, this.pagination)), - contentManagementService.nodeDeleted.subscribe(() => this.load(false, this.pagination)), - contentManagementService.nodeMoved.subscribe(() => this.load(false, this.pagination)), - contentManagementService.nodeRestored.subscribe(() => this.load(false, this.pagination)), + contentService.folderCreate.subscribe(() => this.documentList.reload()), + contentService.folderEdit.subscribe(() => this.documentList.reload()), + contentManagementService.nodeDeleted.subscribe(() => this.documentList.reload()), + contentManagementService.nodeMoved.subscribe(() => this.documentList.reload()), + contentManagementService.nodeRestored.subscribe(() => this.documentList.reload()), uploadService.fileUploadComplete.subscribe(file => this.onFileUploadedEvent(file)), uploadService.fileUploadDeleted.subscribe((file) => this.onFileUploadedEvent(file)) ]); @@ -189,7 +188,7 @@ export class FilesComponent extends PageComponent implements OnInit, OnDestroy { onFileUploadedEvent(event: FileUploadEvent) { if (event && event.file.options.parentId === this.getParentNodeId()) { - this.load(false, this.pagination); + this.documentList.reload(); } } @@ -199,43 +198,10 @@ export class FilesComponent extends PageComponent implements OnInit, OnDestroy { return node && node.entry && node.entry.parentId === this.getParentNodeId(); }); if (newNode) { - this.load(false, this.pagination); + this.documentList.reload(); } } - load(showIndicator: boolean = false, options: { maxItems?: number, skipCount?: number } = {}) { - this.isLoading = showIndicator; - - this.fetchNodes(this.getParentNodeId(), options) - .flatMap((page) => { - if (this.isCurrentPageEmpty(page) && this.isNotFirstPage(page)) { - const newSkipCount = options.skipCount - options.maxItems; - - return this.fetchNodes(this.getParentNodeId(), { - skipCount: newSkipCount, maxItems: options.maxItems - }); - } - - return Observable.of(page); - }) - .subscribe( - (page) => this.zone.run(() => this.onPageLoaded(page)), - error => this.onFetchError(error) - ); - } - - isCurrentPageEmpty(page): boolean { - return !this.hasPageEntries(page); - } - - hasPageEntries(page): boolean { - return page && page.list && page.list.entries && page.list.entries.length > 0; - } - - isNotFirstPage(page): boolean { - return (page.list.pagination.skipCount >= page.list.pagination.maxItems); - } - // todo: review this approach once 5.2.3 is out private async updateCurrentNode(node: MinimalNodeEntryEntity) { this.nodePath = null; diff --git a/src/app/components/libraries/libraries.component.html b/src/app/components/libraries/libraries.component.html index aed07e6e6..60c279828 100644 --- a/src/app/components/libraries/libraries.component.html +++ b/src/app/components/libraries/libraries.component.html @@ -7,7 +7,7 @@
-
+
- - - - + +
diff --git a/src/app/components/page.component.spec.ts b/src/app/components/page.component.spec.ts index 0b1f78e95..7b1539f02 100644 --- a/src/app/components/page.component.spec.ts +++ b/src/app/components/page.component.spec.ts @@ -58,60 +58,6 @@ describe('PageComponent', () => { }); }); - describe('onFetchError()', () => { - it('sets isLoading state to false', () => { - component.isLoading = true; - - component.onFetchError(); - - expect(component.isLoading).toBe(false); - }); - }); - - describe('onPaginationChange()', () => { - it('fetch children nodes for current node id', () => { - component.node = { id: 'node-id' }; - spyOn(component, 'fetchNodes').and.stub(); - - component.onPaginationChange({pagination: 'pagination-data'}); - - expect(component.fetchNodes).toHaveBeenCalledWith('node-id', { pagination: 'pagination-data' }); - }); - }); - - describe('onPageLoaded()', () => { - let page; - - beforeEach(() => { - page = { - list: { - entries: ['a', 'b', 'c'], - pagination: {} - } - }; - - component.isLoading = true; - component.isEmpty = true; - component.onPageLoaded(page); - }); - - it('sets isLoading state to false', () => { - expect(component.isLoading).toBe(false); - }); - - it('sets component paging data', () => { - expect(component.paging).toBe(page); - }); - - it('sets component pagination data', () => { - expect(component.pagination).toEqual(page.list.pagination); - }); - - it('sets component isEmpty state', () => { - expect(component.isEmpty).toBe(false); - }); - }); - describe('hasSelection()', () => { it('returns true when it has nodes selected', () => { const hasSelection = component.hasSelection([ {}, {} ]); diff --git a/src/app/components/page.component.ts b/src/app/components/page.component.ts index 2c66c376b..2cc52f67e 100644 --- a/src/app/components/page.component.ts +++ b/src/app/components/page.component.ts @@ -23,21 +23,13 @@ * along with Alfresco. If not, see . */ -import { MinimalNodeEntity, MinimalNodeEntryEntity, NodePaging, Pagination } from 'alfresco-js-api'; +import { MinimalNodeEntity, MinimalNodeEntryEntity, Pagination } from 'alfresco-js-api'; import { UserPreferencesService } from '@alfresco/adf-core'; import { ShareDataRow } from '@alfresco/adf-content-services'; export abstract class PageComponent { - title = 'Page'; - - isLoading = false; - isEmpty = true; infoDrawerOpened = false; - - paging: NodePaging; - pagination: Pagination; - node: MinimalNodeEntryEntity; static isLockedNode(node) { @@ -49,25 +41,10 @@ export abstract class PageComponent { constructor(protected preferences: UserPreferencesService) { } - onFetchError(error: any) { - this.isLoading = false; - } - getParentNodeId(): string { return this.node ? this.node.id : null; } - onPaginationChange(pagination: any) { - this.fetchNodes(this.getParentNodeId(), pagination); - } - - onPageLoaded(page: NodePaging) { - this.isLoading = false; - this.paging = page; - this.pagination = { ...page.list.pagination }; - this.isEmpty = !(page.list.entries && page.list.entries.length > 0); - } - hasSelection(selection: Array): boolean { return selection && selection.length > 0; } diff --git a/src/app/components/recent-files/recent-files.component.html b/src/app/components/recent-files/recent-files.component.html index 77255f50a..1d993701b 100644 --- a/src/app/components/recent-files/recent-files.component.html +++ b/src/app/components/recent-files/recent-files.component.html @@ -91,7 +91,7 @@
-
+
- - - - + +
-
+
- - - - + +
-
+
- - - - + +
diff --git a/src/app/ui/overrides/_alfresco-document-list.scss b/src/app/ui/overrides/_alfresco-document-list.scss index 43846d3a4..94f837fa8 100644 --- a/src/app/ui/overrides/_alfresco-document-list.scss +++ b/src/app/ui/overrides/_alfresco-document-list.scss @@ -6,18 +6,6 @@ adf-document-list { background-color: white; // TODO: remove when ADF 2.4.0 is out. } -.adf-document-list--loading { - .adf-data-table { - @include flex-column; - justify-content: center; - align-items: center; - } - - .adf-datatable-table-cell { - border: none !important; - } -} - adf-datatable { @include flex-column; overflow-y: scroll; @@ -99,3 +87,7 @@ adf-datatable { cursor: default; } } + +.adf-pagination__empty { + display: none; +}