diff --git a/config/config.example.yml b/config/config.example.yml index b0112ef13a..89902c3fe3 100644 --- a/config/config.example.yml +++ b/config/config.example.yml @@ -174,6 +174,8 @@ browseBy: fiveYearLimit: 30 # The absolute lowest year to display in the dropdown (only used when no lowest date can be found for all items) defaultLowerLimit: 1900 + # If true, thumbnail images for items will be added to BOTH search and browse result lists. + showThumbnails: true # 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. diff --git a/src/app/admin/admin-search-page/admin-search-results/admin-search-result-list-element/collection-search-result/collection-admin-search-result-list-element.component.spec.ts b/src/app/admin/admin-search-page/admin-search-results/admin-search-result-list-element/collection-search-result/collection-admin-search-result-list-element.component.spec.ts index b394caef56..8937847ff5 100644 --- a/src/app/admin/admin-search-page/admin-search-results/admin-search-result-list-element/collection-search-result/collection-admin-search-result-list-element.component.spec.ts +++ b/src/app/admin/admin-search-page/admin-search-results/admin-search-result-list-element/collection-search-result/collection-admin-search-result-list-element.component.spec.ts @@ -13,6 +13,8 @@ import { RouterTestingModule } from '@angular/router/testing'; import { getCollectionEditRoute } from '../../../../../collection-page/collection-page-routing-paths'; import { DSONameService } from '../../../../../core/breadcrumbs/dso-name.service'; import { DSONameServiceMock } from '../../../../../shared/mocks/dso-name.service.mock'; +import { APP_CONFIG } from '../../../../../../config/app-config.interface'; +import { environment } from '../../../../../../environments/environment'; describe('CollectionAdminSearchResultListElementComponent', () => { let component: CollectionAdminSearchResultListElementComponent; @@ -36,7 +38,8 @@ describe('CollectionAdminSearchResultListElementComponent', () => { ], declarations: [CollectionAdminSearchResultListElementComponent], providers: [{ provide: TruncatableService, useValue: {} }, - { provide: DSONameService, useClass: DSONameServiceMock }], + { provide: DSONameService, useClass: DSONameServiceMock }, + { provide: APP_CONFIG, useValue: environment }], schemas: [NO_ERRORS_SCHEMA] }) .compileComponents(); diff --git a/src/app/admin/admin-search-page/admin-search-results/admin-search-result-list-element/community-search-result/community-admin-search-result-list-element.component.spec.ts b/src/app/admin/admin-search-page/admin-search-results/admin-search-result-list-element/community-search-result/community-admin-search-result-list-element.component.spec.ts index 155d7f7509..110d77b1e5 100644 --- a/src/app/admin/admin-search-page/admin-search-results/admin-search-result-list-element/community-search-result/community-admin-search-result-list-element.component.spec.ts +++ b/src/app/admin/admin-search-page/admin-search-results/admin-search-result-list-element/community-search-result/community-admin-search-result-list-element.component.spec.ts @@ -13,6 +13,8 @@ import { Community } from '../../../../../core/shared/community.model'; import { getCommunityEditRoute } from '../../../../../community-page/community-page-routing-paths'; import { DSONameService } from '../../../../../core/breadcrumbs/dso-name.service'; import { DSONameServiceMock } from '../../../../../shared/mocks/dso-name.service.mock'; +import { APP_CONFIG } from '../../../../../../config/app-config.interface'; +import { environment } from '../../../../../../environments/environment'; describe('CommunityAdminSearchResultListElementComponent', () => { let component: CommunityAdminSearchResultListElementComponent; @@ -36,7 +38,8 @@ describe('CommunityAdminSearchResultListElementComponent', () => { ], declarations: [CommunityAdminSearchResultListElementComponent], providers: [{ provide: TruncatableService, useValue: {} }, - { provide: DSONameService, useClass: DSONameServiceMock }], + { provide: DSONameService, useClass: DSONameServiceMock }, + { provide: APP_CONFIG, useValue: environment }], schemas: [NO_ERRORS_SCHEMA] }) .compileComponents(); diff --git a/src/app/admin/admin-search-page/admin-search-results/admin-search-result-list-element/item-search-result/item-admin-search-result-list-element.component.spec.ts b/src/app/admin/admin-search-page/admin-search-results/admin-search-result-list-element/item-search-result/item-admin-search-result-list-element.component.spec.ts index 3774a07757..667e8edea9 100644 --- a/src/app/admin/admin-search-page/admin-search-results/admin-search-result-list-element/item-search-result/item-admin-search-result-list-element.component.spec.ts +++ b/src/app/admin/admin-search-page/admin-search-results/admin-search-result-list-element/item-search-result/item-admin-search-result-list-element.component.spec.ts @@ -10,6 +10,8 @@ import { ItemAdminSearchResultListElementComponent } from './item-admin-search-r import { Item } from '../../../../../core/shared/item.model'; import { DSONameService } from '../../../../../core/breadcrumbs/dso-name.service'; import { DSONameServiceMock } from '../../../../../shared/mocks/dso-name.service.mock'; +import { APP_CONFIG } from '../../../../../../config/app-config.interface'; +import { environment } from '../../../../../../environments/environment'; describe('ItemAdminSearchResultListElementComponent', () => { let component: ItemAdminSearchResultListElementComponent; @@ -33,7 +35,8 @@ describe('ItemAdminSearchResultListElementComponent', () => { ], declarations: [ItemAdminSearchResultListElementComponent], providers: [{ provide: TruncatableService, useValue: {} }, - { provide: DSONameService, useClass: DSONameServiceMock }], + { provide: DSONameService, useClass: DSONameServiceMock }, + { provide: APP_CONFIG, useValue: environment }], schemas: [NO_ERRORS_SCHEMA] }) .compileComponents(); diff --git a/src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-list-element/workflow-item/workflow-item-search-result-admin-workflow-list-element.component.spec.ts b/src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-list-element/workflow-item/workflow-item-search-result-admin-workflow-list-element.component.spec.ts index a792a606e9..a489cb0e57 100644 --- a/src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-list-element/workflow-item/workflow-item-search-result-admin-workflow-list-element.component.spec.ts +++ b/src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-list-element/workflow-item/workflow-item-search-result-admin-workflow-list-element.component.spec.ts @@ -18,6 +18,8 @@ import { createSuccessfulRemoteDataObject$ } from '../../../../../shared/remote- import { getMockLinkService } from '../../../../../shared/mocks/link-service.mock'; import { DSONameService } from '../../../../../core/breadcrumbs/dso-name.service'; import { DSONameServiceMock } from '../../../../../shared/mocks/dso-name.service.mock'; +import { APP_CONFIG } from '../../../../../../config/app-config.interface'; +import { environment } from '../../../../../../environments/environment'; describe('WorkflowItemAdminWorkflowListElementComponent', () => { let component: WorkflowItemSearchResultAdminWorkflowListElementComponent; @@ -51,7 +53,8 @@ describe('WorkflowItemAdminWorkflowListElementComponent', () => { providers: [ { provide: TruncatableService, useValue: mockTruncatableService }, { provide: LinkService, useValue: linkService }, - { provide: DSONameService, useClass: DSONameServiceMock } + { provide: DSONameService, useClass: DSONameServiceMock }, + { provide: APP_CONFIG, useValue: environment } ], schemas: [NO_ERRORS_SCHEMA] }) diff --git a/src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-list-element/workflow-item/workflow-item-search-result-admin-workflow-list-element.component.ts b/src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-list-element/workflow-item/workflow-item-search-result-admin-workflow-list-element.component.ts index 3dd17faf25..3bc75de415 100644 --- a/src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-list-element/workflow-item/workflow-item-search-result-admin-workflow-list-element.component.ts +++ b/src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-list-element/workflow-item/workflow-item-search-result-admin-workflow-list-element.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, Inject, OnInit } from '@angular/core'; import { ViewMode } from '../../../../../core/shared/view-mode.model'; import { listableObjectComponent } from '../../../../../shared/object-collection/shared/listable-object/listable-object.decorator'; import { Context } from '../../../../../core/shared/context.model'; @@ -13,6 +13,7 @@ import { SearchResultListElementComponent } from '../../../../../shared/object-l import { TruncatableService } from '../../../../../shared/truncatable/truncatable.service'; import { WorkflowItemSearchResult } from '../../../../../shared/object-collection/shared/workflow-item-search-result.model'; import { DSONameService } from '../../../../../core/breadcrumbs/dso-name.service'; +import { APP_CONFIG, AppConfig } from '../../../../../../config/app-config.interface'; @listableObjectComponent(WorkflowItemSearchResult, ViewMode.ListElement, Context.AdminWorkflowSearch) @Component({ @@ -32,9 +33,10 @@ export class WorkflowItemSearchResultAdminWorkflowListElementComponent extends S constructor(private linkService: LinkService, protected truncatableService: TruncatableService, - protected dsoNameService: DSONameService + protected dsoNameService: DSONameService, + @Inject(APP_CONFIG) protected appConfig: AppConfig ) { - super(truncatableService, dsoNameService); + super(truncatableService, dsoNameService, appConfig); } /** diff --git a/src/app/browse-by/browse-by-date-page/browse-by-date-page.component.spec.ts b/src/app/browse-by/browse-by-date-page/browse-by-date-page.component.spec.ts index b6ff177d3d..5c2a6d820e 100644 --- a/src/app/browse-by/browse-by-date-page/browse-by-date-page.component.spec.ts +++ b/src/app/browse-by/browse-by-date-page/browse-by-date-page.component.spec.ts @@ -18,13 +18,10 @@ import { BrowseEntrySearchOptions } from '../../core/browse/browse-entry-search- import { toRemoteData } from '../browse-by-metadata-page/browse-by-metadata-page.component.spec'; import { VarDirective } from '../../shared/utils/var.directive'; import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; -import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model'; -import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model'; import { PaginationService } from '../../core/pagination/pagination.service'; import { PaginationServiceStub } from '../../shared/testing/pagination-service.stub'; -import { FindListOptions } from '../../core/data/find-list-options.model'; -import { APP_CONFIG } from 'src/config/app-config.interface'; -import { environment } from 'src/environments/environment'; +import { APP_CONFIG } from '../../../config/app-config.interface'; +import { environment } from '../../../environments/environment'; describe('BrowseByDatePageComponent', () => { let comp: BrowseByDatePageComponent; diff --git a/src/app/browse-by/browse-by-date-page/browse-by-date-page.component.ts b/src/app/browse-by/browse-by-date-page/browse-by-date-page.component.ts index 9b031260ce..c9ee669e51 100644 --- a/src/app/browse-by/browse-by-date-page/browse-by-date-page.component.ts +++ b/src/app/browse-by/browse-by-date-page/browse-by-date-page.component.ts @@ -1,9 +1,8 @@ import { ChangeDetectorRef, Component, Inject } from '@angular/core'; import { BrowseByMetadataPageComponent, - browseParamsToOptions + browseParamsToOptions, getBrowseSearchOptions } from '../browse-by-metadata-page/browse-by-metadata-page.component'; -import { BrowseEntrySearchOptions } from '../../core/browse/browse-entry-search-options.model'; import { combineLatest as observableCombineLatest } from 'rxjs'; import { RemoteData } from '../../core/data/remote-data'; import { Item } from '../../core/shared/item.model'; @@ -13,7 +12,6 @@ import { BrowseService } from '../../core/browse/browse.service'; import { DSpaceObjectDataService } from '../../core/data/dspace-object-data.service'; import { StartsWithType } from '../../shared/starts-with/starts-with-decorator'; import { BrowseByDataType, rendersBrowseBy } from '../browse-by-switcher/browse-by-decorator'; -import { environment } from '../../../environments/environment'; import { PaginationService } from '../../core/pagination/pagination.service'; import { map } from 'rxjs/operators'; import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model'; @@ -45,14 +43,15 @@ export class BrowseByDatePageComponent extends BrowseByMetadataPageComponent { protected router: Router, protected paginationService: PaginationService, protected cdRef: ChangeDetectorRef, - @Inject(APP_CONFIG) protected appConfig: AppConfig) { + @Inject(APP_CONFIG) public appConfig: AppConfig) { super(route, browseService, dsoService, paginationService, router, appConfig); } ngOnInit(): void { const sortConfig = new SortOptions('default', SortDirection.ASC); this.startsWithType = StartsWithType.date; - this.updatePage(new BrowseEntrySearchOptions(this.defaultBrowseId, this.paginationConfig, sortConfig)); + // include the thumbnail configuration in browse search options + this.updatePage(getBrowseSearchOptions(this.defaultBrowseId, this.paginationConfig, sortConfig, this.fetchThumbnails)); this.currentPagination$ = this.paginationService.getCurrentPagination(this.paginationConfig.id, this.paginationConfig); this.currentSort$ = this.paginationService.getCurrentSort(this.paginationConfig.id, sortConfig); this.subs.push( @@ -65,7 +64,7 @@ export class BrowseByDatePageComponent extends BrowseByMetadataPageComponent { const metadataKeys = params.browseDefinition ? params.browseDefinition.metadataKeys : this.defaultMetadataKeys; this.browseId = params.id || this.defaultBrowseId; this.startsWith = +params.startsWith || params.startsWith; - const searchOptions = browseParamsToOptions(params, currentPage, currentSort, this.browseId); + const searchOptions = browseParamsToOptions(params, currentPage, currentSort, this.browseId, this.fetchThumbnails); this.updatePageWithItems(searchOptions, this.value, undefined); this.updateParent(params.scope); this.updateStartsWithOptions(this.browseId, metadataKeys, params.scope); @@ -85,7 +84,7 @@ export class BrowseByDatePageComponent extends BrowseByMetadataPageComponent { updateStartsWithOptions(definition: string, metadataKeys: string[], scope?: string) { this.subs.push( this.browseService.getFirstItemFor(definition, scope).subscribe((firstItemRD: RemoteData) => { - let lowerLimit = environment.browseBy.defaultLowerLimit; + let lowerLimit = this.appConfig.browseBy.defaultLowerLimit; if (hasValue(firstItemRD.payload)) { const date = firstItemRD.payload.firstMetadataValue(metadataKeys); if (isNotEmpty(date) && isValidDate(date)) { @@ -96,8 +95,8 @@ export class BrowseByDatePageComponent extends BrowseByMetadataPageComponent { } const options = []; const currentYear = new Date().getUTCFullYear(); - const oneYearBreak = Math.floor((currentYear - environment.browseBy.oneYearLimit) / 5) * 5; - const fiveYearBreak = Math.floor((currentYear - environment.browseBy.fiveYearLimit) / 10) * 10; + const oneYearBreak = Math.floor((currentYear - this.appConfig.browseBy.oneYearLimit) / 5) * 5; + const fiveYearBreak = Math.floor((currentYear - this.appConfig.browseBy.fiveYearLimit) / 10) * 10; if (lowerLimit <= fiveYearBreak) { lowerLimit -= 10; } else if (lowerLimit <= oneYearBreak) { diff --git a/src/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.spec.ts b/src/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.spec.ts index 03152d0d3e..2984642b73 100644 --- a/src/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.spec.ts +++ b/src/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.spec.ts @@ -1,4 +1,8 @@ -import { BrowseByMetadataPageComponent, browseParamsToOptions } from './browse-by-metadata-page.component'; +import { + BrowseByMetadataPageComponent, + browseParamsToOptions, + getBrowseSearchOptions +} from './browse-by-metadata-page.component'; import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { BrowseService } from '../../core/browse/browse.service'; import { CommonModule } from '@angular/common'; @@ -14,7 +18,7 @@ import { RemoteData } from '../../core/data/remote-data'; import { buildPaginatedList, PaginatedList } from '../../core/data/paginated-list.model'; import { PageInfo } from '../../core/shared/page-info.model'; import { BrowseEntrySearchOptions } from '../../core/browse/browse-entry-search-options.model'; -import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model'; +import { SortDirection } from '../../core/cache/models/sort-options.model'; import { Item } from '../../core/shared/item.model'; import { DSpaceObjectDataService } from '../../core/data/dspace-object-data.service'; import { Community } from '../../core/shared/community.model'; @@ -26,7 +30,6 @@ import { PaginationService } from '../../core/pagination/pagination.service'; import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model'; import { PaginationServiceStub } from '../../shared/testing/pagination-service.stub'; import { APP_CONFIG } from '../../../config/app-config.interface'; -import { environment } from '../../../environments/environment'; describe('BrowseByMetadataPageComponent', () => { let comp: BrowseByMetadataPageComponent; @@ -45,6 +48,13 @@ describe('BrowseByMetadataPageComponent', () => { ] }); + const environmentMock = { + browseBy: { + showThumbnails: true, + pageSize: 10 + } + }; + const mockEntries = [ { type: BrowseEntry.type, @@ -100,7 +110,7 @@ describe('BrowseByMetadataPageComponent', () => { { provide: DSpaceObjectDataService, useValue: mockDsoService }, { provide: PaginationService, useValue: paginationService }, { provide: Router, useValue: new RouterMock() }, - { provide: APP_CONFIG, useValue: environment } + { provide: APP_CONFIG, useValue: environmentMock } ], schemas: [NO_ERRORS_SCHEMA] }).compileComponents(); @@ -121,6 +131,10 @@ describe('BrowseByMetadataPageComponent', () => { expect(comp.items$).toBeUndefined(); }); + it('should set embed thumbnail property to true', () => { + expect(comp.fetchThumbnails).toBeTrue(); + }); + describe('when a value is provided', () => { beforeEach(() => { const paramsWithValue = { @@ -148,14 +162,14 @@ describe('BrowseByMetadataPageComponent', () => { }; const paginationOptions = Object.assign(new PaginationComponentOptions(), { currentPage: 5, - pageSize: 10, + pageSize: comp.appConfig.browseBy.pageSize, }); const sortOptions = { direction: SortDirection.ASC, field: 'fake-field', }; - result = browseParamsToOptions(paramsScope, paginationOptions, sortOptions, 'author'); + result = browseParamsToOptions(paramsScope, paginationOptions, sortOptions, 'author', comp.fetchThumbnails); }); it('should return BrowseEntrySearchOptions with the correct properties', () => { @@ -166,6 +180,36 @@ describe('BrowseByMetadataPageComponent', () => { expect(result.sort.direction).toEqual(SortDirection.ASC); expect(result.sort.field).toEqual('fake-field'); expect(result.scope).toEqual('fake-scope'); + expect(result.fetchThumbnail).toBeTrue(); + }); + }); + + describe('calling getBrowseSearchOptions', () => { + let result: BrowseEntrySearchOptions; + + beforeEach(() => { + const paramsScope = { + scope: 'fake-scope' + }; + const paginationOptions = Object.assign(new PaginationComponentOptions(), { + currentPage: 5, + pageSize: comp.appConfig.browseBy.pageSize, + }); + const sortOptions = { + direction: SortDirection.ASC, + field: 'fake-field', + }; + + result = getBrowseSearchOptions('title', paginationOptions, sortOptions, comp.fetchThumbnails); + }); + it('should return BrowseEntrySearchOptions with the correct properties', () => { + + expect(result.metadataDefinition).toEqual('title'); + expect(result.pagination.currentPage).toEqual(5); + expect(result.pagination.pageSize).toEqual(10); + expect(result.sort.direction).toEqual(SortDirection.ASC); + expect(result.sort.field).toEqual('fake-field'); + expect(result.fetchThumbnail).toBeTrue(); }); }); }); diff --git a/src/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.ts b/src/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.ts index 20817d9675..a95aea7c0a 100644 --- a/src/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.ts +++ b/src/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.ts @@ -1,6 +1,5 @@ import { combineLatest as observableCombineLatest, Observable, Subscription } from 'rxjs'; import { Component, Inject, OnInit } from '@angular/core'; -import { AppConfig, APP_CONFIG } from '../../../config/app-config.interface'; import { RemoteData } from '../../core/data/remote-data'; import { PaginatedList } from '../../core/data/paginated-list.model'; import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model'; @@ -18,6 +17,7 @@ import { StartsWithType } from '../../shared/starts-with/starts-with-decorator'; import { BrowseByDataType, rendersBrowseBy } from '../browse-by-switcher/browse-by-decorator'; import { PaginationService } from '../../core/pagination/pagination.service'; import { map } from 'rxjs/operators'; +import { APP_CONFIG, AppConfig } from '../../../config/app-config.interface'; export const BBM_PAGINATION_ID = 'bbm'; @@ -109,12 +109,19 @@ export class BrowseByMetadataPageComponent implements OnInit { */ startsWith: string; + /** + * Determines whether to request embedded thumbnail. + */ + fetchThumbnails: boolean; + public constructor(protected route: ActivatedRoute, protected browseService: BrowseService, protected dsoService: DSpaceObjectDataService, protected paginationService: PaginationService, protected router: Router, - @Inject(APP_CONFIG) protected appConfig: AppConfig) { + @Inject(APP_CONFIG) public appConfig: AppConfig) { + + this.fetchThumbnails = this.appConfig.browseBy.showThumbnails; this.paginationConfig = Object.assign(new PaginationComponentOptions(), { id: BBM_PAGINATION_ID, currentPage: 1, @@ -122,9 +129,11 @@ export class BrowseByMetadataPageComponent implements OnInit { }); } + ngOnInit(): void { + const sortConfig = new SortOptions('default', SortDirection.ASC); - this.updatePage(new BrowseEntrySearchOptions(this.defaultBrowseId, this.paginationConfig, sortConfig)); + this.updatePage(getBrowseSearchOptions(this.defaultBrowseId, this.paginationConfig, sortConfig)); this.currentPagination$ = this.paginationService.getCurrentPagination(this.paginationConfig.id, this.paginationConfig); this.currentSort$ = this.paginationService.getCurrentSort(this.paginationConfig.id, sortConfig); this.subs.push( @@ -137,15 +146,16 @@ export class BrowseByMetadataPageComponent implements OnInit { this.authority = params.authority; this.value = +params.value || params.value || ''; this.startsWith = +params.startsWith || params.startsWith; - const searchOptions = browseParamsToOptions(params, currentPage, currentSort, this.browseId); if (isNotEmpty(this.value)) { - this.updatePageWithItems(searchOptions, this.value, this.authority); + this.updatePageWithItems( + browseParamsToOptions(params, currentPage, currentSort, this.browseId, this.fetchThumbnails), this.value, this.authority); } else { - this.updatePage(searchOptions); + this.updatePage(browseParamsToOptions(params, currentPage, currentSort, this.browseId, false)); } this.updateParent(params.scope); })); this.updateStartsWithTextOptions(); + } /** @@ -232,22 +242,44 @@ export class BrowseByMetadataPageComponent implements OnInit { } +/** + * Creates browse entry search options. + * @param defaultBrowseId the metadata definition to fetch entries or items for + * @param paginationConfig the required pagination configuration + * @param sortConfig the required sort configuration + * @param fetchThumbnails optional boolean for fetching thumbnails + * @returns BrowseEntrySearchOptions instance + */ +export function getBrowseSearchOptions(defaultBrowseId: string, + paginationConfig: PaginationComponentOptions, + sortConfig: SortOptions, + fetchThumbnails?: boolean) { + if (!hasValue(fetchThumbnails)) { + fetchThumbnails = false; + } + return new BrowseEntrySearchOptions(defaultBrowseId, paginationConfig, sortConfig, null, + null, fetchThumbnails); +} + /** * Function to transform query and url parameters into searchOptions used to fetch browse entries or items * @param params URL and query parameters * @param paginationConfig Pagination configuration * @param sortConfig Sorting configuration * @param metadata Optional metadata definition to fetch browse entries/items for + * @param fetchThumbnail Optional parameter for requesting thumbnail images */ export function browseParamsToOptions(params: any, paginationConfig: PaginationComponentOptions, sortConfig: SortOptions, - metadata?: string): BrowseEntrySearchOptions { + metadata?: string, + fetchThumbnail?: boolean): BrowseEntrySearchOptions { return new BrowseEntrySearchOptions( metadata, paginationConfig, sortConfig, +params.startsWith || params.startsWith, - params.scope + params.scope, + fetchThumbnail ); } diff --git a/src/app/browse-by/browse-by-title-page/browse-by-title-page.component.spec.ts b/src/app/browse-by/browse-by-title-page/browse-by-title-page.component.spec.ts index 600eab575b..e32c0ac430 100644 --- a/src/app/browse-by/browse-by-title-page/browse-by-title-page.component.spec.ts +++ b/src/app/browse-by/browse-by-title-page/browse-by-title-page.component.spec.ts @@ -18,13 +18,11 @@ import { BrowseService } from '../../core/browse/browse.service'; import { RouterMock } from '../../shared/mocks/router.mock'; import { VarDirective } from '../../shared/utils/var.directive'; import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; -import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model'; -import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model'; import { PaginationService } from '../../core/pagination/pagination.service'; import { PaginationServiceStub } from '../../shared/testing/pagination-service.stub'; -import { FindListOptions } from '../../core/data/find-list-options.model'; -import { APP_CONFIG } from 'src/config/app-config.interface'; -import { environment } from 'src/environments/environment'; +import { APP_CONFIG } from '../../../config/app-config.interface'; +import { environment } from '../../../environments/environment'; + describe('BrowseByTitlePageComponent', () => { let comp: BrowseByTitlePageComponent; diff --git a/src/app/browse-by/browse-by-title-page/browse-by-title-page.component.ts b/src/app/browse-by/browse-by-title-page/browse-by-title-page.component.ts index a6528bed49..85693ccecd 100644 --- a/src/app/browse-by/browse-by-title-page/browse-by-title-page.component.ts +++ b/src/app/browse-by/browse-by-title-page/browse-by-title-page.component.ts @@ -4,9 +4,8 @@ import { ActivatedRoute, Params, Router } from '@angular/router'; import { hasValue } from '../../shared/empty.util'; import { BrowseByMetadataPageComponent, - browseParamsToOptions + browseParamsToOptions, getBrowseSearchOptions } from '../browse-by-metadata-page/browse-by-metadata-page.component'; -import { BrowseEntrySearchOptions } from '../../core/browse/browse-entry-search-options.model'; import { DSpaceObjectDataService } from '../../core/data/dspace-object-data.service'; import { BrowseService } from '../../core/browse/browse.service'; import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model'; @@ -32,13 +31,14 @@ export class BrowseByTitlePageComponent extends BrowseByMetadataPageComponent { protected dsoService: DSpaceObjectDataService, protected paginationService: PaginationService, protected router: Router, - @Inject(APP_CONFIG) protected appConfig: AppConfig) { + @Inject(APP_CONFIG) public appConfig: AppConfig) { super(route, browseService, dsoService, paginationService, router, appConfig); } ngOnInit(): void { const sortConfig = new SortOptions('dc.title', SortDirection.ASC); - this.updatePage(new BrowseEntrySearchOptions(this.defaultBrowseId, this.paginationConfig, sortConfig)); + // include the thumbnail configuration in browse search options + this.updatePage(getBrowseSearchOptions(this.defaultBrowseId, this.paginationConfig, sortConfig, this.fetchThumbnails)); this.currentPagination$ = this.paginationService.getCurrentPagination(this.paginationConfig.id, this.paginationConfig); this.currentSort$ = this.paginationService.getCurrentSort(this.paginationConfig.id, sortConfig); this.subs.push( @@ -49,7 +49,7 @@ export class BrowseByTitlePageComponent extends BrowseByMetadataPageComponent { ).subscribe(([params, currentPage, currentSort]: [Params, PaginationComponentOptions, SortOptions]) => { this.startsWith = +params.startsWith || params.startsWith; this.browseId = params.id || this.defaultBrowseId; - this.updatePageWithItems(browseParamsToOptions(params, currentPage, currentSort, this.browseId), undefined, undefined); + this.updatePageWithItems(browseParamsToOptions(params, currentPage, currentSort, this.browseId, this.fetchThumbnails), undefined, undefined); this.updateParent(params.scope); })); this.updateStartsWithTextOptions(); diff --git a/src/app/collection-page/collection-page.component.ts b/src/app/collection-page/collection-page.component.ts index 09471d4c6d..f730f44658 100644 --- a/src/app/collection-page/collection-page.component.ts +++ b/src/app/collection-page/collection-page.component.ts @@ -28,6 +28,7 @@ import { AuthorizationDataService } from '../core/data/feature-authorization/aut import { FeatureID } from '../core/data/feature-authorization/feature-id'; import { getCollectionPageRoute } from './collection-page-routing-paths'; import { redirectOn4xx } from '../core/shared/authorized.operators'; +import { BROWSE_LINKS_TO_FOLLOW } from '../core/browse/browse.service'; @Component({ selector: 'ds-collection-page', @@ -74,6 +75,7 @@ export class CollectionPageComponent implements OnInit { this.paginationConfig.pageSize = 5; this.paginationConfig.currentPage = 1; this.sortConfig = new SortOptions('dc.date.accessioned', SortDirection.DESC); + } ngOnInit(): void { @@ -102,13 +104,14 @@ export class CollectionPageComponent implements OnInit { getFirstSucceededRemoteData(), map((rd) => rd.payload.id), switchMap((id: string) => { - return this.searchService.search( + return this.searchService.search( new PaginatedSearchOptions({ scope: id, pagination: currentPagination, sort: currentSort, dsoTypes: [DSpaceObjectType.ITEM] - })).pipe(toDSpaceObjectListRD()) as Observable>>; + }), null, true, true, ...BROWSE_LINKS_TO_FOLLOW) + .pipe(toDSpaceObjectListRD()) as Observable>>; }), startWith(undefined) // Make sure switching pages shows loading component ) diff --git a/src/app/collection-page/collection-page.resolver.ts b/src/app/collection-page/collection-page.resolver.ts index d476a180d3..2f5b3ed37a 100644 --- a/src/app/collection-page/collection-page.resolver.ts +++ b/src/app/collection-page/collection-page.resolver.ts @@ -17,7 +17,7 @@ export const COLLECTION_PAGE_LINKS_TO_FOLLOW: FollowLinkConfig[] = [ followLink('parentCommunity', {}, followLink('parentCommunity') ), - followLink('logo') + followLink('logo'), ]; /** diff --git a/src/app/core/browse/browse-entry-search-options.model.ts b/src/app/core/browse/browse-entry-search-options.model.ts index 417bf7ce75..1604c693e9 100644 --- a/src/app/core/browse/browse-entry-search-options.model.ts +++ b/src/app/core/browse/browse-entry-search-options.model.ts @@ -6,13 +6,16 @@ import { SortOptions } from '../cache/models/sort-options.model'; * - metadataDefinition: The metadata definition to fetch entries or items for * - pagination: Optional pagination options to use * - sort: Optional sorting options to use + * - startsWith An optional value to use to filter the browse results * - scope: An optional scope to limit the results within a specific collection or community + * - fetchThumbnail An optional boolean to request thumbnail for items */ export class BrowseEntrySearchOptions { constructor(public metadataDefinition: string, public pagination?: PaginationComponentOptions, public sort?: SortOptions, public startsWith?: string, - public scope?: string) { + public scope?: string, + public fetchThumbnail?: boolean) { } } diff --git a/src/app/core/browse/browse.service.ts b/src/app/core/browse/browse.service.ts index 8fd11ad048..2fab189254 100644 --- a/src/app/core/browse/browse.service.ts +++ b/src/app/core/browse/browse.service.ts @@ -21,6 +21,12 @@ import { URLCombiner } from '../url-combiner/url-combiner'; import { BrowseEntrySearchOptions } from './browse-entry-search-options.model'; import { BrowseDefinitionDataService } from './browse-definition-data.service'; import { HrefOnlyDataService } from '../data/href-only-data.service'; +import { followLink, FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; + + +export const BROWSE_LINKS_TO_FOLLOW: FollowLinkConfig[] = [ + followLink('thumbnail') +]; /** * The service handling all browse requests @@ -96,6 +102,9 @@ export class BrowseService { return href; }) ); + if (options.fetchThumbnail ) { + return this.hrefOnlyDataService.findListByHref(href$, {}, null, null, ...BROWSE_LINKS_TO_FOLLOW); + } return this.hrefOnlyDataService.findListByHref(href$); } @@ -141,6 +150,9 @@ export class BrowseService { return href; }), ); + if (options.fetchThumbnail) { + return this.hrefOnlyDataService.findListByHref(href$, {}, null, null, ...BROWSE_LINKS_TO_FOLLOW); + } return this.hrefOnlyDataService.findListByHref(href$); } diff --git a/src/app/core/data/find-list-options.model.ts b/src/app/core/data/find-list-options.model.ts index 52a527d9e0..dc567d4b53 100644 --- a/src/app/core/data/find-list-options.model.ts +++ b/src/app/core/data/find-list-options.model.ts @@ -11,4 +11,5 @@ export class FindListOptions { sort?: SortOptions; searchParams?: RequestParam[]; startsWith?: string; + fetchThumbnail?: boolean; } diff --git a/src/app/core/data/relationship-data.service.spec.ts b/src/app/core/data/relationship-data.service.spec.ts index 2f0ecd9cf4..39aa7cbd58 100644 --- a/src/app/core/data/relationship-data.service.spec.ts +++ b/src/app/core/data/relationship-data.service.spec.ts @@ -202,6 +202,7 @@ describe('RelationshipDataService', () => { }); it('should call getItemRelationshipsByLabel with the correct params', (done) => { + mockOptions = Object.assign(mockOptions, { fetchThumbnail: true }); service.getRelatedItemsByLabel( mockItem, mockLabel, @@ -213,8 +214,8 @@ describe('RelationshipDataService', () => { mockOptions, true, true, - followLink('leftItem'), - followLink('rightItem'), + followLink('leftItem',{}, followLink('thumbnail')), + followLink('rightItem',{}, followLink('thumbnail')), followLink('relationshipType') ); done(); diff --git a/src/app/core/data/relationship-data.service.ts b/src/app/core/data/relationship-data.service.ts index d14a609a96..85adeff754 100644 --- a/src/app/core/data/relationship-data.service.ts +++ b/src/app/core/data/relationship-data.service.ts @@ -45,6 +45,7 @@ import { SearchData, SearchDataImpl } from './base/search-data'; import { PutData, PutDataImpl } from './base/put-data'; import { IdentifiableDataService } from './base/identifiable-data.service'; import { dataService } from './base/data-service.decorator'; +import { itemLinksToFollow } from '../../shared/utils/relation-query.utils'; const relationshipListsStateSelector = (state: AppState) => state.relationshipLists; @@ -185,7 +186,7 @@ export class RelationshipDataService extends IdentifiableDataService !existsInOC && !existsInRC), take(1), - ).subscribe(() => this.itemService.findByHref(item._links.self.href, false)); + ).subscribe(() => this.itemService.findByHref(item._links.self.href)); } /** @@ -258,7 +259,10 @@ export class RelationshipDataService extends IdentifiableDataService>> { - return this.getItemRelationshipsByLabel(item, label, options, true, true, followLink('leftItem'), followLink('rightItem'), followLink('relationshipType')).pipe(this.paginatedRelationsToItems(item.uuid)); + let linksToFollow: FollowLinkConfig[] = itemLinksToFollow(options.fetchThumbnail); + linksToFollow.push(followLink('relationshipType')); + + return this.getItemRelationshipsByLabel(item, label, options, true, true, ...linksToFollow).pipe(this.paginatedRelationsToItems(item.uuid)); } /** @@ -516,14 +520,14 @@ export class RelationshipDataService extends IdentifiableDataService>>; diff --git a/src/app/core/shared/browse-entry.model.ts b/src/app/core/shared/browse-entry.model.ts index df748ac61c..d4842e1294 100644 --- a/src/app/core/shared/browse-entry.model.ts +++ b/src/app/core/shared/browse-entry.model.ts @@ -40,6 +40,12 @@ export class BrowseEntry extends ListableObject implements TypedObject { @autoserializeAs('valueLang') language: string; + /** + * Thumbnail link used when browsing items with showThumbs config enabled. + */ + @autoserializeAs('thumbnail') + thumbnail: string; + /** * The count of this browse entry */ @@ -51,6 +57,7 @@ export class BrowseEntry extends ListableObject implements TypedObject { _links: { self: HALLink; entries: HALLink; + thumbnail: HALLink; }; /** diff --git a/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal-issue/journal-issue-search-result-list-element.component.html b/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal-issue/journal-issue-search-result-list-element.component.html index 6d88c9761b..d93639a5d9 100644 --- a/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal-issue/journal-issue-search-result-list-element.component.html +++ b/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal-issue/journal-issue-search-result-list-element.component.html @@ -1,12 +1,23 @@ - - - - - +
+ +
+ + + + + @@ -22,4 +33,6 @@ - + +
+
diff --git a/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal-issue/journal-issue-search-result-list-element.component.spec.ts b/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal-issue/journal-issue-search-result-list-element.component.spec.ts index b47a767be2..178ed86c40 100644 --- a/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal-issue/journal-issue-search-result-list-element.component.spec.ts +++ b/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal-issue/journal-issue-search-result-list-element.component.spec.ts @@ -9,6 +9,7 @@ import { TruncatePipe } from '../../../../../shared/utils/truncate.pipe'; import { TruncatableService } from '../../../../../shared/truncatable/truncatable.service'; import { DSONameService } from '../../../../../core/breadcrumbs/dso-name.service'; import { DSONameServiceMock } from '../../../../../shared/mocks/dso-name.service.mock'; +import { APP_CONFIG } from '../../../../../../config/app-config.interface'; let journalIssueListElementComponent: JournalIssueSearchResultListElementComponent; let fixture: ComponentFixture; @@ -57,13 +58,26 @@ const mockItemWithoutMetadata: ItemSearchResult = Object.assign( }) }); +const environmentUseThumbs = { + browseBy: { + showThumbnails: true + } +}; + +const enviromentNoThumbs = { + browseBy: { + showThumbnails: false + } +}; + describe('JournalIssueSearchResultListElementComponent', () => { beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ declarations: [JournalIssueSearchResultListElementComponent, TruncatePipe], providers: [ { provide: TruncatableService, useValue: {} }, - { provide: DSONameService, useClass: DSONameServiceMock } + { provide: DSONameService, useClass: DSONameServiceMock }, + { provide: APP_CONFIG, useValue: environmentUseThumbs } ], schemas: [NO_ERRORS_SCHEMA] @@ -78,6 +92,22 @@ describe('JournalIssueSearchResultListElementComponent', () => { })); + describe('with environment.browseBy.showThumbnails set to true', () => { + beforeEach(() => { + journalIssueListElementComponent.object = mockItemWithMetadata; + fixture.detectChanges(); + }); + it('should set showThumbnails to true', () => { + expect(journalIssueListElementComponent.showThumbnails).toBeTrue(); + }); + + it('should add thumbnail element', () => { + const thumbnailElement = fixture.debugElement.query(By.css('ds-thumbnail')); + expect(thumbnailElement).toBeTruthy(); + }); + }); + + describe('When the item has a journal identifier', () => { beforeEach(() => { journalIssueListElementComponent.object = mockItemWithMetadata; @@ -126,3 +156,39 @@ describe('JournalIssueSearchResultListElementComponent', () => { }); }); }); + +describe('JournalIssueSearchResultListElementComponent', () => { + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [JournalIssueSearchResultListElementComponent, TruncatePipe], + providers: [ + {provide: TruncatableService, useValue: {}}, + {provide: DSONameService, useClass: DSONameServiceMock}, + { provide: APP_CONFIG, useValue: enviromentNoThumbs } + ], + + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(JournalIssueSearchResultListElementComponent, { + set: {changeDetection: ChangeDetectionStrategy.Default} + }).compileComponents(); + })); + + beforeEach(waitForAsync(() => { + fixture = TestBed.createComponent(JournalIssueSearchResultListElementComponent); + journalIssueListElementComponent = fixture.componentInstance; + })); + + describe('with environment.browseBy.showThumbnails set to false', () => { + beforeEach(() => { + + journalIssueListElementComponent.object = mockItemWithMetadata; + fixture.detectChanges(); + }); + + it('should not add thumbnail element', () => { + const thumbnailElement = fixture.debugElement.query(By.css('ds-thumbnail')); + expect(thumbnailElement).toBeFalsy(); + }); + }); +}); diff --git a/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal-issue/journal-issue-search-result-list-element.component.ts b/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal-issue/journal-issue-search-result-list-element.component.ts index dcb54b8d39..88f93d9d85 100644 --- a/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal-issue/journal-issue-search-result-list-element.component.ts +++ b/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal-issue/journal-issue-search-result-list-element.component.ts @@ -13,4 +13,15 @@ import { ItemSearchResultListElementComponent } from '../../../../../shared/obje * The component for displaying a list element for an item search result of the type Journal Issue */ export class JournalIssueSearchResultListElementComponent extends ItemSearchResultListElementComponent { + + /** + * Display thumbnails if required by configuration + */ + showThumbnails: boolean; + + ngOnInit(): void { + super.ngOnInit(); + this.showThumbnails = this.appConfig.browseBy.showThumbnails; + } + } diff --git a/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal-volume/journal-volume-search-result-list-element.component.html b/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal-volume/journal-volume-search-result-list-element.component.html index b7cb645e31..2226d03649 100644 --- a/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal-volume/journal-volume-search-result-list-element.component.html +++ b/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal-volume/journal-volume-search-result-list-element.component.html @@ -1,12 +1,23 @@ - - - - - +
+ +
+ + + + + @@ -22,4 +33,6 @@ - + +
+
diff --git a/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal-volume/journal-volume-search-result-list-element.component.spec.ts b/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal-volume/journal-volume-search-result-list-element.component.spec.ts index d03bc29d6b..71fa83a3b1 100644 --- a/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal-volume/journal-volume-search-result-list-element.component.spec.ts +++ b/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal-volume/journal-volume-search-result-list-element.component.spec.ts @@ -9,6 +9,7 @@ import { TruncatePipe } from '../../../../../shared/utils/truncate.pipe'; import { TruncatableService } from '../../../../../shared/truncatable/truncatable.service'; import { DSONameService } from '../../../../../core/breadcrumbs/dso-name.service'; import { DSONameServiceMock } from '../../../../../shared/mocks/dso-name.service.mock'; +import { APP_CONFIG } from '../../../../../../config/app-config.interface'; let journalVolumeListElementComponent: JournalVolumeSearchResultListElementComponent; let fixture: ComponentFixture; @@ -56,6 +57,18 @@ const mockItemWithoutMetadata: ItemSearchResult = Object.assign( }) }); +const environmentUseThumbs = { + browseBy: { + showThumbnails: true + } +}; + +const enviromentNoThumbs = { + browseBy: { + showThumbnails: false + } +}; + describe('JournalVolumeSearchResultListElementComponent', () => { beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ @@ -63,6 +76,7 @@ describe('JournalVolumeSearchResultListElementComponent', () => { providers: [ { provide: TruncatableService, useValue: {} }, { provide: DSONameService, useClass: DSONameServiceMock }, + { provide: APP_CONFIG, useValue: environmentUseThumbs } ], schemas: [NO_ERRORS_SCHEMA] @@ -77,6 +91,21 @@ describe('JournalVolumeSearchResultListElementComponent', () => { })); + describe('with environment.browseBy.showThumbnails set to true', () => { + beforeEach(() => { + journalVolumeListElementComponent.object = mockItemWithMetadata; + fixture.detectChanges(); + }); + it('should set showThumbnails to true', () => { + expect(journalVolumeListElementComponent.showThumbnails).toBeTrue(); + }); + + it('should add thumbnail element', () => { + const thumbnailElement = fixture.debugElement.query(By.css('ds-thumbnail')); + expect(thumbnailElement).toBeTruthy(); + }); + }); + describe('When the item has a journal title', () => { beforeEach(() => { journalVolumeListElementComponent.object = mockItemWithMetadata; @@ -125,3 +154,38 @@ describe('JournalVolumeSearchResultListElementComponent', () => { }); }); }); + +describe('JournalVolumeSearchResultListElementComponent', () => { + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [JournalVolumeSearchResultListElementComponent, TruncatePipe], + providers: [ + {provide: TruncatableService, useValue: {}}, + {provide: DSONameService, useClass: DSONameServiceMock}, + { provide: APP_CONFIG, useValue: enviromentNoThumbs } + ], + + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(JournalVolumeSearchResultListElementComponent, { + set: {changeDetection: ChangeDetectionStrategy.Default} + }).compileComponents(); + })); + + beforeEach(waitForAsync(() => { + fixture = TestBed.createComponent(JournalVolumeSearchResultListElementComponent); + journalVolumeListElementComponent = fixture.componentInstance; + })); + + describe('with environment.browseBy.showThumbnails set to false', () => { + beforeEach(() => { + journalVolumeListElementComponent.object = mockItemWithMetadata; + fixture.detectChanges(); + }); + + it('should not add thumbnail element', () => { + const thumbnailElement = fixture.debugElement.query(By.css('ds-thumbnail')); + expect(thumbnailElement).toBeFalsy(); + }); + }); +}); diff --git a/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal-volume/journal-volume-search-result-list-element.component.ts b/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal-volume/journal-volume-search-result-list-element.component.ts index 7cba2ef591..ec98946937 100644 --- a/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal-volume/journal-volume-search-result-list-element.component.ts +++ b/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal-volume/journal-volume-search-result-list-element.component.ts @@ -13,4 +13,15 @@ import { ItemSearchResultListElementComponent } from '../../../../../shared/obje * The component for displaying a list element for an item search result of the type Journal Volume */ export class JournalVolumeSearchResultListElementComponent extends ItemSearchResultListElementComponent { + + /** + * Display thumbnails if required by configuration + */ + showThumbnails: boolean; + + ngOnInit(): void { + super.ngOnInit(); + this.showThumbnails = this.appConfig.browseBy.showThumbnails; + } + } diff --git a/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal/journal-search-result-list-element.component.html b/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal/journal-search-result-list-element.component.html index 988fb2d4b5..3cafccf0b9 100644 --- a/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal/journal-search-result-list-element.component.html +++ b/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal/journal-search-result-list-element.component.html @@ -1,12 +1,21 @@ - - +
+
- - + [routerLink]="[itemPageRoute]" class="lead item-list-title dont-break-out"> + + + +
+
+ + + + + @@ -16,4 +25,6 @@ - + +
+
diff --git a/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal/journal-search-result-list-element.component.spec.ts b/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal/journal-search-result-list-element.component.spec.ts index 9aca414ec6..07970d7128 100644 --- a/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal/journal-search-result-list-element.component.spec.ts +++ b/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal/journal-search-result-list-element.component.spec.ts @@ -9,6 +9,7 @@ import { TruncatableService } from '../../../../../shared/truncatable/truncatabl import { ItemSearchResult } from '../../../../../shared/object-collection/shared/item-search-result.model'; import { DSONameService } from '../../../../../core/breadcrumbs/dso-name.service'; import { DSONameServiceMock } from '../../../../../shared/mocks/dso-name.service.mock'; +import { APP_CONFIG } from '../../../../../../config/app-config.interface'; let journalListElementComponent: JournalSearchResultListElementComponent; let fixture: ComponentFixture; @@ -52,6 +53,18 @@ const mockItemWithoutMetadata: ItemSearchResult = Object.assign( } ); +const environmentUseThumbs = { + browseBy: { + showThumbnails: true + } +}; + +const enviromentNoThumbs = { + browseBy: { + showThumbnails: false + } +}; + describe('JournalSearchResultListElementComponent', () => { beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ @@ -59,6 +72,7 @@ describe('JournalSearchResultListElementComponent', () => { providers: [ { provide: TruncatableService, useValue: {} }, { provide: DSONameService, useClass: DSONameServiceMock }, + { provide: APP_CONFIG, useValue: environmentUseThumbs } ], schemas: [NO_ERRORS_SCHEMA] @@ -73,6 +87,21 @@ describe('JournalSearchResultListElementComponent', () => { })); + describe('with environment.browseBy.showThumbnails set to true', () => { + beforeEach(() => { + journalListElementComponent.object = mockItemWithMetadata; + fixture.detectChanges(); + }); + it('should set showThumbnails to true', () => { + expect(journalListElementComponent.showThumbnails).toBeTrue(); + }); + + it('should add thumbnail element', () => { + const thumbnailElement = fixture.debugElement.query(By.css('ds-thumbnail')); + expect(thumbnailElement).toBeTruthy(); + }); + }); + describe('When the item has an issn', () => { beforeEach(() => { journalListElementComponent.object = mockItemWithMetadata; @@ -97,3 +126,39 @@ describe('JournalSearchResultListElementComponent', () => { }); }); }); + +describe('JournalSearchResultListElementComponent', () => { + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [JournalSearchResultListElementComponent, TruncatePipe], + providers: [ + {provide: TruncatableService, useValue: {}}, + {provide: DSONameService, useClass: DSONameServiceMock}, + { provide: APP_CONFIG, useValue: enviromentNoThumbs } + ], + + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(JournalSearchResultListElementComponent, { + set: {changeDetection: ChangeDetectionStrategy.Default} + }).compileComponents(); + })); + + beforeEach(waitForAsync(() => { + fixture = TestBed.createComponent(JournalSearchResultListElementComponent); + journalListElementComponent = fixture.componentInstance; + })); + + describe('with environment.browseBy.showThumbnails set to false', () => { + beforeEach(() => { + + journalListElementComponent.object = mockItemWithMetadata; + fixture.detectChanges(); + }); + + it('should not add thumbnail element', () => { + const thumbnailElement = fixture.debugElement.query(By.css('ds-thumbnail')); + expect(thumbnailElement).toBeFalsy(); + }); + }); +}); diff --git a/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal/journal-search-result-list-element.component.ts b/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal/journal-search-result-list-element.component.ts index 85460b47e4..199bd3a748 100644 --- a/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal/journal-search-result-list-element.component.ts +++ b/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal/journal-search-result-list-element.component.ts @@ -13,4 +13,15 @@ import { ItemSearchResultListElementComponent } from '../../../../../shared/obje * The component for displaying a list element for an item search result of the type Journal */ export class JournalSearchResultListElementComponent extends ItemSearchResultListElementComponent { + + /** + * Display thumbnails if required by configuration + */ + showThumbnails: boolean; + + ngOnInit(): void { + super.ngOnInit(); + this.showThumbnails = this.appConfig.browseBy.showThumbnails; + } + } diff --git a/src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/org-unit/org-unit-search-result-list-element.component.html b/src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/org-unit/org-unit-search-result-list-element.component.html index 40f837bcd1..f3f5099211 100644 --- a/src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/org-unit/org-unit-search-result-list-element.component.html +++ b/src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/org-unit/org-unit-search-result-list-element.component.html @@ -1,17 +1,33 @@ - - - - - +
+ +
+ + + + + + [innerHTML]="firstMetadataValue('dc.description')"> - + +
+
diff --git a/src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/org-unit/org-unit-search-result-list-element.component.spec.ts b/src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/org-unit/org-unit-search-result-list-element.component.spec.ts index d116b2ade7..974c418cdc 100644 --- a/src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/org-unit/org-unit-search-result-list-element.component.spec.ts +++ b/src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/org-unit/org-unit-search-result-list-element.component.spec.ts @@ -9,6 +9,7 @@ import { TruncatableService } from '../../../../../shared/truncatable/truncatabl import { ItemSearchResult } from '../../../../../shared/object-collection/shared/item-search-result.model'; import { DSONameService } from '../../../../../core/breadcrumbs/dso-name.service'; import { DSONameServiceMock } from '../../../../../shared/mocks/dso-name.service.mock'; +import { APP_CONFIG } from '../../../../../../config/app-config.interface'; let orgUnitListElementComponent: OrgUnitSearchResultListElementComponent; let fixture: ComponentFixture; @@ -50,13 +51,26 @@ const mockItemWithoutMetadata: ItemSearchResult = Object.assign( }) }); +const environmentUseThumbs = { + browseBy: { + showThumbnails: true + } +}; + +const enviromentNoThumbs = { + browseBy: { + showThumbnails: false + } +}; + describe('OrgUnitSearchResultListElementComponent', () => { beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ declarations: [ OrgUnitSearchResultListElementComponent , TruncatePipe], providers: [ { provide: TruncatableService, useValue: {} }, - { provide: DSONameService, useClass: DSONameServiceMock } + { provide: DSONameService, useClass: DSONameServiceMock }, + { provide: APP_CONFIG, useValue: environmentUseThumbs } ], schemas: [ NO_ERRORS_SCHEMA ] @@ -71,6 +85,21 @@ describe('OrgUnitSearchResultListElementComponent', () => { })); + describe('with environment.browseBy.showThumbnails set to true', () => { + beforeEach(() => { + orgUnitListElementComponent.object = mockItemWithMetadata; + fixture.detectChanges(); + }); + it('should set showThumbnails to true', () => { + expect(orgUnitListElementComponent.showThumbnails).toBeTrue(); + }); + + it('should add thumbnail element', () => { + const thumbnailElement = fixture.debugElement.query(By.css('ds-thumbnail')); + expect(thumbnailElement).toBeTruthy(); + }); + }); + describe('When the item has an org unit description', () => { beforeEach(() => { orgUnitListElementComponent.object = mockItemWithMetadata; @@ -95,3 +124,39 @@ describe('OrgUnitSearchResultListElementComponent', () => { }); }); }); + +describe('OrgUnitSearchResultListElementComponent', () => { + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [OrgUnitSearchResultListElementComponent, TruncatePipe], + providers: [ + {provide: TruncatableService, useValue: {}}, + {provide: DSONameService, useClass: DSONameServiceMock}, + { provide: APP_CONFIG, useValue: enviromentNoThumbs } + ], + + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(OrgUnitSearchResultListElementComponent, { + set: {changeDetection: ChangeDetectionStrategy.Default} + }).compileComponents(); + })); + + beforeEach(waitForAsync(() => { + fixture = TestBed.createComponent(OrgUnitSearchResultListElementComponent); + orgUnitListElementComponent = fixture.componentInstance; + })); + + describe('with environment.browseBy.showThumbnails set to false', () => { + beforeEach(() => { + + orgUnitListElementComponent.object = mockItemWithMetadata; + fixture.detectChanges(); + }); + + it('should not add thumbnail element', () => { + const thumbnailElement = fixture.debugElement.query(By.css('ds-thumbnail')); + expect(thumbnailElement).toBeNull(); + }); + }); +}); diff --git a/src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/org-unit/org-unit-search-result-list-element.component.ts b/src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/org-unit/org-unit-search-result-list-element.component.ts index 2165b7d94d..baa27cdf0c 100644 --- a/src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/org-unit/org-unit-search-result-list-element.component.ts +++ b/src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/org-unit/org-unit-search-result-list-element.component.ts @@ -13,4 +13,15 @@ import { ItemSearchResultListElementComponent } from '../../../../../shared/obje * The component for displaying a list element for an item search result of the type Organisation Unit */ export class OrgUnitSearchResultListElementComponent extends ItemSearchResultListElementComponent { + + /** + * Display thumbnail if required by configuration + */ + showThumbnails: boolean; + + ngOnInit(): void { + super.ngOnInit(); + this.showThumbnails = this.appConfig.browseBy.showThumbnails; + } + } diff --git a/src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/person/person-search-result-list-element.component.html b/src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/person/person-search-result-list-element.component.html index 6d9cfe10c4..e3c7767802 100644 --- a/src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/person/person-search-result-list-element.component.html +++ b/src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/person/person-search-result-list-element.component.html @@ -1,12 +1,26 @@ - - - - - +
+ +
+ + + + + @@ -16,4 +30,7 @@ - + +
+
+ diff --git a/src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/person/person-search-result-list-element.component.spec.ts b/src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/person/person-search-result-list-element.component.spec.ts index 2acb02da7d..6c69bfd58d 100644 --- a/src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/person/person-search-result-list-element.component.spec.ts +++ b/src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/person/person-search-result-list-element.component.spec.ts @@ -9,6 +9,7 @@ import { TruncatePipe } from '../../../../../shared/utils/truncate.pipe'; import { TruncatableService } from '../../../../../shared/truncatable/truncatable.service'; import { DSONameService } from '../../../../../core/breadcrumbs/dso-name.service'; import { DSONameServiceMock } from '../../../../../shared/mocks/dso-name.service.mock'; +import { APP_CONFIG } from '../../../../../../config/app-config.interface'; let personListElementComponent: PersonSearchResultListElementComponent; let fixture: ComponentFixture; @@ -50,13 +51,26 @@ const mockItemWithoutMetadata: ItemSearchResult = Object.assign( }) }); +const environmentUseThumbs = { + browseBy: { + showThumbnails: true + } +}; + +const enviromentNoThumbs = { + browseBy: { + showThumbnails: false + } +}; + describe('PersonSearchResultListElementComponent', () => { beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ declarations: [PersonSearchResultListElementComponent, TruncatePipe], providers: [ { provide: TruncatableService, useValue: {} }, - { provide: DSONameService, useClass: DSONameServiceMock } + { provide: DSONameService, useClass: DSONameServiceMock }, + { provide: APP_CONFIG, useValue: environmentUseThumbs } ], schemas: [NO_ERRORS_SCHEMA] @@ -71,6 +85,21 @@ describe('PersonSearchResultListElementComponent', () => { })); + describe('with environment.browseBy.showThumbnails set to true', () => { + beforeEach(() => { + personListElementComponent.object = mockItemWithMetadata; + fixture.detectChanges(); + }); + it('should set showThumbnails to true', () => { + expect(personListElementComponent.showThumbnails).toBeTrue(); + }); + + it('should add thumbnail element', () => { + const thumbnailElement = fixture.debugElement.query(By.css('ds-thumbnail')); + expect(thumbnailElement).toBeTruthy(); + }); + }); + describe('When the item has a job title', () => { beforeEach(() => { personListElementComponent.object = mockItemWithMetadata; @@ -95,3 +124,39 @@ describe('PersonSearchResultListElementComponent', () => { }); }); }); + +describe('PersonSearchResultListElementComponent', () => { + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [PersonSearchResultListElementComponent, TruncatePipe], + providers: [ + {provide: TruncatableService, useValue: {}}, + {provide: DSONameService, useClass: DSONameServiceMock}, + { provide: APP_CONFIG, useValue: enviromentNoThumbs } + ], + + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(PersonSearchResultListElementComponent, { + set: {changeDetection: ChangeDetectionStrategy.Default} + }).compileComponents(); + })); + + beforeEach(waitForAsync(() => { + fixture = TestBed.createComponent(PersonSearchResultListElementComponent); + personListElementComponent = fixture.componentInstance; + })); + + describe('with environment.browseBy.showThumbnails set to false', () => { + beforeEach(() => { + + personListElementComponent.object = mockItemWithMetadata; + fixture.detectChanges(); + }); + + it('should not add thumbnail element', () => { + const thumbnailElement = fixture.debugElement.query(By.css('ds-thumbnail')); + expect(thumbnailElement).toBeFalsy(); + }); + }); +}); diff --git a/src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/person/person-search-result-list-element.component.ts b/src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/person/person-search-result-list-element.component.ts index 3b222ce27c..186c79eab9 100644 --- a/src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/person/person-search-result-list-element.component.ts +++ b/src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/person/person-search-result-list-element.component.ts @@ -1,4 +1,4 @@ -import { Component } from '@angular/core'; +import { Component, Inject } from '@angular/core'; import { listableObjectComponent } from '../../../../../shared/object-collection/shared/listable-object/listable-object.decorator'; @@ -8,6 +8,7 @@ import { } from '../../../../../shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component'; import { TruncatableService } from '../../../../../shared/truncatable/truncatable.service'; import { DSONameService } from '../../../../../core/breadcrumbs/dso-name.service'; +import { APP_CONFIG, AppConfig } from '../../../../../../config/app-config.interface'; @listableObjectComponent('PersonSearchResult', ViewMode.ListElement) @Component({ @@ -20,8 +21,20 @@ import { DSONameService } from '../../../../../core/breadcrumbs/dso-name.service */ export class PersonSearchResultListElementComponent extends ItemSearchResultListElementComponent { - public constructor(protected truncatableService: TruncatableService, protected dsoNameService: DSONameService) { - super(truncatableService, dsoNameService); + public constructor(protected truncatableService: TruncatableService, + protected dsoNameService: DSONameService, + @Inject(APP_CONFIG) protected appConfig: AppConfig ) { + super(truncatableService, dsoNameService, appConfig); + } + + /** + * Display thumbnail if required by configuration + */ + showThumbnails: boolean; + + ngOnInit(): void { + super.ngOnInit(); + this.showThumbnails = this.appConfig.browseBy.showThumbnails; } /** diff --git a/src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/project/project-search-result-list-element.component.html b/src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/project/project-search-result-list-element.component.html index d711ad7c18..3cfc6eaeb4 100644 --- a/src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/project/project-search-result-list-element.component.html +++ b/src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/project/project-search-result-list-element.component.html @@ -1,19 +1,35 @@ - - - - - - - - - - - - - - - +
+ +
+ + + + + + + + + + + + + + + +
+
diff --git a/src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/project/project-search-result-list-element.component.spec.ts b/src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/project/project-search-result-list-element.component.spec.ts index 8b27a86f6d..0cb3e63e87 100644 --- a/src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/project/project-search-result-list-element.component.spec.ts +++ b/src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/project/project-search-result-list-element.component.spec.ts @@ -8,6 +8,8 @@ import { TruncatePipe } from '../../../../../shared/utils/truncate.pipe'; import { TruncatableService } from '../../../../../shared/truncatable/truncatable.service'; import { DSONameService } from '../../../../../core/breadcrumbs/dso-name.service'; import { DSONameServiceMock } from '../../../../../shared/mocks/dso-name.service.mock'; +import { By } from '@angular/platform-browser'; +import { APP_CONFIG } from '../../../../../../config/app-config.interface'; let projectListElementComponent: ProjectSearchResultListElementComponent; let fixture: ComponentFixture; @@ -50,13 +52,26 @@ const mockItemWithoutMetadata: ItemSearchResult = Object.assign( }) }); +const environmentUseThumbs = { + browseBy: { + showThumbnails: true + } +}; + +const enviromentNoThumbs = { + browseBy: { + showThumbnails: false + } +}; + describe('ProjectSearchResultListElementComponent', () => { beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ declarations: [ProjectSearchResultListElementComponent, TruncatePipe], providers: [ { provide: TruncatableService, useValue: {} }, - { provide: DSONameService, useClass: DSONameServiceMock } + { provide: DSONameService, useClass: DSONameServiceMock }, + { provide: APP_CONFIG, useValue: environmentUseThumbs } ], schemas: [NO_ERRORS_SCHEMA] @@ -71,6 +86,21 @@ describe('ProjectSearchResultListElementComponent', () => { })); + describe('with environment.browseBy.showThumbnails set to true', () => { + beforeEach(() => { + projectListElementComponent.object = mockItemWithMetadata; + fixture.detectChanges(); + }); + it('should set showThumbnails to true', () => { + expect(projectListElementComponent.showThumbnails).toBeTrue(); + }); + + it('should add thumbnail element', () => { + const thumbnailElement = fixture.debugElement.query(By.css('ds-thumbnail')); + expect(thumbnailElement).toBeTruthy(); + }); + }); + // describe('When the item has a status', () => { // beforeEach(() => { // projectListElementComponent.item = mockItemWithMetadata; @@ -95,3 +125,40 @@ describe('ProjectSearchResultListElementComponent', () => { // }); // }); }); + +describe('ProjectSearchResultListElementComponent', () => { + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [ProjectSearchResultListElementComponent, TruncatePipe], + providers: [ + {provide: TruncatableService, useValue: {}}, + {provide: DSONameService, useClass: DSONameServiceMock}, + { provide: APP_CONFIG, useValue: enviromentNoThumbs } + + ], + + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(ProjectSearchResultListElementComponent, { + set: {changeDetection: ChangeDetectionStrategy.Default} + }).compileComponents(); + })); + + beforeEach(waitForAsync(() => { + fixture = TestBed.createComponent(ProjectSearchResultListElementComponent); + projectListElementComponent = fixture.componentInstance; + })); + + describe('with environment.browseBy.showThumbnails set to false', () => { + beforeEach(() => { + + projectListElementComponent.object = mockItemWithMetadata; + fixture.detectChanges(); + }); + + it('should not add thumbnail element', () => { + const thumbnailElement = fixture.debugElement.query(By.css('ds-thumbnail')); + expect(thumbnailElement).toBeFalsy(); + }); + }); +}); diff --git a/src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/project/project-search-result-list-element.component.ts b/src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/project/project-search-result-list-element.component.ts index 6ad8c0fcaa..88e95528ac 100644 --- a/src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/project/project-search-result-list-element.component.ts +++ b/src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/project/project-search-result-list-element.component.ts @@ -13,4 +13,15 @@ import { ItemSearchResultListElementComponent } from '../../../../../shared/obje * The component for displaying a list element for an item search result of the type Project */ export class ProjectSearchResultListElementComponent extends ItemSearchResultListElementComponent { + + /** + * Display thumbnail if required by configuration + */ + showThumbnails: boolean; + + ngOnInit(): void { + super.ngOnInit(); + this.showThumbnails = this.appConfig.browseBy.showThumbnails; + } + } diff --git a/src/app/entity-groups/research-entities/item-pages/org-unit/org-unit.component.html b/src/app/entity-groups/research-entities/item-pages/org-unit/org-unit.component.html index ff4bc4d226..0ce9823ce6 100644 --- a/src/app/entity-groups/research-entities/item-pages/org-unit/org-unit.component.html +++ b/src/app/entity-groups/research-entities/item-pages/org-unit/org-unit.component.html @@ -36,7 +36,7 @@ [label]="'orgunit.page.id'"> -
+
-
+
-->
-
+
; @@ -117,7 +119,8 @@ describe('OrgUnitSearchResultListSubmissionElementComponent', () => { { provide: DSOChangeAnalyzer, useValue: {} }, { provide: DefaultChangeAnalyzer, useValue: {} }, { provide: BitstreamDataService, useValue: mockBitstreamDataService }, - { provide: DSONameService, useClass: DSONameServiceMock } + { provide: DSONameService, useClass: DSONameServiceMock }, + { provide: APP_CONFIG, useValue: environment } ], schemas: [NO_ERRORS_SCHEMA] @@ -179,4 +182,6 @@ describe('OrgUnitSearchResultListSubmissionElementComponent', () => { expect(jobTitleField).toBeNull(); }); }); + }); + diff --git a/src/app/entity-groups/research-entities/submission/item-list-elements/org-unit/org-unit-search-result-list-submission-element.component.ts b/src/app/entity-groups/research-entities/submission/item-list-elements/org-unit/org-unit-search-result-list-submission-element.component.ts index 42a9dc9141..4e8e32f4f8 100644 --- a/src/app/entity-groups/research-entities/submission/item-list-elements/org-unit/org-unit-search-result-list-submission-element.component.ts +++ b/src/app/entity-groups/research-entities/submission/item-list-elements/org-unit/org-unit-search-result-list-submission-element.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, Inject, OnInit } from '@angular/core'; import { BitstreamDataService } from '../../../../../core/data/bitstream-data.service'; import { SearchResultListElementComponent } from '../../../../../shared/object-list/search-result-list-element/search-result-list-element.component'; import { ItemSearchResult } from '../../../../../shared/object-collection/shared/item-search-result.model'; @@ -17,6 +17,7 @@ import { ItemDataService } from '../../../../../core/data/item-data.service'; import { SelectableListService } from '../../../../../shared/object-list/selectable-list/selectable-list.service'; import { NameVariantModalComponent } from '../../name-variant-modal/name-variant-modal.component'; import { DSONameService } from '../../../../../core/breadcrumbs/dso-name.service'; +import { APP_CONFIG, AppConfig } from '../../../../../../config/app-config.interface'; @listableObjectComponent('OrgUnitSearchResult', ViewMode.ListElement, Context.EntitySearchModal) @listableObjectComponent('OrgUnitSearchResult', ViewMode.ListElement, Context.EntitySearchModalWithNameVariants) @@ -35,6 +36,11 @@ export class OrgUnitSearchResultListSubmissionElementComponent extends SearchRes alternativeField = 'dc.title.alternative'; useNameVariants = false; + /** + * Display thumbnail if required by configuration + */ + showThumbnails: boolean; + constructor(protected truncatableService: TruncatableService, private relationshipService: RelationshipDataService, private notificationsService: NotificationsService, @@ -43,9 +49,10 @@ export class OrgUnitSearchResultListSubmissionElementComponent extends SearchRes private itemDataService: ItemDataService, private bitstreamDataService: BitstreamDataService, private selectableListService: SelectableListService, - protected dsoNameService: DSONameService + protected dsoNameService: DSONameService, + @Inject(APP_CONFIG) protected appConfig: AppConfig ) { - super(truncatableService, dsoNameService); + super(truncatableService, dsoNameService, appConfig); } ngOnInit() { @@ -65,6 +72,7 @@ export class OrgUnitSearchResultListSubmissionElementComponent extends SearchRes } ); } + this.showThumbnails = this.appConfig.browseBy.showThumbnails; } select(value) { diff --git a/src/app/entity-groups/research-entities/submission/item-list-elements/person/person-search-result-list-submission-element.component.html b/src/app/entity-groups/research-entities/submission/item-list-elements/person/person-search-result-list-submission-element.component.html index 9fe9898c2b..7d1ab508b7 100644 --- a/src/app/entity-groups/research-entities/submission/item-list-elements/person/person-search-result-list-submission-element.component.html +++ b/src/app/entity-groups/research-entities/submission/item-list-elements/person/person-search-result-list-submission-element.component.html @@ -1,6 +1,20 @@ -
-
- +
+ +
+
+
+ @@ -9,5 +23,7 @@ +
+
diff --git a/src/app/entity-groups/research-entities/submission/item-list-elements/person/person-search-result-list-submission-element.component.spec.ts b/src/app/entity-groups/research-entities/submission/item-list-elements/person/person-search-result-list-submission-element.component.spec.ts index c0fad3ed3f..9e4d347150 100644 --- a/src/app/entity-groups/research-entities/submission/item-list-elements/person/person-search-result-list-submission-element.component.spec.ts +++ b/src/app/entity-groups/research-entities/submission/item-list-elements/person/person-search-result-list-submission-element.component.spec.ts @@ -27,6 +27,7 @@ import { createSuccessfulRemoteDataObject$ } from '../../../../../shared/remote- import { TruncatableService } from '../../../../../shared/truncatable/truncatable.service'; import { TruncatePipe } from '../../../../../shared/utils/truncate.pipe'; import { PersonSearchResultListSubmissionElementComponent } from './person-search-result-list-submission-element.component'; +import { APP_CONFIG } from '../../../../../../config/app-config.interface'; let personListElementComponent: PersonSearchResultListSubmissionElementComponent; let fixture: ComponentFixture; @@ -37,6 +38,18 @@ let mockItemWithoutMetadata: ItemSearchResult; let nameVariant; let mockRelationshipService; +const environmentUseThumbs = { + browseBy: { + showThumbnails: true + } +}; + +const enviromentNoThumbs = { + browseBy: { + showThumbnails: false + } +}; + function init() { mockItemWithMetadata = Object.assign( new ItemSearchResult(), @@ -109,6 +122,7 @@ describe('PersonSearchResultListElementSubmissionComponent', () => { { provide: DSOChangeAnalyzer, useValue: {} }, { provide: DefaultChangeAnalyzer, useValue: {} }, { provide: BitstreamDataService, useValue: mockBitstreamDataService }, + { provide: APP_CONFIG, useValue: environmentUseThumbs } ], schemas: [NO_ERRORS_SCHEMA] @@ -146,4 +160,72 @@ describe('PersonSearchResultListElementSubmissionComponent', () => { expect(jobTitleField).toBeNull(); }); }); + + describe('When the environment is set to show thumbnails', () => { + beforeEach(() => { + personListElementComponent.object = mockItemWithoutMetadata; + fixture.detectChanges(); + }); + + it('should add the ds-thumbnail element', () => { + const thumbnail = fixture.debugElement.query(By.css('ds-thumbnail')); + expect(thumbnail).toBeTruthy(); + }); + }); +}); + +describe('PersonSearchResultListElementSubmissionComponent', () => { + const mockBitstreamDataService = { + getThumbnailFor(item: Item): Observable> { + return createSuccessfulRemoteDataObject$(new Bitstream()); + } + }; + beforeEach(waitForAsync(() => { + init(); + TestBed.configureTestingModule({ + declarations: [PersonSearchResultListSubmissionElementComponent, TruncatePipe], + providers: [ + { provide: TruncatableService, useValue: {} }, + { provide: RelationshipDataService, useValue: mockRelationshipService }, + { provide: NotificationsService, useValue: {} }, + { provide: TranslateService, useValue: {} }, + { provide: NgbModal, useValue: {} }, + { provide: ItemDataService, useValue: {} }, + { provide: SelectableListService, useValue: {} }, + { provide: Store, useValue: {}}, + { provide: ObjectCacheService, useValue: {} }, + { provide: UUIDService, useValue: {} }, + { provide: RemoteDataBuildService, useValue: {} }, + { provide: CommunityDataService, useValue: {} }, + { provide: HALEndpointService, useValue: {} }, + { provide: HttpClient, useValue: {} }, + { provide: DSOChangeAnalyzer, useValue: {} }, + { provide: DefaultChangeAnalyzer, useValue: {} }, + { provide: BitstreamDataService, useValue: mockBitstreamDataService }, + { provide: APP_CONFIG, useValue: enviromentNoThumbs } + ], + + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(PersonSearchResultListSubmissionElementComponent, { + set: { changeDetection: ChangeDetectionStrategy.Default } + }).compileComponents(); + })); + + beforeEach(waitForAsync(() => { + fixture = TestBed.createComponent(PersonSearchResultListSubmissionElementComponent); + personListElementComponent = fixture.componentInstance; + + })); + + describe('When the environment is not set to show thumbnails', () => { + beforeEach(() => { + personListElementComponent.object = mockItemWithoutMetadata; + fixture.detectChanges(); + }); + + it('should not add the ds-thumbnail element', () => { + const thumbnail = fixture.debugElement.query(By.css('ds-thumbnail')); + expect(thumbnail).toBeNull(); + }); + }); }); diff --git a/src/app/entity-groups/research-entities/submission/item-list-elements/person/person-search-result-list-submission-element.component.ts b/src/app/entity-groups/research-entities/submission/item-list-elements/person/person-search-result-list-submission-element.component.ts index 395b275f69..8b4811a588 100644 --- a/src/app/entity-groups/research-entities/submission/item-list-elements/person/person-search-result-list-submission-element.component.ts +++ b/src/app/entity-groups/research-entities/submission/item-list-elements/person/person-search-result-list-submission-element.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, Inject, OnInit } from '@angular/core'; import { BitstreamDataService } from '../../../../../core/data/bitstream-data.service'; import { SearchResultListElementComponent } from '../../../../../shared/object-list/search-result-list-element/search-result-list-element.component'; import { ItemSearchResult } from '../../../../../shared/object-collection/shared/item-search-result.model'; @@ -17,6 +17,7 @@ import { MetadataValue } from '../../../../../core/shared/metadata.models'; import { ItemDataService } from '../../../../../core/data/item-data.service'; import { SelectableListService } from '../../../../../shared/object-list/selectable-list/selectable-list.service'; import { DSONameService } from '../../../../../core/breadcrumbs/dso-name.service'; +import { APP_CONFIG, AppConfig } from '../../../../../../config/app-config.interface'; @listableObjectComponent('PersonSearchResult', ViewMode.ListElement, Context.EntitySearchModalWithNameVariants) @Component({ @@ -33,6 +34,11 @@ export class PersonSearchResultListSubmissionElementComponent extends SearchResu selectedName: string; alternativeField = 'dc.title.alternative'; + /** + * Display thumbnail if required by configuration + */ + showThumbnails: boolean; + constructor(protected truncatableService: TruncatableService, private relationshipService: RelationshipDataService, private notificationsService: NotificationsService, @@ -41,9 +47,10 @@ export class PersonSearchResultListSubmissionElementComponent extends SearchResu private itemDataService: ItemDataService, private bitstreamDataService: BitstreamDataService, private selectableListService: SelectableListService, - protected dsoNameService: DSONameService + protected dsoNameService: DSONameService, + @Inject(APP_CONFIG) protected appConfig: AppConfig ) { - super(truncatableService, dsoNameService); + super(truncatableService, dsoNameService, appConfig); } ngOnInit() { @@ -58,6 +65,7 @@ export class PersonSearchResultListSubmissionElementComponent extends SearchResu this.selectedName = nameVariant || defaultValue; } ); + this.showThumbnails = this.appConfig.browseBy.showThumbnails; } select(value) { diff --git a/src/app/item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.spec.ts b/src/app/item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.spec.ts index 76c5717bb8..4cd663f0fb 100644 --- a/src/app/item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.spec.ts +++ b/src/app/item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.spec.ts @@ -31,6 +31,7 @@ import { SearchConfigurationServiceStub } from '../../../../shared/testing/searc import { ConfigurationProperty } from '../../../../core/shared/configuration-property.model'; import { Router } from '@angular/router'; import { RouterMock } from '../../../../shared/mocks/router.mock'; +import { APP_CONFIG } from '../../../../../config/app-config.interface'; let comp: EditRelationshipListComponent; let fixture: ComponentFixture; @@ -201,6 +202,12 @@ describe('EditRelationshipListComponent', () => { })) }); + const environmentUseThumbs = { + browseBy: { + showThumbnails: true + } + }; + TestBed.configureTestingModule({ imports: [SharedModule, TranslateModule.forRoot()], declarations: [EditRelationshipListComponent], @@ -217,6 +224,7 @@ describe('EditRelationshipListComponent', () => { { provide: LinkHeadService, useValue: linkHeadService }, { provide: ConfigurationDataService, useValue: configurationDataService }, { provide: SearchConfigurationService, useValue: new SearchConfigurationServiceStub() }, + { provide: APP_CONFIG, useValue: environmentUseThumbs } ], schemas: [ NO_ERRORS_SCHEMA ] @@ -259,9 +267,11 @@ describe('EditRelationshipListComponent', () => { const callArgs = relationshipService.getItemRelationshipsByLabel.calls.mostRecent().args; const findListOptions = callArgs[2]; - + const linksToFollow = callArgs[5]; expect(findListOptions.elementsPerPage).toEqual(paginationOptions.pageSize); expect(findListOptions.currentPage).toEqual(paginationOptions.currentPage); + expect(linksToFollow.linksToFollow[0].name).toEqual('thumbnail'); + }); describe('when the publication is on the left side of the relationship', () => { diff --git a/src/app/item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.ts b/src/app/item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.ts index f5a5a583d7..b8542f5806 100644 --- a/src/app/item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.ts +++ b/src/app/item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.ts @@ -1,4 +1,4 @@ -import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core'; +import { Component, EventEmitter, Inject, Input, OnDestroy, OnInit, Output } from '@angular/core'; import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; import { LinkService } from '../../../../core/cache/builders/link.service'; import { ObjectUpdatesService } from '../../../../core/data/object-updates/object-updates.service'; @@ -29,7 +29,7 @@ import { DsDynamicLookupRelationModalComponent } from '../../../../shared/form/b import { RelationshipOptions } from '../../../../shared/form/builder/models/relationship-options.model'; import { SelectableListService } from '../../../../shared/object-list/selectable-list/selectable-list.service'; import { SearchResult } from '../../../../shared/search/models/search-result.model'; -import { followLink } from '../../../../shared/utils/follow-link-config.model'; +import { FollowLinkConfig } from '../../../../shared/utils/follow-link-config.model'; import { PaginatedList } from '../../../../core/data/paginated-list.model'; import { RemoteData } from '../../../../core/data/remote-data'; import { Collection } from '../../../../core/shared/collection.model'; @@ -39,6 +39,8 @@ import { RelationshipTypeDataService } from '../../../../core/data/relationship- import { FieldUpdate } from '../../../../core/data/object-updates/field-update.model'; import { FieldUpdates } from '../../../../core/data/object-updates/field-updates.model'; import { FieldChangeType } from '../../../../core/data/object-updates/field-change-type.model'; +import { APP_CONFIG, AppConfig } from '../../../../../config/app-config.interface'; +import { itemLinksToFollow } from '../../../../shared/utils/relation-query.utils'; @Component({ selector: 'ds-edit-relationship-list', @@ -138,6 +140,10 @@ export class EditRelationshipListComponent implements OnInit, OnDestroy { */ modalRef: NgbModalRef; + /** + * Determines whether to ask for the embedded item thumbnail. + */ + fetchThumbnail: boolean; constructor( protected objectUpdatesService: ObjectUpdatesService, @@ -147,7 +153,9 @@ export class EditRelationshipListComponent implements OnInit, OnDestroy { protected modalService: NgbModal, protected paginationService: PaginationService, protected selectableListService: SelectableListService, + @Inject(APP_CONFIG) protected appConfig: AppConfig ) { + this.fetchThumbnail = this.appConfig.browseBy.showThumbnails; } /** @@ -484,6 +492,9 @@ export class EditRelationshipListComponent implements OnInit, OnDestroy { tap(() => this.loading$.next(true)) ); + // this adds thumbnail images when required by configuration + let linksToFollow: FollowLinkConfig[] = itemLinksToFollow(this.fetchThumbnail); + this.subs.push( observableCombineLatest([ currentPagination$, @@ -496,12 +507,11 @@ export class EditRelationshipListComponent implements OnInit, OnDestroy { currentItemIsLeftItem ? this.relationshipType.leftwardType : this.relationshipType.rightwardType, { elementsPerPage: currentPagination.pageSize, - currentPage: currentPagination.currentPage, + currentPage: currentPagination.currentPage }, false, true, - followLink('leftItem'), - followLink('rightItem'), + ...linksToFollow )), ).subscribe((rd: RemoteData>) => { this.relationshipsRd$.next(rd); diff --git a/src/app/item-page/simple/abstract-incremental-list/abstract-incremental-list.component.ts b/src/app/item-page/simple/abstract-incremental-list/abstract-incremental-list.component.ts index c965648c15..e788e6e575 100644 --- a/src/app/item-page/simple/abstract-incremental-list/abstract-incremental-list.component.ts +++ b/src/app/item-page/simple/abstract-incremental-list/abstract-incremental-list.component.ts @@ -10,6 +10,7 @@ import { hasValue, isNotEmpty } from '../../../shared/empty.util'; * An abstract component for displaying an incremental list of objects */ export class AbstractIncrementalListComponent implements OnInit, OnDestroy { + /** * The amount to increment the list by * Define this amount in the child component overriding this component @@ -21,6 +22,11 @@ export class AbstractIncrementalListComponent implements OnInit, OnDestroy { */ objects: T[]; + /** + * Placeholder css class (defined in global-styles) + */ + placeholderFontClass: string; + /** * A list of open subscriptions */ diff --git a/src/app/item-page/simple/item-types/publication/publication.component.html b/src/app/item-page/simple/item-types/publication/publication.component.html index d83202ce12..6d1ce9a1ad 100644 --- a/src/app/item-page/simple/item-types/publication/publication.component.html +++ b/src/app/item-page/simple/item-types/publication/publication.component.html @@ -53,7 +53,7 @@ [label]="'publication.page.publisher'">
-
+
>>> { + /** * The parent of the list of related items to display */ @@ -53,8 +57,28 @@ export class RelatedItemsComponent extends AbstractIncrementalListComponent>> { - return this.relationshipService.getRelatedItemsByLabel(this.parentItem, this.relationType, Object.assign(this.options, { elementsPerPage: this.incrementBy, currentPage: page })); + return this.relationshipService.getRelatedItemsByLabel(this.parentItem, this.relationType, Object.assign(this.options, + { elementsPerPage: this.incrementBy, currentPage: page, fetchThumbnail: this.fetchThumbnail })); } } diff --git a/src/app/item-page/simple/related-items/related-items.component.html b/src/app/item-page/simple/related-items/related-items.component.html index 2a43ae90dc..0d1e14941d 100644 --- a/src/app/item-page/simple/related-items/related-items.component.html +++ b/src/app/item-page/simple/related-items/related-items.component.html @@ -1,4 +1,4 @@ - + { let comp: RelatedItemsComponent; let fixture: ComponentFixture; @@ -45,7 +58,8 @@ describe('RelatedItemsComponent', () => { imports: [TranslateModule.forRoot()], declarations: [RelatedItemsComponent, VarDirective], providers: [ - { provide: RelationshipDataService, useValue: relationshipService } + { provide: RelationshipDataService, useValue: relationshipService }, + { provide: APP_CONFIG, useValue: environmentUseThumbs } ], schemas: [NO_ERRORS_SCHEMA] }).overrideComponent(RelatedItemsComponent, { @@ -82,9 +96,11 @@ describe('RelatedItemsComponent', () => { it('should call relationship-service\'s getRelatedItemsByLabel with the correct arguments (second page)', () => { expect(relationshipService.getRelatedItemsByLabel).toHaveBeenCalledWith(parentItem, relationType, Object.assign(comp.options, { elementsPerPage: comp.incrementBy, - currentPage: 2 + currentPage: 2, + fetchThumbnail: true })); }); + }); describe('when decrease is called', () => { @@ -100,3 +116,42 @@ describe('RelatedItemsComponent', () => { }); }); +describe('RelatedItemsComponent', () => { + let comp: RelatedItemsComponent; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + relationshipService = jasmine.createSpyObj('relationshipService', + { + getRelatedItemsByLabel: createSuccessfulRemoteDataObject$(createPaginatedList(mockItems)), + } + ); + + TestBed.configureTestingModule({ + imports: [TranslateModule.forRoot()], + declarations: [RelatedItemsComponent, VarDirective], + providers: [ + {provide: RelationshipDataService, useValue: relationshipService}, + {provide: APP_CONFIG, useValue: enviromentNoThumbs} + ], + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(RelatedItemsComponent, { + set: {changeDetection: ChangeDetectionStrategy.Default} + }).compileComponents(); + })); + + beforeEach(waitForAsync(() => { + fixture = TestBed.createComponent(RelatedItemsComponent); + comp = fixture.componentInstance; + comp.parentItem = parentItem; + comp.relationType = relationType; + fixture.detectChanges(); + })); + it('should call relationship-service\'s getRelatedItemsByLabel with the correct arguments (second page)', () => { + expect(relationshipService.getRelatedItemsByLabel).toHaveBeenCalledWith(parentItem, relationType, Object.assign(comp.options, { + elementsPerPage: comp.incrementBy, + currentPage: 2, + fetchThumbnail: false + })); + }); +}); diff --git a/src/app/shared/browse-by/browse-by.component.spec.ts b/src/app/shared/browse-by/browse-by.component.spec.ts index 720c6b2686..2a679035f6 100644 --- a/src/app/shared/browse-by/browse-by.component.spec.ts +++ b/src/app/shared/browse-by/browse-by.component.spec.ts @@ -132,7 +132,7 @@ describe('BrowseByComponent', () => { { provide: PaginationService, useValue: paginationService }, { provide: MockThemedBrowseEntryListElementComponent }, { provide: ThemeService, useValue: themeService }, - {provide: RouteService, useValue: routeServiceStub}, + { provide: RouteService, useValue: routeServiceStub}, { provide: SelectableListService, useValue: {} }, { provide: HostWindowService, useValue: new HostWindowServiceStub(800) }, ], diff --git a/src/app/shared/object-collection/object-collection.component.html b/src/app/shared/object-collection/object-collection.component.html index 999ae9a120..eb72d07fe5 100644 --- a/src/app/shared/object-collection/object-collection.component.html +++ b/src/app/shared/object-collection/object-collection.component.html @@ -1,4 +1,4 @@ - { expect(fixture.debugElement.query(By.css('ds-object-list'))).toBeDefined(); expect(fixture.debugElement.query(By.css('ds-object-grid'))).toBeNull(); }); + it('should set fallback placeholder font size during test', () => { + objectCollectionComponent.currentMode$ = observableOf(ViewMode.ListElement); + expect(fixture.debugElement.query(By.css('thumb-font-3'))).toBeDefined(); + }); }); diff --git a/src/app/shared/object-collection/object-collection.component.ts b/src/app/shared/object-collection/object-collection.component.ts index f2706f1a4c..3c3de72d21 100644 --- a/src/app/shared/object-collection/object-collection.component.ts +++ b/src/app/shared/object-collection/object-collection.component.ts @@ -1,4 +1,11 @@ -import { ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { + ChangeDetectorRef, + Component, ElementRef, + EventEmitter, Inject, + Input, + OnInit, + Output, PLATFORM_ID, +} from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { Observable } from 'rxjs'; @@ -14,6 +21,8 @@ import { ViewMode } from '../../core/shared/view-mode.model'; import { CollectionElementLinkType } from './collection-element-link.type'; import { PaginatedList } from '../../core/data/paginated-list.model'; import { Context } from '../../core/shared/context.model'; +import { setPlaceHolderAttributes } from '../utils/object-list-utils'; +import { isPlatformBrowser } from '@angular/common'; /** * Component that can render a list of listable objects in different view modes @@ -147,14 +156,13 @@ export class ObjectCollectionComponent implements OnInit { */ viewModeEnum = ViewMode; - ngOnInit(): void { - this.currentMode$ = this.route - .queryParams - .pipe( - map((params) => isEmpty(params?.view) ? ViewMode.ListElement : params.view), - distinctUntilChanged() - ); - } + /** + * Placeholder class (defined in global-styles) + */ + placeholderFontClass: string; + + + /** * @param cdRef @@ -163,11 +171,30 @@ export class ObjectCollectionComponent implements OnInit { * Route is a singleton service provided by Angular. * @param router * Router is a singleton service provided by Angular. + * @param elementRef + * Used only to read DOM for the element width */ constructor( private cdRef: ChangeDetectorRef, private route: ActivatedRoute, - private router: Router) { + private router: Router, + private elementRef: ElementRef, + @Inject(PLATFORM_ID) private platformId: Object) { + } + + ngOnInit(): void { + this.currentMode$ = this.route + .queryParams + .pipe( + map((params) => isEmpty(params?.view) ? ViewMode.ListElement : params.view), + distinctUntilChanged() + ); + if (isPlatformBrowser(this.platformId)) { + const width = this.elementRef.nativeElement.offsetWidth; + this.placeholderFontClass = setPlaceHolderAttributes(width); + } else { + this.placeholderFontClass = 'hide-placeholder-text'; + } } /** diff --git a/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-approved-search-result/claimed-approved-search-result-list-element.component.spec.ts b/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-approved-search-result/claimed-approved-search-result-list-element.component.spec.ts index c2b106c76a..742d71c59e 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-approved-search-result/claimed-approved-search-result-list-element.component.spec.ts +++ b/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-approved-search-result/claimed-approved-search-result-list-element.component.spec.ts @@ -17,6 +17,8 @@ import { ClaimedApprovedTaskSearchResult } from '../../../../object-collection/s import { ClaimedApprovedSearchResultListElementComponent } from './claimed-approved-search-result-list-element.component'; import { DSONameService } from '../../../../../core/breadcrumbs/dso-name.service'; import { DSONameServiceMock } from '../../../../mocks/dso-name.service.mock'; +import { APP_CONFIG } from '../../../../../../config/app-config.interface'; +import { environment } from '../../../../../../environments/environment'; let component: ClaimedApprovedSearchResultListElementComponent; let fixture: ComponentFixture; @@ -67,7 +69,8 @@ describe('ClaimedApprovedSearchResultListElementComponent', () => { providers: [ { provide: TruncatableService, useValue: {} }, { provide: LinkService, useValue: linkService }, - { provide: DSONameService, useClass: DSONameServiceMock } + { provide: DSONameService, useClass: DSONameServiceMock }, + { provide: APP_CONFIG, useValue: environment } ], schemas: [NO_ERRORS_SCHEMA] }).overrideComponent(ClaimedApprovedSearchResultListElementComponent, { diff --git a/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-approved-search-result/claimed-approved-search-result-list-element.component.ts b/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-approved-search-result/claimed-approved-search-result-list-element.component.ts index eaf407d787..5dda0e44ea 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-approved-search-result/claimed-approved-search-result-list-element.component.ts +++ b/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-approved-search-result/claimed-approved-search-result-list-element.component.ts @@ -1,4 +1,4 @@ -import { Component } from '@angular/core'; +import { Component, Inject } from '@angular/core'; import { ViewMode } from '../../../../../core/shared/view-mode.model'; import { ClaimedApprovedTaskSearchResult } from '../../../../object-collection/shared/claimed-approved-task-search-result.model'; import { listableObjectComponent } from '../../../../object-collection/shared/listable-object/listable-object.decorator'; @@ -13,6 +13,7 @@ import { SearchResultListElementComponent } from '../../../search-result-list-el import { ClaimedTaskSearchResult } from '../../../../object-collection/shared/claimed-task-search-result.model'; import { ClaimedTask } from '../../../../../core/tasks/models/claimed-task-object.model'; import { DSONameService } from '../../../../../core/breadcrumbs/dso-name.service'; +import { APP_CONFIG, AppConfig } from '../../../../../../config/app-config.interface'; /** * This component renders claimed task approved object for the search result in the list view. @@ -43,9 +44,10 @@ export class ClaimedApprovedSearchResultListElementComponent extends SearchResul public constructor( protected linkService: LinkService, protected truncatableService: TruncatableService, - protected dsoNameService: DSONameService + protected dsoNameService: DSONameService, + @Inject(APP_CONFIG) protected appConfig: AppConfig ) { - super(truncatableService, dsoNameService); + super(truncatableService, dsoNameService, appConfig); } /** diff --git a/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-declined-search-result/claimed-declided-search-result-list-element.component.spec.ts b/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-declined-search-result/claimed-declided-search-result-list-element.component.spec.ts index 15b5a5345d..16ed3d7f41 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-declined-search-result/claimed-declided-search-result-list-element.component.spec.ts +++ b/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-declined-search-result/claimed-declided-search-result-list-element.component.spec.ts @@ -17,6 +17,8 @@ import { LinkService } from '../../../../../core/cache/builders/link.service'; import { MyDspaceItemStatusType } from '../../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type'; import { DSONameService } from '../../../../../core/breadcrumbs/dso-name.service'; import { DSONameServiceMock } from '../../../../mocks/dso-name.service.mock'; +import { APP_CONFIG } from '../../../../../../config/app-config.interface'; +import { environment } from '../../../../../../environments/environment'; let component: ClaimedDeclinedSearchResultListElementComponent; let fixture: ComponentFixture; @@ -67,7 +69,8 @@ describe('ClaimedDeclinedSearchResultListElementComponent', () => { providers: [ { provide: TruncatableService, useValue: {} }, { provide: LinkService, useValue: linkService }, - { provide: DSONameService, useClass: DSONameServiceMock } + { provide: DSONameService, useClass: DSONameServiceMock }, + { provide: APP_CONFIG, useValue: environment } ], schemas: [NO_ERRORS_SCHEMA] }).overrideComponent(ClaimedDeclinedSearchResultListElementComponent, { diff --git a/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-declined-search-result/claimed-declined-search-result-list-element.component.ts b/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-declined-search-result/claimed-declined-search-result-list-element.component.ts index 0b9a925dbf..90f523a7fb 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-declined-search-result/claimed-declined-search-result-list-element.component.ts +++ b/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-declined-search-result/claimed-declined-search-result-list-element.component.ts @@ -1,4 +1,4 @@ -import { Component } from '@angular/core'; +import { Component, Inject } from '@angular/core'; import { listableObjectComponent } from '../../../../object-collection/shared/listable-object/listable-object.decorator'; import { ClaimedDeclinedTaskSearchResult } from '../../../../object-collection/shared/claimed-declined-task-search-result.model'; @@ -14,6 +14,7 @@ import { SearchResultListElementComponent } from '../../../search-result-list-el import { ClaimedTaskSearchResult } from '../../../../object-collection/shared/claimed-task-search-result.model'; import { ClaimedTask } from '../../../../../core/tasks/models/claimed-task-object.model'; import { DSONameService } from '../../../../../core/breadcrumbs/dso-name.service'; +import { APP_CONFIG, AppConfig } from '../../../../../../config/app-config.interface'; /** * This component renders claimed task declined object for the search result in the list view. @@ -44,9 +45,10 @@ export class ClaimedDeclinedSearchResultListElementComponent extends SearchResul public constructor( protected linkService: LinkService, protected truncatableService: TruncatableService, - protected dsoNameService: DSONameService + protected dsoNameService: DSONameService, + @Inject(APP_CONFIG) protected appConfig: AppConfig ) { - super(truncatableService, dsoNameService); + super(truncatableService, dsoNameService, appConfig); } /** diff --git a/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-search-result-list-element.component.spec.ts b/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-search-result-list-element.component.spec.ts index 7896061a73..24cacb79a9 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-search-result-list-element.component.spec.ts +++ b/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-search-result-list-element.component.spec.ts @@ -18,6 +18,8 @@ import { getMockLinkService } from '../../../mocks/link-service.mock'; import { By } from '@angular/platform-browser'; import { DSONameService } from '../../../../core/breadcrumbs/dso-name.service'; import { DSONameServiceMock } from '../../../mocks/dso-name.service.mock'; +import { APP_CONFIG } from '../../../../../config/app-config.interface'; +import { environment } from '../../../../../environments/environment'; let component: ClaimedSearchResultListElementComponent; let fixture: ComponentFixture; @@ -68,7 +70,8 @@ describe('ClaimedSearchResultListElementComponent', () => { providers: [ { provide: TruncatableService, useValue: {} }, { provide: LinkService, useValue: linkService }, - { provide: DSONameService, useClass: DSONameServiceMock } + { provide: DSONameService, useClass: DSONameServiceMock }, + { provide: APP_CONFIG, useValue: environment } ], schemas: [NO_ERRORS_SCHEMA] }).overrideComponent(ClaimedSearchResultListElementComponent, { diff --git a/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-search-result-list-element.component.ts b/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-search-result-list-element.component.ts index 2cf8f9a231..27e5831c39 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-search-result-list-element.component.ts +++ b/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-search-result-list-element.component.ts @@ -1,4 +1,4 @@ -import { Component } from '@angular/core'; +import { Component, Inject } from '@angular/core'; import { ViewMode } from '../../../../core/shared/view-mode.model'; import { listableObjectComponent } from '../../../object-collection/shared/listable-object/listable-object.decorator'; @@ -13,6 +13,7 @@ import { followLink } from '../../../utils/follow-link-config.model'; import { SearchResultListElementComponent } from '../../search-result-list-element/search-result-list-element.component'; import { ClaimedTask } from '../../../../core/tasks/models/claimed-task-object.model'; import { DSONameService } from '../../../../core/breadcrumbs/dso-name.service'; +import { APP_CONFIG, AppConfig } from '../../../../../config/app-config.interface'; @Component({ selector: 'ds-claimed-search-result-list-element', @@ -40,9 +41,10 @@ export class ClaimedSearchResultListElementComponent extends SearchResultListEle public constructor( protected linkService: LinkService, protected truncatableService: TruncatableService, - protected dsoNameService: DSONameService + protected dsoNameService: DSONameService, + @Inject(APP_CONFIG) protected appConfig: AppConfig ) { - super(truncatableService, dsoNameService); + super(truncatableService, dsoNameService, appConfig); } /** diff --git a/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.html b/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.html index 78181dc65a..daa89ec806 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.html +++ b/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.html @@ -1,23 +1,33 @@ -
- - - -
- - +
+
+ +
- -

-
+
+
+ + + +
+ + +
+ +

+
( - ) + ) - {{'mydspace.results.no-authors' | translate}} - + {{'mydspace.results.no-authors' | translate}} + ; @@ -26,12 +36,14 @@ - + - + +
+
+
- - +
diff --git a/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.spec.ts b/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.spec.ts index 023b51a8dd..aaddebd8eb 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.spec.ts +++ b/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.spec.ts @@ -10,6 +10,7 @@ import { ItemListPreviewComponent } from './item-list-preview.component'; import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; import { TranslateLoaderMock } from '../../../mocks/translate-loader.mock'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; +import { APP_CONFIG } from '../../../../../config/app-config.interface'; let component: ItemListPreviewComponent; let fixture: ComponentFixture; @@ -66,6 +67,18 @@ const mockItemWithEntityType: Item = Object.assign(new Item(), { } }); +const environmentUseThumbs = { + browseBy: { + showThumbnails: true + } +}; + +const enviromentNoThumbs = { + browseBy: { + showThumbnails: false + } +}; + describe('ItemListPreviewComponent', () => { beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ @@ -80,8 +93,8 @@ describe('ItemListPreviewComponent', () => { ], declarations: [ItemListPreviewComponent, TruncatePipe], providers: [ - { provide: 'objectElementProvider', useValue: { mockItemWithAuthorAndDate } } - + { provide: 'objectElementProvider', useValue: { mockItemWithAuthorAndDate }}, + { provide: APP_CONFIG, useValue: environmentUseThumbs } ], schemas: [NO_ERRORS_SCHEMA] @@ -100,6 +113,17 @@ describe('ItemListPreviewComponent', () => { component.object = { hitHighlights: {} } as any; }); + describe('When showThumbnails is true', () => { + beforeEach(() => { + component.item = mockItemWithAuthorAndDate; + fixture.detectChanges(); + }); + it('should add the ds-thumbnail element', () => { + const thumbnail = fixture.debugElement.query(By.css('ds-thumbnail')); + expect(thumbnail).toBeTruthy(); + }); + }); + describe('When the item has an author', () => { beforeEach(() => { component.item = mockItemWithAuthorAndDate; @@ -160,3 +184,48 @@ describe('ItemListPreviewComponent', () => { }); }); }); + +describe('ItemListPreviewComponent', () => { + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + imports: [ + TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: TranslateLoaderMock + } + }), + NoopAnimationsModule + ], + declarations: [ItemListPreviewComponent, TruncatePipe], + providers: [ + {provide: 'objectElementProvider', useValue: {mockItemWithAuthorAndDate}}, + {provide: APP_CONFIG, useValue: enviromentNoThumbs} + ], + + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(ItemListPreviewComponent, { + set: {changeDetection: ChangeDetectionStrategy.Default} + }).compileComponents(); + })); + beforeEach(waitForAsync(() => { + fixture = TestBed.createComponent(ItemListPreviewComponent); + component = fixture.componentInstance; + + })); + + beforeEach(() => { + component.object = { hitHighlights: {} } as any; + }); + + describe('When showThumbnails is true', () => { + beforeEach(() => { + component.item = mockItemWithAuthorAndDate; + fixture.detectChanges(); + }); + it('should add the ds-thumbnail element', () => { + const thumbnail = fixture.debugElement.query(By.css('ds-thumbnail')); + expect(thumbnail).toBeFalsy(); + }); + }); +}); diff --git a/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.ts b/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.ts index 34b2d979c1..3b52e0b879 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.ts +++ b/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.ts @@ -1,9 +1,10 @@ -import { Component, Input } from '@angular/core'; +import { Component, Inject, Input, OnInit } from '@angular/core'; import { Item } from '../../../../core/shared/item.model'; import { fadeInOut } from '../../../animations/fade'; import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type'; import { SearchResult } from '../../../search/models/search-result.model'; +import { APP_CONFIG, AppConfig } from '../../../../../config/app-config.interface'; /** * This component show metadata for the given item object in the list view. @@ -14,7 +15,7 @@ import { SearchResult } from '../../../search/models/search-result.model'; templateUrl: 'item-list-preview.component.html', animations: [fadeInOut] }) -export class ItemListPreviewComponent { +export class ItemListPreviewComponent implements OnInit{ /** * The item to display @@ -35,4 +36,17 @@ export class ItemListPreviewComponent { * A boolean representing if to show submitter information */ @Input() showSubmitter = false; + + + /** + * Display thumbnails if required by configuration + */ + showThumbnails: boolean; + + constructor(@Inject(APP_CONFIG) protected appConfig: AppConfig) { + } + + ngOnInit(): void { + this.showThumbnails = this.appConfig.browseBy.showThumbnails; + } } diff --git a/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/themed-item-list-preview.component.ts b/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/themed-item-list-preview.component.ts index 982a04207f..3fe825d236 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/themed-item-list-preview.component.ts +++ b/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/themed-item-list-preview.component.ts @@ -24,6 +24,7 @@ export class ThemedItemListPreviewComponent extends ThemedComponent - +
+
+ +
+
+ diff --git a/src/app/shared/object-list/my-dspace-result-list-element/item-search-result/item-search-result-list-element-submission.component.spec.ts b/src/app/shared/object-list/my-dspace-result-list-element/item-search-result/item-search-result-list-element-submission.component.spec.ts index d1871d0996..1dc221f2ed 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/item-search-result/item-search-result-list-element-submission.component.spec.ts +++ b/src/app/shared/object-list/my-dspace-result-list-element/item-search-result/item-search-result-list-element-submission.component.spec.ts @@ -12,6 +12,8 @@ import { TruncatableService } from '../../../truncatable/truncatable.service'; import { By } from '@angular/platform-browser'; import { DSONameService } from '../../../../core/breadcrumbs/dso-name.service'; import { DSONameServiceMock } from '../../../mocks/dso-name.service.mock'; +import { APP_CONFIG } from '../../../../../config/app-config.interface'; +import { environment } from '../../../../../environments/environment'; let component: ItemSearchResultListElementSubmissionComponent; let fixture: ComponentFixture; @@ -56,7 +58,8 @@ describe('ItemMyDSpaceResultListElementComponent', () => { declarations: [ItemSearchResultListElementSubmissionComponent], providers: [ { provide: TruncatableService, useValue: {} }, - { provide: DSONameService, useClass: DSONameServiceMock } + { provide: DSONameService, useClass: DSONameServiceMock }, + { provide: APP_CONFIG, useValue: environment } ], schemas: [NO_ERRORS_SCHEMA] }).overrideComponent(ItemSearchResultListElementSubmissionComponent, { diff --git a/src/app/shared/object-list/my-dspace-result-list-element/item-search-result/item-search-result-list-element-submission.component.ts b/src/app/shared/object-list/my-dspace-result-list-element/item-search-result/item-search-result-list-element-submission.component.ts index 3ef45d1c47..26cbafc102 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/item-search-result/item-search-result-list-element-submission.component.ts +++ b/src/app/shared/object-list/my-dspace-result-list-element/item-search-result/item-search-result-list-element-submission.component.ts @@ -25,7 +25,13 @@ export class ItemSearchResultListElementSubmissionComponent extends SearchResult */ public status = MyDspaceItemStatusType.ARCHIVED; + /** + * Display thumbnails if required by configuration + */ + showThumbnails: boolean; + ngOnInit() { super.ngOnInit(); + this.showThumbnails = this.appConfig.browseBy.showThumbnails; } } diff --git a/src/app/shared/object-list/my-dspace-result-list-element/pool-search-result/pool-search-result-list-element.component.html b/src/app/shared/object-list/my-dspace-result-list-element/pool-search-result/pool-search-result-list-element.component.html index 346f9b249a..0fd27eb073 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/pool-search-result/pool-search-result-list-element.component.html +++ b/src/app/shared/object-list/my-dspace-result-list-element/pool-search-result/pool-search-result-list-element.component.html @@ -4,5 +4,10 @@ [object]="object" [showSubmitter]="showSubmitter" [status]="status"> - +
+
+ +
+
diff --git a/src/app/shared/object-list/my-dspace-result-list-element/pool-search-result/pool-search-result-list-element.component.spec.ts b/src/app/shared/object-list/my-dspace-result-list-element/pool-search-result/pool-search-result-list-element.component.spec.ts index 18db9abd67..4f839a5d86 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/pool-search-result/pool-search-result-list-element.component.spec.ts +++ b/src/app/shared/object-list/my-dspace-result-list-element/pool-search-result/pool-search-result-list-element.component.spec.ts @@ -18,6 +18,7 @@ import { getMockLinkService } from '../../../mocks/link-service.mock'; import { By } from '@angular/platform-browser'; import { DSONameService } from '../../../../core/breadcrumbs/dso-name.service'; import { DSONameServiceMock } from '../../../mocks/dso-name.service.mock'; +import { APP_CONFIG } from '../../../../../config/app-config.interface'; let component: PoolSearchResultListElementComponent; let fixture: ComponentFixture; @@ -54,6 +55,13 @@ const item = Object.assign(new Item(), { ] } }); + +const environmentUseThumbs = { + browseBy: { + showThumbnails: true + } +}; + const rdItem = createSuccessfulRemoteDataObject(item); const workflowitem = Object.assign(new WorkflowItem(), { item: observableOf(rdItem) }); const rdWorkflowitem = createSuccessfulRemoteDataObject(workflowitem); @@ -68,7 +76,8 @@ describe('PoolSearchResultListElementComponent', () => { providers: [ { provide: TruncatableService, useValue: {} }, { provide: LinkService, useValue: linkService }, - { provide: DSONameService, useClass: DSONameServiceMock } + { provide: DSONameService, useClass: DSONameServiceMock }, + { provide: APP_CONFIG, useValue: environmentUseThumbs } ], schemas: [NO_ERRORS_SCHEMA] }).overrideComponent(PoolSearchResultListElementComponent, { @@ -112,4 +121,9 @@ describe('PoolSearchResultListElementComponent', () => { expect(component.reloadedObject.emit).toHaveBeenCalledWith(actionPayload.reloadedObject); })); + + it('should add an offset to the actions element', () => { + const thumbnail = fixture.debugElement.query(By.css('.offset-3')); + expect(thumbnail).toBeTruthy(); + }); }); diff --git a/src/app/shared/object-list/my-dspace-result-list-element/pool-search-result/pool-search-result-list-element.component.ts b/src/app/shared/object-list/my-dspace-result-list-element/pool-search-result/pool-search-result-list-element.component.ts index e9d64db572..71feeb6bdd 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/pool-search-result/pool-search-result-list-element.component.ts +++ b/src/app/shared/object-list/my-dspace-result-list-element/pool-search-result/pool-search-result-list-element.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, Inject, OnInit } from '@angular/core'; import { Observable } from 'rxjs'; @@ -14,6 +14,7 @@ import { TruncatableService } from '../../../truncatable/truncatable.service'; import { followLink } from '../../../utils/follow-link-config.model'; import { LinkService } from '../../../../core/cache/builders/link.service'; import { DSONameService } from '../../../../core/breadcrumbs/dso-name.service'; +import { APP_CONFIG, AppConfig } from '../../../../../config/app-config.interface'; /** * This component renders pool task object for the search result in the list view. @@ -47,12 +48,18 @@ export class PoolSearchResultListElementComponent extends SearchResultListElemen */ public index: number; + /** + * Display thumbnails if required by configuration + */ + showThumbnails: boolean; + constructor( protected linkService: LinkService, protected truncatableService: TruncatableService, - protected dsoNameService: DSONameService + protected dsoNameService: DSONameService, + @Inject(APP_CONFIG) protected appConfig: AppConfig ) { - super(truncatableService, dsoNameService); + super(truncatableService, dsoNameService, appConfig); } /** @@ -64,6 +71,7 @@ export class PoolSearchResultListElementComponent extends SearchResultListElemen followLink('item'), followLink('submitter') ), followLink('action')); this.workflowitemRD$ = this.dso.workflowitem as Observable>; + this.showThumbnails = this.appConfig.browseBy.showThumbnails; } } diff --git a/src/app/shared/object-list/my-dspace-result-list-element/workflow-item-search-result/workflow-item-search-result-list-element.component.html b/src/app/shared/object-list/my-dspace-result-list-element/workflow-item-search-result/workflow-item-search-result-list-element.component.html index 4a5f330f46..74a2433ba3 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/workflow-item-search-result/workflow-item-search-result-list-element.component.html +++ b/src/app/shared/object-list/my-dspace-result-list-element/workflow-item-search-result/workflow-item-search-result-list-element.component.html @@ -3,9 +3,13 @@ [item]="item$ | async" [object]="object" [status]="status"> - - +
+
+ +
+
+ diff --git a/src/app/shared/object-list/my-dspace-result-list-element/workflow-item-search-result/workflow-item-search-result-list-element.component.spec.ts b/src/app/shared/object-list/my-dspace-result-list-element/workflow-item-search-result/workflow-item-search-result-list-element.component.spec.ts index 61337b0cb3..0f051c3c4b 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/workflow-item-search-result/workflow-item-search-result-list-element.component.spec.ts +++ b/src/app/shared/object-list/my-dspace-result-list-element/workflow-item-search-result/workflow-item-search-result-list-element.component.spec.ts @@ -18,6 +18,7 @@ import { WorkflowItemSearchResultListElementComponent } from './workflow-item-se import { By } from '@angular/platform-browser'; import { DSONameService } from '../../../../core/breadcrumbs/dso-name.service'; import { DSONameServiceMock } from '../../../mocks/dso-name.service.mock'; +import { APP_CONFIG } from '../../../../../config/app-config.interface'; let component: WorkflowItemSearchResultListElementComponent; let fixture: ComponentFixture; @@ -54,6 +55,13 @@ const item = Object.assign(new Item(), { ] } }); + +const environmentUseThumbs = { + browseBy: { + showThumbnails: true + } +}; + const rd = createSuccessfulRemoteDataObject(item); mockResultObject.indexableObject = Object.assign(new WorkflowItem(), { item: observableOf(rd) }); @@ -69,7 +77,8 @@ describe('WorkflowItemSearchResultListElementComponent', () => { { provide: TruncatableService, useValue: {} }, { provide: ItemDataService, useValue: {} }, { provide: LinkService, useValue: linkService }, - { provide: DSONameService, useClass: DSONameServiceMock } + { provide: DSONameService, useClass: DSONameServiceMock }, + { provide: APP_CONFIG, useValue: environmentUseThumbs } ], schemas: [NO_ERRORS_SCHEMA] }).overrideComponent(WorkflowItemSearchResultListElementComponent, { @@ -110,4 +119,10 @@ describe('WorkflowItemSearchResultListElementComponent', () => { expect(component.reloadedObject.emit).toHaveBeenCalledWith(actionPayload.reloadedObject); })); + + it('should add an offset to the actions element', () => { + const thumbnail = fixture.debugElement.query(By.css('.offset-3')); + expect(thumbnail).toBeTruthy(); + }); + }); diff --git a/src/app/shared/object-list/my-dspace-result-list-element/workflow-item-search-result/workflow-item-search-result-list-element.component.ts b/src/app/shared/object-list/my-dspace-result-list-element/workflow-item-search-result/workflow-item-search-result-list-element.component.ts index 836a3ee268..f123caf1ac 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/workflow-item-search-result/workflow-item-search-result-list-element.component.ts +++ b/src/app/shared/object-list/my-dspace-result-list-element/workflow-item-search-result/workflow-item-search-result-list-element.component.ts @@ -1,4 +1,4 @@ -import { Component } from '@angular/core'; +import { Component, Inject } from '@angular/core'; import { Observable } from 'rxjs'; import { find, map } from 'rxjs/operators'; @@ -16,6 +16,7 @@ import { TruncatableService } from '../../../truncatable/truncatable.service'; import { followLink } from '../../../utils/follow-link-config.model'; import { SearchResultListElementComponent } from '../../search-result-list-element/search-result-list-element.component'; import { DSONameService } from '../../../../core/breadcrumbs/dso-name.service'; +import { APP_CONFIG, AppConfig } from '../../../../../config/app-config.interface'; /** * This component renders workflowitem object for the search result in the list view. @@ -39,12 +40,18 @@ export class WorkflowItemSearchResultListElementComponent extends SearchResultLi */ public status = MyDspaceItemStatusType.WORKFLOW; + /** + * Display thumbnails if required by configuration + */ + showThumbnails: boolean; + constructor( protected truncatableService: TruncatableService, protected linkService: LinkService, - protected dsoNameService: DSONameService + protected dsoNameService: DSONameService, + @Inject(APP_CONFIG) protected appConfig: AppConfig ) { - super(truncatableService, dsoNameService); + super(truncatableService, dsoNameService, appConfig); } /** @@ -54,6 +61,7 @@ export class WorkflowItemSearchResultListElementComponent extends SearchResultLi super.ngOnInit(); this.linkService.resolveLink(this.dso, followLink('item')); this.initItem(this.dso.item as Observable> ); + this.showThumbnails = this.appConfig.browseBy.showThumbnails; } /** diff --git a/src/app/shared/object-list/my-dspace-result-list-element/workspace-item-search-result/workspace-item-search-result-list-element.component.html b/src/app/shared/object-list/my-dspace-result-list-element/workspace-item-search-result/workspace-item-search-result-list-element.component.html index 07e8aaa75d..4781b87f01 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/workspace-item-search-result/workspace-item-search-result-list-element.component.html +++ b/src/app/shared/object-list/my-dspace-result-list-element/workspace-item-search-result/workspace-item-search-result-list-element.component.html @@ -4,7 +4,11 @@ [object]="object" [status]="status"> - +
+
+ +
+
; @@ -54,6 +55,13 @@ const item = Object.assign(new Item(), { ] } }); + +const environmentUseThumbs = { + browseBy: { + showThumbnails: true + } +}; + const rd = createSuccessfulRemoteDataObject(item); mockResultObject.indexableObject = Object.assign(new WorkspaceItem(), { item: observableOf(rd) }); let linkService; @@ -68,7 +76,8 @@ describe('WorkspaceItemSearchResultListElementComponent', () => { { provide: TruncatableService, useValue: {} }, { provide: ItemDataService, useValue: {} }, { provide: LinkService, useValue: linkService }, - { provide: DSONameService, useClass: DSONameServiceMock } + { provide: DSONameService, useClass: DSONameServiceMock }, + { provide: APP_CONFIG, useValue: environmentUseThumbs } ], schemas: [NO_ERRORS_SCHEMA] }).overrideComponent(WorkspaceItemSearchResultListElementComponent, { @@ -110,4 +119,10 @@ describe('WorkspaceItemSearchResultListElementComponent', () => { expect(component.reloadedObject.emit).toHaveBeenCalledWith(actionPayload.reloadedObject); })); + + + it('should add an offset to the actions element', () => { + const thumbnail = fixture.debugElement.query(By.css('.offset-3')); + expect(thumbnail).toBeTruthy(); + }); }); diff --git a/src/app/shared/object-list/my-dspace-result-list-element/workspace-item-search-result/workspace-item-search-result-list-element.component.ts b/src/app/shared/object-list/my-dspace-result-list-element/workspace-item-search-result/workspace-item-search-result-list-element.component.ts index 5edfc1929e..675cdef975 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/workspace-item-search-result/workspace-item-search-result-list-element.component.ts +++ b/src/app/shared/object-list/my-dspace-result-list-element/workspace-item-search-result/workspace-item-search-result-list-element.component.ts @@ -1,4 +1,4 @@ -import { Component } from '@angular/core'; +import { Component, Inject } from '@angular/core'; import { Observable } from 'rxjs'; import { find, map } from 'rxjs/operators'; @@ -16,6 +16,7 @@ import { TruncatableService } from '../../../truncatable/truncatable.service'; import { followLink } from '../../../utils/follow-link-config.model'; import { SearchResultListElementComponent } from '../../search-result-list-element/search-result-list-element.component'; import { DSONameService } from '../../../../core/breadcrumbs/dso-name.service'; +import { APP_CONFIG, AppConfig } from '../../../../../config/app-config.interface'; /** * This component renders workspaceitem object for the search result in the list view. @@ -39,12 +40,18 @@ export class WorkspaceItemSearchResultListElementComponent extends SearchResultL */ status = MyDspaceItemStatusType.WORKSPACE; + /** + * Display thumbnails if required by configuration + */ + showThumbnails: boolean; + constructor( protected truncatableService: TruncatableService, protected linkService: LinkService, - protected dsoNameService: DSONameService + protected dsoNameService: DSONameService, + @Inject(APP_CONFIG) protected appConfig: AppConfig ) { - super(truncatableService, dsoNameService); + super(truncatableService, dsoNameService, appConfig); } /** @@ -54,6 +61,7 @@ export class WorkspaceItemSearchResultListElementComponent extends SearchResultL super.ngOnInit(); this.linkService.resolveLink(this.dso, followLink('item')); this.initItem(this.dso.item as Observable>); + this.showThumbnails = this.appConfig.browseBy.showThumbnails; } /** diff --git a/src/app/shared/object-list/search-result-list-element/collection-search-result/collection-search-result-list-element.component.html b/src/app/shared/object-list/search-result-list-element/collection-search-result/collection-search-result-list-element.component.html index c98003cd1d..83a8f4fdfa 100644 --- a/src/app/shared/object-list/search-result-list-element/collection-search-result/collection-search-result-list-element.component.html +++ b/src/app/shared/object-list/search-result-list-element/collection-search-result/collection-search-result-list-element.component.html @@ -1,4 +1,9 @@ - - - -
+
+
+
+ + + +
+
+
diff --git a/src/app/shared/object-list/search-result-list-element/collection-search-result/collection-search-result-list-element.component.spec.ts b/src/app/shared/object-list/search-result-list-element/collection-search-result/collection-search-result-list-element.component.spec.ts index bdfe825c82..e5ea60b35d 100644 --- a/src/app/shared/object-list/search-result-list-element/collection-search-result/collection-search-result-list-element.component.spec.ts +++ b/src/app/shared/object-list/search-result-list-element/collection-search-result/collection-search-result-list-element.component.spec.ts @@ -9,6 +9,7 @@ import { TruncatableService } from '../../../truncatable/truncatable.service'; import { CollectionSearchResult } from '../../../object-collection/shared/collection-search-result.model'; import { DSONameService } from '../../../../core/breadcrumbs/dso-name.service'; import { DSONameServiceMock } from '../../../mocks/dso-name.service.mock'; +import { APP_CONFIG } from '../../../../../config/app-config.interface'; let collectionSearchResultListElementComponent: CollectionSearchResultListElementComponent; let fixture: ComponentFixture; @@ -43,13 +44,20 @@ mockCollectionWithoutAbstract.indexableObject = Object.assign(new Collection(), } }); +const environmentUseThumbs = { + browseBy: { + showThumbnails: true + } +}; + describe('CollectionSearchResultListElementComponent', () => { beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ declarations: [CollectionSearchResultListElementComponent, TruncatePipe], providers: [ { provide: TruncatableService, useValue: truncatableServiceStub }, - { provide: DSONameService, useClass: DSONameServiceMock } + { provide: DSONameService, useClass: DSONameServiceMock }, + { provide: APP_CONFIG, useValue: environmentUseThumbs } ], schemas: [NO_ERRORS_SCHEMA] }).overrideComponent(CollectionSearchResultListElementComponent, { @@ -87,4 +95,11 @@ describe('CollectionSearchResultListElementComponent', () => { expect(collectionAbstractField).toBeNull(); }); }); + + describe('when environment is set to show thumbnail images', () => { + it('should offset content', () => { + const offset = fixture.debugElement.query(By.css('offset-md-2')); + }); + }); + }); diff --git a/src/app/shared/object-list/search-result-list-element/collection-search-result/collection-search-result-list-element.component.ts b/src/app/shared/object-list/search-result-list-element/collection-search-result/collection-search-result-list-element.component.ts index 9e09068ddf..60415f649e 100644 --- a/src/app/shared/object-list/search-result-list-element/collection-search-result/collection-search-result-list-element.component.ts +++ b/src/app/shared/object-list/search-result-list-element/collection-search-result/collection-search-result-list-element.component.ts @@ -14,4 +14,16 @@ import { listableObjectComponent } from '../../../object-collection/shared/lista * Component representing a collection search result in list view */ @listableObjectComponent(CollectionSearchResult, ViewMode.ListElement) -export class CollectionSearchResultListElementComponent extends SearchResultListElementComponent {} +export class CollectionSearchResultListElementComponent extends SearchResultListElementComponent { + + /** + * Display thumbnails if required by configuration + */ + showThumbnails: boolean; + + ngOnInit(): void { + super.ngOnInit(); + this.showThumbnails = this.appConfig.browseBy.showThumbnails; + } + +} diff --git a/src/app/shared/object-list/search-result-list-element/community-search-result/community-search-result-list-element.component.html b/src/app/shared/object-list/search-result-list-element/community-search-result/community-search-result-list-element.component.html index e0f0319ffc..22be357a53 100644 --- a/src/app/shared/object-list/search-result-list-element/community-search-result/community-search-result-list-element.component.html +++ b/src/app/shared/object-list/search-result-list-element/community-search-result/community-search-result-list-element.component.html @@ -1,4 +1,9 @@ - - - -
+
+
+
+ + + +
+
+
diff --git a/src/app/shared/object-list/search-result-list-element/community-search-result/community-search-result-list-element.component.spec.ts b/src/app/shared/object-list/search-result-list-element/community-search-result/community-search-result-list-element.component.spec.ts index 529594671c..ce12f5f7da 100644 --- a/src/app/shared/object-list/search-result-list-element/community-search-result/community-search-result-list-element.component.spec.ts +++ b/src/app/shared/object-list/search-result-list-element/community-search-result/community-search-result-list-element.component.spec.ts @@ -9,6 +9,7 @@ import { TruncatableService } from '../../../truncatable/truncatable.service'; import { CommunitySearchResult } from '../../../object-collection/shared/community-search-result.model'; import { DSONameService } from '../../../../core/breadcrumbs/dso-name.service'; import { DSONameServiceMock } from '../../../mocks/dso-name.service.mock'; +import { APP_CONFIG } from '../../../../../config/app-config.interface'; let communitySearchResultListElementComponent: CommunitySearchResultListElementComponent; let fixture: ComponentFixture; @@ -43,13 +44,20 @@ mockCommunityWithoutAbstract.indexableObject = Object.assign(new Community(), { } }); +const environmentUseThumbs = { + browseBy: { + showThumbnails: true + } +}; + describe('CommunitySearchResultListElementComponent', () => { beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ declarations: [CommunitySearchResultListElementComponent, TruncatePipe], providers: [ { provide: TruncatableService, useValue: truncatableServiceStub }, - { provide: DSONameService, useClass: DSONameServiceMock } + { provide: DSONameService, useClass: DSONameServiceMock }, + { provide: APP_CONFIG, useValue: environmentUseThumbs } ], schemas: [NO_ERRORS_SCHEMA] @@ -88,4 +96,10 @@ describe('CommunitySearchResultListElementComponent', () => { expect(communityAbstractField).toBeNull(); }); }); + + describe('when environment is set to show thumbnail images', () => { + it('should offset content', () => { + const offset = fixture.debugElement.query(By.css('offset-md-2')); + }); + }); }); diff --git a/src/app/shared/object-list/search-result-list-element/community-search-result/community-search-result-list-element.component.ts b/src/app/shared/object-list/search-result-list-element/community-search-result/community-search-result-list-element.component.ts index b2fd695b39..4cc25b8b76 100644 --- a/src/app/shared/object-list/search-result-list-element/community-search-result/community-search-result-list-element.component.ts +++ b/src/app/shared/object-list/search-result-list-element/community-search-result/community-search-result-list-element.component.ts @@ -15,5 +15,14 @@ import { listableObjectComponent } from '../../../object-collection/shared/lista */ @listableObjectComponent(CommunitySearchResult, ViewMode.ListElement) export class CommunitySearchResultListElementComponent extends SearchResultListElementComponent { + /** + * Display thumbnails if required by configuration + */ + showThumbnails: boolean; + + ngOnInit(): void { + super.ngOnInit(); + this.showThumbnails = this.appConfig.browseBy.showThumbnails; + } } diff --git a/src/app/shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component.html b/src/app/shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component.html index 645cace8dc..90a6633980 100644 --- a/src/app/shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component.html +++ b/src/app/shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component.html @@ -1,15 +1,24 @@ -
- - -
- - +
+
- - + [routerLink]="[itemPageRoute]" class="dont-break-out"> + + + +
+
+
+ + +
+ + + + + ( @@ -23,9 +32,11 @@ -
+
+ [innerHTML]="firstMetadataValue('dc.description.abstract')"> -
- +
+
+
+
diff --git a/src/app/shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component.spec.ts b/src/app/shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component.spec.ts index d32ac8058d..d1e6c27ba4 100644 --- a/src/app/shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component.spec.ts +++ b/src/app/shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component.spec.ts @@ -9,6 +9,7 @@ import { TruncatableService } from '../../../../../truncatable/truncatable.servi import { ItemSearchResult } from '../../../../../object-collection/shared/item-search-result.model'; import { DSONameService } from '../../../../../../core/breadcrumbs/dso-name.service'; import { DSONameServiceMock, UNDEFINED_NAME } from '../../../../../mocks/dso-name.service.mock'; +import { APP_CONFIG } from '../../../../../../../config/app-config.interface'; let publicationListElementComponent: ItemSearchResultListElementComponent; let fixture: ComponentFixture; @@ -59,14 +60,26 @@ const mockItemWithoutMetadata: ItemSearchResult = Object.assign(new ItemSearchRe }) }); +const environmentUseThumbs = { + browseBy: { + showThumbnails: true + } +}; -describe('ItemListElementComponent', () => { +const enviromentNoThumbs = { + browseBy: { + showThumbnails: false + } +}; + +describe('ItemSearchResultListElementComponent', () => { beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ declarations: [ItemSearchResultListElementComponent, TruncatePipe], providers: [ { provide: TruncatableService, useValue: {} }, - { provide: DSONameService, useClass: DSONameServiceMock } + { provide: DSONameService, useClass: DSONameServiceMock }, + { provide: APP_CONFIG, useValue: environmentUseThumbs } ], schemas: [NO_ERRORS_SCHEMA] @@ -81,6 +94,21 @@ describe('ItemListElementComponent', () => { })); + describe('with environment.browseBy.showThumbnails set to true', () => { + beforeEach(() => { + publicationListElementComponent.object = mockItemWithMetadata; + fixture.detectChanges(); + }); + it('should set showThumbnails to true', () => { + expect(publicationListElementComponent.showThumbnails).toBeTrue(); + }); + + it('should add ds-thumbnail element', () => { + const thumbnailElement = fixture.debugElement.query(By.css('ds-thumbnail')); + expect(thumbnailElement).toBeTruthy(); + }); + }); + describe('When the item has an author', () => { beforeEach(() => { publicationListElementComponent.object = mockItemWithMetadata; @@ -189,3 +217,39 @@ describe('ItemListElementComponent', () => { }); }); }); + +describe('ItemSearchResultListElementComponent', () => { + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [ItemSearchResultListElementComponent, TruncatePipe], + providers: [ + {provide: TruncatableService, useValue: {}}, + {provide: DSONameService, useClass: DSONameServiceMock}, + { provide: APP_CONFIG, useValue: enviromentNoThumbs } + ], + + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(ItemSearchResultListElementComponent, { + set: {changeDetection: ChangeDetectionStrategy.Default} + }).compileComponents(); + })); + + beforeEach(waitForAsync(() => { + fixture = TestBed.createComponent(ItemSearchResultListElementComponent); + publicationListElementComponent = fixture.componentInstance; + })); + + describe('with environment.browseBy.showThumbnails set to false', () => { + beforeEach(() => { + + publicationListElementComponent.object = mockItemWithMetadata; + fixture.detectChanges(); + }); + + it('should not add ds-thumbnail element', () => { + const thumbnailElement = fixture.debugElement.query(By.css('ds-thumbnail')); + expect(thumbnailElement).toBeFalsy(); + }); + }); +}); diff --git a/src/app/shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component.ts b/src/app/shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component.ts index b5eb64869d..f84ae642ad 100644 --- a/src/app/shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component.ts +++ b/src/app/shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component.ts @@ -22,8 +22,14 @@ export class ItemSearchResultListElementComponent extends SearchResultListElemen */ itemPageRoute: string; + /** + * Display thumbnails if required by configuration + */ + showThumbnails: boolean; + ngOnInit(): void { super.ngOnInit(); + this.showThumbnails = this.appConfig.browseBy.showThumbnails; this.itemPageRoute = getItemPageRoute(this.dso); } } diff --git a/src/app/shared/object-list/search-result-list-element/search-result-list-element.component.ts b/src/app/shared/object-list/search-result-list-element/search-result-list-element.component.ts index b85fb0a39f..72120a6b68 100644 --- a/src/app/shared/object-list/search-result-list-element/search-result-list-element.component.ts +++ b/src/app/shared/object-list/search-result-list-element/search-result-list-element.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, Inject, OnInit } from '@angular/core'; import { Observable } from 'rxjs'; import { SearchResult } from '../../search/models/search-result.model'; @@ -8,6 +8,7 @@ import { AbstractListableElementComponent } from '../../object-collection/shared import { TruncatableService } from '../../truncatable/truncatable.service'; import { Metadata } from '../../../core/shared/metadata.utils'; import { DSONameService } from '../../../core/breadcrumbs/dso-name.service'; +import { APP_CONFIG, AppConfig } from '../../../../config/app-config.interface'; @Component({ selector: 'ds-search-result-list-element', @@ -20,7 +21,9 @@ export class SearchResultListElementComponent, K exten dso: K; dsoTitle: string; - public constructor(protected truncatableService: TruncatableService, protected dsoNameService: DSONameService) { + public constructor(protected truncatableService: TruncatableService, + protected dsoNameService: DSONameService, + @Inject(APP_CONFIG) protected appConfig?: AppConfig) { super(); } diff --git a/src/app/shared/object-list/sidebar-search-list-element/sidebar-search-list-element.component.ts b/src/app/shared/object-list/sidebar-search-list-element/sidebar-search-list-element.component.ts index 91f3ba6d88..7f343c6dce 100644 --- a/src/app/shared/object-list/sidebar-search-list-element/sidebar-search-list-element.component.ts +++ b/src/app/shared/object-list/sidebar-search-list-element/sidebar-search-list-element.component.ts @@ -42,7 +42,7 @@ export class SidebarSearchListElementComponent, K exte protected linkService: LinkService, protected dsoNameService: DSONameService ) { - super(truncatableService, dsoNameService); + super(truncatableService, dsoNameService, null); } /** diff --git a/src/app/shared/pagination/pagination.component.scss b/src/app/shared/pagination/pagination.component.scss index 755d916faa..dac826dc6d 100644 --- a/src/app/shared/pagination/pagination.component.scss +++ b/src/app/shared/pagination/pagination.component.scss @@ -5,4 +5,4 @@ .dropdown-item { padding-left: 20px; } -} \ No newline at end of file +} diff --git a/src/app/shared/utils/object-list-utils.ts b/src/app/shared/utils/object-list-utils.ts new file mode 100644 index 0000000000..cb7b6b739a --- /dev/null +++ b/src/app/shared/utils/object-list-utils.ts @@ -0,0 +1,15 @@ +/** + * Sets the class to be used for the "no thumbnail" + * placeholder font size in lists. + */ +export function setPlaceHolderAttributes(width: number): string { + if (width < 400) { + return 'thumb-font-0'; + } else if (width < 750) { + return 'thumb-font-1'; + } else if (width < 1000) { + return 'thumb-font-2'; + } else { + return 'thumb-font-3'; + } +} diff --git a/src/app/shared/utils/relation-query.utils.ts b/src/app/shared/utils/relation-query.utils.ts index 13248c5a6b..62a69075fc 100644 --- a/src/app/shared/utils/relation-query.utils.ts +++ b/src/app/shared/utils/relation-query.utils.ts @@ -1,3 +1,6 @@ +import { followLink, FollowLinkConfig } from './follow-link-config.model'; +import { Relationship } from '../../core/shared/item-relationships/relationship.model'; + /** * Get the query for looking up items by relation type * @param {string} relationType Relation type @@ -16,3 +19,21 @@ export function getQueryByRelations(relationType: string, itemUUID: string): str export function getFilterByRelation(relationType: string, itemUUID: string): string { return `f.${relationType}=${itemUUID},equals`; } + +/** + * Creates links to follow for the leftItem and rightItem. Links will include + * @param showThumbnail thumbnail image configuration + * @returns followLink array + */ +export function itemLinksToFollow(showThumbnail: boolean): FollowLinkConfig[] { + let linksToFollow: FollowLinkConfig[]; + if (showThumbnail) { + linksToFollow = [ + followLink('leftItem',{}, followLink('thumbnail')), + followLink('rightItem',{}, followLink('thumbnail')) + ]; + } else { + linksToFollow = [followLink('leftItem'), followLink('rightItem')]; + } + return linksToFollow; +} diff --git a/src/app/thumbnail/thumbnail.component.html b/src/app/thumbnail/thumbnail.component.html index aedfd95c5a..e73ad77ce5 100644 --- a/src/app/thumbnail/thumbnail.component.html +++ b/src/app/thumbnail/thumbnail.component.html @@ -7,7 +7,7 @@ [src]="src | dsSafeUrl" [alt]="alt | translate" (error)="errorHandler()">
-
{{ placeholder | translate }}
+
{{ placeholder | translate }}
diff --git a/src/app/thumbnail/thumbnail.component.ts b/src/app/thumbnail/thumbnail.component.ts index 3e122cde78..b47beb22d4 100644 --- a/src/app/thumbnail/thumbnail.component.ts +++ b/src/app/thumbnail/thumbnail.component.ts @@ -14,6 +14,7 @@ import { RemoteData } from '../core/data/remote-data'; templateUrl: './thumbnail.component.html', }) export class ThumbnailComponent implements OnChanges { + /** * The thumbnail Bitstream */ @@ -90,4 +91,5 @@ export class ThumbnailComponent implements OnChanges { this.src = null; } } + } diff --git a/src/config/browse-by-config.interface.ts b/src/config/browse-by-config.interface.ts index c67d072731..e82edacfdd 100644 --- a/src/config/browse-by-config.interface.ts +++ b/src/config/browse-by-config.interface.ts @@ -23,10 +23,16 @@ export interface BrowseByConfig extends Config { */ defaultLowerLimit: number; + /** + * If true, thumbnail images for items will be added to BOTH search and browse result lists. + */ + showThumbnails: boolean; + /** * Number of entries in the viewport of a paginated browse-by list. * Rounded to the nearest size in the list of selectable sizes on the settings * menu. See pageSizeOptions in 'pagination-component-options.model.ts'. */ pageSize: number; + } diff --git a/src/config/default-app-config.ts b/src/config/default-app-config.ts index fde6d89102..c2c0b308e7 100644 --- a/src/config/default-app-config.ts +++ b/src/config/default-app-config.ts @@ -210,6 +210,8 @@ export class DefaultAppConfig implements AppConfig { fiveYearLimit: 30, // The absolute lowest year to display in the dropdown (only used when no lowest date can be found for all items) defaultLowerLimit: 1900, + // Whether to add item thumbnail images to BOTH browse and search result lists. + showThumbnails: true, // 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. See pageSizeOptions in 'pagination-component-options.model.ts'. diff --git a/src/environments/environment.test.ts b/src/environments/environment.test.ts index 8a3492c207..edbf33b79c 100644 --- a/src/environments/environment.test.ts +++ b/src/environments/environment.test.ts @@ -203,6 +203,8 @@ export const environment: BuildConfig = { fiveYearLimit: 30, // The absolute lowest year to display in the dropdown (only used when no lowest date can be found for all items) defaultLowerLimit: 1900, + // Whether to add item thumbnail images to BOTH browse and search result lists. + showThumbnails: true, // 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. See pageSizeOptions in 'pagination-component-options.model.ts'. diff --git a/src/styles/_custom_variables.scss b/src/styles/_custom_variables.scss index 40180d8342..2000e007a0 100644 --- a/src/styles/_custom_variables.scss +++ b/src/styles/_custom_variables.scss @@ -45,7 +45,7 @@ --ds-edit-item-metadata-field-width: 190px; --ds-edit-item-language-field-width: 43px; - --ds-thumbnail-max-width: 175px; + --ds-thumbnail-max-width: 125px; --ds-thumbnail-placeholder-background: #{$gray-100}; --ds-thumbnail-placeholder-border: 1px solid #{$gray-300}; --ds-thumbnail-placeholder-color: #{lighten($gray-800, 7%)}; diff --git a/src/styles/_global-styles.scss b/src/styles/_global-styles.scss index 89d1d76e9a..486b09bc70 100644 --- a/src/styles/_global-styles.scss +++ b/src/styles/_global-styles.scss @@ -138,3 +138,51 @@ ds-dynamic-form-control-container.d-none { .btn-dark { background-color: var(--ds-admin-sidebar-bg); } +/* The font sizes used in "no thumbnail" placeholder */ +.thumb-font-0 { + .thumbnail-placeholder { + @media screen and (max-width: map-get($grid-breakpoints, lg)) { + font-size: 0.7rem; + padding: 0.2rem; + } + @media screen and (max-width: map-get($grid-breakpoints, sm)) { + font-size: 0.6rem; + padding: 0.1rem; + } + font-size: 0.4rem; + padding: 0.1rem; + } +} + +.hide-placeholder-text { + .thumbnail-placeholder { + color: transparent !important; + } +} + +.thumb-font-1 { + .thumbnail-placeholder { + @media screen and (max-width: map-get($grid-breakpoints, sm)) { + font-size: 0.9rem; + padding: 0.1rem; + } + @media screen and (max-width: 950px) { + font-size: 0.4rem; + padding: 0.1rem; + } + font-size: 0.6rem; + padding: 0.125rem; + } +} +.thumb-font-2 { + .thumbnail-placeholder { + font-size: 0.9rem; + padding: 0.125rem; + } +} +.thumb-font-3 { + .thumbnail-placeholder { + font-size: 1.25rem; + padding: 0.5rem; + } +}