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:
@@ -225,7 +225,7 @@ platformBrowserDynamic().bootstrapModule(AppModule);
|
||||
|
||||
| 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 |
|
||||
|
||||
#### Options
|
||||
@@ -237,6 +237,7 @@ platformBrowserDynamic().bootstrapModule(AppModule);
|
||||
| `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. |
|
||||
| `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
|
||||
|
||||
|
@@ -214,7 +214,7 @@ describe('AlfrescoSearchAutocompleteComponent', () => {
|
||||
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')
|
||||
.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')
|
||||
.and.returnValue(Promise.resolve(folderResult));
|
||||
@@ -240,7 +240,7 @@ describe('AlfrescoSearchAutocompleteComponent', () => {
|
||||
component.resultsLoad.subscribe(() => {
|
||||
fixture.detectChanges();
|
||||
(<any> element.querySelector('#result_row_0')).click();
|
||||
expect(component.fileSelect.emit).not.toHaveBeenCalled();
|
||||
expect(component.fileSelect.emit).toHaveBeenCalled();
|
||||
done();
|
||||
});
|
||||
|
||||
|
@@ -19,6 +19,7 @@ import { Component, ElementRef, EventEmitter, Input, OnInit, OnChanges, Output,
|
||||
import { AlfrescoSearchService, SearchOptions } from './../services/alfresco-search.service';
|
||||
import { AlfrescoThumbnailService } from './../services/alfresco-thumbnail.service';
|
||||
import { AlfrescoTranslationService } from 'ng2-alfresco-core';
|
||||
import { MinimalNodeEntity } from 'alfresco-js-api';
|
||||
|
||||
@Component({
|
||||
moduleId: module.id,
|
||||
@@ -121,10 +122,12 @@ export class AlfrescoSearchAutocompleteComponent implements OnInit, OnChanges {
|
||||
* @param node Node to get URL for.
|
||||
* @returns {string} URL address.
|
||||
*/
|
||||
getMimeTypeIcon(node: any): string {
|
||||
getMimeTypeIcon(node: MinimalNodeEntity): string {
|
||||
if (node.entry.content && node.entry.content.mimeType) {
|
||||
let icon = this.alfrescoThumbnailService.getMimeTypeIcon(node.entry.content.mimeType);
|
||||
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.
|
||||
* @returns {string} URL address.
|
||||
*/
|
||||
getMimeTypeKey(node: any): string {
|
||||
getMimeTypeKey(node: MinimalNodeEntity): string {
|
||||
if (node.entry.content && node.entry.content.mimeType) {
|
||||
return 'SEARCH.ICONS.' + this.alfrescoThumbnailService.getMimeTypeKey(node.entry.content.mimeType);
|
||||
} else {
|
||||
@@ -161,13 +164,9 @@ export class AlfrescoSearchAutocompleteComponent implements OnInit, OnChanges {
|
||||
firstResult.focus();
|
||||
}
|
||||
|
||||
onItemClick(node): void {
|
||||
onItemClick(node: MinimalNodeEntity): void {
|
||||
if (node && node.entry) {
|
||||
if (node.entry.isFile) {
|
||||
this.fileSelect.emit({
|
||||
value: node
|
||||
});
|
||||
}
|
||||
this.fileSelect.emit(node);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,12 +178,10 @@ export class AlfrescoSearchAutocompleteComponent implements OnInit, OnChanges {
|
||||
this.searchFocus.emit($event);
|
||||
}
|
||||
|
||||
onRowEnter(node): void {
|
||||
onRowEnter(node: MinimalNodeEntity): void {
|
||||
if (node && node.entry) {
|
||||
if (node.entry.isFile) {
|
||||
this.fileSelect.emit({
|
||||
value: node
|
||||
});
|
||||
this.fileSelect.emit(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -72,7 +72,7 @@ export class AlfrescoSearchControlComponent implements OnInit, OnDestroy {
|
||||
liveSearchRoot: string = '-root-';
|
||||
|
||||
@Input()
|
||||
liveSearchResultType: string = 'cm:content';
|
||||
liveSearchResultType: string = null;
|
||||
|
||||
@Input()
|
||||
liveSearchResultSort: string = null;
|
||||
@@ -171,9 +171,7 @@ export class AlfrescoSearchControlComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
onFileClicked(event): void {
|
||||
this.fileSelect.emit({
|
||||
value: event.value
|
||||
});
|
||||
this.fileSelect.emit(event);
|
||||
}
|
||||
|
||||
onSearchFocus($event): void {
|
||||
|
@@ -16,7 +16,7 @@
|
||||
</thead>
|
||||
<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 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>
|
||||
|
@@ -15,7 +15,8 @@
|
||||
* 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 { ActivatedRoute } from '@angular/router';
|
||||
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);
|
||||
spyOn(searchService, 'getQueryNodesPromise')
|
||||
.and.returnValue(Promise.resolve(result));
|
||||
beforeEach(() => {
|
||||
debugElement = fixture.debugElement;
|
||||
searchService = fixture.debugElement.injector.get(AlfrescoSearchService);
|
||||
querySpy = spyOn(searchService, 'getQueryNodesPromise').and.returnValue(Promise.resolve(result));
|
||||
emitSpy = spyOn(component.navigate, 'emit');
|
||||
});
|
||||
|
||||
component.resultsLoad.subscribe(() => {
|
||||
fixture.detectChanges();
|
||||
(<HTMLTableRowElement> element.querySelector('#result_row_0')).click();
|
||||
describe('click results', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
component.navigationMode = AlfrescoSearchComponent.SINGLE_CLICK_NAVIGATION;
|
||||
});
|
||||
|
||||
component.searchTerm = 'searchTerm';
|
||||
component.ngOnInit();
|
||||
it('should emit navigation event when file item clicked', (done) => {
|
||||
|
||||
component.preview.subscribe(() => {
|
||||
done();
|
||||
component.resultsLoad.subscribe(() => {
|
||||
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);
|
||||
spyOn(searchService, 'getQueryNodesPromise')
|
||||
.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();
|
||||
beforeEach(() => {
|
||||
component.navigationMode = AlfrescoSearchComponent.DOUBLE_CLICK_NAVIGATION;
|
||||
});
|
||||
|
||||
component.searchTerm = 'searchTerm';
|
||||
component.ngOnInit();
|
||||
it('should emit navigation event when file item clicked', (done) => {
|
||||
|
||||
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 { AlfrescoThumbnailService } from './../services/alfresco-thumbnail.service';
|
||||
import { AlfrescoTranslationService } from 'ng2-alfresco-core';
|
||||
import { MinimalNodeEntity } from 'alfresco-js-api';
|
||||
|
||||
@Component({
|
||||
moduleId: module.id,
|
||||
@@ -29,6 +30,9 @@ import { AlfrescoTranslationService } from 'ng2-alfresco-core';
|
||||
})
|
||||
export class AlfrescoSearchComponent implements OnChanges, OnInit {
|
||||
|
||||
static SINGLE_CLICK_NAVIGATION: string = 'click';
|
||||
static DOUBLE_CLICK_NAVIGATION: string = 'dblclick';
|
||||
|
||||
@Input()
|
||||
searchTerm: string = '';
|
||||
|
||||
@@ -44,8 +48,11 @@ export class AlfrescoSearchComponent implements OnChanges, OnInit {
|
||||
@Input()
|
||||
resultType: string = null;
|
||||
|
||||
@Input()
|
||||
navigationMode: string = AlfrescoSearchComponent.DOUBLE_CLICK_NAVIGATION; // click|dblclick
|
||||
|
||||
@Output()
|
||||
preview: EventEmitter<any> = new EventEmitter();
|
||||
navigate: EventEmitter<MinimalNodeEntity> = new EventEmitter<MinimalNodeEntity>();
|
||||
|
||||
@Output()
|
||||
resultsLoad = new EventEmitter();
|
||||
@@ -92,6 +99,8 @@ export class AlfrescoSearchComponent implements OnChanges, OnInit {
|
||||
if (node.entry.content && node.entry.content.mimeType) {
|
||||
let icon = this._alfrescoThumbnailService.getMimeTypeIcon(node.entry.content.mimeType);
|
||||
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 {
|
||||
if (event) {
|
||||
event.preventDefault();
|
||||
if (this.navigate && this.navigationMode === AlfrescoSearchComponent.SINGLE_CLICK_NAVIGATION) {
|
||||
if (node && node.entry) {
|
||||
this.navigate.emit(node);
|
||||
}
|
||||
}
|
||||
if (node && node.entry) {
|
||||
if (node.entry.isFile) {
|
||||
this.preview.emit({
|
||||
value: node
|
||||
});
|
||||
}
|
||||
|
||||
onItemDblClick(node: MinimalNodeEntity) {
|
||||
if (this.navigate && this.navigationMode === AlfrescoSearchComponent.DOUBLE_CLICK_NAVIGATION) {
|
||||
if (node && node.entry) {
|
||||
this.navigate.emit(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user