Skip to content

Commit cc618eb

Browse files
authored
Merge pull request #838 from atmire/file-pagination-simple-and-full-item-pages
File pagination simple and full item pages
2 parents 708b549 + 1247088 commit cc618eb

8 files changed

Lines changed: 481 additions & 71 deletions

File tree

Lines changed: 84 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,87 @@
11
<ds-metadata-field-wrapper [label]="label | translate">
2-
<div class="file-section row" *ngFor="let file of (bitstreams$ | async); let last=last;">
3-
<div class="col-3">
4-
<ds-thumbnail [thumbnail]="(file.thumbnail | async)?.payload"></ds-thumbnail>
5-
</div>
6-
<div class="col-7">
7-
<dl class="row">
8-
<dt class="col-md-4">{{"item.page.filesection.name" | translate}}</dt>
9-
<dd class="col-md-8">{{file.name}}</dd>
10-
11-
<dt class="col-md-4">{{"item.page.filesection.size" | translate}}</dt>
12-
<dd class="col-md-8">{{(file.sizeBytes) | dsFileSize }}</dd>
13-
14-
15-
<dt class="col-md-4">{{"item.page.filesection.format" | translate}}</dt>
16-
<dd class="col-md-8">{{(file.format | async)?.payload?.description}}</dd>
17-
18-
19-
<dt class="col-md-4">{{"item.page.filesection.description" | translate}}</dt>
20-
<dd class="col-md-8">{{file.firstMetadataValue("dc.description")}}</dd>
21-
</dl>
22-
</div>
23-
<div class="col-2">
24-
<ds-file-download-link [href]="file._links.content.href" [download]="file.name">
25-
{{"item.page.filesection.download" | translate}}
26-
</ds-file-download-link>
27-
</div>
2+
<div *ngVar="(originals$ | async)?.payload as originals">
3+
<h5 class="simple-view-element-header">{{"item.page.filesection.original.bundle" | translate}}</h5>
4+
<ds-pagination *ngIf="originals?.page?.length > 0"
5+
[hideGear]="true"
6+
[hidePagerWhenSinglePage]="true"
7+
[paginationOptions]="originalOptions"
8+
[pageInfoState]="originals"
9+
[collectionSize]="originals?.totalElements"
10+
[disableRouteParameterUpdate]="true"
11+
(pageChange)="switchOriginalPage($event)">
12+
13+
14+
15+
<div class="file-section row" *ngFor="let file of originals?.page;">
16+
<div class="col-3">
17+
<ds-thumbnail [thumbnail]="(file.thumbnail | async)?.payload"></ds-thumbnail>
18+
</div>
19+
<div class="col-7">
20+
<dl class="row">
21+
<dt class="col-md-4">{{"item.page.filesection.name" | translate}}</dt>
22+
<dd class="col-md-8">{{file.name}}</dd>
23+
24+
<dt class="col-md-4">{{"item.page.filesection.size" | translate}}</dt>
25+
<dd class="col-md-8">{{(file.sizeBytes) | dsFileSize }}</dd>
26+
27+
28+
<dt class="col-md-4">{{"item.page.filesection.format" | translate}}</dt>
29+
<dd class="col-md-8">{{(file.format | async)?.payload?.description}}</dd>
30+
31+
32+
<dt class="col-md-4">{{"item.page.filesection.description" | translate}}</dt>
33+
<dd class="col-md-8">{{file.firstMetadataValue("dc.description")}}</dd>
34+
</dl>
35+
</div>
36+
<div class="col-2">
37+
<ds-file-download-link [href]="file._links.content.href" [download]="file.name">
38+
{{"item.page.filesection.download" | translate}}
39+
</ds-file-download-link>
40+
</div>
41+
</div>
42+
</ds-pagination>
43+
</div>
44+
45+
<div *ngVar="(licenses$ | async)?.payload as licenses">
46+
<h5 class="simple-view-element-header">{{"item.page.filesection.license.bundle" | translate}}</h5>
47+
<ds-pagination *ngIf="licenses?.page?.length > 0"
48+
[hideGear]="true"
49+
[hidePagerWhenSinglePage]="true"
50+
[paginationOptions]="licenseOptions"
51+
[pageInfoState]="licenses"
52+
[collectionSize]="licenses?.totalElements"
53+
[disableRouteParameterUpdate]="true"
54+
(pageChange)="switchLicensePage($event)">
55+
56+
57+
58+
<div class="file-section row" *ngFor="let file of licenses?.page;">
59+
<div class="col-3">
60+
<ds-thumbnail [thumbnail]="(file.thumbnail | async)?.payload"></ds-thumbnail>
61+
</div>
62+
<div class="col-7">
63+
<dl class="row">
64+
<dt class="col-md-4">{{"item.page.filesection.name" | translate}}</dt>
65+
<dd class="col-md-8">{{file.name}}</dd>
66+
67+
<dt class="col-md-4">{{"item.page.filesection.size" | translate}}</dt>
68+
<dd class="col-md-8">{{(file.sizeBytes) | dsFileSize }}</dd>
69+
70+
71+
<dt class="col-md-4">{{"item.page.filesection.format" | translate}}</dt>
72+
<dd class="col-md-8">{{(file.format | async)?.payload?.description}}</dd>
73+
74+
75+
<dt class="col-md-4">{{"item.page.filesection.description" | translate}}</dt>
76+
<dd class="col-md-8">{{file.firstMetadataValue("dc.description")}}</dd>
77+
</dl>
78+
</div>
79+
<div class="col-2">
80+
<ds-file-download-link [href]="file._links.content.href" [download]="file.name">
81+
{{"item.page.filesection.download" | translate}}
82+
</ds-file-download-link>
83+
</div>
84+
</div>
85+
</ds-pagination>
2886
</div>
2987
</ds-metadata-field-wrapper>
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
import {FullFileSectionComponent} from './full-file-section.component';
2+
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
3+
import {createSuccessfulRemoteDataObject$} from '../../../../shared/remote-data.utils';
4+
import {createPaginatedList} from '../../../../shared/testing/utils.test';
5+
import {TranslateLoader, TranslateModule} from '@ngx-translate/core';
6+
import {TranslateLoaderMock} from '../../../../shared/mocks/translate-loader.mock';
7+
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
8+
import {VarDirective} from '../../../../shared/utils/var.directive';
9+
import {FileSizePipe} from '../../../../shared/utils/file-size-pipe';
10+
import {MetadataFieldWrapperComponent} from '../../../field-components/metadata-field-wrapper/metadata-field-wrapper.component';
11+
import {BitstreamDataService} from '../../../../core/data/bitstream-data.service';
12+
import {NO_ERRORS_SCHEMA} from '@angular/core';
13+
import {Bitstream} from '../../../../core/shared/bitstream.model';
14+
import {of as observableOf} from 'rxjs';
15+
import {MockBitstreamFormat1} from '../../../../shared/mocks/item.mock';
16+
import {By} from '@angular/platform-browser';
17+
18+
describe('FullFileSectionComponent', () => {
19+
let comp: FullFileSectionComponent;
20+
let fixture: ComponentFixture<FullFileSectionComponent>;
21+
22+
const mockBitstream: Bitstream = Object.assign(new Bitstream(),
23+
{
24+
sizeBytes: 10201,
25+
content: 'https://dspace7.4science.it/dspace-spring-rest/api/core/bitstreams/cf9b0c8e-a1eb-4b65-afd0-567366448713/content',
26+
format: observableOf(MockBitstreamFormat1),
27+
bundleName: 'ORIGINAL',
28+
_links: {
29+
self: {
30+
href: 'https://dspace7.4science.it/dspace-spring-rest/api/core/bitstreams/cf9b0c8e-a1eb-4b65-afd0-567366448713'
31+
},
32+
content: {
33+
href: 'https://dspace7.4science.it/dspace-spring-rest/api/core/bitstreams/cf9b0c8e-a1eb-4b65-afd0-567366448713/content'
34+
}
35+
},
36+
id: 'cf9b0c8e-a1eb-4b65-afd0-567366448713',
37+
uuid: 'cf9b0c8e-a1eb-4b65-afd0-567366448713',
38+
type: 'bitstream',
39+
metadata: {
40+
'dc.title': [
41+
{
42+
language: null,
43+
value: 'test_word.docx'
44+
}
45+
]
46+
}
47+
});
48+
49+
const bitstreamDataService = jasmine.createSpyObj('bitstreamDataService', {
50+
findAllByItemAndBundleName: createSuccessfulRemoteDataObject$(createPaginatedList([mockBitstream, mockBitstream, mockBitstream]))
51+
});
52+
53+
beforeEach(async(() => {
54+
55+
TestBed.configureTestingModule({
56+
imports: [TranslateModule.forRoot({
57+
loader: {
58+
provide: TranslateLoader,
59+
useClass: TranslateLoaderMock
60+
}
61+
}), BrowserAnimationsModule],
62+
declarations: [FullFileSectionComponent, VarDirective, FileSizePipe, MetadataFieldWrapperComponent],
63+
providers: [
64+
{provide: BitstreamDataService, useValue: bitstreamDataService}
65+
],
66+
67+
schemas: [NO_ERRORS_SCHEMA]
68+
}).compileComponents();
69+
}));
70+
71+
beforeEach(async(() => {
72+
fixture = TestBed.createComponent(FullFileSectionComponent);
73+
comp = fixture.componentInstance;
74+
fixture.detectChanges();
75+
}));
76+
77+
describe('when the full file section gets loaded with bitstreams available', () => {
78+
it ('should contain a list with bitstreams', () => {
79+
const fileSection = fixture.debugElement.queryAll(By.css('.file-section'));
80+
expect(fileSection.length).toEqual(6);
81+
});
82+
83+
describe('when we press the pageChange button for original bundle', () => {
84+
beforeEach(() => {
85+
comp.switchOriginalPage(2);
86+
fixture.detectChanges();
87+
});
88+
89+
it ('should give the value to the currentpage', () => {
90+
expect(comp.originalOptions.currentPage).toBe(2);
91+
})
92+
it ('should call the next function on the originalCurrentPage', (done) => {
93+
comp.originalCurrentPage$.subscribe((event) => {
94+
expect(event).toEqual(2);
95+
done();
96+
})
97+
})
98+
})
99+
100+
describe('when we press the pageChange button for license bundle', () => {
101+
beforeEach(() => {
102+
comp.switchLicensePage(2);
103+
fixture.detectChanges();
104+
});
105+
106+
it ('should give the value to the currentpage', () => {
107+
expect(comp.licenseOptions.currentPage).toBe(2);
108+
})
109+
it ('should call the next function on the licenseCurrentPage', (done) => {
110+
comp.licenseCurrentPage$.subscribe((event) => {
111+
expect(event).toEqual(2);
112+
done();
113+
})
114+
})
115+
})
116+
})
117+
})
Lines changed: 57 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
1-
import { Component, Injector, Input, OnInit } from '@angular/core';
2-
import { combineLatest as observableCombineLatest, Observable } from 'rxjs';
3-
import { map, startWith } from 'rxjs/operators';
1+
import { Component, Input, OnInit } from '@angular/core';
2+
import { BehaviorSubject, Observable } from 'rxjs';
43
import { BitstreamDataService } from '../../../../core/data/bitstream-data.service';
54

