[CST-4591] Fix issue with showing empty collection results

This commit is contained in:
Giuseppe Digilio
2021-09-24 13:18:50 +02:00
parent a170f1f8a9
commit c6a73f2dcb
6 changed files with 87 additions and 97 deletions

View File

@@ -1,6 +1,5 @@
<div class="form-group w-100 pr-2 pl-2"> <div *ngIf="searchField" class="form-group w-100 pr-2 pl-2">
<input *ngIf="searchField" <input type="search"
type="search"
class="form-control w-100" class="form-control w-100"
(click)="$event.stopPropagation();" (click)="$event.stopPropagation();"
placeholder="{{ 'submission.sections.general.search-collection' | translate }}" placeholder="{{ 'submission.sections.general.search-collection' | translate }}"
@@ -8,8 +7,7 @@
#searchFieldEl> #searchFieldEl>
</div> </div>
<div class="dropdown-divider"></div> <div class="dropdown-divider"></div>
<div <div class="scrollable-menu"
class="scrollable-menu"
aria-labelledby="dropdownMenuButton" aria-labelledby="dropdownMenuButton"
(scroll)="onScroll($event)" (scroll)="onScroll($event)"
infiniteScroll infiniteScroll
@@ -19,11 +17,11 @@
[fromRoot]="true" [fromRoot]="true"
[scrollWindow]="false" [scrollWindow]="false"
(scrolled)="onScrollDown()"> (scrolled)="onScrollDown()">
<button class="dropdown-item disabled" *ngIf="searchListCollection?.length == 0 && !(isLoadingList | async)"> <button class="dropdown-item disabled" *ngIf="searchListCollection?.length == 0 && !(isLoadingList | async)">
{{'submission.sections.general.no-collection' | translate}} {{'submission.sections.general.no-collection' | translate}}
</button> </button>
<button <button *ngFor="let listItem of searchListCollection"
*ngFor="let listItem of searchListCollection"
class="dropdown-item collection-item" class="dropdown-item collection-item"
title="{{ listItem.collection.name }}" title="{{ listItem.collection.name }}"
(click)="onSelect(listItem)"> (click)="onSelect(listItem)">
@@ -34,7 +32,7 @@
<li class="list-item text-truncate text-primary font-weight-bold">{{ listItem.collection.name}}</li> <li class="list-item text-truncate text-primary font-weight-bold">{{ listItem.collection.name}}</li>
</ul> </ul>
</button> </button>
<button class="dropdown-item disabled" *ngIf="(isLoadingList | async)" > <button class="dropdown-item disabled" *ngIf="(isLoadingList | async)">
<ds-loading message="{{'loading.default' | translate}}"> <ds-loading message="{{'loading.default' | translate}}">
</ds-loading> </ds-loading>
</button> </button>

View File

