mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 01:54:15 +00:00
[CST-4591] Fix issue with showing empty collection results
This commit is contained in:
@@ -1,42 +1,40 @@
|
||||
<div class="form-group w-100 pr-2 pl-2">
|
||||
<input *ngIf="searchField"
|
||||
type="search"
|
||||
class="form-control w-100"
|
||||
(click)="$event.stopPropagation();"
|
||||
placeholder="{{ 'submission.sections.general.search-collection' | translate }}"
|
||||
[formControl]="searchField"
|
||||
#searchFieldEl>
|
||||
<div *ngIf="searchField" class="form-group w-100 pr-2 pl-2">
|
||||
<input type="search"
|
||||
class="form-control w-100"
|
||||
(click)="$event.stopPropagation();"
|
||||
placeholder="{{ 'submission.sections.general.search-collection' | translate }}"
|
||||
[formControl]="searchField"
|
||||
#searchFieldEl>
|
||||
</div>
|
||||
<div class="dropdown-divider"></div>
|
||||
<div
|
||||
class="scrollable-menu"
|
||||
aria-labelledby="dropdownMenuButton"
|
||||
(scroll)="onScroll($event)"
|
||||
infiniteScroll
|
||||
[infiniteScrollDistance]="5"
|
||||
[infiniteScrollThrottle]="300"
|
||||
[infiniteScrollUpDistance]="1.5"
|
||||
[fromRoot]="true"
|
||||
[scrollWindow]="false"
|
||||
(scrolled)="onScrollDown()">
|
||||
<button class="dropdown-item disabled" *ngIf="searchListCollection?.length == 0 && !(isLoadingList | async)">
|
||||
{{'submission.sections.general.no-collection' | translate}}
|
||||
</button>
|
||||
<button
|
||||
*ngFor="let listItem of searchListCollection"
|
||||
<div class="scrollable-menu"
|
||||
aria-labelledby="dropdownMenuButton"
|
||||
(scroll)="onScroll($event)"
|
||||
infiniteScroll
|
||||
[infiniteScrollDistance]="5"
|
||||
[infiniteScrollThrottle]="300"
|
||||
[infiniteScrollUpDistance]="1.5"
|
||||
[fromRoot]="true"
|
||||
[scrollWindow]="false"
|
||||
(scrolled)="onScrollDown()">
|
||||
|
||||
<button class="dropdown-item disabled" *ngIf="searchListCollection?.length == 0 && !(isLoadingList | async)">
|
||||
{{'submission.sections.general.no-collection' | translate}}
|
||||
</button>
|
||||
<button *ngFor="let listItem of searchListCollection"
|
||||
class="dropdown-item collection-item"
|
||||
title="{{ listItem.collection.name }}"
|
||||
(click)="onSelect(listItem)">
|
||||
<ul class="list-unstyled mb-0">
|
||||
<li class="list-item text-truncate text-secondary" *ngFor="let item of listItem.communities">
|
||||
{{ item.name}} <i class="fa fa-level-down" aria-hidden="true"></i>
|
||||
</li>
|
||||
<li class="list-item text-truncate text-primary font-weight-bold">{{ listItem.collection.name}}</li>
|
||||
</ul>
|
||||
</button>
|
||||
<button class="dropdown-item disabled" *ngIf="(isLoadingList | async)" >
|
||||
<ds-loading message="{{'loading.default' | translate}}">
|
||||
</ds-loading>
|
||||
</button>
|
||||
<ul class="list-unstyled mb-0">
|
||||
<li class="list-item text-truncate text-secondary" *ngFor="let item of listItem.communities">
|
||||
{{ item.name}} <i class="fa fa-level-down" aria-hidden="true"></i>
|
||||
</li>
|
||||
<li class="list-item text-truncate text-primary font-weight-bold">{{ listItem.collection.name}}</li>
|
||||
</ul>
|
||||
</button>
|
||||
<button class="dropdown-item disabled" *ngIf="(isLoadingList | async)">
|
||||
<ds-loading message="{{'loading.default' | translate}}">
|
||||
</ds-loading>
|
||||
</button>
|
||||
|
||||
</div>
|
||||
|
@@ -226,23 +226,11 @@ describe('CollectionDropdownComponent', () => {
|
||||
});
|
||||
|
||||
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();
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.hasChoice.emit).toHaveBeenCalledWith(true);
|
||||
});
|
||||
|
||||
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);
|
||||
expect(component.searchComplete.emit).toHaveBeenCalledWith();
|
||||
});
|
||||
|
||||
it('should emit theOnlySelectable when totalElements is equal to one', () => {
|
||||
|
@@ -11,7 +11,7 @@ import {
|
||||
} from '@angular/core';
|
||||
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 { hasValue } from '../empty.util';
|
||||
@@ -22,10 +22,7 @@ import { Community } from '../../core/shared/community.model';
|
||||
import { CollectionDataService } from '../../core/data/collection-data.service';
|
||||
import { Collection } from '../../core/shared/collection.model';
|
||||
import { followLink } from '../utils/follow-link-config.model';
|
||||
import {
|
||||
getFirstSucceededRemoteDataPayload,
|
||||
getFirstSucceededRemoteWithNotEmptyData
|
||||
} from '../../core/shared/operators';
|
||||
import { getFirstCompletedRemoteData, getFirstSucceededRemoteDataPayload } from '../../core/shared/operators';
|
||||
|
||||
/**
|
||||
* An interface to represent a collection entry
|
||||
@@ -113,9 +110,9 @@ export class CollectionDropdownComponent implements OnInit, OnDestroy {
|
||||
@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.
|
||||
@@ -213,35 +210,44 @@ export class CollectionDropdownComponent implements OnInit, OnDestroy {
|
||||
query,
|
||||
this.entityType,
|
||||
findOptions,
|
||||
false,
|
||||
true,
|
||||
followLink('parentCommunity'));
|
||||
} else {
|
||||
searchListService$ = this.collectionDataService
|
||||
.getAuthorizedCollection(query, findOptions, true, false, followLink('parentCommunity'));
|
||||
.getAuthorizedCollection(query, findOptions, true, true, followLink('parentCommunity'));
|
||||
}
|
||||
this.searchListCollection$ = searchListService$.pipe(
|
||||
getFirstSucceededRemoteWithNotEmptyData(),
|
||||
switchMap((collections: RemoteData<PaginatedList<Collection>>) => {
|
||||
if ( (this.searchListCollection.length + findOptions.elementsPerPage) >= collections.payload.totalElements ) {
|
||||
getFirstCompletedRemoteData(),
|
||||
switchMap((collectionsRD: RemoteData<PaginatedList<Collection>>) => {
|
||||
this.searchComplete.emit();
|
||||
if (collectionsRD.hasSucceeded && collectionsRD.payload.totalElements > 0) {
|
||||
if ( (this.searchListCollection.length + findOptions.elementsPerPage) >= collectionsRD.payload.totalElements ) {
|
||||
this.hasNextPage = false;
|
||||
this.emitSelectionEvents(collectionsRD);
|
||||
return observableFrom(collectionsRD.payload.page).pipe(
|
||||
mergeMap((collection: Collection) => collection.parentCommunity.pipe(
|
||||
getFirstSucceededRemoteDataPayload(),
|
||||
map((community: Community) => ({
|
||||
communities: [{ id: community.id, name: community.name }],
|
||||
collection: { id: collection.id, uuid: collection.id, name: collection.name }
|
||||
})
|
||||
))),
|
||||
reduce((acc: any, value: any) => [...acc, value], []),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
this.hasNextPage = false;
|
||||
return observableOf([]);
|
||||
}
|
||||
this.emitSelectionEvents(collections);
|
||||
return collections.payload.page;
|
||||
}),
|
||||
mergeMap((collection: Collection) => collection.parentCommunity.pipe(
|
||||
getFirstSucceededRemoteDataPayload(),
|
||||
map((community: Community) => ({
|
||||
communities: [{ id: community.id, name: community.name }],
|
||||
collection: { id: collection.id, uuid: collection.id, name: collection.name }
|
||||
})
|
||||
))),
|
||||
reduce((acc: any, value: any) => [...acc, value], []),
|
||||
startWith([])
|
||||
})
|
||||
);
|
||||
this.subs.push(this.searchListCollection$.subscribe(
|
||||
(next) => { this.searchListCollection.push(...next); }, undefined,
|
||||
() => { this.hideShowLoader(false); this.changeDetectorRef.detectChanges(); }
|
||||
));
|
||||
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 emitSelectionEvents(collections: RemoteData<PaginatedList<Collection>>) {
|
||||
this.hasChoice.emit(collections.payload.totalElements > 1);
|
||||
if (collections.payload.totalElements === 1) {
|
||||
const collection = collections.payload.page[0];
|
||||
collections.payload.page[0].parentCommunity.pipe(
|
||||
|
@@ -1,5 +1,5 @@
|
||||
<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">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
@@ -8,7 +8,7 @@
|
||||
<ds-loading *ngIf="isLoading()"></ds-loading>
|
||||
<ds-collection-dropdown [ngClass]="{'d-none': isLoading()}"
|
||||
(selectionChange)="selectObject($event)"
|
||||
(hasChoice)="onHasChoice($event)"
|
||||
(searchComplete)="searchComplete()"
|
||||
(theOnlySelectable)="theOnlySelectable($event)"
|
||||
[entityType]="entityType">
|
||||
</ds-collection-dropdown>
|
||||
|
@@ -78,25 +78,23 @@ describe('SubmissionImportExternalCollectionComponent test suite', () => {
|
||||
expect(compAsAny.activeModal.dismiss).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should be in loading state when hasChoice variable is different to true', () => {
|
||||
comp.hasChoice = null;
|
||||
it('should be in loading state when search is not completed', () => {
|
||||
comp.loading = null;
|
||||
expect(comp.isLoading()).toBeFalse();
|
||||
|
||||
comp.loading = true;
|
||||
expect(comp.isLoading()).toBeTrue();
|
||||
|
||||
comp.hasChoice = false;
|
||||
expect(comp.isLoading()).toBeTrue();
|
||||
|
||||
comp.hasChoice = true;
|
||||
comp.loading = false;
|
||||
expect(comp.isLoading()).toBeFalse();
|
||||
});
|
||||
|
||||
it('should set hasChoice variable on hasChoice event', () => {
|
||||
comp.hasChoice = null;
|
||||
it('should set loading variable to false on searchComplete event', () => {
|
||||
comp.loading = null;
|
||||
|
||||
comp.onHasChoice(true);
|
||||
expect(comp.hasChoice).toBe(true);
|
||||
comp.searchComplete();
|
||||
expect(comp.loading).toBe(false);
|
||||
|
||||
comp.onHasChoice(false);
|
||||
expect(comp.hasChoice).toBe(false);
|
||||
});
|
||||
|
||||
it('should emit theOnlySelectable', () => {
|
||||
|
@@ -22,9 +22,9 @@ export class SubmissionImportExternalCollectionComponent {
|
||||
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.
|
||||
@@ -60,14 +60,15 @@ export class SubmissionImportExternalCollectionComponent {
|
||||
* Set the hasChoice state
|
||||
* @param hasChoice
|
||||
*/
|
||||
public onHasChoice(hasChoice: boolean) {
|
||||
this.hasChoice = hasChoice;
|
||||
public searchComplete() {
|
||||
this.loading = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the component is in loading state.
|
||||
*/
|
||||
public isLoading(): boolean {
|
||||
return this.hasChoice !== true;
|
||||
return !!this.loading;
|
||||
}
|
||||
|
||||
}
|
||||
|
Reference in New Issue
Block a user