mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-07-24 17:32:15 +00:00
[ADF-2065] Refactored Content node selector component (#2778)
* [ADF-2065] created dialog component for content node selector * [ADF-2065] removing SiteModel from site dropdown to use SitePaging model of js-api * [ADF-2065] - removed site model and updated documentation * [ADF-2065] fixed test for site component * [ADF-2065] refactored content node selector and created content node selector dialog * [ADF-2065] fixed test on site-api service * [ADF-2065] added a new content node dialog service to centralise the logic for content node dialog * [ADF-2065] start adding test for node-actions service| * [ADF-2065] added test for node-actions service * [ADF-2065] added test for node action service * [ADF-2065] renamed components to keep backward compatibility * [ADF-2065] added input just for backward compatibility * [ADF-2065] added some changes for backward compatibility and updated documentation * [ADF-2065] updated documentation for content node selector
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/*tslint:disable: ban*/
|
||||
|
||||
import { async, TestBed } from '@angular/core/testing';
|
||||
import { MinimalNodeEntryEntity } from 'alfresco-js-api';
|
||||
import { AppConfigService } from '@alfresco/adf-core';
|
||||
import { DocumentListService } from '../document-list/services/document-list.service';
|
||||
import { ContentNodeDialogService } from './content-node-dialog.service';
|
||||
import { MatDialog } from '@angular/material';
|
||||
|
||||
const fakeNode: MinimalNodeEntryEntity = <MinimalNodeEntryEntity> {
|
||||
id: 'fake',
|
||||
name: 'fake-name'
|
||||
};
|
||||
|
||||
describe('ContentNodeDialogService', () => {
|
||||
|
||||
let service: ContentNodeDialogService;
|
||||
// let documentListService: DocumentListService;
|
||||
// let contentDialogService: ContentNodeDialogService;
|
||||
let materialDialog: MatDialog;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [],
|
||||
providers: [
|
||||
ContentNodeDialogService,
|
||||
DocumentListService,
|
||||
MatDialog
|
||||
]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
let appConfig: AppConfigService = TestBed.get(AppConfigService);
|
||||
appConfig.config.ecmHost = 'http://localhost:9876/ecm';
|
||||
|
||||
service = TestBed.get(ContentNodeDialogService);
|
||||
materialDialog = TestBed.get(MatDialog);
|
||||
spyOn(materialDialog, 'open').and.stub();
|
||||
spyOn(materialDialog, 'closeAll').and.stub();
|
||||
|
||||
});
|
||||
|
||||
it('should be able to create the service', () => {
|
||||
expect(service).not.toBeNull();
|
||||
});
|
||||
|
||||
it('should be able to open the dialog when node has permission', () => {
|
||||
service.openCopyMoveDialog('fake-action', fakeNode, '!update');
|
||||
expect(materialDialog.open).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should be able to open the dialog when node has NOT permission', () => {
|
||||
service.openCopyMoveDialog('fake-action', fakeNode, 'noperm').subscribe(
|
||||
() => { },
|
||||
(error) => {
|
||||
expect(materialDialog.open).not.toHaveBeenCalled();
|
||||
expect(error.statusCode).toBe(403);
|
||||
});
|
||||
});
|
||||
|
||||
it('should be able to close the material dialog', () => {
|
||||
service.close();
|
||||
expect(materialDialog.closeAll).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
});
|
@@ -0,0 +1,81 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { MatDialog } from '@angular/material';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { ContentService } from '@alfresco/adf-core';
|
||||
import { Subject } from 'rxjs/Subject';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { ShareDataRow } from '../document-list/data/share-data-row.model';
|
||||
import { MinimalNodeEntryEntity } from 'alfresco-js-api';
|
||||
import { DataColumn } from '@alfresco/adf-core';
|
||||
import { DocumentListService } from '../document-list/services/document-list.service';
|
||||
import { ContentNodeSelectorComponent } from './content-node-selector.component';
|
||||
import { ContentNodeSelectorComponentData } from './content-node-selector.component-data.interface';
|
||||
|
||||
@Injectable()
|
||||
export class ContentNodeDialogService {
|
||||
|
||||
constructor(private dialog: MatDialog,
|
||||
private contentService?: ContentService,
|
||||
private documentListService?: DocumentListService) { }
|
||||
|
||||
openCopyMoveDialog(action: string, contentEntry: MinimalNodeEntryEntity, permission?: string): Observable<MinimalNodeEntryEntity[]> {
|
||||
if (this.contentService.hasPermission(contentEntry, permission)) {
|
||||
const select = new Subject<MinimalNodeEntryEntity[]>();
|
||||
select.subscribe({
|
||||
complete: this.close.bind(this)
|
||||
});
|
||||
|
||||
const data: ContentNodeSelectorComponentData = {
|
||||
title: `${action} '${contentEntry.name}' to ...`,
|
||||
actionName: action,
|
||||
currentFolderId: contentEntry.parentId,
|
||||
rowFilter: this.rowFilter.bind(this, contentEntry.id),
|
||||
imageResolver: this.imageResolver.bind(this),
|
||||
select: select
|
||||
};
|
||||
this.dialog.open(ContentNodeSelectorComponent, { data, panelClass: 'adf-content-node-selector-dialog', width: '630px' });
|
||||
return select;
|
||||
} else {
|
||||
return Observable.throw({ statusCode: 403 });
|
||||
}
|
||||
}
|
||||
|
||||
private imageResolver(row: ShareDataRow, col: DataColumn): string | null {
|
||||
const entry: MinimalNodeEntryEntity = row.node.entry;
|
||||
if (!this.contentService.hasPermission(entry, 'create')) {
|
||||
return this.documentListService.getMimeTypeIcon('disable/folder');
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private rowFilter(currentNodeId, row: ShareDataRow): boolean {
|
||||
const node: MinimalNodeEntryEntity = row.node.entry;
|
||||
|
||||
if (node.id === currentNodeId || node.isFile) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
close() {
|
||||
this.dialog.closeAll();
|
||||
}
|
||||
}
|
@@ -0,0 +1,95 @@
|
||||
<div (node-select)="onNodeSelect($event)">
|
||||
<mat-form-field floatPlaceholder="never" class="adf-content-node-selector-content-input">
|
||||
<input matInput
|
||||
id="searchInput"
|
||||
[formControl]="searchInput"
|
||||
type="text"
|
||||
placeholder="Search"
|
||||
[value]="searchTerm"
|
||||
data-automation-id="content-node-selector-search-input">
|
||||
|
||||
<mat-icon *ngIf="searchTerm.length > 0"
|
||||
matSuffix (click)="clear()"
|
||||
class="adf-content-node-selector-content-input-icon"
|
||||
data-automation-id="content-node-selector-search-clear">clear
|
||||
</mat-icon>
|
||||
|
||||
<mat-icon *ngIf="searchTerm.length === 0"
|
||||
matSuffix
|
||||
class="adf-content-node-selector-content-input-icon"
|
||||
data-automation-id="content-node-selector-search-icon">search
|
||||
</mat-icon>
|
||||
|
||||
</mat-form-field>
|
||||
|
||||
<adf-sites-dropdown
|
||||
(change)="siteChanged($event)"
|
||||
[placeholder]="'NODE_SELECTOR.SELECT_LOCATION'"
|
||||
[hideMyFiles]="dropdownHideMyFiles"
|
||||
[siteList]="dropdownSiteList"
|
||||
data-automation-id="content-node-selector-sites-combo"></adf-sites-dropdown>
|
||||
|
||||
<adf-toolbar>
|
||||
<adf-toolbar-title>
|
||||
<adf-dropdown-breadcrumb *ngIf="needBreadcrumbs()"
|
||||
class="adf-content-node-selector-content-breadcrumb"
|
||||
(navigate)="clear()"
|
||||
[target]="documentList"
|
||||
[folderNode]="breadcrumbFolderNode"
|
||||
data-automation-id="content-node-selector-content-breadcrumb">
|
||||
</adf-dropdown-breadcrumb>
|
||||
</adf-toolbar-title>
|
||||
</adf-toolbar>
|
||||
|
||||
<div
|
||||
class="adf-content-node-selector-content-list"
|
||||
[class.adf-content-node-selector-content-list-searchLayout]="showingSearchResults"
|
||||
data-automation-id="content-node-selector-content-list">
|
||||
<adf-document-list
|
||||
#documentList
|
||||
adf-highlight
|
||||
adf-highlight-selector="adf-name-location-cell .adf-name-location-cell-name"
|
||||
[node]="nodes"
|
||||
[maxItems]="pageSize"
|
||||
[skipCount]="skipCount"
|
||||
[enableInfiniteScrolling]="infiniteScroll"
|
||||
[rowFilter]="rowFilter"
|
||||
[imageResolver]="imageResolver"
|
||||
[currentFolderId]="folderIdToShow"
|
||||
selectionMode="single"
|
||||
[contextMenuActions]="false"
|
||||
[contentActions]="false"
|
||||
[allowDropFiles]="false"
|
||||
(folderChange)="onFolderChange()"
|
||||
(ready)="onFolderLoaded($event)"
|
||||
(node-dblclick)="onNodeDoubleClick($event)"
|
||||
data-automation-id="content-node-selector-document-list">
|
||||
<empty-folder-content>
|
||||
<ng-template>
|
||||
<div>{{ 'NODE_SELECTOR.NO_RESULTS' | translate }}</div>
|
||||
</ng-template>
|
||||
</empty-folder-content>
|
||||
|
||||
<data-columns>
|
||||
<data-column key="$thumbnail" type="image"></data-column>
|
||||
<data-column key="name" type="text" class="full-width ellipsis-cell">
|
||||
<ng-template let-context="$implicit">
|
||||
<adf-name-location-cell [data]="context.data" [column]="context.col" [row]="context.row"></adf-name-location-cell>
|
||||
</ng-template>
|
||||
</data-column>
|
||||
<data-column key="modifiedAt" type="date" format="timeAgo" class="adf-content-selector-modified-cell"></data-column>
|
||||
<data-column key="modifiedByUser.displayName" type="text" class="adf-content-selector-modifier-cell"></data-column>
|
||||
</data-columns>
|
||||
|
||||
</adf-document-list>
|
||||
|
||||
<adf-infinite-pagination
|
||||
[pagination]="pagination"
|
||||
[pageSize]="pageSize"
|
||||
[loading]="loadingSearchResults"
|
||||
(loadMore)="getNextPageOfSearch($event)"
|
||||
data-automation-id="content-node-selector-search-pagination">
|
||||
{{ 'ADF-DOCUMENT-LIST.LAYOUT.LOAD_MORE' | translate }}
|
||||
</adf-infinite-pagination>
|
||||
</div>
|
||||
</div>
|
@@ -0,0 +1,161 @@
|
||||
@mixin adf-content-node-selector-theme($theme) {
|
||||
$primary: map-get($theme, primary);
|
||||
$foreground: map-get($theme, foreground);
|
||||
$background: map-get($theme, background);
|
||||
|
||||
.adf-content-node-selector {
|
||||
|
||||
&-content {
|
||||
padding-top: 0;
|
||||
|
||||
&-input {
|
||||
width: 100%;
|
||||
|
||||
&-icon {
|
||||
color: mat-color($foreground, disabled-button);
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
color: mat-color($foreground, base);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.mat-input-underline .mat-input-ripple {
|
||||
height: 1px;
|
||||
transition: none;
|
||||
}
|
||||
|
||||
.adf-site-dropdown-container {
|
||||
.mat-form-field {
|
||||
display: block;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
.adf-site-dropdown-list-element {
|
||||
width: 100%;
|
||||
margin-bottom: 0;
|
||||
|
||||
.mat-select-trigger {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.adf-toolbar .mat-toolbar {
|
||||
border-bottom-width: 0;
|
||||
font-size: 14px;
|
||||
|
||||
&.mat-toolbar-single-row {
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
|
||||
&-breadcrumb {
|
||||
.adf-dropdown-breadcumb-trigger {
|
||||
outline: none;
|
||||
.mat-icon {
|
||||
color: mat-color($foreground, base, 0.45);
|
||||
|
||||
&:hover {
|
||||
color: mat-color($foreground, base, 0.65);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.adf-dropddown-breadcrumb-item-chevron {
|
||||
color: mat-color($foreground, base, 0.45);
|
||||
}
|
||||
}
|
||||
|
||||
&-list {
|
||||
height: 200px;
|
||||
overflow: auto;
|
||||
border: 1px solid mat-color($foreground, base, 0.07);
|
||||
|
||||
.adf-highlight {
|
||||
color: mat-color($primary);
|
||||
}
|
||||
|
||||
.adf-data-table {
|
||||
border: none;
|
||||
|
||||
.adf-no-content-container {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
thead {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.adf-data-table-cell {
|
||||
padding-top: 8px;
|
||||
padding-bottom: 8px;
|
||||
height: 30px;
|
||||
|
||||
& .adf-name-location-cell-location {
|
||||
display: none;
|
||||
}
|
||||
|
||||
& .adf-name-location-cell-name {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
&--image {
|
||||
padding-left: 16px;
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
&--text {
|
||||
padding-left: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
tbody tr {
|
||||
height: auto !important;
|
||||
|
||||
&:first-child {
|
||||
.adf-data-table-cell {
|
||||
border-top: none;
|
||||
}
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
.adf-data-table-cell {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-searchLayout {
|
||||
|
||||
.adf-data-table {
|
||||
.adf-data-table-cell {
|
||||
height: 56px;
|
||||
padding-bottom: 24px;
|
||||
|
||||
& .adf-name-location-cell-location {
|
||||
display: block
|
||||
}
|
||||
|
||||
& .adf-name-location-cell-name {
|
||||
padding: 18px 0 2px 0;
|
||||
}
|
||||
|
||||
&.adf-content-selector-modified-cell {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&.adf-content-selector-modifier-cell {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,654 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { MinimalNodeEntryEntity, SiteEntry } from 'alfresco-js-api';
|
||||
import {
|
||||
AlfrescoApiService,
|
||||
ContentService,
|
||||
TranslationService,
|
||||
SearchService,
|
||||
SitesService,
|
||||
UserPreferencesService
|
||||
} from '@alfresco/adf-core';
|
||||
import { DataTableModule } from '@alfresco/adf-core';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { Observer } from 'rxjs/Observer';
|
||||
import { MaterialModule } from '../material.module';
|
||||
import { EmptyFolderContentDirective, DocumentListComponent, DocumentListService } from '../document-list';
|
||||
import { DropdownSitesComponent } from '../site-dropdown';
|
||||
import { DropdownBreadcrumbComponent } from '../breadcrumb';
|
||||
import { ContentNodeSelectorPanelComponent } from './content-node-selector-panel.component';
|
||||
import { ContentNodeSelectorService } from './content-node-selector.service';
|
||||
import { NodePaging } from 'alfresco-js-api';
|
||||
|
||||
const ONE_FOLDER_RESULT = {
|
||||
list: {
|
||||
entries: [
|
||||
{
|
||||
entry: {
|
||||
id: '123', name: 'MyFolder', isFile: false, isFolder: true,
|
||||
createdByUser: { displayName: 'John Doe' },
|
||||
modifiedByUser: { displayName: 'John Doe' }
|
||||
}
|
||||
}
|
||||
],
|
||||
pagination: {
|
||||
hasMoreItems: true
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
describe('ContentNodeSelectorComponent', () => {
|
||||
let component: ContentNodeSelectorPanelComponent;
|
||||
let fixture: ComponentFixture<ContentNodeSelectorPanelComponent>;
|
||||
let searchService: SearchService;
|
||||
let searchSpy: jasmine.Spy;
|
||||
|
||||
let _observer: Observer<NodePaging>;
|
||||
|
||||
function typeToSearchBox(searchTerm = 'string-to-search') {
|
||||
let searchInput = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-search-input"]'));
|
||||
searchInput.nativeElement.value = searchTerm;
|
||||
component.searchInput.setValue(searchTerm);
|
||||
fixture.detectChanges();
|
||||
}
|
||||
|
||||
function respondWithSearchResults(result) {
|
||||
_observer.next(result);
|
||||
}
|
||||
|
||||
function setupTestbed(plusProviders) {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
DataTableModule,
|
||||
MaterialModule
|
||||
],
|
||||
declarations: [
|
||||
DocumentListComponent,
|
||||
EmptyFolderContentDirective,
|
||||
DropdownSitesComponent,
|
||||
DropdownBreadcrumbComponent,
|
||||
ContentNodeSelectorPanelComponent
|
||||
],
|
||||
providers: [
|
||||
AlfrescoApiService,
|
||||
ContentService,
|
||||
SearchService,
|
||||
TranslationService,
|
||||
DocumentListService,
|
||||
SitesService,
|
||||
ContentNodeSelectorService,
|
||||
UserPreferencesService,
|
||||
...plusProviders
|
||||
],
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
||||
});
|
||||
}
|
||||
|
||||
afterEach(() => {
|
||||
fixture.destroy();
|
||||
TestBed.resetTestingModule();
|
||||
});
|
||||
|
||||
describe('General component features', () => {
|
||||
|
||||
beforeEach(async(() => {
|
||||
setupTestbed([]);
|
||||
TestBed.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ContentNodeSelectorPanelComponent);
|
||||
component = fixture.componentInstance;
|
||||
component.debounceSearch = 0;
|
||||
|
||||
searchService = TestBed.get(SearchService);
|
||||
searchSpy = spyOn(searchService, 'search').and.callFake(() => {
|
||||
return Observable.create((observer: Observer<NodePaging>) => {
|
||||
_observer = observer;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Parameters', () => {
|
||||
|
||||
it('should trigger the select event when selection has been made', (done) => {
|
||||
const expectedNode = <MinimalNodeEntryEntity> {};
|
||||
component.select.subscribe((nodes) => {
|
||||
expect(nodes.length).toBe(1);
|
||||
expect(nodes[0]).toBe(expectedNode);
|
||||
done();
|
||||
});
|
||||
|
||||
component.chosenNode = expectedNode;
|
||||
});
|
||||
});
|
||||
|
||||
describe('Breadcrumbs', () => {
|
||||
|
||||
let documentListService,
|
||||
sitesService,
|
||||
expectedDefaultFolderNode;
|
||||
|
||||
beforeEach(() => {
|
||||
expectedDefaultFolderNode = <MinimalNodeEntryEntity> { path: { elements: [] } };
|
||||
documentListService = TestBed.get(DocumentListService);
|
||||
sitesService = TestBed.get(SitesService);
|
||||
spyOn(documentListService, 'getFolderNode').and.returnValue(Promise.resolve(expectedDefaultFolderNode));
|
||||
spyOn(documentListService, 'getFolder').and.returnValue(Observable.throw('No results for test'));
|
||||
spyOn(sitesService, 'getSites').and.returnValue(Observable.of({ list: { entries: [] } }));
|
||||
spyOn(component.documentList, 'loadFolderNodesByFolderNodeId').and.returnValue(Promise.resolve());
|
||||
component.currentFolderId = 'cat-girl-nuku-nuku';
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should show the breadcrumb for the currentFolderId by default', (done) => {
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
const breadcrumb = fixture.debugElement.query(By.directive(DropdownBreadcrumbComponent));
|
||||
expect(breadcrumb).not.toBeNull();
|
||||
expect(breadcrumb.componentInstance.folderNode).toBe(expectedDefaultFolderNode);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not show the breadcrumb if search was performed as last action', (done) => {
|
||||
typeToSearchBox();
|
||||
fixture.detectChanges();
|
||||
|
||||
setTimeout(() => {
|
||||
respondWithSearchResults(ONE_FOLDER_RESULT);
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
const breadcrumb = fixture.debugElement.query(By.directive(DropdownBreadcrumbComponent));
|
||||
expect(breadcrumb).toBeNull();
|
||||
done();
|
||||
});
|
||||
}, 300);
|
||||
|
||||
});
|
||||
|
||||
it('should show the breadcrumb again on folder navigation in the results list', (done) => {
|
||||
typeToSearchBox();
|
||||
fixture.detectChanges();
|
||||
|
||||
setTimeout(() => {
|
||||
respondWithSearchResults(ONE_FOLDER_RESULT);
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
component.onFolderChange();
|
||||
fixture.detectChanges();
|
||||
const breadcrumb = fixture.debugElement.query(By.directive(DropdownBreadcrumbComponent));
|
||||
expect(breadcrumb).not.toBeNull();
|
||||
done();
|
||||
});
|
||||
}, 300);
|
||||
|
||||
});
|
||||
|
||||
it('should show the breadcrumb for the selected node when search results are displayed', (done) => {
|
||||
const alfrescoContentService = TestBed.get(ContentService);
|
||||
spyOn(alfrescoContentService, 'hasPermission').and.returnValue(true);
|
||||
|
||||
typeToSearchBox();
|
||||
|
||||
setTimeout(() => {
|
||||
respondWithSearchResults(ONE_FOLDER_RESULT);
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
|
||||
const chosenNode = <MinimalNodeEntryEntity> { path: { elements: ['one'] } };
|
||||
component.onNodeSelect({ detail: { node: { entry: chosenNode } } });
|
||||
fixture.detectChanges();
|
||||
|
||||
const breadcrumb = fixture.debugElement.query(By.directive(DropdownBreadcrumbComponent));
|
||||
expect(breadcrumb).not.toBeNull();
|
||||
expect(breadcrumb.componentInstance.folderNode.path).toBe(chosenNode.path);
|
||||
done();
|
||||
});
|
||||
}, 300);
|
||||
});
|
||||
|
||||
it('should NOT show the breadcrumb for the selected node when not on search results list', (done) => {
|
||||
const alfrescoContentService = TestBed.get(ContentService);
|
||||
spyOn(alfrescoContentService, 'hasPermission').and.returnValue(true);
|
||||
|
||||
typeToSearchBox();
|
||||
|
||||
setTimeout(() => {
|
||||
respondWithSearchResults(ONE_FOLDER_RESULT);
|
||||
|
||||
fixture.detectChanges();
|
||||
component.onFolderChange();
|
||||
fixture.detectChanges();
|
||||
|
||||
const chosenNode = <MinimalNodeEntryEntity> { path: { elements: [] } };
|
||||
component.onNodeSelect({ detail: { node: { entry: chosenNode } } });
|
||||
fixture.detectChanges();
|
||||
|
||||
const breadcrumb = fixture.debugElement.query(By.directive(DropdownBreadcrumbComponent));
|
||||
expect(breadcrumb).not.toBeNull();
|
||||
expect(breadcrumb.componentInstance.folderNode).toBe(expectedDefaultFolderNode);
|
||||
done();
|
||||
}, 300);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Search functionality', () => {
|
||||
|
||||
function defaultSearchOptions(searchTerm, rootNodeId = undefined, skipCount = 0) {
|
||||
|
||||
const parentFiltering = rootNodeId ? [{ query: `ANCESTOR:'workspace://SpacesStore/${rootNodeId}'` }] : [];
|
||||
|
||||
let defaultSearchNode: any = {
|
||||
query: {
|
||||
query: searchTerm ? `${searchTerm}* OR name:${searchTerm}*` : searchTerm
|
||||
},
|
||||
include: ['path', 'allowableOperations'],
|
||||
paging: {
|
||||
maxItems: '25',
|
||||
skipCount: skipCount.toString()
|
||||
},
|
||||
filterQueries: [
|
||||
{ query: "TYPE:'cm:folder'" },
|
||||
{ query: 'NOT cm:creator:System' },
|
||||
...parentFiltering
|
||||
],
|
||||
scope: {
|
||||
locations: ['nodes']
|
||||
}
|
||||
};
|
||||
|
||||
return defaultSearchNode;
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
const documentListService = TestBed.get(DocumentListService);
|
||||
const expectedDefaultFolderNode = <MinimalNodeEntryEntity> { path: { elements: [] } };
|
||||
|
||||
spyOn(documentListService, 'getFolderNode').and.returnValue(Promise.resolve(expectedDefaultFolderNode));
|
||||
spyOn(component.documentList, 'loadFolderNodesByFolderNodeId').and.returnValue(Promise.resolve());
|
||||
|
||||
component.currentFolderId = 'cat-girl-nuku-nuku';
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should load the results by calling the search api on search change', (done) => {
|
||||
typeToSearchBox('kakarot');
|
||||
|
||||
setTimeout(() => {
|
||||
expect(searchSpy).toHaveBeenCalledWith(defaultSearchOptions('kakarot'));
|
||||
done();
|
||||
}, 300);
|
||||
});
|
||||
|
||||
it('should reset the currently chosen node in case of starting a new search', (done) => {
|
||||
component.chosenNode = <MinimalNodeEntryEntity> {};
|
||||
typeToSearchBox('kakarot');
|
||||
|
||||
setTimeout(() => {
|
||||
expect(component.chosenNode).toBeNull();
|
||||
done();
|
||||
}, 300);
|
||||
});
|
||||
|
||||
it('should call the search api on changing the site selectbox\'s value', (done) => {
|
||||
typeToSearchBox('vegeta');
|
||||
|
||||
setTimeout(() => {
|
||||
expect(searchSpy.calls.count()).toBe(1, 'Search count should be one after only one search');
|
||||
|
||||
component.siteChanged(<SiteEntry> { entry: { guid: 'namek' } });
|
||||
|
||||
expect(searchSpy.calls.count()).toBe(2, 'Search count should be two after the site change');
|
||||
expect(searchSpy.calls.argsFor(1)).toEqual([defaultSearchOptions('vegeta', 'namek')]);
|
||||
done();
|
||||
}, 300);
|
||||
});
|
||||
|
||||
it('should show the search icon by default without the X (clear) icon', (done) => {
|
||||
fixture.detectChanges();
|
||||
setTimeout(() => {
|
||||
|
||||
let searchIcon = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-search-icon"]'));
|
||||
let clearIcon = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-search-clear"]'));
|
||||
|
||||
expect(searchIcon).not.toBeNull('Search icon should be in the DOM');
|
||||
expect(clearIcon).toBeNull('Clear icon should NOT be in the DOM');
|
||||
done();
|
||||
}, 300);
|
||||
});
|
||||
|
||||
it('should show the X (clear) icon without the search icon when the search contains at least one character', (done) => {
|
||||
fixture.detectChanges();
|
||||
typeToSearchBox('123');
|
||||
|
||||
setTimeout(() => {
|
||||
fixture.detectChanges();
|
||||
|
||||
let searchIcon = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-search-icon"]'));
|
||||
let clearIcon = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-search-clear"]'));
|
||||
|
||||
expect(searchIcon).toBeNull('Search icon should NOT be in the DOM');
|
||||
expect(clearIcon).not.toBeNull('Clear icon should be in the DOM');
|
||||
done();
|
||||
}, 300);
|
||||
});
|
||||
|
||||
it('should clear the search field, nodes and chosenNode when clicking on the X (clear) icon', () => {
|
||||
component.chosenNode = <MinimalNodeEntryEntity> {};
|
||||
component.nodes = {
|
||||
list: {
|
||||
entries: [{ entry: component.chosenNode }]
|
||||
}
|
||||
};
|
||||
component.searchTerm = 'piccolo';
|
||||
component.showingSearchResults = true;
|
||||
|
||||
component.clear();
|
||||
|
||||
expect(component.searchTerm).toBe('');
|
||||
expect(component.nodes).toEqual(null);
|
||||
expect(component.chosenNode).toBeNull();
|
||||
expect(component.showingSearchResults).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should show the current folder\'s content instead of search results if search was not performed', () => {
|
||||
let documentList = fixture.debugElement.query(By.directive(DocumentListComponent));
|
||||
expect(documentList).not.toBeNull('Document list should be shown');
|
||||
expect(documentList.componentInstance.currentFolderId).toBe('cat-girl-nuku-nuku');
|
||||
});
|
||||
|
||||
it('should pass through the rowFilter to the documentList', () => {
|
||||
const filter = () => {
|
||||
};
|
||||
component.rowFilter = filter;
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
let documentList = fixture.debugElement.query(By.directive(DocumentListComponent));
|
||||
expect(documentList).not.toBeNull('Document list should be shown');
|
||||
expect(documentList.componentInstance.rowFilter).toBe(filter);
|
||||
});
|
||||
|
||||
it('should pass through the imageResolver to the documentList', () => {
|
||||
const resolver = () => 'piccolo';
|
||||
component.imageResolver = resolver;
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
let documentList = fixture.debugElement.query(By.directive(DocumentListComponent));
|
||||
expect(documentList).not.toBeNull('Document list should be shown');
|
||||
expect(documentList.componentInstance.imageResolver).toBe(resolver);
|
||||
});
|
||||
|
||||
it('should show the result list when search was performed', (done) => {
|
||||
typeToSearchBox();
|
||||
|
||||
setTimeout(() => {
|
||||
respondWithSearchResults(ONE_FOLDER_RESULT);
|
||||
|
||||
fixture.detectChanges();
|
||||
let documentList = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-document-list"]'));
|
||||
expect(documentList).not.toBeNull('Document list should be shown');
|
||||
expect(documentList.componentInstance.currentFolderId).toBeNull();
|
||||
done();
|
||||
}, 300);
|
||||
});
|
||||
|
||||
xit('should highlight the results when search was performed in the next timeframe', (done) => {
|
||||
spyOn(component.highlighter, 'highlight');
|
||||
typeToSearchBox('shenron');
|
||||
|
||||
setTimeout(() => {
|
||||
respondWithSearchResults(ONE_FOLDER_RESULT);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.highlighter.highlight).not.toHaveBeenCalled();
|
||||
|
||||
setTimeout(() => {
|
||||
expect(component.highlighter.highlight).toHaveBeenCalledWith('shenron');
|
||||
}, 300);
|
||||
|
||||
done();
|
||||
}, 300);
|
||||
|
||||
});
|
||||
|
||||
it('should show the default text instead of result list if search was cleared', (done) => {
|
||||
typeToSearchBox();
|
||||
|
||||
setTimeout(() => {
|
||||
respondWithSearchResults(ONE_FOLDER_RESULT);
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
let clearButton = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-search-clear"]'));
|
||||
expect(clearButton).not.toBeNull('Clear button should be in DOM');
|
||||
clearButton.triggerEventHandler('click', {});
|
||||
fixture.detectChanges();
|
||||
|
||||
let documentList = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-document-list"]'));
|
||||
expect(documentList).not.toBeNull('Document list should be shown');
|
||||
expect(documentList.componentInstance.currentFolderId).toBe('cat-girl-nuku-nuku');
|
||||
done();
|
||||
});
|
||||
}, 300);
|
||||
});
|
||||
|
||||
xit('should reload the original documentlist when clearing the search input', (done) => {
|
||||
typeToSearchBox('shenron');
|
||||
|
||||
setTimeout(() => {
|
||||
respondWithSearchResults(ONE_FOLDER_RESULT);
|
||||
|
||||
typeToSearchBox('');
|
||||
fixture.detectChanges();
|
||||
|
||||
setTimeout(() => {
|
||||
let documentList = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-document-list"]'));
|
||||
expect(documentList.componentInstance.currentFolderId).toBe('cat-girl-nuku-nuku');
|
||||
}, 300);
|
||||
|
||||
done();
|
||||
}, 300);
|
||||
});
|
||||
|
||||
it('should set the folderIdToShow to the default "currentFolderId" if siteId is undefined', (done) => {
|
||||
component.siteChanged(<SiteEntry> { entry: { guid: 'Kame-Sennin Muten Roshi' } });
|
||||
fixture.detectChanges();
|
||||
|
||||
let documentList = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-document-list"]'));
|
||||
expect(documentList.componentInstance.currentFolderId).toBe('Kame-Sennin Muten Roshi');
|
||||
|
||||
component.siteChanged(<SiteEntry> { entry: { guid: undefined } });
|
||||
fixture.detectChanges();
|
||||
|
||||
documentList = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-document-list"]'));
|
||||
expect(documentList.componentInstance.currentFolderId).toBe('cat-girl-nuku-nuku');
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
describe('Pagination "Load more" button', () => {
|
||||
|
||||
it('should NOT be shown by default', () => {
|
||||
fixture.detectChanges();
|
||||
const pagination = fixture.debugElement.query(By.css('[data-automation-id="adf-infinite-pagination-button"]'));
|
||||
expect(pagination).toBeNull();
|
||||
});
|
||||
|
||||
it('should be shown when diplaying search results', (done) => {
|
||||
typeToSearchBox('shenron');
|
||||
|
||||
setTimeout(() => {
|
||||
respondWithSearchResults(ONE_FOLDER_RESULT);
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
const pagination = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-search-pagination"]'));
|
||||
expect(pagination).not.toBeNull();
|
||||
done();
|
||||
});
|
||||
}, 300);
|
||||
});
|
||||
|
||||
it('button\'s callback should load the next batch of results by calling the search api', () => {
|
||||
const skipCount = 8;
|
||||
component.searchTerm = 'kakarot';
|
||||
|
||||
component.getNextPageOfSearch({ skipCount });
|
||||
|
||||
expect(searchSpy).toHaveBeenCalledWith(defaultSearchOptions('kakarot', undefined, skipCount));
|
||||
});
|
||||
|
||||
it('should set its loading state to true after search was started', (done) => {
|
||||
component.showingSearchResults = true;
|
||||
component.pagination = { hasMoreItems: true };
|
||||
|
||||
typeToSearchBox('shenron');
|
||||
|
||||
setTimeout(() => {
|
||||
fixture.detectChanges();
|
||||
|
||||
const spinnerSelector = By.css('[data-automation-id="content-node-selector-search-pagination"] [data-automation-id="adf-infinite-pagination-spinner"]');
|
||||
const paginationLoading = fixture.debugElement.query(spinnerSelector);
|
||||
expect(paginationLoading).not.toBeNull();
|
||||
done();
|
||||
}, 300);
|
||||
});
|
||||
|
||||
it('should set its loading state to true after search was performed', (done) => {
|
||||
component.showingSearchResults = true;
|
||||
component.pagination = { hasMoreItems: true };
|
||||
|
||||
typeToSearchBox('shenron');
|
||||
fixture.detectChanges();
|
||||
|
||||
setTimeout(() => {
|
||||
respondWithSearchResults(ONE_FOLDER_RESULT);
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
const spinnerSelector = By.css('[data-automation-id="content-node-selector-search-pagination"] [data-automation-id="adf-infinite-pagination-spinner"]');
|
||||
const paginationLoading = fixture.debugElement.query(spinnerSelector);
|
||||
expect(paginationLoading).toBeNull();
|
||||
done();
|
||||
});
|
||||
}, 300);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Action button for the chosen node', () => {
|
||||
|
||||
const entry: MinimalNodeEntryEntity = <MinimalNodeEntryEntity> { list: {entries: [{}]}};
|
||||
const nodePage: NodePaging = <NodePaging> {list: {}, pagination: {}};
|
||||
let hasPermission;
|
||||
|
||||
beforeEach(() => {
|
||||
const alfrescoContentService = TestBed.get(ContentService);
|
||||
spyOn(alfrescoContentService, 'hasPermission').and.callFake(() => hasPermission);
|
||||
});
|
||||
|
||||
it('should become enabled after loading node with the necessary permissions', async(() => {
|
||||
hasPermission = true;
|
||||
component.documentList.folderNode = entry;
|
||||
|
||||
component.select.subscribe((nodes) => {
|
||||
expect(nodes).toBeDefined();
|
||||
expect(nodes).not.toBeNull();
|
||||
});
|
||||
|
||||
component.documentList.ready.emit(nodePage);
|
||||
fixture.detectChanges();
|
||||
}));
|
||||
|
||||
it('should remain disabled after loading node without the necessary permissions', () => {
|
||||
hasPermission = false;
|
||||
component.documentList.folderNode = entry;
|
||||
|
||||
component.select.subscribe((nodes) => {
|
||||
expect(nodes).toBeDefined();
|
||||
expect(nodes).not.toBeNull();
|
||||
});
|
||||
|
||||
component.documentList.ready.emit(nodePage);
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should be enabled when clicking on a node (with the right permissions) in the list (onNodeSelect)', () => {
|
||||
hasPermission = true;
|
||||
|
||||
component.select.subscribe((nodes) => {
|
||||
expect(nodes).toBeDefined();
|
||||
expect(nodes).not.toBeNull();
|
||||
});
|
||||
|
||||
component.onNodeSelect({ detail: { node: { entry } } });
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should remain disabled when clicking on a node (with the WRONG permissions) in the list (onNodeSelect)', () => {
|
||||
hasPermission = false;
|
||||
|
||||
component.select.subscribe((nodes) => {
|
||||
expect(nodes).toBeDefined();
|
||||
expect(nodes).not.toBeNull();
|
||||
});
|
||||
|
||||
component.onNodeSelect({ detail: { node: { entry } } });
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should become disabled when clicking on a node (with the WRONG permissions) after previously selecting a right node', () => {
|
||||
component.select.subscribe((nodes) => {
|
||||
expect(nodes).toBeDefined();
|
||||
expect(nodes).not.toBeNull();
|
||||
});
|
||||
|
||||
hasPermission = true;
|
||||
component.onNodeSelect({ detail: { node: { entry } } });
|
||||
fixture.detectChanges();
|
||||
|
||||
hasPermission = false;
|
||||
component.onNodeSelect({ detail: { node: { entry } } });
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should be disabled when resetting the chosen node', () => {
|
||||
hasPermission = true;
|
||||
component.onNodeSelect({ detail: { node: { entry: <MinimalNodeEntryEntity> {} } } });
|
||||
fixture.detectChanges();
|
||||
|
||||
component.select.subscribe((nodes) => {
|
||||
expect(nodes).toBeDefined();
|
||||
expect(nodes).not.toBeNull();
|
||||
});
|
||||
|
||||
component.resetChosenNode();
|
||||
fixture.detectChanges();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@@ -0,0 +1,309 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
Component,
|
||||
EventEmitter,
|
||||
Input,
|
||||
OnInit,
|
||||
Output,
|
||||
ViewChild,
|
||||
ViewEncapsulation
|
||||
} from '@angular/core';
|
||||
import {
|
||||
AlfrescoApiService,
|
||||
ContentService,
|
||||
HighlightDirective,
|
||||
UserPreferencesService
|
||||
} from '@alfresco/adf-core';
|
||||
import { FormControl } from '@angular/forms';
|
||||
import { MinimalNodeEntryEntity, NodePaging, Pagination, SiteEntry, SitePaging } from 'alfresco-js-api';
|
||||
import { DocumentListComponent, PaginationStrategy } from '../document-list/components/document-list.component';
|
||||
import { RowFilter } from '../document-list/data/row-filter.model';
|
||||
import { ImageResolver } from '../document-list/data/image-resolver.model';
|
||||
import { ContentNodeSelectorService } from './content-node-selector.service';
|
||||
import { debounceTime } from 'rxjs/operators';
|
||||
|
||||
@Component({
|
||||
selector: 'adf-content-node-selector-panel',
|
||||
styleUrls: ['./content-node-selector-panel.component.scss'],
|
||||
templateUrl: './content-node-selector-panel.component.html',
|
||||
encapsulation: ViewEncapsulation.None
|
||||
})
|
||||
export class ContentNodeSelectorPanelComponent implements OnInit {
|
||||
|
||||
nodes: NodePaging | null = null;
|
||||
siteId: null | string;
|
||||
searchTerm: string = '';
|
||||
showingSearchResults: boolean = false;
|
||||
loadingSearchResults: boolean = false;
|
||||
inDialog: boolean = false;
|
||||
_chosenNode: MinimalNodeEntryEntity = null;
|
||||
folderIdToShow: string | null = null;
|
||||
paginationStrategy: PaginationStrategy;
|
||||
pagination: Pagination;
|
||||
skipCount: number = 0;
|
||||
infiniteScroll: boolean = false;
|
||||
|
||||
@Input()
|
||||
currentFolderId: string = null;
|
||||
|
||||
@Input()
|
||||
dropdownHideMyFiles: boolean = false;
|
||||
|
||||
@Input()
|
||||
dropdownSiteList: SitePaging = null;
|
||||
|
||||
@Input()
|
||||
rowFilter: RowFilter = null;
|
||||
|
||||
@Input()
|
||||
imageResolver: ImageResolver = null;
|
||||
|
||||
@Input()
|
||||
pageSize: number;
|
||||
|
||||
@Output()
|
||||
select: EventEmitter<MinimalNodeEntryEntity[]> = new EventEmitter<MinimalNodeEntryEntity[]>();
|
||||
|
||||
@ViewChild(DocumentListComponent)
|
||||
documentList: DocumentListComponent;
|
||||
|
||||
@ViewChild(HighlightDirective)
|
||||
highlighter: HighlightDirective;
|
||||
|
||||
debounceSearch: number= 200;
|
||||
|
||||
searchInput: FormControl = new FormControl();
|
||||
|
||||
constructor(private contentNodeSelectorService: ContentNodeSelectorService,
|
||||
private contentService: ContentService,
|
||||
private apiService: AlfrescoApiService,
|
||||
private preferences: UserPreferencesService) {
|
||||
this.searchInput.valueChanges
|
||||
.pipe(
|
||||
debounceTime(this.debounceSearch)
|
||||
)
|
||||
.subscribe((searchValue) => {
|
||||
this.search(searchValue);
|
||||
});
|
||||
|
||||
this.pageSize = this.preferences.paginationSize;
|
||||
}
|
||||
|
||||
set chosenNode(value: MinimalNodeEntryEntity) {
|
||||
this._chosenNode = value;
|
||||
this.select.next([value]);
|
||||
}
|
||||
|
||||
get chosenNode() {
|
||||
return this._chosenNode;
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.folderIdToShow = this.currentFolderId;
|
||||
this.paginationStrategy = PaginationStrategy.Infinite;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the site attribute and starts a new search
|
||||
*
|
||||
* @param chosenSite SiteEntry to search within
|
||||
*/
|
||||
siteChanged(chosenSite: SiteEntry): void {
|
||||
this.siteId = chosenSite.entry.guid;
|
||||
this.updateResults();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the searchTerm attribute and starts a new search
|
||||
*
|
||||
* @param searchTerm string value to search against
|
||||
*/
|
||||
search(searchTerm: string): void {
|
||||
this.searchTerm = searchTerm;
|
||||
this.updateResults();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether breadcrumb has to be shown or not
|
||||
*/
|
||||
needBreadcrumbs() {
|
||||
const whenInFolderNavigation = !this.showingSearchResults,
|
||||
whenInSelectingSearchResult = this.showingSearchResults && this.chosenNode;
|
||||
|
||||
return whenInFolderNavigation || whenInSelectingSearchResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the actually selected|entered folder node or null in case of searching for the breadcrumb
|
||||
*/
|
||||
get breadcrumbFolderNode(): MinimalNodeEntryEntity | null {
|
||||
if (this.showingSearchResults && this.chosenNode) {
|
||||
return this.chosenNode;
|
||||
} else {
|
||||
return this.documentList.folderNode;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the search input
|
||||
*/
|
||||
clear(): void {
|
||||
this.searchTerm = '';
|
||||
this.nodes = null;
|
||||
this.skipCount = 0;
|
||||
this.chosenNode = null;
|
||||
this.showingSearchResults = false;
|
||||
this.folderIdToShow = this.currentFolderId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the result list depending on the criterias
|
||||
*/
|
||||
private updateResults(): void {
|
||||
if (this.searchTerm.length === 0) {
|
||||
this.folderIdToShow = this.siteId || this.currentFolderId;
|
||||
} else {
|
||||
this.startNewSearch();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the first page of a new search result
|
||||
*/
|
||||
private startNewSearch(): void {
|
||||
this.nodes = null;
|
||||
this.skipCount = 0;
|
||||
this.chosenNode = null;
|
||||
this.folderIdToShow = null;
|
||||
this.querySearch();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the next batch of search results
|
||||
*
|
||||
* @param event Pagination object
|
||||
*/
|
||||
getNextPageOfSearch(event: Pagination): void {
|
||||
this.infiniteScroll = true;
|
||||
this.skipCount = event.skipCount;
|
||||
this.querySearch();
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the call to searchService with the proper parameters
|
||||
*/
|
||||
private querySearch(): void {
|
||||
this.loadingSearchResults = true;
|
||||
|
||||
this.contentNodeSelectorService.search(this.searchTerm, this.siteId, this.skipCount, this.pageSize)
|
||||
.subscribe(this.showSearchResults.bind(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the results of the search
|
||||
*
|
||||
* @param results Search results
|
||||
*/
|
||||
private showSearchResults(results: NodePaging): void {
|
||||
this.showingSearchResults = true;
|
||||
this.loadingSearchResults = false;
|
||||
|
||||
// Documentlist hack, since data displaying for preloaded nodes is a little bit messy there
|
||||
if (!this.nodes) {
|
||||
this.nodes = results;
|
||||
} else {
|
||||
this.documentList.data.loadPage(results, true);
|
||||
}
|
||||
|
||||
this.pagination = results.list.pagination;
|
||||
this.highlight();
|
||||
}
|
||||
|
||||
/**
|
||||
* Hightlight the actual searchterm in the next frame
|
||||
*/
|
||||
highlight(): void {
|
||||
setTimeout(() => {
|
||||
this.highlighter.highlight(this.searchTerm);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets showingSearchResults state to be able to differentiate between search results or folder results
|
||||
*/
|
||||
onFolderChange(): void {
|
||||
this.skipCount = 0;
|
||||
this.infiniteScroll = false;
|
||||
this.showingSearchResults = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to set the currently loaded node
|
||||
*/
|
||||
onFolderLoaded(nodePage: NodePaging): void {
|
||||
this.attemptNodeSelection(this.documentList.folderNode);
|
||||
this.pagination = nodePage.list.pagination;
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects node as chosen if it has the right permission, clears the selection otherwise
|
||||
*
|
||||
* @param entry
|
||||
*/
|
||||
private attemptNodeSelection(entry: MinimalNodeEntryEntity): void {
|
||||
if (this.contentService.hasPermission(entry, 'create')) {
|
||||
this.chosenNode = entry;
|
||||
} else {
|
||||
this.resetChosenNode();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the chosen node
|
||||
*/
|
||||
resetChosenNode(): void {
|
||||
this.chosenNode = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked when user selects a node
|
||||
*
|
||||
* @param event CustomEvent for node-select
|
||||
*/
|
||||
onNodeSelect(event: any): void {
|
||||
this.attemptNodeSelection(event.detail.node.entry);
|
||||
}
|
||||
|
||||
onNodeDoubleClick(e: CustomEvent) {
|
||||
const node: any = e.detail.node.entry;
|
||||
|
||||
if (node && node.guid) {
|
||||
const options = {
|
||||
maxItems: this.pageSize,
|
||||
skipCount: this.skipCount,
|
||||
include: ['path', 'properties', 'allowableOperations']
|
||||
};
|
||||
|
||||
this.apiService.nodesApi.getNode(node.guid, options)
|
||||
.then(documentLibrary => {
|
||||
this.documentList.performCustomSourceNavigation(documentLibrary);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@@ -15,16 +15,16 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { EventEmitter } from '@angular/core';
|
||||
import { MinimalNodeEntryEntity } from 'alfresco-js-api';
|
||||
import { MinimalNodeEntryEntity, SitePaging } from 'alfresco-js-api';
|
||||
import { Subject } from 'rxjs/Subject';
|
||||
|
||||
export interface ContentNodeSelectorComponentData {
|
||||
title: string;
|
||||
actionName?: string;
|
||||
currentFolderId?: string;
|
||||
currentFolderId: string;
|
||||
dropdownHideMyFiles?: boolean;
|
||||
dropdownSiteList?: any[];
|
||||
dropdownSiteList?: SitePaging;
|
||||
rowFilter?: any;
|
||||
imageResolver?: any;
|
||||
select: EventEmitter<MinimalNodeEntryEntity[]>;
|
||||
select: Subject<MinimalNodeEntryEntity[]>;
|
||||
}
|
||||
|
@@ -1,111 +1,22 @@
|
||||
<header matDialogTitle
|
||||
class="adf-content-node-selector-title"
|
||||
data-automation-id="content-node-selector-title">{{title}}
|
||||
class="adf-content-node-selector-dialog-title"
|
||||
data-automation-id="content-node-selector-title">{{title || data?.title}}
|
||||
</header>
|
||||
|
||||
<section matDialogContent
|
||||
class="adf-content-node-selector-content"
|
||||
(node-select)="onNodeSelect($event)">
|
||||
|
||||
<mat-form-field floatPlaceholder="never" class="adf-content-node-selector-content-input">
|
||||
<input matInput
|
||||
id="searchInput"
|
||||
[formControl]="searchInput"
|
||||
type="text"
|
||||
placeholder="Search"
|
||||
[value]="searchTerm"
|
||||
data-automation-id="content-node-selector-search-input">
|
||||
|
||||
<mat-icon *ngIf="searchTerm.length > 0"
|
||||
matSuffix (click)="clear()"
|
||||
class="adf-content-node-selector-content-input-icon"
|
||||
data-automation-id="content-node-selector-search-clear">clear
|
||||
</mat-icon>
|
||||
|
||||
<mat-icon *ngIf="searchTerm.length === 0"
|
||||
matSuffix
|
||||
class="adf-content-node-selector-content-input-icon"
|
||||
data-automation-id="content-node-selector-search-icon">search
|
||||
</mat-icon>
|
||||
|
||||
</mat-form-field>
|
||||
|
||||
<adf-sites-dropdown
|
||||
(change)="siteChanged($event)"
|
||||
[placeholder]="'NODE_SELECTOR.SELECT_LOCATION'"
|
||||
[hideMyFiles]="dropdownHideMyFiles"
|
||||
[siteList]="dropdownSiteList"
|
||||
data-automation-id="content-node-selector-sites-combo"></adf-sites-dropdown>
|
||||
|
||||
<adf-toolbar>
|
||||
<adf-toolbar-title>
|
||||
<adf-dropdown-breadcrumb *ngIf="needBreadcrumbs()"
|
||||
class="adf-content-node-selector-content-breadcrumb"
|
||||
(navigate)="clear()"
|
||||
[target]="documentList"
|
||||
[folderNode]="breadcrumbFolderNode"
|
||||
data-automation-id="content-node-selector-content-breadcrumb">
|
||||
</adf-dropdown-breadcrumb>
|
||||
</adf-toolbar-title>
|
||||
</adf-toolbar>
|
||||
|
||||
<div
|
||||
class="adf-content-node-selector-content-list"
|
||||
[class.adf-content-node-selector-content-list-searchLayout]="showingSearchResults"
|
||||
data-automation-id="content-node-selector-content-list">
|
||||
<adf-document-list
|
||||
#documentList
|
||||
adf-highlight
|
||||
adf-highlight-selector="adf-name-location-cell .adf-name-location-cell-name"
|
||||
[node]="nodes"
|
||||
[maxItems]="pageSize"
|
||||
[skipCount]="skipCount"
|
||||
[enableInfiniteScrolling]="infiniteScroll"
|
||||
[rowFilter]="rowFilter"
|
||||
[imageResolver]="imageResolver"
|
||||
[currentFolderId]="folderIdToShow"
|
||||
selectionMode="single"
|
||||
[contextMenuActions]="false"
|
||||
[contentActions]="false"
|
||||
[allowDropFiles]="false"
|
||||
(folderChange)="onFolderChange()"
|
||||
(ready)="onFolderLoaded($event)"
|
||||
(node-dblclick)="onNodeDoubleClick($event)"
|
||||
data-automation-id="content-node-selector-document-list">
|
||||
<empty-folder-content>
|
||||
<ng-template>
|
||||
<div>{{ 'NODE_SELECTOR.NO_RESULTS' | translate }}</div>
|
||||
</ng-template>
|
||||
</empty-folder-content>
|
||||
|
||||
<data-columns>
|
||||
<data-column key="$thumbnail" type="image"></data-column>
|
||||
<data-column key="name" type="text" class="full-width ellipsis-cell">
|
||||
<ng-template let-context="$implicit">
|
||||
<adf-name-location-cell [data]="context.data" [column]="context.col" [row]="context.row"></adf-name-location-cell>
|
||||
</ng-template>
|
||||
</data-column>
|
||||
<data-column key="modifiedAt" type="date" format="timeAgo" class="adf-content-selector-modified-cell"></data-column>
|
||||
<data-column key="modifiedByUser.displayName" type="text" class="adf-content-selector-modifier-cell"></data-column>
|
||||
</data-columns>
|
||||
|
||||
</adf-document-list>
|
||||
|
||||
<adf-infinite-pagination
|
||||
[pagination]="pagination"
|
||||
[pageSize]="pageSize"
|
||||
[loading]="loadingSearchResults"
|
||||
(loadMore)="getNextPageOfSearch($event)"
|
||||
data-automation-id="content-node-selector-search-pagination">
|
||||
{{ 'ADF-DOCUMENT-LIST.LAYOUT.LOAD_MORE' | translate }}
|
||||
</adf-infinite-pagination>
|
||||
</div>
|
||||
|
||||
class="adf-content-node-selector-dialog-content">
|
||||
<adf-content-node-selector-panel
|
||||
[currentFolderId]="currentFolderId || data?.currentFolderId"
|
||||
[dropdownHideMyFiles]="dropdownHideMyFiles || data?.dropdownHideMyFiles"
|
||||
[dropdownSiteList]="dropdownSiteList || data?.dropdownSiteList"
|
||||
[rowFilter]="rowFilter || data?.rowFilter"
|
||||
[imageResolver]="imageResolver || data?.imageResolver"
|
||||
(select)="onSelect($event)">
|
||||
</adf-content-node-selector-panel>
|
||||
</section>
|
||||
|
||||
<footer matDialogActions class="adf-content-node-selector-actions">
|
||||
|
||||
<button *ngIf="inDialog"
|
||||
<button
|
||||
mat-button
|
||||
class="adf-content-node-selector-actions-cancel"
|
||||
(click)="close()"
|
||||
@@ -115,8 +26,9 @@
|
||||
<button mat-button
|
||||
[disabled]="!chosenNode"
|
||||
class="adf-content-node-selector-actions-choose"
|
||||
(click)="choose()"
|
||||
(click)="onClick()"
|
||||
data-automation-id="content-node-selector-actions-choose">{{ buttonActionName | translate }}
|
||||
</button>
|
||||
|
||||
</footer>
|
||||
|
||||
|
@@ -1,207 +1,56 @@
|
||||
@mixin adf-content-node-selector-theme($theme) {
|
||||
@mixin adf-content-node-selector-dialog-theme($theme) {
|
||||
$primary: map-get($theme, primary);
|
||||
$foreground: map-get($theme, foreground);
|
||||
$background: map-get($theme, background);
|
||||
|
||||
.adf-content-node-selector-dialog {
|
||||
.mat-dialog-container {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.mat-dialog-container {
|
||||
padding: 0;
|
||||
.adf-content-node-selector-dialog {
|
||||
&-title,
|
||||
&-content,
|
||||
&-actions {
|
||||
padding: 16px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.adf-content-node-selector {
|
||||
&-title,
|
||||
&-content,
|
||||
&-actions {
|
||||
padding: 16px;
|
||||
margin: 0;
|
||||
}
|
||||
&-content{
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
&-title::first-letter {
|
||||
&-title::first-letter {
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
&-actions {
|
||||
padding: 8px;
|
||||
background-color: mat-color($background, background);
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
color: mat-color($foreground, secondary-text);
|
||||
|
||||
button {
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
&-content {
|
||||
padding-top: 0;
|
||||
|
||||
&-input {
|
||||
width: 100%;
|
||||
|
||||
&-icon {
|
||||
color: mat-color($foreground, disabled-button);
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
color: mat-color($foreground, base);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.mat-input-underline .mat-input-ripple {
|
||||
height: 1px;
|
||||
transition: none;
|
||||
}
|
||||
|
||||
.adf-site-dropdown-container {
|
||||
.mat-form-field {
|
||||
display: block;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
.adf-site-dropdown-list-element {
|
||||
width: 100%;
|
||||
margin-bottom: 0;
|
||||
|
||||
.mat-select-trigger {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.adf-toolbar .mat-toolbar {
|
||||
border-bottom-width: 0;
|
||||
font-size: 14px;
|
||||
|
||||
&.mat-toolbar-single-row {
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
|
||||
&-breadcrumb {
|
||||
.adf-dropdown-breadcumb-trigger {
|
||||
outline: none;
|
||||
.mat-icon {
|
||||
color: mat-color($foreground, base, 0.45);
|
||||
|
||||
&:hover {
|
||||
color: mat-color($foreground, base, 0.65);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.adf-dropddown-breadcrumb-item-chevron {
|
||||
color: mat-color($foreground, base, 0.45);
|
||||
}
|
||||
}
|
||||
|
||||
&-list {
|
||||
height: 200px;
|
||||
overflow: auto;
|
||||
border: 1px solid mat-color($foreground, base, 0.07);
|
||||
|
||||
.adf-highlight {
|
||||
color: mat-color($primary);
|
||||
}
|
||||
|
||||
.adf-data-table {
|
||||
border: none;
|
||||
|
||||
.adf-no-content-container {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
thead {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.adf-data-table-cell {
|
||||
padding-top: 8px;
|
||||
padding-bottom: 8px;
|
||||
height: 30px;
|
||||
|
||||
& .adf-name-location-cell-location {
|
||||
display: none;
|
||||
}
|
||||
|
||||
& .adf-name-location-cell-name {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
&--image {
|
||||
padding-left: 16px;
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
&--text {
|
||||
padding-left: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
tbody tr {
|
||||
height: auto !important;
|
||||
|
||||
&:first-child {
|
||||
.adf-data-table-cell {
|
||||
border-top: none;
|
||||
}
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
.adf-data-table-cell {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-searchLayout {
|
||||
|
||||
.adf-data-table {
|
||||
.adf-data-table-cell {
|
||||
height: 56px;
|
||||
padding-bottom: 24px;
|
||||
|
||||
& .adf-name-location-cell-location {
|
||||
display: block
|
||||
}
|
||||
|
||||
& .adf-name-location-cell-name {
|
||||
padding: 18px 0 2px 0;
|
||||
}
|
||||
|
||||
&.adf-content-selector-modified-cell {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&.adf-content-selector-modifier-cell {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
&:last-child {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
&-actions {
|
||||
padding: 8px;
|
||||
background-color: mat-color($background, background);
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
color: mat-color($foreground, secondary-text);
|
||||
&-cancel {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
button {
|
||||
text-transform: uppercase;
|
||||
&-choose {
|
||||
font-weight: normal;
|
||||
|
||||
&[disabled] {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
&-cancel {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
&-choose {
|
||||
font-weight: normal;
|
||||
|
||||
&[disabled] {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
&:enabled {
|
||||
color: mat-color($primary);
|
||||
}
|
||||
&:enabled {
|
||||
color: mat-color($primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -15,92 +15,48 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { CUSTOM_ELEMENTS_SCHEMA, EventEmitter } from '@angular/core';
|
||||
import { MAT_DIALOG_DATA } from '@angular/material';
|
||||
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||
import { EventEmitter } from '@angular/core';
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { MinimalNodeEntryEntity } from 'alfresco-js-api';
|
||||
import {
|
||||
AlfrescoApiService,
|
||||
ContentService,
|
||||
TranslationService,
|
||||
SearchService,
|
||||
SiteModel,
|
||||
SitesService,
|
||||
UserPreferencesService
|
||||
} from '@alfresco/adf-core';
|
||||
import { DataTableModule } from '@alfresco/adf-core';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { Observer } from 'rxjs/Observer';
|
||||
import { MaterialModule } from '../material.module';
|
||||
import { EmptyFolderContentDirective, DocumentListComponent, DocumentListService } from '../document-list';
|
||||
import { DropdownSitesComponent } from '../site-dropdown';
|
||||
import { DropdownBreadcrumbComponent } from '../breadcrumb';
|
||||
import { ContentNodeSelectorComponent } from './content-node-selector.component';
|
||||
import { ContentNodeSelectorPanelComponent } from './content-node-selector-panel.component';
|
||||
import { ContentNodeSelectorService } from './content-node-selector.service';
|
||||
import { NodePaging } from 'alfresco-js-api';
|
||||
import { MinimalNodeEntryEntity } from 'alfresco-js-api';
|
||||
import { MaterialModule } from '../material.module';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import {
|
||||
EmptyFolderContentDirective,
|
||||
DocumentListComponent,
|
||||
DocumentListService
|
||||
} from '../document-list';
|
||||
import { AlfrescoApiService, ContentService } from '@alfresco/adf-core';
|
||||
|
||||
const ONE_FOLDER_RESULT = {
|
||||
list: {
|
||||
entries: [
|
||||
{
|
||||
entry: {
|
||||
id: '123', name: 'MyFolder', isFile: false, isFolder: true,
|
||||
createdByUser: { displayName: 'John Doe' },
|
||||
modifiedByUser: { displayName: 'John Doe' }
|
||||
}
|
||||
}
|
||||
],
|
||||
pagination: {
|
||||
hasMoreItems: true
|
||||
}
|
||||
}
|
||||
};
|
||||
describe('ContentNodeSelectorDialogComponent', () => {
|
||||
|
||||
describe('ContentNodeSelectorComponent', () => {
|
||||
let component: ContentNodeSelectorComponent;
|
||||
let fixture: ComponentFixture<ContentNodeSelectorComponent>;
|
||||
let data: any;
|
||||
let searchService: SearchService;
|
||||
let searchSpy: jasmine.Spy;
|
||||
let apiService: AlfrescoApiService;
|
||||
let nodesApi;
|
||||
|
||||
let _observer: Observer<NodePaging>;
|
||||
|
||||
function typeToSearchBox(searchTerm = 'string-to-search') {
|
||||
let searchInput = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-search-input"]'));
|
||||
searchInput.nativeElement.value = searchTerm;
|
||||
component.searchInput.setValue(searchTerm);
|
||||
fixture.detectChanges();
|
||||
}
|
||||
|
||||
function respondWithSearchResults(result) {
|
||||
_observer.next(result);
|
||||
}
|
||||
|
||||
function setupTestbed(plusProviders) {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
DataTableModule,
|
||||
MaterialModule
|
||||
],
|
||||
declarations: [
|
||||
ContentNodeSelectorComponent,
|
||||
ContentNodeSelectorPanelComponent,
|
||||
DocumentListComponent,
|
||||
EmptyFolderContentDirective,
|
||||
DropdownSitesComponent,
|
||||
DropdownBreadcrumbComponent,
|
||||
ContentNodeSelectorComponent
|
||||
EmptyFolderContentDirective
|
||||
],
|
||||
providers: [
|
||||
ContentNodeSelectorService,
|
||||
ContentNodeSelectorPanelComponent,
|
||||
DocumentListService,
|
||||
AlfrescoApiService,
|
||||
ContentService,
|
||||
SearchService,
|
||||
TranslationService,
|
||||
DocumentListService,
|
||||
SitesService,
|
||||
ContentNodeSelectorService,
|
||||
UserPreferencesService,
|
||||
...plusProviders
|
||||
],
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
||||
@@ -112,685 +68,100 @@ describe('ContentNodeSelectorComponent', () => {
|
||||
TestBed.resetTestingModule();
|
||||
});
|
||||
|
||||
describe('Dialog features', () => {
|
||||
beforeEach(async(() => {
|
||||
data = {
|
||||
title: 'Move along citizen...',
|
||||
actionName: 'move',
|
||||
select: new EventEmitter<MinimalNodeEntryEntity>(),
|
||||
rowFilter: () => {
|
||||
},
|
||||
imageResolver: () => 'piccolo',
|
||||
currentFolderId: 'cat-girl-nuku-nuku'
|
||||
};
|
||||
|
||||
beforeEach(async(() => {
|
||||
data = {
|
||||
title: 'Move along citizen...',
|
||||
actionName: 'move',
|
||||
select: new EventEmitter<MinimalNodeEntryEntity>(),
|
||||
rowFilter: () => {
|
||||
},
|
||||
imageResolver: () => 'piccolo',
|
||||
currentFolderId: 'cat-girl-nuku-nuku'
|
||||
};
|
||||
setupTestbed([{ provide: MAT_DIALOG_DATA, useValue: data }]);
|
||||
TestBed.compileComponents();
|
||||
}));
|
||||
|
||||
setupTestbed([{ provide: MAT_DIALOG_DATA, useValue: data }]);
|
||||
TestBed.compileComponents();
|
||||
}));
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ContentNodeSelectorComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ContentNodeSelectorComponent);
|
||||
component = fixture.componentInstance;
|
||||
describe('Data injecting with the "Material dialog way"', () => {
|
||||
|
||||
it('should show the INJECTED title', () => {
|
||||
const titleElement = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-title"]'));
|
||||
expect(titleElement).not.toBeNull();
|
||||
expect(titleElement.nativeElement.innerText).toBe('Move along citizen...');
|
||||
});
|
||||
|
||||
it('should have the INJECTED actionName on the name of the choose button', () => {
|
||||
const actionButton = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-actions-choose"]'));
|
||||
expect(actionButton).not.toBeNull();
|
||||
expect(actionButton.nativeElement.innerText).toBe('NODE_SELECTOR.MOVE');
|
||||
});
|
||||
|
||||
it('should pass through the injected currentFolderId to the documentlist', () => {
|
||||
let documentList = fixture.debugElement.query(By.directive(DocumentListComponent));
|
||||
expect(documentList).not.toBeNull('Document list should be shown');
|
||||
expect(documentList.componentInstance.currentFolderId).toBe('cat-girl-nuku-nuku');
|
||||
});
|
||||
|
||||
it('should pass through the injected rowFilter to the documentlist', () => {
|
||||
let documentList = fixture.debugElement.query(By.directive(DocumentListComponent));
|
||||
expect(documentList).not.toBeNull('Document list should be shown');
|
||||
expect(documentList.componentInstance.rowFilter).toBe(data.rowFilter);
|
||||
});
|
||||
|
||||
it('should pass through the injected imageResolver to the documentlist', () => {
|
||||
let documentList = fixture.debugElement.query(By.directive(DocumentListComponent));
|
||||
expect(documentList).not.toBeNull('Document list should be shown');
|
||||
expect(documentList.componentInstance.imageResolver).toBe(data.imageResolver);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('Cancel button', () => {
|
||||
|
||||
it('should complete the data stream when user click "CANCEL"', () => {
|
||||
let cancelButton;
|
||||
data.select.subscribe(
|
||||
() => { },
|
||||
() => { },
|
||||
() => {
|
||||
cancelButton = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-actions-cancel"]'));
|
||||
expect(cancelButton).not.toBeNull();
|
||||
});
|
||||
|
||||
cancelButton = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-actions-cancel"]'));
|
||||
cancelButton.triggerEventHandler('click', {});
|
||||
});
|
||||
|
||||
it('should not be shown if dialogRef is NOT injected', () => {
|
||||
const closeButton = fixture.debugElement.query(By.css('[content-node-selector-actions-cancel]'));
|
||||
expect(closeButton).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Action button for the chosen node', () => {
|
||||
|
||||
it('should be disabled by default', () => {
|
||||
fixture.detectChanges();
|
||||
|
||||
let actionButton = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-actions-choose"]'));
|
||||
expect(actionButton.nativeElement.disabled).toBeTruthy();
|
||||
});
|
||||
|
||||
describe('Data injecting with the "Material dialog way"', () => {
|
||||
it('should be enabled when a node is chosen', () => {
|
||||
component.onSelect([{ id: 'fake' }]);
|
||||
fixture.detectChanges();
|
||||
|
||||
it('should show the INJECTED title', () => {
|
||||
const titleElement = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-title"]'));
|
||||
expect(titleElement).not.toBeNull();
|
||||
expect(titleElement.nativeElement.innerText).toBe('Move along citizen...');
|
||||
});
|
||||
|
||||
it('should have the INJECTED actionName on the name of the choose button', () => {
|
||||
const actionButton = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-actions-choose"]'));
|
||||
expect(actionButton).not.toBeNull();
|
||||
expect(actionButton.nativeElement.innerText).toBe('NODE_SELECTOR.MOVE');
|
||||
});
|
||||
|
||||
it('should pass through the injected currentFolderId to the documentlist', () => {
|
||||
let documentList = fixture.debugElement.query(By.directive(DocumentListComponent));
|
||||
expect(documentList).not.toBeNull('Document list should be shown');
|
||||
expect(documentList.componentInstance.currentFolderId).toBe('cat-girl-nuku-nuku');
|
||||
});
|
||||
|
||||
it('should pass through the injected rowFilter to the documentlist', () => {
|
||||
let documentList = fixture.debugElement.query(By.directive(DocumentListComponent));
|
||||
expect(documentList).not.toBeNull('Document list should be shown');
|
||||
expect(documentList.componentInstance.rowFilter).toBe(data.rowFilter);
|
||||
});
|
||||
|
||||
it('should pass through the injected imageResolver to the documentlist', () => {
|
||||
let documentList = fixture.debugElement.query(By.directive(DocumentListComponent));
|
||||
expect(documentList).not.toBeNull('Document list should be shown');
|
||||
expect(documentList.componentInstance.imageResolver).toBe(data.imageResolver);
|
||||
});
|
||||
|
||||
it('should trigger the INJECTED select event when selection has been made', (done) => {
|
||||
const expectedNode = <MinimalNodeEntryEntity> {};
|
||||
data.select.subscribe((nodes) => {
|
||||
expect(nodes.length).toBe(1);
|
||||
expect(nodes[0]).toBe(expectedNode);
|
||||
done();
|
||||
});
|
||||
|
||||
component.chosenNode = expectedNode;
|
||||
component.choose();
|
||||
});
|
||||
let actionButton = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-actions-choose"]'));
|
||||
expect(actionButton.nativeElement.disabled).toBeFalsy();
|
||||
});
|
||||
|
||||
describe('Cancel button', () => {
|
||||
|
||||
let dummyMdDialogRef;
|
||||
let fakePreference: UserPreferencesService = <UserPreferencesService> jasmine.createSpyObj('UserPreferencesService', ['paginationSize']);
|
||||
fakePreference.paginationSize = 10;
|
||||
|
||||
beforeEach(() => {
|
||||
dummyMdDialogRef = <MatDialogRef<ContentNodeSelectorComponent>> {
|
||||
close: () => {
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
it('should be shown if dialogRef is injected', () => {
|
||||
const componentInstance = new ContentNodeSelectorComponent(null, null, null, fakePreference, data, dummyMdDialogRef);
|
||||
expect(componentInstance.inDialog).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should should call the close method in the injected dialogRef', () => {
|
||||
spyOn(dummyMdDialogRef, 'close');
|
||||
const componentInstance = new ContentNodeSelectorComponent(null, null, null, fakePreference, data, dummyMdDialogRef);
|
||||
|
||||
componentInstance.close();
|
||||
|
||||
expect(dummyMdDialogRef.close).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('General component features', () => {
|
||||
|
||||
beforeEach(async(() => {
|
||||
setupTestbed([]);
|
||||
TestBed.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ContentNodeSelectorComponent);
|
||||
component = fixture.componentInstance;
|
||||
component.debounceSearch = 0;
|
||||
|
||||
searchService = TestBed.get(SearchService);
|
||||
searchSpy = spyOn(searchService, 'search').and.callFake(() => {
|
||||
return Observable.create((observer: Observer<NodePaging>) => {
|
||||
_observer = observer;
|
||||
});
|
||||
});
|
||||
|
||||
apiService = TestBed.get(AlfrescoApiService);
|
||||
nodesApi = apiService.nodesApi;
|
||||
|
||||
});
|
||||
|
||||
describe('Parameters', () => {
|
||||
|
||||
it('should show the title', () => {
|
||||
component.title = 'Move along citizen...';
|
||||
fixture.detectChanges();
|
||||
|
||||
const titleElement = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-title"]'));
|
||||
expect(titleElement).not.toBeNull();
|
||||
expect(titleElement.nativeElement.innerText).toBe('Move along citizen...');
|
||||
});
|
||||
|
||||
it('should trigger the select event when selection has been made', (done) => {
|
||||
const expectedNode = <MinimalNodeEntryEntity> {};
|
||||
component.select.subscribe((nodes) => {
|
||||
expect(nodes.length).toBe(1);
|
||||
expect(nodes[0]).toBe(expectedNode);
|
||||
done();
|
||||
});
|
||||
|
||||
component.chosenNode = expectedNode;
|
||||
component.choose();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Breadcrumbs', () => {
|
||||
|
||||
let documentListService,
|
||||
sitesService,
|
||||
expectedDefaultFolderNode;
|
||||
|
||||
beforeEach(() => {
|
||||
expectedDefaultFolderNode = <MinimalNodeEntryEntity> { path: { elements: [] } };
|
||||
documentListService = TestBed.get(DocumentListService);
|
||||
sitesService = TestBed.get(SitesService);
|
||||
spyOn(documentListService, 'getFolderNode').and.returnValue(Promise.resolve(expectedDefaultFolderNode));
|
||||
spyOn(documentListService, 'getFolder').and.returnValue(Observable.throw('No results for test'));
|
||||
spyOn(sitesService, 'getSites').and.returnValue(Observable.of([]));
|
||||
spyOn(component.documentList, 'loadFolderNodesByFolderNodeId').and.returnValue(Promise.resolve());
|
||||
component.currentFolderId = 'cat-girl-nuku-nuku';
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should show the breadcrumb for the currentFolderId by default', (done) => {
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
const breadcrumb = fixture.debugElement.query(By.directive(DropdownBreadcrumbComponent));
|
||||
expect(breadcrumb).not.toBeNull();
|
||||
expect(breadcrumb.componentInstance.folderNode).toBe(expectedDefaultFolderNode);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not show the breadcrumb if search was performed as last action', (done) => {
|
||||
typeToSearchBox();
|
||||
fixture.detectChanges();
|
||||
|
||||
setTimeout(() => {
|
||||
respondWithSearchResults(ONE_FOLDER_RESULT);
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
const breadcrumb = fixture.debugElement.query(By.directive(DropdownBreadcrumbComponent));
|
||||
expect(breadcrumb).toBeNull();
|
||||
done();
|
||||
});
|
||||
}, 300);
|
||||
|
||||
});
|
||||
|
||||
it('should show the breadcrumb again on folder navigation in the results list', (done) => {
|
||||
typeToSearchBox();
|
||||
fixture.detectChanges();
|
||||
|
||||
setTimeout(() => {
|
||||
respondWithSearchResults(ONE_FOLDER_RESULT);
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
component.onFolderChange();
|
||||
fixture.detectChanges();
|
||||
const breadcrumb = fixture.debugElement.query(By.directive(DropdownBreadcrumbComponent));
|
||||
expect(breadcrumb).not.toBeNull();
|
||||
done();
|
||||
});
|
||||
}, 300);
|
||||
|
||||
});
|
||||
|
||||
it('should show the breadcrumb for the selected node when search results are displayed', (done) => {
|
||||
const alfrescoContentService = TestBed.get(ContentService);
|
||||
spyOn(alfrescoContentService, 'hasPermission').and.returnValue(true);
|
||||
|
||||
typeToSearchBox();
|
||||
|
||||
setTimeout(() => {
|
||||
respondWithSearchResults(ONE_FOLDER_RESULT);
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
|
||||
const chosenNode = <MinimalNodeEntryEntity> { path: { elements: ['one'] } };
|
||||
component.onNodeSelect({ detail: { node: { entry: chosenNode } } });
|
||||
fixture.detectChanges();
|
||||
|
||||
const breadcrumb = fixture.debugElement.query(By.directive(DropdownBreadcrumbComponent));
|
||||
expect(breadcrumb).not.toBeNull();
|
||||
expect(breadcrumb.componentInstance.folderNode.path).toBe(chosenNode.path);
|
||||
done();
|
||||
});
|
||||
}, 300);
|
||||
});
|
||||
|
||||
it('should NOT show the breadcrumb for the selected node when not on search results list', (done) => {
|
||||
const alfrescoContentService = TestBed.get(ContentService);
|
||||
spyOn(alfrescoContentService, 'hasPermission').and.returnValue(true);
|
||||
|
||||
typeToSearchBox();
|
||||
|
||||
setTimeout(() => {
|
||||
respondWithSearchResults(ONE_FOLDER_RESULT);
|
||||
|
||||
fixture.detectChanges();
|
||||
component.onFolderChange();
|
||||
fixture.detectChanges();
|
||||
|
||||
const chosenNode = <MinimalNodeEntryEntity> { path: { elements: [] } };
|
||||
component.onNodeSelect({ detail: { node: { entry: chosenNode } } });
|
||||
fixture.detectChanges();
|
||||
|
||||
const breadcrumb = fixture.debugElement.query(By.directive(DropdownBreadcrumbComponent));
|
||||
expect(breadcrumb).not.toBeNull();
|
||||
expect(breadcrumb.componentInstance.folderNode).toBe(expectedDefaultFolderNode);
|
||||
done();
|
||||
}, 300);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Search functionality', () => {
|
||||
|
||||
function defaultSearchOptions(searchTerm, rootNodeId = undefined, skipCount = 0) {
|
||||
|
||||
const parentFiltering = rootNodeId ? [ { query: `ANCESTOR:'workspace://SpacesStore/${rootNodeId}'` } ] : [];
|
||||
|
||||
let defaultSearchNode: any = {
|
||||
query: {
|
||||
query: searchTerm ? `${searchTerm}* OR name:${searchTerm}*` : searchTerm
|
||||
},
|
||||
include: ['path', 'allowableOperations'],
|
||||
paging: {
|
||||
maxItems: '25',
|
||||
skipCount: skipCount.toString()
|
||||
},
|
||||
filterQueries: [
|
||||
{ query: "TYPE:'cm:folder'" },
|
||||
{ query: 'NOT cm:creator:System' },
|
||||
...parentFiltering
|
||||
],
|
||||
scope: {
|
||||
locations: ['nodes']
|
||||
}
|
||||
};
|
||||
|
||||
return defaultSearchNode;
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
const documentListService = TestBed.get(DocumentListService);
|
||||
const expectedDefaultFolderNode = <MinimalNodeEntryEntity> { path: { elements: [] } };
|
||||
|
||||
spyOn(documentListService, 'getFolderNode').and.returnValue(Promise.resolve(expectedDefaultFolderNode));
|
||||
spyOn(component.documentList, 'loadFolderNodesByFolderNodeId').and.returnValue(Promise.resolve());
|
||||
|
||||
component.currentFolderId = 'cat-girl-nuku-nuku';
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should load the results by calling the search api on search change', (done) => {
|
||||
typeToSearchBox('kakarot');
|
||||
|
||||
setTimeout(() => {
|
||||
expect(searchSpy).toHaveBeenCalledWith(defaultSearchOptions('kakarot'));
|
||||
done();
|
||||
}, 300);
|
||||
});
|
||||
|
||||
it('should reset the currently chosen node in case of starting a new search', (done) => {
|
||||
component.chosenNode = <MinimalNodeEntryEntity> {};
|
||||
typeToSearchBox('kakarot');
|
||||
|
||||
setTimeout(() => {
|
||||
expect(component.chosenNode).toBeNull();
|
||||
done();
|
||||
}, 300);
|
||||
});
|
||||
|
||||
it('should call the search api on changing the site selectbox\'s value', (done) => {
|
||||
typeToSearchBox('vegeta');
|
||||
|
||||
setTimeout(() => {
|
||||
expect(searchSpy.calls.count()).toBe(1, 'Search count should be one after only one search');
|
||||
|
||||
component.siteChanged(<SiteModel> { guid: 'namek' });
|
||||
|
||||
expect(searchSpy.calls.count()).toBe(2, 'Search count should be two after the site change');
|
||||
expect(searchSpy.calls.argsFor(1)).toEqual([defaultSearchOptions('vegeta', 'namek')]);
|
||||
done();
|
||||
}, 300);
|
||||
});
|
||||
|
||||
it('should show the search icon by default without the X (clear) icon', (done) => {
|
||||
fixture.detectChanges();
|
||||
setTimeout(() => {
|
||||
|
||||
let searchIcon = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-search-icon"]'));
|
||||
let clearIcon = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-search-clear"]'));
|
||||
|
||||
expect(searchIcon).not.toBeNull('Search icon should be in the DOM');
|
||||
expect(clearIcon).toBeNull('Clear icon should NOT be in the DOM');
|
||||
done();
|
||||
}, 300);
|
||||
});
|
||||
|
||||
it('should show the X (clear) icon without the search icon when the search contains at least one character', (done) => {
|
||||
fixture.detectChanges();
|
||||
typeToSearchBox('123');
|
||||
|
||||
setTimeout(() => {
|
||||
fixture.detectChanges();
|
||||
|
||||
let searchIcon = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-search-icon"]'));
|
||||
let clearIcon = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-search-clear"]'));
|
||||
|
||||
expect(searchIcon).toBeNull('Search icon should NOT be in the DOM');
|
||||
expect(clearIcon).not.toBeNull('Clear icon should be in the DOM');
|
||||
done();
|
||||
}, 300);
|
||||
});
|
||||
|
||||
it('should clear the search field, nodes and chosenNode when clicking on the X (clear) icon', () => {
|
||||
component.chosenNode = <MinimalNodeEntryEntity> {};
|
||||
component.nodes = {
|
||||
list: {
|
||||
entries: [{ entry: component.chosenNode }]
|
||||
}
|
||||
};
|
||||
component.searchTerm = 'piccolo';
|
||||
component.showingSearchResults = true;
|
||||
|
||||
component.clear();
|
||||
|
||||
expect(component.searchTerm).toBe('');
|
||||
expect(component.nodes).toEqual(null);
|
||||
expect(component.chosenNode).toBeNull();
|
||||
expect(component.showingSearchResults).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should show the current folder\'s content instead of search results if search was not performed', () => {
|
||||
let documentList = fixture.debugElement.query(By.directive(DocumentListComponent));
|
||||
expect(documentList).not.toBeNull('Document list should be shown');
|
||||
expect(documentList.componentInstance.currentFolderId).toBe('cat-girl-nuku-nuku');
|
||||
});
|
||||
|
||||
it('should pass through the rowFilter to the documentList', () => {
|
||||
const filter = () => {
|
||||
};
|
||||
component.rowFilter = filter;
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
let documentList = fixture.debugElement.query(By.directive(DocumentListComponent));
|
||||
expect(documentList).not.toBeNull('Document list should be shown');
|
||||
expect(documentList.componentInstance.rowFilter).toBe(filter);
|
||||
});
|
||||
|
||||
it('should pass through the imageResolver to the documentList', () => {
|
||||
const resolver = () => 'piccolo';
|
||||
component.imageResolver = resolver;
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
let documentList = fixture.debugElement.query(By.directive(DocumentListComponent));
|
||||
expect(documentList).not.toBeNull('Document list should be shown');
|
||||
expect(documentList.componentInstance.imageResolver).toBe(resolver);
|
||||
});
|
||||
|
||||
it('should show the result list when search was performed', (done) => {
|
||||
typeToSearchBox();
|
||||
|
||||
setTimeout(() => {
|
||||
respondWithSearchResults(ONE_FOLDER_RESULT);
|
||||
|
||||
fixture.detectChanges();
|
||||
let documentList = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-document-list"]'));
|
||||
expect(documentList).not.toBeNull('Document list should be shown');
|
||||
expect(documentList.componentInstance.currentFolderId).toBeNull();
|
||||
done();
|
||||
}, 300);
|
||||
});
|
||||
|
||||
xit('should highlight the results when search was performed in the next timeframe', (done) => {
|
||||
spyOn(component.highlighter, 'highlight');
|
||||
typeToSearchBox('shenron');
|
||||
|
||||
setTimeout(() => {
|
||||
respondWithSearchResults(ONE_FOLDER_RESULT);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.highlighter.highlight).not.toHaveBeenCalled();
|
||||
|
||||
setTimeout(() => {
|
||||
expect(component.highlighter.highlight).toHaveBeenCalledWith('shenron');
|
||||
}, 300);
|
||||
|
||||
done();
|
||||
}, 300);
|
||||
|
||||
});
|
||||
|
||||
it('should show the default text instead of result list if search was cleared', (done) => {
|
||||
typeToSearchBox();
|
||||
|
||||
setTimeout(() => {
|
||||
respondWithSearchResults(ONE_FOLDER_RESULT);
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
let clearButton = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-search-clear"]'));
|
||||
expect(clearButton).not.toBeNull('Clear button should be in DOM');
|
||||
clearButton.triggerEventHandler('click', {});
|
||||
fixture.detectChanges();
|
||||
|
||||
let documentList = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-document-list"]'));
|
||||
expect(documentList).not.toBeNull('Document list should be shown');
|
||||
expect(documentList.componentInstance.currentFolderId).toBe('cat-girl-nuku-nuku');
|
||||
done();
|
||||
});
|
||||
}, 300);
|
||||
});
|
||||
|
||||
xit('should reload the original documentlist when clearing the search input', (done) => {
|
||||
typeToSearchBox('shenron');
|
||||
|
||||
setTimeout(() => {
|
||||
respondWithSearchResults(ONE_FOLDER_RESULT);
|
||||
|
||||
typeToSearchBox('');
|
||||
fixture.detectChanges();
|
||||
|
||||
setTimeout(() => {
|
||||
let documentList = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-document-list"]'));
|
||||
expect(documentList.componentInstance.currentFolderId).toBe('cat-girl-nuku-nuku');
|
||||
}, 300);
|
||||
|
||||
done();
|
||||
}, 300);
|
||||
});
|
||||
|
||||
it('should set the folderIdToShow to the default "currentFolderId" if siteId is undefined', (done) => {
|
||||
component.siteChanged(<SiteModel> { guid: 'Kame-Sennin Muten Roshi' });
|
||||
fixture.detectChanges();
|
||||
|
||||
let documentList = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-document-list"]'));
|
||||
expect(documentList.componentInstance.currentFolderId).toBe('Kame-Sennin Muten Roshi');
|
||||
|
||||
component.siteChanged(<SiteModel> { guid: undefined });
|
||||
fixture.detectChanges();
|
||||
|
||||
documentList = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-document-list"]'));
|
||||
expect(documentList.componentInstance.currentFolderId).toBe('cat-girl-nuku-nuku');
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
describe('Pagination "Load more" button', () => {
|
||||
|
||||
it('should NOT be shown by default', () => {
|
||||
fixture.detectChanges();
|
||||
const pagination = fixture.debugElement.query(By.css('[data-automation-id="adf-infinite-pagination-button"]'));
|
||||
expect(pagination).toBeNull();
|
||||
});
|
||||
|
||||
it('should be shown when diplaying search results', (done) => {
|
||||
typeToSearchBox('shenron');
|
||||
|
||||
setTimeout(() => {
|
||||
respondWithSearchResults(ONE_FOLDER_RESULT);
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
const pagination = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-search-pagination"]'));
|
||||
expect(pagination).not.toBeNull();
|
||||
done();
|
||||
});
|
||||
}, 300);
|
||||
});
|
||||
|
||||
it('button\'s callback should load the next batch of results by calling the search api', () => {
|
||||
const skipCount = 8;
|
||||
component.searchTerm = 'kakarot';
|
||||
|
||||
component.getNextPageOfSearch({ skipCount });
|
||||
|
||||
expect(searchSpy).toHaveBeenCalledWith(defaultSearchOptions('kakarot', undefined, skipCount));
|
||||
});
|
||||
|
||||
it('should set its loading state to true after search was started', (done) => {
|
||||
component.showingSearchResults = true;
|
||||
component.pagination = { hasMoreItems: true };
|
||||
|
||||
typeToSearchBox('shenron');
|
||||
|
||||
setTimeout(() => {
|
||||
fixture.detectChanges();
|
||||
|
||||
const spinnerSelector = By.css('[data-automation-id="content-node-selector-search-pagination"] [data-automation-id="adf-infinite-pagination-spinner"]');
|
||||
const paginationLoading = fixture.debugElement.query(spinnerSelector);
|
||||
expect(paginationLoading).not.toBeNull();
|
||||
done();
|
||||
}, 300);
|
||||
});
|
||||
|
||||
it('should set its loading state to true after search was performed', (done) => {
|
||||
component.showingSearchResults = true;
|
||||
component.pagination = { hasMoreItems: true };
|
||||
|
||||
typeToSearchBox('shenron');
|
||||
fixture.detectChanges();
|
||||
|
||||
setTimeout(() => {
|
||||
respondWithSearchResults(ONE_FOLDER_RESULT);
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
const spinnerSelector = By.css('[data-automation-id="content-node-selector-search-pagination"] [data-automation-id="adf-infinite-pagination-spinner"]');
|
||||
const paginationLoading = fixture.debugElement.query(spinnerSelector);
|
||||
expect(paginationLoading).toBeNull();
|
||||
done();
|
||||
});
|
||||
}, 300);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Cancel button', () => {
|
||||
|
||||
it('should not be shown if dialogRef is NOT injected', () => {
|
||||
const closeButton = fixture.debugElement.query(By.css('[content-node-selector-actions-cancel]'));
|
||||
expect(closeButton).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Action button for the chosen node', () => {
|
||||
|
||||
const entry: MinimalNodeEntryEntity = <MinimalNodeEntryEntity> {};
|
||||
let hasPermission;
|
||||
|
||||
beforeEach(() => {
|
||||
const alfrescoContentService = TestBed.get(ContentService);
|
||||
spyOn(alfrescoContentService, 'hasPermission').and.callFake(() => hasPermission);
|
||||
});
|
||||
|
||||
it('should be disabled by default', () => {
|
||||
fixture.detectChanges();
|
||||
|
||||
let actionButton = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-actions-choose"]'));
|
||||
expect(actionButton.nativeElement.disabled).toBe(true);
|
||||
});
|
||||
|
||||
it('should become enabled after loading node with the necessary permissions', () => {
|
||||
hasPermission = true;
|
||||
component.documentList.folderNode = entry;
|
||||
component.documentList.ready.emit();
|
||||
fixture.detectChanges();
|
||||
|
||||
let actionButton = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-actions-choose"]'));
|
||||
expect(actionButton.nativeElement.disabled).toBe(false);
|
||||
});
|
||||
|
||||
it('should remain disabled after loading node without the necessary permissions', () => {
|
||||
hasPermission = false;
|
||||
component.documentList.folderNode = entry;
|
||||
component.documentList.ready.emit();
|
||||
fixture.detectChanges();
|
||||
|
||||
let actionButton = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-actions-choose"]'));
|
||||
expect(actionButton.nativeElement.disabled).toBe(true);
|
||||
});
|
||||
|
||||
it('should be enabled when clicking on a node (with the right permissions) in the list (onNodeSelect)', () => {
|
||||
hasPermission = true;
|
||||
|
||||
component.onNodeSelect({ detail: { node: { entry } } });
|
||||
fixture.detectChanges();
|
||||
|
||||
let actionButton = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-actions-choose"]'));
|
||||
expect(actionButton.nativeElement.disabled).toBe(false);
|
||||
});
|
||||
|
||||
it('should remain disabled when clicking on a node (with the WRONG permissions) in the list (onNodeSelect)', () => {
|
||||
hasPermission = false;
|
||||
|
||||
component.onNodeSelect({ detail: { node: { entry } } });
|
||||
fixture.detectChanges();
|
||||
|
||||
let actionButton = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-actions-choose"]'));
|
||||
expect(actionButton.nativeElement.disabled).toBe(true);
|
||||
});
|
||||
|
||||
it('should become disabled when clicking on a node (with the WRONG permissions) after previously selecting a right node', () => {
|
||||
hasPermission = true;
|
||||
component.onNodeSelect({ detail: { node: { entry } } });
|
||||
fixture.detectChanges();
|
||||
|
||||
hasPermission = false;
|
||||
component.onNodeSelect({ detail: { node: { entry } } });
|
||||
fixture.detectChanges();
|
||||
|
||||
let actionButton = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-actions-choose"]'));
|
||||
expect(actionButton.nativeElement.disabled).toBe(true);
|
||||
});
|
||||
|
||||
it('should be disabled when resetting the chosen node', () => {
|
||||
hasPermission = true;
|
||||
component.onNodeSelect({ detail: { node: { entry: <MinimalNodeEntryEntity> {} } } });
|
||||
fixture.detectChanges();
|
||||
|
||||
component.resetChosenNode();
|
||||
fixture.detectChanges();
|
||||
|
||||
let actionButton = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-actions-choose"]'));
|
||||
expect(actionButton.nativeElement.disabled).toBe(true);
|
||||
});
|
||||
|
||||
it('should make the call to get the corresponding node entry to emit when a site node is selected as destination', () => {
|
||||
spyOn(nodesApi, 'getNode').and.callFake((nodeId) => {
|
||||
return new Promise(resolve => {
|
||||
resolve({ entry: { id: nodeId } });
|
||||
});
|
||||
});
|
||||
|
||||
const siteNode1 = { title: 'my files', guid: '-my-' };
|
||||
const siteNode2 = { title: 'my sites', guid: '-mysites-' };
|
||||
|
||||
component.dropdownSiteList = [siteNode1, siteNode2];
|
||||
fixture.detectChanges();
|
||||
component.chosenNode = siteNode1;
|
||||
fixture.detectChanges();
|
||||
component.choose();
|
||||
|
||||
const options = {
|
||||
include: ['path', 'properties', 'allowableOperations']
|
||||
};
|
||||
expect(nodesApi.getNode).toHaveBeenCalledWith(
|
||||
'-my-',
|
||||
options
|
||||
);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@@ -15,345 +15,81 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
Component,
|
||||
EventEmitter,
|
||||
Inject,
|
||||
Input,
|
||||
OnInit,
|
||||
Optional,
|
||||
Output,
|
||||
ViewChild,
|
||||
ViewEncapsulation
|
||||
} from '@angular/core';
|
||||
import {
|
||||
AlfrescoApiService,
|
||||
ContentService,
|
||||
HighlightDirective,
|
||||
SiteModel,
|
||||
UserPreferencesService
|
||||
} from '@alfresco/adf-core';
|
||||
import { FormControl } from '@angular/forms';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material';
|
||||
import { MinimalNodeEntryEntity, NodePaging, Pagination, Site } from 'alfresco-js-api';
|
||||
import { DocumentListComponent, PaginationStrategy } from '../document-list/components/document-list.component';
|
||||
import { Component, Inject, ViewEncapsulation, Input } from '@angular/core';
|
||||
import { MAT_DIALOG_DATA } from '@angular/material';
|
||||
import { MinimalNodeEntryEntity, SitePaging } from 'alfresco-js-api';
|
||||
import { ContentNodeSelectorComponentData } from './content-node-selector.component-data.interface';
|
||||
import { RowFilter } from '../document-list/data/row-filter.model';
|
||||
import { ImageResolver } from '../document-list/data/image-resolver.model';
|
||||
|
||||
import { ContentNodeSelectorComponentData } from './content-node-selector.component-data.interface';
|
||||
import { ContentNodeSelectorService } from './content-node-selector.service';
|
||||
import { debounceTime } from 'rxjs/operators';
|
||||
|
||||
@Component({
|
||||
selector: 'adf-content-node-selector',
|
||||
styleUrls: ['./content-node-selector.component.scss'],
|
||||
templateUrl: './content-node-selector.component.html',
|
||||
styleUrls: ['./content-node-selector.component.scss'],
|
||||
encapsulation: ViewEncapsulation.None
|
||||
})
|
||||
export class ContentNodeSelectorComponent implements OnInit {
|
||||
export class ContentNodeSelectorComponent {
|
||||
|
||||
nodes: NodePaging | null = null;
|
||||
siteId: null | string;
|
||||
searchTerm: string = '';
|
||||
showingSearchResults: boolean = false;
|
||||
loadingSearchResults: boolean = false;
|
||||
inDialog: boolean = false;
|
||||
chosenNode: MinimalNodeEntryEntity | Site | null = null;
|
||||
folderIdToShow: string | null = null;
|
||||
paginationStrategy: PaginationStrategy;
|
||||
pagination: Pagination;
|
||||
skipCount: number = 0;
|
||||
infiniteScroll: boolean = false;
|
||||
buttonActionName: string;
|
||||
|
||||
/**
|
||||
* @deprecated in 2.1.0
|
||||
*/
|
||||
@Input()
|
||||
title: string;
|
||||
title: string = null;
|
||||
|
||||
/**
|
||||
* @deprecated in 2.1.0
|
||||
*/
|
||||
@Input()
|
||||
actionName: string;
|
||||
|
||||
@Input()
|
||||
currentFolderId: string | null = null;
|
||||
currentFolderId: string = null;
|
||||
|
||||
/**
|
||||
* @deprecated in 2.1.0
|
||||
*/
|
||||
@Input()
|
||||
dropdownHideMyFiles: boolean = false;
|
||||
|
||||
/**
|
||||
* @deprecated in 2.1.0
|
||||
*/
|
||||
@Input()
|
||||
dropdownSiteList: any[] = null;
|
||||
dropdownSiteList: SitePaging = null;
|
||||
|
||||
/**
|
||||
* @deprecated in 2.1.0
|
||||
*/
|
||||
@Input()
|
||||
rowFilter: RowFilter = null;
|
||||
|
||||
/**
|
||||
* @deprecated in 2.1.0
|
||||
*/
|
||||
@Input()
|
||||
imageResolver: ImageResolver = null;
|
||||
|
||||
/**
|
||||
* @deprecated in 2.1.0
|
||||
*/
|
||||
@Input()
|
||||
pageSize: number;
|
||||
|
||||
@Output()
|
||||
select: EventEmitter<MinimalNodeEntryEntity[]> = new EventEmitter<MinimalNodeEntryEntity[]>();
|
||||
buttonActionName: string;
|
||||
private chosenNode: MinimalNodeEntryEntity[];
|
||||
|
||||
@ViewChild(DocumentListComponent)
|
||||
documentList: DocumentListComponent;
|
||||
|
||||
@ViewChild(HighlightDirective)
|
||||
highlighter: HighlightDirective;
|
||||
|
||||
debounceSearch: number= 200;
|
||||
|
||||
searchInput: FormControl = new FormControl();
|
||||
|
||||
constructor(private contentNodeSelectorService: ContentNodeSelectorService,
|
||||
private contentService: ContentService,
|
||||
private apiService: AlfrescoApiService,
|
||||
private preferences: UserPreferencesService,
|
||||
@Optional() @Inject(MAT_DIALOG_DATA) data?: ContentNodeSelectorComponentData,
|
||||
@Optional() private containingDialog?: MatDialogRef<ContentNodeSelectorComponent>) {
|
||||
if (data) {
|
||||
this.title = data.title;
|
||||
this.actionName = data.actionName;
|
||||
this.select = data.select;
|
||||
this.currentFolderId = data.currentFolderId;
|
||||
this.dropdownHideMyFiles = data.dropdownHideMyFiles;
|
||||
this.dropdownSiteList = data.dropdownSiteList;
|
||||
this.rowFilter = data.rowFilter;
|
||||
this.imageResolver = data.imageResolver;
|
||||
}
|
||||
this.buttonActionName = this.actionName ? `NODE_SELECTOR.${this.actionName.toUpperCase()}` : 'NODE_SELECTOR.CHOOSE';
|
||||
|
||||
if (this.containingDialog) {
|
||||
this.inDialog = true;
|
||||
}
|
||||
|
||||
this.searchInput.valueChanges
|
||||
.pipe(
|
||||
debounceTime(this.debounceSearch)
|
||||
)
|
||||
.subscribe((searchValue) => {
|
||||
this.search(searchValue);
|
||||
});
|
||||
|
||||
this.pageSize = this.preferences.paginationSize;
|
||||
constructor(@Inject(MAT_DIALOG_DATA) public data: ContentNodeSelectorComponentData) {
|
||||
this.buttonActionName = data.actionName ? `NODE_SELECTOR.${data.actionName.toUpperCase()}` : 'NODE_SELECTOR.CHOOSE';
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.folderIdToShow = this.currentFolderId;
|
||||
this.paginationStrategy = PaginationStrategy.Infinite;
|
||||
close() {
|
||||
this.data.select.complete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the site attribute and starts a new search
|
||||
*
|
||||
* @param chosenSite Sitemodel to search within
|
||||
*/
|
||||
siteChanged(chosenSite: SiteModel): void {
|
||||
this.siteId = chosenSite.guid;
|
||||
this.updateResults();
|
||||
onSelect(nodeList: MinimalNodeEntryEntity[]) {
|
||||
this.chosenNode = nodeList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the searchTerm attribute and starts a new search
|
||||
*
|
||||
* @param searchTerm string value to search against
|
||||
*/
|
||||
search(searchTerm: string): void {
|
||||
this.searchTerm = searchTerm;
|
||||
this.updateResults();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether breadcrumb has to be shown or not
|
||||
*/
|
||||
needBreadcrumbs() {
|
||||
const whenInFolderNavigation = !this.showingSearchResults,
|
||||
whenInSelectingSearchResult = this.showingSearchResults && this.chosenNode;
|
||||
|
||||
return whenInFolderNavigation || whenInSelectingSearchResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the actually selected|entered folder node or null in case of searching for the breadcrumb
|
||||
*/
|
||||
get breadcrumbFolderNode(): MinimalNodeEntryEntity | null {
|
||||
if (this.showingSearchResults && this.chosenNode) {
|
||||
return this.chosenNode;
|
||||
} else {
|
||||
return this.documentList.folderNode;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the search input
|
||||
*/
|
||||
clear(): void {
|
||||
this.searchTerm = '';
|
||||
this.nodes = null;
|
||||
this.skipCount = 0;
|
||||
this.chosenNode = null;
|
||||
this.showingSearchResults = false;
|
||||
this.folderIdToShow = this.currentFolderId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the result list depending on the criterias
|
||||
*/
|
||||
private updateResults(): void {
|
||||
if (this.searchTerm.length === 0) {
|
||||
this.folderIdToShow = this.siteId || this.currentFolderId;
|
||||
} else {
|
||||
this.startNewSearch();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the first page of a new search result
|
||||
*/
|
||||
private startNewSearch(): void {
|
||||
this.nodes = null;
|
||||
this.skipCount = 0;
|
||||
this.chosenNode = null;
|
||||
this.folderIdToShow = null;
|
||||
this.querySearch();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the next batch of search results
|
||||
*
|
||||
* @param event Pagination object
|
||||
*/
|
||||
getNextPageOfSearch(event: Pagination): void {
|
||||
this.infiniteScroll = true;
|
||||
this.skipCount = event.skipCount;
|
||||
this.querySearch();
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the call to searchService with the proper parameters
|
||||
*/
|
||||
private querySearch(): void {
|
||||
this.loadingSearchResults = true;
|
||||
|
||||
this.contentNodeSelectorService.search(this.searchTerm, this.siteId, this.skipCount, this.pageSize)
|
||||
.subscribe(this.showSearchResults.bind(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the results of the search
|
||||
*
|
||||
* @param results Search results
|
||||
*/
|
||||
private showSearchResults(results: NodePaging): void {
|
||||
this.showingSearchResults = true;
|
||||
this.loadingSearchResults = false;
|
||||
|
||||
// Documentlist hack, since data displaying for preloaded nodes is a little bit messy there
|
||||
if (!this.nodes) {
|
||||
this.nodes = results;
|
||||
} else {
|
||||
this.documentList.data.loadPage(results, true);
|
||||
}
|
||||
|
||||
this.pagination = results.list.pagination;
|
||||
this.highlight();
|
||||
}
|
||||
|
||||
/**
|
||||
* Hightlight the actual searchterm in the next frame
|
||||
*/
|
||||
highlight(): void {
|
||||
setTimeout(() => {
|
||||
this.highlighter.highlight(this.searchTerm);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked when user selects a node
|
||||
*
|
||||
* @param event CustomEvent for node-select
|
||||
*/
|
||||
onNodeSelect(event: any): void {
|
||||
this.attemptNodeSelection(event.detail.node.entry);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets showingSearchResults state to be able to differentiate between search results or folder results
|
||||
*/
|
||||
onFolderChange(): void {
|
||||
this.skipCount = 0;
|
||||
this.infiniteScroll = false;
|
||||
this.showingSearchResults = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to set the currently loaded node
|
||||
*/
|
||||
onFolderLoaded(nodePage: NodePaging): void {
|
||||
this.attemptNodeSelection(this.documentList.folderNode);
|
||||
this.pagination = nodePage.list.pagination;
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects node as chosen if it has the right permission, clears the selection otherwise
|
||||
*
|
||||
* @param entry
|
||||
*/
|
||||
private attemptNodeSelection(entry: MinimalNodeEntryEntity): void {
|
||||
if (this.contentService.hasPermission(entry, 'create')) {
|
||||
this.chosenNode = entry;
|
||||
} else {
|
||||
this.resetChosenNode();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the chosen node
|
||||
*/
|
||||
resetChosenNode(): void {
|
||||
this.chosenNode = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit event with the chosen node
|
||||
*/
|
||||
choose(): void {
|
||||
const entry: any = this.chosenNode;
|
||||
|
||||
if (entry && entry.guid) {
|
||||
const options = {
|
||||
include: ['path', 'properties', 'allowableOperations']
|
||||
};
|
||||
this.apiService.nodesApi.getNode(entry.guid, options)
|
||||
.then(chosenSiteNode => {
|
||||
this.select.next([chosenSiteNode.entry]);
|
||||
});
|
||||
|
||||
} else {
|
||||
this.select.next([this.chosenNode]);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the dialog
|
||||
*/
|
||||
close(): void {
|
||||
this.containingDialog.close();
|
||||
}
|
||||
|
||||
onNodeDoubleClick(e: CustomEvent) {
|
||||
const node: any = e.detail.node.entry;
|
||||
|
||||
if (node && node.guid) {
|
||||
const options = {
|
||||
maxItems: this.pageSize,
|
||||
skipCount: this.skipCount,
|
||||
include: ['path', 'properties', 'allowableOperations']
|
||||
};
|
||||
|
||||
this.apiService.nodesApi.getNode(node.guid, options)
|
||||
.then(documentLibrary => {
|
||||
this.documentList.performCustomSourceNavigation(documentLibrary);
|
||||
});
|
||||
}
|
||||
onClick(): void {
|
||||
this.data.select.next(this.chosenNode);
|
||||
this.data.select.complete();
|
||||
}
|
||||
}
|
||||
|
@@ -21,8 +21,10 @@ import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { MaterialModule } from '../material.module';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
|
||||
import { ContentNodeSelectorPanelComponent } from './content-node-selector-panel.component';
|
||||
import { ContentNodeSelectorComponent } from './content-node-selector.component';
|
||||
import { ContentNodeSelectorService } from './content-node-selector.service';
|
||||
import { ContentNodeDialogService } from './content-node-dialog.service';
|
||||
import { SitesDropdownModule } from '../site-dropdown/sites-dropdown.module';
|
||||
import { BreadcrumbModule } from '../breadcrumb/breadcrumb.module';
|
||||
import { PaginationModule, ToolbarModule, DirectiveModule, DataColumnModule, DataTableModule } from '@alfresco/adf-core';
|
||||
@@ -46,17 +48,19 @@ import { NameLocationCellComponent } from './name-location-cell/name-location-ce
|
||||
PaginationModule
|
||||
],
|
||||
exports: [
|
||||
ContentNodeSelectorComponent
|
||||
ContentNodeSelectorPanelComponent, ContentNodeSelectorComponent
|
||||
],
|
||||
entryComponents: [
|
||||
ContentNodeSelectorComponent
|
||||
ContentNodeSelectorPanelComponent, ContentNodeSelectorComponent
|
||||
],
|
||||
declarations: [
|
||||
ContentNodeSelectorComponent,
|
||||
NameLocationCellComponent
|
||||
ContentNodeSelectorPanelComponent,
|
||||
NameLocationCellComponent,
|
||||
ContentNodeSelectorComponent
|
||||
],
|
||||
providers: [
|
||||
ContentNodeSelectorService
|
||||
ContentNodeSelectorService,
|
||||
ContentNodeDialogService
|
||||
]
|
||||
})
|
||||
export class ContentNodeSelectorModule {}
|
||||
|
@@ -16,5 +16,6 @@
|
||||
*/
|
||||
|
||||
export * from './content-node-selector.component-data.interface';
|
||||
export * from './content-node-selector-panel.component';
|
||||
export * from './content-node-selector.component';
|
||||
export * from './content-node-selector.service';
|
||||
|
Reference in New Issue
Block a user