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); }