Skip to content

Commit

Permalink
Merged in task/dspace-cris-2023_02_x/DSC-1650-davide (pull request DS…
Browse files Browse the repository at this point in the history
…pace#1929)

Task/dspace cris 2023 02 x/DSC-1650 davide

Approved-by: Francesco Molinaro
  • Loading branch information
Davide Negretti authored and FrancescoMolinaro committed Jul 29, 2024
2 parents 0ff731c + 44e69cf commit f81924c
Show file tree
Hide file tree
Showing 20 changed files with 266 additions and 158 deletions.
2 changes: 2 additions & 0 deletions config/config.example.yml
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,8 @@ browseBy:
showLabels: true
# If true, thumbnail images for items will be added to BOTH search and browse result lists.
showThumbnails: true
# Whether to add item thumbnail images to BOTH browse and search result lists.
showMetrics: false
# The number of entries in a paginated browse results list.
# Rounded to the nearest size in the list of selectable sizes on the
# settings menu.
Expand Down
8 changes: 8 additions & 0 deletions src/app/core/layout/models/section.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ export interface TopSection extends SectionComponent {
componentType: 'top';
numberOfItems: number;
showThumbnails: boolean;
template: TopSectionTemplateType;
}

export interface SearchSection extends SectionComponent {
Expand Down Expand Up @@ -96,3 +97,10 @@ export interface TopSectionColumn {
metadataField: string;
titleKey: string;
}

/**
* Represents the type of template to use for the section
*/
export enum TopSectionTemplateType {
DEFAULT = 'default', // CRIS default template
}
3 changes: 2 additions & 1 deletion src/app/explore-page/explore-page.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
import { Observable, of } from 'rxjs';
import { TranslateLoaderMock } from '../shared/mocks/translate-loader.mock';
import { RemoteData } from '../core/data/remote-data';
import { BrowseSection, FacetSection, SearchSection, Section, TopSection } from '../core/layout/models/section.model';
import { BrowseSection, FacetSection, SearchSection, Section, TopSectionTemplateType, TopSection } from '../core/layout/models/section.model';
import { SectionDataService } from '../core/layout/section-data.service';
import { createSuccessfulRemoteDataObject$ } from '../shared/remote-data.utils';
import { ExplorePageComponent } from './explore-page.component';
Expand All @@ -37,6 +37,7 @@ describe('ExploreComponent', () => {
numberOfItems: 5,
titleKey: 'lastPublications',
showThumbnails: false,
template: TopSectionTemplateType.DEFAULT
};

const searchComponent: SearchSection = {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { followLink } from '../utils/follow-link-config.model';
import { CollectionElementLinkType } from '../object-collection/collection-element-link.type';
import { TopSection } from '../../core/layout/models/section.model';
import { Component, Input, OnChanges, OnInit, PLATFORM_ID, inject } from '@angular/core';
import { isPlatformServer } from '@angular/common';

import { SearchService } from '../../core/shared/search/search.service';
import { PaginatedSearchOptions } from '../search/models/paginated-search-options.model';
import { DSpaceObject } from '../../core/shared/dspace-object.model';
import { SearchResult } from '../search/models/search-result.model';
import { Context } from '../../core/shared/context.model';
import { RemoteData } from '../../core/data/remote-data';
import { PaginatedList } from '../../core/data/paginated-list.model';
import {
getAllCompletedRemoteData,
getPaginatedListPayload,
getRemoteDataPayload,
toDSpaceObjectListRD,
} from '../../core/shared/operators';
import { APP_CONFIG } from '../../../config/app-config.interface';
import { BehaviorSubject, Observable, mergeMap } from 'rxjs';
import { Item } from '../../core/shared/item.model';
import { getItemPageRoute } from '../../item-page/item-page-routing-paths';

@Component({
template: ''
})
export abstract class AbstractBrowseElementsComponent implements OnInit, OnChanges {

protected readonly appConfig = inject(APP_CONFIG);
protected readonly platformId = inject(PLATFORM_ID);
protected readonly searchService = inject(SearchService);

protected followThumbnailLink: boolean; // to be overridden

@Input() paginatedSearchOptions: PaginatedSearchOptions;

@Input() context: Context;

@Input() topSection: TopSection;

public collectionElementLinkTypeEnum = CollectionElementLinkType;

paginatedSearchOptionsBS: BehaviorSubject<PaginatedSearchOptions>;

searchResults$: Observable<RemoteData<PaginatedList<SearchResult<DSpaceObject>>>>;

searchResultArray$: Observable<DSpaceObject[]>;

ngOnChanges() {
if (isPlatformServer(this.platformId)) {
return;
}
this.paginatedSearchOptionsBS?.next(this.paginatedSearchOptions);
}

ngOnInit() {
const followLinks = this.followThumbnailLink ? [followLink('thumbnail')] : [];
this.paginatedSearchOptionsBS = new BehaviorSubject<PaginatedSearchOptions>(this.paginatedSearchOptions);
this.searchResults$ = this.paginatedSearchOptionsBS.asObservable().pipe(
mergeMap((paginatedSearchOptions) =>
this.searchService.search(paginatedSearchOptions, null, true, true, ...followLinks),
),
getAllCompletedRemoteData(),
);

this.searchResultArray$ = this.searchResults$.pipe(
toDSpaceObjectListRD(),
getRemoteDataPayload(),
getPaginatedListPayload(),
);
}

getItemPageRoute(item: DSpaceObject | Item) {
return getItemPageRoute(item as Item);
}
}
Original file line number Diff line number Diff line change
@@ -1,20 +1,14 @@
<div class="m-2">
<ng-container *ngIf="searchResults">
<ul *ngIf="searchResults?.hasSucceeded" class="list-unstyled">
<li *ngFor="let object of searchResults?.payload?.page; let i = index; let last = last" class="mt-4 mb-4 d-flex" [class.border-bottom]="true && !last">
<ds-listable-object-component-loader [context]="context"
[index]="i"
[listID]="paginatedSearchOptions.configuration"
[object]="object"
[showLabel]="showLabel"
[showMetrics]="showMetrics"
[showThumbnails]="showThumbnails"
[viewMode]="paginatedSearchOptions.view"></ds-listable-object-component-loader>
</li>
</ul>
<div *ngIf="searchResults?.hasFailed">
{{ 'remote.error' | translate }}
</div>
</ng-container>
<ds-loading *ngIf="!searchResults"></ds-loading>
<div [ngSwitch]="(sectionTemplateType | lowercase)">

<ng-container *ngSwitchDefault>
<ds-themed-default-browse-elements
[showMetrics]="showMetrics"
[showThumbnails]="topSection.showThumbnails"
[topSection]="topSection"
[showLabel]="showLabel"
[paginatedSearchOptions]="paginatedSearchOptionsBS.asObservable() | async"
[context]="context"
></ds-themed-default-browse-elements>
</ng-container>

</div>
Original file line number Diff line number Diff line change
@@ -1,20 +1,12 @@
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { ChangeDetectorRef, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';

import { of } from 'rxjs';
import { NO_ERRORS_SCHEMA } from '@angular/core';

import { BrowseMostElementsComponent } from './browse-most-elements.component';
import { SearchManager } from '../../core/browse/search-manager';
import { PaginatedSearchOptions } from '../search/models/paginated-search-options.model';
import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model';
import { PaginationComponentOptions } from '../pagination/pagination-component-options.model';
import { APP_CONFIG } from '../../../config/app-config.interface';
import { TopSectionTemplateType } from '../../core/layout/models/section.model';
import { By } from '@angular/platform-browser';
import { ItemSearchResult } from '../object-collection/shared/item-search-result.model';
import { Item } from '../../core/shared/item.model';
import { createSuccessfulRemoteDataObject } from '../remote-data.utils';
import { buildPaginatedList } from '../../core/data/paginated-list.model';
import { PageInfo } from '../../core/shared/page-info.model';
import { followLink } from '../utils/follow-link-config.model';
import { of } from 'rxjs';

describe('BrowseMostElementsComponent', () => {
let component: BrowseMostElementsComponent;
Expand Down Expand Up @@ -53,91 +45,60 @@ describe('BrowseMostElementsComponent', () => {
]
}
});
const mockResponse = createSuccessfulRemoteDataObject(buildPaginatedList(new PageInfo(), [mockResultObject]));

const mockSearchService = {
search: jasmine.createSpy('search').and.returnValue(of(mockResponse)), // Replace with your desired response
};

const mockConfig = {
browseBy: {
showThumbnails: true
}
};

beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [BrowseMostElementsComponent],
providers: [
{ provide: APP_CONFIG, useValue: mockConfig },
{ provide: SearchManager, useValue: mockSearchService },
{ provide: ChangeDetectorRef, useValue: {} },
],
schemas: [CUSTOM_ELEMENTS_SCHEMA], // Ignore unknown Angular elements
schemas: [NO_ERRORS_SCHEMA],
}).compileComponents();
}));

