mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-07-24 17:32:15 +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:
@@ -54,6 +54,11 @@ export const appRoutes: Routes = [
|
|||||||
component: FilesComponent,
|
component: FilesComponent,
|
||||||
canActivate: [AuthGuardEcm]
|
canActivate: [AuthGuardEcm]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'files/:id',
|
||||||
|
component: FilesComponent,
|
||||||
|
canActivate: [AuthGuardEcm]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: 'datatable',
|
path: 'datatable',
|
||||||
component: DataTableDemoComponent,
|
component: DataTableDemoComponent,
|
||||||
|
@@ -5,7 +5,7 @@
|
|||||||
(onSuccess)="documentList.reload()">
|
(onSuccess)="documentList.reload()">
|
||||||
<alfresco-document-list-breadcrumb
|
<alfresco-document-list-breadcrumb
|
||||||
[currentFolderPath]="currentPath"
|
[currentFolderPath]="currentPath"
|
||||||
[target]="documentList">
|
(pathChanged)="onBreadcrumbPathChanged($event)" *ngIf="!currentFolderId">
|
||||||
</alfresco-document-list-breadcrumb>
|
</alfresco-document-list-breadcrumb>
|
||||||
<div *ngIf="errorMessage" class="error-message">
|
<div *ngIf="errorMessage" class="error-message">
|
||||||
<button (click)="resetError()" class="mdl-button mdl-js-button mdl-button--icon">
|
<button (click)="resetError()" class="mdl-button mdl-js-button mdl-button--icon">
|
||||||
@@ -15,7 +15,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<alfresco-document-list
|
<alfresco-document-list
|
||||||
#documentList
|
#documentList
|
||||||
|
[rootFolderId]="rootFolderId"
|
||||||
[currentFolderPath]="currentPath"
|
[currentFolderPath]="currentPath"
|
||||||
|
[currentFolderId]="currentFolderId"
|
||||||
[contextMenuActions]="true"
|
[contextMenuActions]="true"
|
||||||
[contentActions]="true"
|
[contentActions]="true"
|
||||||
(error)="onNavigationError($event)"
|
(error)="onNavigationError($event)"
|
||||||
|
@@ -15,8 +15,8 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Component, OnInit, ViewChild } from '@angular/core';
|
import { Component, OnInit, Optional, ViewChild } from '@angular/core';
|
||||||
import { Router } from '@angular/router';
|
import { ActivatedRoute, Params, Router } from '@angular/router';
|
||||||
import { AlfrescoAuthenticationService } from 'ng2-alfresco-core';
|
import { AlfrescoAuthenticationService } from 'ng2-alfresco-core';
|
||||||
import {
|
import {
|
||||||
DocumentActionsService,
|
DocumentActionsService,
|
||||||
@@ -34,6 +34,8 @@ import { FormService } from 'ng2-activiti-form';
|
|||||||
})
|
})
|
||||||
export class FilesComponent implements OnInit {
|
export class FilesComponent implements OnInit {
|
||||||
currentPath: string = '/Sites/swsdp/documentLibrary';
|
currentPath: string = '/Sites/swsdp/documentLibrary';
|
||||||
|
rootFolderId: string = '-root-';
|
||||||
|
currentFolderId: string = null;
|
||||||
|
|
||||||
errorMessage: string = null;
|
errorMessage: string = null;
|
||||||
fileNodeId: any;
|
fileNodeId: any;
|
||||||
@@ -51,7 +53,8 @@ export class FilesComponent implements OnInit {
|
|||||||
constructor(private documentActions: DocumentActionsService,
|
constructor(private documentActions: DocumentActionsService,
|
||||||
public auth: AlfrescoAuthenticationService,
|
public auth: AlfrescoAuthenticationService,
|
||||||
private formService: FormService,
|
private formService: FormService,
|
||||||
private router: Router) {
|
private router: Router,
|
||||||
|
@Optional() private route: ActivatedRoute) {
|
||||||
documentActions.setHandler('my-handler', this.myDocumentActionHandler.bind(this));
|
documentActions.setHandler('my-handler', this.myDocumentActionHandler.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,6 +85,12 @@ export class FilesComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onBreadcrumbPathChanged(event?: any) {
|
||||||
|
if (event) {
|
||||||
|
this.currentPath = event.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
toggleMultipleFileUpload() {
|
toggleMultipleFileUpload() {
|
||||||
this.multipleFileUpload = !this.multipleFileUpload;
|
this.multipleFileUpload = !this.multipleFileUpload;
|
||||||
return this.multipleFileUpload;
|
return this.multipleFileUpload;
|
||||||
@@ -104,14 +113,19 @@ export class FilesComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
if ( this.auth.isBpmLoggedIn() ) {
|
if (this.route) {
|
||||||
this.formService.getProcessDefinitions().subscribe(
|
this.route.params.forEach((params: Params) => {
|
||||||
defs => this.setupBpmActions(defs || []),
|
this.currentFolderId = params.hasOwnProperty('id') ? params['id'] : null;
|
||||||
err => console.log(err)
|
});
|
||||||
);
|
}
|
||||||
} else {
|
if (this.auth.isBpmLoggedIn()) {
|
||||||
console.log('You are not logged in');
|
this.formService.getProcessDefinitions().subscribe(
|
||||||
}
|
defs => this.setupBpmActions(defs || []),
|
||||||
|
err => console.log(err)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
console.log('You are not logged in');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
viewActivitiForm(event?: any) {
|
viewActivitiForm(event?: any) {
|
||||||
|
@@ -1,11 +1,10 @@
|
|||||||
<alfresco-search-control *ngIf="isLoggedIn()"
|
<alfresco-search-control *ngIf="isLoggedIn()"
|
||||||
[searchTerm]="searchTerm"
|
[searchTerm]="searchTerm"
|
||||||
[autocomplete]="false"
|
[autocomplete]="false"
|
||||||
[liveSearchResultType]="'cm:content'"
|
|
||||||
(searchSubmit)="onSearchSubmit($event);"
|
(searchSubmit)="onSearchSubmit($event);"
|
||||||
(searchChange)="onSearchTermChange($event);"
|
(searchChange)="onSearchTermChange($event);"
|
||||||
(expand)="onExpandToggle($event);"
|
(expand)="onExpandToggle($event);"
|
||||||
(fileSelect)="onFileClicked($event)">
|
(fileSelect)="onItemClicked($event)">
|
||||||
</alfresco-search-control>
|
</alfresco-search-control>
|
||||||
|
|
||||||
<alfresco-viewer [(showViewer)]="fileShowed"
|
<alfresco-viewer [(showViewer)]="fileShowed"
|
||||||
|
@@ -18,6 +18,7 @@
|
|||||||
import { Component, EventEmitter, Output } from '@angular/core';
|
import { Component, EventEmitter, Output } from '@angular/core';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
import { AlfrescoAuthenticationService } from 'ng2-alfresco-core';
|
import { AlfrescoAuthenticationService } from 'ng2-alfresco-core';
|
||||||
|
import { MinimalNodeEntity } from 'alfresco-js-api';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'search-bar',
|
selector: 'search-bar',
|
||||||
@@ -51,10 +52,12 @@ export class SearchBarComponent {
|
|||||||
}]);
|
}]);
|
||||||
}
|
}
|
||||||
|
|
||||||
onFileClicked(event) {
|
onItemClicked(event: MinimalNodeEntity) {
|
||||||
if (event.value.entry.isFile) {
|
if (event.entry.isFile) {
|
||||||
this.fileNodeId = event.value.entry.id;
|
this.fileNodeId = event.entry.id;
|
||||||
this.fileShowed = true;
|
this.fileShowed = true;
|
||||||
|
} else if (event.entry.isFolder) {
|
||||||
|
this.router.navigate(['/files', event.entry.id]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
<div class="search-results-container">
|
<div class="search-results-container">
|
||||||
<h1>Search results</h1>
|
<h1>Search results</h1>
|
||||||
<alfresco-search [resultType]="'cm:content'" (preview)="onFileClicked($event)"></alfresco-search>
|
<alfresco-search (navigate)="onNavigateItem($event)"></alfresco-search>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<alfresco-viewer [(showViewer)]="fileShowed" [fileNodeId]="fileNodeId" [overlayMode]="true">
|
<alfresco-viewer [(showViewer)]="fileShowed" [fileNodeId]="fileNodeId" [overlayMode]="true">
|
||||||
|
@@ -16,6 +16,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { MinimalNodeEntity } from 'alfresco-js-api';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'search-component',
|
selector: 'search-component',
|
||||||
@@ -48,10 +50,15 @@ export class SearchComponent {
|
|||||||
fileShowed: boolean = false;
|
fileShowed: boolean = false;
|
||||||
fileNodeId: string;
|
fileNodeId: string;
|
||||||
|
|
||||||
onFileClicked(event) {
|
constructor(public router: Router) {
|
||||||
if (event.value.entry.isFile) {
|
}
|
||||||
this.fileNodeId = event.value.entry.id;
|
|
||||||
|
onNavigateItem(event: MinimalNodeEntity) {
|
||||||
|
if (event.entry.isFile) {
|
||||||
|
this.fileNodeId = event.entry.id;
|
||||||
this.fileShowed = true;
|
this.fileShowed = true;
|
||||||
|
} else if (event.entry.isFolder) {
|
||||||
|
this.router.navigate(['/files', event.entry.id]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -168,7 +168,9 @@ platformBrowserDynamic().bootstrapModule(AppModule);
|
|||||||
|
|
||||||
| Name | Type | Default | Description |
|
| Name | Type | Default | Description |
|
||||||
| --- | --- | --- | --- |
|
| --- | --- | --- | --- |
|
||||||
| `rootPath` | string | -root- | Root node path, i.e. `-root-`, `-shared-`, `-my-`, etc. |
|
| `rootFolderId` | string | -root- | Root node ID, i.e. `-root-`, `-shared-`, `-my-`, etc. or a fixed node ID |
|
||||||
|
| `currentFolderPath` | string | null | Initial path of displayed folder below the root node, e.g. "/Sites/swsdp/documentLibrary" |
|
||||||
|
| `currentFolderId` | string | null | Initial node ID of displayed folder, if given |
|
||||||
| `navigate` | boolean | true | Toggles navigation to folder content or file preview |
|
| `navigate` | boolean | true | Toggles navigation to folder content or file preview |
|
||||||
| `navigationMode` | string (click\|dblclick) | dblclick | User interaction for folder navigation or file preview |
|
| `navigationMode` | string (click\|dblclick) | dblclick | User interaction for folder navigation or file preview |
|
||||||
| `thumbnails` | boolean | false | Show document thumbnails rather than icons |
|
| `thumbnails` | boolean | false | Show document thumbnails rather than icons |
|
||||||
|
@@ -87,7 +87,7 @@ describe('DocumentListBreadcrumb', () => {
|
|||||||
|
|
||||||
it('should update document list on click', (done) => {
|
it('should update document list on click', (done) => {
|
||||||
let documentList = new DocumentList(null, null, null);
|
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' };
|
let node = <PathNode> { name: 'name', path: '/path' };
|
||||||
component.target = documentList;
|
component.target = documentList;
|
||||||
|
@@ -83,8 +83,11 @@ export class DocumentListBreadcrumb {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.currentFolderPath = route.path;
|
||||||
|
|
||||||
if (this.target) {
|
if (this.target) {
|
||||||
this.target.currentFolderPath = route.path;
|
this.target.currentFolderPath = route.path;
|
||||||
|
this.target.loadFolder();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -15,7 +15,7 @@
|
|||||||
* limitations under the License.
|
* 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 { DataTableComponent, DataColumn, DataRowEvent } from 'ng2-alfresco-datatable';
|
||||||
import { DocumentList } from './document-list';
|
import { DocumentList } from './document-list';
|
||||||
import { DocumentListServiceMock } from './../assets/document-list.service.mock';
|
import { DocumentListServiceMock } from './../assets/document-list.service.mock';
|
||||||
@@ -48,12 +48,12 @@ describe('DocumentList', () => {
|
|||||||
window['componentHandler'] = componentHandler;
|
window['componentHandler'] = componentHandler;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should update root path', () => {
|
it('should update root folder ID', () => {
|
||||||
let adapter = documentList.data;
|
let adapter = documentList.data;
|
||||||
expect(adapter.rootPath).toBe(adapter.DEFAULT_ROOT_PATH);
|
expect(adapter.rootFolderId).toBe(adapter.DEFAULT_ROOT_ID);
|
||||||
|
|
||||||
documentList.rootPath = '-shared-';
|
documentList.rootFolderId = '-shared-';
|
||||||
expect(adapter.rootPath).toBe('-shared-');
|
expect(adapter.rootFolderId).toBe('-shared-');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should setup default columns', () => {
|
it('should setup default columns', () => {
|
||||||
@@ -163,7 +163,7 @@ describe('DocumentList', () => {
|
|||||||
let node = new FolderNode('<display name>');
|
let node = new FolderNode('<display name>');
|
||||||
|
|
||||||
spyOn(documentList, 'getNodePath').and.returnValue(path);
|
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.navigationMode = DocumentList.SINGLE_CLICK_NAVIGATION;
|
||||||
documentList.onNodeClick(node);
|
documentList.onNodeClick(node);
|
||||||
@@ -173,31 +173,31 @@ describe('DocumentList', () => {
|
|||||||
|
|
||||||
it('should not display folder content when no target node provided', () => {
|
it('should not display folder content when no target node provided', () => {
|
||||||
expect(documentList.navigate).toBe(true);
|
expect(documentList.navigate).toBe(true);
|
||||||
spyOn(documentList, 'displayFolderContent').and.stub();
|
spyOn(documentList, 'loadFolderByPath').and.stub();
|
||||||
|
|
||||||
documentList.onNodeClick(null);
|
documentList.onNodeClick(null);
|
||||||
expect(documentList.displayFolderContent).not.toHaveBeenCalled();
|
expect(documentList.loadFolderByPath).not.toHaveBeenCalled();
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should display folder content only on folder node click', () => {
|
it('should display folder content only on folder node click', () => {
|
||||||
expect(documentList.navigate).toBe(true);
|
expect(documentList.navigate).toBe(true);
|
||||||
spyOn(documentList, 'displayFolderContent').and.stub();
|
spyOn(documentList, 'loadFolderByPath').and.stub();
|
||||||
|
|
||||||
let node = new FileNode();
|
let node = new FileNode();
|
||||||
documentList.onNodeClick(node);
|
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', () => {
|
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>');
|
let node = new FolderNode('<display name>');
|
||||||
documentList.navigate = false;
|
documentList.navigate = false;
|
||||||
documentList.onNodeClick(node);
|
documentList.onNodeClick(node);
|
||||||
|
|
||||||
expect(documentList.displayFolderContent).not.toHaveBeenCalled();
|
expect(documentList.loadFolderByPath).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should require node to get path', () => {
|
it('should require node to get path', () => {
|
||||||
@@ -205,31 +205,35 @@ describe('DocumentList', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should display folder content for new folder path', () => {
|
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';
|
let newPath = '/some/new/path';
|
||||||
documentList.currentFolderPath = newPath;
|
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', () => {
|
it('should reset to default path', () => {
|
||||||
spyOn(documentList, 'displayFolderContent').and.returnValue(Promise.resolve());
|
spyOn(documentList, 'loadFolderByPath').and.returnValue(Promise.resolve());
|
||||||
documentList.currentFolderPath = null;
|
documentList.currentFolderPath = null;
|
||||||
|
documentList.ngOnChanges({currentFolderPath: new SimpleChange('', null)});
|
||||||
|
|
||||||
expect(documentList.currentFolderPath).toBe(documentList.DEFAULT_ROOT_FOLDER);
|
expect(documentList.currentFolderPath).toBe(documentList.DEFAULT_FOLDER_PATH);
|
||||||
expect(documentList.displayFolderContent).toHaveBeenCalledWith(documentList.DEFAULT_ROOT_FOLDER);
|
expect(documentList.loadFolderByPath).toHaveBeenCalledWith(documentList.DEFAULT_FOLDER_PATH);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should emit folder changed event', (done) => {
|
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 => {
|
documentList.folderChange.subscribe(e => {
|
||||||
done();
|
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) => {
|
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';
|
let path = '/path';
|
||||||
|
|
||||||
@@ -239,6 +243,7 @@ describe('DocumentList', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
documentList.currentFolderPath = path;
|
documentList.currentFolderPath = path;
|
||||||
|
documentList.ngOnChanges({currentFolderPath: new SimpleChange(null, path)});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should execute context action on callback', () => {
|
it('should execute context action on callback', () => {
|
||||||
@@ -259,7 +264,7 @@ describe('DocumentList', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should subscribe to context action handler', () => {
|
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();
|
spyOn(documentList, 'contextActionCallback').and.stub();
|
||||||
let value = {};
|
let value = {};
|
||||||
documentList.ngOnInit();
|
documentList.ngOnInit();
|
||||||
@@ -384,16 +389,16 @@ describe('DocumentList', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should display folder content on reload', () => {
|
it('should display folder content on reload', () => {
|
||||||
spyOn(documentList, 'displayFolderContent').and.callThrough();
|
spyOn(documentList, 'loadFolderByPath').and.callThrough();
|
||||||
documentList.reload();
|
documentList.reload();
|
||||||
expect(documentList.displayFolderContent).toHaveBeenCalled();
|
expect(documentList.loadFolderByPath).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should require path to display folder content', () => {
|
it('should require path to display folder content', () => {
|
||||||
spyOn(documentListService, 'getFolder').and.callThrough();
|
spyOn(documentListService, 'getFolder').and.callThrough();
|
||||||
|
|
||||||
documentList.displayFolderContent(null);
|
documentList.loadFolderByPath(null);
|
||||||
documentList.displayFolderContent('');
|
documentList.loadFolderByPath('');
|
||||||
|
|
||||||
expect(documentListService.getFolder).not.toHaveBeenCalled();
|
expect(documentListService.getFolder).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
@@ -464,11 +469,11 @@ describe('DocumentList', () => {
|
|||||||
});
|
});
|
||||||
expect(documentList.currentFolderPath).toBeNull();
|
expect(documentList.currentFolderPath).toBeNull();
|
||||||
|
|
||||||
spyOn(documentList, 'displayFolderContent').and.stub();
|
spyOn(documentList, 'loadFolderByPath').and.stub();
|
||||||
|
|
||||||
documentList.reload();
|
documentList.reload();
|
||||||
|
|
||||||
expect(documentList.displayFolderContent).not.toHaveBeenCalled();
|
expect(documentList.loadFolderByPath).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should enforce single-click on mobile browser', () => {
|
it('should enforce single-click on mobile browser', () => {
|
||||||
@@ -482,9 +487,10 @@ describe('DocumentList', () => {
|
|||||||
it('should emit error on wrong path', (done) => {
|
it('should emit error on wrong path', (done) => {
|
||||||
let raised = false;
|
let raised = false;
|
||||||
documentList.error.subscribe(err => raised = true);
|
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.currentFolderPath = 'wrong-path';
|
||||||
|
documentList.ngOnChanges({currentFolderPath: new SimpleChange(null, documentList.currentFolderPath)});
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
expect(raised).toBeTruthy();
|
expect(raised).toBeTruthy();
|
||||||
done();
|
done();
|
||||||
@@ -506,24 +512,24 @@ describe('DocumentList', () => {
|
|||||||
expect(documentList.isEmptyTemplateDefined()).toBeFalsy();
|
expect(documentList.isEmptyTemplateDefined()).toBeFalsy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set root path for underlying adapter', () => {
|
it('should set root folder ID for underlying adapter', () => {
|
||||||
documentList.rootPath = 'test';
|
documentList.rootFolderId = 'test';
|
||||||
expect(documentList.data.rootPath).toBe('test');
|
expect(documentList.data.rootFolderId).toBe('test');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set default root path for underlying adapter', () => {
|
it('should set default root folder ID for underlying adapter', () => {
|
||||||
documentList.rootPath = null;
|
documentList.rootFolderId = null;
|
||||||
expect(documentList.data.rootPath).toBe(documentList.data.DEFAULT_ROOT_PATH);
|
expect(documentList.data.rootFolderId).toBe(documentList.data.DEFAULT_ROOT_ID);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fetch root path from underlying adapter', () => {
|
it('should fetch root folder ID from underlying adapter', () => {
|
||||||
documentList.data.rootPath = 'test';
|
documentList.data.rootFolderId = 'test';
|
||||||
expect(documentList.rootPath).toBe('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;
|
documentList.data = null;
|
||||||
expect(documentList.rootPath).toBeNull();
|
expect(documentList.rootFolderId).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set row filter for underlying adapter', () => {
|
it('should set row filter for underlying adapter', () => {
|
||||||
@@ -561,4 +567,49 @@ describe('DocumentList', () => {
|
|||||||
documentList.onRowDblClick(event);
|
documentList.onRowDblClick(event);
|
||||||
expect(documentList.onNodeDblClick).toHaveBeenCalledWith(node);
|
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,
|
Component,
|
||||||
OnInit,
|
OnInit,
|
||||||
Input,
|
Input,
|
||||||
|
OnChanges,
|
||||||
Output,
|
Output,
|
||||||
|
SimpleChanges,
|
||||||
EventEmitter,
|
EventEmitter,
|
||||||
AfterContentInit,
|
AfterContentInit,
|
||||||
TemplateRef,
|
TemplateRef,
|
||||||
@@ -48,22 +50,25 @@ declare var module: any;
|
|||||||
styleUrls: ['./document-list.css'],
|
styleUrls: ['./document-list.css'],
|
||||||
templateUrl: './document-list.html'
|
templateUrl: './document-list.html'
|
||||||
})
|
})
|
||||||
export class DocumentList implements OnInit, AfterContentInit {
|
export class DocumentList implements OnInit, OnChanges, AfterContentInit {
|
||||||
|
|
||||||
static SINGLE_CLICK_NAVIGATION: string = 'click';
|
static SINGLE_CLICK_NAVIGATION: string = 'click';
|
||||||
static DOUBLE_CLICK_NAVIGATION: string = 'dblclick';
|
static DOUBLE_CLICK_NAVIGATION: string = 'dblclick';
|
||||||
static DEFAULT_PAGE_SIZE: number = 20;
|
static DEFAULT_PAGE_SIZE: number = 20;
|
||||||
|
|
||||||
DEFAULT_ROOT_FOLDER: string = '/';
|
DEFAULT_FOLDER_PATH: string = '/';
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
set rootPath(value: string) {
|
set rootFolderId(value: string) {
|
||||||
this.data.rootPath = value || this.data.DEFAULT_ROOT_PATH;
|
this.data.rootFolderId = value || this.data.DEFAULT_ROOT_ID;
|
||||||
}
|
}
|
||||||
|
|
||||||
get rootPath(): string {
|
@Input()
|
||||||
|
currentFolderId: string = null;
|
||||||
|
|
||||||
|
get rootFolderId(): string {
|
||||||
if (this.data) {
|
if (this.data) {
|
||||||
return this.data.rootPath;
|
return this.data.rootFolderId;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -127,21 +132,11 @@ export class DocumentList implements OnInit, AfterContentInit {
|
|||||||
@ViewChild(DataTableComponent)
|
@ViewChild(DataTableComponent)
|
||||||
dataTable: DataTableComponent;
|
dataTable: DataTableComponent;
|
||||||
|
|
||||||
private _path = this.DEFAULT_ROOT_FOLDER;
|
private _path = this.DEFAULT_FOLDER_PATH;
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
set currentFolderPath(value: string) {
|
set currentFolderPath(value: string) {
|
||||||
if (value !== this._path) {
|
this._path = value;
|
||||||
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);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get currentFolderPath(): string {
|
get currentFolderPath(): string {
|
||||||
@@ -220,6 +215,8 @@ export class DocumentList implements OnInit, AfterContentInit {
|
|||||||
if (this.isMobile()) {
|
if (this.isMobile()) {
|
||||||
this.navigationMode = DocumentList.SINGLE_CLICK_NAVIGATION;
|
this.navigationMode = DocumentList.SINGLE_CLICK_NAVIGATION;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.loadFolder();
|
||||||
}
|
}
|
||||||
|
|
||||||
ngAfterContentInit() {
|
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() {
|
isEmptyTemplateDefined() {
|
||||||
if (this.dataTable) {
|
if (this.dataTable) {
|
||||||
if (this.emptyFolderTemplate) {
|
if (this.emptyFolderTemplate) {
|
||||||
@@ -277,6 +308,9 @@ export class DocumentList implements OnInit, AfterContentInit {
|
|||||||
performNavigation(node: MinimalNodeEntity): boolean {
|
performNavigation(node: MinimalNodeEntity): boolean {
|
||||||
if (node && node.entry && node.entry.isFolder) {
|
if (node && node.entry && node.entry.isFolder) {
|
||||||
this.currentFolderPath = this.getNodePath(node);
|
this.currentFolderPath = this.getNodePath(node);
|
||||||
|
this.currentFolderId = null;
|
||||||
|
this.loadFolder();
|
||||||
|
this.folderChange.emit({ path: this.currentFolderPath });
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
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);
|
return this.data.loadPath(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
loadFolderById(id: string): Promise<any> {
|
||||||
|
return this.data.loadById(id);
|
||||||
|
}
|
||||||
|
|
||||||
reload() {
|
reload() {
|
||||||
this.ngZone.run(() => {
|
this.ngZone.run(() => {
|
||||||
if (this.currentFolderPath) {
|
this.loadFolder();
|
||||||
this.displayFolderContent(this.currentFolderPath)
|
|
||||||
.catch(err => {
|
|
||||||
this.error.emit(err);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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.
|
* Gets a path for a given node.
|
||||||
* @param node
|
* @param node
|
||||||
|
@@ -31,7 +31,7 @@ export class ShareDataTableAdapter implements DataTableAdapter, PaginationProvid
|
|||||||
ERR_ROW_NOT_FOUND: string = 'Row not found';
|
ERR_ROW_NOT_FOUND: string = 'Row not found';
|
||||||
ERR_COL_NOT_FOUND: string = 'Column not found';
|
ERR_COL_NOT_FOUND: string = 'Column not found';
|
||||||
|
|
||||||
DEFAULT_ROOT_PATH: string = '-root-';
|
DEFAULT_ROOT_ID: string = '-root-';
|
||||||
DEFAULT_DATE_FORMAT: string = 'medium';
|
DEFAULT_DATE_FORMAT: string = 'medium';
|
||||||
DEFAULT_PAGE_SIZE: number = 20;
|
DEFAULT_PAGE_SIZE: number = 20;
|
||||||
MIN_PAGE_SIZE: number = 5;
|
MIN_PAGE_SIZE: number = 5;
|
||||||
@@ -53,7 +53,7 @@ export class ShareDataTableAdapter implements DataTableAdapter, PaginationProvid
|
|||||||
|
|
||||||
thumbnails: boolean = false;
|
thumbnails: boolean = false;
|
||||||
dataLoaded: DataLoadedEventEmitter;
|
dataLoaded: DataLoadedEventEmitter;
|
||||||
rootPath: string = this.DEFAULT_ROOT_PATH;
|
rootFolderId: string = this.DEFAULT_ROOT_ID;
|
||||||
|
|
||||||
constructor(private documentListService: DocumentListService,
|
constructor(private documentListService: DocumentListService,
|
||||||
private basePath: string,
|
private basePath: string,
|
||||||
@@ -210,7 +210,7 @@ export class ShareDataTableAdapter implements DataTableAdapter, PaginationProvid
|
|||||||
.getFolder(path, {
|
.getFolder(path, {
|
||||||
maxItems: this._maxItems,
|
maxItems: this._maxItems,
|
||||||
skipCount: this._skipCount,
|
skipCount: this._skipCount,
|
||||||
rootPath: this.rootPath
|
rootFolderId: this.rootFolderId
|
||||||
})
|
})
|
||||||
.subscribe(val => {
|
.subscribe(val => {
|
||||||
this.currentPath = path;
|
this.currentPath = path;
|
||||||
@@ -228,6 +228,30 @@ export class ShareDataTableAdapter implements DataTableAdapter, PaginationProvid
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
loadById(id: string): Promise<any> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
if (id && this.documentListService) {
|
||||||
|
this.documentListService
|
||||||
|
.getFolder(null, {
|
||||||
|
maxItems: this._maxItems,
|
||||||
|
skipCount: this._skipCount,
|
||||||
|
rootFolderId: id
|
||||||
|
})
|
||||||
|
.subscribe(val => {
|
||||||
|
this.loadPage(<NodePaging>val);
|
||||||
|
this.dataLoaded.emit(null);
|
||||||
|
resolve(true);
|
||||||
|
},
|
||||||
|
error => {
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
resolve(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
setFilter(filter: RowFilter) {
|
setFilter(filter: RowFilter) {
|
||||||
this.filter = filter;
|
this.filter = filter;
|
||||||
|
|
||||||
|
@@ -30,6 +30,8 @@ export class DocumentListService {
|
|||||||
|
|
||||||
static DEFAULT_MIME_TYPE_ICON: string = 'ft_ic_miscellaneous.svg';
|
static DEFAULT_MIME_TYPE_ICON: string = 'ft_ic_miscellaneous.svg';
|
||||||
|
|
||||||
|
static ROOT_ID = '-root-';
|
||||||
|
|
||||||
mimeTypeIcons: any = {
|
mimeTypeIcons: any = {
|
||||||
'image/png': 'ft_ic_raster_image.svg',
|
'image/png': 'ft_ic_raster_image.svg',
|
||||||
'image/jpeg': 'ft_ic_raster_image.svg',
|
'image/jpeg': 'ft_ic_raster_image.svg',
|
||||||
@@ -63,17 +65,21 @@ export class DocumentListService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private getNodesPromise(folder: string, opts?: any): Promise<NodePaging> {
|
private getNodesPromise(folder: string, opts?: any): Promise<NodePaging> {
|
||||||
let rootPath = '-root-';
|
|
||||||
|
|
||||||
if (opts && opts.rootPath) {
|
let rootNodeId = DocumentListService.ROOT_ID;
|
||||||
rootPath = opts.rootPath;
|
if (opts && opts.rootFolderId) {
|
||||||
|
rootNodeId = opts.rootFolderId;
|
||||||
}
|
}
|
||||||
|
|
||||||
let params: any = {
|
let params: any = {
|
||||||
relativePath: folder,
|
includeSource: true,
|
||||||
include: ['path', 'properties']
|
include: ['path', 'properties']
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (folder) {
|
||||||
|
params.relativePath = folder;
|
||||||
|
}
|
||||||
|
|
||||||
if (opts) {
|
if (opts) {
|
||||||
if (opts.maxItems) {
|
if (opts.maxItems) {
|
||||||
params.maxItems = opts.maxItems;
|
params.maxItems = opts.maxItems;
|
||||||
@@ -83,7 +89,7 @@ export class DocumentListService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.apiService.getInstance().nodes.getNodeChildren(rootPath, params);
|
return this.apiService.getInstance().nodes.getNodeChildren(rootNodeId, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteNode(nodeId: string): Observable<any> {
|
deleteNode(nodeId: string): Observable<any> {
|
||||||
@@ -105,7 +111,7 @@ export class DocumentListService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the folder node with the content.
|
* Gets the folder node with the specified relative name path below the root node.
|
||||||
* @param folder Path to folder.
|
* @param folder Path to folder.
|
||||||
* @param opts Options.
|
* @param opts Options.
|
||||||
* @returns {Observable<NodePaging>} Folder entity.
|
* @returns {Observable<NodePaging>} Folder entity.
|
||||||
|
@@ -225,7 +225,7 @@ platformBrowserDynamic().bootstrapModule(AppModule);
|
|||||||
|
|
||||||
| Name | Description |
|
| Name | Description |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| `preview` | Emitted when a file result is clicked/selected |
|
| `navigate` | Emitted when a search result is clicked or double-clicked |
|
||||||
| `resultsLoad` | Emitted when search results have fully loaded |
|
| `resultsLoad` | Emitted when search results have fully loaded |
|
||||||
|
|
||||||
#### Options
|
#### Options
|
||||||
@@ -237,6 +237,7 @@ platformBrowserDynamic().bootstrapModule(AppModule);
|
|||||||
| `resultType` | {boolean} | (optional) | (none) | Node type to filter search results by, e.g. 'cm:content'. |
|
| `resultType` | {boolean} | (optional) | (none) | Node type to filter search results by, e.g. 'cm:content'. |
|
||||||
| `maxResults` | {boolean} | (optional) | 20 | Maximum number of results to show in the search. |
|
| `maxResults` | {boolean} | (optional) | 20 | Maximum number of results to show in the search. |
|
||||||
| `resultSort` | {boolean} | (optional) | (none) | Criteria to sort search results by, must be one of "name" , "modifiedAt" or "createdAt" |
|
| `resultSort` | {boolean} | (optional) | (none) | Criteria to sort search results by, must be one of "name" , "modifiedAt" or "createdAt" |
|
||||||
|
| `navigationMode` | {string} | (optional) | "dblclick" | Event used to initiate a navigation action to a specific result, one of "click" or "dblclick" |
|
||||||
|
|
||||||
### Build from sources
|
### Build from sources
|
||||||
|
|
||||||
|
@@ -214,7 +214,7 @@ describe('AlfrescoSearchAutocompleteComponent', () => {
|
|||||||
searchService = fixture.debugElement.injector.get(AlfrescoSearchService);
|
searchService = fixture.debugElement.injector.get(AlfrescoSearchService);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should emit file select when file item clicked', (done) => {
|
it('should emit fileSelect event when file item clicked', (done) => {
|
||||||
|
|
||||||
spyOn(searchService, 'getQueryNodesPromise')
|
spyOn(searchService, 'getQueryNodesPromise')
|
||||||
.and.returnValue(Promise.resolve(result));
|
.and.returnValue(Promise.resolve(result));
|
||||||
@@ -231,7 +231,7 @@ describe('AlfrescoSearchAutocompleteComponent', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not emit preview if a non-file item is clicked', (done) => {
|
it('should emit fileSelect event if when folder item clicked', (done) => {
|
||||||
|
|
||||||
spyOn(searchService, 'getQueryNodesPromise')
|
spyOn(searchService, 'getQueryNodesPromise')
|
||||||
.and.returnValue(Promise.resolve(folderResult));
|
.and.returnValue(Promise.resolve(folderResult));
|
||||||
@@ -240,7 +240,7 @@ describe('AlfrescoSearchAutocompleteComponent', () => {
|
|||||||
component.resultsLoad.subscribe(() => {
|
component.resultsLoad.subscribe(() => {
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
(<any> element.querySelector('#result_row_0')).click();
|
(<any> element.querySelector('#result_row_0')).click();
|
||||||
expect(component.fileSelect.emit).not.toHaveBeenCalled();
|
expect(component.fileSelect.emit).toHaveBeenCalled();
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -19,6 +19,7 @@ import { Component, ElementRef, EventEmitter, Input, OnInit, OnChanges, Output,
|
|||||||
import { AlfrescoSearchService, SearchOptions } from './../services/alfresco-search.service';
|
import { AlfrescoSearchService, SearchOptions } from './../services/alfresco-search.service';
|
||||||
import { AlfrescoThumbnailService } from './../services/alfresco-thumbnail.service';
|
import { AlfrescoThumbnailService } from './../services/alfresco-thumbnail.service';
|
||||||
import { AlfrescoTranslationService } from 'ng2-alfresco-core';
|
import { AlfrescoTranslationService } from 'ng2-alfresco-core';
|
||||||
|
import { MinimalNodeEntity } from 'alfresco-js-api';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
moduleId: module.id,
|
moduleId: module.id,
|
||||||
@@ -121,10 +122,12 @@ export class AlfrescoSearchAutocompleteComponent implements OnInit, OnChanges {
|
|||||||
* @param node Node to get URL for.
|
* @param node Node to get URL for.
|
||||||
* @returns {string} URL address.
|
* @returns {string} URL address.
|
||||||
*/
|
*/
|
||||||
getMimeTypeIcon(node: any): string {
|
getMimeTypeIcon(node: MinimalNodeEntity): string {
|
||||||
if (node.entry.content && node.entry.content.mimeType) {
|
if (node.entry.content && node.entry.content.mimeType) {
|
||||||
let icon = this.alfrescoThumbnailService.getMimeTypeIcon(node.entry.content.mimeType);
|
let icon = this.alfrescoThumbnailService.getMimeTypeIcon(node.entry.content.mimeType);
|
||||||
return this.resolveIconPath(icon);
|
return this.resolveIconPath(icon);
|
||||||
|
} else if (node.entry.isFolder) {
|
||||||
|
return 'ft_ic_folder.svg';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,7 +151,7 @@ export class AlfrescoSearchAutocompleteComponent implements OnInit, OnChanges {
|
|||||||
* @param node Node to get URL for.
|
* @param node Node to get URL for.
|
||||||
* @returns {string} URL address.
|
* @returns {string} URL address.
|
||||||
*/
|
*/
|
||||||
getMimeTypeKey(node: any): string {
|
getMimeTypeKey(node: MinimalNodeEntity): string {
|
||||||
if (node.entry.content && node.entry.content.mimeType) {
|
if (node.entry.content && node.entry.content.mimeType) {
|
||||||
return 'SEARCH.ICONS.' + this.alfrescoThumbnailService.getMimeTypeKey(node.entry.content.mimeType);
|
return 'SEARCH.ICONS.' + this.alfrescoThumbnailService.getMimeTypeKey(node.entry.content.mimeType);
|
||||||
} else {
|
} else {
|
||||||
@@ -161,13 +164,9 @@ export class AlfrescoSearchAutocompleteComponent implements OnInit, OnChanges {
|
|||||||
firstResult.focus();
|
firstResult.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
onItemClick(node): void {
|
onItemClick(node: MinimalNodeEntity): void {
|
||||||
if (node && node.entry) {
|
if (node && node.entry) {
|
||||||
if (node.entry.isFile) {
|
this.fileSelect.emit(node);
|
||||||
this.fileSelect.emit({
|
|
||||||
value: node
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -179,12 +178,10 @@ export class AlfrescoSearchAutocompleteComponent implements OnInit, OnChanges {
|
|||||||
this.searchFocus.emit($event);
|
this.searchFocus.emit($event);
|
||||||
}
|
}
|
||||||
|
|
||||||
onRowEnter(node): void {
|
onRowEnter(node: MinimalNodeEntity): void {
|
||||||
if (node && node.entry) {
|
if (node && node.entry) {
|
||||||
if (node.entry.isFile) {
|
if (node.entry.isFile) {
|
||||||
this.fileSelect.emit({
|
this.fileSelect.emit(node);
|
||||||
value: node
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -72,7 +72,7 @@ export class AlfrescoSearchControlComponent implements OnInit, OnDestroy {
|
|||||||
liveSearchRoot: string = '-root-';
|
liveSearchRoot: string = '-root-';
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
liveSearchResultType: string = 'cm:content';
|
liveSearchResultType: string = null;
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
liveSearchResultSort: string = null;
|
liveSearchResultSort: string = null;
|
||||||
@@ -171,9 +171,7 @@ export class AlfrescoSearchControlComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onFileClicked(event): void {
|
onFileClicked(event): void {
|
||||||
this.fileSelect.emit({
|
this.fileSelect.emit(event);
|
||||||
value: event.value
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onSearchFocus($event): void {
|
onSearchFocus($event): void {
|
||||||
|
@@ -16,7 +16,7 @@
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
|
||||||
<tr id="result_row_{{idx}}" tabindex="0" *ngFor="let result of results; let idx = index" (click)="onItemClick(result, $event)" (keyup.enter)="onItemClick(result, $event)">
|
<tr id="result_row_{{idx}}" tabindex="0" *ngFor="let result of results; let idx = index" (click)="onItemClick(result, $event)" (dblclick)="onItemDblClick(result, $event)" (keyup.enter)="onItemClick(result, $event)">
|
||||||
<td class="col-mimetype-icon"><img src="{{getMimeTypeIcon(result)}}" alt="{{getMimeTypeKey(result)|translate}}" /></td>
|
<td class="col-mimetype-icon"><img src="{{getMimeTypeIcon(result)}}" alt="{{getMimeTypeKey(result)|translate}}" /></td>
|
||||||
<td id="result_name_{{idx}}" class="mdl-data-table__cell--non-numeric col-display-name"
|
<td id="result_name_{{idx}}" class="mdl-data-table__cell--non-numeric col-display-name"
|
||||||
attr.data-automation-id=file_{{result.entry.name}} >{{result.entry.name}}</td>
|
attr.data-automation-id=file_{{result.entry.name}} >{{result.entry.name}}</td>
|
||||||
|
@@ -15,7 +15,8 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ReflectiveInjector, SimpleChange } from '@angular/core';
|
import { DebugElement, ReflectiveInjector, SimpleChange } from '@angular/core';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
import { ComponentFixture, TestBed, async } from '@angular/core/testing';
|
import { ComponentFixture, TestBed, async } from '@angular/core/testing';
|
||||||
import { ActivatedRoute } from '@angular/router';
|
import { ActivatedRoute } from '@angular/router';
|
||||||
import { Observable } from 'rxjs/Rx';
|
import { Observable } from 'rxjs/Rx';
|
||||||
@@ -256,44 +257,91 @@ describe('AlfrescoSearchComponent', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('search result actions', () => {
|
describe('search result interactions', () => {
|
||||||
|
|
||||||
it('should emit preview when file item clicked', (done) => {
|
let debugElement: DebugElement;
|
||||||
|
let searchService: AlfrescoSearchService;
|
||||||
|
let querySpy: jasmine.Spy;
|
||||||
|
let emitSpy: jasmine.Spy;
|
||||||
|
const rowSelector = '[data-automation-id="search_result_table"] tbody tr';
|
||||||
|
|
||||||
let searchService = fixture.debugElement.injector.get(AlfrescoSearchService);
|
beforeEach(() => {
|
||||||
spyOn(searchService, 'getQueryNodesPromise')
|
debugElement = fixture.debugElement;
|
||||||
.and.returnValue(Promise.resolve(result));
|
searchService = fixture.debugElement.injector.get(AlfrescoSearchService);
|
||||||
|
querySpy = spyOn(searchService, 'getQueryNodesPromise').and.returnValue(Promise.resolve(result));
|
||||||
|
emitSpy = spyOn(component.navigate, 'emit');
|
||||||
|
});
|
||||||
|
|
||||||
component.resultsLoad.subscribe(() => {
|
describe('click results', () => {
|
||||||
fixture.detectChanges();
|
|
||||||
(<HTMLTableRowElement> element.querySelector('#result_row_0')).click();
|
beforeEach(() => {
|
||||||
|
component.navigationMode = AlfrescoSearchComponent.SINGLE_CLICK_NAVIGATION;
|
||||||
});
|
});
|
||||||
|
|
||||||
component.searchTerm = 'searchTerm';
|
it('should emit navigation event when file item clicked', (done) => {
|
||||||
component.ngOnInit();
|
|
||||||
|
|
||||||
component.preview.subscribe(() => {
|
component.resultsLoad.subscribe(() => {
|
||||||
done();
|
fixture.detectChanges();
|
||||||
|
debugElement.query(By.css(rowSelector)).triggerEventHandler('click', {});
|
||||||
|
expect(emitSpy).toHaveBeenCalled();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
component.searchTerm = 'searchTerm';
|
||||||
|
component.ngOnInit();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should emit navigation event when non-file item is clicked', (done) => {
|
||||||
|
|
||||||
|
querySpy.and.returnValue(Promise.resolve(folderResult));
|
||||||
|
|
||||||
|
component.resultsLoad.subscribe(() => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
debugElement.query(By.css(rowSelector)).triggerEventHandler('click', {});
|
||||||
|
expect(emitSpy).toHaveBeenCalled();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
component.searchTerm = 'searchTerm';
|
||||||
|
component.ngOnInit();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not emit preview when non-file item is clicked', (done) => {
|
describe('double click results', () => {
|
||||||
|
|
||||||
let searchService = fixture.debugElement.injector.get(AlfrescoSearchService);
|
beforeEach(() => {
|
||||||
spyOn(searchService, 'getQueryNodesPromise')
|
component.navigationMode = AlfrescoSearchComponent.DOUBLE_CLICK_NAVIGATION;
|
||||||
.and.returnValue(Promise.resolve(folderResult));
|
|
||||||
|
|
||||||
spyOn(component.preview, 'emit');
|
|
||||||
component.resultsLoad.subscribe(() => {
|
|
||||||
fixture.detectChanges();
|
|
||||||
(<HTMLTableRowElement> element.querySelector('#result_row_0')).click();
|
|
||||||
expect(component.preview.emit).not.toHaveBeenCalled();
|
|
||||||
done();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
component.searchTerm = 'searchTerm';
|
it('should emit navigation event when file item clicked', (done) => {
|
||||||
component.ngOnInit();
|
|
||||||
|
component.resultsLoad.subscribe(() => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
debugElement.query(By.css(rowSelector)).triggerEventHandler('dblclick', {});
|
||||||
|
expect(emitSpy).toHaveBeenCalled();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
component.searchTerm = 'searchTerm';
|
||||||
|
component.ngOnInit();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should emit navigation event when non-file item is clicked', (done) => {
|
||||||
|
|
||||||
|
querySpy.and.returnValue(Promise.resolve(folderResult));
|
||||||
|
|
||||||
|
component.resultsLoad.subscribe(() => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
debugElement.query(By.css(rowSelector)).triggerEventHandler('dblclick', {});
|
||||||
|
expect(emitSpy).toHaveBeenCalled();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
component.searchTerm = 'searchTerm';
|
||||||
|
component.ngOnInit();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@@ -20,6 +20,7 @@ import { ActivatedRoute, Params } from '@angular/router';
|
|||||||
import { AlfrescoSearchService, SearchOptions } from './../services/alfresco-search.service';
|
import { AlfrescoSearchService, SearchOptions } from './../services/alfresco-search.service';
|
||||||
import { AlfrescoThumbnailService } from './../services/alfresco-thumbnail.service';
|
import { AlfrescoThumbnailService } from './../services/alfresco-thumbnail.service';
|
||||||
import { AlfrescoTranslationService } from 'ng2-alfresco-core';
|
import { AlfrescoTranslationService } from 'ng2-alfresco-core';
|
||||||
|
import { MinimalNodeEntity } from 'alfresco-js-api';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
moduleId: module.id,
|
moduleId: module.id,
|
||||||
@@ -29,6 +30,9 @@ import { AlfrescoTranslationService } from 'ng2-alfresco-core';
|
|||||||
})
|
})
|
||||||
export class AlfrescoSearchComponent implements OnChanges, OnInit {
|
export class AlfrescoSearchComponent implements OnChanges, OnInit {
|
||||||
|
|
||||||
|
static SINGLE_CLICK_NAVIGATION: string = 'click';
|
||||||
|
static DOUBLE_CLICK_NAVIGATION: string = 'dblclick';
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
searchTerm: string = '';
|
searchTerm: string = '';
|
||||||
|
|
||||||
@@ -44,8 +48,11 @@ export class AlfrescoSearchComponent implements OnChanges, OnInit {
|
|||||||
@Input()
|
@Input()
|
||||||
resultType: string = null;
|
resultType: string = null;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
navigationMode: string = AlfrescoSearchComponent.DOUBLE_CLICK_NAVIGATION; // click|dblclick
|
||||||
|
|
||||||
@Output()
|
@Output()
|
||||||
preview: EventEmitter<any> = new EventEmitter();
|
navigate: EventEmitter<MinimalNodeEntity> = new EventEmitter<MinimalNodeEntity>();
|
||||||
|
|
||||||
@Output()
|
@Output()
|
||||||
resultsLoad = new EventEmitter();
|
resultsLoad = new EventEmitter();
|
||||||
@@ -92,6 +99,8 @@ export class AlfrescoSearchComponent implements OnChanges, OnInit {
|
|||||||
if (node.entry.content && node.entry.content.mimeType) {
|
if (node.entry.content && node.entry.content.mimeType) {
|
||||||
let icon = this._alfrescoThumbnailService.getMimeTypeIcon(node.entry.content.mimeType);
|
let icon = this._alfrescoThumbnailService.getMimeTypeIcon(node.entry.content.mimeType);
|
||||||
return this.resolveIconPath(icon);
|
return this.resolveIconPath(icon);
|
||||||
|
} else if (node.entry.isFolder) {
|
||||||
|
return 'ft_ic_folder.svg';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,14 +163,17 @@ export class AlfrescoSearchComponent implements OnChanges, OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onItemClick(node, event?: Event): void {
|
onItemClick(node, event?: Event): void {
|
||||||
if (event) {
|
if (this.navigate && this.navigationMode === AlfrescoSearchComponent.SINGLE_CLICK_NAVIGATION) {
|
||||||
event.preventDefault();
|
if (node && node.entry) {
|
||||||
|
this.navigate.emit(node);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (node && node.entry) {
|
}
|
||||||
if (node.entry.isFile) {
|
|
||||||
this.preview.emit({
|
onItemDblClick(node: MinimalNodeEntity) {
|
||||||
value: node
|
if (this.navigate && this.navigationMode === AlfrescoSearchComponent.DOUBLE_CLICK_NAVIGATION) {
|
||||||
});
|
if (node && node.entry) {
|
||||||
|
this.navigate.emit(node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user