mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-11 12:03:03 +00:00
Pagination for collections & Process feedback
This commit is contained in:
@@ -343,6 +343,7 @@
|
|||||||
"communityList.tabTitle": "DSpace - Community List",
|
"communityList.tabTitle": "DSpace - Community List",
|
||||||
|
|
||||||
"communityList.title": "List of Communities",
|
"communityList.title": "List of Communities",
|
||||||
|
|
||||||
"communityList.showMore": "Show More",
|
"communityList.showMore": "Show More",
|
||||||
|
|
||||||
|
|
||||||
|
@@ -1,225 +0,0 @@
|
|||||||
import {Injectable} from '@angular/core';
|
|
||||||
import {combineLatest as observableCombineLatest} from 'rxjs/internal/observable/combineLatest';
|
|
||||||
import {merge, Observable, of, of as observableOf} from 'rxjs';
|
|
||||||
import {CommunityDataService} from '../core/data/community-data.service';
|
|
||||||
import {PaginationComponentOptions} from '../shared/pagination/pagination-component-options.model';
|
|
||||||
import {SortDirection, SortOptions} from '../core/cache/models/sort-options.model';
|
|
||||||
import {catchError, defaultIfEmpty, filter, map, switchMap, take, tap} from 'rxjs/operators';
|
|
||||||
import {Community} from '../core/shared/community.model';
|
|
||||||
import {Collection} from '../core/shared/collection.model';
|
|
||||||
import {hasValue, isEmpty, isNotEmpty} from '../shared/empty.util';
|
|
||||||
import {RemoteData} from '../core/data/remote-data';
|
|
||||||
import {PaginatedList} from '../core/data/paginated-list';
|
|
||||||
import {getCommunityPageRoute} from '../+community-page/community-page-routing.module';
|
|
||||||
import {getCollectionPageRoute} from '../+collection-page/collection-page-routing.module';
|
|
||||||
import {CollectionDataService} from '../core/data/collection-data.service';
|
|
||||||
|
|
||||||
export interface FlatNode {
|
|
||||||
isExpandable: boolean;
|
|
||||||
name: string;
|
|
||||||
id: string;
|
|
||||||
level: number;
|
|
||||||
isExpanded?: boolean;
|
|
||||||
parent?: FlatNode;
|
|
||||||
payload: Community | Collection;
|
|
||||||
isShowMoreNode: boolean;
|
|
||||||
route?: string;
|
|
||||||
currentCommunityPage?: number;
|
|
||||||
currentCollectionPage?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const combineAndFlatten = (obsList: Array<Observable<FlatNode[]>>): Observable<FlatNode[]> =>
|
|
||||||
observableCombineLatest(...obsList).pipe(
|
|
||||||
map((matrix: FlatNode[][]) =>
|
|
||||||
matrix.reduce((combinedList, currentList: FlatNode[]) => [...combinedList, ...currentList]))
|
|
||||||
);
|
|
||||||
|
|
||||||
export const toFlatNode = (
|
|
||||||
c: Community | Collection,
|
|
||||||
level: number,
|
|
||||||
isExpanded: boolean,
|
|
||||||
parent?: FlatNode
|
|
||||||
): FlatNode => ({
|
|
||||||
isExpandable: c instanceof Community,
|
|
||||||
name: c.name,
|
|
||||||
id: c.id,
|
|
||||||
level: level,
|
|
||||||
isExpanded,
|
|
||||||
parent,
|
|
||||||
payload: c,
|
|
||||||
isShowMoreNode: false,
|
|
||||||
route: c instanceof Community ? getCommunityPageRoute(c.id) : getCollectionPageRoute(c.id),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const showMoreFlatNode = (
|
|
||||||
c: Community | Collection,
|
|
||||||
level: number,
|
|
||||||
parent?: FlatNode
|
|
||||||
): FlatNode => ({
|
|
||||||
isExpandable: false,
|
|
||||||
name: c.name,
|
|
||||||
id: c.id,
|
|
||||||
level: level,
|
|
||||||
isExpanded: false,
|
|
||||||
parent: parent,
|
|
||||||
payload: c,
|
|
||||||
isShowMoreNode: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class CommunityListAdapter {
|
|
||||||
|
|
||||||
payloads$: Array<Observable<PaginatedList<Community>>>;
|
|
||||||
|
|
||||||
topCommunitiesConfig: PaginationComponentOptions;
|
|
||||||
topCommunitiesSortConfig: SortOptions;
|
|
||||||
|
|
||||||
maxSubCommunitiesPerPage: number;
|
|
||||||
|
|
||||||
constructor(private communityDataService: CommunityDataService, private collectionDataService: CollectionDataService) {
|
|
||||||
this.topCommunitiesConfig = new PaginationComponentOptions();
|
|
||||||
this.topCommunitiesConfig.id = 'top-level-pagination';
|
|
||||||
this.topCommunitiesConfig.pageSize = 10;
|
|
||||||
this.topCommunitiesConfig.currentPage = 1;
|
|
||||||
this.topCommunitiesSortConfig = new SortOptions('dc.title', SortDirection.ASC);
|
|
||||||
this.initTopCommunityList()
|
|
||||||
|
|
||||||
this.maxSubCommunitiesPerPage = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
private initTopCommunityList(): void {
|
|
||||||
this.payloads$ = [this.communityDataService.findTop({
|
|
||||||
currentPage: this.topCommunitiesConfig.currentPage,
|
|
||||||
elementsPerPage: this.topCommunitiesConfig.pageSize,
|
|
||||||
sort: {field: this.topCommunitiesSortConfig.field, direction: this.topCommunitiesSortConfig.direction}
|
|
||||||
}).pipe(
|
|
||||||
take(1),
|
|
||||||
map((results) => results.payload),
|
|
||||||
)];
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
getNextPageTopCommunities(): void {
|
|
||||||
this.topCommunitiesConfig.currentPage = this.topCommunitiesConfig.currentPage + 1;
|
|
||||||
this.payloads$ = [...this.payloads$, this.communityDataService.findTop({
|
|
||||||
currentPage: this.topCommunitiesConfig.currentPage,
|
|
||||||
elementsPerPage: this.topCommunitiesConfig.pageSize,
|
|
||||||
sort: {field: this.topCommunitiesSortConfig.field, direction: this.topCommunitiesSortConfig.direction}
|
|
||||||
}).pipe(
|
|
||||||
take(1),
|
|
||||||
map((results) => results.payload),
|
|
||||||
)];
|
|
||||||
}
|
|
||||||
|
|
||||||
loadCommunities(expandedNodes: FlatNode[]): Observable<FlatNode[]> {
|
|
||||||
const res = this.payloads$.map((payload) => {
|
|
||||||
return payload.pipe(
|
|
||||||
take(1),
|
|
||||||
switchMap((result: PaginatedList<Community>) => {
|
|
||||||
return this.transformListOfCommunities(result, 0, null, expandedNodes);
|
|
||||||
}),
|
|
||||||
catchError(() => observableOf([])),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
return combineAndFlatten(res);
|
|
||||||
};
|
|
||||||
|
|
||||||
private transformListOfCommunities(listOfPaginatedCommunities: PaginatedList<Community>,
|
|
||||||
level: number,
|
|
||||||
parent: FlatNode,
|
|
||||||
expandedNodes: FlatNode[]): Observable<FlatNode[]> {
|
|
||||||
if (isNotEmpty(listOfPaginatedCommunities.page)) {
|
|
||||||
let currentPage = this.topCommunitiesConfig.currentPage;
|
|
||||||
if (isNotEmpty(parent)) {
|
|
||||||
currentPage = expandedNodes.find((node: FlatNode) => node.id === parent.id).currentCommunityPage;
|
|
||||||
}
|
|
||||||
const isNotAllCommunities = (listOfPaginatedCommunities.totalElements > (listOfPaginatedCommunities.elementsPerPage * currentPage));
|
|
||||||
let obsList = listOfPaginatedCommunities.page
|
|
||||||
.map((community: Community) => {
|
|
||||||
return this.transformCommunity(community, level, parent, expandedNodes)
|
|
||||||
});
|
|
||||||
if (isNotAllCommunities && listOfPaginatedCommunities.currentPage > currentPage) {
|
|
||||||
obsList = [...obsList, this.addPossibleShowMoreComunityFlatNode(level, parent)];
|
|
||||||
}
|
|
||||||
|
|
||||||
return combineAndFlatten(obsList);
|
|
||||||
} else {
|
|
||||||
return observableOf([]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private transformCommunity(community: Community, level: number, parent: FlatNode, expandedNodes: FlatNode[]): Observable<FlatNode[]> {
|
|
||||||
let isExpanded = false;
|
|
||||||
if (isNotEmpty(expandedNodes)) {
|
|
||||||
isExpanded = hasValue(expandedNodes.find((node) => (node.id === community.id)));
|
|
||||||
}
|
|
||||||
|
|
||||||
const communityFlatNode = toFlatNode(community, level, isExpanded, parent);
|
|
||||||
|
|
||||||
let obsList = [observableOf([communityFlatNode])];
|
|
||||||
|
|
||||||
if (isExpanded) {
|
|
||||||
const currentPage = expandedNodes.find((node: FlatNode) => node.id === community.id).currentCommunityPage;
|
|
||||||
let subcoms$ = [];
|
|
||||||
for (let i = 1; i <= currentPage ; i++) {
|
|
||||||
const p = this.communityDataService.findSubCommunitiesPerParentCommunity(community.uuid,{elementsPerPage: this.maxSubCommunitiesPerPage, currentPage: i})
|
|
||||||
.pipe(
|
|
||||||
filter((rd: RemoteData<PaginatedList<Community>>) => rd.hasSucceeded),
|
|
||||||
take(1),
|
|
||||||
switchMap((rd: RemoteData<PaginatedList<Community>>) =>
|
|
||||||
this.transformListOfCommunities(rd.payload, level + 1, communityFlatNode, expandedNodes))
|
|
||||||
|
|
||||||
);
|
|
||||||
subcoms$ = [...subcoms$, p];
|
|
||||||
}
|
|
||||||
|
|
||||||
obsList = [...obsList, combineAndFlatten(subcoms$)];
|
|
||||||
|
|
||||||
// need to be authorized (logged in) to receive collections this way
|
|
||||||
// const cols = this.collectionDataService.getAuthorizedCollectionByCommunity(community.uuid,{elementsPerPage: 2});
|
|
||||||
// cols.pipe(take(1)).subscribe((val) => console.log('cols:', val));
|
|
||||||
|
|
||||||
const collectionNodes$ = community.collections.pipe(
|
|
||||||
filter((rd: RemoteData<PaginatedList<Collection>>) => rd.hasSucceeded),
|
|
||||||
take(1),
|
|
||||||
map((rd: RemoteData<PaginatedList<Collection>>) => {
|
|
||||||
let nodes$ = rd.payload.page
|
|
||||||
.map((collection: Collection) => toFlatNode(collection, level + 1, false, parent));
|
|
||||||
if (rd.payload.elementsPerPage < rd.payload.totalElements) {
|
|
||||||
nodes$ = [...nodes$, this.addPossibleShowMoreCollectionFlatNode(level + 1, parent)];
|
|
||||||
}
|
|
||||||
return nodes$;
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
obsList = [...obsList, collectionNodes$];
|
|
||||||
}
|
|
||||||
|
|
||||||
return combineAndFlatten(obsList);
|
|
||||||
}
|
|
||||||
|
|
||||||
private addPossibleShowMoreComunityFlatNode(level: number, parent: FlatNode): Observable<FlatNode[]> {
|
|
||||||
const dummyCommunity = Object.assign(new Community(), {
|
|
||||||
id: '999999',
|
|
||||||
metadata: {
|
|
||||||
'dc.title': [
|
|
||||||
{ language: 'en_US', value: 'Test' }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return of([showMoreFlatNode(dummyCommunity, level, parent)]);
|
|
||||||
}
|
|
||||||
|
|
||||||
private addPossibleShowMoreCollectionFlatNode(level: number, parent: FlatNode): FlatNode {
|
|
||||||
const dummyCollection = Object.assign(new Collection(), {
|
|
||||||
id: '999999',
|
|
||||||
metadata: {
|
|
||||||
'dc.title': [
|
|
||||||
{ language: 'en_US', value: 'Test' }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return showMoreFlatNode(dummyCollection, level, parent);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
@@ -1,14 +1,14 @@
|
|||||||
import {CommunityListAdapter, FlatNode} from './community-list-adapter';
|
import { CommunityListService, FlatNode } from './community-list-service';
|
||||||
import {CollectionViewer, DataSource} from '@angular/cdk/typings/collections';
|
import { CollectionViewer, DataSource } from '@angular/cdk/typings/collections';
|
||||||
import {BehaviorSubject, Observable,} from 'rxjs';
|
import { BehaviorSubject, Observable, } from 'rxjs';
|
||||||
import {finalize, take,} from 'rxjs/operators';
|
import { finalize, take, } from 'rxjs/operators';
|
||||||
|
|
||||||
export class CommunityListDatasource implements DataSource<FlatNode> {
|
export class CommunityListDatasource implements DataSource<FlatNode> {
|
||||||
|
|
||||||
private communityList$ = new BehaviorSubject<FlatNode[]>([]);
|
private communityList$ = new BehaviorSubject<FlatNode[]>([]);
|
||||||
private loading$ = new BehaviorSubject<boolean>(false);
|
public loading$ = new BehaviorSubject<boolean>(false);
|
||||||
|
|
||||||
constructor(private communityListService: CommunityListAdapter) {
|
constructor(private communityListService: CommunityListService) {
|
||||||
}
|
}
|
||||||
|
|
||||||
connect(collectionViewer: CollectionViewer): Observable<FlatNode[]> {
|
connect(collectionViewer: CollectionViewer): Observable<FlatNode[]> {
|
||||||
@@ -22,7 +22,9 @@ export class CommunityListDatasource implements DataSource<FlatNode> {
|
|||||||
this.communityListService.loadCommunities(expandedNodes).pipe(
|
this.communityListService.loadCommunities(expandedNodes).pipe(
|
||||||
take(1),
|
take(1),
|
||||||
finalize(() => this.loading$.next(false)),
|
finalize(() => this.loading$.next(false)),
|
||||||
).subscribe((flatNodes: FlatNode[]) => this.communityList$.next(flatNodes));
|
).subscribe((flatNodes: FlatNode[]) => {
|
||||||
|
this.communityList$.next(flatNodes);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
disconnect(collectionViewer: CollectionViewer): void {
|
disconnect(collectionViewer: CollectionViewer): void {
|
||||||
|
@@ -1,6 +1,9 @@
|
|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
import { async, ComponentFixture, TestBed, inject } from '@angular/core/testing';
|
||||||
|
|
||||||
import { CommunityListPageComponent } from './community-list-page.component';
|
import { CommunityListPageComponent } from './community-list-page.component';
|
||||||
|
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { MockTranslateLoader } from '../shared/mocks/mock-translate-loader';
|
||||||
|
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||||
|
|
||||||
describe('CommunityListPageComponent', () => {
|
describe('CommunityListPageComponent', () => {
|
||||||
let component: CommunityListPageComponent;
|
let component: CommunityListPageComponent;
|
||||||
@@ -8,7 +11,19 @@ describe('CommunityListPageComponent', () => {
|
|||||||
|
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
declarations: [ CommunityListPageComponent ]
|
imports: [
|
||||||
|
TranslateModule.forRoot({
|
||||||
|
loader: {
|
||||||
|
provide: TranslateLoader,
|
||||||
|
useClass: MockTranslateLoader
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
declarations: [CommunityListPageComponent],
|
||||||
|
providers: [
|
||||||
|
CommunityListPageComponent,
|
||||||
|
],
|
||||||
|
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
||||||
})
|
})
|
||||||
.compileComponents();
|
.compileComponents();
|
||||||
}));
|
}));
|
||||||
@@ -19,7 +34,8 @@ describe('CommunityListPageComponent', () => {
|
|||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create', () => {
|
it('should create', inject([CommunityListPageComponent], (comp: CommunityListPageComponent) => {
|
||||||
expect(component).toBeTruthy();
|
expect(comp).toBeTruthy();
|
||||||
});
|
}));
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@@ -1,15 +1,9 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-community-list-page',
|
selector: 'ds-community-list-page',
|
||||||
templateUrl: './community-list-page.component.html',
|
templateUrl: './community-list-page.component.html',
|
||||||
styleUrls: ['./community-list-page.component.scss']
|
|
||||||
})
|
})
|
||||||
export class CommunityListPageComponent implements OnInit {
|
export class CommunityListPageComponent {
|
||||||
|
|
||||||
constructor() { }
|
|
||||||
|
|
||||||
ngOnInit() {
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -1,10 +1,10 @@
|
|||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { SharedModule } from '../shared/shared.module';
|
import { SharedModule } from '../shared/shared.module';
|
||||||
import {CommunityListPageComponent} from './community-list-page.component';
|
import { CommunityListPageComponent } from './community-list-page.component';
|
||||||
import {CommunityListPageRoutingModule} from './community-list-page.routing.module';
|
import { CommunityListPageRoutingModule } from './community-list-page.routing.module';
|
||||||
import { CommunityListComponent } from './community-list/community-list.component';
|
import { CommunityListComponent } from './community-list/community-list.component';
|
||||||
import {CdkTreeModule} from '@angular/cdk/tree';
|
import { CdkTreeModule } from '@angular/cdk/tree';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
|
@@ -1,17 +1,23 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { RouterModule } from '@angular/router';
|
import { RouterModule } from '@angular/router';
|
||||||
import {CdkTreeModule} from '@angular/cdk/tree';
|
import { CdkTreeModule } from '@angular/cdk/tree';
|
||||||
|
|
||||||
import {CommunityListPageComponent} from './community-list-page.component';
|
import { CommunityListPageComponent } from './community-list-page.component';
|
||||||
import {CommunityListAdapter} from './community-list-adapter';
|
import { CommunityListService } from './community-list-service';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
RouterModule.forChild([
|
RouterModule.forChild([
|
||||||
{ path: '', component: CommunityListPageComponent, pathMatch: 'full', data: { title: 'communityList.tabTitle' } }
|
{
|
||||||
|
path: '',
|
||||||
|
component: CommunityListPageComponent,
|
||||||
|
pathMatch: 'full',
|
||||||
|
data: { title: 'communityList.tabTitle' }
|
||||||
|
}
|
||||||
]),
|
]),
|
||||||
CdkTreeModule,
|
CdkTreeModule,
|
||||||
],
|
],
|
||||||
providers: [CommunityListAdapter]
|
providers: [CommunityListService]
|
||||||
})
|
})
|
||||||
export class CommunityListPageRoutingModule { }
|
export class CommunityListPageRoutingModule {
|
||||||
|
}
|
||||||
|
560
src/app/community-list-page/community-list-service.spec.ts
Normal file
560
src/app/community-list-page/community-list-service.spec.ts
Normal file
@@ -0,0 +1,560 @@
|
|||||||
|
import { TestBed, inject, async, fakeAsync } from '@angular/core/testing';
|
||||||
|
import { CommunityListService, FlatNode, toFlatNode } from './community-list-service';
|
||||||
|
import { CollectionDataService } from '../core/data/collection-data.service';
|
||||||
|
import { PaginatedList } from '../core/data/paginated-list';
|
||||||
|
import { PageInfo } from '../core/shared/page-info.model';
|
||||||
|
import { CommunityDataService } from '../core/data/community-data.service';
|
||||||
|
import {
|
||||||
|
createFailedRemoteDataObject$,
|
||||||
|
createSuccessfulRemoteDataObject$
|
||||||
|
} from '../shared/testing/utils';
|
||||||
|
import { Community } from '../core/shared/community.model';
|
||||||
|
import { Collection } from '../core/shared/collection.model';
|
||||||
|
import { map, take } from 'rxjs/operators';
|
||||||
|
import { FindListOptions } from '../core/data/request.models';
|
||||||
|
|
||||||
|
describe('CommunityListService', () => {
|
||||||
|
const standardElementsPerPage = 2;
|
||||||
|
let collectionDataServiceStub: any;
|
||||||
|
let communityDataServiceStub: any;
|
||||||
|
const mockSubcommunities1Page1 = [Object.assign(new Community(), {
|
||||||
|
id: 'ce64f48e-2c9b-411a-ac36-ee429c0e6a88',
|
||||||
|
uuid: 'ce64f48e-2c9b-411a-ac36-ee429c0e6a88',
|
||||||
|
}),
|
||||||
|
Object.assign(new Community(), {
|
||||||
|
id: '59ee713b-ee53-4220-8c3f-9860dc84fe33',
|
||||||
|
uuid: '59ee713b-ee53-4220-8c3f-9860dc84fe33',
|
||||||
|
})
|
||||||
|
];
|
||||||
|
const mockCollectionsPage1 = [
|
||||||
|
Object.assign(new Collection(), {
|
||||||
|
id: 'e9dbf393-7127-415f-8919-55be34a6e9ed',
|
||||||
|
uuid: 'e9dbf393-7127-415f-8919-55be34a6e9ed',
|
||||||
|
name: 'Collection 1'
|
||||||
|
}),
|
||||||
|
Object.assign(new Collection(), {
|
||||||
|
id: '59da2ff0-9bf4-45bf-88be-e35abd33f304',
|
||||||
|
uuid: '59da2ff0-9bf4-45bf-88be-e35abd33f304',
|
||||||
|
name: 'Collection 2'
|
||||||
|
})
|
||||||
|
];
|
||||||
|
const mockCollectionsPage2 = [
|
||||||
|
Object.assign(new Collection(), {
|
||||||
|
id: 'a5159760-f362-4659-9e81-e3253ad91ede',
|
||||||
|
uuid: 'a5159760-f362-4659-9e81-e3253ad91ede',
|
||||||
|
name: 'Collection 3'
|
||||||
|
}),
|
||||||
|
Object.assign(new Collection(), {
|
||||||
|
id: 'a392e16b-fcf2-400a-9a88-53ef7ecbdcd3',
|
||||||
|
uuid: 'a392e16b-fcf2-400a-9a88-53ef7ecbdcd3',
|
||||||
|
name: 'Collection 4'
|
||||||
|
})
|
||||||
|
];
|
||||||
|
const mockListOfTopCommunitiesPage1 = [
|
||||||
|
Object.assign(new Community(), {
|
||||||
|
id: '7669c72a-3f2a-451f-a3b9-9210e7a4c02f',
|
||||||
|
uuid: '7669c72a-3f2a-451f-a3b9-9210e7a4c02f',
|
||||||
|
subcommunities: createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), mockSubcommunities1Page1)),
|
||||||
|
collections: createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), [])),
|
||||||
|
}),
|
||||||
|
Object.assign(new Community(), {
|
||||||
|
id: '9076bd16-e69a-48d6-9e41-0238cb40d863',
|
||||||
|
uuid: '9076bd16-e69a-48d6-9e41-0238cb40d863',
|
||||||
|
subcommunities: createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), [])),
|
||||||
|
collections: createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), [...mockCollectionsPage1, ...mockCollectionsPage2])),
|
||||||
|
}),
|
||||||
|
Object.assign(new Community(), {
|
||||||
|
id: 'efbf25e1-2d8c-4c28-8f3e-2e04c215be24',
|
||||||
|
uuid: 'efbf25e1-2d8c-4c28-8f3e-2e04c215be24',
|
||||||
|
subcommunities: createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), [])),
|
||||||
|
collections: createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), [])),
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
const mockListOfTopCommunitiesPage2 = [
|
||||||
|
Object.assign(new Community(), {
|
||||||
|
id: 'c2e04392-3b8a-4dfa-976d-d76fb1b8a4b6',
|
||||||
|
uuid: 'c2e04392-3b8a-4dfa-976d-d76fb1b8a4b6',
|
||||||
|
subcommunities: createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), [])),
|
||||||
|
collections: createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), [])),
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
const mockTopCommunitiesWithChildrenArraysPage1 = [
|
||||||
|
{
|
||||||
|
id: '7669c72a-3f2a-451f-a3b9-9210e7a4c02f',
|
||||||
|
uuid: '7669c72a-3f2a-451f-a3b9-9210e7a4c02f',
|
||||||
|
subcommunities: mockSubcommunities1Page1,
|
||||||
|
collections: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '9076bd16-e69a-48d6-9e41-0238cb40d863',
|
||||||
|
uuid: '9076bd16-e69a-48d6-9e41-0238cb40d863',
|
||||||
|
subcommunities: [],
|
||||||
|
collections: [...mockCollectionsPage1, ...mockCollectionsPage2],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'efbf25e1-2d8c-4c28-8f3e-2e04c215be24',
|
||||||
|
uuid: 'efbf25e1-2d8c-4c28-8f3e-2e04c215be24',
|
||||||
|
subcommunities: [],
|
||||||
|
collections: [],
|
||||||
|
}];
|
||||||
|
const mockTopCommunitiesWithChildrenArraysPage2 = [
|
||||||
|
{
|
||||||
|
id: 'c2e04392-3b8a-4dfa-976d-d76fb1b8a4b6',
|
||||||
|
uuid: 'c2e04392-3b8a-4dfa-976d-d76fb1b8a4b6',
|
||||||
|
subcommunities: [],
|
||||||
|
collections: [],
|
||||||
|
}];
|
||||||
|
|
||||||
|
const allCommunities = [...mockTopCommunitiesWithChildrenArraysPage1, ...mockTopCommunitiesWithChildrenArraysPage2, ...mockSubcommunities1Page1];
|
||||||
|
|
||||||
|
let service: CommunityListService;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
communityDataServiceStub = {
|
||||||
|
findTop(options: FindListOptions = {}) {
|
||||||
|
const allTopComs = [...mockListOfTopCommunitiesPage1, ...mockListOfTopCommunitiesPage2]
|
||||||
|
let currentPage = options.currentPage;
|
||||||
|
const elementsPerPage = 3;
|
||||||
|
if (currentPage === undefined) {
|
||||||
|
currentPage = 1
|
||||||
|
}
|
||||||
|
const startPageIndex = (currentPage - 1) * elementsPerPage;
|
||||||
|
let endPageIndex = (currentPage * elementsPerPage);
|
||||||
|
if (endPageIndex > allTopComs.length) {
|
||||||
|
endPageIndex = allTopComs.length;
|
||||||
|
}
|
||||||
|
return createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), allTopComs.slice(startPageIndex, endPageIndex)));
|
||||||
|
},
|
||||||
|
findByParent(parentUUID: string, options: FindListOptions = {}) {
|
||||||
|
const foundCom = allCommunities.find((community) => (community.id === parentUUID));
|
||||||
|
let currentPage = options.currentPage;
|
||||||
|
let elementsPerPage = options.elementsPerPage;
|
||||||
|
if (currentPage === undefined) {
|
||||||
|
currentPage = 1
|
||||||
|
}
|
||||||
|
if (elementsPerPage === 0) {
|
||||||
|
return createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), (foundCom.subcommunities as [Community])));
|
||||||
|
}
|
||||||
|
elementsPerPage = standardElementsPerPage;
|
||||||
|
if (foundCom !== undefined && foundCom.subcommunities !== undefined) {
|
||||||
|
const coms = foundCom.subcommunities as [Community];
|
||||||
|
const startPageIndex = (currentPage - 1) * elementsPerPage;
|
||||||
|
let endPageIndex = (currentPage * elementsPerPage);
|
||||||
|
if (endPageIndex > coms.length) {
|
||||||
|
endPageIndex = coms.length;
|
||||||
|
}
|
||||||
|
return createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), coms.slice(startPageIndex, endPageIndex)));
|
||||||
|
} else {
|
||||||
|
return createFailedRemoteDataObject$();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
collectionDataServiceStub = {
|
||||||
|
findByParent(parentUUID: string, options: FindListOptions = {}) {
|
||||||
|
const foundCom = allCommunities.find((community) => (community.id === parentUUID));
|
||||||
|
let currentPage = options.currentPage;
|
||||||
|
let elementsPerPage = options.elementsPerPage;
|
||||||
|
if (currentPage === undefined) {
|
||||||
|
currentPage = 1
|
||||||
|
}
|
||||||
|
if (elementsPerPage === 0) {
|
||||||
|
return createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), (foundCom.collections as [Collection])));
|
||||||
|
}
|
||||||
|
elementsPerPage = standardElementsPerPage;
|
||||||
|
if (foundCom !== undefined && foundCom.collections !== undefined) {
|
||||||
|
const colls = foundCom.collections as [Collection];
|
||||||
|
const startPageIndex = (currentPage - 1) * elementsPerPage;
|
||||||
|
let endPageIndex = (currentPage * elementsPerPage);
|
||||||
|
if (endPageIndex > colls.length) {
|
||||||
|
endPageIndex = colls.length;
|
||||||
|
}
|
||||||
|
return createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), colls.slice(startPageIndex, endPageIndex)));
|
||||||
|
} else {
|
||||||
|
return createFailedRemoteDataObject$();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
providers: [CommunityListService,
|
||||||
|
{ provide: CollectionDataService, useValue: collectionDataServiceStub },
|
||||||
|
{ provide: CommunityDataService, useValue: communityDataServiceStub },],
|
||||||
|
});
|
||||||
|
service = new CommunityListService(communityDataServiceStub, collectionDataServiceStub);
|
||||||
|
}));
|
||||||
|
|
||||||
|
afterAll(() => service = new CommunityListService(communityDataServiceStub, collectionDataServiceStub));
|
||||||
|
|
||||||
|
it('should create', inject([CommunityListService], (serviceIn: CommunityListService) => {
|
||||||
|
expect(serviceIn).toBeTruthy();
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('getNextPageTopCommunities', () => {
|
||||||
|
describe('also load in second page of top communities', () => {
|
||||||
|
let flatNodeList;
|
||||||
|
describe('None expanded: should return list containing only flatnodes of the test top communities page 1 and 2', () => {
|
||||||
|
let findTopSpy;
|
||||||
|
beforeEach(() => {
|
||||||
|
findTopSpy = spyOn(communityDataServiceStub, 'findTop').and.callThrough();
|
||||||
|
service.getNextPageTopCommunities();
|
||||||
|
|
||||||
|
const sub = service.loadCommunities(null)
|
||||||
|
.subscribe((value) => flatNodeList = value);
|
||||||
|
sub.unsubscribe();
|
||||||
|
});
|
||||||
|
it('flatnode list should contain just flatnodes of top community list page 1 and 2', () => {
|
||||||
|
expect(findTopSpy).toHaveBeenCalled();
|
||||||
|
expect(flatNodeList.length).toEqual(mockListOfTopCommunitiesPage1.length + mockListOfTopCommunitiesPage2.length);
|
||||||
|
mockListOfTopCommunitiesPage1.map((community) => {
|
||||||
|
expect(flatNodeList.find((flatnode) => (flatnode.id === community.id))).toBeTruthy();
|
||||||
|
});
|
||||||
|
mockListOfTopCommunitiesPage2.map((community) => {
|
||||||
|
expect(flatNodeList.find((flatnode) => (flatnode.id === community.id))).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('loadCommunities', () => {
|
||||||
|
describe('should transform all communities in a list of flatnodes with possible subcoms and collections as subflatnodes if they\'re expanded', () => {
|
||||||
|
let flatNodeList;
|
||||||
|
describe('None expanded: should return list containing only flatnodes of the test top communities', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
const sub = service.loadCommunities(null)
|
||||||
|
.pipe(take(1)).subscribe((value) => flatNodeList = value);
|
||||||
|
sub.unsubscribe();
|
||||||
|
});
|
||||||
|
it('length of flatnode list should be as big as top community list', () => {
|
||||||
|
expect(flatNodeList.length).toEqual(mockListOfTopCommunitiesPage1.length);
|
||||||
|
});
|
||||||
|
it('flatnode list should contain flatNode representations of top communities', () => {
|
||||||
|
mockListOfTopCommunitiesPage1.map((community) => {
|
||||||
|
expect(flatNodeList.find((flatnode) => (flatnode.id === community.id))).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('none of the flatnodes in the list should be expanded', () => {
|
||||||
|
flatNodeList.map((flatnode: FlatNode) => {
|
||||||
|
expect(flatnode.isExpanded).toEqual(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('All top expanded, all page 1: should return list containing flatnodes of the communities in the test list and all its possible page-limited children (subcommunities and collections)', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
const expandedNodes = [];
|
||||||
|
mockListOfTopCommunitiesPage1.map((community: Community) => {
|
||||||
|
const communityFlatNode = toFlatNode(community, true, 0, true, null);
|
||||||
|
communityFlatNode.currentCollectionPage = 1;
|
||||||
|
communityFlatNode.currentCommunityPage = 1;
|
||||||
|
expandedNodes.push(communityFlatNode);
|
||||||
|
});
|
||||||
|
const sub = service.loadCommunities(expandedNodes)
|
||||||
|
.pipe(take(1)).subscribe((value) => flatNodeList = value);
|
||||||
|
sub.unsubscribe();
|
||||||
|
});
|
||||||
|
it('length of flatnode list should be as big as top community list and size of its possible page-limited children', () => {
|
||||||
|
expect(flatNodeList.length).toEqual(mockListOfTopCommunitiesPage1.length + mockSubcommunities1Page1.length + mockSubcommunities1Page1.length);
|
||||||
|
});
|
||||||
|
it('flatnode list should contain flatNode representations of all page-limited children', () => {
|
||||||
|
mockSubcommunities1Page1.map((subcommunity) => {
|
||||||
|
expect(flatNodeList.find((flatnode) => (flatnode.id === subcommunity.id))).toBeTruthy();
|
||||||
|
});
|
||||||
|
mockCollectionsPage1.map((collection) => {
|
||||||
|
expect(flatNodeList.find((flatnode) => (flatnode.id === collection.id))).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('Just first top comm expanded, all page 1: should return list containing flatnodes of the communities in the test list and all its possible page-limited children (subcommunities and collections)', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
const communityFlatNode = toFlatNode(mockListOfTopCommunitiesPage1[0], true, 0, true, null);
|
||||||
|
communityFlatNode.currentCollectionPage = 1;
|
||||||
|
communityFlatNode.currentCommunityPage = 1;
|
||||||
|
const expandedNodes = [communityFlatNode];
|
||||||
|
const sub = service.loadCommunities(expandedNodes)
|
||||||
|
.pipe(take(1)).subscribe((value) => flatNodeList = value);
|
||||||
|
sub.unsubscribe();
|
||||||
|
});
|
||||||
|
it('length of flatnode list should be as big as top community list and size of page-limited children of first top community', () => {
|
||||||
|
expect(flatNodeList.length).toEqual(mockListOfTopCommunitiesPage1.length + mockSubcommunities1Page1.length);
|
||||||
|
});
|
||||||
|
it('flatnode list should contain flatNode representations of all page-limited children of first top community', () => {
|
||||||
|
mockSubcommunities1Page1.map((subcommunity) => {
|
||||||
|
expect(flatNodeList.find((flatnode) => (flatnode.id === subcommunity.id))).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('Just second top comm expanded, collections at page 2: should return list containing flatnodes of the communities in the test list and all its possible page-limited children (subcommunities and collections)', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
const communityFlatNode = toFlatNode(mockListOfTopCommunitiesPage1[1], true, 0, true, null);
|
||||||
|
communityFlatNode.currentCollectionPage = 2;
|
||||||
|
communityFlatNode.currentCommunityPage = 1;
|
||||||
|
const expandedNodes = [communityFlatNode];
|
||||||
|
const sub = service.loadCommunities(expandedNodes)
|
||||||
|
.pipe(take(1)).subscribe((value) => flatNodeList = value);
|
||||||
|
sub.unsubscribe();
|
||||||
|
});
|
||||||
|
it('length of flatnode list should be as big as top community list and size of page-limited children of second top community', () => {
|
||||||
|
expect(flatNodeList.length).toEqual(mockListOfTopCommunitiesPage1.length + mockCollectionsPage1.length + mockCollectionsPage2.length);
|
||||||
|
});
|
||||||
|
it('flatnode list should contain flatNode representations of all page-limited children of first top community', () => {
|
||||||
|
mockCollectionsPage1.map((collection) => {
|
||||||
|
expect(flatNodeList.find((flatnode) => (flatnode.id === collection.id))).toBeTruthy();
|
||||||
|
});
|
||||||
|
mockCollectionsPage2.map((collection) => {
|
||||||
|
expect(flatNodeList.find((flatnode) => (flatnode.id === collection.id))).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('transformListOfCommunities', () => {
|
||||||
|
describe('should transform list of communities in a list of flatnodes with possible subcoms and collections as subflatnodes if they\'re expanded', () => {
|
||||||
|
describe('list of communities with possible children', () => {
|
||||||
|
const listOfCommunities = mockListOfTopCommunitiesPage1;
|
||||||
|
let flatNodeList;
|
||||||
|
describe('None expanded: should return list containing only flatnodes of the communities in the test list', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
const sub = service.transformListOfCommunities(new PaginatedList(new PageInfo(), listOfCommunities), 0, null, null)
|
||||||
|
.pipe(take(1)).subscribe((value) => flatNodeList = value);
|
||||||
|
sub.unsubscribe();
|
||||||
|
});
|
||||||
|
it('length of flatnode list should be as big as community test list', () => {
|
||||||
|
expect(flatNodeList.length).toEqual(listOfCommunities.length);
|
||||||
|
});
|
||||||
|
it('flatnode list should contain flatNode representations of all communities from test list', () => {
|
||||||
|
listOfCommunities.map((community) => {
|
||||||
|
expect(flatNodeList.find((flatnode) => (flatnode.id === community.id))).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('none of the flatnodes in the list should be expanded', () => {
|
||||||
|
flatNodeList.map((flatnode: FlatNode) => {
|
||||||
|
expect(flatnode.isExpanded).toEqual(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('All top expanded, all page 1: should return list containing flatnodes of the communities in the test list and all its possible page-limited children (subcommunities and collections)', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
const expandedNodes = [];
|
||||||
|
listOfCommunities.map((community: Community) => {
|
||||||
|
const communityFlatNode = toFlatNode(community, true, 0, true, null);
|
||||||
|
communityFlatNode.currentCollectionPage = 1;
|
||||||
|
communityFlatNode.currentCommunityPage = 1;
|
||||||
|
expandedNodes.push(communityFlatNode);
|
||||||
|
});
|
||||||
|
const sub = service.transformListOfCommunities(new PaginatedList(new PageInfo(), listOfCommunities), 0, null, expandedNodes)
|
||||||
|
.pipe(take(1)).subscribe((value) => flatNodeList = value);
|
||||||
|
sub.unsubscribe();
|
||||||
|
});
|
||||||
|
it('length of flatnode list should be as big as community test list and size of its possible children', () => {
|
||||||
|
expect(flatNodeList.length).toEqual(listOfCommunities.length + mockSubcommunities1Page1.length + mockSubcommunities1Page1.length);
|
||||||
|
});
|
||||||
|
it('flatnode list should contain flatNode representations of all children', () => {
|
||||||
|
mockSubcommunities1Page1.map((subcommunity) => {
|
||||||
|
expect(flatNodeList.find((flatnode) => (flatnode.id === subcommunity.id))).toBeTruthy();
|
||||||
|
});
|
||||||
|
mockSubcommunities1Page1.map((collection) => {
|
||||||
|
expect(flatNodeList.find((flatnode) => (flatnode.id === collection.id))).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('transformCommunity', () => {
|
||||||
|
describe('should transform community in list of flatnodes with possible subcoms and collections as subflatnodes if its expanded', () => {
|
||||||
|
describe('topcommunity without subcoms or collections, unexpanded', () => {
|
||||||
|
const communityWithNoSubcomsOrColls = Object.assign(new Community(), {
|
||||||
|
id: 'efbf25e1-2d8c-4c28-8f3e-2e04c215be24',
|
||||||
|
uuid: 'efbf25e1-2d8c-4c28-8f3e-2e04c215be24',
|
||||||
|
subcommunities: createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), [])),
|
||||||
|
collections: createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), [])),
|
||||||
|
metadata: {
|
||||||
|
'dc.description': [{ language: 'en_US', value: 'no subcoms, 2 coll' }],
|
||||||
|
'dc.title': [{ language: 'en_US', value: 'Community 2' }]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let flatNodeList;
|
||||||
|
describe('should return list containing only flatnode corresponding to that community', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
const sub = service.transformCommunity(communityWithNoSubcomsOrColls, 0, null, null)
|
||||||
|
.pipe(take(1)).subscribe((value) => flatNodeList = value);
|
||||||
|
sub.unsubscribe();
|
||||||
|
});
|
||||||
|
it('length of flatnode list should be 1', () => {
|
||||||
|
expect(flatNodeList.length).toEqual(1);
|
||||||
|
});
|
||||||
|
it('flatnode list only element should be flatNode of test community', () => {
|
||||||
|
expect(flatNodeList[0].id).toEqual(communityWithNoSubcomsOrColls.id);
|
||||||
|
});
|
||||||
|
it('flatnode from test community is not expanded', () => {
|
||||||
|
expect(flatNodeList[0].isExpanded).toEqual(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('topcommunity with subcoms or collections, unexpanded', () => {
|
||||||
|
const communityWithSubcoms = Object.assign(new Community(), {
|
||||||
|
id: '7669c72a-3f2a-451f-a3b9-9210e7a4c02f',
|
||||||
|
uuid: '7669c72a-3f2a-451f-a3b9-9210e7a4c02f',
|
||||||
|
subcommunities: createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), mockSubcommunities1Page1)),
|
||||||
|
collections: createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), [])),
|
||||||
|
metadata: {
|
||||||
|
'dc.description': [{ language: 'en_US', value: '2 subcoms, no coll' }],
|
||||||
|
'dc.title': [{ language: 'en_US', value: 'Community 1' }]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let flatNodeList;
|
||||||
|
describe('should return list containing only flatnode corresponding to that community', () => {
|
||||||
|
beforeAll(() => {
|
||||||
|
const sub = service.transformCommunity(communityWithSubcoms, 0, null, null)
|
||||||
|
.pipe(take(1)).subscribe((value) => flatNodeList = value);
|
||||||
|
sub.unsubscribe();
|
||||||
|
});
|
||||||
|
it('length of flatnode list should be 1', () => {
|
||||||
|
expect(flatNodeList.length).toEqual(1);
|
||||||
|
});
|
||||||
|
it('flatnode list only element should be flatNode of test community', () => {
|
||||||
|
expect(flatNodeList[0].id).toEqual(communityWithSubcoms.id);
|
||||||
|
});
|
||||||
|
it('flatnode from test community is not expanded', () => {
|
||||||
|
expect(flatNodeList[0].isExpanded).toEqual(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('topcommunity with subcoms, expanded, first page for all', () => {
|
||||||
|
describe('should return list containing flatnodes of that community, its possible subcommunities and its possible collections', () => {
|
||||||
|
const communityWithSubcoms = Object.assign(new Community(), {
|
||||||
|
id: '7669c72a-3f2a-451f-a3b9-9210e7a4c02f',
|
||||||
|
uuid: '7669c72a-3f2a-451f-a3b9-9210e7a4c02f',
|
||||||
|
subcommunities: createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), mockSubcommunities1Page1)),
|
||||||
|
collections: createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), [])),
|
||||||
|
metadata: {
|
||||||
|
'dc.description': [{ language: 'en_US', value: '2 subcoms, no coll' }],
|
||||||
|
'dc.title': [{ language: 'en_US', value: 'Community 1' }]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let flatNodeList;
|
||||||
|
beforeEach(() => {
|
||||||
|
const communityFlatNode = toFlatNode(communityWithSubcoms, true, 0, true, null);
|
||||||
|
communityFlatNode.currentCollectionPage = 1;
|
||||||
|
communityFlatNode.currentCommunityPage = 1;
|
||||||
|
const expandedNodes = [communityFlatNode];
|
||||||
|
const sub = service.transformCommunity(communityWithSubcoms, 0, null, expandedNodes)
|
||||||
|
.pipe(take(1)).subscribe((value) => flatNodeList = value);
|
||||||
|
sub.unsubscribe();
|
||||||
|
});
|
||||||
|
it('list of flatnodes is length is 1 + nrOfSubcoms & first flatnode is of expanded test community', () => {
|
||||||
|
expect(flatNodeList.length).toEqual(1 + mockSubcommunities1Page1.length);
|
||||||
|
expect(flatNodeList[0].isExpanded).toEqual(true);
|
||||||
|
expect(flatNodeList[0].id).toEqual(communityWithSubcoms.id);
|
||||||
|
});
|
||||||
|
it('list of flatnodes contains flatnodes for all subcoms of test community', () => {
|
||||||
|
mockSubcommunities1Page1.map((subcommunity) => {
|
||||||
|
expect(flatNodeList.find((flatnode) => (flatnode.id === subcommunity.id))).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('the subcoms of the test community are a level higher than the parent community', () => {
|
||||||
|
mockSubcommunities1Page1.map((subcommunity) => {
|
||||||
|
expect((flatNodeList.find((flatnode) => (flatnode.id === subcommunity.id))).level).toEqual(flatNodeList[0].level + 1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('topcommunity with collections, expanded, on second page of collections', () => {
|
||||||
|
describe('should return list containing flatnodes of that community, its collections of the first two pages', () => {
|
||||||
|
const communityWithCollections = Object.assign(new Community(), {
|
||||||
|
id: '9076bd16-e69a-48d6-9e41-0238cb40d863',
|
||||||
|
uuid: '9076bd16-e69a-48d6-9e41-0238cb40d863',
|
||||||
|
subcommunities: createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), [])),
|
||||||
|
collections: createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), [...mockCollectionsPage1, ...mockCollectionsPage2])),
|
||||||
|
metadata: {
|
||||||
|
'dc.description': [{ language: 'en_US', value: '2 subcoms, no coll' }],
|
||||||
|
'dc.title': [{ language: 'en_US', value: 'Community 1' }]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let flatNodeList;
|
||||||
|
beforeEach(() => {
|
||||||
|
const communityFlatNode = toFlatNode(communityWithCollections, true, 0, true, null);
|
||||||
|
communityFlatNode.currentCollectionPage = 2;
|
||||||
|
communityFlatNode.currentCommunityPage = 1;
|
||||||
|
const expandedNodes = [communityFlatNode];
|
||||||
|
const sub = service.transformCommunity(communityWithCollections, 0, null, expandedNodes)
|
||||||
|
.pipe(take(1)).subscribe((value) => flatNodeList = value);
|
||||||
|
sub.unsubscribe();
|
||||||
|
});
|
||||||
|
it('list of flatnodes is length is 1 + nrOfCollections & first flatnode is of expanded test community', () => {
|
||||||
|
expect(flatNodeList.length).toEqual(1 + mockCollectionsPage1.length + mockCollectionsPage2.length);
|
||||||
|
expect(flatNodeList[0].isExpanded).toEqual(true);
|
||||||
|
expect(flatNodeList[0].id).toEqual(communityWithCollections.id);
|
||||||
|
});
|
||||||
|
it('list of flatnodes contains flatnodes for all subcolls (first 2 pages) of test community', () => {
|
||||||
|
mockCollectionsPage1.map((collection) => {
|
||||||
|
expect(flatNodeList.find((flatnode) => (flatnode.id === collection.id))).toBeTruthy();
|
||||||
|
});
|
||||||
|
mockCollectionsPage2.map((collection) => {
|
||||||
|
expect(flatNodeList.find((flatnode) => (flatnode.id === collection.id))).toBeTruthy();
|
||||||
|
})
|
||||||
|
});
|
||||||
|
it('the collections of the test community are a level higher than the parent community', () => {
|
||||||
|
mockCollectionsPage1.map((collection) => {
|
||||||
|
expect((flatNodeList.find((flatnode) => (flatnode.id === collection.id))).level).toEqual(flatNodeList[0].level + 1);
|
||||||
|
});
|
||||||
|
mockCollectionsPage2.map((collection) => {
|
||||||
|
expect((flatNodeList.find((flatnode) => (flatnode.id === collection.id))).level).toEqual(flatNodeList[0].level + 1);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getIsExpandable', () => {
|
||||||
|
describe('should return true', () => {
|
||||||
|
it('if community has subcommunities', () => {
|
||||||
|
const communityWithSubcoms = Object.assign(new Community(), {
|
||||||
|
id: '7669c72a-3f2a-451f-a3b9-9210e7a4c02f',
|
||||||
|
uuid: '7669c72a-3f2a-451f-a3b9-9210e7a4c02f',
|
||||||
|
subcommunities: createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), mockSubcommunities1Page1)),
|
||||||
|
collections: createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), [])),
|
||||||
|
metadata: {
|
||||||
|
'dc.description': [{ language: 'en_US', value: '2 subcoms, no coll' }],
|
||||||
|
'dc.title': [{ language: 'en_US', value: 'Community 1' }]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
expect(service.getIsExpandable(communityWithSubcoms)).toEqual(true);
|
||||||
|
});
|
||||||
|
it('if community has collections', () => {
|
||||||
|
const communityWithCollections = Object.assign(new Community(), {
|
||||||
|
id: '9076bd16-e69a-48d6-9e41-0238cb40d863',
|
||||||
|
uuid: '9076bd16-e69a-48d6-9e41-0238cb40d863',
|
||||||
|
subcommunities: createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), [])),
|
||||||
|
collections: createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), mockCollectionsPage1)),
|
||||||
|
metadata: {
|
||||||
|
'dc.description': [{ language: 'en_US', value: 'no subcoms, 2 coll' }],
|
||||||
|
'dc.title': [{ language: 'en_US', value: 'Community 2' }]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
expect(service.getIsExpandable(communityWithCollections)).toEqual(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('should return false', () => {
|
||||||
|
it('if community has neither subcommunities nor collections', () => {
|
||||||
|
const communityWithNoSubcomsOrColls = Object.assign(new Community(), {
|
||||||
|
id: 'efbf25e1-2d8c-4c28-8f3e-2e04c215be24',
|
||||||
|
uuid: 'efbf25e1-2d8c-4c28-8f3e-2e04c215be24',
|
||||||
|
subcommunities: createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), [])),
|
||||||
|
collections: createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), [])),
|
||||||
|
metadata: {
|
||||||
|
'dc.description': [{ language: 'en_US', value: 'no subcoms, no coll' }],
|
||||||
|
'dc.title': [{ language: 'en_US', value: 'Community 3' }]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
expect(service.getIsExpandable(communityWithNoSubcomsOrColls)).toEqual(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
288
src/app/community-list-page/community-list-service.ts
Normal file
288
src/app/community-list-page/community-list-service.ts
Normal file
@@ -0,0 +1,288 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { combineLatest as observableCombineLatest } from 'rxjs/internal/observable/combineLatest';
|
||||||
|
import { Observable, of as observableOf } from 'rxjs';
|
||||||
|
import { CommunityDataService } from '../core/data/community-data.service';
|
||||||
|
import { PaginationComponentOptions } from '../shared/pagination/pagination-component-options.model';
|
||||||
|
import { SortDirection, SortOptions } from '../core/cache/models/sort-options.model';
|
||||||
|
import { catchError, filter, map, switchMap, take, tap } from 'rxjs/operators';
|
||||||
|
import { Community } from '../core/shared/community.model';
|
||||||
|
import { Collection } from '../core/shared/collection.model';
|
||||||
|
import { hasValue, isNotEmpty } from '../shared/empty.util';
|
||||||
|
import { RemoteData } from '../core/data/remote-data';
|
||||||
|
import { PaginatedList } from '../core/data/paginated-list';
|
||||||
|
import { getCommunityPageRoute } from '../+community-page/community-page-routing.module';
|
||||||
|
import { getCollectionPageRoute } from '../+collection-page/collection-page-routing.module';
|
||||||
|
import { CollectionDataService } from '../core/data/collection-data.service';
|
||||||
|
|
||||||
|
export interface FlatNode {
|
||||||
|
isExpandable: boolean;
|
||||||
|
name: string;
|
||||||
|
id: string;
|
||||||
|
level: number;
|
||||||
|
isExpanded?: boolean;
|
||||||
|
parent?: FlatNode;
|
||||||
|
payload: Community | Collection | ShowMoreFlatNode;
|
||||||
|
isShowMoreNode: boolean;
|
||||||
|
route?: string;
|
||||||
|
currentCommunityPage?: number;
|
||||||
|
currentCollectionPage?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ShowMoreFlatNode {
|
||||||
|
}
|
||||||
|
|
||||||
|
export const combineAndFlatten = (obsList: Array<Observable<FlatNode[]>>): Observable<FlatNode[]> =>
|
||||||
|
observableCombineLatest(...obsList).pipe(
|
||||||
|
map((matrix: FlatNode[][]) =>
|
||||||
|
matrix.reduce((combinedList, currentList: FlatNode[]) => [...combinedList, ...currentList]))
|
||||||
|
);
|
||||||
|
|
||||||
|
export const toFlatNode = (
|
||||||
|
c: Community | Collection,
|
||||||
|
isExpandable: boolean,
|
||||||
|
level: number,
|
||||||
|
isExpanded: boolean,
|
||||||
|
parent?: FlatNode
|
||||||
|
): FlatNode => ({
|
||||||
|
isExpandable: isExpandable,
|
||||||
|
name: c.name,
|
||||||
|
id: c.id,
|
||||||
|
level: level,
|
||||||
|
isExpanded,
|
||||||
|
parent,
|
||||||
|
payload: c,
|
||||||
|
isShowMoreNode: false,
|
||||||
|
route: c instanceof Community ? getCommunityPageRoute(c.id) : getCollectionPageRoute(c.id),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const showMoreFlatNode = (
|
||||||
|
id: string,
|
||||||
|
level: number,
|
||||||
|
parent: FlatNode
|
||||||
|
): FlatNode => ({
|
||||||
|
isExpandable: false,
|
||||||
|
name: 'Show More Flatnode',
|
||||||
|
id: id,
|
||||||
|
level: level,
|
||||||
|
isExpanded: false,
|
||||||
|
parent: parent,
|
||||||
|
payload: new ShowMoreFlatNode(),
|
||||||
|
isShowMoreNode: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// tslint:disable-next-line:max-classes-per-file
|
||||||
|
@Injectable()
|
||||||
|
export class CommunityListService {
|
||||||
|
|
||||||
|
// page-limited list of top-level communities
|
||||||
|
payloads$: Array<Observable<PaginatedList<Community>>>;
|
||||||
|
|
||||||
|
topCommunitiesConfig: PaginationComponentOptions;
|
||||||
|
topCommunitiesSortConfig: SortOptions;
|
||||||
|
|
||||||
|
maxSubCommunitiesPerPage: number;
|
||||||
|
maxCollectionsPerPage: number;
|
||||||
|
|
||||||
|
constructor(private communityDataService: CommunityDataService, private collectionDataService: CollectionDataService) {
|
||||||
|
this.topCommunitiesConfig = new PaginationComponentOptions();
|
||||||
|
this.topCommunitiesConfig.id = 'top-level-pagination';
|
||||||
|
this.topCommunitiesConfig.pageSize = 10;
|
||||||
|
this.topCommunitiesConfig.currentPage = 1;
|
||||||
|
this.topCommunitiesSortConfig = new SortOptions('dc.title', SortDirection.ASC);
|
||||||
|
this.initTopCommunityList();
|
||||||
|
|
||||||
|
this.maxSubCommunitiesPerPage = 3;
|
||||||
|
this.maxCollectionsPerPage = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increases the payload so it contains the next page of top level communities
|
||||||
|
*/
|
||||||
|
getNextPageTopCommunities(): void {
|
||||||
|
this.topCommunitiesConfig.currentPage = this.topCommunitiesConfig.currentPage + 1;
|
||||||
|
this.payloads$ = [...this.payloads$, this.communityDataService.findTop({
|
||||||
|
currentPage: this.topCommunitiesConfig.currentPage,
|
||||||
|
elementsPerPage: this.topCommunitiesConfig.pageSize,
|
||||||
|
sort: {
|
||||||
|
field: this.topCommunitiesSortConfig.field,
|
||||||
|
direction: this.topCommunitiesSortConfig.direction
|
||||||
|
}
|
||||||
|
}).pipe(
|
||||||
|
take(1),
|
||||||
|
map((results) => results.payload),
|
||||||
|
)];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets all top communities, limited by page, and transforms this in a list of flatnodes.
|
||||||
|
* @param expandedNodes List of expanded nodes; if a node is not expanded its subcommunities and collections need not be added to the list
|
||||||
|
*/
|
||||||
|
loadCommunities(expandedNodes: FlatNode[]): Observable<FlatNode[]> {
|
||||||
|
const res = this.payloads$.map((payload) => {
|
||||||
|
return payload.pipe(
|
||||||
|
take(1),
|
||||||
|
switchMap((result: PaginatedList<Community>) => {
|
||||||
|
return this.transformListOfCommunities(result, 0, null, expandedNodes);
|
||||||
|
}),
|
||||||
|
catchError(() => observableOf([])),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
return combineAndFlatten(res);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Puts the initial top level communities in a list to be called upon
|
||||||
|
*/
|
||||||
|
private initTopCommunityList(): void {
|
||||||
|
this.payloads$ = [this.communityDataService.findTop({
|
||||||
|
currentPage: this.topCommunitiesConfig.currentPage,
|
||||||
|
elementsPerPage: this.topCommunitiesConfig.pageSize,
|
||||||
|
sort: {
|
||||||
|
field: this.topCommunitiesSortConfig.field,
|
||||||
|
direction: this.topCommunitiesSortConfig.direction
|
||||||
|
}
|
||||||
|
}).pipe(
|
||||||
|
take(1),
|
||||||
|
map((results) => results.payload),
|
||||||
|
)];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transforms a list of communities to a list of FlatNodes according to the instructions detailed in transformCommunity
|
||||||
|
* @param listOfPaginatedCommunities Paginated list of communities to be transformed
|
||||||
|
* @param level Level the tree is currently at
|
||||||
|
* @param parent FlatNode of the parent of this list of communities
|
||||||
|
* @param expandedNodes List of expanded nodes; if a node is not expanded its subcommunities and collections need not be added to the list
|
||||||
|
*/
|
||||||
|
public transformListOfCommunities(listOfPaginatedCommunities: PaginatedList<Community>,
|
||||||
|
level: number,
|
||||||
|
parent: FlatNode,
|
||||||
|
expandedNodes: FlatNode[]): Observable<FlatNode[]> {
|
||||||
|
if (isNotEmpty(listOfPaginatedCommunities.page)) {
|
||||||
|
let currentPage = this.topCommunitiesConfig.currentPage;
|
||||||
|
if (isNotEmpty(parent)) {
|
||||||
|
currentPage = expandedNodes.find((node: FlatNode) => node.id === parent.id).currentCommunityPage;
|
||||||
|
}
|
||||||
|
const isNotAllCommunities = (listOfPaginatedCommunities.totalElements > (listOfPaginatedCommunities.elementsPerPage * currentPage));
|
||||||
|
let obsList = listOfPaginatedCommunities.page
|
||||||
|
.map((community: Community) => {
|
||||||
|
return this.transformCommunity(community, level, parent, expandedNodes)
|
||||||
|
});
|
||||||
|
if (isNotAllCommunities && listOfPaginatedCommunities.currentPage > currentPage) {
|
||||||
|
obsList = [...obsList, observableOf([showMoreFlatNode('community', level, parent)])];
|
||||||
|
}
|
||||||
|
|
||||||
|
return combineAndFlatten(obsList);
|
||||||
|
} else {
|
||||||
|
return observableOf([]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transforms a community in a list of FlatNodes containing firstly a flatnode of the community itself,
|
||||||
|
* followed by flatNodes of its possible subcommunities and collection
|
||||||
|
* It gets called recursively for each subcommunity to add its subcommunities and collections to the list
|
||||||
|
* Number of subcommunities and collections added, is dependant on the current page the parent is at for respectively subcommunities and collections.
|
||||||
|
* @param community Community being transformed
|
||||||
|
* @param level Depth of the community in the list, subcommunities and collections go one level deeper
|
||||||
|
* @param parent Flatnode of the parent community
|
||||||
|
* @param expandedNodes List of nodes which are expanded, if node is not expanded, it need not add its page-limited subcommunities or collections
|
||||||
|
*/
|
||||||
|
public transformCommunity(community: Community, level: number, parent: FlatNode, expandedNodes: FlatNode[]): Observable<FlatNode[]> {
|
||||||
|
let isExpanded = false;
|
||||||
|
if (isNotEmpty(expandedNodes)) {
|
||||||
|
isExpanded = hasValue(expandedNodes.find((node) => (node.id === community.id)));
|
||||||
|
}
|
||||||
|
|
||||||
|
const isExpandable = this.getIsExpandable(community);
|
||||||
|
|
||||||
|
const communityFlatNode = toFlatNode(community, isExpandable, level, isExpanded, parent);
|
||||||
|
|
||||||
|
let obsList = [observableOf([communityFlatNode])];
|
||||||
|
|
||||||
|
if (isExpanded) {
|
||||||
|
const currentCommunityPage = expandedNodes.find((node: FlatNode) => node.id === community.id).currentCommunityPage;
|
||||||
|
let subcoms = [];
|
||||||
|
for (let i = 1; i <= currentCommunityPage; i++) {
|
||||||
|
const nextSetOfSubcommunitiesPage = this.communityDataService.findByParent(community.uuid, {
|
||||||
|
elementsPerPage: this.maxSubCommunitiesPerPage,
|
||||||
|
currentPage: i
|
||||||
|
})
|
||||||
|
.pipe(
|
||||||
|
filter((rd: RemoteData<PaginatedList<Community>>) => rd.hasSucceeded),
|
||||||
|
take(1),
|
||||||
|
switchMap((rd: RemoteData<PaginatedList<Community>>) =>
|
||||||
|
this.transformListOfCommunities(rd.payload, level + 1, communityFlatNode, expandedNodes))
|
||||||
|
);
|
||||||
|
|
||||||
|
subcoms = [...subcoms, nextSetOfSubcommunitiesPage];
|
||||||
|
}
|
||||||
|
|
||||||
|
obsList = [...obsList, combineAndFlatten(subcoms)];
|
||||||
|
|
||||||
|
const currentCollectionPage = expandedNodes.find((node: FlatNode) => node.id === community.id).currentCollectionPage;
|
||||||
|
let collections = [];
|
||||||
|
for (let i = 1; i <= currentCollectionPage; i++) {
|
||||||
|
const nextSetOfCollectionsPage = this.collectionDataService.findByParent(community.uuid, {
|
||||||
|
elementsPerPage: this.maxCollectionsPerPage,
|
||||||
|
currentPage: i
|
||||||
|
})
|
||||||
|
.pipe(
|
||||||
|
filter((rd: RemoteData<PaginatedList<Collection>>) => rd.hasSucceeded),
|
||||||
|
take(1),
|
||||||
|
map((rd: RemoteData<PaginatedList<Collection>>) => {
|
||||||
|
let nodes = rd.payload.page
|
||||||
|
.map((collection: Collection) => toFlatNode(collection, false, level + 1, false, communityFlatNode));
|
||||||
|
if ((rd.payload.elementsPerPage * currentCollectionPage) < rd.payload.totalElements && rd.payload.currentPage > currentCollectionPage) {
|
||||||
|
nodes = [...nodes, showMoreFlatNode('collection', level + 1, communityFlatNode)];
|
||||||
|
}
|
||||||
|
return nodes;
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
collections = [...collections, nextSetOfCollectionsPage];
|
||||||
|
}
|
||||||
|
obsList = [...obsList, combineAndFlatten(collections)];
|
||||||
|
}
|
||||||
|
|
||||||
|
return combineAndFlatten(obsList);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a community has subcommunities or collections by querying the respective services with a pageSize = 0
|
||||||
|
* If it does, it is expandable => returns true, false if not
|
||||||
|
* @param community Community being checked whether it is expandable (if it has subcommunities or collections)
|
||||||
|
*/
|
||||||
|
public getIsExpandable(community: Community): boolean {
|
||||||
|
let isExpandable = true;
|
||||||
|
let nrOfSubcoms;
|
||||||
|
let nrOfColl;
|
||||||
|
this.communityDataService.findByParent(community.uuid, { elementsPerPage: 0 })
|
||||||
|
.pipe(
|
||||||
|
filter((rd: RemoteData<PaginatedList<Community>>) => rd.hasSucceeded),
|
||||||
|
take(1),
|
||||||
|
)
|
||||||
|
.subscribe((result) => {
|
||||||
|
if (result.payload.totalElements === 0) {
|
||||||
|
nrOfSubcoms = result.payload.totalElements;
|
||||||
|
}
|
||||||
|
;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.collectionDataService.findByParent(community.uuid, { elementsPerPage: 0 })
|
||||||
|
.pipe(
|
||||||
|
filter((rd: RemoteData<PaginatedList<Collection>>) => rd.hasSucceeded),
|
||||||
|
take(1),
|
||||||
|
)
|
||||||
|
.subscribe((result) => {
|
||||||
|
if (result.payload.totalElements === 0) {
|
||||||
|
nrOfColl = result.payload.totalElements;
|
||||||
|
}
|
||||||
|
;
|
||||||
|
});
|
||||||
|
if (nrOfSubcoms === 0 && nrOfColl === 0) {
|
||||||
|
isExpandable = false;
|
||||||
|
}
|
||||||
|
return isExpandable;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -1,33 +1,71 @@
|
|||||||
<cdk-tree [dataSource]="dataSource" [treeControl]="treeControl">
|
<ds-loading *ngIf="(dataSource.loading$ | async) && loadingNode === undefined "></ds-loading>
|
||||||
|
|
||||||
|
<cdk-tree [dataSource]="dataSource" [treeControl]="treeControl">
|
||||||
<!-- This is the tree node template for show more node -->
|
<!-- This is the tree node template for show more node -->
|
||||||
<cdk-tree-node *cdkTreeNodeDef="let node; when: isShowMore" cdkTreeNodePadding
|
<cdk-tree-node *cdkTreeNodeDef="let node; when: isShowMore" cdkTreeNodePadding
|
||||||
class="example-tree-node">
|
class="example-tree-node show-more-node">
|
||||||
<div class="btn-group"
|
<div class="btn-group">
|
||||||
(click)="getNextPage(node)">
|
<button type="button" class="btn btn-default" cdkTreeNodeToggle>
|
||||||
<button type="button" class="btn btn-default" cdkTreeNodeToggle
|
<span class="fa fa-chevron-right invisible" aria-hidden="true"></span>
|
||||||
(click)="getNextPage(node)">
|
|
||||||
<span class="fa fa-plus"
|
|
||||||
aria-hidden="true"></span>
|
|
||||||
</button>
|
</button>
|
||||||
<h5 class="align-middle pt-2">
|
<div class="align-middle pt-2">
|
||||||
|
<a *ngIf="node!==loadingNode" [routerLink]="" (click)="getNextPage(node)"
|
||||||
|
class="btn btn-outline-secondary btn-sm">
|
||||||
{{ 'communityList.showMore' | translate }}
|
{{ 'communityList.showMore' | translate }}
|
||||||
</h5>
|
</a>
|
||||||
|
<ds-loading *ngIf="node===loadingNode && dataSource.loading$ | async"></ds-loading>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-muted" cdkTreeNodePadding>
|
<div class="text-muted" cdkTreeNodePadding>
|
||||||
<div class="d-flex" *ngIf="node.payload.shortDescription">
|
<div class="d-flex">
|
||||||
<button type="button" class="btn btn-default invisible">
|
|
||||||
</button>
|
|
||||||
<span>{{node.payload.shortDescription}}</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</cdk-tree-node>
|
</cdk-tree-node>
|
||||||
<!-- This is the tree node template for leaf nodes -->
|
<!-- This is the tree node template for expandable nodes (coms and subcoms with children) -->
|
||||||
|
<cdk-tree-node *cdkTreeNodeDef="let node; when: hasChild" cdkTreeNodePadding
|
||||||
|
class="example-tree-node expandable-node">
|
||||||
|
<div class="btn-group">
|
||||||
|
<button type="button" class="btn btn-default" cdkTreeNodeToggle
|
||||||
|
[attr.aria-label]="'toggle ' + node.name"
|
||||||
|
(click)="toggleExpanded(node)"
|
||||||
|
[ngClass]="node.isExpandable ? 'visible' : 'invisible'">
|
||||||
|
<span class="{{node.isExpanded ? 'fa fa-chevron-down' : 'fa fa-chevron-right'}}"
|
||||||
|
aria-hidden="true"></span>
|
||||||
|
</button>
|
||||||
|
<h5 class="align-middle pt-2">
|
||||||
|
<a [routerLink]="node.route" class="lead">
|
||||||
|
{{node.name}}
|
||||||
|
</a>
|
||||||
|
</h5>
|
||||||
|
</div>
|
||||||
|
<ds-truncatable [id]="node.id">
|
||||||
|
<div class="text-muted" cdkTreeNodePadding>
|
||||||
|
<div class="d-flex" *ngIf="node.payload.shortDescription">
|
||||||
|
<button type="button" class="btn btn-default invisible">
|
||||||
|
<span class="{{node.isExpanded ? 'fa fa-chevron-down' : 'fa fa-chevron-right'}}"
|
||||||
|
aria-hidden="true"></span>
|
||||||
|
</button>
|
||||||
|
<ds-truncatable-part [id]="node.id" [minLines]="3">
|
||||||
|
<span>{{node.payload.shortDescription}}</span>
|
||||||
|
</ds-truncatable-part>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ds-truncatable>
|
||||||
|
<div class="d-flex" *ngIf="node===loadingNode && dataSource.loading$ | async"
|
||||||
|
cdkTreeNodePadding>
|
||||||
|
<button type="button" class="btn btn-default invisible">
|
||||||
|
<span class="{{node.isExpanded ? 'fa fa-chevron-down' : 'fa fa-chevron-right'}}"
|
||||||
|
aria-hidden="true"></span>
|
||||||
|
</button>
|
||||||
|
<ds-loading></ds-loading>
|
||||||
|
</div>
|
||||||
|
</cdk-tree-node>
|
||||||
|
<!-- This is the tree node template for leaf nodes (collections and (sub)coms without children) -->
|
||||||
<cdk-tree-node *cdkTreeNodeDef="let node; when: !(hasChild && isShowMore)" cdkTreeNodePadding
|
<cdk-tree-node *cdkTreeNodeDef="let node; when: !(hasChild && isShowMore)" cdkTreeNodePadding
|
||||||
class="example-tree-node">
|
class="example-tree-node childless-node">
|
||||||
<div class="btn-group">
|
<div class="btn-group">
|
||||||
<button type="button" class="btn btn-default" cdkTreeNodeToggle>
|
<button type="button" class="btn btn-default" cdkTreeNodeToggle>
|
||||||
<span class="fa fa-minus"
|
<span class="fa fa-chevron-right invisible"
|
||||||
aria-hidden="true"></span>
|
aria-hidden="true"></span>
|
||||||
</button>
|
</button>
|
||||||
<h6 class="align-middle pt-2">
|
<h6 class="align-middle pt-2">
|
||||||
@@ -36,39 +74,18 @@
|
|||||||
</a>
|
</a>
|
||||||
</h6>
|
</h6>
|
||||||
</div>
|
</div>
|
||||||
|
<ds-truncatable [id]="node.id">
|
||||||
<div class="text-muted" cdkTreeNodePadding>
|
<div class="text-muted" cdkTreeNodePadding>
|
||||||
<div class="d-flex" *ngIf="node.payload.shortDescription">
|
<div class="d-flex" *ngIf="node.payload.shortDescription">
|
||||||
<button type="button" class="btn btn-default invisible">
|
<button type="button" class="btn btn-default invisible">
|
||||||
<span class="{{node.isExpanded ? 'fa fa-chevron-down' : 'fa fa-chevron-right'}}"
|
<span class="{{node.isExpanded ? 'fa fa-chevron-down' : 'fa fa-chevron-right'}}"
|
||||||
aria-hidden="true"></span>
|
aria-hidden="true"></span>
|
||||||
</button>
|
</button>
|
||||||
|
<ds-truncatable-part [id]="node.id" [minLines]="3">
|
||||||
<span>{{node.payload.shortDescription}}</span>
|
<span>{{node.payload.shortDescription}}</span>
|
||||||
|
</ds-truncatable-part>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</cdk-tree-node>
|
</ds-truncatable>
|
||||||
<!-- This is the tree node template for expandable nodes -->
|
|
||||||
<cdk-tree-node *cdkTreeNodeDef="let node; when: hasChild" cdkTreeNodePadding
|
|
||||||
class="example-tree-node">
|
|
||||||
<div class="btn-group">
|
|
||||||
<button type="button" class="btn btn-default" cdkTreeNodeToggle
|
|
||||||
[attr.aria-label]="'toggle ' + node.name"
|
|
||||||
(click)="toggleExpanded(node)"
|
|
||||||
[ngClass]="node.isExpandable ? 'visible' : 'invisible'">
|
|
||||||
<span class="{{node.isExpanded ? 'fa fa-chevron-down' : 'fa fa-chevron-right'}}" aria-hidden="true"></span>
|
|
||||||
</button>
|
|
||||||
<h5 class="align-middle pt-2">
|
|
||||||
<a [routerLink]="node.route" class="lead">
|
|
||||||
{{node.name}}
|
|
||||||
</a>
|
|
||||||
</h5>
|
|
||||||
</div>
|
|
||||||
<div class="text-muted" cdkTreeNodePadding>
|
|
||||||
<div class="d-flex" *ngIf="node.payload.shortDescription">
|
|
||||||
<button type="button" class="btn btn-default invisible">
|
|
||||||
<span class="{{node.isExpanded ? 'fa fa-chevron-down' : 'fa fa-chevron-right'}}" aria-hidden="true"></span>
|
|
||||||
</button>
|
|
||||||
<span>{{node.payload.shortDescription}}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</cdk-tree-node>
|
</cdk-tree-node>
|
||||||
</cdk-tree>
|
</cdk-tree>
|
||||||
|
@@ -1,14 +1,201 @@
|
|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
import { async, ComponentFixture, fakeAsync, inject, TestBed, tick } from '@angular/core/testing';
|
||||||
|
|
||||||
import { CommunityListComponent } from './community-list.component';
|
import { CommunityListComponent } from './community-list.component';
|
||||||
|
import {
|
||||||
|
CommunityListService,
|
||||||
|
FlatNode,
|
||||||
|
showMoreFlatNode,
|
||||||
|
toFlatNode
|
||||||
|
} from '../community-list-service';
|
||||||
|
import { CdkTreeModule } from '@angular/cdk/tree';
|
||||||
|
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { MockTranslateLoader } from '../../shared/mocks/mock-translate-loader';
|
||||||
|
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||||
|
import { RouterTestingModule } from '@angular/router/testing';
|
||||||
|
import { Community } from '../../core/shared/community.model';
|
||||||
|
import { createSuccessfulRemoteDataObject$ } from '../../shared/testing/utils';
|
||||||
|
import { PaginatedList } from '../../core/data/paginated-list';
|
||||||
|
import { PageInfo } from '../../core/shared/page-info.model';
|
||||||
|
import { Collection } from '../../core/shared/collection.model';
|
||||||
|
import { Observable, of as observableOf } from 'rxjs';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
import { isEmpty, isNotEmpty } from '../../shared/empty.util';
|
||||||
|
|
||||||
describe('CommunityListComponent', () => {
|
describe('CommunityListComponent', () => {
|
||||||
let component: CommunityListComponent;
|
let component: CommunityListComponent;
|
||||||
let fixture: ComponentFixture<CommunityListComponent>;
|
let fixture: ComponentFixture<CommunityListComponent>;
|
||||||
|
|
||||||
|
const mockSubcommunities1Page1 = [Object.assign(new Community(), {
|
||||||
|
id: 'ce64f48e-2c9b-411a-ac36-ee429c0e6a88',
|
||||||
|
uuid: 'ce64f48e-2c9b-411a-ac36-ee429c0e6a88',
|
||||||
|
name: 'subcommunity1',
|
||||||
|
}),
|
||||||
|
Object.assign(new Community(), {
|
||||||
|
id: '59ee713b-ee53-4220-8c3f-9860dc84fe33',
|
||||||
|
uuid: '59ee713b-ee53-4220-8c3f-9860dc84fe33',
|
||||||
|
name: 'subcommunity2',
|
||||||
|
})
|
||||||
|
];
|
||||||
|
const mockCollectionsPage1 = [
|
||||||
|
Object.assign(new Collection(), {
|
||||||
|
id: 'e9dbf393-7127-415f-8919-55be34a6e9ed',
|
||||||
|
uuid: 'e9dbf393-7127-415f-8919-55be34a6e9ed',
|
||||||
|
name: 'collection1',
|
||||||
|
}),
|
||||||
|
Object.assign(new Collection(), {
|
||||||
|
id: '59da2ff0-9bf4-45bf-88be-e35abd33f304',
|
||||||
|
uuid: '59da2ff0-9bf4-45bf-88be-e35abd33f304',
|
||||||
|
name: 'collection2',
|
||||||
|
})
|
||||||
|
];
|
||||||
|
const mockCollectionsPage2 = [
|
||||||
|
Object.assign(new Collection(), {
|
||||||
|
id: 'a5159760-f362-4659-9e81-e3253ad91ede',
|
||||||
|
uuid: 'a5159760-f362-4659-9e81-e3253ad91ede',
|
||||||
|
name: 'collection3',
|
||||||
|
}),
|
||||||
|
Object.assign(new Collection(), {
|
||||||
|
id: 'a392e16b-fcf2-400a-9a88-53ef7ecbdcd3',
|
||||||
|
uuid: 'a392e16b-fcf2-400a-9a88-53ef7ecbdcd3',
|
||||||
|
name: 'collection4',
|
||||||
|
})
|
||||||
|
];
|
||||||
|
|
||||||
|
const mockTopCommunitiesWithChildrenArrays = [
|
||||||
|
{
|
||||||
|
id: '7669c72a-3f2a-451f-a3b9-9210e7a4c02f',
|
||||||
|
uuid: '7669c72a-3f2a-451f-a3b9-9210e7a4c02f',
|
||||||
|
subcommunities: mockSubcommunities1Page1,
|
||||||
|
collections: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '9076bd16-e69a-48d6-9e41-0238cb40d863',
|
||||||
|
uuid: '9076bd16-e69a-48d6-9e41-0238cb40d863',
|
||||||
|
subcommunities: [],
|
||||||
|
collections: [...mockCollectionsPage1, ...mockCollectionsPage2],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'efbf25e1-2d8c-4c28-8f3e-2e04c215be24',
|
||||||
|
uuid: 'efbf25e1-2d8c-4c28-8f3e-2e04c215be24',
|
||||||
|
subcommunities: [],
|
||||||
|
collections: [],
|
||||||
|
}];
|
||||||
|
|
||||||
|
const mockTopFlatnodesUnexpanded: FlatNode[] = [
|
||||||
|
toFlatNode(
|
||||||
|
Object.assign(new Community(), {
|
||||||
|
id: '7669c72a-3f2a-451f-a3b9-9210e7a4c02f',
|
||||||
|
uuid: '7669c72a-3f2a-451f-a3b9-9210e7a4c02f',
|
||||||
|
subcommunities: createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), mockSubcommunities1Page1)),
|
||||||
|
collections: createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), [])),
|
||||||
|
name: 'community1',
|
||||||
|
}), true, 0, false, null
|
||||||
|
),
|
||||||
|
toFlatNode(
|
||||||
|
Object.assign(new Community(), {
|
||||||
|
id: '9076bd16-e69a-48d6-9e41-0238cb40d863',
|
||||||
|
uuid: '9076bd16-e69a-48d6-9e41-0238cb40d863',
|
||||||
|
subcommunities: createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), [])),
|
||||||
|
collections: createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), [...mockCollectionsPage1, ...mockCollectionsPage2])),
|
||||||
|
name: 'community2',
|
||||||
|
}), true, 0, false, null
|
||||||
|
),
|
||||||
|
toFlatNode(
|
||||||
|
Object.assign(new Community(), {
|
||||||
|
id: 'efbf25e1-2d8c-4c28-8f3e-2e04c215be24',
|
||||||
|
uuid: 'efbf25e1-2d8c-4c28-8f3e-2e04c215be24',
|
||||||
|
subcommunities: createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), [])),
|
||||||
|
collections: createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), [])),
|
||||||
|
name: 'community3',
|
||||||
|
}), false, 0, false, null
|
||||||
|
),
|
||||||
|
];
|
||||||
|
let communityListServiceStub;
|
||||||
|
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
|
communityListServiceStub = {
|
||||||
|
topPageSize: 2,
|
||||||
|
topCurrentPage: 1,
|
||||||
|
collectionPageSize: 2,
|
||||||
|
subcommunityPageSize: 2,
|
||||||
|
getNextPageTopCommunities() {
|
||||||
|
this.topCurrentPage++;
|
||||||
|
},
|
||||||
|
loadCommunities(expandedNodes) {
|
||||||
|
let flatnodes;
|
||||||
|
let showMoreTopComNode = false;
|
||||||
|
flatnodes = [...mockTopFlatnodesUnexpanded];
|
||||||
|
const currentPage = this.topCurrentPage;
|
||||||
|
const elementsPerPage = this.topPageSize;
|
||||||
|
let endPageIndex = (currentPage * elementsPerPage);
|
||||||
|
if (endPageIndex >= flatnodes.length) {
|
||||||
|
endPageIndex = flatnodes.length;
|
||||||
|
} else {
|
||||||
|
showMoreTopComNode = true;
|
||||||
|
}
|
||||||
|
if (expandedNodes === null || isEmpty(expandedNodes)) {
|
||||||
|
if (showMoreTopComNode) {
|
||||||
|
return observableOf([...mockTopFlatnodesUnexpanded.slice(0, endPageIndex), showMoreFlatNode('community', 0, null)]);
|
||||||
|
} else {
|
||||||
|
return observableOf(mockTopFlatnodesUnexpanded.slice(0, endPageIndex));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
flatnodes = [];
|
||||||
|
const topFlatnodes = mockTopFlatnodesUnexpanded.slice(0, endPageIndex);
|
||||||
|
topFlatnodes.map((topNode: FlatNode) => {
|
||||||
|
flatnodes = [...flatnodes, topNode]
|
||||||
|
const expandedParent: FlatNode = expandedNodes.find((expandedNode: FlatNode) => expandedNode.id === topNode.id);
|
||||||
|
if (isNotEmpty(expandedParent)) {
|
||||||
|
const matchingTopComWithArrays = mockTopCommunitiesWithChildrenArrays.find((topcom) => topcom.id === topNode.id);
|
||||||
|
if (isNotEmpty(matchingTopComWithArrays)) {
|
||||||
|
const possibleSubcoms: Community[] = matchingTopComWithArrays.subcommunities;
|
||||||
|
let subComFlatnodes = [];
|
||||||
|
possibleSubcoms.map((subcom: Community) => {
|
||||||
|
subComFlatnodes = [...subComFlatnodes, toFlatNode(subcom, false, topNode.level + 1, false, topNode)];
|
||||||
|
});
|
||||||
|
const possibleColls: Collection[] = matchingTopComWithArrays.collections;
|
||||||
|
let collFlatnodes = [];
|
||||||
|
possibleColls.map((coll: Collection) => {
|
||||||
|
collFlatnodes = [...collFlatnodes, toFlatNode(coll, false, topNode.level + 1, false, topNode)];
|
||||||
|
});
|
||||||
|
if (isNotEmpty(subComFlatnodes)) {
|
||||||
|
const endSubComIndex = this.subcommunityPageSize * expandedParent.currentCommunityPage;
|
||||||
|
flatnodes = [...flatnodes, ...subComFlatnodes.slice(0, endSubComIndex)];
|
||||||
|
if (subComFlatnodes.length > endSubComIndex) {
|
||||||
|
flatnodes = [...flatnodes, showMoreFlatNode('community', topNode.level + 1, expandedParent)];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isNotEmpty(collFlatnodes)) {
|
||||||
|
const endColIndex = this.collectionPageSize * expandedParent.currentCollectionPage;
|
||||||
|
flatnodes = [...flatnodes, ...collFlatnodes.slice(0, endColIndex)];
|
||||||
|
if (collFlatnodes.length > endColIndex) {
|
||||||
|
flatnodes = [...flatnodes, showMoreFlatNode('collection', topNode.level + 1, expandedParent)];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (showMoreTopComNode) {
|
||||||
|
flatnodes = [...flatnodes, showMoreFlatNode('community', 0, null)];
|
||||||
|
}
|
||||||
|
return observableOf(flatnodes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
declarations: [ CommunityListComponent ]
|
imports: [
|
||||||
|
TranslateModule.forRoot({
|
||||||
|
loader: {
|
||||||
|
provide: TranslateLoader,
|
||||||
|
useClass: MockTranslateLoader
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
CdkTreeModule,
|
||||||
|
RouterTestingModule],
|
||||||
|
declarations: [CommunityListComponent],
|
||||||
|
providers: [CommunityListComponent,
|
||||||
|
{ provide: CommunityListService, useValue: communityListServiceStub },],
|
||||||
|
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
||||||
})
|
})
|
||||||
.compileComponents();
|
.compileComponents();
|
||||||
}));
|
}));
|
||||||
@@ -19,7 +206,119 @@ describe('CommunityListComponent', () => {
|
|||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create', () => {
|
it('should create', inject([CommunityListComponent], (comp: CommunityListComponent) => {
|
||||||
expect(component).toBeTruthy();
|
expect(comp).toBeTruthy();
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should render a cdk tree with the first elementsPerPage (2) nr of top level communities, unexpanded', () => {
|
||||||
|
const expandableNodesFound = fixture.debugElement.queryAll(By.css('.expandable-node a'));
|
||||||
|
const childlessNodesFound = fixture.debugElement.queryAll(By.css('.childless-node a'));
|
||||||
|
const allNodes = [...expandableNodesFound, ...childlessNodesFound];
|
||||||
|
expect(allNodes.length).toEqual(2);
|
||||||
|
mockTopFlatnodesUnexpanded.slice(0, 2).map((topFlatnode: FlatNode) => {
|
||||||
|
expect(allNodes.find((foundEl) => {
|
||||||
|
return (foundEl.nativeElement.textContent.trim() === topFlatnode.name);
|
||||||
|
})).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('show more node is present at end of nodetree', () => {
|
||||||
|
const showMoreEl = fixture.debugElement.queryAll(By.css('.show-more-node'));
|
||||||
|
expect(showMoreEl.length).toEqual(1);
|
||||||
|
expect(showMoreEl).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when show more of top communities is clicked', () => {
|
||||||
|
beforeEach(fakeAsync(() => {
|
||||||
|
const showMoreLink = fixture.debugElement.query(By.css('.show-more-node a'));
|
||||||
|
showMoreLink.triggerEventHandler('click', {
|
||||||
|
preventDefault: () => {/**/
|
||||||
|
}
|
||||||
|
});
|
||||||
|
tick();
|
||||||
|
fixture.detectChanges();
|
||||||
|
}));
|
||||||
|
it('tree contains maximum of currentPage (2) * (2) elementsPerPage of first top communities, or less if there are less communities (3)', () => {
|
||||||
|
const expandableNodesFound = fixture.debugElement.queryAll(By.css('.expandable-node a'));
|
||||||
|
const childlessNodesFound = fixture.debugElement.queryAll(By.css('.childless-node a'));
|
||||||
|
const allNodes = [...expandableNodesFound, ...childlessNodesFound];
|
||||||
|
expect(allNodes.length).toEqual(3);
|
||||||
|
mockTopFlatnodesUnexpanded.map((topFlatnode: FlatNode) => {
|
||||||
|
expect(allNodes.find((foundEl) => {
|
||||||
|
return (foundEl.nativeElement.textContent.trim() === topFlatnode.name);
|
||||||
|
})).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('show more node is gone from end of nodetree', () => {
|
||||||
|
const showMoreEl = fixture.debugElement.queryAll(By.css('.show-more-node'));
|
||||||
|
expect(showMoreEl.length).toEqual(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when first expandable node is expanded', () => {
|
||||||
|
let allNodes;
|
||||||
|
beforeEach(fakeAsync(() => {
|
||||||
|
const chevronExpand = fixture.debugElement.query(By.css('.expandable-node button'));
|
||||||
|
const chevronExpandSpan = fixture.debugElement.query(By.css('.expandable-node button span'));
|
||||||
|
if (chevronExpandSpan.nativeElement.classList.contains('fa-chevron-right')) {
|
||||||
|
chevronExpand.nativeElement.click();
|
||||||
|
tick();
|
||||||
|
fixture.detectChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
const expandableNodesFound = fixture.debugElement.queryAll(By.css('.expandable-node a'));
|
||||||
|
const childlessNodesFound = fixture.debugElement.queryAll(By.css('.childless-node a'));
|
||||||
|
allNodes = [...expandableNodesFound, ...childlessNodesFound];
|
||||||
|
}));
|
||||||
|
describe('children of first expandable node are added to tree (page-limited)', () => {
|
||||||
|
it('tree contains page-limited topcoms (2) and children of first expandable node (2subcoms)', () => {
|
||||||
|
expect(allNodes.length).toEqual(4);
|
||||||
|
mockTopFlatnodesUnexpanded.slice(0, 2).map((topFlatnode: FlatNode) => {
|
||||||
|
expect(allNodes.find((foundEl) => {
|
||||||
|
return (foundEl.nativeElement.textContent.trim() === topFlatnode.name);
|
||||||
|
})).toBeTruthy();
|
||||||
|
});
|
||||||
|
mockSubcommunities1Page1.map((subcom) => {
|
||||||
|
expect(allNodes.find((foundEl) => {
|
||||||
|
return (foundEl.nativeElement.textContent.trim() === subcom.name);
|
||||||
|
})).toBeTruthy();
|
||||||
|
})
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('second top community node is expanded and has more children (collections) than page size of collection', () => {
|
||||||
|
describe('children of second top com are added (page-limited pageSize 2)', () => {
|
||||||
|
let allNodes;
|
||||||
|
beforeEach(fakeAsync(() => {
|
||||||
|
const chevronExpand = fixture.debugElement.queryAll(By.css('.expandable-node button'));
|
||||||
|
const chevronExpandSpan = fixture.debugElement.queryAll(By.css('.expandable-node button span'));
|
||||||
|
if (chevronExpandSpan[1].nativeElement.classList.contains('fa-chevron-right')) {
|
||||||
|
chevronExpand[1].nativeElement.click();
|
||||||
|
tick();
|
||||||
|
fixture.detectChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
const expandableNodesFound = fixture.debugElement.queryAll(By.css('.expandable-node a'));
|
||||||
|
const childlessNodesFound = fixture.debugElement.queryAll(By.css('.childless-node a'));
|
||||||
|
allNodes = [...expandableNodesFound, ...childlessNodesFound];
|
||||||
|
}));
|
||||||
|
it('tree contains 2 (page-limited) top com, 2 (page-limited) coll of 2nd top com, a show more for those page-limited coll and show more for page-limited top com', () => {
|
||||||
|
mockTopFlatnodesUnexpanded.slice(0, 2).map((topFlatnode: FlatNode) => {
|
||||||
|
expect(allNodes.find((foundEl) => {
|
||||||
|
return (foundEl.nativeElement.textContent.trim() === topFlatnode.name);
|
||||||
|
})).toBeTruthy();
|
||||||
|
});
|
||||||
|
mockCollectionsPage1.map((coll) => {
|
||||||
|
expect(allNodes.find((foundEl) => {
|
||||||
|
return (foundEl.nativeElement.textContent.trim() === coll.name);
|
||||||
|
})).toBeTruthy();
|
||||||
|
})
|
||||||
|
expect(allNodes.length).toEqual(4);
|
||||||
|
const showMoreEl = fixture.debugElement.queryAll(By.css('.show-more-node'));
|
||||||
|
expect(showMoreEl.length).toEqual(2);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@@ -1,19 +1,17 @@
|
|||||||
import {Component, OnInit} from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
import {CommunityListAdapter, FlatNode} from '../community-list-adapter';
|
import { CommunityListService, FlatNode } from '../community-list-service';
|
||||||
import {CommunityListDatasource} from '../community-list-datasource';
|
import { CommunityListDatasource } from '../community-list-datasource';
|
||||||
import {FlatTreeControl} from '@angular/cdk/tree';
|
import { FlatTreeControl } from '@angular/cdk/tree';
|
||||||
import {Collection} from '../../core/shared/collection.model';
|
import { isEmpty } from '../../shared/empty.util';
|
||||||
import {Community} from '../../core/shared/community.model';
|
|
||||||
import {isEmpty} from '../../shared/empty.util';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-community-list',
|
selector: 'ds-community-list',
|
||||||
templateUrl: './community-list.component.html',
|
templateUrl: './community-list.component.html',
|
||||||
styleUrls: ['./community-list.component.scss']
|
|
||||||
})
|
})
|
||||||
export class CommunityListComponent implements OnInit {
|
export class CommunityListComponent implements OnInit {
|
||||||
|
|
||||||
private expandedNodes: FlatNode[] = [];
|
private expandedNodes: FlatNode[] = [];
|
||||||
|
public loadingNode: FlatNode;
|
||||||
|
|
||||||
treeControl = new FlatTreeControl<FlatNode>(
|
treeControl = new FlatTreeControl<FlatNode>(
|
||||||
(node) => node.level, (node) => node.isExpandable
|
(node) => node.level, (node) => node.isExpandable
|
||||||
@@ -21,23 +19,30 @@ export class CommunityListComponent implements OnInit {
|
|||||||
|
|
||||||
dataSource: CommunityListDatasource;
|
dataSource: CommunityListDatasource;
|
||||||
|
|
||||||
constructor(private communityListAdapter: CommunityListAdapter) {
|
constructor(private communityListService: CommunityListService) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.dataSource = new CommunityListDatasource(this.communityListAdapter);
|
this.dataSource = new CommunityListDatasource(this.communityListService);
|
||||||
this.dataSource.loadCommunities(null);
|
this.dataSource.loadCommunities(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// whether or not this node has children (subcommunities or collections)
|
||||||
hasChild(_: number, node: FlatNode) {
|
hasChild(_: number, node: FlatNode) {
|
||||||
return node.isExpandable;
|
return node.isExpandable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// whether or not it is a show more node (contains no data, but is indication that there are more topcoms, subcoms or collections
|
||||||
isShowMore(_: number, node: FlatNode) {
|
isShowMore(_: number, node: FlatNode) {
|
||||||
return node.isShowMoreNode;
|
return node.isShowMoreNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggles the expanded variable of a node, adds it to the exapanded nodes list and reloads the tree so this node is expanded
|
||||||
|
* @param node Node we want to expand
|
||||||
|
*/
|
||||||
toggleExpanded(node: FlatNode) {
|
toggleExpanded(node: FlatNode) {
|
||||||
|
this.loadingNode = node;
|
||||||
if (node.isExpanded) {
|
if (node.isExpanded) {
|
||||||
this.expandedNodes = this.expandedNodes.filter((node2) => node2.name !== node.name);
|
this.expandedNodes = this.expandedNodes.filter((node2) => node2.name !== node.name);
|
||||||
node.isExpanded = false;
|
node.isExpanded = false;
|
||||||
@@ -54,21 +59,28 @@ export class CommunityListComponent implements OnInit {
|
|||||||
this.dataSource.loadCommunities(this.expandedNodes);
|
this.dataSource.loadCommunities(this.expandedNodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes sure the next page of a node is added to the tree (top community, sub community of collection)
|
||||||
|
* > Finds its parent (if not top community) and increases its corresponding collection/subcommunity currentPage
|
||||||
|
* > Reloads tree with new page added to corresponding top community lis, sub community list or collection list
|
||||||
|
* @param node The show more node indicating whether it's an increase in top communities, sub communities or collections
|
||||||
|
*/
|
||||||
getNextPage(node: FlatNode): void {
|
getNextPage(node: FlatNode): void {
|
||||||
|
this.loadingNode = node;
|
||||||
if (node.parent != null) {
|
if (node.parent != null) {
|
||||||
if (node.parent.isExpandable) {
|
if (node.parent.isExpandable) {
|
||||||
if (node.payload instanceof Collection) {
|
if (node.id === 'collection') {
|
||||||
const parentNodeInExpandedNodes = this.expandedNodes.find((node2:FlatNode) => node.parent.id === node2.id);
|
const parentNodeInExpandedNodes = this.expandedNodes.find((node2: FlatNode) => node.parent.id === node2.id);
|
||||||
parentNodeInExpandedNodes.currentCollectionPage++;
|
parentNodeInExpandedNodes.currentCollectionPage++;
|
||||||
}
|
}
|
||||||
if (node.payload instanceof Community) {
|
if (node.id === 'community') {
|
||||||
const parentNodeInExpandedNodes = this.expandedNodes.find((node2:FlatNode) => node.parent.id === node2.id);
|
const parentNodeInExpandedNodes = this.expandedNodes.find((node2: FlatNode) => node.parent.id === node2.id);
|
||||||
parentNodeInExpandedNodes.currentCommunityPage++;
|
parentNodeInExpandedNodes.currentCommunityPage++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.dataSource.loadCommunities(this.expandedNodes);
|
this.dataSource.loadCommunities(this.expandedNodes);
|
||||||
} else {
|
} else {
|
||||||
this.communityListAdapter.getNextPageTopCommunities();
|
this.communityListService.getNextPageTopCommunities();
|
||||||
this.dataSource.loadCommunities(this.expandedNodes);
|
this.dataSource.loadCommunities(this.expandedNodes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -16,7 +16,7 @@ import { HttpClient } from '@angular/common/http';
|
|||||||
import { NormalizedObjectBuildService } from '../cache/builders/normalized-object-build.service';
|
import { NormalizedObjectBuildService } from '../cache/builders/normalized-object-build.service';
|
||||||
import { DSOChangeAnalyzer } from './dso-change-analyzer.service';
|
import { DSOChangeAnalyzer } from './dso-change-analyzer.service';
|
||||||
import { Observable } from 'rxjs/internal/Observable';
|
import { Observable } from 'rxjs/internal/Observable';
|
||||||
import { FindListOptions, GetRequest } from './request.models';
|
import {FindListOptions, FindListRequest, GetRequest} from './request.models';
|
||||||
import { RemoteData } from './remote-data';
|
import { RemoteData } from './remote-data';
|
||||||
import { PaginatedList } from './paginated-list';
|
import { PaginatedList } from './paginated-list';
|
||||||
import { configureRequest } from '../shared/operators';
|
import { configureRequest } from '../shared/operators';
|
||||||
|
@@ -13,7 +13,7 @@ import { RequestService } from './request.service';
|
|||||||
import { NormalizedObject } from '../cache/models/normalized-object.model';
|
import { NormalizedObject } from '../cache/models/normalized-object.model';
|
||||||
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||||
import { RequestEntry } from './request.reducer';
|
import { RequestEntry } from './request.reducer';
|
||||||
import { of as observableOf } from 'rxjs';
|
import {Observable, of as observableOf} from 'rxjs';
|
||||||
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
||||||
import { HttpClient } from '@angular/common/http';
|
import { HttpClient } from '@angular/common/http';
|
||||||
import { NormalizedObjectBuildService } from '../cache/builders/normalized-object-build.service';
|
import { NormalizedObjectBuildService } from '../cache/builders/normalized-object-build.service';
|
||||||
@@ -45,6 +45,11 @@ class TestService extends ComColDataService<any> {
|
|||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected getFindByParentHref(parentUUID: string): Observable<string> {
|
||||||
|
// implementation in subclasses for communities/collections
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* tslint:enable:max-classes-per-file */
|
/* tslint:enable:max-classes-per-file */
|
||||||
|
@@ -23,7 +23,6 @@ import { DSOChangeAnalyzer } from './dso-change-analyzer.service';
|
|||||||
export class CommunityDataService extends ComColDataService<Community> {
|
export class CommunityDataService extends ComColDataService<Community> {
|
||||||
protected linkPath = 'communities';
|
protected linkPath = 'communities';
|
||||||
protected topLinkPath = 'communities/search/top';
|
protected topLinkPath = 'communities/search/top';
|
||||||
protected subcommunitiesLinkPath = 'communities/search/subCommunities';
|
|
||||||
protected cds = this;
|
protected cds = this;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@@ -57,20 +56,6 @@ export class CommunityDataService extends ComColDataService<Community> {
|
|||||||
return this.rdbService.buildList<Community>(hrefObs) as Observable<RemoteData<PaginatedList<Community>>>;
|
return this.rdbService.buildList<Community>(hrefObs) as Observable<RemoteData<PaginatedList<Community>>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
findSubCommunitiesPerParentCommunity(parentCommunityUUID: string, options: FindListOptions = {}): Observable<RemoteData<PaginatedList<Community>>> {
|
|
||||||
const hrefObs = this.getFindAllHref(options, this.subcommunitiesLinkPath + '?parent=' + parentCommunityUUID);
|
|
||||||
|
|
||||||
hrefObs.pipe(
|
|
||||||
filter((href: string) => hasValue(href)),
|
|
||||||
take(1))
|
|
||||||
.subscribe((href: string) => {
|
|
||||||
const request = new FindListRequest(this.requestService.generateRequestId(), href, options);
|
|
||||||
this.requestService.configure(request);
|
|
||||||
});
|
|
||||||
|
|
||||||
return this.rdbService.buildList<Community>(hrefObs) as Observable<RemoteData<PaginatedList<Community>>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected getFindByParentHref(parentUUID: string): Observable<string> {
|
protected getFindByParentHref(parentUUID: string): Observable<string> {
|
||||||
return this.halService.getEndpoint(this.linkPath).pipe(
|
return this.halService.getEndpoint(this.linkPath).pipe(
|
||||||
switchMap((communityEndpointHref: string) =>
|
switchMap((communityEndpointHref: string) =>
|
||||||
|
@@ -128,9 +128,11 @@ export abstract class DataService<T extends CacheableObject> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
findAll(options: FindListOptions = {}): Observable<RemoteData<PaginatedList<T>>> {
|
findAll(options: FindListOptions = {}): Observable<RemoteData<PaginatedList<T>>> {
|
||||||
const hrefObs = this.getFindAllHref(options);
|
return this.findList(this.getFindAllHref(options), options);
|
||||||
|
}
|
||||||
|
|
||||||
hrefObs.pipe(
|
protected findList(href$, options: FindListOptions) {
|
||||||
|
href$.pipe(
|
||||||
first((href: string) => hasValue(href)))
|
first((href: string) => hasValue(href)))
|
||||||
.subscribe((href: string) => {
|
.subscribe((href: string) => {
|
||||||
const request = new FindListRequest(this.requestService.generateRequestId(), href, options);
|
const request = new FindListRequest(this.requestService.generateRequestId(), href, options);
|
||||||
@@ -140,7 +142,7 @@ export abstract class DataService<T extends CacheableObject> {
|
|||||||
this.requestService.configure(request);
|
this.requestService.configure(request);
|
||||||
});
|
});
|
||||||
|
|
||||||
return this.rdbService.buildList<T>(hrefObs) as Observable<RemoteData<PaginatedList<T>>>;
|
return this.rdbService.buildList<T>(href$) as Observable<RemoteData<PaginatedList<T>>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -53,17 +53,18 @@ export class NavbarComponent extends MenuComponent implements OnInit {
|
|||||||
} as TextMenuItemModel,
|
} as TextMenuItemModel,
|
||||||
index: 0
|
index: 0
|
||||||
},
|
},
|
||||||
// {
|
/* Communities & Collections tree */
|
||||||
// id: 'browse_global_communities_and_collections',
|
{
|
||||||
// parentID: 'browse_global',
|
id: `browse_global_communities_and_collections`,
|
||||||
// active: false,
|
parentID: 'browse_global',
|
||||||
// visible: true,
|
active: false,
|
||||||
// model: {
|
visible: true,
|
||||||
// type: MenuItemType.LINK,
|
model: {
|
||||||
// text: 'menu.section.browse_global_communities_and_collections',
|
type: MenuItemType.LINK,
|
||||||
// link: '#'
|
text: `menu.section.browse_global_communities_and_collections`,
|
||||||
// } as LinkMenuItemModel,
|
link: `/community-list`
|
||||||
// },
|
} as LinkMenuItemModel
|
||||||
|
},
|
||||||
|
|
||||||
/* Statistics */
|
/* Statistics */
|
||||||
{
|
{
|
||||||
|
Reference in New Issue
Block a user