beforeEach(() => {
fixture = TestBed.createComponent(BrowseMostElementsComponent);
component = fixture.componentInstance;
component.paginatedSearchOptions = new PaginatedSearchOptions({
configuration: 'test',
pagination: Object.assign(new PaginationComponentOptions(), {
id: 'search-object-pagination',
pageSize: 5,
currentPage: 1
}),
sort: new SortOptions('dc.title', SortDirection.ASC)
});
component.topSection = {
template: TopSectionTemplateType.DEFAULT
} as any;
fixture.detectChanges();
});

it('should create', () => {
component.showThumbnails = true;
fixture.detectChanges();
expect(component).toBeTruthy();
});

it('should call searchService.search on ngOnInit with followLinks', () => {
component.showThumbnails = true;
fixture.detectChanges();

expect(mockSearchService.search).toHaveBeenCalledWith(
component.paginatedSearchOptions,
null,
true,
true,
followLink('thumbnail')
);
});

it('should call searchService.search on ngOnInit with followLinks', () => {
component.showThumbnails = undefined;
fixture.detectChanges();

expect(mockSearchService.search).toHaveBeenCalledWith(
component.paginatedSearchOptions,
null,
true,
true,
followLink('thumbnail')
);
});
describe('when the templateType is DEFAULT', () => {
beforeEach(() => {
component.topSection = {
template: TopSectionTemplateType.DEFAULT
} as any;
fixture.detectChanges();
});

it('should call searchService.search on ngOnInit without followLinks', () => {
component.showThumbnails = false;
fixture.detectChanges();
it('should display ds-themed-default-browse-elements', () => {
const defaultElement = fixture.debugElement.query(By.css('ds-themed-default-browse-elements'));
expect(defaultElement).toBeTruthy();
});

expect(mockSearchService.search).toHaveBeenCalledWith(
component.paginatedSearchOptions,
null,
true,
true
);
it('should not display ds-themed-images-browse-elements', () => {
const imageElement = fixture.debugElement.query(By.css('ds-themed-images-browse-elements'));
expect(imageElement).toBeNull();
});
});

it('should update searchResults after searchService response', () => {
component.ngOnInit();
describe('when the templateType is not recognized', () => {
beforeEach(() => {
component.topSection = {
template: 'not recognized' as any
} as any;
fixture.detectChanges();
});

expect(component.searchResults).toEqual(mockResponse);
it('should display ds-themed-default-browse-elements', () => {
const defaultElement = fixture.debugElement.query(By.css('ds-themed-default-browse-elements'));
expect(defaultElement).toBeTruthy();
});
});
});
Original file line number Diff line number Diff line change
@@ -1,66 +1,38 @@
import { ChangeDetectorRef, Component, Inject, Input, OnInit, PLATFORM_ID } from '@angular/core';
import { isPlatformServer } from '@angular/common';

import { SearchManager } from '../../core/browse/search-manager';
import { TopSection, TopSectionTemplateType } from './../../core/layout/models/section.model';
import { Component, Input, OnChanges, OnInit } from '@angular/core';
import { PaginatedSearchOptions } from '../search/models/paginated-search-options.model';
import { DSpaceObject } from '../../core/shared/dspace-object.model';
import { SearchResult } from '../search/models/search-result.model';
import { Context } from '../../core/shared/context.model';
import { RemoteData } from '../../core/data/remote-data';
import { PaginatedList } from '../../core/data/paginated-list.model';
import { getFirstCompletedRemoteData } from '../../core/shared/operators';
import { followLink } from '../utils/follow-link-config.model';
import { APP_CONFIG, AppConfig } from '../../../config/app-config.interface';
import { BehaviorSubject } from 'rxjs';

@Component({
selector: 'ds-browse-most-elements',
styleUrls: ['./browse-most-elements.component.scss'],
templateUrl: './browse-most-elements.component.html'
})

export class BrowseMostElementsComponent implements OnInit {
export class BrowseMostElementsComponent implements OnInit, OnChanges {

@Input() paginatedSearchOptions: PaginatedSearchOptions;

@Input() context: Context;
/**
* Whether to show the badge label or not
*/
@Input() showLabel: boolean;

/**
* Whether to show the metrics badges
*/
@Input() showMetrics: boolean;
showLabel: boolean;

/**
* Whether to show the thumbnail preview
*/
@Input() showThumbnails: boolean;
showMetrics = true;

searchResults: RemoteData<PaginatedList<SearchResult<DSpaceObject>>>;
@Input() topSection: TopSection;

constructor(
@Inject(APP_CONFIG) protected appConfig: AppConfig,
@Inject(PLATFORM_ID) private platformId: Object,
private searchService: SearchManager,
private cdr: ChangeDetectorRef) {
paginatedSearchOptionsBS = new BehaviorSubject<PaginatedSearchOptions>(null);

}
templateTypeEnum = TopSectionTemplateType;

ngOnInit() {
if (isPlatformServer(this.platformId)) {
return;
}
sectionTemplateType: TopSectionTemplateType;

const showThumbnails = this.showThumbnails ?? this.appConfig.browseBy.showThumbnails;
const followLinks = showThumbnails ? [followLink('thumbnail')] : [];
this.searchService.search(this.paginatedSearchOptions, null, true, true, ...followLinks).pipe(
getFirstCompletedRemoteData(),
).subscribe((response: RemoteData<PaginatedList<SearchResult<DSpaceObject>>>) => {
this.searchResults = response as any;
this.cdr.detectChanges();
});
ngOnInit(): void {
this.sectionTemplateType = this.topSection?.template ?? TopSectionTemplateType.DEFAULT;
}

ngOnChanges() { // trigger change detection on child components
this.paginatedSearchOptionsBS.next(this.paginatedSearchOptions);
}
}
Loading

0 comments on commit f81924c

Please sign in to comment.