mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-09-17 14:21:29 +00:00
Allow navigation to folders from search results (#1209)
* Allow navigation to folders from search results - Uses router to pass ID of the folder - Modified document list component to accept folder ID without path - Current limitations - Breadcrumb cannot currently be shown when navigating via folder id - Clicking between folders does not update the current route * Allow root folder ID to be changed and have documentlist reload - e.g switching from Company home to My Files * New tests for navigating to folders based on ID Refs #666
This commit is contained in:
@@ -87,7 +87,7 @@ describe('DocumentListBreadcrumb', () => {
|
||||
|
||||
it('should update document list on click', (done) => {
|
||||
let documentList = new DocumentList(null, null, null);
|
||||
spyOn(documentList, 'displayFolderContent').and.returnValue(Promise.resolve());
|
||||
spyOn(documentList, 'loadFolderByPath').and.returnValue(Promise.resolve());
|
||||
|
||||
let node = <PathNode> { name: 'name', path: '/path' };
|
||||
component.target = documentList;
|
||||
|
@@ -83,8 +83,11 @@ export class DocumentListBreadcrumb {
|
||||
}
|
||||
});
|
||||
|
||||
this.currentFolderPath = route.path;
|
||||
|
||||
if (this.target) {
|
||||
this.target.currentFolderPath = route.path;
|
||||
this.target.loadFolder();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -15,7 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { NgZone, TemplateRef } from '@angular/core';
|
||||
import { NgZone, SimpleChange, TemplateRef } from '@angular/core';
|
||||
import { DataTableComponent, DataColumn, DataRowEvent } from 'ng2-alfresco-datatable';
|
||||
import { DocumentList } from './document-list';
|
||||
import { DocumentListServiceMock } from './../assets/document-list.service.mock';
|
||||
@@ -48,12 +48,12 @@ describe('DocumentList', () => {
|
||||
window['componentHandler'] = componentHandler;
|
||||
});
|
||||
|
||||
it('should update root path', () => {
|
||||
it('should update root folder ID', () => {
|
||||
let adapter = documentList.data;
|
||||
expect(adapter.rootPath).toBe(adapter.DEFAULT_ROOT_PATH);
|
||||
expect(adapter.rootFolderId).toBe(adapter.DEFAULT_ROOT_ID);
|
||||
|
||||
documentList.rootPath = '-shared-';
|
||||
expect(adapter.rootPath).toBe('-shared-');
|
||||
documentList.rootFolderId = '-shared-';
|
||||
expect(adapter.rootFolderId).toBe('-shared-');
|
||||
});
|
||||
|
||||
it('should setup default columns', () => {
|
||||
@@ -163,7 +163,7 @@ describe('DocumentList', () => {
|
||||
let node = new FolderNode('<display name>');
|
||||
|
||||
spyOn(documentList, 'getNodePath').and.returnValue(path);
|
||||
spyOn(documentList, 'displayFolderContent').and.stub();
|
||||
spyOn(documentList, 'loadFolderByPath').and.returnValue(Promise.resolve(true));
|
||||
|
||||
documentList.navigationMode = DocumentList.SINGLE_CLICK_NAVIGATION;
|
||||
documentList.onNodeClick(node);
|
||||
@@ -173,31 +173,31 @@ describe('DocumentList', () => {
|
||||
|
||||
it('should not display folder content when no target node provided', () => {
|
||||
expect(documentList.navigate).toBe(true);
|
||||
spyOn(documentList, 'displayFolderContent').and.stub();
|
||||
spyOn(documentList, 'loadFolderByPath').and.stub();
|
||||
|
||||
documentList.onNodeClick(null);
|
||||
expect(documentList.displayFolderContent).not.toHaveBeenCalled();
|
||||
expect(documentList.loadFolderByPath).not.toHaveBeenCalled();
|
||||
|
||||
});
|
||||
|
||||
it('should display folder content only on folder node click', () => {
|
||||
expect(documentList.navigate).toBe(true);
|
||||
spyOn(documentList, 'displayFolderContent').and.stub();
|
||||
spyOn(documentList, 'loadFolderByPath').and.stub();
|
||||
|
||||
let node = new FileNode();
|
||||
documentList.onNodeClick(node);
|
||||
|
||||
expect(documentList.displayFolderContent).not.toHaveBeenCalled();
|
||||
expect(documentList.loadFolderByPath).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not display folder content on click when navigation is off', () => {
|
||||
spyOn(documentList, 'displayFolderContent').and.stub();
|
||||
spyOn(documentList, 'loadFolderByPath').and.stub();
|
||||
|
||||
let node = new FolderNode('<display name>');
|
||||
documentList.navigate = false;
|
||||
documentList.onNodeClick(node);
|
||||
|
||||
expect(documentList.displayFolderContent).not.toHaveBeenCalled();
|
||||
expect(documentList.loadFolderByPath).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should require node to get path', () => {
|
||||
@@ -205,31 +205,35 @@ describe('DocumentList', () => {
|
||||
});
|
||||
|
||||
it('should display folder content for new folder path', () => {
|
||||
spyOn(documentList, 'displayFolderContent').and.returnValue(Promise.resolve());
|
||||
spyOn(documentList, 'loadFolderByPath').and.returnValue(Promise.resolve());
|
||||
let newPath = '/some/new/path';
|
||||
documentList.currentFolderPath = newPath;
|
||||
expect(documentList.displayFolderContent).toHaveBeenCalledWith(newPath);
|
||||
documentList.ngOnChanges({currentFolderPath: new SimpleChange(null, newPath)});
|
||||
expect(documentList.loadFolderByPath).toHaveBeenCalledWith(newPath);
|
||||
});
|
||||
|
||||
it('should reset to default path', () => {
|
||||
spyOn(documentList, 'displayFolderContent').and.returnValue(Promise.resolve());
|
||||
spyOn(documentList, 'loadFolderByPath').and.returnValue(Promise.resolve());
|
||||
documentList.currentFolderPath = null;
|
||||
documentList.ngOnChanges({currentFolderPath: new SimpleChange('', null)});
|
||||
|
||||
expect(documentList.currentFolderPath).toBe(documentList.DEFAULT_ROOT_FOLDER);
|
||||
expect(documentList.displayFolderContent).toHaveBeenCalledWith(documentList.DEFAULT_ROOT_FOLDER);
|
||||
expect(documentList.currentFolderPath).toBe(documentList.DEFAULT_FOLDER_PATH);
|
||||
expect(documentList.loadFolderByPath).toHaveBeenCalledWith(documentList.DEFAULT_FOLDER_PATH);
|
||||
});
|
||||
|
||||
it('should emit folder changed event', (done) => {
|
||||
spyOn(documentList, 'displayFolderContent').and.returnValue(Promise.resolve());
|
||||
spyOn(documentList, 'loadFolderByPath').and.returnValue(Promise.resolve());
|
||||
documentList.folderChange.subscribe(e => {
|
||||
done();
|
||||
});
|
||||
|
||||
documentList.currentFolderPath = '/some/new/path';
|
||||
let newPath = '/some/new/path';
|
||||
documentList.currentFolderPath = newPath;
|
||||
documentList.ngOnChanges({currentFolderPath: new SimpleChange(null, newPath)});
|
||||
});
|
||||
|
||||
it('should emit folder changed event with folder details', (done) => {
|
||||
spyOn(documentList, 'displayFolderContent').and.returnValue(Promise.resolve());
|
||||
spyOn(documentList, 'loadFolderByPath').and.returnValue(Promise.resolve());
|
||||
|
||||
let path = '/path';
|
||||
|
||||
@@ -239,6 +243,7 @@ describe('DocumentList', () => {
|
||||
});
|
||||
|
||||
documentList.currentFolderPath = path;
|
||||
documentList.ngOnChanges({currentFolderPath: new SimpleChange(null, path)});
|
||||
});
|
||||
|
||||
it('should execute context action on callback', () => {
|
||||
@@ -259,7 +264,7 @@ describe('DocumentList', () => {
|
||||
});
|
||||
|
||||
it('should subscribe to context action handler', () => {
|
||||
spyOn(documentList, 'displayFolderContent').and.stub();
|
||||
spyOn(documentList, 'loadFolderByPath').and.returnValue(Promise.resolve(true));
|
||||
spyOn(documentList, 'contextActionCallback').and.stub();
|
||||
let value = {};
|
||||
documentList.ngOnInit();
|
||||
@@ -384,16 +389,16 @@ describe('DocumentList', () => {
|
||||
});
|
||||
|
||||
it('should display folder content on reload', () => {
|
||||
spyOn(documentList, 'displayFolderContent').and.callThrough();
|
||||
spyOn(documentList, 'loadFolderByPath').and.callThrough();
|
||||
documentList.reload();
|
||||
expect(documentList.displayFolderContent).toHaveBeenCalled();
|
||||
expect(documentList.loadFolderByPath).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should require path to display folder content', () => {
|
||||
spyOn(documentListService, 'getFolder').and.callThrough();
|
||||
|
||||
documentList.displayFolderContent(null);
|
||||
documentList.displayFolderContent('');
|
||||
documentList.loadFolderByPath(null);
|
||||
documentList.loadFolderByPath('');
|
||||
|
||||
expect(documentListService.getFolder).not.toHaveBeenCalled();
|
||||
});
|
||||
@@ -464,11 +469,11 @@ describe('DocumentList', () => {
|
||||
});
|
||||
expect(documentList.currentFolderPath).toBeNull();
|
||||
|
||||
spyOn(documentList, 'displayFolderContent').and.stub();
|
||||
spyOn(documentList, 'loadFolderByPath').and.stub();
|
||||
|
||||
documentList.reload();
|
||||
|
||||
expect(documentList.displayFolderContent).not.toHaveBeenCalled();
|
||||
expect(documentList.loadFolderByPath).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should enforce single-click on mobile browser', () => {
|
||||
@@ -482,9 +487,10 @@ describe('DocumentList', () => {
|
||||
it('should emit error on wrong path', (done) => {
|
||||
let raised = false;
|
||||
documentList.error.subscribe(err => raised = true);
|
||||
spyOn(documentList, 'displayFolderContent').and.returnValue(Promise.reject(false));
|
||||
spyOn(documentList, 'loadFolderByPath').and.returnValue(Promise.reject(false));
|
||||
|
||||
documentList.currentFolderPath = 'wrong-path';
|
||||
documentList.ngOnChanges({currentFolderPath: new SimpleChange(null, documentList.currentFolderPath)});
|
||||
setTimeout(() => {
|
||||
expect(raised).toBeTruthy();
|
||||
done();
|
||||
@@ -506,24 +512,24 @@ describe('DocumentList', () => {
|
||||
expect(documentList.isEmptyTemplateDefined()).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should set root path for underlying adapter', () => {
|
||||
documentList.rootPath = 'test';
|
||||
expect(documentList.data.rootPath).toBe('test');
|
||||
it('should set root folder ID for underlying adapter', () => {
|
||||
documentList.rootFolderId = 'test';
|
||||
expect(documentList.data.rootFolderId).toBe('test');
|
||||
});
|
||||
|
||||
it('should set default root path for underlying adapter', () => {
|
||||
documentList.rootPath = null;
|
||||
expect(documentList.data.rootPath).toBe(documentList.data.DEFAULT_ROOT_PATH);
|
||||
it('should set default root folder ID for underlying adapter', () => {
|
||||
documentList.rootFolderId = null;
|
||||
expect(documentList.data.rootFolderId).toBe(documentList.data.DEFAULT_ROOT_ID);
|
||||
});
|
||||
|
||||
it('should fetch root path from underlying adapter', () => {
|
||||
documentList.data.rootPath = 'test';
|
||||
expect(documentList.rootPath).toBe('test');
|
||||
it('should fetch root folder ID from underlying adapter', () => {
|
||||
documentList.data.rootFolderId = 'test';
|
||||
expect(documentList.rootFolderId).toBe('test');
|
||||
});
|
||||
|
||||
it('should not fetch root path when adapter missing', () => {
|
||||
it('should not fetch root folder ID when adapter missing', () => {
|
||||
documentList.data = null;
|
||||
expect(documentList.rootPath).toBeNull();
|
||||
expect(documentList.rootFolderId).toBeNull();
|
||||
});
|
||||
|
||||
it('should set row filter for underlying adapter', () => {
|
||||
@@ -561,4 +567,49 @@ describe('DocumentList', () => {
|
||||
documentList.onRowDblClick(event);
|
||||
expect(documentList.onNodeDblClick).toHaveBeenCalledWith(node);
|
||||
});
|
||||
|
||||
describe('navigate by folder ID', () => {
|
||||
|
||||
it('should load folder by ID on init', () => {
|
||||
|
||||
documentList.currentFolderId = '1d26e465-dea3-42f3-b415-faa8364b9692';
|
||||
|
||||
let loadbyIdSpy: jasmine.Spy = spyOn(documentList.data, 'loadById').and.returnValue(Promise.resolve());
|
||||
|
||||
documentList.ngOnInit();
|
||||
expect(loadbyIdSpy).toHaveBeenCalled();
|
||||
expect(documentList.currentFolderPath).toBe('/');
|
||||
});
|
||||
|
||||
it('should load folder by ID on changes', () => {
|
||||
|
||||
let newNodeId = '1d26e465-dea3-42f3-b415-faa8364b9692';
|
||||
|
||||
documentList.ngOnChanges({currentFolderId: new SimpleChange(null, newNodeId)});
|
||||
|
||||
let loadbyPathSpy: jasmine.Spy = spyOn(documentList.data, 'loadPath').and.returnValue(Promise.resolve());
|
||||
|
||||
documentList.ngOnInit();
|
||||
expect(loadbyPathSpy).toHaveBeenCalled();
|
||||
expect(documentList.currentFolderPath).toBe('/');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('configure root folder', () => {
|
||||
|
||||
it('should re-load folder when rootFolderId changed', () => {
|
||||
|
||||
let newRootFolder = '-new-';
|
||||
|
||||
documentList.ngOnChanges({rootFolderId: new SimpleChange(null, newRootFolder)});
|
||||
|
||||
let loadbyPathSpy: jasmine.Spy = spyOn(documentList.data, 'loadPath').and.returnValue(Promise.resolve());
|
||||
|
||||
documentList.ngOnInit();
|
||||
expect(loadbyPathSpy).toHaveBeenCalled();
|
||||
expect(documentList.currentFolderPath).toBe('/');
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
@@ -19,7 +19,9 @@ import {
|
||||
Component,
|
||||
OnInit,
|
||||
Input,
|
||||
OnChanges,
|
||||
Output,
|
||||
SimpleChanges,
|
||||
EventEmitter,
|
||||
AfterContentInit,
|
||||
TemplateRef,
|
||||
@@ -48,22 +50,25 @@ declare var module: any;
|
||||
styleUrls: ['./document-list.css'],
|
||||
templateUrl: './document-list.html'
|
||||
})
|
||||
export class DocumentList implements OnInit, AfterContentInit {
|
||||
export class DocumentList implements OnInit, OnChanges, AfterContentInit {
|
||||
|
||||
static SINGLE_CLICK_NAVIGATION: string = 'click';
|
||||
static DOUBLE_CLICK_NAVIGATION: string = 'dblclick';
|
||||
static DEFAULT_PAGE_SIZE: number = 20;
|
||||
|
||||
DEFAULT_ROOT_FOLDER: string = '/';
|
||||
DEFAULT_FOLDER_PATH: string = '/';
|
||||
|
||||
@Input()
|
||||
set rootPath(value: string) {
|
||||
this.data.rootPath = value || this.data.DEFAULT_ROOT_PATH;
|
||||
set rootFolderId(value: string) {
|
||||
this.data.rootFolderId = value || this.data.DEFAULT_ROOT_ID;
|
||||
}
|
||||
|
||||
get rootPath(): string {
|
||||
@Input()
|
||||
currentFolderId: string = null;
|
||||
|
||||
get rootFolderId(): string {
|
||||
if (this.data) {
|
||||
return this.data.rootPath;
|
||||
return this.data.rootFolderId;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -127,21 +132,11 @@ export class DocumentList implements OnInit, AfterContentInit {
|
||||
@ViewChild(DataTableComponent)
|
||||
dataTable: DataTableComponent;
|
||||
|
||||
private _path = this.DEFAULT_ROOT_FOLDER;
|
||||
private _path = this.DEFAULT_FOLDER_PATH;
|
||||
|
||||
@Input()
|
||||
set currentFolderPath(value: string) {
|
||||
if (value !== this._path) {
|
||||
const path = value || this.DEFAULT_ROOT_FOLDER;
|
||||
this.displayFolderContent(path)
|
||||
.then(() => {
|
||||
this._path = path;
|
||||
this.folderChange.emit({ path: path });
|
||||
})
|
||||
.catch(err => {
|
||||
this.error.emit(err);
|
||||
});
|
||||
}
|
||||
this._path = value;
|
||||
}
|
||||
|
||||
get currentFolderPath(): string {
|
||||
@@ -220,6 +215,8 @@ export class DocumentList implements OnInit, AfterContentInit {
|
||||
if (this.isMobile()) {
|
||||
this.navigationMode = DocumentList.SINGLE_CLICK_NAVIGATION;
|
||||
}
|
||||
|
||||
this.loadFolder();
|
||||
}
|
||||
|
||||
ngAfterContentInit() {
|
||||
@@ -229,6 +226,40 @@ export class DocumentList implements OnInit, AfterContentInit {
|
||||
}
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
if (changes['currentFolderId'] && changes['currentFolderId'].currentValue) {
|
||||
let folderId = changes['currentFolderId'].currentValue;
|
||||
this.loadFolderById(folderId)
|
||||
.then(() => {
|
||||
this._path = null;
|
||||
})
|
||||
.catch(err => {
|
||||
this.error.emit(err);
|
||||
});
|
||||
} else if (changes['currentFolderPath']) {
|
||||
const path = changes['currentFolderPath'].currentValue || this.DEFAULT_FOLDER_PATH;
|
||||
this.currentFolderPath = path;
|
||||
this.loadFolderByPath(path)
|
||||
.then(() => {
|
||||
this._path = path;
|
||||
this.folderChange.emit({ path: path });
|
||||
})
|
||||
.catch(err => {
|
||||
this.error.emit(err);
|
||||
});
|
||||
} else if (changes['rootFolderId']) {
|
||||
// this.currentFolderPath = this.DEFAULT_FOLDER_PATH;
|
||||
this.loadFolderByPath(this.currentFolderPath)
|
||||
.then(() => {
|
||||
this._path = this.currentFolderPath;
|
||||
this.folderChange.emit({ path: this.currentFolderPath });
|
||||
})
|
||||
.catch(err => {
|
||||
this.error.emit(err);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
isEmptyTemplateDefined() {
|
||||
if (this.dataTable) {
|
||||
if (this.emptyFolderTemplate) {
|
||||
@@ -277,6 +308,9 @@ export class DocumentList implements OnInit, AfterContentInit {
|
||||
performNavigation(node: MinimalNodeEntity): boolean {
|
||||
if (node && node.entry && node.entry.isFolder) {
|
||||
this.currentFolderPath = this.getNodePath(node);
|
||||
this.currentFolderId = null;
|
||||
this.loadFolder();
|
||||
this.folderChange.emit({ path: this.currentFolderPath });
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -293,21 +327,34 @@ export class DocumentList implements OnInit, AfterContentInit {
|
||||
}
|
||||
}
|
||||
|
||||
displayFolderContent(path: string): Promise<any> {
|
||||
loadFolderByPath(path: string): Promise<any> {
|
||||
return this.data.loadPath(path);
|
||||
}
|
||||
|
||||
loadFolderById(id: string): Promise<any> {
|
||||
return this.data.loadById(id);
|
||||
}
|
||||
|
||||
reload() {
|
||||
this.ngZone.run(() => {
|
||||
if (this.currentFolderPath) {
|
||||
this.displayFolderContent(this.currentFolderPath)
|
||||
.catch(err => {
|
||||
this.error.emit(err);
|
||||
});
|
||||
}
|
||||
this.loadFolder();
|
||||
});
|
||||
}
|
||||
|
||||
public loadFolder() {
|
||||
if (this.currentFolderId) {
|
||||
this.loadFolderById(this.currentFolderId)
|
||||
.catch(err => {
|
||||
this.error.emit(err);
|
||||
});
|
||||
} else if (this.currentFolderPath) {
|
||||
this.loadFolderByPath(this.currentFolderPath)
|
||||
.catch(err => {
|
||||
this.error.emit(err);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a path for a given node.
|
||||
* @param node
|
||||
|
Reference in New Issue
Block a user