From 30c0bd8ebbf853beb6b1a99e271dfcd98d41e7d3 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Mon, 11 Feb 2019 13:11:47 +0100 Subject: [PATCH 01/36] 59695: Browse by date base --- resources/i18n/en.json | 3 +- .../browse-by-date-page.component.html | 11 ++++++ .../browse-by-date-page.component.scss | 0 .../browse-by-date-page.component.ts | 39 +++++++++++++++++++ .../browse-by-metadata-page.component.ts | 9 ++--- .../+browse-by/browse-by-routing.module.ts | 2 + src/app/+browse-by/browse-by.module.ts | 4 +- .../browse-entry-search-options.model.ts | 1 + src/app/core/browse/browse.service.ts | 3 ++ src/app/core/shared/operators.ts | 2 +- 10 files changed, 66 insertions(+), 8 deletions(-) create mode 100644 src/app/+browse-by/+browse-by-date-page/browse-by-date-page.component.html create mode 100644 src/app/+browse-by/+browse-by-date-page/browse-by-date-page.component.scss create mode 100644 src/app/+browse-by/+browse-by-date-page/browse-by-date-page.component.ts diff --git a/resources/i18n/en.json b/resources/i18n/en.json index 954a8ca087..b4a421baf4 100644 --- a/resources/i18n/en.json +++ b/resources/i18n/en.json @@ -284,7 +284,8 @@ "metadata": { "title": "Title", "author": "Author", - "subject": "Subject" + "subject": "Subject", + "dateissued": "Issue Date" }, "comcol": { "head": "Browse", diff --git a/src/app/+browse-by/+browse-by-date-page/browse-by-date-page.component.html b/src/app/+browse-by/+browse-by-date-page/browse-by-date-page.component.html new file mode 100644 index 0000000000..0ba72e2e14 --- /dev/null +++ b/src/app/+browse-by/+browse-by-date-page/browse-by-date-page.component.html @@ -0,0 +1,11 @@ +
+ +
+ diff --git a/src/app/+browse-by/+browse-by-date-page/browse-by-date-page.component.scss b/src/app/+browse-by/+browse-by-date-page/browse-by-date-page.component.scss new file mode 100644 index 0000000000..e69de29bb2 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 new file mode 100644 index 0000000000..cf3e189a57 --- /dev/null +++ b/src/app/+browse-by/+browse-by-date-page/browse-by-date-page.component.ts @@ -0,0 +1,39 @@ +import { Component } from '@angular/core'; +import { + BrowseByMetadataPageComponent, + browseParamsToOptions +} 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/internal/observable/combineLatest'; + +@Component({ + selector: 'ds-browse-by-date-page', + styleUrls: ['./browse-by-date-page.component.scss'], + templateUrl: './browse-by-date-page.component.html' +}) +/** + * Component for browsing items by metadata definition of type 'date' + * A metadata definition is a short term used to describe one or multiple metadata fields. + * An example would be 'dateissued' for 'dc.date.issued' + */ +export class BrowseByDatePageComponent extends BrowseByMetadataPageComponent { + + ngOnInit(): void { + this.updatePage(new BrowseEntrySearchOptions(null, this.paginationConfig, this.sortConfig)); + this.subs.push( + observableCombineLatest( + this.route.params, + this.route.queryParams, + this.route.data, + (params, queryParams, data ) => { + return Object.assign({}, params, queryParams, data); + }) + .subscribe((params) => { + this.metadata = params.metadata || this.defaultMetadata; + const searchOptions = browseParamsToOptions(params, Object.assign({}), this.sortConfig, this.metadata); + this.updatePageWithItems(searchOptions, this.value); + this.updateParent(params.scope); + })); + } + +} 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 87ccb20c0b..310c5b509e 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 @@ -10,8 +10,6 @@ import { BrowseService } from '../../core/browse/browse.service'; import { BrowseEntry } from '../../core/shared/browse-entry.model'; import { Item } from '../../core/shared/item.model'; import { BrowseEntrySearchOptions } from '../../core/browse/browse-entry-search-options.model'; -import { Community } from '../../core/shared/community.model'; -import { Collection } from '../../core/shared/collection.model'; import { getSucceededRemoteData } from '../../core/shared/operators'; import { DSpaceObjectDataService } from '../../core/data/dspace-object-data.service'; import { DSpaceObject } from '../../core/shared/dspace-object.model'; @@ -79,9 +77,9 @@ export class BrowseByMetadataPageComponent implements OnInit { */ value = ''; - public constructor(private route: ActivatedRoute, - private browseService: BrowseService, - private dsoService: DSpaceObjectDataService) { + public constructor(protected route: ActivatedRoute, + protected browseService: BrowseService, + protected dsoService: DSpaceObjectDataService) { } ngOnInit(): void { @@ -177,6 +175,7 @@ export function browseParamsToOptions(params: any, field: params.sortField || sortConfig.field } ), + +params.startsWith || params.startsWith, params.scope ); } diff --git a/src/app/+browse-by/browse-by-routing.module.ts b/src/app/+browse-by/browse-by-routing.module.ts index 38915fffca..9295209d11 100644 --- a/src/app/+browse-by/browse-by-routing.module.ts +++ b/src/app/+browse-by/browse-by-routing.module.ts @@ -2,11 +2,13 @@ import { RouterModule } from '@angular/router'; import { NgModule } from '@angular/core'; import { BrowseByTitlePageComponent } from './+browse-by-title-page/browse-by-title-page.component'; import { BrowseByMetadataPageComponent } from './+browse-by-metadata-page/browse-by-metadata-page.component'; +import { BrowseByDatePageComponent } from './+browse-by-date-page/browse-by-date-page.component'; @NgModule({ imports: [ RouterModule.forChild([ { path: 'title', component: BrowseByTitlePageComponent }, + { path: 'dateissued', component: BrowseByDatePageComponent, data: { metadata: 'dateissued' } }, { path: ':metadata', component: BrowseByMetadataPageComponent } ]) ] diff --git a/src/app/+browse-by/browse-by.module.ts b/src/app/+browse-by/browse-by.module.ts index 38e5001b80..706d20d12e 100644 --- a/src/app/+browse-by/browse-by.module.ts +++ b/src/app/+browse-by/browse-by.module.ts @@ -6,6 +6,7 @@ import { SharedModule } from '../shared/shared.module'; import { BrowseByRoutingModule } from './browse-by-routing.module'; import { BrowseService } from '../core/browse/browse.service'; import { BrowseByMetadataPageComponent } from './+browse-by-metadata-page/browse-by-metadata-page.component'; +import { BrowseByDatePageComponent } from './+browse-by-date-page/browse-by-date-page.component'; @NgModule({ imports: [ @@ -15,7 +16,8 @@ import { BrowseByMetadataPageComponent } from './+browse-by-metadata-page/browse ], declarations: [ BrowseByTitlePageComponent, - BrowseByMetadataPageComponent + BrowseByMetadataPageComponent, + BrowseByDatePageComponent ], providers: [ ItemDataService, 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 a4911a33f1..417bf7ce75 100644 --- a/src/app/core/browse/browse-entry-search-options.model.ts +++ b/src/app/core/browse/browse-entry-search-options.model.ts @@ -12,6 +12,7 @@ export class BrowseEntrySearchOptions { constructor(public metadataDefinition: string, public pagination?: PaginationComponentOptions, public sort?: SortOptions, + public startsWith?: string, public scope?: string) { } } diff --git a/src/app/core/browse/browse.service.ts b/src/app/core/browse/browse.service.ts index 815570f348..56ef72e6b7 100644 --- a/src/app/core/browse/browse.service.ts +++ b/src/app/core/browse/browse.service.ts @@ -161,6 +161,9 @@ export class BrowseService { args.push(`page=${options.pagination.currentPage - 1}`); args.push(`size=${options.pagination.pageSize}`); } + if (isNotEmpty(options.startsWith)) { + args.push(`startsWith=${options.startsWith}`); + } if (isNotEmpty(filterValue)) { args.push(`filterValue=${filterValue}`); } diff --git a/src/app/core/shared/operators.ts b/src/app/core/shared/operators.ts index 5434a4f04c..a9294b2fc9 100644 --- a/src/app/core/shared/operators.ts +++ b/src/app/core/shared/operators.ts @@ -78,7 +78,7 @@ export const getBrowseDefinitionLinks = (definitionID: string) => source.pipe( getRemoteDataPayload(), map((browseDefinitions: BrowseDefinition[]) => browseDefinitions - .find((def: BrowseDefinition) => def.id === definitionID && def.metadataBrowse === true) + .find((def: BrowseDefinition) => def.id === definitionID) ), map((def: BrowseDefinition) => { if (isNotEmpty(def)) { From 38e9d553888e2c514b71f8c435d240368c0bd2d4 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Mon, 11 Feb 2019 16:00:15 +0100 Subject: [PATCH 02/36] 59695: Arrow navigation for browse-by-date --- .../browse-by-date-page.component.html | 7 +- .../browse-by-date-page.component.ts | 40 +++++++ src/app/core/browse/browse.service.ts | 111 ++++++++++++------ .../shared/browse-by/browse-by.component.html | 36 +++++- .../shared/browse-by/browse-by.component.ts | 40 ++++++- 5 files changed, 187 insertions(+), 47 deletions(-) diff --git a/src/app/+browse-by/+browse-by-date-page/browse-by-date-page.component.html b/src/app/+browse-by/+browse-by-date-page/browse-by-date-page.component.html index 0ba72e2e14..81201f819f 100644 --- a/src/app/+browse-by/+browse-by-date-page/browse-by-date-page.component.html +++ b/src/app/+browse-by/+browse-by-date-page/browse-by-date-page.component.html @@ -4,7 +4,12 @@ title="{{'browse.title' | translate:{collection: (parent$ | async)?.payload?.name || '', field: 'browse.metadata.' + metadata | translate, value: (value)? '"' + value + '"': ''} }}" [objects$]="items$" [paginationConfig]="paginationConfig" - [sortConfig]="sortConfig"> + [sortConfig]="sortConfig" + [enableArrows]="startsWith" + (prev)="goPrev()" + (next)="goNext()" + (pageSizeChange)="pageSizeChange($event)" + (sortDirectionChange)="sortDirectionChange($event)"> 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 cf3e189a57..8066185cc8 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 @@ -5,6 +5,10 @@ import { } 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/internal/observable/combineLatest'; +import { take } from 'rxjs/operators'; +import { ActivatedRoute, Router } from '@angular/router'; +import { BrowseService } from '../../core/browse/browse.service'; +import { DSpaceObjectDataService } from '../../core/data/dspace-object-data.service'; @Component({ selector: 'ds-browse-by-date-page', @@ -18,6 +22,15 @@ import { combineLatest as observableCombineLatest } from 'rxjs/internal/observab */ export class BrowseByDatePageComponent extends BrowseByMetadataPageComponent { + startsWith: string; + + public constructor(protected route: ActivatedRoute, + protected browseService: BrowseService, + protected dsoService: DSpaceObjectDataService, + protected router: Router) { + super(route, browseService, dsoService); + } + ngOnInit(): void { this.updatePage(new BrowseEntrySearchOptions(null, this.paginationConfig, this.sortConfig)); this.subs.push( @@ -30,10 +43,37 @@ export class BrowseByDatePageComponent extends BrowseByMetadataPageComponent { }) .subscribe((params) => { this.metadata = params.metadata || this.defaultMetadata; + this.startsWith = +params.startsWith || params.startsWith; const searchOptions = browseParamsToOptions(params, Object.assign({}), this.sortConfig, this.metadata); this.updatePageWithItems(searchOptions, this.value); this.updateParent(params.scope); })); } + goPrev() { + this.items$.pipe(take(1)).subscribe((items) => { + this.items$ = this.browseService.getPrevBrowseItems(items); + }); + } + + goNext() { + this.items$.pipe(take(1)).subscribe((items) => { + this.items$ = this.browseService.getNextBrowseItems(items); + }); + } + + pageSizeChange(size) { + this.router.navigate([], { + queryParams: Object.assign({ pageSize: size }), + queryParamsHandling: 'merge' + }); + } + + sortDirectionChange(direction) { + this.router.navigate([], { + queryParams: Object.assign({ sortDirection: direction }), + queryParamsHandling: 'merge' + }); + } + } diff --git a/src/app/core/browse/browse.service.ts b/src/app/core/browse/browse.service.ts index 56ef72e6b7..51c59cdf5f 100644 --- a/src/app/core/browse/browse.service.ts +++ b/src/app/core/browse/browse.service.ts @@ -1,6 +1,6 @@ import { Injectable } from '@angular/core'; -import { Observable } from 'rxjs'; -import { distinctUntilChanged, map, startWith } from 'rxjs/operators'; +import { Observable, of as observableOf } from 'rxjs'; +import { distinctUntilChanged, map, startWith, take } from 'rxjs/operators'; import { ensureArrayHasValue, hasValueOperator, @@ -38,6 +38,7 @@ import { URLCombiner } from '../url-combiner/url-combiner'; import { Item } from '../shared/item.model'; import { DSpaceObject } from '../shared/dspace-object.model'; import { BrowseEntrySearchOptions } from './browse-entry-search-options.model'; +import { observable } from 'rxjs/internal-compatibility'; @Injectable() export class BrowseService { @@ -89,7 +90,7 @@ export class BrowseService { } getBrowseEntriesFor(options: BrowseEntrySearchOptions): Observable>> { - const request$ = this.getBrowseDefinitions().pipe( + return this.getBrowseDefinitions().pipe( getBrowseDefinitionLinks(options.metadataDefinition), hasValueOperator(), map((_links: any) => _links.entries), @@ -112,26 +113,8 @@ export class BrowseService { } return href; }), - map((endpointURL: string) => new BrowseEntriesRequest(this.requestService.generateRequestId(), endpointURL)), - configureRequest(this.requestService) + getBrowseEntriesFor(this.requestService, this.responseCache, this.rdb) ); - - const href$ = request$.pipe(map((request: RestRequest) => request.href)); - - const requestEntry$ = href$.pipe(getRequestFromSelflink(this.requestService)); - const responseCache$ = href$.pipe(getResponseFromSelflink(this.responseCache)); - - const payload$ = responseCache$.pipe( - filterSuccessfulResponses(), - map((entry: ResponseCacheEntry) => entry.response), - map((response: GenericSuccessResponse) => new PaginatedList(response.pageInfo, response.payload)), - map((list: PaginatedList) => Object.assign(list, { - page: list.page ? list.page.map((entry: BrowseEntry) => Object.assign(new BrowseEntry(), entry)) : list.page - })), - distinctUntilChanged() - ); - - return this.rdb.toRemoteDataObservable(requestEntry$, responseCache$, payload$); } /** @@ -144,7 +127,7 @@ export class BrowseService { * @returns {Observable>>} */ getBrowseItemsFor(filterValue: string, options: BrowseEntrySearchOptions): Observable>> { - const request$ = this.getBrowseDefinitions().pipe( + return this.getBrowseDefinitions().pipe( getBrowseDefinitionLinks(options.metadataDefinition), hasValueOperator(), map((_links: any) => _links.items), @@ -172,26 +155,20 @@ export class BrowseService { } return href; }), - map((endpointURL: string) => new BrowseItemsRequest(this.requestService.generateRequestId(), endpointURL)), - configureRequest(this.requestService) + getBrowseItemsFor(this.requestService, this.responseCache, this.rdb) ); + } - const href$ = request$.pipe(map((request: RestRequest) => request.href)); - - const requestEntry$ = href$.pipe(getRequestFromSelflink(this.requestService)); - const responseCache$ = href$.pipe(getResponseFromSelflink(this.responseCache)); - - const payload$ = responseCache$.pipe( - filterSuccessfulResponses(), - map((entry: ResponseCacheEntry) => entry.response), - map((response: GenericSuccessResponse) => new PaginatedList(response.pageInfo, response.payload)), - map((list: PaginatedList) => Object.assign(list, { - page: list.page ? list.page.map((item: DSpaceObject) => Object.assign(new Item(), item)) : list.page - })), - distinctUntilChanged() + getPrevBrowseItems(items: RemoteData>): Observable>> { + return observableOf(items.payload.prev).pipe( + getBrowseItemsFor(this.requestService, this.responseCache, this.rdb) ); + } - return this.rdb.toRemoteDataObservable(requestEntry$, responseCache$, payload$); + getNextBrowseItems(items: RemoteData>): Observable>> { + return observableOf(items.payload.next).pipe( + getBrowseItemsFor(this.requestService, this.responseCache, this.rdb) + ); } getBrowseURLFor(metadatumKey: string, linkPath: string): Observable { @@ -217,3 +194,59 @@ export class BrowseService { } } + +export const getBrowseEntriesFor = (requestService: RequestService, responseCache: ResponseCacheService, rdb: RemoteDataBuildService) => + (source: Observable): Observable>> => + source.pipe( + map((href: string) => new BrowseEntriesRequest(requestService.generateRequestId(), href)), + configureRequest(requestService), + toRDPaginatedBrowseEntries(requestService, responseCache, rdb) + ); + +export const getBrowseItemsFor = (requestService: RequestService, responseCache: ResponseCacheService, rdb: RemoteDataBuildService) => + (source: Observable): Observable>> => + source.pipe( + map((href: string) => new BrowseItemsRequest(requestService.generateRequestId(), href)), + configureRequest(requestService), + toRDPaginatedBrowseItems(requestService, responseCache, rdb) + ); + +export const toRDPaginatedBrowseItems = (requestService: RequestService, responseCache: ResponseCacheService, rdb: RemoteDataBuildService) => + (source: Observable): Observable>> => { + const href$ = source.pipe(map((request: RestRequest) => request.href)); + + const requestEntry$ = href$.pipe(getRequestFromSelflink(requestService)); + const responseCache$ = href$.pipe(getResponseFromSelflink(responseCache)); + + const payload$ = responseCache$.pipe( + filterSuccessfulResponses(), + map((entry: ResponseCacheEntry) => entry.response), + map((response: GenericSuccessResponse) => new PaginatedList(response.pageInfo, response.payload)), + map((list: PaginatedList) => Object.assign(list, { + page: list.page ? list.page.map((item: DSpaceObject) => Object.assign(new Item(), item)) : list.page + })), + distinctUntilChanged() + ); + + return rdb.toRemoteDataObservable(requestEntry$, responseCache$, payload$); + }; + +export const toRDPaginatedBrowseEntries = (requestService: RequestService, responseCache: ResponseCacheService, rdb: RemoteDataBuildService) => + (source: Observable): Observable>> => { + const href$ = source.pipe(map((request: RestRequest) => request.href)); + + const requestEntry$ = href$.pipe(getRequestFromSelflink(requestService)); + const responseCache$ = href$.pipe(getResponseFromSelflink(responseCache)); + + const payload$ = responseCache$.pipe( + filterSuccessfulResponses(), + map((entry: ResponseCacheEntry) => entry.response), + map((response: GenericSuccessResponse) => new PaginatedList(response.pageInfo, response.payload)), + map((list: PaginatedList) => Object.assign(list, { + page: list.page ? list.page.map((entry: BrowseEntry) => Object.assign(new BrowseEntry(), entry)) : list.page + })), + distinctUntilChanged() + ); + + return rdb.toRemoteDataObservable(requestEntry$, responseCache$, payload$); + }; diff --git a/src/app/shared/browse-by/browse-by.component.html b/src/app/shared/browse-by/browse-by.component.html index fc3300ae72..42d5f95c9e 100644 --- a/src/app/shared/browse-by/browse-by.component.html +++ b/src/app/shared/browse-by/browse-by.component.html @@ -1,11 +1,37 @@

{{title | translate}}

- - +
+ + +
+
+
+
+
+ +
+ + + + +
+
+
+
+
    +
  • + +
  • +
+
+ + +
+
diff --git a/src/app/shared/browse-by/browse-by.component.ts b/src/app/shared/browse-by/browse-by.component.ts index 2e9e825a6b..233d97a7a4 100644 --- a/src/app/shared/browse-by/browse-by.component.ts +++ b/src/app/shared/browse-by/browse-by.component.ts @@ -1,8 +1,8 @@ -import { Component, Input } from '@angular/core'; +import { Component, EventEmitter, Input, Output } from '@angular/core'; import { RemoteData } from '../../core/data/remote-data'; import { PaginatedList } from '../../core/data/paginated-list'; import { PaginationComponentOptions } from '../pagination/pagination-component-options.model'; -import { SortOptions } from '../../core/cache/models/sort-options.model'; +import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model'; import { fadeIn, fadeInOut } from '../animations/fade'; import { Observable } from 'rxjs'; import { ListableObject } from '../object-collection/shared/listable-object.model'; @@ -39,4 +39,40 @@ export class BrowseByComponent { * The sorting configuration used for the list */ @Input() sortConfig: SortOptions; + + @Input() enableArrows = false; + + @Input() hideGear = false; + + @Output() prev = new EventEmitter(); + + @Output() next = new EventEmitter(); + + @Output() pageSizeChange = new EventEmitter(); + + @Output() sortDirectionChange = new EventEmitter(); + + /** + * Declare SortDirection enumeration to use it in the template + */ + public sortDirections = SortDirection; + + goPrev() { + this.prev.emit(true); + } + + goNext() { + this.next.emit(true); + } + + doPageSizeChange(size) { + this.paginationConfig.pageSize = size; + this.pageSizeChange.emit(size); + } + + doSortDirectionChange(direction) { + this.sortConfig.direction = direction; + this.sortDirectionChange.emit(direction); + } + } From 10f3e985f1f4661a1489777e117ff2f7bb0a1b10 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Mon, 11 Feb 2019 16:06:50 +0100 Subject: [PATCH 03/36] 59695: Gear style fix --- src/app/shared/browse-by/browse-by.component.scss | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/app/shared/browse-by/browse-by.component.scss b/src/app/shared/browse-by/browse-by.component.scss index e69de29bb2..5d847a8609 100644 --- a/src/app/shared/browse-by/browse-by.component.scss +++ b/src/app/shared/browse-by/browse-by.component.scss @@ -0,0 +1,8 @@ +:host { + .dropdown-toggle::after { + display: none; + } + .dropdown-item { + padding-left: 20px; + } +} From 8e9116f14f6987236d944a35056a92f4076e6e7c Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Mon, 11 Feb 2019 16:17:33 +0100 Subject: [PATCH 04/36] 59695: Refactored startsWith to browse-by-metadata-page --- .../browse-by-date-page.component.html | 16 ------- .../browse-by-date-page.component.scss | 0 .../browse-by-date-page.component.ts | 43 +------------------ .../browse-by-metadata-page.component.html | 7 ++- .../browse-by-metadata-page.component.ts | 35 ++++++++++++++- src/app/core/browse/browse.service.ts | 3 ++ 6 files changed, 44 insertions(+), 60 deletions(-) delete mode 100644 src/app/+browse-by/+browse-by-date-page/browse-by-date-page.component.html delete mode 100644 src/app/+browse-by/+browse-by-date-page/browse-by-date-page.component.scss diff --git a/src/app/+browse-by/+browse-by-date-page/browse-by-date-page.component.html b/src/app/+browse-by/+browse-by-date-page/browse-by-date-page.component.html deleted file mode 100644 index 81201f819f..0000000000 --- a/src/app/+browse-by/+browse-by-date-page/browse-by-date-page.component.html +++ /dev/null @@ -1,16 +0,0 @@ -
- -
- diff --git a/src/app/+browse-by/+browse-by-date-page/browse-by-date-page.component.scss b/src/app/+browse-by/+browse-by-date-page/browse-by-date-page.component.scss deleted file mode 100644 index e69de29bb2..0000000000 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 8066185cc8..dd7380f226 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 @@ -5,15 +5,11 @@ import { } 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/internal/observable/combineLatest'; -import { take } from 'rxjs/operators'; -import { ActivatedRoute, Router } from '@angular/router'; -import { BrowseService } from '../../core/browse/browse.service'; -import { DSpaceObjectDataService } from '../../core/data/dspace-object-data.service'; @Component({ selector: 'ds-browse-by-date-page', - styleUrls: ['./browse-by-date-page.component.scss'], - templateUrl: './browse-by-date-page.component.html' + styleUrls: ['../+browse-by-metadata-page/browse-by-metadata-page.component.scss'], + templateUrl: '../+browse-by-metadata-page/browse-by-metadata-page.component.html' }) /** * Component for browsing items by metadata definition of type 'date' @@ -22,15 +18,6 @@ import { DSpaceObjectDataService } from '../../core/data/dspace-object-data.serv */ export class BrowseByDatePageComponent extends BrowseByMetadataPageComponent { - startsWith: string; - - public constructor(protected route: ActivatedRoute, - protected browseService: BrowseService, - protected dsoService: DSpaceObjectDataService, - protected router: Router) { - super(route, browseService, dsoService); - } - ngOnInit(): void { this.updatePage(new BrowseEntrySearchOptions(null, this.paginationConfig, this.sortConfig)); this.subs.push( @@ -50,30 +37,4 @@ export class BrowseByDatePageComponent extends BrowseByMetadataPageComponent { })); } - goPrev() { - this.items$.pipe(take(1)).subscribe((items) => { - this.items$ = this.browseService.getPrevBrowseItems(items); - }); - } - - goNext() { - this.items$.pipe(take(1)).subscribe((items) => { - this.items$ = this.browseService.getNextBrowseItems(items); - }); - } - - pageSizeChange(size) { - this.router.navigate([], { - queryParams: Object.assign({ pageSize: size }), - queryParamsHandling: 'merge' - }); - } - - sortDirectionChange(direction) { - this.router.navigate([], { - queryParams: Object.assign({ sortDirection: direction }), - queryParamsHandling: 'merge' - }); - } - } diff --git a/src/app/+browse-by/+browse-by-metadata-page/browse-by-metadata-page.component.html b/src/app/+browse-by/+browse-by-metadata-page/browse-by-metadata-page.component.html index 08fb762db0..c4ad1037ed 100644 --- a/src/app/+browse-by/+browse-by-metadata-page/browse-by-metadata-page.component.html +++ b/src/app/+browse-by/+browse-by-metadata-page/browse-by-metadata-page.component.html @@ -4,7 +4,12 @@ title="{{'browse.title' | translate:{collection: (parent$ | async)?.payload?.name || '', field: 'browse.metadata.' + metadata | translate, value: (value)? '"' + value + '"': ''} }}" [objects$]="(items$ !== undefined)? items$ : browseEntries$" [paginationConfig]="paginationConfig" - [sortConfig]="sortConfig"> + [sortConfig]="sortConfig" + [enableArrows]="startsWith" + (prev)="goPrev()" + (next)="goNext()" + (pageSizeChange)="pageSizeChange($event)" + (sortDirectionChange)="sortDirectionChange($event)"> 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 310c5b509e..ed7029bd2d 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 @@ -4,7 +4,7 @@ import { RemoteData } from '../../core/data/remote-data'; import { PaginatedList } from '../../core/data/paginated-list'; import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model'; import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model'; -import { ActivatedRoute } from '@angular/router'; +import { ActivatedRoute, Router } from '@angular/router'; import { hasValue, isNotEmpty } from '../../shared/empty.util'; import { BrowseService } from '../../core/browse/browse.service'; import { BrowseEntry } from '../../core/shared/browse-entry.model'; @@ -13,6 +13,7 @@ import { BrowseEntrySearchOptions } from '../../core/browse/browse-entry-search- import { getSucceededRemoteData } from '../../core/shared/operators'; import { DSpaceObjectDataService } from '../../core/data/dspace-object-data.service'; import { DSpaceObject } from '../../core/shared/dspace-object.model'; +import { take } from 'rxjs/operators'; @Component({ selector: 'ds-browse-by-metadata-page', @@ -77,9 +78,12 @@ export class BrowseByMetadataPageComponent implements OnInit { */ value = ''; + startsWith: string; + public constructor(protected route: ActivatedRoute, protected browseService: BrowseService, - protected dsoService: DSpaceObjectDataService) { + protected dsoService: DSpaceObjectDataService, + protected router: Router) { } ngOnInit(): void { @@ -94,6 +98,7 @@ export class BrowseByMetadataPageComponent implements OnInit { .subscribe((params) => { this.metadata = params.metadata || this.defaultMetadata; this.value = +params.value || params.value || ''; + this.startsWith = +params.startsWith || params.startsWith; const searchOptions = browseParamsToOptions(params, this.paginationConfig, this.sortConfig, this.metadata); if (isNotEmpty(this.value)) { this.updatePageWithItems(searchOptions, this.value); @@ -142,6 +147,32 @@ export class BrowseByMetadataPageComponent implements OnInit { } } + goPrev() { + this.items$.pipe(take(1)).subscribe((items) => { + this.items$ = this.browseService.getPrevBrowseItems(items); + }); + } + + goNext() { + this.items$.pipe(take(1)).subscribe((items) => { + this.items$ = this.browseService.getNextBrowseItems(items); + }); + } + + pageSizeChange(size) { + this.router.navigate([], { + queryParams: Object.assign({ pageSize: size }), + queryParamsHandling: 'merge' + }); + } + + sortDirectionChange(direction) { + this.router.navigate([], { + queryParams: Object.assign({ sortDirection: direction }), + queryParamsHandling: 'merge' + }); + } + ngOnDestroy(): void { this.subs.filter((sub) => hasValue(sub)).forEach((sub) => sub.unsubscribe()); } diff --git a/src/app/core/browse/browse.service.ts b/src/app/core/browse/browse.service.ts index 51c59cdf5f..94c13c7d05 100644 --- a/src/app/core/browse/browse.service.ts +++ b/src/app/core/browse/browse.service.ts @@ -108,6 +108,9 @@ export class BrowseService { args.push(`page=${options.pagination.currentPage - 1}`); args.push(`size=${options.pagination.pageSize}`); } + if (isNotEmpty(options.startsWith)) { + args.push(`startsWith=${options.startsWith}`); + } if (isNotEmpty(args)) { href = new URLCombiner(href, `?${args.join('&')}`).toString(); } From b956dbfe08b2a2c9e41bbab4df7213c42fc9fed4 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Mon, 11 Feb 2019 17:30:05 +0100 Subject: [PATCH 05/36] 59695: Browse-By-Starts-With components and switcher --- .../browse-by-date-page.component.ts | 2 ++ .../browse-by-metadata-page.component.html | 2 ++ .../browse-by-metadata-page.component.ts | 5 +++ ...rowse-by-starts-with-abstract.component.ts | 6 ++++ .../browse-by-starts-with-decorator.ts | 16 ++++++++++ .../browse-by-starts-with-date.component.html | 4 +++ .../browse-by-starts-with-date.component.scss | 0 .../browse-by-starts-with-date.component.ts | 14 +++++++++ .../browse-by-starts-with-text.component.html | 4 +++ .../browse-by-starts-with-text.component.scss | 0 .../browse-by-starts-with-text.component.ts | 14 +++++++++ .../shared/browse-by/browse-by.component.html | 1 + .../shared/browse-by/browse-by.component.ts | 31 +++++++++++++++++-- src/app/shared/shared.module.ts | 6 +++- 14 files changed, 102 insertions(+), 3 deletions(-) create mode 100644 src/app/shared/browse-by/browse-by-starts-with/browse-by-starts-with-abstract.component.ts create mode 100644 src/app/shared/browse-by/browse-by-starts-with/browse-by-starts-with-decorator.ts create mode 100644 src/app/shared/browse-by/browse-by-starts-with/date/browse-by-starts-with-date.component.html create mode 100644 src/app/shared/browse-by/browse-by-starts-with/date/browse-by-starts-with-date.component.scss create mode 100644 src/app/shared/browse-by/browse-by-starts-with/date/browse-by-starts-with-date.component.ts create mode 100644 src/app/shared/browse-by/browse-by-starts-with/text/browse-by-starts-with-text.component.html create mode 100644 src/app/shared/browse-by/browse-by-starts-with/text/browse-by-starts-with-text.component.scss create mode 100644 src/app/shared/browse-by/browse-by-starts-with/text/browse-by-starts-with-text.component.ts 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 dd7380f226..3856d45da0 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 @@ -5,6 +5,7 @@ import { } 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/internal/observable/combineLatest'; +import { BrowseByStartsWithType } from '../../shared/browse-by/browse-by.component'; @Component({ selector: 'ds-browse-by-date-page', @@ -19,6 +20,7 @@ import { combineLatest as observableCombineLatest } from 'rxjs/internal/observab export class BrowseByDatePageComponent extends BrowseByMetadataPageComponent { ngOnInit(): void { + this.startsWithType = BrowseByStartsWithType.date; this.updatePage(new BrowseEntrySearchOptions(null, this.paginationConfig, this.sortConfig)); this.subs.push( observableCombineLatest( diff --git a/src/app/+browse-by/+browse-by-metadata-page/browse-by-metadata-page.component.html b/src/app/+browse-by/+browse-by-metadata-page/browse-by-metadata-page.component.html index c4ad1037ed..1cf5c51d7c 100644 --- a/src/app/+browse-by/+browse-by-metadata-page/browse-by-metadata-page.component.html +++ b/src/app/+browse-by/+browse-by-metadata-page/browse-by-metadata-page.component.html @@ -5,6 +5,8 @@ [objects$]="(items$ !== undefined)? items$ : browseEntries$" [paginationConfig]="paginationConfig" [sortConfig]="sortConfig" + [type]="startsWithType" + [startsWithOptions]="startsWithOptions" [enableArrows]="startsWith" (prev)="goPrev()" (next)="goNext()" 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 ed7029bd2d..7b4e34cf6d 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 @@ -14,6 +14,7 @@ import { getSucceededRemoteData } from '../../core/shared/operators'; import { DSpaceObjectDataService } from '../../core/data/dspace-object-data.service'; import { DSpaceObject } from '../../core/shared/dspace-object.model'; import { take } from 'rxjs/operators'; +import { BrowseByStartsWithType } from '../../shared/browse-by/browse-by.component'; @Component({ selector: 'ds-browse-by-metadata-page', @@ -71,6 +72,10 @@ export class BrowseByMetadataPageComponent implements OnInit { */ metadata = this.defaultMetadata; + startsWithType = BrowseByStartsWithType.text; + + startsWithOptions = []; + /** * The value we're browing items for * - When the value is not empty, we're browsing items diff --git a/src/app/shared/browse-by/browse-by-starts-with/browse-by-starts-with-abstract.component.ts b/src/app/shared/browse-by/browse-by-starts-with/browse-by-starts-with-abstract.component.ts new file mode 100644 index 0000000000..c8651dba21 --- /dev/null +++ b/src/app/shared/browse-by/browse-by-starts-with/browse-by-starts-with-abstract.component.ts @@ -0,0 +1,6 @@ +import { Inject } from '@angular/core'; + +export class BrowseByStartsWithAbstractComponent { + public constructor(@Inject('startsWithOptions') public startsWithOptions: any[]) { + } +} diff --git a/src/app/shared/browse-by/browse-by-starts-with/browse-by-starts-with-decorator.ts b/src/app/shared/browse-by/browse-by-starts-with/browse-by-starts-with-decorator.ts new file mode 100644 index 0000000000..eb16254100 --- /dev/null +++ b/src/app/shared/browse-by/browse-by-starts-with/browse-by-starts-with-decorator.ts @@ -0,0 +1,16 @@ +import { BrowseByStartsWithType } from '../browse-by.component'; + +const startsWithMap = new Map(); + +export function renderStartsWithFor(type: BrowseByStartsWithType) { + return function decorator(objectElement: any) { + if (!objectElement) { + return; + } + startsWithMap.set(type, objectElement); + }; +} + +export function getStartsWithComponent(type: BrowseByStartsWithType) { + return startsWithMap.get(type); +} diff --git a/src/app/shared/browse-by/browse-by-starts-with/date/browse-by-starts-with-date.component.html b/src/app/shared/browse-by/browse-by-starts-with/date/browse-by-starts-with-date.component.html new file mode 100644 index 0000000000..c2221ed257 --- /dev/null +++ b/src/app/shared/browse-by/browse-by-starts-with/date/browse-by-starts-with-date.component.html @@ -0,0 +1,4 @@ +

Test Starts-With Date

+

+ {{element}}, +

diff --git a/src/app/shared/browse-by/browse-by-starts-with/date/browse-by-starts-with-date.component.scss b/src/app/shared/browse-by/browse-by-starts-with/date/browse-by-starts-with-date.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/app/shared/browse-by/browse-by-starts-with/date/browse-by-starts-with-date.component.ts b/src/app/shared/browse-by/browse-by-starts-with/date/browse-by-starts-with-date.component.ts new file mode 100644 index 0000000000..b527ef77fb --- /dev/null +++ b/src/app/shared/browse-by/browse-by-starts-with/date/browse-by-starts-with-date.component.ts @@ -0,0 +1,14 @@ +import { Component } from '@angular/core'; +import { renderStartsWithFor } from '../browse-by-starts-with-decorator'; +import { BrowseByStartsWithType } from '../../browse-by.component'; +import { BrowseByStartsWithAbstractComponent } from '../browse-by-starts-with-abstract.component'; + +@Component({ + selector: 'ds-browse-by-starts-with-date', + styleUrls: ['./browse-by-starts-with-date.component.scss'], + templateUrl: './browse-by-starts-with-date.component.html' +}) +@renderStartsWithFor(BrowseByStartsWithType.date) +export class BrowseByStartsWithDateComponent extends BrowseByStartsWithAbstractComponent { + +} diff --git a/src/app/shared/browse-by/browse-by-starts-with/text/browse-by-starts-with-text.component.html b/src/app/shared/browse-by/browse-by-starts-with/text/browse-by-starts-with-text.component.html new file mode 100644 index 0000000000..b2144ed9d3 --- /dev/null +++ b/src/app/shared/browse-by/browse-by-starts-with/text/browse-by-starts-with-text.component.html @@ -0,0 +1,4 @@ +

Test Starts-With Text

+

+ {{element}}, +

diff --git a/src/app/shared/browse-by/browse-by-starts-with/text/browse-by-starts-with-text.component.scss b/src/app/shared/browse-by/browse-by-starts-with/text/browse-by-starts-with-text.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/app/shared/browse-by/browse-by-starts-with/text/browse-by-starts-with-text.component.ts b/src/app/shared/browse-by/browse-by-starts-with/text/browse-by-starts-with-text.component.ts new file mode 100644 index 0000000000..db0fac33fd --- /dev/null +++ b/src/app/shared/browse-by/browse-by-starts-with/text/browse-by-starts-with-text.component.ts @@ -0,0 +1,14 @@ +import { Component, Inject } from '@angular/core'; +import { renderStartsWithFor } from '../browse-by-starts-with-decorator'; +import { BrowseByStartsWithType } from '../../browse-by.component'; +import { BrowseByStartsWithAbstractComponent } from '../browse-by-starts-with-abstract.component'; + +@Component({ + selector: 'ds-browse-by-starts-with-text', + styleUrls: ['./browse-by-starts-with-text.component.scss'], + templateUrl: './browse-by-starts-with-text.component.html' +}) +@renderStartsWithFor(BrowseByStartsWithType.text) +export class BrowseByStartsWithTextComponent extends BrowseByStartsWithAbstractComponent { + +} diff --git a/src/app/shared/browse-by/browse-by.component.html b/src/app/shared/browse-by/browse-by.component.html index 42d5f95c9e..a047363194 100644 --- a/src/app/shared/browse-by/browse-by.component.html +++ b/src/app/shared/browse-by/browse-by.component.html @@ -1,5 +1,6 @@

{{title | translate}}

+
(); + objectInjector: Injector; + /** * Declare SortDirection enumeration to use it in the template */ public sortDirections = SortDirection; + public constructor(private injector: Injector) { + + } + goPrev() { this.prev.emit(true); } @@ -75,4 +91,15 @@ export class BrowseByComponent { this.sortDirectionChange.emit(direction); } + getStartsWithComponent() { + return getStartsWithComponent(this.type); + } + + ngOnInit(): void { + this.objectInjector = Injector.create({ + providers: [{ provide: 'startsWithOptions', useFactory: () => (this.startsWithOptions), deps:[] }], + parent: this.injector + }); + } + } diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index 5a7541018b..b8530101f9 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -88,6 +88,8 @@ import { MomentModule } from 'ngx-moment'; import { MenuModule } from './menu/menu.module'; import {LangSwitchComponent} from './lang-switch/lang-switch.component'; import { ComcolPageBrowseByComponent } from './comcol-page-browse-by/comcol-page-browse-by.component'; +import { BrowseByStartsWithDateComponent } from './browse-by/browse-by-starts-with/date/browse-by-starts-with-date.component'; +import { BrowseByStartsWithTextComponent } from './browse-by/browse-by-starts-with/text/browse-by-starts-with-text.component'; const MODULES = [ // Do NOT include UniversalModule, HttpModule, or JsonpModule here @@ -177,7 +179,9 @@ const ENTRY_COMPONENTS = [ CollectionGridElementComponent, CommunityGridElementComponent, SearchResultGridElementComponent, - BrowseEntryListElementComponent + BrowseEntryListElementComponent, + BrowseByStartsWithDateComponent, + BrowseByStartsWithTextComponent ]; const PROVIDERS = [ From d80233074f90c877cce4ce54abdd2912c9eccfe1 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Mon, 11 Feb 2019 17:55:27 +0100 Subject: [PATCH 06/36] 59695: Intermediate date jump options test --- .../browse-by-date-page.component.ts | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) 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 3856d45da0..0d07bcc073 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 @@ -19,6 +19,9 @@ import { BrowseByStartsWithType } from '../../shared/browse-by/browse-by.compone */ export class BrowseByDatePageComponent extends BrowseByMetadataPageComponent { + oneYearLimit = 10; + fiveYearLimit = 30; + ngOnInit(): void { this.startsWithType = BrowseByStartsWithType.date; this.updatePage(new BrowseEntrySearchOptions(null, this.paginationConfig, this.sortConfig)); @@ -37,6 +40,23 @@ export class BrowseByDatePageComponent extends BrowseByMetadataPageComponent { this.updatePageWithItems(searchOptions, this.value); this.updateParent(params.scope); })); + const options = []; + const currentYear = new Date().getFullYear(); + const oneYearBreak = Math.floor((currentYear - this.oneYearLimit) / 5) * 5; + const fiveYearBreak = Math.floor((currentYear - this.fiveYearLimit) / 10) * 10; + const lowerLimit = 1900; // Hardcoded atm, this should be the lowest date issued + let i = currentYear; + while (i > lowerLimit) { + options.push(i); + if (i <= fiveYearBreak) { + i -= 10; + } else if (i <= oneYearBreak) { + i -= 5; + } else { + i--; + } + } + console.log(options); } } From cdcacedfaecb911530a84477ce7a010864e7e250 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Tue, 12 Feb 2019 10:40:47 +0100 Subject: [PATCH 07/36] 59695: Starts-with implementation for browse-by date --- config/environment.default.js | 11 ++- resources/i18n/en.json | 5 ++ .../browse-by-date-page.component.ts | 79 ++++++++++++++----- .../+browse-by/browse-by-routing.module.ts | 2 +- src/app/core/browse/browse.service.ts | 24 +++++- ...rowse-by-starts-with-abstract.component.ts | 56 ++++++++++++- .../browse-by-starts-with-date.component.html | 26 +++++- .../browse-by-starts-with-date.component.scss | 7 ++ .../browse-by-starts-with-text.component.html | 5 +- src/config/browse-by-config.interface.ts | 7 ++ src/config/global-config.interface.ts | 2 + 11 files changed, 190 insertions(+), 34 deletions(-) create mode 100644 src/config/browse-by-config.interface.ts diff --git a/config/environment.default.js b/config/environment.default.js index d3758e66bd..b99f027c08 100644 --- a/config/environment.default.js +++ b/config/environment.default.js @@ -72,5 +72,14 @@ module.exports = { code: 'nl', label: 'Nederlands', active: false, - }] + }], + // Browse-By Pages + browseBy: { + // Amount of years to display using jumps of one year (current year - oneYearLimit) + oneYearLimit: 10, + // Limit for years to display using jumps of five years (current year - fiveYearLimit) + 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 + } }; diff --git a/resources/i18n/en.json b/resources/i18n/en.json index b4a421baf4..a357a9c622 100644 --- a/resources/i18n/en.json +++ b/resources/i18n/en.json @@ -281,6 +281,11 @@ }, "browse": { "title": "Browsing {{ collection }} by {{ field }} {{ value }}", + "startsWith": { + "choose_year": "(Choose year)", + "type_year": "Or type in a year:", + "submit": "Go" + }, "metadata": { "title": "Title", "author": "Author", 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 0d07bcc073..9ae8ea56df 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,4 +1,4 @@ -import { Component } from '@angular/core'; +import { Component, Inject } from '@angular/core'; import { BrowseByMetadataPageComponent, browseParamsToOptions @@ -6,6 +6,14 @@ import { import { BrowseEntrySearchOptions } from '../../core/browse/browse-entry-search-options.model'; import { combineLatest as observableCombineLatest } from 'rxjs/internal/observable/combineLatest'; import { BrowseByStartsWithType } from '../../shared/browse-by/browse-by.component'; +import { RemoteData } from '../../core/data/remote-data'; +import { PaginatedList } from '../../core/data/paginated-list'; +import { Item } from '../../core/shared/item.model'; +import { hasValue, isNotEmpty } from '../../shared/empty.util'; +import { ActivatedRoute, Router } from '@angular/router'; +import { BrowseService } from '../../core/browse/browse.service'; +import { DSpaceObjectDataService } from '../../core/data/dspace-object-data.service'; +import { GLOBAL_CONFIG, GlobalConfig } from '../../../config'; @Component({ selector: 'ds-browse-by-date-page', @@ -19,8 +27,15 @@ import { BrowseByStartsWithType } from '../../shared/browse-by/browse-by.compone */ export class BrowseByDatePageComponent extends BrowseByMetadataPageComponent { - oneYearLimit = 10; - fiveYearLimit = 30; + defaultMetadataField = 'dc.date.issued'; + + public constructor(@Inject(GLOBAL_CONFIG) public config: GlobalConfig, + protected route: ActivatedRoute, + protected browseService: BrowseService, + protected dsoService: DSpaceObjectDataService, + protected router: Router) { + super(route, browseService, dsoService, router); + } ngOnInit(): void { this.startsWithType = BrowseByStartsWithType.date; @@ -34,29 +49,53 @@ export class BrowseByDatePageComponent extends BrowseByMetadataPageComponent { return Object.assign({}, params, queryParams, data); }) .subscribe((params) => { + const metadataField = params.metadataField || this.defaultMetadataField; this.metadata = params.metadata || this.defaultMetadata; this.startsWith = +params.startsWith || params.startsWith; const searchOptions = browseParamsToOptions(params, Object.assign({}), this.sortConfig, this.metadata); this.updatePageWithItems(searchOptions, this.value); this.updateParent(params.scope); + this.updateStartsWithOptions(this.metadata, metadataField, params.scope); })); - const options = []; - const currentYear = new Date().getFullYear(); - const oneYearBreak = Math.floor((currentYear - this.oneYearLimit) / 5) * 5; - const fiveYearBreak = Math.floor((currentYear - this.fiveYearLimit) / 10) * 10; - const lowerLimit = 1900; // Hardcoded atm, this should be the lowest date issued - let i = currentYear; - while (i > lowerLimit) { - options.push(i); - if (i <= fiveYearBreak) { - i -= 10; - } else if (i <= oneYearBreak) { - i -= 5; - } else { - i--; - } - } - console.log(options); + } + + updateStartsWithOptions(definition: string, metadataField: string, scope?: string) { + this.subs.push( + this.browseService.getFirstItemFor(definition, scope).subscribe((firstItemRD: RemoteData>) => { + let lowerLimit = this.config.browseBy.defaultLowerLimit; + if (firstItemRD.payload.page.length > 0) { + const date = firstItemRD.payload.page[0].findMetadata(metadataField); + if (hasValue(date) && hasValue(+date.split('-')[0])) { + lowerLimit = +date.split('-')[0]; + } + } + const options = []; + const currentYear = new Date().getFullYear(); + const oneYearBreak = Math.floor((currentYear - this.config.browseBy.oneYearLimit) / 5) * 5; + const fiveYearBreak = Math.floor((currentYear - this.config.browseBy.fiveYearLimit) / 10) * 10; + if (lowerLimit <= fiveYearBreak) { + lowerLimit -= 10; + } else if (lowerLimit <= oneYearBreak) { + lowerLimit -= 5; + } else { + lowerLimit -= 1; + } + let i = currentYear; + while (i > lowerLimit) { + options.push(i); + if (i <= fiveYearBreak) { + i -= 10; + } else if (i <= oneYearBreak) { + i -= 5; + } else { + i--; + } + } + if (isNotEmpty(options)) { + this.startsWithOptions = options; + } + }) + ); } } diff --git a/src/app/+browse-by/browse-by-routing.module.ts b/src/app/+browse-by/browse-by-routing.module.ts index 9295209d11..f4c8b688f6 100644 --- a/src/app/+browse-by/browse-by-routing.module.ts +++ b/src/app/+browse-by/browse-by-routing.module.ts @@ -8,7 +8,7 @@ import { BrowseByDatePageComponent } from './+browse-by-date-page/browse-by-date imports: [ RouterModule.forChild([ { path: 'title', component: BrowseByTitlePageComponent }, - { path: 'dateissued', component: BrowseByDatePageComponent, data: { metadata: 'dateissued' } }, + { path: 'dateissued', component: BrowseByDatePageComponent, data: { metadata: 'dateissued', metadataField: 'dc.date.issued' } }, { path: ':metadata', component: BrowseByMetadataPageComponent } ]) ] diff --git a/src/app/core/browse/browse.service.ts b/src/app/core/browse/browse.service.ts index 94c13c7d05..5388721218 100644 --- a/src/app/core/browse/browse.service.ts +++ b/src/app/core/browse/browse.service.ts @@ -2,7 +2,7 @@ import { Injectable } from '@angular/core'; import { Observable, of as observableOf } from 'rxjs'; import { distinctUntilChanged, map, startWith, take } from 'rxjs/operators'; import { - ensureArrayHasValue, + ensureArrayHasValue, hasValue, hasValueOperator, isEmpty, isNotEmpty, @@ -162,6 +162,28 @@ export class BrowseService { ); } + getFirstItemFor(definition: string, scope?: string): Observable>> { + return this.getBrowseDefinitions().pipe( + getBrowseDefinitionLinks(definition), + hasValueOperator(), + map((_links: any) => _links.items), + hasValueOperator(), + map((href: string) => { + const args = []; + if (hasValue(scope)) { + args.push(`scope=${scope}`); + } + args.push('page=0'); + args.push('size=1'); + if (isNotEmpty(args)) { + href = new URLCombiner(href, `?${args.join('&')}`).toString(); + } + return href; + }), + getBrowseItemsFor(this.requestService, this.responseCache, this.rdb) + ); + } + getPrevBrowseItems(items: RemoteData>): Observable>> { return observableOf(items.payload.prev).pipe( getBrowseItemsFor(this.requestService, this.responseCache, this.rdb) diff --git a/src/app/shared/browse-by/browse-by-starts-with/browse-by-starts-with-abstract.component.ts b/src/app/shared/browse-by/browse-by-starts-with/browse-by-starts-with-abstract.component.ts index c8651dba21..ff113f1773 100644 --- a/src/app/shared/browse-by/browse-by-starts-with/browse-by-starts-with-abstract.component.ts +++ b/src/app/shared/browse-by/browse-by-starts-with/browse-by-starts-with-abstract.component.ts @@ -1,6 +1,56 @@ -import { Inject } from '@angular/core'; +import { Inject, OnDestroy, OnInit } from '@angular/core'; +import { ActivatedRoute, Router } from '@angular/router'; +import { hasValue } from '../../empty.util'; +import { Subscription } from 'rxjs/internal/Subscription'; +import { FormControl, FormGroup } from '@angular/forms'; -export class BrowseByStartsWithAbstractComponent { - public constructor(@Inject('startsWithOptions') public startsWithOptions: any[]) { +export class BrowseByStartsWithAbstractComponent implements OnInit, OnDestroy { + startsWith: string; + + formData: FormGroup; + + /** + * List of subscriptions + */ + subs: Subscription[] = []; + + public constructor(@Inject('startsWithOptions') public startsWithOptions: any[], + protected route: ActivatedRoute, + protected router: Router) { + } + + ngOnInit(): void { + this.subs.push( + this.route.queryParams.subscribe((params) => { + this.startsWith = params.startsWith; + }) + ); + this.formData = new FormGroup({ + startsWith: new FormControl() + }); + } + + setStartsWith(event: Event) { + this.startsWith = (event.target as HTMLInputElement).value; + this.setStartsWithParam(); + } + + setStartsWithParam() { + if (this.startsWith === '-1') { + this.startsWith = undefined; + } + this.router.navigate([], { + queryParams: Object.assign({ startsWith: this.startsWith }), + queryParamsHandling: 'merge' + }); + } + + submitForm(data) { + this.startsWith = data.startsWith; + this.setStartsWithParam(); + } + + ngOnDestroy(): void { + this.subs.filter((sub) => hasValue(sub)).forEach((sub) => sub.unsubscribe()); } } diff --git a/src/app/shared/browse-by/browse-by-starts-with/date/browse-by-starts-with-date.component.html b/src/app/shared/browse-by/browse-by-starts-with/date/browse-by-starts-with-date.component.html index c2221ed257..768d6c4049 100644 --- a/src/app/shared/browse-by/browse-by-starts-with/date/browse-by-starts-with-date.component.html +++ b/src/app/shared/browse-by/browse-by-starts-with/date/browse-by-starts-with-date.component.html @@ -1,4 +1,22 @@ -

Test Starts-With Date

-

- {{element}}, -

+
+
+
+ +
+ + + + +
+
+
+
diff --git a/src/app/shared/browse-by/browse-by-starts-with/date/browse-by-starts-with-date.component.scss b/src/app/shared/browse-by/browse-by-starts-with/date/browse-by-starts-with-date.component.scss index e69de29bb2..e516151d57 100644 --- a/src/app/shared/browse-by/browse-by-starts-with/date/browse-by-starts-with-date.component.scss +++ b/src/app/shared/browse-by/browse-by-starts-with/date/browse-by-starts-with-date.component.scss @@ -0,0 +1,7 @@ +@import '../../../../../styles/variables.scss'; + +// temporary fix for bootstrap 4 beta btn color issue +.btn-secondary { + background-color: $input-bg; + color: $input-color; +} diff --git a/src/app/shared/browse-by/browse-by-starts-with/text/browse-by-starts-with-text.component.html b/src/app/shared/browse-by/browse-by-starts-with/text/browse-by-starts-with-text.component.html index b2144ed9d3..7a1b58e832 100644 --- a/src/app/shared/browse-by/browse-by-starts-with/text/browse-by-starts-with-text.component.html +++ b/src/app/shared/browse-by/browse-by-starts-with/text/browse-by-starts-with-text.component.html @@ -1,4 +1 @@ -

Test Starts-With Text

-

- {{element}}, -

+ diff --git a/src/config/browse-by-config.interface.ts b/src/config/browse-by-config.interface.ts new file mode 100644 index 0000000000..1bc804c3b8 --- /dev/null +++ b/src/config/browse-by-config.interface.ts @@ -0,0 +1,7 @@ +import { Config } from './config.interface'; + +export interface BrowseByConfig extends Config { + oneYearLimit: number; + fiveYearLimit: number; + defaultLowerLimit: number; +} diff --git a/src/config/global-config.interface.ts b/src/config/global-config.interface.ts index 6d1d4da6f9..38532941a8 100644 --- a/src/config/global-config.interface.ts +++ b/src/config/global-config.interface.ts @@ -5,6 +5,7 @@ import { UniversalConfig } from './universal-config.interface'; import { INotificationBoardOptions } from './notifications-config.interfaces'; import { FormConfig } from './form-config.interfaces'; import {LangConfig} from './lang-config.interface'; +import { BrowseByConfig } from './browse-by-config.interface'; export interface GlobalConfig extends Config { ui: ServerConfig; @@ -19,4 +20,5 @@ export interface GlobalConfig extends Config { debug: boolean; defaultLanguage: string; languages: LangConfig[]; + browseBy: BrowseByConfig; } From c49f9f2ef6b0f6317281190101b7a61440a031f1 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Tue, 12 Feb 2019 11:27:24 +0100 Subject: [PATCH 08/36] 59695: Browse-by-date style finetuning + navigation + parsing service fix for empty pages --- resources/i18n/en.json | 2 ++ .../browse-items-response-parsing-service.ts | 4 +++- src/app/navbar/navbar.component.ts | 11 ++++++++++ .../browse-by-starts-with-date.component.html | 21 +++++++++++-------- .../comcol-page-browse-by.component.html | 1 + 5 files changed, 29 insertions(+), 10 deletions(-) diff --git a/resources/i18n/en.json b/resources/i18n/en.json index a357a9c622..031a30945a 100644 --- a/resources/i18n/en.json +++ b/resources/i18n/en.json @@ -282,6 +282,7 @@ "browse": { "title": "Browsing {{ collection }} by {{ field }} {{ value }}", "startsWith": { + "jump": "Jump to a point in the index:", "choose_year": "(Choose year)", "type_year": "Or type in a year:", "submit": "Go" @@ -296,6 +297,7 @@ "head": "Browse", "by": { "title": "By Title", + "dateissued": "By Issue Date", "author": "By Author", "subject": "By Subject" } diff --git a/src/app/core/data/browse-items-response-parsing-service.ts b/src/app/core/data/browse-items-response-parsing-service.ts index e513ad0898..9228204b32 100644 --- a/src/app/core/data/browse-items-response-parsing-service.ts +++ b/src/app/core/data/browse-items-response-parsing-service.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@angular/core'; import { GLOBAL_CONFIG } from '../../../config'; import { GlobalConfig } from '../../../config/global-config.interface'; -import { isNotEmpty } from '../../shared/empty.util'; +import { hasValue, isNotEmpty } from '../../shared/empty.util'; import { ObjectCacheService } from '../cache/object-cache.service'; import { ErrorResponse, @@ -45,6 +45,8 @@ export class BrowseItemsResponseParsingService extends BaseResponseParsingServic const serializer = new DSpaceRESTv2Serializer(DSpaceObject); const items = serializer.deserializeArray(data.payload._embedded[Object.keys(data.payload._embedded)[0]]); return new GenericSuccessResponse(items, data.statusCode, this.processPageInfo(data.payload)); + } else if (hasValue(data.payload) && hasValue(data.payload.page) && data.payload.page.totalElements === 0) { + return new GenericSuccessResponse([], data.statusCode, this.processPageInfo(data.payload)); } else { return new ErrorResponse( Object.assign( diff --git a/src/app/navbar/navbar.component.ts b/src/app/navbar/navbar.component.ts index df6226aedc..008a86599d 100644 --- a/src/app/navbar/navbar.component.ts +++ b/src/app/navbar/navbar.component.ts @@ -73,6 +73,17 @@ export class NavbarComponent extends MenuComponent implements OnInit { link: '/browse/title' } as LinkMenuItemModel, }, + { + id: 'browse_global_global_by_issue_date', + parentID: 'browse_global', + active: false, + visible: true, + model: { + type: MenuItemType.LINK, + text: 'menu.section.browse_global_by_issue_date', + link: '/browse/dateissued' + } as LinkMenuItemModel, + }, { id: 'browse_global_by_author', parentID: 'browse_global', diff --git a/src/app/shared/browse-by/browse-by-starts-with/date/browse-by-starts-with-date.component.html b/src/app/shared/browse-by/browse-by-starts-with/date/browse-by-starts-with-date.component.html index 768d6c4049..43e53ea54d 100644 --- a/src/app/shared/browse-by/browse-by-starts-with/date/browse-by-starts-with-date.component.html +++ b/src/app/shared/browse-by/browse-by-starts-with/date/browse-by-starts-with-date.component.html @@ -1,7 +1,10 @@
-
-
- @@ -11,12 +14,12 @@ {{option}} -
- - - - -
+
+
+ + + +
diff --git a/src/app/shared/comcol-page-browse-by/comcol-page-browse-by.component.html b/src/app/shared/comcol-page-browse-by/comcol-page-browse-by.component.html index 653bd1ed53..f9ef4e5232 100644 --- a/src/app/shared/comcol-page-browse-by/comcol-page-browse-by.component.html +++ b/src/app/shared/comcol-page-browse-by/comcol-page-browse-by.component.html @@ -1,6 +1,7 @@

{{'browse.comcol.head' | translate}}

From b1b239d45142701212f2a8db28352446bbae7fc1 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Tue, 12 Feb 2019 11:50:35 +0100 Subject: [PATCH 09/36] 59695: Wait for startsWithOptions to prevent empty dropdown + loading message --- resources/i18n/en.json | 3 ++- .../+browse-by-date-page/browse-by-date-page.component.ts | 6 ++++-- .../browse-by-metadata-page.component.html | 3 ++- .../browse-by-metadata-page.component.ts | 3 ++- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/resources/i18n/en.json b/resources/i18n/en.json index 031a30945a..a9a456df33 100644 --- a/resources/i18n/en.json +++ b/resources/i18n/en.json @@ -444,7 +444,8 @@ "item": "Loading item...", "objects": "Loading...", "search-results": "Loading search results...", - "browse-by": "Loading items..." + "browse-by": "Loading items...", + "browse-by-page": "Loading page..." }, "error": { "default": "Error", 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 9ae8ea56df..f40683f387 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,4 +1,4 @@ -import { Component, Inject } from '@angular/core'; +import { ChangeDetectorRef, Component, Inject } from '@angular/core'; import { BrowseByMetadataPageComponent, browseParamsToOptions @@ -33,7 +33,8 @@ export class BrowseByDatePageComponent extends BrowseByMetadataPageComponent { protected route: ActivatedRoute, protected browseService: BrowseService, protected dsoService: DSpaceObjectDataService, - protected router: Router) { + protected router: Router, + protected cdRef: ChangeDetectorRef) { super(route, browseService, dsoService, router); } @@ -93,6 +94,7 @@ export class BrowseByDatePageComponent extends BrowseByMetadataPageComponent { } if (isNotEmpty(options)) { this.startsWithOptions = options; + this.cdRef.detectChanges(); } }) ); diff --git a/src/app/+browse-by/+browse-by-metadata-page/browse-by-metadata-page.component.html b/src/app/+browse-by/+browse-by-metadata-page/browse-by-metadata-page.component.html index 1cf5c51d7c..9ccc466d90 100644 --- a/src/app/+browse-by/+browse-by-metadata-page/browse-by-metadata-page.component.html +++ b/src/app/+browse-by/+browse-by-metadata-page/browse-by-metadata-page.component.html @@ -1,6 +1,6 @@
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 7b4e34cf6d..f2e25d84d9 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 @@ -74,7 +74,7 @@ export class BrowseByMetadataPageComponent implements OnInit { startsWithType = BrowseByStartsWithType.text; - startsWithOptions = []; + startsWithOptions; /** * The value we're browing items for @@ -112,6 +112,7 @@ export class BrowseByMetadataPageComponent implements OnInit { } this.updateParent(params.scope); })); + this.startsWithOptions = []; } /** From bd211ce0f4b79ee85f5d1ba7caad9dfb7bfaf02e Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Tue, 12 Feb 2019 12:53:23 +0100 Subject: [PATCH 10/36] 59695: Browse-By-Title-Page refactoring --- .../browse-by-title-page.component.ts | 88 +++++-------------- .../+browse-by/browse-by-routing.module.ts | 2 +- 2 files changed, 25 insertions(+), 65 deletions(-) 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 6ba43c8f10..a7a35f49cc 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 @@ -1,80 +1,52 @@ -import { combineLatest as observableCombineLatest, merge as observableMerge, Observable, Subscription } from 'rxjs'; -import { Component, OnInit } from '@angular/core'; -import { RemoteData } from '../../core/data/remote-data'; -import { PaginatedList } from '../../core/data/paginated-list'; +import { combineLatest as observableCombineLatest } from 'rxjs'; +import { Component } from '@angular/core'; import { ItemDataService } from '../../core/data/item-data.service'; -import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model'; -import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model'; -import { Item } from '../../core/shared/item.model'; -import { ActivatedRoute } from '@angular/router'; +import { ActivatedRoute, Router } from '@angular/router'; import { hasValue } from '../../shared/empty.util'; -import { Collection } from '../../core/shared/collection.model'; -import { browseParamsToOptions } from '../+browse-by-metadata-page/browse-by-metadata-page.component'; +import { + BrowseByMetadataPageComponent, + browseParamsToOptions +} from '../+browse-by-metadata-page/browse-by-metadata-page.component'; import { BrowseEntrySearchOptions } from '../../core/browse/browse-entry-search-options.model'; -import { Community } from '../../core/shared/community.model'; -import { getSucceededRemoteData } from '../../core/shared/operators'; import { DSpaceObjectDataService } from '../../core/data/dspace-object-data.service'; -import { DSpaceObject } from '../../core/shared/dspace-object.model'; +import { BrowseService } from '../../core/browse/browse.service'; +import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model'; @Component({ selector: 'ds-browse-by-title-page', - styleUrls: ['./browse-by-title-page.component.scss'], - templateUrl: './browse-by-title-page.component.html' + styleUrls: ['../+browse-by-metadata-page/browse-by-metadata-page.component.scss'], + templateUrl: '../+browse-by-metadata-page/browse-by-metadata-page.component.html' }) /** * Component for browsing items by title (dc.title) */ -export class BrowseByTitlePageComponent implements OnInit { - - /** - * The list of items to display - */ - items$: Observable>>; - - /** - * The current Community or Collection we're browsing metadata/items in - */ - parent$: Observable>; - - /** - * The pagination configuration to use for displaying the list of items - */ - paginationConfig: PaginationComponentOptions = Object.assign(new PaginationComponentOptions(), { - id: 'browse-by-title-pagination', - currentPage: 1, - pageSize: 20 - }); - - /** - * The sorting configuration to use for displaying the list of items - * Sorted by title (Ascending by default) - */ - sortConfig: SortOptions = new SortOptions('dc.title', SortDirection.ASC); - - /** - * List of subscriptions - */ - subs: Subscription[] = []; - - public constructor(private itemDataService: ItemDataService, - private route: ActivatedRoute, - private dsoService: DSpaceObjectDataService) { +export class BrowseByTitlePageComponent extends BrowseByMetadataPageComponent { + public constructor(protected route: ActivatedRoute, + protected browseService: BrowseService, + protected dsoService: DSpaceObjectDataService, + protected router: Router, + protected itemDataService: ItemDataService) { + super(route, browseService, dsoService, router); } ngOnInit(): void { + this.sortConfig = new SortOptions('dc.title', SortDirection.ASC); this.updatePage(new BrowseEntrySearchOptions(null, this.paginationConfig, this.sortConfig)); this.subs.push( observableCombineLatest( this.route.params, this.route.queryParams, - (params, queryParams, ) => { - return Object.assign({}, params, queryParams); + this.route.data, + (params, queryParams, data ) => { + return Object.assign({}, params, queryParams, data); }) .subscribe((params) => { + this.metadata = params.metadata || this.defaultMetadata; this.updatePage(browseParamsToOptions(params, this.paginationConfig, this.sortConfig)); this.updateParent(params.scope) })); + this.startsWithOptions = []; } /** @@ -92,18 +64,6 @@ export class BrowseByTitlePageComponent implements OnInit { }); } - /** - * Update the parent Community or Collection using their scope - * @param scope The UUID of the Community or Collection to fetch - */ - updateParent(scope: string) { - if (hasValue(scope)) { - this.parent$ = this.dsoService.findById(scope).pipe( - getSucceededRemoteData() - ); - } - } - ngOnDestroy(): void { this.subs.filter((sub) => hasValue(sub)).forEach((sub) => sub.unsubscribe()); } diff --git a/src/app/+browse-by/browse-by-routing.module.ts b/src/app/+browse-by/browse-by-routing.module.ts index f4c8b688f6..51acd19645 100644 --- a/src/app/+browse-by/browse-by-routing.module.ts +++ b/src/app/+browse-by/browse-by-routing.module.ts @@ -7,7 +7,7 @@ import { BrowseByDatePageComponent } from './+browse-by-date-page/browse-by-date @NgModule({ imports: [ RouterModule.forChild([ - { path: 'title', component: BrowseByTitlePageComponent }, + { path: 'title', component: BrowseByTitlePageComponent, data: { metadata: 'title' } }, { path: 'dateissued', component: BrowseByDatePageComponent, data: { metadata: 'dateissued', metadataField: 'dc.date.issued' } }, { path: ':metadata', component: BrowseByMetadataPageComponent } ]) From 86dc17e17d13ba9982aa457ada4b08f22b166396 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Tue, 12 Feb 2019 13:00:10 +0100 Subject: [PATCH 11/36] 59695: Removal of unnecessary files after refactor --- .../browse-by-title-page.component.html | 10 ---------- .../browse-by-title-page.component.scss | 0 2 files changed, 10 deletions(-) delete mode 100644 src/app/+browse-by/+browse-by-title-page/browse-by-title-page.component.html delete mode 100644 src/app/+browse-by/+browse-by-title-page/browse-by-title-page.component.scss diff --git a/src/app/+browse-by/+browse-by-title-page/browse-by-title-page.component.html b/src/app/+browse-by/+browse-by-title-page/browse-by-title-page.component.html deleted file mode 100644 index 84b0baf1f6..0000000000 --- a/src/app/+browse-by/+browse-by-title-page/browse-by-title-page.component.html +++ /dev/null @@ -1,10 +0,0 @@ -
-
- - -
-
diff --git a/src/app/+browse-by/+browse-by-title-page/browse-by-title-page.component.scss b/src/app/+browse-by/+browse-by-title-page/browse-by-title-page.component.scss deleted file mode 100644 index e69de29bb2..0000000000 From c15a7fd4bac0aaaf29eee8fd6c31a9e00bf571ef Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Tue, 12 Feb 2019 13:29:31 +0100 Subject: [PATCH 12/36] 59695: Fix existing tests --- .../browse-by-metadata-page.component.spec.ts | 4 +++- .../browse-by-title-page.component.spec.ts | 10 +++++++--- src/app/shared/browse-by/browse-by.component.spec.ts | 10 +++++++++- 3 files changed, 19 insertions(+), 5 deletions(-) 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 87f9aa498d..7b22f6afe1 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 @@ -19,6 +19,7 @@ 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'; +import { MockRouter } from '../../shared/mocks/mock-router'; describe('BrowseByMetadataPageComponent', () => { let comp: BrowseByMetadataPageComponent; @@ -81,7 +82,8 @@ describe('BrowseByMetadataPageComponent', () => { providers: [ { provide: ActivatedRoute, useValue: activatedRouteStub }, { provide: BrowseService, useValue: mockBrowseService }, - { provide: DSpaceObjectDataService, useValue: mockDsoService } + { provide: DSpaceObjectDataService, useValue: mockDsoService }, + { provide: Router, useValue: new MockRouter() } ], schemas: [NO_ERRORS_SCHEMA] }).compileComponents(); 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 f99c2b0113..d7bc7397b8 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 @@ -1,5 +1,5 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; -import { ActivatedRoute } from '@angular/router'; +import { ActivatedRoute, Router } from '@angular/router'; import { Item } from '../../core/shared/item.model'; import { ActivatedRouteStub } from '../../shared/testing/active-router-stub'; import { of as observableOf } from 'rxjs/internal/observable/of'; @@ -15,6 +15,8 @@ import { ItemDataService } from '../../core/data/item-data.service'; import { Community } from '../../core/shared/community.model'; import { RemoteData } from '../../core/data/remote-data'; import { DSpaceObjectDataService } from '../../core/data/dspace-object-data.service'; +import { BrowseService } from '../../core/browse/browse.service'; +import { MockRouter } from '../../shared/mocks/mock-router'; describe('BrowseByTitlePageComponent', () => { let comp: BrowseByTitlePageComponent; @@ -57,8 +59,10 @@ describe('BrowseByTitlePageComponent', () => { declarations: [BrowseByTitlePageComponent, EnumKeysPipe], providers: [ { provide: ActivatedRoute, useValue: activatedRouteStub }, - { provide: ItemDataService, useValue: mockItemDataService }, - { provide: DSpaceObjectDataService, useValue: mockDsoService } + { provide: BrowseService, useValue: {} }, + { provide: DSpaceObjectDataService, useValue: mockDsoService }, + { provide: Router, useValue: new MockRouter() }, + { provide: ItemDataService, useValue: mockItemDataService } ], schemas: [NO_ERRORS_SCHEMA] }).compileComponents(); 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 2417dde7ca..83c07daf34 100644 --- a/src/app/shared/browse-by/browse-by.component.spec.ts +++ b/src/app/shared/browse-by/browse-by.component.spec.ts @@ -5,6 +5,10 @@ import { By } from '@angular/platform-browser'; import { NO_ERRORS_SCHEMA } from '@angular/core'; import { of as observableOf } from 'rxjs'; import { SharedModule } from '../shared.module'; +import { CommonModule } from '@angular/common'; +import { ActivatedRoute, Router } from '@angular/router'; +import { ActivatedRouteStub } from '../testing/active-router-stub'; +import { MockRouter } from '../mocks/mock-router'; describe('BrowseByComponent', () => { let comp: BrowseByComponent; @@ -12,8 +16,12 @@ describe('BrowseByComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - imports: [TranslateModule.forRoot(), SharedModule], + imports: [CommonModule, TranslateModule.forRoot(), SharedModule], declarations: [], + providers: [ + { provide: ActivatedRoute, useValue: new ActivatedRouteStub() }, + { provide: Router, useValue: new MockRouter() } + ], schemas: [NO_ERRORS_SCHEMA] }).compileComponents(); })); From 9f01361e091e02f682738c8d3870e98995fafb2a Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Tue, 12 Feb 2019 14:43:32 +0100 Subject: [PATCH 13/36] 59695: BrowseByDatePage tests --- .../browse-by-date-page.component.spec.ts | 95 +++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 src/app/+browse-by/+browse-by-date-page/browse-by-date-page.component.spec.ts 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 new file mode 100644 index 0000000000..77f41be76a --- /dev/null +++ b/src/app/+browse-by/+browse-by-date-page/browse-by-date-page.component.spec.ts @@ -0,0 +1,95 @@ +import { BrowseByDatePageComponent } from './browse-by-date-page.component'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { CommonModule } from '@angular/common'; +import { RouterTestingModule } from '@angular/router/testing'; +import { TranslateModule } from '@ngx-translate/core'; +import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; +import { EnumKeysPipe } from '../../shared/utils/enum-keys-pipe'; +import { ActivatedRoute, Router } from '@angular/router'; +import { BrowseService } from '../../core/browse/browse.service'; +import { DSpaceObjectDataService } from '../../core/data/dspace-object-data.service'; +import { MockRouter } from '../../shared/mocks/mock-router'; +import { ChangeDetectorRef, NO_ERRORS_SCHEMA } from '@angular/core'; +import { of as observableOf } from 'rxjs/internal/observable/of'; +import { RemoteData } from '../../core/data/remote-data'; +import { ActivatedRouteStub } from '../../shared/testing/active-router-stub'; +import { Community } from '../../core/shared/community.model'; +import { Item } from '../../core/shared/item.model'; +import { ENV_CONFIG, GLOBAL_CONFIG } from '../../../config'; +import { BrowseEntrySearchOptions } from '../../core/browse/browse-entry-search-options.model'; +import { toRemoteData } from '../+browse-by-metadata-page/browse-by-metadata-page.component.spec'; + +describe('BrowseByDatePageComponent', () => { + let comp: BrowseByDatePageComponent; + let fixture: ComponentFixture; + let route: ActivatedRoute; + + const mockCommunity = Object.assign(new Community(), { + id: 'test-uuid', + name: 'test community' + }); + + const firstItem = Object.assign(new Item(), { + id: 'first-item-id', + metadata: [ + { key: 'dc.date.issued', value: '1950-01-01' } + ] + }); + + const mockBrowseService = { + getBrowseEntriesFor: (options: BrowseEntrySearchOptions) => toRemoteData([]), + getBrowseItemsFor: (value: string, options: BrowseEntrySearchOptions) => toRemoteData([firstItem]), + getFirstItemFor: () => toRemoteData([firstItem]) + }; + + const mockDsoService = { + findById: () => observableOf(new RemoteData(false, false, true, null, mockCommunity)) + }; + + const activatedRouteStub = Object.assign(new ActivatedRouteStub(), { + params: observableOf({}), + queryParams: observableOf({}), + data: observableOf({ metadata: 'dateissued', metadataField: 'dc.date.issued' }) + }); + + const mockCdRef = Object.assign({ + detectChanges: () => fixture.detectChanges() + }); + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [CommonModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule.forRoot()], + declarations: [BrowseByDatePageComponent, EnumKeysPipe], + providers: [ + { provide: GLOBAL_CONFIG, useValue: ENV_CONFIG }, + { provide: ActivatedRoute, useValue: activatedRouteStub }, + { provide: BrowseService, useValue: mockBrowseService }, + { provide: DSpaceObjectDataService, useValue: mockDsoService }, + { provide: Router, useValue: new MockRouter() }, + { provide: ChangeDetectorRef, useValue: mockCdRef } + ], + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(BrowseByDatePageComponent); + comp = fixture.componentInstance; + fixture.detectChanges(); + route = (comp as any).route; + }); + + it('should initialize the list of items', () => { + comp.items$.subscribe((result) => { + expect(result.payload.page).toEqual([firstItem]); + }); + }); + + it('should create a list of startsWith options with the earliest year at the end (rounded down by 10)', () => { + expect(comp.startsWithOptions[comp.startsWithOptions.length - 1]).toEqual(1950); + }); + + it('should create a list of startsWith options with the current year first', () => { + expect(comp.startsWithOptions[0]).toEqual(new Date().getFullYear()); + }); +}); From 51242ecd1c6513f81c039df1477f6af079f4a6ea Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Tue, 12 Feb 2019 14:51:47 +0100 Subject: [PATCH 14/36] 59695: BrowseByStartsWithDecorator test --- .../browse-by-starts-with-decorator.spec.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 src/app/shared/browse-by/browse-by-starts-with/browse-by-starts-with-decorator.spec.ts diff --git a/src/app/shared/browse-by/browse-by-starts-with/browse-by-starts-with-decorator.spec.ts b/src/app/shared/browse-by/browse-by-starts-with/browse-by-starts-with-decorator.spec.ts new file mode 100644 index 0000000000..8eaa9eee09 --- /dev/null +++ b/src/app/shared/browse-by/browse-by-starts-with/browse-by-starts-with-decorator.spec.ts @@ -0,0 +1,14 @@ +import { renderStartsWithFor } from './browse-by-starts-with-decorator'; +import { BrowseByStartsWithType } from '../browse-by.component'; + +describe('BrowseByStartsWithDecorator', () => { + const textDecorator = renderStartsWithFor(BrowseByStartsWithType.text); + const dateDecorator = renderStartsWithFor(BrowseByStartsWithType.date); + it('should have a decorator for both text and date', () => { + expect(textDecorator.length).not.toBeNull(); + expect(dateDecorator.length).not.toBeNull(); + }); + it('should have 2 separate decorators for text and date', () => { + expect(textDecorator).not.toEqual(dateDecorator); + }); +}); From e9bff44ba2ac9ed9b73e4455e68b986310644fed Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Tue, 12 Feb 2019 16:08:04 +0100 Subject: [PATCH 15/36] 59695: BrowseByStartsWithDate tests --- ...owse-by-starts-with-date.component.spec.ts | 128 ++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/app/shared/browse-by/browse-by-starts-with/date/browse-by-starts-with-date.component.spec.ts diff --git a/src/app/shared/browse-by/browse-by-starts-with/date/browse-by-starts-with-date.component.spec.ts b/src/app/shared/browse-by/browse-by-starts-with/date/browse-by-starts-with-date.component.spec.ts new file mode 100644 index 0000000000..c0812245e9 --- /dev/null +++ b/src/app/shared/browse-by/browse-by-starts-with/date/browse-by-starts-with-date.component.spec.ts @@ -0,0 +1,128 @@ +import { BrowseByStartsWithDateComponent } from './browse-by-starts-with-date.component'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { CommonModule } from '@angular/common'; +import { RouterTestingModule } from '@angular/router/testing'; +import { TranslateModule } from '@ngx-translate/core'; +import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; +import { EnumKeysPipe } from '../../../utils/enum-keys-pipe'; +import { ActivatedRoute, Router } from '@angular/router'; +import { DebugElement, NO_ERRORS_SCHEMA } from '@angular/core'; +import { ActivatedRouteStub } from '../../../testing/active-router-stub'; +import { of as observableOf } from 'rxjs/internal/observable/of'; +import { RouterStub } from '../../../testing/router-stub'; +import { By } from '@angular/platform-browser'; + +describe('BrowseByStartsWithDateComponent', () => { + let comp: BrowseByStartsWithDateComponent; + let fixture: ComponentFixture; + let route: ActivatedRoute; + let router: Router; + + const options = [2019, 2018, 2017, 2016, 2015]; + + const activatedRouteStub = Object.assign(new ActivatedRouteStub(), { + params: observableOf({}), + queryParams: observableOf({}) + }); + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [CommonModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule.forRoot()], + declarations: [BrowseByStartsWithDateComponent, EnumKeysPipe], + providers: [ + { provide: 'startsWithOptions', useValue: options }, + { provide: ActivatedRoute, useValue: activatedRouteStub }, + { provide: Router, useValue: new RouterStub() } + ], + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(BrowseByStartsWithDateComponent); + comp = fixture.componentInstance; + fixture.detectChanges(); + route = (comp as any).route; + router = (comp as any).router; + }); + + it('should create a FormGroup containing a startsWith FormControl', () => { + expect(comp.formData.value.startsWith).toBeDefined(); + }); + + describe('when selecting the first option in the dropdown', () => { + let select; + + beforeEach(() => { + select = fixture.debugElement.query(By.css('select')).nativeElement; + select.value = select.options[0].value; + select.dispatchEvent(new Event('change')); + fixture.detectChanges(); + }); + + it('should set startsWith to undefined', () => { + expect(comp.startsWith).toBeUndefined(); + }); + + it('should not add a startsWith query parameter', () => { + route.queryParams.subscribe((params) => { + expect(params.startsWith).toBeUndefined(); + }); + }); + }); + + describe('when selecting the second option in the dropdown', () => { + let select; + let input; + const expectedValue = '' + options[0]; + const extras = { + queryParams: Object.assign({ startsWith: expectedValue }), + queryParamsHandling: 'merge' + }; + + beforeEach(() => { + select = fixture.debugElement.query(By.css('select')).nativeElement; + input = fixture.debugElement.query(By.css('input')).nativeElement; + select.value = select.options[1].value; + select.dispatchEvent(new Event('change')); + fixture.detectChanges(); + }); + + it('should set startsWith to the correct value', () => { + expect(comp.startsWith).toEqual(expectedValue); + }); + + it('should add a startsWith query parameter', () => { + expect(router.navigate).toHaveBeenCalledWith([], extras); + }); + + it('should automatically fill in the input field', () => { + expect(input.value).toEqual(expectedValue); + }); + }); + + describe('when filling in the input form', () => { + let form; + const expectedValue = '2015'; + const extras = { + queryParams: Object.assign({ startsWith: expectedValue }), + queryParamsHandling: 'merge' + }; + + beforeEach(() => { + form = fixture.debugElement.query(By.css('form')); + comp.formData.value.startsWith = expectedValue; + form.triggerEventHandler('ngSubmit', null); + fixture.detectChanges(); + }); + + it('should set startsWith to the correct value', () => { + expect(comp.startsWith).toEqual(expectedValue); + }); + + it('should add a startsWith query parameter', () => { + expect(router.navigate).toHaveBeenCalledWith([], extras); + }); + }); + +}); From 0bdd4789f6fbee5701051a2c56ec3ea951eac210 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Tue, 12 Feb 2019 16:36:07 +0100 Subject: [PATCH 16/36] 59695: Added BrowseService tests --- src/app/core/browse/browse.service.spec.ts | 35 ++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/app/core/browse/browse.service.spec.ts b/src/app/core/browse/browse.service.spec.ts index 79df1ea88c..d17c9541f6 100644 --- a/src/app/core/browse/browse.service.spec.ts +++ b/src/app/core/browse/browse.service.spec.ts @@ -294,4 +294,39 @@ describe('BrowseService', () => { }); }); }); + + describe('getFirstItemFor', () => { + beforeEach(() => { + responseCache = initMockResponseCacheService(true); + requestService = getMockRequestService(); + rdbService = getMockRemoteDataBuildService(); + service = initTestService(); + spyOn(service, 'getBrowseDefinitions').and + .returnValue(hot('--a-', { a: { + payload: browseDefinitions + }})); + spyOn(rdbService, 'toRemoteDataObservable').and.callThrough(); + }); + + describe('when getFirstItemFor is called with a valid browse definition id', () => { + const expectedURL = browseDefinitions[1]._links.items + '?page=0&size=1'; + + it('should configure a new BrowseItemsRequest', () => { + const expected = new BrowseItemsRequest(requestService.generateRequestId(), expectedURL); + + scheduler.schedule(() => service.getFirstItemFor(browseDefinitions[1].id).subscribe()); + scheduler.flush(); + + expect(requestService.configure).toHaveBeenCalledWith(expected); + }); + + it('should call RemoteDataBuildService to create the RemoteData Observable', () => { + service.getFirstItemFor(browseDefinitions[1].id); + + expect(rdbService.toRemoteDataObservable).toHaveBeenCalled(); + }); + + }); + }); + }); From a1bed63442c7097d454885f31d82a0d689b1b914 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Wed, 13 Feb 2019 09:33:51 +0100 Subject: [PATCH 17/36] 59695: Added BrowseBy tests --- .../shared/browse-by/browse-by.component.html | 8 +- .../browse-by/browse-by.component.spec.ts | 122 ++++++++++++++++-- 2 files changed, 117 insertions(+), 13 deletions(-) diff --git a/src/app/shared/browse-by/browse-by.component.html b/src/app/shared/browse-by/browse-by.component.html index a047363194..bad9f3fe8c 100644 --- a/src/app/shared/browse-by/browse-by.component.html +++ b/src/app/shared/browse-by/browse-by.component.html @@ -16,9 +16,9 @@
- + - +
@@ -29,8 +29,8 @@
- - + +
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 83c07daf34..bae345d009 100644 --- a/src/app/shared/browse-by/browse-by.component.spec.ts +++ b/src/app/shared/browse-by/browse-by.component.spec.ts @@ -1,27 +1,68 @@ import { BrowseByComponent } from './browse-by.component'; import { async, ComponentFixture, TestBed } from '@angular/core/testing'; -import { TranslateModule } from '@ngx-translate/core'; +import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; import { By } from '@angular/platform-browser'; import { NO_ERRORS_SCHEMA } from '@angular/core'; import { of as observableOf } from 'rxjs'; import { SharedModule } from '../shared.module'; import { CommonModule } from '@angular/common'; -import { ActivatedRoute, Router } from '@angular/router'; -import { ActivatedRouteStub } from '../testing/active-router-stub'; -import { MockRouter } from '../mocks/mock-router'; +import { Item } from '../../core/shared/item.model'; +import { RemoteData } from '../../core/data/remote-data'; +import { PaginatedList } from '../../core/data/paginated-list'; +import { PageInfo } from '../../core/shared/page-info.model'; +import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; +import { StoreModule } from '@ngrx/store'; +import { MockTranslateLoader } from '../mocks/mock-translate-loader'; +import { RouterTestingModule } from '@angular/router/testing'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { PaginationComponentOptions } from '../pagination/pagination-component-options.model'; +import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model'; describe('BrowseByComponent', () => { let comp: BrowseByComponent; let fixture: ComponentFixture; + const mockItems = [ + Object.assign(new Item(), { + id: 'fakeId-1', + metadata: [ + { + key: 'dc.title', + value: 'First Fake Title' + } + ] + }), + Object.assign(new Item(), { + id: 'fakeId-2', + metadata: [ + { + key: 'dc.title', + value: 'Second Fake Title' + } + ] + }) + ]; + const mockItemsRD$ = observableOf(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), mockItems))); + beforeEach(async(() => { TestBed.configureTestingModule({ - imports: [CommonModule, TranslateModule.forRoot(), SharedModule], - declarations: [], - providers: [ - { provide: ActivatedRoute, useValue: new ActivatedRouteStub() }, - { provide: Router, useValue: new MockRouter() } + imports: [ + CommonModule, + TranslateModule.forRoot(), + SharedModule, + NgbModule.forRoot(), + StoreModule.forRoot({}), + TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: MockTranslateLoader + } + }), + RouterTestingModule, + BrowserAnimationsModule ], + declarations: [], + providers: [], schemas: [NO_ERRORS_SCHEMA] }).compileComponents(); })); @@ -49,4 +90,67 @@ describe('BrowseByComponent', () => { expect(fixture.debugElement.query(By.css('ds-viewable-collection'))).toBeDefined(); }); + describe('when enableArrows is true and objects are defined', () => { + beforeEach(() => { + comp.enableArrows = true; + comp.objects$ = mockItemsRD$; + comp.paginationConfig = Object.assign(new PaginationComponentOptions(), { + id: 'test-pagination', + currentPage: 1, + pageSizeOptions: [5,10,15,20], + pageSize: 15 + }); + comp.sortConfig = Object.assign(new SortOptions('dc.title', SortDirection.ASC)); + fixture.detectChanges(); + }); + + describe('when clicking the previous arrow button', () => { + beforeEach(() => { + spyOn(comp.prev, 'emit'); + fixture.debugElement.query(By.css('#nav-prev')).triggerEventHandler('click', null); + fixture.detectChanges(); + }); + + it('should emit a signal to the EventEmitter',() => { + expect(comp.prev.emit).toHaveBeenCalled(); + }); + }); + + describe('when clicking the next arrow button', () => { + beforeEach(() => { + spyOn(comp.next, 'emit'); + fixture.debugElement.query(By.css('#nav-next')).triggerEventHandler('click', null); + fixture.detectChanges(); + }); + + it('should emit a signal to the EventEmitter',() => { + expect(comp.next.emit).toHaveBeenCalled(); + }); + }); + + describe('when clicking a different page size', () => { + beforeEach(() => { + spyOn(comp.pageSizeChange, 'emit'); + fixture.debugElement.query(By.css('.page-size-change')).triggerEventHandler('click', null); + fixture.detectChanges(); + }); + + it('should emit a signal to the EventEmitter',() => { + expect(comp.pageSizeChange.emit).toHaveBeenCalled(); + }); + }); + + describe('when clicking a different sort direction', () => { + beforeEach(() => { + spyOn(comp.sortDirectionChange, 'emit'); + fixture.debugElement.query(By.css('.sort-direction-change')).triggerEventHandler('click', null); + fixture.detectChanges(); + }); + + it('should emit a signal to the EventEmitter',() => { + expect(comp.sortDirectionChange.emit).toHaveBeenCalled(); + }); + }); + }); + }); From 40342af0295cd406e16f4b9e5498ed566c4455b1 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Wed, 13 Feb 2019 10:13:18 +0100 Subject: [PATCH 18/36] 59695: JSDocs --- .../browse-by-date-page.component.ts | 13 ++++ .../browse-by-metadata-page.component.ts | 25 ++++++++ src/app/core/browse/browse.service.ts | 61 ++++++++++++++++--- ...rowse-by-starts-with-abstract.component.ts | 20 ++++++ .../browse-by-starts-with-decorator.ts | 8 +++ .../browse-by-starts-with-date.component.ts | 4 ++ .../browse-by-starts-with-text.component.ts | 3 + .../shared/browse-by/browse-by.component.ts | 48 +++++++++++++++ 8 files changed, 174 insertions(+), 8 deletions(-) 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 f40683f387..701082b6a2 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 @@ -27,6 +27,9 @@ import { GLOBAL_CONFIG, GlobalConfig } from '../../../config'; */ export class BrowseByDatePageComponent extends BrowseByMetadataPageComponent { + /** + * The default metadata-field to use for determining the lower limit of the StartsWith dropdown options + */ defaultMetadataField = 'dc.date.issued'; public constructor(@Inject(GLOBAL_CONFIG) public config: GlobalConfig, @@ -60,6 +63,16 @@ export class BrowseByDatePageComponent extends BrowseByMetadataPageComponent { })); } + /** + * Update the StartsWith options + * In this implementation, it creates a list of years starting from now, going all the way back to the earliest + * date found on an item within this scope. The further back in time, the bigger the change in years become to avoid + * extremely long lists with a one-year difference. + * To determine the change in years, the config found under GlobalConfig.BrowseBy is used for this. + * @param definition The metadata definition to fetch the first item for + * @param metadataField The metadata field to fetch the earliest date from (expects a date field) + * @param scope The scope under which to fetch the earliest item for + */ updateStartsWithOptions(definition: string, metadataField: string, scope?: string) { this.subs.push( this.browseService.getFirstItemFor(definition, scope).subscribe((firstItemRD: RemoteData>) => { 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 f2e25d84d9..a8bd7086a4 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 @@ -72,8 +72,16 @@ export class BrowseByMetadataPageComponent implements OnInit { */ metadata = this.defaultMetadata; + /** + * The type of StartsWith options to render + * Defaults to text + */ startsWithType = BrowseByStartsWithType.text; + /** + * The list of StartsWith options + * Should be defined after ngOnInit is called! + */ startsWithOptions; /** @@ -83,6 +91,9 @@ export class BrowseByMetadataPageComponent implements OnInit { */ value = ''; + /** + * The current startsWith option (fetched and updated from query-params) + */ startsWith: string; public constructor(protected route: ActivatedRoute, @@ -153,18 +164,28 @@ export class BrowseByMetadataPageComponent implements OnInit { } } + /** + * Navigate to the previous page + */ goPrev() { this.items$.pipe(take(1)).subscribe((items) => { this.items$ = this.browseService.getPrevBrowseItems(items); }); } + /** + * Navigate to the next page + */ goNext() { this.items$.pipe(take(1)).subscribe((items) => { this.items$ = this.browseService.getNextBrowseItems(items); }); } + /** + * Change the page size + * @param size + */ pageSizeChange(size) { this.router.navigate([], { queryParams: Object.assign({ pageSize: size }), @@ -172,6 +193,10 @@ export class BrowseByMetadataPageComponent implements OnInit { }); } + /** + * Change the sorting direction + * @param direction + */ sortDirectionChange(direction) { this.router.navigate([], { queryParams: Object.assign({ sortDirection: direction }), diff --git a/src/app/core/browse/browse.service.ts b/src/app/core/browse/browse.service.ts index 5388721218..40c51130d6 100644 --- a/src/app/core/browse/browse.service.ts +++ b/src/app/core/browse/browse.service.ts @@ -8,9 +8,7 @@ import { isNotEmpty, isNotEmptyOperator } from '../../shared/empty.util'; -import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; -import { SortOptions } from '../cache/models/sort-options.model'; import { GenericSuccessResponse } from '../cache/response-cache.models'; import { ResponseCacheEntry } from '../cache/response-cache.reducer'; import { ResponseCacheService } from '../cache/response-cache.service'; @@ -20,7 +18,6 @@ import { BrowseEndpointRequest, BrowseEntriesRequest, BrowseItemsRequest, - GetRequest, RestRequest } from '../data/request.models'; import { RequestService } from '../data/request.service'; @@ -38,8 +35,10 @@ import { URLCombiner } from '../url-combiner/url-combiner'; import { Item } from '../shared/item.model'; import { DSpaceObject } from '../shared/dspace-object.model'; import { BrowseEntrySearchOptions } from './browse-entry-search-options.model'; -import { observable } from 'rxjs/internal-compatibility'; +/** + * The service handling all browse requests + */ @Injectable() export class BrowseService { protected linkPath = 'browses'; @@ -65,6 +64,9 @@ export class BrowseService { ) { } + /** + * Get all BrowseDefinitions + */ getBrowseDefinitions(): Observable> { const request$ = this.halService.getEndpoint(this.linkPath).pipe( isNotEmptyOperator(), @@ -89,6 +91,10 @@ export class BrowseService { return this.rdb.toRemoteDataObservable(requestEntry$, responseCache$, payload$); } + /** + * Get all BrowseEntries filtered or modified by BrowseEntrySearchOptions + * @param options + */ getBrowseEntriesFor(options: BrowseEntrySearchOptions): Observable>> { return this.getBrowseDefinitions().pipe( getBrowseDefinitionLinks(options.metadataDefinition), @@ -122,11 +128,8 @@ export class BrowseService { /** * Get all items linked to a certain metadata value - * @param {string} definitionID definition ID to define the metadata-field (e.g. author) * @param {string} filterValue metadata value to filter by (e.g. author's name) - * @param options Options to narrow down your search: - * { pagination: PaginationComponentOptions, - * sort: SortOptions } + * @param options Options to narrow down your search * @returns {Observable>>} */ getBrowseItemsFor(filterValue: string, options: BrowseEntrySearchOptions): Observable>> { @@ -162,6 +165,11 @@ export class BrowseService { ); } + /** + * Get the first item for a metadata definition in an optional scope + * @param definition + * @param scope + */ getFirstItemFor(definition: string, scope?: string): Observable>> { return this.getBrowseDefinitions().pipe( getBrowseDefinitionLinks(definition), @@ -184,18 +192,31 @@ export class BrowseService { ); } + /** + * Get the previous page using the paginated list's prev link + * @param items + */ getPrevBrowseItems(items: RemoteData>): Observable>> { return observableOf(items.payload.prev).pipe( getBrowseItemsFor(this.requestService, this.responseCache, this.rdb) ); } + /** + * Get the next page using the paginated list's next link + * @param items + */ getNextBrowseItems(items: RemoteData>): Observable>> { return observableOf(items.payload.next).pipe( getBrowseItemsFor(this.requestService, this.responseCache, this.rdb) ); } + /** + * Get the browse URL by providing a metadatum key and linkPath + * @param metadatumKey + * @param linkPath + */ getBrowseURLFor(metadatumKey: string, linkPath: string): Observable { const searchKeyArray = BrowseService.toSearchKeyArray(metadatumKey); return this.getBrowseDefinitions().pipe( @@ -220,6 +241,12 @@ export class BrowseService { } +/** + * Operator for turning a href into a PaginatedList of BrowseEntries + * @param requestService + * @param responseCache + * @param rdb + */ export const getBrowseEntriesFor = (requestService: RequestService, responseCache: ResponseCacheService, rdb: RemoteDataBuildService) => (source: Observable): Observable>> => source.pipe( @@ -228,6 +255,12 @@ export const getBrowseEntriesFor = (requestService: RequestService, responseCach toRDPaginatedBrowseEntries(requestService, responseCache, rdb) ); +/** + * Operator for turning a href into a PaginatedList of Items + * @param requestService + * @param responseCache + * @param rdb + */ export const getBrowseItemsFor = (requestService: RequestService, responseCache: ResponseCacheService, rdb: RemoteDataBuildService) => (source: Observable): Observable>> => source.pipe( @@ -236,6 +269,12 @@ export const getBrowseItemsFor = (requestService: RequestService, responseCache: toRDPaginatedBrowseItems(requestService, responseCache, rdb) ); +/** + * Operator for turning a RestRequest into a PaginatedList of Items + * @param requestService + * @param responseCache + * @param rdb + */ export const toRDPaginatedBrowseItems = (requestService: RequestService, responseCache: ResponseCacheService, rdb: RemoteDataBuildService) => (source: Observable): Observable>> => { const href$ = source.pipe(map((request: RestRequest) => request.href)); @@ -256,6 +295,12 @@ export const toRDPaginatedBrowseItems = (requestService: RequestService, respons return rdb.toRemoteDataObservable(requestEntry$, responseCache$, payload$); }; +/** + * Operator for turning a RestRequest into a PaginatedList of BrowseEntries + * @param requestService + * @param responseCache + * @param rdb + */ export const toRDPaginatedBrowseEntries = (requestService: RequestService, responseCache: ResponseCacheService, rdb: RemoteDataBuildService) => (source: Observable): Observable>> => { const href$ = source.pipe(map((request: RestRequest) => request.href)); diff --git a/src/app/shared/browse-by/browse-by-starts-with/browse-by-starts-with-abstract.component.ts b/src/app/shared/browse-by/browse-by-starts-with/browse-by-starts-with-abstract.component.ts index ff113f1773..5486deb057 100644 --- a/src/app/shared/browse-by/browse-by-starts-with/browse-by-starts-with-abstract.component.ts +++ b/src/app/shared/browse-by/browse-by-starts-with/browse-by-starts-with-abstract.component.ts @@ -4,9 +4,18 @@ import { hasValue } from '../../empty.util'; import { Subscription } from 'rxjs/internal/Subscription'; import { FormControl, FormGroup } from '@angular/forms'; +/** + * An abstract component to render StartsWith options + */ export class BrowseByStartsWithAbstractComponent implements OnInit, OnDestroy { + /** + * The currently selected startsWith in string format + */ startsWith: string; + /** + * The formdata controlling the StartsWith input + */ formData: FormGroup; /** @@ -30,11 +39,18 @@ export class BrowseByStartsWithAbstractComponent implements OnInit, OnDestroy { }); } + /** + * Set the startsWith by event + * @param event + */ setStartsWith(event: Event) { this.startsWith = (event.target as HTMLInputElement).value; this.setStartsWithParam(); } + /** + * Add/Change the url query parameter startsWith using the local variable + */ setStartsWithParam() { if (this.startsWith === '-1') { this.startsWith = undefined; @@ -45,6 +61,10 @@ export class BrowseByStartsWithAbstractComponent implements OnInit, OnDestroy { }); } + /** + * Submit the form data. Called when clicking a submit button on the form. + * @param data + */ submitForm(data) { this.startsWith = data.startsWith; this.setStartsWithParam(); diff --git a/src/app/shared/browse-by/browse-by-starts-with/browse-by-starts-with-decorator.ts b/src/app/shared/browse-by/browse-by-starts-with/browse-by-starts-with-decorator.ts index eb16254100..88f07c766f 100644 --- a/src/app/shared/browse-by/browse-by-starts-with/browse-by-starts-with-decorator.ts +++ b/src/app/shared/browse-by/browse-by-starts-with/browse-by-starts-with-decorator.ts @@ -2,6 +2,10 @@ import { BrowseByStartsWithType } from '../browse-by.component'; const startsWithMap = new Map(); +/** + * Fetch a decorator to render a StartsWith component for type + * @param type + */ export function renderStartsWithFor(type: BrowseByStartsWithType) { return function decorator(objectElement: any) { if (!objectElement) { @@ -11,6 +15,10 @@ export function renderStartsWithFor(type: BrowseByStartsWithType) { }; } +/** + * Get the correct component depending on the StartsWith type + * @param type + */ export function getStartsWithComponent(type: BrowseByStartsWithType) { return startsWithMap.get(type); } diff --git a/src/app/shared/browse-by/browse-by-starts-with/date/browse-by-starts-with-date.component.ts b/src/app/shared/browse-by/browse-by-starts-with/date/browse-by-starts-with-date.component.ts index b527ef77fb..78551270d6 100644 --- a/src/app/shared/browse-by/browse-by-starts-with/date/browse-by-starts-with-date.component.ts +++ b/src/app/shared/browse-by/browse-by-starts-with/date/browse-by-starts-with-date.component.ts @@ -3,6 +3,10 @@ import { renderStartsWithFor } from '../browse-by-starts-with-decorator'; import { BrowseByStartsWithType } from '../../browse-by.component'; import { BrowseByStartsWithAbstractComponent } from '../browse-by-starts-with-abstract.component'; +/** + * A switchable component rendering StartsWith options for the type "Date". + * The options are rendered in a dropdown with an input field (of type number) next to it. + */ @Component({ selector: 'ds-browse-by-starts-with-date', styleUrls: ['./browse-by-starts-with-date.component.scss'], diff --git a/src/app/shared/browse-by/browse-by-starts-with/text/browse-by-starts-with-text.component.ts b/src/app/shared/browse-by/browse-by-starts-with/text/browse-by-starts-with-text.component.ts index db0fac33fd..23ecacfa34 100644 --- a/src/app/shared/browse-by/browse-by-starts-with/text/browse-by-starts-with-text.component.ts +++ b/src/app/shared/browse-by/browse-by-starts-with/text/browse-by-starts-with-text.component.ts @@ -3,6 +3,9 @@ import { renderStartsWithFor } from '../browse-by-starts-with-decorator'; import { BrowseByStartsWithType } from '../../browse-by.component'; import { BrowseByStartsWithAbstractComponent } from '../browse-by-starts-with-abstract.component'; +/** + * A switchable component rendering StartsWith options for the type "Text". + */ @Component({ selector: 'ds-browse-by-starts-with-text', styleUrls: ['./browse-by-starts-with-text.component.scss'], diff --git a/src/app/shared/browse-by/browse-by.component.ts b/src/app/shared/browse-by/browse-by.component.ts index 1abf056cbc..4d5c35f3bc 100644 --- a/src/app/shared/browse-by/browse-by.component.ts +++ b/src/app/shared/browse-by/browse-by.component.ts @@ -8,6 +8,9 @@ import { Observable } from 'rxjs'; import { ListableObject } from '../object-collection/shared/listable-object.model'; import { getStartsWithComponent } from './browse-by-starts-with/browse-by-starts-with-decorator'; +/** + * An enum that defines the type of StartsWith options + */ export enum BrowseByStartsWithType { text = 'Text', date = 'Date' @@ -46,22 +49,50 @@ export class BrowseByComponent implements OnInit { */ @Input() sortConfig: SortOptions; + /** + * The type of StartsWith options used to define what component to render for the options + * Defaults to text + */ @Input() type = BrowseByStartsWithType.text; + /** + * The list of options to render for the StartsWith component + */ @Input() startsWithOptions = []; + /** + * Whether or not the pagination should be rendered as simple previous and next buttons instead of the normal pagination + */ @Input() enableArrows = false; + /** + * If enableArrows is set to true, should it hide the options gear? + */ @Input() hideGear = false; + /** + * If enableArrows is set to true, emit when the previous button is clicked + */ @Output() prev = new EventEmitter(); + /** + * If enableArrows is set to true, emit when the next button is clicked + */ @Output() next = new EventEmitter(); + /** + * If enableArrows is set to true, emit when the page size is changed + */ @Output() pageSizeChange = new EventEmitter(); + /** + * If enableArrows is set to true, emit when the sort direction is changed + */ @Output() sortDirectionChange = new EventEmitter(); + /** + * An object injector used to inject the startsWithOptions to the switchable StartsWith component + */ objectInjector: Injector; /** @@ -73,24 +104,41 @@ export class BrowseByComponent implements OnInit { } + /** + * Go to the previous page + */ goPrev() { this.prev.emit(true); } + /** + * Go to the next page + */ goNext() { this.next.emit(true); } + /** + * Change the page size + * @param size + */ doPageSizeChange(size) { this.paginationConfig.pageSize = size; this.pageSizeChange.emit(size); } + /** + * Change the sort direction + * @param direction + */ doSortDirectionChange(direction) { this.sortConfig.direction = direction; this.sortDirectionChange.emit(direction); } + /** + * Get the switchable StartsWith component dependant on the type + */ getStartsWithComponent() { return getStartsWithComponent(this.type); } From 394a32761171e28e6ba0006faa04e9480406ef8e Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Wed, 13 Feb 2019 10:41:52 +0100 Subject: [PATCH 19/36] 59695: Refactored getFirstItemFor --- .../browse-by-date-page.component.spec.ts | 2 +- .../browse-by-date-page.component.ts | 6 +++--- src/app/core/browse/browse.service.ts | 7 ++++--- src/app/core/shared/operators.ts | 9 +++++++++ 4 files changed, 17 insertions(+), 7 deletions(-) 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 77f41be76a..bb2187d9a6 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 @@ -39,7 +39,7 @@ describe('BrowseByDatePageComponent', () => { const mockBrowseService = { getBrowseEntriesFor: (options: BrowseEntrySearchOptions) => toRemoteData([]), getBrowseItemsFor: (value: string, options: BrowseEntrySearchOptions) => toRemoteData([firstItem]), - getFirstItemFor: () => toRemoteData([firstItem]) + getFirstItemFor: () => observableOf(new RemoteData(false, false, true, undefined, firstItem)) }; const mockDsoService = { 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 701082b6a2..c5048c9520 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 @@ -75,10 +75,10 @@ export class BrowseByDatePageComponent extends BrowseByMetadataPageComponent { */ updateStartsWithOptions(definition: string, metadataField: string, scope?: string) { this.subs.push( - this.browseService.getFirstItemFor(definition, scope).subscribe((firstItemRD: RemoteData>) => { + this.browseService.getFirstItemFor(definition, scope).subscribe((firstItemRD: RemoteData) => { let lowerLimit = this.config.browseBy.defaultLowerLimit; - if (firstItemRD.payload.page.length > 0) { - const date = firstItemRD.payload.page[0].findMetadata(metadataField); + if (hasValue(firstItemRD.payload)) { + const date = firstItemRD.payload.findMetadata(metadataField); if (hasValue(date) && hasValue(+date.split('-')[0])) { lowerLimit = +date.split('-')[0]; } diff --git a/src/app/core/browse/browse.service.ts b/src/app/core/browse/browse.service.ts index 40c51130d6..c7ff4d3478 100644 --- a/src/app/core/browse/browse.service.ts +++ b/src/app/core/browse/browse.service.ts @@ -26,7 +26,7 @@ import { BrowseEntry } from '../shared/browse-entry.model'; import { HALEndpointService } from '../shared/hal-endpoint.service'; import { configureRequest, - filterSuccessfulResponses, getBrowseDefinitionLinks, + filterSuccessfulResponses, getBrowseDefinitionLinks, getFirstOccurrence, getRemoteDataPayload, getRequestFromSelflink, getResponseFromSelflink @@ -170,7 +170,7 @@ export class BrowseService { * @param definition * @param scope */ - getFirstItemFor(definition: string, scope?: string): Observable>> { + getFirstItemFor(definition: string, scope?: string): Observable> { return this.getBrowseDefinitions().pipe( getBrowseDefinitionLinks(definition), hasValueOperator(), @@ -188,7 +188,8 @@ export class BrowseService { } return href; }), - getBrowseItemsFor(this.requestService, this.responseCache, this.rdb) + getBrowseItemsFor(this.requestService, this.responseCache, this.rdb), + getFirstOccurrence() ); } diff --git a/src/app/core/shared/operators.ts b/src/app/core/shared/operators.ts index a9294b2fc9..2dfe2691da 100644 --- a/src/app/core/shared/operators.ts +++ b/src/app/core/shared/operators.ts @@ -88,3 +88,12 @@ export const getBrowseDefinitionLinks = (definitionID: string) => } }) ); + +/** + * Get the first occurrence of an object within a paginated list + */ +export const getFirstOccurrence = () => + (source: Observable>>): Observable> => + source.pipe( + map((rd) => Object.assign(rd, { payload: rd.payload.page.length > 0 ? rd.payload.page[0] : undefined })) + ); From 5288f817b03d43b561b0c76da31acff7c5ed2bc1 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Wed, 13 Feb 2019 11:08:01 +0100 Subject: [PATCH 20/36] 59695: Fix AoT error and console warning --- .../browse-by-starts-with-abstract.component.ts | 7 +++++++ .../date/browse-by-starts-with-date.component.html | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/app/shared/browse-by/browse-by-starts-with/browse-by-starts-with-abstract.component.ts b/src/app/shared/browse-by/browse-by-starts-with/browse-by-starts-with-abstract.component.ts index 5486deb057..a51ad5e2ea 100644 --- a/src/app/shared/browse-by/browse-by-starts-with/browse-by-starts-with-abstract.component.ts +++ b/src/app/shared/browse-by/browse-by-starts-with/browse-by-starts-with-abstract.component.ts @@ -39,6 +39,13 @@ export class BrowseByStartsWithAbstractComponent implements OnInit, OnDestroy { }); } + /** + * Get startsWith as a number; + */ + getStartsWithAsNumber() { + return +this.startsWith; + } + /** * Set the startsWith by event * @param event diff --git a/src/app/shared/browse-by/browse-by-starts-with/date/browse-by-starts-with-date.component.html b/src/app/shared/browse-by/browse-by-starts-with/date/browse-by-starts-with-date.component.html index 43e53ea54d..0403e1b94f 100644 --- a/src/app/shared/browse-by/browse-by-starts-with/date/browse-by-starts-with-date.component.html +++ b/src/app/shared/browse-by/browse-by-starts-with/date/browse-by-starts-with-date.component.html @@ -16,7 +16,7 @@
- + From 25b65d840c634e0ed0adf2519d879ee09d8e6680 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Tue, 19 Feb 2019 08:57:37 +0100 Subject: [PATCH 21/36] 59695: Arrow-navigation enabled by default + support for entries --- .../browse-by-metadata-page.component.html | 2 +- .../browse-by-metadata-page.component.ts | 24 ++++++++++++++----- .../browse-by-title-page.component.spec.ts | 9 ++++--- .../browse-by-title-page.component.ts | 20 ++-------------- src/app/core/browse/browse.service.ts | 24 +++++++++++++++++-- 5 files changed, 47 insertions(+), 32 deletions(-) diff --git a/src/app/+browse-by/+browse-by-metadata-page/browse-by-metadata-page.component.html b/src/app/+browse-by/+browse-by-metadata-page/browse-by-metadata-page.component.html index 9ccc466d90..23f52e506a 100644 --- a/src/app/+browse-by/+browse-by-metadata-page/browse-by-metadata-page.component.html +++ b/src/app/+browse-by/+browse-by-metadata-page/browse-by-metadata-page.component.html @@ -7,7 +7,7 @@ [sortConfig]="sortConfig" [type]="startsWithType" [startsWithOptions]="startsWithOptions" - [enableArrows]="startsWith" + [enableArrows]="true" (prev)="goPrev()" (next)="goNext()" (pageSizeChange)="pageSizeChange($event)" 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 a8bd7086a4..cfd8c74107 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 @@ -168,18 +168,30 @@ export class BrowseByMetadataPageComponent implements OnInit { * Navigate to the previous page */ goPrev() { - this.items$.pipe(take(1)).subscribe((items) => { - this.items$ = this.browseService.getPrevBrowseItems(items); - }); + if (this.items$) { + this.items$.pipe(take(1)).subscribe((items) => { + this.items$ = this.browseService.getPrevBrowseItems(items); + }); + } else if (this.browseEntries$) { + this.browseEntries$.pipe(take(1)).subscribe((entries) => { + this.browseEntries$ = this.browseService.getPrevBrowseEntries(entries); + }); + } } /** * Navigate to the next page */ goNext() { - this.items$.pipe(take(1)).subscribe((items) => { - this.items$ = this.browseService.getNextBrowseItems(items); - }); + if (this.items$) { + this.items$.pipe(take(1)).subscribe((items) => { + this.items$ = this.browseService.getNextBrowseItems(items); + }); + } else if (this.browseEntries$) { + this.browseEntries$.pipe(take(1)).subscribe((entries) => { + this.browseEntries$ = this.browseService.getNextBrowseEntries(entries); + }); + } } /** 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 d7bc7397b8..bf62993d02 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 @@ -41,8 +41,8 @@ describe('BrowseByTitlePageComponent', () => { }) ]; - const mockItemDataService = { - findAll: () => toRemoteData(mockItems) + const mockBrowseService = { + getBrowseItemsFor: () => toRemoteData(mockItems) }; const mockDsoService = { @@ -59,10 +59,9 @@ describe('BrowseByTitlePageComponent', () => { declarations: [BrowseByTitlePageComponent, EnumKeysPipe], providers: [ { provide: ActivatedRoute, useValue: activatedRouteStub }, - { provide: BrowseService, useValue: {} }, + { provide: BrowseService, useValue: mockBrowseService }, { provide: DSpaceObjectDataService, useValue: mockDsoService }, - { provide: Router, useValue: new MockRouter() }, - { provide: ItemDataService, useValue: mockItemDataService } + { provide: Router, useValue: new MockRouter() } ], schemas: [NO_ERRORS_SCHEMA] }).compileComponents(); 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 a7a35f49cc..8703193dc5 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 @@ -25,8 +25,7 @@ export class BrowseByTitlePageComponent extends BrowseByMetadataPageComponent { public constructor(protected route: ActivatedRoute, protected browseService: BrowseService, protected dsoService: DSpaceObjectDataService, - protected router: Router, - protected itemDataService: ItemDataService) { + protected router: Router) { super(route, browseService, dsoService, router); } @@ -43,27 +42,12 @@ export class BrowseByTitlePageComponent extends BrowseByMetadataPageComponent { }) .subscribe((params) => { this.metadata = params.metadata || this.defaultMetadata; - this.updatePage(browseParamsToOptions(params, this.paginationConfig, this.sortConfig)); + this.updatePageWithItems(browseParamsToOptions(params, this.paginationConfig, this.sortConfig, this.metadata), undefined); this.updateParent(params.scope) })); this.startsWithOptions = []; } - /** - * Updates the current page with searchOptions - * @param searchOptions Options to narrow down your search: - * { pagination: PaginationComponentOptions, - * sort: SortOptions } - */ - updatePage(searchOptions: BrowseEntrySearchOptions) { - this.items$ = this.itemDataService.findAll({ - currentPage: searchOptions.pagination.currentPage, - elementsPerPage: searchOptions.pagination.pageSize, - sort: searchOptions.sort, - scopeID: searchOptions.scope - }); - } - ngOnDestroy(): void { this.subs.filter((sub) => hasValue(sub)).forEach((sub) => sub.unsubscribe()); } diff --git a/src/app/core/browse/browse.service.ts b/src/app/core/browse/browse.service.ts index c7ff4d3478..91dd5aff75 100644 --- a/src/app/core/browse/browse.service.ts +++ b/src/app/core/browse/browse.service.ts @@ -194,7 +194,7 @@ export class BrowseService { } /** - * Get the previous page using the paginated list's prev link + * Get the previous page of items using the paginated list's prev link * @param items */ getPrevBrowseItems(items: RemoteData>): Observable>> { @@ -204,7 +204,7 @@ export class BrowseService { } /** - * Get the next page using the paginated list's next link + * Get the next page of items using the paginated list's next link * @param items */ getNextBrowseItems(items: RemoteData>): Observable>> { @@ -213,6 +213,26 @@ export class BrowseService { ); } + /** + * Get the previous page of browse-entries using the paginated list's prev link + * @param entries + */ + getPrevBrowseEntries(entries: RemoteData>): Observable>> { + return observableOf(entries.payload.prev).pipe( + getBrowseEntriesFor(this.requestService, this.responseCache, this.rdb) + ); + } + + /** + * Get the next page of browse-entries using the paginated list's next link + * @param entries + */ + getNextBrowseEntries(entries: RemoteData>): Observable>> { + return observableOf(entries.payload.next).pipe( + getBrowseEntriesFor(this.requestService, this.responseCache, this.rdb) + ); + } + /** * Get the browse URL by providing a metadatum key and linkPath * @param metadatumKey From 0da38dbd523b2df97ba12d49a5d636438d8c0b20 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Tue, 19 Feb 2019 09:15:47 +0100 Subject: [PATCH 22/36] 59695: Browse-By-Title test fix --- .../browse-by-title-page.component.spec.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) 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 bf62993d02..acb1fe72e3 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 @@ -42,7 +42,8 @@ describe('BrowseByTitlePageComponent', () => { ]; const mockBrowseService = { - getBrowseItemsFor: () => toRemoteData(mockItems) + getBrowseItemsFor: () => toRemoteData(mockItems), + getBrowseEntriesFor: () => toRemoteData([]) }; const mockDsoService = { @@ -50,7 +51,8 @@ describe('BrowseByTitlePageComponent', () => { }; const activatedRouteStub = Object.assign(new ActivatedRouteStub(), { - params: observableOf({}) + params: observableOf({}), + data: observableOf({ metadata: 'title' }) }); beforeEach(async(() => { From 16f448021b1d4cd43b185405a1fe50b40308bba5 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Wed, 20 Feb 2019 08:54:51 +0100 Subject: [PATCH 23/36] 60168: Browse-by startsWith text component --- resources/i18n/en.json | 1 + .../browse-by-metadata-page.component.ts | 6 +++++- .../browse-by-title-page.component.ts | 2 +- ...browse-by-starts-with-abstract.component.ts | 8 ++++++++ .../browse-by-starts-with-text.component.html | 18 +++++++++++++++++- 5 files changed, 32 insertions(+), 3 deletions(-) diff --git a/resources/i18n/en.json b/resources/i18n/en.json index a9a456df33..b1e76631f0 100644 --- a/resources/i18n/en.json +++ b/resources/i18n/en.json @@ -285,6 +285,7 @@ "jump": "Jump to a point in the index:", "choose_year": "(Choose year)", "type_year": "Or type in a year:", + "type_text": "Or enter first few letters:", "submit": "Go" }, "metadata": { 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 cfd8c74107..79a8ff0421 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 @@ -123,7 +123,11 @@ export class BrowseByMetadataPageComponent implements OnInit { } this.updateParent(params.scope); })); - this.startsWithOptions = []; + this.updateStartsWithTextOptions(); + } + + updateStartsWithTextOptions() { + this.startsWithOptions = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split(''); } /** 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 8703193dc5..717275bf8b 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 @@ -45,7 +45,7 @@ export class BrowseByTitlePageComponent extends BrowseByMetadataPageComponent { this.updatePageWithItems(browseParamsToOptions(params, this.paginationConfig, this.sortConfig, this.metadata), undefined); this.updateParent(params.scope) })); - this.startsWithOptions = []; + this.updateStartsWithTextOptions(); } ngOnDestroy(): void { diff --git a/src/app/shared/browse-by/browse-by-starts-with/browse-by-starts-with-abstract.component.ts b/src/app/shared/browse-by/browse-by-starts-with/browse-by-starts-with-abstract.component.ts index a51ad5e2ea..97030f7cdf 100644 --- a/src/app/shared/browse-by/browse-by-starts-with/browse-by-starts-with-abstract.component.ts +++ b/src/app/shared/browse-by/browse-by-starts-with/browse-by-starts-with-abstract.component.ts @@ -39,6 +39,14 @@ export class BrowseByStartsWithAbstractComponent implements OnInit, OnDestroy { }); } + getStartsWithAsText() { + if (hasValue(this.startsWith)) { + return this.startsWith; + } else { + return ''; + } + } + /** * Get startsWith as a number; */ diff --git a/src/app/shared/browse-by/browse-by-starts-with/text/browse-by-starts-with-text.component.html b/src/app/shared/browse-by/browse-by-starts-with/text/browse-by-starts-with-text.component.html index 7a1b58e832..5255423d81 100644 --- a/src/app/shared/browse-by/browse-by-starts-with/text/browse-by-starts-with-text.component.html +++ b/src/app/shared/browse-by/browse-by-starts-with/text/browse-by-starts-with-text.component.html @@ -1 +1,17 @@ - +
+
+
+ +
+
+ + + + +
+
+
From a0501b0c3ca40939a20782a42bc6083c753d4162 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Wed, 20 Feb 2019 09:24:07 +0100 Subject: [PATCH 24/36] 60168: Refactor browse-by-starts-with components to starts-with components + further refactoring by seperating date from text --- .../browse-by-date-page.component.ts | 4 +- .../browse-by-metadata-page.component.ts | 4 +- .../browse-by-starts-with-date.component.ts | 18 --------- .../browse-by-starts-with-text.component.scss | 0 .../browse-by-starts-with-text.component.ts | 17 --------- .../shared/browse-by/browse-by.component.ts | 12 +----- src/app/shared/shared.module.ts | 8 ++-- .../date/starts-with-date.component.html} | 2 +- .../date/starts-with-date.component.scss} | 2 +- .../date/starts-with-date.component.spec.ts} | 16 ++++---- .../date/starts-with-date.component.ts | 34 +++++++++++++++++ .../starts-with-abstract.component.ts} | 21 +++------- .../starts-with-decorator.spec.ts} | 7 ++-- .../starts-with-decorator.ts} | 14 +++++-- .../text/starts-with-text.component.html} | 2 +- .../text/starts-with-text.component.scss | 7 ++++ .../text/starts-with-text.component.ts | 38 +++++++++++++++++++ 17 files changed, 118 insertions(+), 88 deletions(-) delete mode 100644 src/app/shared/browse-by/browse-by-starts-with/date/browse-by-starts-with-date.component.ts delete mode 100644 src/app/shared/browse-by/browse-by-starts-with/text/browse-by-starts-with-text.component.scss delete mode 100644 src/app/shared/browse-by/browse-by-starts-with/text/browse-by-starts-with-text.component.ts rename src/app/shared/{browse-by/browse-by-starts-with/date/browse-by-starts-with-date.component.html => starts-with/date/starts-with-date.component.html} (94%) rename src/app/shared/{browse-by/browse-by-starts-with/date/browse-by-starts-with-date.component.scss => starts-with/date/starts-with-date.component.scss} (72%) rename src/app/shared/{browse-by/browse-by-starts-with/date/browse-by-starts-with-date.component.spec.ts => starts-with/date/starts-with-date.component.spec.ts} (87%) create mode 100644 src/app/shared/starts-with/date/starts-with-date.component.ts rename src/app/shared/{browse-by/browse-by-starts-with/browse-by-starts-with-abstract.component.ts => starts-with/starts-with-abstract.component.ts} (80%) rename src/app/shared/{browse-by/browse-by-starts-with/browse-by-starts-with-decorator.spec.ts => starts-with/starts-with-decorator.spec.ts} (54%) rename src/app/shared/{browse-by/browse-by-starts-with/browse-by-starts-with-decorator.ts => starts-with/starts-with-decorator.ts} (60%) rename src/app/shared/{browse-by/browse-by-starts-with/text/browse-by-starts-with-text.component.html => starts-with/text/starts-with-text.component.html} (92%) create mode 100644 src/app/shared/starts-with/text/starts-with-text.component.scss create mode 100644 src/app/shared/starts-with/text/starts-with-text.component.ts 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 c5048c9520..8e7502fec9 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 @@ -5,7 +5,6 @@ import { } 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/internal/observable/combineLatest'; -import { BrowseByStartsWithType } from '../../shared/browse-by/browse-by.component'; import { RemoteData } from '../../core/data/remote-data'; import { PaginatedList } from '../../core/data/paginated-list'; import { Item } from '../../core/shared/item.model'; @@ -14,6 +13,7 @@ import { ActivatedRoute, Router } from '@angular/router'; import { BrowseService } from '../../core/browse/browse.service'; import { DSpaceObjectDataService } from '../../core/data/dspace-object-data.service'; import { GLOBAL_CONFIG, GlobalConfig } from '../../../config'; +import { StartsWithType } from '../../shared/starts-with/starts-with-decorator'; @Component({ selector: 'ds-browse-by-date-page', @@ -42,7 +42,7 @@ export class BrowseByDatePageComponent extends BrowseByMetadataPageComponent { } ngOnInit(): void { - this.startsWithType = BrowseByStartsWithType.date; + this.startsWithType = StartsWithType.date; this.updatePage(new BrowseEntrySearchOptions(null, this.paginationConfig, this.sortConfig)); this.subs.push( observableCombineLatest( 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 79a8ff0421..e960ac2ae9 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 @@ -14,7 +14,7 @@ import { getSucceededRemoteData } from '../../core/shared/operators'; import { DSpaceObjectDataService } from '../../core/data/dspace-object-data.service'; import { DSpaceObject } from '../../core/shared/dspace-object.model'; import { take } from 'rxjs/operators'; -import { BrowseByStartsWithType } from '../../shared/browse-by/browse-by.component'; +import { StartsWithType } from '../../shared/starts-with/starts-with-decorator'; @Component({ selector: 'ds-browse-by-metadata-page', @@ -76,7 +76,7 @@ export class BrowseByMetadataPageComponent implements OnInit { * The type of StartsWith options to render * Defaults to text */ - startsWithType = BrowseByStartsWithType.text; + startsWithType = StartsWithType.text; /** * The list of StartsWith options diff --git a/src/app/shared/browse-by/browse-by-starts-with/date/browse-by-starts-with-date.component.ts b/src/app/shared/browse-by/browse-by-starts-with/date/browse-by-starts-with-date.component.ts deleted file mode 100644 index 78551270d6..0000000000 --- a/src/app/shared/browse-by/browse-by-starts-with/date/browse-by-starts-with-date.component.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Component } from '@angular/core'; -import { renderStartsWithFor } from '../browse-by-starts-with-decorator'; -import { BrowseByStartsWithType } from '../../browse-by.component'; -import { BrowseByStartsWithAbstractComponent } from '../browse-by-starts-with-abstract.component'; - -/** - * A switchable component rendering StartsWith options for the type "Date". - * The options are rendered in a dropdown with an input field (of type number) next to it. - */ -@Component({ - selector: 'ds-browse-by-starts-with-date', - styleUrls: ['./browse-by-starts-with-date.component.scss'], - templateUrl: './browse-by-starts-with-date.component.html' -}) -@renderStartsWithFor(BrowseByStartsWithType.date) -export class BrowseByStartsWithDateComponent extends BrowseByStartsWithAbstractComponent { - -} diff --git a/src/app/shared/browse-by/browse-by-starts-with/text/browse-by-starts-with-text.component.scss b/src/app/shared/browse-by/browse-by-starts-with/text/browse-by-starts-with-text.component.scss deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/app/shared/browse-by/browse-by-starts-with/text/browse-by-starts-with-text.component.ts b/src/app/shared/browse-by/browse-by-starts-with/text/browse-by-starts-with-text.component.ts deleted file mode 100644 index 23ecacfa34..0000000000 --- a/src/app/shared/browse-by/browse-by-starts-with/text/browse-by-starts-with-text.component.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { Component, Inject } from '@angular/core'; -import { renderStartsWithFor } from '../browse-by-starts-with-decorator'; -import { BrowseByStartsWithType } from '../../browse-by.component'; -import { BrowseByStartsWithAbstractComponent } from '../browse-by-starts-with-abstract.component'; - -/** - * A switchable component rendering StartsWith options for the type "Text". - */ -@Component({ - selector: 'ds-browse-by-starts-with-text', - styleUrls: ['./browse-by-starts-with-text.component.scss'], - templateUrl: './browse-by-starts-with-text.component.html' -}) -@renderStartsWithFor(BrowseByStartsWithType.text) -export class BrowseByStartsWithTextComponent extends BrowseByStartsWithAbstractComponent { - -} diff --git a/src/app/shared/browse-by/browse-by.component.ts b/src/app/shared/browse-by/browse-by.component.ts index 4d5c35f3bc..6c4bc78213 100644 --- a/src/app/shared/browse-by/browse-by.component.ts +++ b/src/app/shared/browse-by/browse-by.component.ts @@ -6,15 +6,7 @@ import { SortDirection, SortOptions } from '../../core/cache/models/sort-options import { fadeIn, fadeInOut } from '../animations/fade'; import { Observable } from 'rxjs'; import { ListableObject } from '../object-collection/shared/listable-object.model'; -import { getStartsWithComponent } from './browse-by-starts-with/browse-by-starts-with-decorator'; - -/** - * An enum that defines the type of StartsWith options - */ -export enum BrowseByStartsWithType { - text = 'Text', - date = 'Date' -} +import { getStartsWithComponent, StartsWithType } from '../starts-with/starts-with-decorator'; @Component({ selector: 'ds-browse-by', @@ -53,7 +45,7 @@ export class BrowseByComponent implements OnInit { * The type of StartsWith options used to define what component to render for the options * Defaults to text */ - @Input() type = BrowseByStartsWithType.text; + @Input() type = StartsWithType.text; /** * The list of options to render for the StartsWith component diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index b8530101f9..18f151fccb 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -88,8 +88,8 @@ import { MomentModule } from 'ngx-moment'; import { MenuModule } from './menu/menu.module'; import {LangSwitchComponent} from './lang-switch/lang-switch.component'; import { ComcolPageBrowseByComponent } from './comcol-page-browse-by/comcol-page-browse-by.component'; -import { BrowseByStartsWithDateComponent } from './browse-by/browse-by-starts-with/date/browse-by-starts-with-date.component'; -import { BrowseByStartsWithTextComponent } from './browse-by/browse-by-starts-with/text/browse-by-starts-with-text.component'; +import { StartsWithDateComponent } from './starts-with/date/starts-with-date.component'; +import { StartsWithTextComponent } from './starts-with/text/starts-with-text.component'; const MODULES = [ // Do NOT include UniversalModule, HttpModule, or JsonpModule here @@ -180,8 +180,8 @@ const ENTRY_COMPONENTS = [ CommunityGridElementComponent, SearchResultGridElementComponent, BrowseEntryListElementComponent, - BrowseByStartsWithDateComponent, - BrowseByStartsWithTextComponent + StartsWithDateComponent, + StartsWithTextComponent ]; const PROVIDERS = [ diff --git a/src/app/shared/browse-by/browse-by-starts-with/date/browse-by-starts-with-date.component.html b/src/app/shared/starts-with/date/starts-with-date.component.html similarity index 94% rename from src/app/shared/browse-by/browse-by-starts-with/date/browse-by-starts-with-date.component.html rename to src/app/shared/starts-with/date/starts-with-date.component.html index 0403e1b94f..22f59b0875 100644 --- a/src/app/shared/browse-by/browse-by-starts-with/date/browse-by-starts-with-date.component.html +++ b/src/app/shared/starts-with/date/starts-with-date.component.html @@ -16,7 +16,7 @@
- + diff --git a/src/app/shared/browse-by/browse-by-starts-with/date/browse-by-starts-with-date.component.scss b/src/app/shared/starts-with/date/starts-with-date.component.scss similarity index 72% rename from src/app/shared/browse-by/browse-by-starts-with/date/browse-by-starts-with-date.component.scss rename to src/app/shared/starts-with/date/starts-with-date.component.scss index e516151d57..ceec56c8c2 100644 --- a/src/app/shared/browse-by/browse-by-starts-with/date/browse-by-starts-with-date.component.scss +++ b/src/app/shared/starts-with/date/starts-with-date.component.scss @@ -1,4 +1,4 @@ -@import '../../../../../styles/variables.scss'; +@import '../../../../styles/variables.scss'; // temporary fix for bootstrap 4 beta btn color issue .btn-secondary { diff --git a/src/app/shared/browse-by/browse-by-starts-with/date/browse-by-starts-with-date.component.spec.ts b/src/app/shared/starts-with/date/starts-with-date.component.spec.ts similarity index 87% rename from src/app/shared/browse-by/browse-by-starts-with/date/browse-by-starts-with-date.component.spec.ts rename to src/app/shared/starts-with/date/starts-with-date.component.spec.ts index c0812245e9..e2956a2a97 100644 --- a/src/app/shared/browse-by/browse-by-starts-with/date/browse-by-starts-with-date.component.spec.ts +++ b/src/app/shared/starts-with/date/starts-with-date.component.spec.ts @@ -1,20 +1,20 @@ -import { BrowseByStartsWithDateComponent } from './browse-by-starts-with-date.component'; import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { CommonModule } from '@angular/common'; import { RouterTestingModule } from '@angular/router/testing'; import { TranslateModule } from '@ngx-translate/core'; import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; -import { EnumKeysPipe } from '../../../utils/enum-keys-pipe'; import { ActivatedRoute, Router } from '@angular/router'; import { DebugElement, NO_ERRORS_SCHEMA } from '@angular/core'; -import { ActivatedRouteStub } from '../../../testing/active-router-stub'; import { of as observableOf } from 'rxjs/internal/observable/of'; -import { RouterStub } from '../../../testing/router-stub'; import { By } from '@angular/platform-browser'; +import { StartsWithDateComponent } from './starts-with-date.component'; +import { ActivatedRouteStub } from '../../testing/active-router-stub'; +import { EnumKeysPipe } from '../../utils/enum-keys-pipe'; +import { RouterStub } from '../../testing/router-stub'; describe('BrowseByStartsWithDateComponent', () => { - let comp: BrowseByStartsWithDateComponent; - let fixture: ComponentFixture; + let comp: StartsWithDateComponent; + let fixture: ComponentFixture; let route: ActivatedRoute; let router: Router; @@ -28,7 +28,7 @@ describe('BrowseByStartsWithDateComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ imports: [CommonModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule.forRoot()], - declarations: [BrowseByStartsWithDateComponent, EnumKeysPipe], + declarations: [StartsWithDateComponent, EnumKeysPipe], providers: [ { provide: 'startsWithOptions', useValue: options }, { provide: ActivatedRoute, useValue: activatedRouteStub }, @@ -39,7 +39,7 @@ describe('BrowseByStartsWithDateComponent', () => { })); beforeEach(() => { - fixture = TestBed.createComponent(BrowseByStartsWithDateComponent); + fixture = TestBed.createComponent(StartsWithDateComponent); comp = fixture.componentInstance; fixture.detectChanges(); route = (comp as any).route; diff --git a/src/app/shared/starts-with/date/starts-with-date.component.ts b/src/app/shared/starts-with/date/starts-with-date.component.ts new file mode 100644 index 0000000000..ab4a26498e --- /dev/null +++ b/src/app/shared/starts-with/date/starts-with-date.component.ts @@ -0,0 +1,34 @@ +import { Component } from '@angular/core'; +import { renderStartsWithFor, StartsWithType } from '../starts-with-decorator'; +import { StartsWithAbstractComponent } from '../starts-with-abstract.component'; + +/** + * A switchable component rendering StartsWith options for the type "Date". + * The options are rendered in a dropdown with an input field (of type number) next to it. + */ +@Component({ + selector: 'ds-starts-with-date', + styleUrls: ['./starts-with-date.component.scss'], + templateUrl: './starts-with-date.component.html' +}) +@renderStartsWithFor(StartsWithType.date) +export class StartsWithDateComponent extends StartsWithAbstractComponent { + + /** + * Get startsWith as a number; + */ + getStartsWith() { + return +this.startsWith; + } + + /** + * Add/Change the url query parameter startsWith using the local variable + */ + setStartsWithParam() { + if (this.startsWith === '-1') { + this.startsWith = undefined; + } + super.setStartsWithParam(); + } + +} diff --git a/src/app/shared/browse-by/browse-by-starts-with/browse-by-starts-with-abstract.component.ts b/src/app/shared/starts-with/starts-with-abstract.component.ts similarity index 80% rename from src/app/shared/browse-by/browse-by-starts-with/browse-by-starts-with-abstract.component.ts rename to src/app/shared/starts-with/starts-with-abstract.component.ts index 97030f7cdf..f1137004a6 100644 --- a/src/app/shared/browse-by/browse-by-starts-with/browse-by-starts-with-abstract.component.ts +++ b/src/app/shared/starts-with/starts-with-abstract.component.ts @@ -1,13 +1,13 @@ import { Inject, OnDestroy, OnInit } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; -import { hasValue } from '../../empty.util'; import { Subscription } from 'rxjs/internal/Subscription'; import { FormControl, FormGroup } from '@angular/forms'; +import { hasValue } from '../empty.util'; /** * An abstract component to render StartsWith options */ -export class BrowseByStartsWithAbstractComponent implements OnInit, OnDestroy { +export class StartsWithAbstractComponent implements OnInit, OnDestroy { /** * The currently selected startsWith in string format */ @@ -39,19 +39,11 @@ export class BrowseByStartsWithAbstractComponent implements OnInit, OnDestroy { }); } - getStartsWithAsText() { - if (hasValue(this.startsWith)) { - return this.startsWith; - } else { - return ''; - } - } - /** - * Get startsWith as a number; + * Get startsWith */ - getStartsWithAsNumber() { - return +this.startsWith; + getStartsWith(): any { + return this.startsWith; } /** @@ -67,9 +59,6 @@ export class BrowseByStartsWithAbstractComponent implements OnInit, OnDestroy { * Add/Change the url query parameter startsWith using the local variable */ setStartsWithParam() { - if (this.startsWith === '-1') { - this.startsWith = undefined; - } this.router.navigate([], { queryParams: Object.assign({ startsWith: this.startsWith }), queryParamsHandling: 'merge' diff --git a/src/app/shared/browse-by/browse-by-starts-with/browse-by-starts-with-decorator.spec.ts b/src/app/shared/starts-with/starts-with-decorator.spec.ts similarity index 54% rename from src/app/shared/browse-by/browse-by-starts-with/browse-by-starts-with-decorator.spec.ts rename to src/app/shared/starts-with/starts-with-decorator.spec.ts index 8eaa9eee09..0ba72d8ac4 100644 --- a/src/app/shared/browse-by/browse-by-starts-with/browse-by-starts-with-decorator.spec.ts +++ b/src/app/shared/starts-with/starts-with-decorator.spec.ts @@ -1,9 +1,8 @@ -import { renderStartsWithFor } from './browse-by-starts-with-decorator'; -import { BrowseByStartsWithType } from '../browse-by.component'; +import { renderStartsWithFor, StartsWithType } from './starts-with-decorator'; describe('BrowseByStartsWithDecorator', () => { - const textDecorator = renderStartsWithFor(BrowseByStartsWithType.text); - const dateDecorator = renderStartsWithFor(BrowseByStartsWithType.date); + const textDecorator = renderStartsWithFor(StartsWithType.text); + const dateDecorator = renderStartsWithFor(StartsWithType.date); it('should have a decorator for both text and date', () => { expect(textDecorator.length).not.toBeNull(); expect(dateDecorator.length).not.toBeNull(); diff --git a/src/app/shared/browse-by/browse-by-starts-with/browse-by-starts-with-decorator.ts b/src/app/shared/starts-with/starts-with-decorator.ts similarity index 60% rename from src/app/shared/browse-by/browse-by-starts-with/browse-by-starts-with-decorator.ts rename to src/app/shared/starts-with/starts-with-decorator.ts index 88f07c766f..7592f00a8b 100644 --- a/src/app/shared/browse-by/browse-by-starts-with/browse-by-starts-with-decorator.ts +++ b/src/app/shared/starts-with/starts-with-decorator.ts @@ -1,12 +1,18 @@ -import { BrowseByStartsWithType } from '../browse-by.component'; - const startsWithMap = new Map(); +/** + * An enum that defines the type of StartsWith options + */ +export enum StartsWithType { + text = 'Text', + date = 'Date' +} + /** * Fetch a decorator to render a StartsWith component for type * @param type */ -export function renderStartsWithFor(type: BrowseByStartsWithType) { +export function renderStartsWithFor(type: StartsWithType) { return function decorator(objectElement: any) { if (!objectElement) { return; @@ -19,6 +25,6 @@ export function renderStartsWithFor(type: BrowseByStartsWithType) { * Get the correct component depending on the StartsWith type * @param type */ -export function getStartsWithComponent(type: BrowseByStartsWithType) { +export function getStartsWithComponent(type: StartsWithType) { return startsWithMap.get(type); } diff --git a/src/app/shared/browse-by/browse-by-starts-with/text/browse-by-starts-with-text.component.html b/src/app/shared/starts-with/text/starts-with-text.component.html similarity index 92% rename from src/app/shared/browse-by/browse-by-starts-with/text/browse-by-starts-with-text.component.html rename to src/app/shared/starts-with/text/starts-with-text.component.html index 5255423d81..8ca2ad7565 100644 --- a/src/app/shared/browse-by/browse-by-starts-with/text/browse-by-starts-with-text.component.html +++ b/src/app/shared/starts-with/text/starts-with-text.component.html @@ -8,7 +8,7 @@
- + diff --git a/src/app/shared/starts-with/text/starts-with-text.component.scss b/src/app/shared/starts-with/text/starts-with-text.component.scss new file mode 100644 index 0000000000..ceec56c8c2 --- /dev/null +++ b/src/app/shared/starts-with/text/starts-with-text.component.scss @@ -0,0 +1,7 @@ +@import '../../../../styles/variables.scss'; + +// temporary fix for bootstrap 4 beta btn color issue +.btn-secondary { + background-color: $input-bg; + color: $input-color; +} diff --git a/src/app/shared/starts-with/text/starts-with-text.component.ts b/src/app/shared/starts-with/text/starts-with-text.component.ts new file mode 100644 index 0000000000..ef6ff87163 --- /dev/null +++ b/src/app/shared/starts-with/text/starts-with-text.component.ts @@ -0,0 +1,38 @@ +import { Component, Inject } from '@angular/core'; +import { renderStartsWithFor, StartsWithType } from '../starts-with-decorator'; +import { StartsWithAbstractComponent } from '../starts-with-abstract.component'; +import { hasValue } from '../../empty.util'; + +/** + * A switchable component rendering StartsWith options for the type "Text". + */ +@Component({ + selector: 'ds-starts-with-text', + styleUrls: ['./starts-with-text.component.scss'], + templateUrl: './starts-with-text.component.html' +}) +@renderStartsWithFor(StartsWithType.text) +export class StartsWithTextComponent extends StartsWithAbstractComponent { + + /** + * Get startsWith as text; + */ + getStartsWith() { + if (hasValue(this.startsWith)) { + return this.startsWith; + } else { + return ''; + } + } + + /** + * Add/Change the url query parameter startsWith using the local variable + */ + setStartsWithParam() { + if (this.startsWith === '0-9') { + this.startsWith = '0'; + } + super.setStartsWithParam(); + } + +} From 7ce9b01adfb1131ba826805dd2707c0b21047aec Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Wed, 20 Feb 2019 09:38:00 +0100 Subject: [PATCH 25/36] 60168: 0-9 option for alphabetic startsWith --- .../browse-by-metadata-page.component.ts | 2 +- .../starts-with/date/starts-with-date.component.html | 2 +- .../starts-with/starts-with-abstract.component.ts | 11 ++++++++++- .../starts-with/text/starts-with-text.component.html | 2 +- 4 files changed, 13 insertions(+), 4 deletions(-) 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 e960ac2ae9..47ca9e76c0 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 @@ -127,7 +127,7 @@ export class BrowseByMetadataPageComponent implements OnInit { } updateStartsWithTextOptions() { - this.startsWithOptions = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split(''); + this.startsWithOptions = ['0-9', ...'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('')]; } /** diff --git a/src/app/shared/starts-with/date/starts-with-date.component.html b/src/app/shared/starts-with/date/starts-with-date.component.html index 22f59b0875..bddbb6f391 100644 --- a/src/app/shared/starts-with/date/starts-with-date.component.html +++ b/src/app/shared/starts-with/date/starts-with-date.component.html @@ -4,7 +4,7 @@ {{'browse.startsWith.jump' | translate}}
- diff --git a/src/app/shared/starts-with/starts-with-abstract.component.ts b/src/app/shared/starts-with/starts-with-abstract.component.ts index f1137004a6..f9105e2756 100644 --- a/src/app/shared/starts-with/starts-with-abstract.component.ts +++ b/src/app/shared/starts-with/starts-with-abstract.component.ts @@ -50,11 +50,20 @@ export class StartsWithAbstractComponent implements OnInit, OnDestroy { * Set the startsWith by event * @param event */ - setStartsWith(event: Event) { + setStartsWithEvent(event: Event) { this.startsWith = (event.target as HTMLInputElement).value; this.setStartsWithParam(); } + /** + * Set the startsWith by string + * @param startsWith + */ + setStartsWith(startsWith: string) { + this.startsWith = startsWith; + this.setStartsWithParam(); + } + /** * Add/Change the url query parameter startsWith using the local variable */ diff --git a/src/app/shared/starts-with/text/starts-with-text.component.html b/src/app/shared/starts-with/text/starts-with-text.component.html index 8ca2ad7565..41ab7294f1 100644 --- a/src/app/shared/starts-with/text/starts-with-text.component.html +++ b/src/app/shared/starts-with/text/starts-with-text.component.html @@ -3,7 +3,7 @@ From 9a0989f240c8b22d5c0907f5b88963a3e8960eeb Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Wed, 20 Feb 2019 10:08:12 +0100 Subject: [PATCH 26/36] 60168: Starts-With text dropdown options + styling --- resources/i18n/en.json | 1 + .../browse-by-metadata-page.component.html | 2 +- .../date/starts-with-date.component.ts | 10 ---------- .../starts-with-abstract.component.ts | 3 +++ .../text/starts-with-text.component.html | 16 ++++++++++++++-- .../text/starts-with-text.component.ts | 11 +++++++++++ 6 files changed, 30 insertions(+), 13 deletions(-) diff --git a/resources/i18n/en.json b/resources/i18n/en.json index b1e76631f0..5778edfa6c 100644 --- a/resources/i18n/en.json +++ b/resources/i18n/en.json @@ -284,6 +284,7 @@ "startsWith": { "jump": "Jump to a point in the index:", "choose_year": "(Choose year)", + "choose_start": "(Choose start)", "type_year": "Or type in a year:", "type_text": "Or enter first few letters:", "submit": "Go" diff --git a/src/app/+browse-by/+browse-by-metadata-page/browse-by-metadata-page.component.html b/src/app/+browse-by/+browse-by-metadata-page/browse-by-metadata-page.component.html index 23f52e506a..cf43f74eb0 100644 --- a/src/app/+browse-by/+browse-by-metadata-page/browse-by-metadata-page.component.html +++ b/src/app/+browse-by/+browse-by-metadata-page/browse-by-metadata-page.component.html @@ -1,5 +1,5 @@
-