[ADF-3723] Tree view component (#3939)

* [ADF-3723] added first step to adf tree view component

* [ADF-3723] start adding tests for the new component

* [ADF-3723] fixed style and start adding tests

* [ADF-3723] working on unit test

* [ADF-3723] added test for the new tree view component

* [ADF-3723] added event when clicked on a tree node

* [ADF-3723] refactored code

* [ADF-3723 added peer review changes

* [ADF-3723] fixed extra space

* [ADF-3723] fixed unit test

* [ADF-3723] fixed failing unit test
This commit is contained in:
Vito
2018-11-07 19:32:55 +00:00
committed by Eugenio Romano
parent 0ee592607a
commit 6ead388e55
25 changed files with 720 additions and 10 deletions

View File

@@ -85,7 +85,8 @@
"WORD_TO_SEARCH": "Search Word",
"SEARCH_CREATED_BY": "Created By",
"SEARCH_SERVICE_APPROACH": "Check this to disable the input property and configure using the service",
"HEADER_DATA": "Header Data"
"HEADER_DATA": "Header Data",
"TREE_VIEW": "Tree View"
},
"TRASHCAN": {
"ACTIONS": {

View File

@@ -12,8 +12,8 @@
"clientId": "activiti",
"scope": "openid",
"secret": "",
"implicitFlow": true,
"silentLogin": true,
"implicitFlow": false,
"silentLogin": false,
"redirectUri": "/",
"redirectUriLogout": "/logout"
},

View File

@@ -67,6 +67,7 @@ import { ProcessServicesCloudModule } from '@alfresco/adf-process-services-cloud
import { CloudComponent } from './components/cloud/cloud.component';
import { TaskListCloudDemoComponent } from './components/task-list-cloud-demo/task-list-cloud-demo.component';
import { ProcessListCloudExampleComponent } from './components/cloud/process-list-cloud-example.component';
import { TreeViewSampleComponent } from './components/tree-view/tree-view-sample.component';
@NgModule({
imports: [
@@ -117,7 +118,8 @@ import { ProcessListCloudExampleComponent } from './components/cloud/process-lis
FormLoadingComponent,
ReportIssueComponent,
TaskListCloudDemoComponent,
ProcessListCloudExampleComponent
ProcessListCloudExampleComponent,
TreeViewSampleComponent
],
providers: [
{

View File

@@ -43,6 +43,7 @@ import { AppComponent } from './app.component';
import { CloudComponent } from './components/cloud/cloud.component';
import { TaskListCloudDemoComponent } from './components/task-list-cloud-demo/task-list-cloud-demo.component';
import { ProcessListCloudExampleComponent } from './components/cloud/process-list-cloud-example.component';
import { TreeViewSampleComponent } from './components/tree-view/tree-view-sample.component';
export const appRoutes: Routes = [
{ path: 'login', component: LoginComponent },
@@ -279,6 +280,11 @@ export const appRoutes: Routes = [
component: DemoPermissionComponent,
canActivate: [AuthGuardEcm]
},
{
path: 'treeview',
component: TreeViewSampleComponent,
canActivate: [AuthGuardEcm]
},
{
path: 'about',
loadChildren: 'app/components/about/about.module#AppAboutModule'

View File

@@ -58,6 +58,7 @@ export class AppLayoutComponent implements OnInit {
{ href: '/extendedSearch', icon: 'search', title: 'APP_LAYOUT.SEARCH' },
/* cspell:disable-next-line */
{ href: '/overlay-viewer', icon: 'pageview', title: 'APP_LAYOUT.OVERLAY_VIEWER' },
{ href: '/treeview', icon: 'nature', title: 'APP_LAYOUT.TREE_VIEW' },
{ href: '/about', icon: 'info_outline', title: 'APP_LAYOUT.ABOUT' }
];

View File

@@ -0,0 +1,10 @@
<div>TREE VIEW TEST</div>
<mat-form-field class="example-full-width">
<input matInput placeholder="Node Id" [(ngModel)]="nodeIdSample">
</mat-form-field>
<span>
CLICKED NODE: {{clickedNodeName}}
</span>
<adf-tree-view-list [nodeId]="nodeIdSample" (nodeClicked)="onClick($event)">
</adf-tree-view-list>

View File

@@ -0,0 +1,3 @@
.example-full-width {
width: 100%;
}

View File

@@ -0,0 +1,34 @@
/*!
* @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 } from '@angular/core';
@Component({
selector: 'app-tree-view',
templateUrl: 'tree-view-sample.component.html',
styleUrls: ['tree-view-sample.component.scss']
})
export class TreeViewSampleComponent {
clickedNodeName: string = '';
nodeIdSample: string = '-my-';
onClick(node) {
this.clickedNodeName = node.entry.name;
}
}

View File

@@ -0,0 +1,32 @@
---
Added: v2.6.1
Status: Active
---
# Rating component
Allow a user to show the folder and subfolders of a node in a tree view
![TreeView component screenshot](../docassets/images/tree-view.png)
## Basic Usage
```html
<adf-tree-view-list [nodeId]="'74cd8a96-8a21-47e5-9b3b-a1b3e296787d'"
(nodeClicked)="onClick($event)">
</adf-tree-view-list>
```
## Class members
### Properties
| Name | Type | Default value | Description |
| ---- | ---- | ------------- | ----------- |
| nodeId | `string` | | Identifier of the node to apply the rating to. |
### Events
| Name | Type | Description |
| ---- | ---- | ----------- |
| nodeClicked | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<NodeEntry>` | Emitted when a node on the tree view is clicked |

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -38,6 +38,7 @@ import { DialogModule } from './dialogs/dialog.module';
import { FolderDirectiveModule } from './folder-directive/folder-directive.module';
import { ContentMetadataModule } from './content-metadata/content-metadata.module';
import { PermissionManagerModule } from './permission-manager/permission-manager.module';
import { TreeViewModule } from './tree-view/tree-view.module';
@NgModule({
imports: [
@@ -61,7 +62,8 @@ import { PermissionManagerModule } from './permission-manager/permission-manager
FolderDirectiveModule,
ContentDirectiveModule,
PermissionManagerModule,
VersionManagerModule
VersionManagerModule,
TreeViewModule
],
exports: [
SocialModule,
@@ -79,7 +81,8 @@ import { PermissionManagerModule } from './permission-manager/permission-manager
FolderDirectiveModule,
ContentDirectiveModule,
PermissionManagerModule,
VersionManagerModule
VersionManagerModule,
TreeViewModule
]
})
export class ContentModuleLazy {}
@@ -106,7 +109,8 @@ export class ContentModuleLazy {}
FolderDirectiveModule,
ContentDirectiveModule,
PermissionManagerModule,
VersionManagerModule
VersionManagerModule,
TreeViewModule
],
providers: [
{
@@ -134,7 +138,8 @@ export class ContentModuleLazy {}
FolderDirectiveModule,
ContentDirectiveModule,
PermissionManagerModule,
VersionManagerModule
VersionManagerModule,
TreeViewModule
]
})
export class ContentModule {

View File

@@ -289,5 +289,8 @@
"DUPLICATE-PERMISSION": "One or more of the permissions you have set is already present : {{list}}",
"NOT-ALLOWED": "You are not allowed to change permissions"
}
},
"ADF-TREE-VIEW": {
"MISSING-ID": "No nodeId provided!"
}
}

View File

@@ -35,7 +35,8 @@ import {
MatDatepickerModule,
MatSlideToggleModule,
MatRadioModule,
MatSliderModule
MatSliderModule,
MatTreeModule
} from '@angular/material';
export function modules() {
@@ -58,7 +59,8 @@ export function modules() {
MatDatepickerModule,
MatSlideToggleModule,
MatRadioModule,
MatSliderModule
MatSliderModule,
MatTreeModule
];
}

View File

@@ -18,6 +18,7 @@
@import '../permission-manager/components/add-permission/add-permission.component';
@import '../permission-manager/components/add-permission/add-permission-dialog.component';
@import '../permission-manager/components/add-permission/add-permission-panel.component';
@import '../tree-view/components/tree-view.component';
@mixin adf-content-services-theme($theme) {
@include adf-breadcrumb-theme($theme);
@@ -36,4 +37,5 @@
@include adf-add-permission-theme($theme);
@include adf-add-permission-dialog-theme($theme);
@include adf-add-permission-panel-theme($theme);
@include adf-tree-view-theme($theme);
}

View File

@@ -0,0 +1,21 @@
<mat-tree class="adf-tree-view-main" [dataSource]="dataSource" [treeControl]="treeControl" *ngIf="nodeId; else missingNodeId">
<mat-tree-node class="adf-tree-view-node" *matTreeNodeDef="let treeNode" id="{{treeNode.name + '-tree-node'}}"
matTreeNodePadding [matTreeNodePaddingIndent]="15">
{{treeNode.name}}
</mat-tree-node>
<mat-tree-node class="adf-tree-view-node" id="{{treeNode.name + '-tree-child-node'}}" *matTreeNodeDef="let treeNode; when: hasChild"
matTreeNodePadding [matTreeNodePaddingIndent]="15">
<button id="{{'button-'+treeNode.name}}" (click)="onNodeClicked(treeNode.node)"
mat-icon-button [attr.aria-label]="'toggle ' + treeNode.filename" matTreeNodeToggle>
<mat-icon class="adf-tree-view-icon">
{{treeControl.isExpanded(treeNode) ? 'folder_open' : 'folder'}}
</mat-icon>
</button>
{{treeNode.name}}
</mat-tree-node>
</mat-tree>
<ng-template #missingNodeId>
<div id="adf-tree-view-missing-node">
{{'ADF-TREE-VIEW.MISSING-ID' | translate}}
</div>
</ng-template>

View File

@@ -0,0 +1,17 @@
@mixin adf-tree-view-theme($theme) {
$primary: map-get($theme, primary);
$foreground: map-get($theme, foreground);
$background: map-get($theme, background);
.adf {
&-tree-view-icon {
color: #D9E022;
}
&-tree-view-node.mat-tree-node {
min-height: 40px;
font-size: 12px;
align-items: baseline;
}
}
}

View File

@@ -0,0 +1,181 @@
/*!
* @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 { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { setupTestBed } from '@alfresco/adf-core';
import { TreeViewComponent } from './tree-view.component';
import { ContentTestingModule } from '../../testing/content.testing.module';
import { TreeViewService } from '../services/tree-view.service';
import { of } from 'rxjs';
import { TreeBaseNode } from '../models/tree-view.model';
import { NodeEntry } from 'alfresco-js-api';
describe('TreeViewComponent', () => {
let fixture: ComponentFixture<TreeViewComponent>;
let element: HTMLElement;
let treeService: TreeViewService;
let component: any;
let fakeNodeList: TreeBaseNode[] = [
{ nodeId: 'fake-node-id', name: 'fake-node-name', level: 0, expandable: true,
node: { entry: { name: 'fake-node-name', id: 'fake-node-id' } } }
];
let fakeChildrenList: TreeBaseNode[] = [
{ nodeId: 'fake-child-id', name: 'fake-child-name', level: 0, expandable: true, node : {} },
{ nodeId: 'fake-second-id', name: 'fake-second-name', level: 0, expandable: true, node : {} }
];
let fakeNextChildrenList: TreeBaseNode[] = [
{ nodeId: 'fake-next-child-id', name: 'fake-next-child-name', level: 0, expandable: true, node : {} },
{ nodeId: 'fake-next-second-id', name: 'fake-next-second-name', level: 0, expandable: true, node : {} }
];
let returnRootOrChildrenNode = function (nodeId: string) {
if (nodeId === '9999999') {
return of(fakeNodeList);
} else if (nodeId === 'fake-second-id') {
return of(fakeNextChildrenList);
} else {
return of(fakeChildrenList);
}
};
setupTestBed({
imports: [
ContentTestingModule
],
declarations: [
]
});
describe('When there is a nodeId', () => {
beforeEach(async(() => {
treeService = TestBed.get(TreeViewService);
fixture = TestBed.createComponent(TreeViewComponent);
element = fixture.nativeElement;
component = fixture.componentInstance;
spyOn(treeService, 'getTreeNodes').and.callFake((nodeId) => returnRootOrChildrenNode(nodeId));
component.nodeId = '9999999';
fixture.detectChanges();
}));
afterEach(() => {
fixture.destroy();
});
it('should show the folder', async(() => {
expect(element.querySelector('#fake-node-name-tree-child-node')).not.toBeNull();
}));
it('should show the subfolders when the folder is clicked', async(() => {
let rootFolderButton: HTMLButtonElement = <HTMLButtonElement> element.querySelector('#button-fake-node-name');
expect(rootFolderButton).not.toBeNull();
rootFolderButton.click();
fixture.detectChanges();
fixture.whenStable().then(() => {
expect(element.querySelector('#fake-child-name-tree-child-node')).not.toBeNull();
expect(element.querySelector('#fake-second-name-tree-child-node')).not.toBeNull();
});
}));
it('should throw a nodeClicked event when a node is clicked', (done) => {
component.nodeClicked.subscribe((nodeClicked: NodeEntry) => {
expect(nodeClicked).toBeDefined();
expect(nodeClicked).not.toBeNull();
expect(nodeClicked.entry.name).toBe('fake-node-name');
expect(nodeClicked.entry.id).toBe('fake-node-id');
done();
});
let rootFolderButton: HTMLButtonElement = <HTMLButtonElement> element.querySelector('#button-fake-node-name');
expect(rootFolderButton).not.toBeNull();
rootFolderButton.click();
});
it('should change the icon of the opened folders', async(() => {
let rootFolderButton: HTMLButtonElement = <HTMLButtonElement> element.querySelector('#button-fake-node-name');
expect(rootFolderButton).not.toBeNull();
expect(element.querySelector('#button-fake-node-name .mat-icon').textContent.trim()).toBe('folder');
rootFolderButton.click();
fixture.detectChanges();
fixture.whenStable().then(() => {
expect(element.querySelector('#button-fake-node-name .mat-icon').textContent.trim()).toBe('folder_open');
});
}));
it('should show the subfolders of a subfolder if there are any', async(() => {
let rootFolderButton: HTMLButtonElement = <HTMLButtonElement> element.querySelector('#button-fake-node-name');
expect(rootFolderButton).not.toBeNull();
rootFolderButton.click();
fixture.detectChanges();
fixture.whenStable().then(() => {
expect(element.querySelector('#fake-second-name-tree-child-node')).not.toBeNull();
let childButton: HTMLButtonElement = <HTMLButtonElement> element.querySelector('#button-fake-second-name');
expect(childButton).not.toBeNull();
childButton.click();
fixture.detectChanges();
fixture.whenStable().then(() => {
expect(element.querySelector('#fake-next-child-name-tree-child-node')).not.toBeNull();
expect(element.querySelector('#fake-next-second-name-tree-child-node')).not.toBeNull();
});
});
}));
it('should hide the subfolders when clicked again', async(() => {
let rootFolderButton: HTMLButtonElement = <HTMLButtonElement> element.querySelector('#button-fake-node-name');
expect(rootFolderButton).not.toBeNull();
rootFolderButton.click();
fixture.detectChanges();
fixture.whenStable().then(() => {
expect(element.querySelector('#fake-child-name-tree-child-node')).not.toBeNull();
expect(element.querySelector('#fake-second-name-tree-child-node')).not.toBeNull();
rootFolderButton.click();
fixture.detectChanges();
fixture.whenStable().then(() => {
expect(element.querySelector('#button-fake-node-name .mat-icon').textContent.trim()).toBe('folder');
expect(element.querySelector('#fake-child-name-tree-child-node')).toBeNull();
expect(element.querySelector('#fake-second-name-tree-child-node')).toBeNull();
});
});
}));
});
describe('When no nodeId is given', () => {
let emptyElement: HTMLElement;
beforeEach(async(() => {
fixture = TestBed.createComponent(TreeViewComponent);
emptyElement = fixture.nativeElement;
}));
afterEach(() => {
fixture.destroy();
});
it('should show an error message when no nodeId is provided', async(() => {
fixture.detectChanges();
fixture.whenStable().then(() => {
fixture.detectChanges();
expect(emptyElement.querySelector('#adf-tree-view-missing-node')).toBeDefined();
expect(emptyElement.querySelector('#adf-tree-view-missing-node')).not.toBeNull();
});
}));
});
});

View File

@@ -0,0 +1,77 @@
/*!
* @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 { FlatTreeControl } from '@angular/cdk/tree';
import { Component, Input, OnInit, OnChanges, SimpleChanges, Output, EventEmitter } from '@angular/core';
import { TreeBaseNode } from '../models/tree-view.model';
import { TreeViewDataSource } from '../data/tree-view-datasource';
import { TreeViewService } from '../services/tree-view.service';
import { NodeEntry } from 'alfresco-js-api';
@Component({
selector: 'adf-tree-view-list',
templateUrl: 'tree-view.component.html',
styleUrls: ['tree-view.component.scss']
})
export class TreeViewComponent implements OnInit, OnChanges {
@Input()
nodeId: string;
@Output()
nodeClicked: EventEmitter<NodeEntry> = new EventEmitter();
treeControl: FlatTreeControl<TreeBaseNode>;
dataSource: TreeViewDataSource;
constructor(private treeViewService: TreeViewService) {
this.treeControl = new FlatTreeControl<TreeBaseNode>(this.getLevel, this.isExpandable);
this.dataSource = new TreeViewDataSource(this.treeControl, this.treeViewService);
}
ngOnInit() {
if (this.nodeId) {
this.loadTreeNode();
}
}
ngOnChanges(changes: SimpleChanges) {
if (changes['nodeId'].currentValue &&
changes['nodeId'].currentValue !== changes['nodeId'].previousValue) {
this.loadTreeNode();
}
}
onNodeClicked(node: NodeEntry) {
this.nodeClicked.emit(node);
}
getLevel = (node: TreeBaseNode) => node.level;
isExpandable = (node: TreeBaseNode) => node.expandable;
hasChild = (level: number, nodeData: TreeBaseNode) => nodeData.expandable;
private loadTreeNode() {
this.treeViewService.getTreeNodes(this.nodeId)
.subscribe(
(treeNode: TreeBaseNode[]) => {
this.dataSource.data = treeNode;
});
}
}

View File

@@ -0,0 +1,91 @@
/*!
* @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 { Injectable } from '@angular/core';
import { CollectionViewer, SelectionChange } from '@angular/cdk/collections';
import { BehaviorSubject, merge, Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { FlatTreeControl } from '@angular/cdk/tree';
import { TreeBaseNode } from '../models/tree-view.model';
import { TreeViewService } from '../services/tree-view.service';
@Injectable()
export class TreeViewDataSource {
treeNodes: TreeBaseNode[];
dataChange = new BehaviorSubject<TreeBaseNode[]>([]);
get data(): TreeBaseNode[] {
return this.treeNodes;
}
set data(value: TreeBaseNode[]) {
this.treeControl.dataNodes = value;
this.dataChange.next(value);
}
constructor(private treeControl: FlatTreeControl<TreeBaseNode>,
private treeViewService: TreeViewService) {
this.dataChange.subscribe((treeNodes) => this.treeNodes = treeNodes);
}
connect(collectionViewer: CollectionViewer): Observable<TreeBaseNode[]> {
this.treeControl.expansionModel.onChange!.subscribe(change => {
if ((change as SelectionChange<TreeBaseNode>).added &&
(change as SelectionChange<TreeBaseNode>).added.length > 0) {
this.expandTreeNodes(change as SelectionChange<TreeBaseNode>);
} else if ((change as SelectionChange<TreeBaseNode>).removed) {
this.reduceTreeNodes(change as SelectionChange<TreeBaseNode>);
}
});
return merge(collectionViewer.viewChange, this.dataChange).pipe(map(() => this.data));
}
private expandTreeNodes(change: SelectionChange<TreeBaseNode>) {
change.added.forEach(node => this.expandNode(node));
}
private reduceTreeNodes(change: SelectionChange<TreeBaseNode>) {
change.removed.slice().reverse().forEach(node => this.toggleNode(node));
}
private expandNode(node: TreeBaseNode) {
this.treeViewService.getTreeNodes(node.nodeId).subscribe((children) => {
const index = this.data.indexOf(node);
if (!children || index < 0) {
node.expandable = false;
return;
}
const nodes = children.map(actualNode => {
actualNode.level = node.level + 1;
return actualNode;
});
this.data.splice(index + 1, 0, ...nodes);
this.dataChange.next(this.data);
});
}
toggleNode(node: TreeBaseNode) {
const index = this.data.indexOf(node);
let count = 0;
for (let i = index + 1; i < this.data.length
&& this.data[i].level > node.level; i++ , count++) { }
this.data.splice(index + 1, count);
this.dataChange.next(this.data);
}
}

View File

@@ -0,0 +1,18 @@
/*!
* @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.
*/
export * from './public-api';

View File

@@ -0,0 +1,34 @@
/*!
* @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 { NodeEntry } from 'alfresco-js-api';
export class TreeBaseNode {
name: string;
nodeId: string;
level: number;
expandable = true;
node: NodeEntry;
constructor(nodeEntry: NodeEntry, level?: number) {
this.name = nodeEntry.entry.name;
this.level = level ? level : 0;
this.nodeId = nodeEntry.entry.id;
this.node = nodeEntry;
}
}

View File

@@ -0,0 +1,21 @@
/*!
* @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.
*/
export * from './tree-view.module';
export * from './components/tree-view.component';
export * from './data/tree-view-datasource';
export * from './models/tree-view.model';

View File

@@ -0,0 +1,68 @@
/*!
* @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 { setupTestBed, NodesApiService } from '@alfresco/adf-core';
import { TreeViewService } from './tree-view.service';
import { TestBed } from '@angular/core/testing';
import { ContentTestingModule } from '../../testing/content.testing.module';
import { of } from 'rxjs';
import { TreeBaseNode } from 'tree-view/models/tree-view.model';
describe('TreeViewService', () => {
let service: TreeViewService;
let nodeService: NodesApiService;
let fakeNodeList = { list: { entries: [
{ entry: { id: 'fake-node-id', name: 'fake-node-name', isFolder: true } }
] } };
let fakeMixedNodeList = { list: { entries: [
{ entry: { id: 'fake-node-id', name: 'fake-node-name', isFolder: true } },
{ entry: { id: 'fake-file-id', name: 'fake-file-name', isFolder: false } }
] } };
setupTestBed({
imports: [ContentTestingModule]
});
beforeEach(() => {
service = TestBed.get(TreeViewService);
nodeService = TestBed.get(NodesApiService);
});
it('should returns TreeBaseNode elements', (done) => {
spyOn(nodeService, 'getNodeChildren').and.returnValue(of(fakeNodeList));
service.getTreeNodes('fake-node-id').subscribe((nodes: TreeBaseNode[]) => {
expect(nodes.length).toBe(1);
expect(nodes[0].nodeId).toBe('fake-node-id');
expect(nodes[0].name).toBe('fake-node-name');
done();
});
});
it('should returns only folders elements', (done) => {
spyOn(nodeService, 'getNodeChildren').and.returnValue(of(fakeMixedNodeList));
service.getTreeNodes('fake-node-id').subscribe((nodes: TreeBaseNode[]) => {
expect(nodes.length).toBe(1);
expect(nodes[0].nodeId).toBe('fake-node-id');
expect(nodes[0].name).toBe('fake-node-name');
done();
});
});
});

View File

@@ -0,0 +1,43 @@
/*!
* @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 { NodesApiService } from '@alfresco/adf-core';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { TreeBaseNode } from '../models/tree-view.model';
import { NodePaging, NodeEntry } from 'alfresco-js-api';
import { map } from 'rxjs/operators';
@Injectable({
providedIn: 'root'
})
export class TreeViewService {
constructor(private nodeApi: NodesApiService) {
}
getTreeNodes(nodeId): Observable<TreeBaseNode[]> {
return this.nodeApi.getNodeChildren(nodeId)
.pipe(
map((nodePage: NodePaging) => {
return nodePage.list.entries.filter((node) => node.entry.isFolder ? node : null);
}),
map((nodes: NodeEntry[]) => nodes.map(node => new TreeBaseNode(node)))
);
}
}

View File

@@ -0,0 +1,38 @@
/*!
* @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 { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { TranslateModule } from '@ngx-translate/core';
import { MaterialModule } from '../material.module';
import { TreeViewComponent } from './components/tree-view.component';
@NgModule({
imports: [
CommonModule,
MaterialModule,
TranslateModule.forChild()
],
declarations: [
TreeViewComponent
],
exports: [
TreeViewComponent
]
})
export class TreeViewModule {
}