@@ -226,23 +226,11 @@ describe('CollectionDropdownComponent', () => {
}); });
it('should emit hasChoice true when totalElements is greater then one', () => { it('should emit hasChoice true when totalElements is greater then one', () => {
spyOn(component.hasChoice, 'emit').and.callThrough(); spyOn(component.searchComplete, 'emit').and.callThrough();
component.ngOnInit(); component.ngOnInit();
fixture.detectChanges(); fixture.detectChanges();
expect(component.hasChoice.emit).toHaveBeenCalledWith(true); expect(component.searchComplete.emit).toHaveBeenCalledWith();
});
it('should emit hasChoice false when totalElements is not greater then one', () => {
componentAsAny.collectionDataService.getAuthorizedCollection.and.returnValue(paginatedEmptyCollectionRD$);
componentAsAny.collectionDataService.getAuthorizedCollectionByEntityType.and.returnValue(paginatedEmptyCollectionRD$);
spyOn(component.hasChoice, 'emit').and.callThrough();
component.ngOnInit();
fixture.detectChanges();
expect(component.hasChoice.emit).toHaveBeenCalledWith(false);
}); });
it('should emit theOnlySelectable when totalElements is equal to one', () => { it('should emit theOnlySelectable when totalElements is equal to one', () => {

View File

@@ -11,7 +11,7 @@ import {
} from '@angular/core'; } from '@angular/core';
import { FormControl } from '@angular/forms'; import { FormControl } from '@angular/forms';
import { BehaviorSubject, Observable, Subscription } from 'rxjs'; import { BehaviorSubject, from as observableFrom, Observable, of as observableOf, Subscription } from 'rxjs';
import { debounceTime, distinctUntilChanged, map, mergeMap, reduce, startWith, switchMap, take } from 'rxjs/operators'; import { debounceTime, distinctUntilChanged, map, mergeMap, reduce, startWith, switchMap, take } from 'rxjs/operators';
import { hasValue } from '../empty.util'; import { hasValue } from '../empty.util';
@@ -22,10 +22,7 @@ import { Community } from '../../core/shared/community.model';
import { CollectionDataService } from '../../core/data/collection-data.service'; import { CollectionDataService } from '../../core/data/collection-data.service';
import { Collection } from '../../core/shared/collection.model'; import { Collection } from '../../core/shared/collection.model';
import { followLink } from '../utils/follow-link-config.model'; import { followLink } from '../utils/follow-link-config.model';
import { import { getFirstCompletedRemoteData, getFirstSucceededRemoteDataPayload } from '../../core/shared/operators';
getFirstSucceededRemoteDataPayload,
getFirstSucceededRemoteWithNotEmptyData
} from '../../core/shared/operators';
/** /**
* An interface to represent a collection entry * An interface to represent a collection entry
@@ -113,9 +110,9 @@ export class CollectionDropdownComponent implements OnInit, OnDestroy {
@Input() entityType: string; @Input() entityType: string;
/** /**
* Emit to notify whether collections to choice from are more than one * Emit to notify whether search is complete
*/ */
@Output() hasChoice = new EventEmitter<boolean>(); @Output() searchComplete = new EventEmitter<any>();
/** /**
* Emit to notify the only selectable collection. * Emit to notify the only selectable collection.
@@ -213,21 +210,21 @@ export class CollectionDropdownComponent implements OnInit, OnDestroy {
query, query,
this.entityType, this.entityType,
findOptions, findOptions,
false, true,
followLink('parentCommunity')); followLink('parentCommunity'));
} else { } else {
searchListService$ = this.collectionDataService searchListService$ = this.collectionDataService
.getAuthorizedCollection(query, findOptions, true, false, followLink('parentCommunity')); .getAuthorizedCollection(query, findOptions, true, true, followLink('parentCommunity'));
} }
this.searchListCollection$ = searchListService$.pipe( this.searchListCollection$ = searchListService$.pipe(
getFirstSucceededRemoteWithNotEmptyData(), getFirstCompletedRemoteData(),
switchMap((collections: RemoteData<PaginatedList<Collection>>) => { switchMap((collectionsRD: RemoteData<PaginatedList<Collection>>) => {
if ( (this.searchListCollection.length + findOptions.elementsPerPage) >= collections.payload.totalElements ) { this.searchComplete.emit();
if (collectionsRD.hasSucceeded && collectionsRD.payload.totalElements > 0) {
if ( (this.searchListCollection.length + findOptions.elementsPerPage) >= collectionsRD.payload.totalElements ) {
this.hasNextPage = false; this.hasNextPage = false;
} this.emitSelectionEvents(collectionsRD);
this.emitSelectionEvents(collections); return observableFrom(collectionsRD.payload.page).pipe(
return collections.payload.page;
}),
mergeMap((collection: Collection) => collection.parentCommunity.pipe( mergeMap((collection: Collection) => collection.parentCommunity.pipe(
getFirstSucceededRemoteDataPayload(), getFirstSucceededRemoteDataPayload(),
map((community: Community) => ({ map((community: Community) => ({
@@ -236,12 +233,21 @@ export class CollectionDropdownComponent implements OnInit, OnDestroy {
}) })
))), ))),
reduce((acc: any, value: any) => [...acc, value], []), reduce((acc: any, value: any) => [...acc, value], []),
startWith([])
); );
this.subs.push(this.searchListCollection$.subscribe( }
(next) => { this.searchListCollection.push(...next); }, undefined, } else {
() => { this.hideShowLoader(false); this.changeDetectorRef.detectChanges(); } this.hasNextPage = false;
)); return observableOf([]);
}
})
);
this.subs.push(
this.searchListCollection$.subscribe((list: CollectionListEntry[]) => {
this.searchListCollection.push(...list);
this.hideShowLoader(false);
this.changeDetectorRef.detectChanges();
})
);
} }
/** /**
@@ -284,7 +290,6 @@ export class CollectionDropdownComponent implements OnInit, OnDestroy {
* @private * @private
*/ */
private emitSelectionEvents(collections: RemoteData<PaginatedList<Collection>>) { private emitSelectionEvents(collections: RemoteData<PaginatedList<Collection>>) {
this.hasChoice.emit(collections.payload.totalElements > 1);
if (collections.payload.totalElements === 1) { if (collections.payload.totalElements === 1) {
const collection = collections.payload.page[0]; const collection = collections.payload.page[0];
collections.payload.page[0].parentCommunity.pipe( collections.payload.page[0].parentCommunity.pipe(

View File

@@ -1,5 +1,5 @@
<div> <div>
<div class="modal-header">{{'dso-selector.create.collection.head' | translate}} <div class="modal-header">{{'dso-selector.select.collection.head' | translate}}
<button type="button" class="close" (click)="closeCollectionModal()" aria-label="Close"> <button type="button" class="close" (click)="closeCollectionModal()" aria-label="Close">
<span aria-hidden="true">×</span> <span aria-hidden="true">×</span>
</button> </button>
@@ -8,7 +8,7 @@
<ds-loading *ngIf="isLoading()"></ds-loading> <ds-loading *ngIf="isLoading()"></ds-loading>
<ds-collection-dropdown [ngClass]="{'d-none': isLoading()}" <ds-collection-dropdown [ngClass]="{'d-none': isLoading()}"
(selectionChange)="selectObject($event)" (selectionChange)="selectObject($event)"
(hasChoice)="onHasChoice($event)" (searchComplete)="searchComplete()"
(theOnlySelectable)="theOnlySelectable($event)" (theOnlySelectable)="theOnlySelectable($event)"
[entityType]="entityType"> [entityType]="entityType">
</ds-collection-dropdown> </ds-collection-dropdown>

View File

@@ -78,25 +78,23 @@ describe('SubmissionImportExternalCollectionComponent test suite', () => {
expect(compAsAny.activeModal.dismiss).toHaveBeenCalled(); expect(compAsAny.activeModal.dismiss).toHaveBeenCalled();
}); });
it('should be in loading state when hasChoice variable is different to true', () => { it('should be in loading state when search is not completed', () => {
comp.hasChoice = null; comp.loading = null;
expect(comp.isLoading()).toBeFalse();
comp.loading = true;
expect(comp.isLoading()).toBeTrue(); expect(comp.isLoading()).toBeTrue();
comp.hasChoice = false; comp.loading = false;
expect(comp.isLoading()).toBeTrue();
comp.hasChoice = true;
expect(comp.isLoading()).toBeFalse(); expect(comp.isLoading()).toBeFalse();
}); });
it('should set hasChoice variable on hasChoice event', () => { it('should set loading variable to false on searchComplete event', () => {
comp.hasChoice = null; comp.loading = null;
comp.onHasChoice(true); comp.searchComplete();
expect(comp.hasChoice).toBe(true); expect(comp.loading).toBe(false);
comp.onHasChoice(false);
expect(comp.hasChoice).toBe(false);
}); });
it('should emit theOnlySelectable', () => { it('should emit theOnlySelectable', () => {

View File

@@ -22,9 +22,9 @@ export class SubmissionImportExternalCollectionComponent {
public entityType: string; public entityType: string;
/** /**
* If a collection choice is available * If collection searching is pending or not
*/ */
public hasChoice: boolean = null; public loading = true;
/** /**
* Initialize the component variables. * Initialize the component variables.
@@ -60,14 +60,15 @@ export class SubmissionImportExternalCollectionComponent {
* Set the hasChoice state * Set the hasChoice state
* @param hasChoice * @param hasChoice
*/ */
public onHasChoice(hasChoice: boolean) { public searchComplete() {
this.hasChoice = hasChoice; this.loading = false;
} }
/** /**
* If the component is in loading state. * If the component is in loading state.
*/ */
public isLoading(): boolean { public isLoading(): boolean {
return this.hasChoice !== true; return !!this.loading;
} }
} }