Skip to content

Commit ad13a39

Browse files
authored
Merge pull request DSpace#1860 from atmire/Fix-recent-submissions-list-thumbnails
Fix recent submissions list thumbnails
2 parents 0e23e85 + 396bbd4 commit ad13a39

6 files changed

Lines changed: 133 additions & 84 deletions

File tree

src/app/home-page/home-page.module.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ import { StatisticsModule } from '../statistics/statistics.module';
1010
import { ThemedHomeNewsComponent } from './home-news/themed-home-news.component';
1111
import { ThemedHomePageComponent } from './themed-home-page.component';
1212
import { RecentItemListComponent } from './recent-item-list/recent-item-list.component';
13+
import { JournalEntitiesModule } from '../entity-groups/journal-entities/journal-entities.module';
14+
import { ResearchEntitiesModule } from '../entity-groups/research-entities/research-entities.module';
15+
1316
const DECLARATIONS = [
1417
HomePageComponent,
1518
ThemedHomePageComponent,
@@ -22,7 +25,9 @@ const DECLARATIONS = [
2225
@NgModule({
2326
imports: [
2427
CommonModule,
25-
SharedModule,
28+
SharedModule.withEntryComponents(),
29+
JournalEntitiesModule.withEntryComponents(),
30+
ResearchEntitiesModule.withEntryComponents(),
2631
HomePageRoutingModule,
2732
StatisticsModule.forRoot()
2833
],

src/app/home-page/recent-item-list/recent-item-list.component.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<ng-container *ngVar="(itemRD$ | async) as itemRD">
2-
<div class="mt-4" *ngIf="itemRD?.hasSucceeded && itemRD?.payload?.page.length > 0" @fadeIn>
2+
<div class="mt-4" [ngClass]="placeholderFontClass" *ngIf="itemRD?.hasSucceeded && itemRD?.payload?.page.length > 0" @fadeIn>
33
<div class="d-flex flex-row border-bottom mb-4 pb-4 ng-tns-c416-2"></div>
44
<h2> {{'home.recent-submissions.head' | translate}}</h2>
55
<div class="my-4" *ngFor="let item of itemRD?.payload?.page">
@@ -12,4 +12,4 @@ <h2> {{'home.recent-submissions.head' | translate}}</h2>
1212
<ds-error *ngIf="itemRD?.hasFailed" message="{{'error.recent-submissions' | translate}}"></ds-error>
1313
<ds-loading *ngIf="!itemRD || itemRD.isLoading" message="{{'loading.recent-submissions' | translate}}">
1414
</ds-loading>
15-
</ng-container>
15+
</ng-container>

src/app/home-page/recent-item-list/recent-item-list.component.spec.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,11 @@ import { SearchConfigurationService } from '../../core/shared/search/search-conf
1010
import { PaginatedSearchOptions } from '../../shared/search/models/paginated-search-options.model';
1111
import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
1212
import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model';
13-
import { ViewMode } from 'src/app/core/shared/view-mode.model';
1413
import { of as observableOf } from 'rxjs';
14+
import { APP_CONFIG } from '../../../config/app-config.interface';
15+
import { environment } from '../../../environments/environment';
16+
import { PLATFORM_ID } from '@angular/core';
17+
1518
describe('RecentItemListComponent', () => {
1619
let component: RecentItemListComponent;
1720
let fixture: ComponentFixture<RecentItemListComponent>;
@@ -42,6 +45,8 @@ describe('RecentItemListComponent', () => {
4245
{ provide: SearchService, useValue: searchServiceStub },
4346
{ provide: PaginationService, useValue: paginationService },
4447
{ provide: SearchConfigurationService, useValue: searchConfigServiceStub },
48+
{ provide: APP_CONFIG, useValue: environment },
49+
{ provide: PLATFORM_ID, useValue: 'browser' },
4550
],
4651
})
4752
.compileComponents();

src/app/home-page/recent-item-list/recent-item-list.component.ts

Lines changed: 44 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
1+
import { ChangeDetectionStrategy, Component, ElementRef, Inject, OnInit, PLATFORM_ID } from '@angular/core';
22
import { PaginatedSearchOptions } from '../../shared/search/models/paginated-search-options.model';
33
import { fadeIn, fadeInOut } from '../../shared/animations/fade';
44
import { RemoteData } from '../../core/data/remote-data';
@@ -11,12 +11,13 @@ import { SortDirection, SortOptions } from '../../core/cache/models/sort-options
1111
import { environment } from '../../../environments/environment';
1212
import { ViewMode } from '../../core/shared/view-mode.model';
1313
import { SearchConfigurationService } from '../../core/shared/search/search-configuration.service';
14-
import {
15-
toDSpaceObjectListRD
16-
} from '../../core/shared/operators';
17-
import {
18-
Observable,
19-
} from 'rxjs';
14+
import { toDSpaceObjectListRD } from '../../core/shared/operators';
15+
import { Observable } from 'rxjs';
16+
import { followLink, FollowLinkConfig } from '../../shared/utils/follow-link-config.model';
17+
import { APP_CONFIG, AppConfig } from '../../../config/app-config.interface';
18+
import { isPlatformBrowser } from '@angular/common';
19+
import { setPlaceHolderAttributes } from '../../shared/utils/object-list-utils';
20+
2021
@Component({
2122
selector: 'ds-recent-item-list',
2223
templateUrl: './recent-item-list.component.html',
@@ -31,14 +32,22 @@ export class RecentItemListComponent implements OnInit {
3132
itemRD$: Observable<RemoteData<PaginatedList<Item>>>;
3233
paginationConfig: PaginationComponentOptions;
3334
sortConfig: SortOptions;
35+
3436
/**
3537
* The view-mode we're currently on
3638
* @type {ViewMode}
3739
*/
3840
viewMode = ViewMode.ListElement;
39-
constructor(private searchService: SearchService,
41+
42+
private _placeholderFontClass: string;
43+
44+
constructor(
45+
private searchService: SearchService,
4046
private paginationService: PaginationService,
41-
public searchConfigurationService: SearchConfigurationService
47+
public searchConfigurationService: SearchConfigurationService,
48+
protected elementRef: ElementRef,
49+
@Inject(APP_CONFIG) private appConfig: AppConfig,
50+
@Inject(PLATFORM_ID) private platformId: Object,
4251
) {
4352

4453
this.paginationConfig = Object.assign(new PaginationComponentOptions(), {
@@ -50,16 +59,29 @@ export class RecentItemListComponent implements OnInit {
5059
this.sortConfig = new SortOptions(environment.homePage.recentSubmissions.sortField, SortDirection.DESC);
5160
}
5261
ngOnInit(): void {
62+
const linksToFollow: FollowLinkConfig<Item>[] = [];
63+
if (this.appConfig.browseBy.showThumbnails) {
64+
linksToFollow.push(followLink('thumbnail'));
65+
}
66+
5367
this.itemRD$ = this.searchService.search(
5468
new PaginatedSearchOptions({
5569
pagination: this.paginationConfig,
5670
sort: this.sortConfig,
5771
}),
58-
).pipe(toDSpaceObjectListRD()) as Observable<RemoteData<PaginatedList<Item>>>;
72+
undefined,
73+
undefined,
74+
undefined,
75+
...linksToFollow,
76+
).pipe(
77+
toDSpaceObjectListRD()
78+
) as Observable<RemoteData<PaginatedList<Item>>>;
5979
}
80+
6081
ngOnDestroy(): void {
6182
this.paginationService.clearPagination(this.paginationConfig.id);
6283
}
84+
6385
onLoadMore(): void {
6486
this.paginationService.updateRouteWithUrl(this.searchConfigurationService.paginationID, ['search'], {
6587
sortField: environment.homePage.recentSubmissions.sortField,
@@ -68,5 +90,17 @@ export class RecentItemListComponent implements OnInit {
6890
});
6991
}
7092

93+
get placeholderFontClass(): string {
94+
if (this._placeholderFontClass === undefined) {
95+
if (isPlatformBrowser(this.platformId)) {
96+
const width = this.elementRef.nativeElement.offsetWidth;
97+
this._placeholderFontClass = setPlaceHolderAttributes(width);
98+
} else {
99+
this._placeholderFontClass = 'hide-placeholder-text';
100+
}
101+
}
102+
return this._placeholderFontClass;
103+
}
104+
71105
}
72106

src/app/thumbnail/thumbnail.component.spec.ts

Lines changed: 47 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -127,19 +127,18 @@ describe('ThumbnailComponent', () => {
127127
});
128128

129129
const errorHandler = () => {
130-
let fallbackSpy;
130+
let setSrcSpy;
131131

132132
beforeEach(() => {
133-
fallbackSpy = spyOn(comp, 'showFallback').and.callThrough();
133+
// disconnect error handler to be sure it's only called once
134+
const img = fixture.debugElement.query(By.css('img.thumbnail-content'));
135+
img.nativeNode.onerror = null;
136+
137+
comp.ngOnChanges();
138+
setSrcSpy = spyOn(comp, 'setSrc').and.callThrough();
134139
});
135140

136141
describe('retry with authentication token', () => {
137-
beforeEach(() => {
138-
// disconnect error handler to be sure it's only called once
139-
const img = fixture.debugElement.query(By.css('img.thumbnail-content'));
140-
img.nativeNode.onerror = null;
141-
});
142-
143142
it('should remember that it already retried once', () => {
144143
expect(comp.retriedWithToken).toBeFalse();
145144
comp.errorHandler();
@@ -153,7 +152,7 @@ describe('ThumbnailComponent', () => {
153152

154153
it('should fall back to default', () => {
155154
comp.errorHandler();
156-
expect(fallbackSpy).toHaveBeenCalled();
155+
expect(setSrcSpy).toHaveBeenCalledWith(comp.defaultImage);
157156
});
158157
});
159158

@@ -172,11 +171,9 @@ describe('ThumbnailComponent', () => {
172171

173172
if ((comp.thumbnail as RemoteData<Bitstream>)?.hasFailed) {
174173
// If we failed to retrieve the Bitstream in the first place, fall back to the default
175-
expect(comp.src$.getValue()).toBe(null);
176-
expect(fallbackSpy).toHaveBeenCalled();
174+
expect(setSrcSpy).toHaveBeenCalledWith(comp.defaultImage);
177175
} else {
178-
expect(comp.src$.getValue()).toBe(CONTENT + '?authentication-token=fake');
179-
expect(fallbackSpy).not.toHaveBeenCalled();
176+
expect(setSrcSpy).toHaveBeenCalledWith(CONTENT + '?authentication-token=fake');
180177
}
181178
});
182179
});
@@ -189,8 +186,7 @@ describe('ThumbnailComponent', () => {
189186
it('should fall back to default', () => {
190187
comp.errorHandler();
191188

192-
expect(comp.src$.getValue()).toBe(null);
193-
expect(fallbackSpy).toHaveBeenCalled();
189+
expect(setSrcSpy).toHaveBeenCalledWith(comp.defaultImage);
194190

195191
// We don't need to check authorization if we failed to retrieve the Bitstreamin the first place
196192
if (!(comp.thumbnail as RemoteData<Bitstream>)?.hasFailed) {
@@ -210,7 +206,7 @@ describe('ThumbnailComponent', () => {
210206
comp.errorHandler();
211207
expect(authService.isAuthenticated).not.toHaveBeenCalled();
212208
expect(fileService.retrieveFileDownloadLink).not.toHaveBeenCalled();
213-
expect(fallbackSpy).toHaveBeenCalled();
209+
expect(setSrcSpy).toHaveBeenCalledWith(comp.defaultImage);
214210
});
215211
});
216212
};
@@ -263,21 +259,23 @@ describe('ThumbnailComponent', () => {
263259
comp.thumbnail = thumbnail;
264260
});
265261

266-
it('should display an image', () => {
267-
comp.ngOnChanges();
268-
fixture.detectChanges();
269-
const image: HTMLElement = fixture.debugElement.query(By.css('img')).nativeElement;
270-
expect(image.getAttribute('src')).toBe(thumbnail._links.content.href);
271-
});
262+
describe('if content can be loaded', () => {
263+
it('should display an image', () => {
264+
comp.ngOnChanges();
265+
fixture.detectChanges();
266+
const image: HTMLElement = fixture.debugElement.query(By.css('img')).nativeElement;
267+
expect(image.getAttribute('src')).toBe(thumbnail._links.content.href);
268+
});
272269

273-
it('should include the alt text', () => {
274-
comp.ngOnChanges();
275-
fixture.detectChanges();
276-
const image: HTMLElement = fixture.debugElement.query(By.css('img')).nativeElement;
277-
expect(image.getAttribute('alt')).toBe('TRANSLATED ' + comp.alt);
270+
it('should include the alt text', () => {
271+
comp.ngOnChanges();
272+
fixture.detectChanges();
273+
const image: HTMLElement = fixture.debugElement.query(By.css('img')).nativeElement;
274+
expect(image.getAttribute('alt')).toBe('TRANSLATED ' + comp.alt);
275+
});
278276
});
279277

280-
describe('when there is no thumbnail', () => {
278+
describe('if content can\'t be loaded', () => {
281279
errorHandler();
282280
});
283281
});
@@ -296,36 +294,42 @@ describe('ThumbnailComponent', () => {
296294
};
297295
});
298296

299-
describe('when there is a thumbnail', () => {
297+
describe('if RemoteData succeeded', () => {
300298
beforeEach(() => {
301299
comp.thumbnail = createSuccessfulRemoteDataObject(thumbnail);
302300
});
303301

304-
it('should display an image', () => {
305-
comp.ngOnChanges();
306-
fixture.detectChanges();
307-
const image: HTMLElement = de.query(By.css('img')).nativeElement;
308-
expect(image.getAttribute('src')).toBe(thumbnail._links.content.href);
309-
});
302+
describe('if content can be loaded', () => {
303+
it('should display an image', () => {
304+
comp.ngOnChanges();
305+
fixture.detectChanges();
306+
const image: HTMLElement = de.query(By.css('img')).nativeElement;
307+
expect(image.getAttribute('src')).toBe(thumbnail._links.content.href);
308+
});
310309

311-
it('should display the alt text', () => {
312-
comp.ngOnChanges();
313-
fixture.detectChanges();
314-
const image: HTMLElement = de.query(By.css('img')).nativeElement;
315-
expect(image.getAttribute('alt')).toBe('TRANSLATED ' + comp.alt);
310+
it('should display the alt text', () => {
311+
comp.ngOnChanges();
312+
fixture.detectChanges();
313+
const image: HTMLElement = de.query(By.css('img')).nativeElement;
314+
expect(image.getAttribute('alt')).toBe('TRANSLATED ' + comp.alt);
315+
});
316316
});
317317

318-
describe('but it can\'t be loaded', () => {
318+
describe('if content can\'t be loaded', () => {
319319
errorHandler();
320320
});
321321
});
322322

323-
describe('when there is no thumbnail', () => {
323+
describe('if RemoteData failed', () => {
324324
beforeEach(() => {
325325
comp.thumbnail = createFailedRemoteDataObject();
326326
});
327327

328-
errorHandler();
328+
it('should show the default image', () => {
329+
comp.defaultImage = 'default/image.jpg';
330+
comp.ngOnChanges();
331+
expect(comp.src$.getValue()).toBe('default/image.jpg');
332+
});
329333
});
330334
});
331335
});

0 commit comments

Comments
 (0)