65
import { Bitstream } from '../../../../core/shared/bitstream.model';
76
import { Item } from '../../../../core/shared/item.model';
8-
import { getFirstSucceededRemoteListPayload } from '../../../../core/shared/operators';
97
import { followLink } from '../../../../shared/utils/follow-link-config.model';
108
import { FileSectionComponent } from '../../../simple/field-components/file-section/file-section.component';
9+
import { PaginationComponentOptions } from '../../../../shared/pagination/pagination-component-options.model';
10+
import { PaginatedList } from '../../../../core/data/paginated-list';
11+
import { RemoteData } from '../../../../core/data/remote-data';
12+
import { switchMap } from 'rxjs/operators';
1113

1214
/**
1315
* This component renders the file section of the item
@@ -25,7 +27,23 @@ export class FullFileSectionComponent extends FileSectionComponent implements On
2527

2628
label: string;
2729

28-
bitstreams$: Observable<Bitstream[]>;
30+
originals$: Observable<RemoteData<PaginatedList<Bitstream>>>;
31+
licenses$: Observable<RemoteData<PaginatedList<Bitstream>>>;
32+
33+
pageSize = 5;
34+
originalOptions = Object.assign(new PaginationComponentOptions(),{
35+
id: 'original-bitstreams-options',
36+
currentPage: 1,
37+
pageSize: this.pageSize
38+
});
39+
originalCurrentPage$ = new BehaviorSubject<number>(1);
40+
41+
licenseOptions = Object.assign(new PaginationComponentOptions(),{
42+
id: 'license-bitstreams-options',
43+
currentPage: 1,
44+
pageSize: this.pageSize
45+
});
46+
licenseCurrentPage$ = new BehaviorSubject<number>(1);
2947

3048
constructor(
3149
bitstreamDataService: BitstreamDataService
@@ -34,40 +52,45 @@ export class FullFileSectionComponent extends FileSectionComponent implements On
3452
}
3553

3654
ngOnInit(): void {
37-
super.ngOnInit();
55+
this.initialize();
3856
}
3957

4058
initialize(): void {
41-
// TODO pagination
42-
const originals$ = this.bitstreamDataService.findAllByItemAndBundleName(
43-
this.item,
44-
'ORIGINAL',
45-
{ elementsPerPage: Number.MAX_SAFE_INTEGER },
46-
followLink( 'format')
47-
).pipe(
48-
getFirstSucceededRemoteListPayload(),
49-
startWith([])
59+
this.originals$ = this.originalCurrentPage$.pipe(
60+
switchMap((pageNumber: number) => this.bitstreamDataService.findAllByItemAndBundleName(
61+
this.item,
62+
'ORIGINAL',
63+
{ elementsPerPage: this.pageSize, currentPage: pageNumber },
64+
followLink( 'format')
65+
))
5066
);
51-
const licenses$ = this.bitstreamDataService.findAllByItemAndBundleName(
52-
this.item,
53-
'LICENSE',
54-
{ elementsPerPage: Number.MAX_SAFE_INTEGER },
55-
followLink( 'format')
56-
).pipe(
57-
getFirstSucceededRemoteListPayload(),
58-
startWith([])
59-
);
60-
this.bitstreams$ = observableCombineLatest(originals$, licenses$).pipe(
61-
map(([o, l]) => [...o, ...l]),
62-
map((files: Bitstream[]) =>
63-
files.map(
64-
(original) => {
65-
original.thumbnail = this.bitstreamDataService.getMatchingThumbnail(this.item, original);
66-
return original;
67-
}
68-
)
69-
)
67+
68+
this.licenses$ = this.licenseCurrentPage$.pipe(
69+
switchMap((pageNumber: number) => this.bitstreamDataService.findAllByItemAndBundleName(
70+
this.item,
71+
'LICENSE',
72+
{ elementsPerPage: this.pageSize, currentPage: pageNumber },
73+
followLink( 'format')
74+
))
7075
);
76+
77+
}
78+
79+
/**
80+
* Update the current page for the original bundle bitstreams
81+
* @param page
82+
*/
83+
switchOriginalPage(page: number) {
84+
this.originalOptions.currentPage = page;
85+
this.originalCurrentPage$.next(page);
7186
}
7287

88+
/**
89+
* Update the current page for the license bundle bitstreams
90+
* @param page
91+
*/
92+
switchLicensePage(page: number) {
93+
this.licenseOptions.currentPage = page;
94+
this.licenseCurrentPage$.next(page);
95+
}
7396
}

src/app/+item-page/simple/field-components/file-section/file-section.component.html

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,13 @@
66
<span>({{(file?.sizeBytes) | dsFileSize }})</span>
77
<span *ngIf="!last" innerHTML="{{separator}}"></span>
88
</ds-file-download-link>
9+
<ds-loading *ngIf="isLoading" message="{{'loading.default' | translate}}" [showMessage]="false"></ds-loading>
10+
<div *ngIf="!isLastPage" class="mt-1" id="view-more">
11+
<a class="bitstream-view-more btn btn-outline-secondary btn-sm" [routerLink]="" (click)="getNextPage()">{{'item.page.bitstreams.view-more' | translate}}</a>
12+
</div>
13+
<div *ngIf="isLastPage && currentPage != 1" class="mt-1" id="collapse">
14+
<a class="bitstream-collapse btn btn-outline-secondary btn-sm" [routerLink]="" (click)="currentPage = undefined; getNextPage();">{{'item.page.bitstreams.collapse' | translate}}</a>
15+
</div>
916
</div>
1017
</ds-metadata-field-wrapper>
1118
</ng-container>

0 commit comments

Comments
 (0)