From 67e5578bba220891198b04d34b78a84a0b5c2f87 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Tue, 30 Apr 2019 16:24:22 +0200 Subject: [PATCH 1/7] 62063: Configurable browse-by --- config/environment.default.js | 21 ++++++++++- .../browse-by-date-page.component.ts | 7 ++-- .../browse-by-metadata-page.component.ts | 2 ++ .../browse-by-decorator.ts | 29 +++++++++++++++ .../browse-by-switcher.component.html | 1 + .../browse-by-switcher.component.ts | 36 +++++++++++++++++++ .../browse-by-title-page.component.ts | 3 +- src/app/+browse-by/browse-by-guard.ts | 16 ++++++--- .../+browse-by/browse-by-routing.module.ts | 8 ++--- src/app/+browse-by/browse-by.module.ts | 9 ++++- src/config/browse-by-config.interface.ts | 3 ++ src/config/browse-by-type-config.interface.ts | 8 +++++ 12 files changed, 128 insertions(+), 15 deletions(-) create mode 100644 src/app/+browse-by/+browse-by-switcher/browse-by-decorator.ts create mode 100644 src/app/+browse-by/+browse-by-switcher/browse-by-switcher.component.html create mode 100644 src/app/+browse-by/+browse-by-switcher/browse-by-switcher.component.ts create mode 100644 src/config/browse-by-type-config.interface.ts diff --git a/config/environment.default.js b/config/environment.default.js index f70f132fa4..3013705a2e 100644 --- a/config/environment.default.js +++ b/config/environment.default.js @@ -149,7 +149,26 @@ module.exports = { // 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 + defaultLowerLimit: 1900, + types: [ + { + metadata: 'title', + type: 'title' + }, + { + metadata: 'dateissued', + type: 'date', + metadataField: 'dc.date.issued' + }, + { + metadata: 'author', + type: 'metadata' + }, + { + metadata: 'subject', + type: 'metadata' + } + ] }, item: { edit: { 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 2acd96adb0..ab22216afa 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 @@ -13,6 +13,7 @@ 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'; +import { BrowseByType, rendersBrowseBy } from '../+browse-by-switcher/browse-by-decorator'; @Component({ selector: 'ds-browse-by-date-page', @@ -24,6 +25,7 @@ import { StartsWithType } from '../../shared/starts-with/starts-with-decorator'; * A metadata definition is a short term used to describe one or multiple metadata fields. * An example would be 'dateissued' for 'dc.date.issued' */ +@rendersBrowseBy(BrowseByType.Date) export class BrowseByDatePageComponent extends BrowseByMetadataPageComponent { /** @@ -78,8 +80,9 @@ export class BrowseByDatePageComponent extends BrowseByMetadataPageComponent { let lowerLimit = this.config.browseBy.defaultLowerLimit; if (hasValue(firstItemRD.payload)) { const date = firstItemRD.payload.firstMetadataValue(metadataField); - if (hasValue(date) && hasValue(+date.split('-')[0])) { - lowerLimit = +date.split('-')[0]; + if (hasValue(date)) { + const dateObj = new Date(date); + lowerLimit = dateObj.getFullYear(); } } const options = []; 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 52c63341bd..7a85cc258f 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 @@ -15,6 +15,7 @@ import { DSpaceObjectDataService } from '../../core/data/dspace-object-data.serv import { DSpaceObject } from '../../core/shared/dspace-object.model'; import { take } from 'rxjs/operators'; import { StartsWithType } from '../../shared/starts-with/starts-with-decorator'; +import { BrowseByType, rendersBrowseBy } from '../+browse-by-switcher/browse-by-decorator'; @Component({ selector: 'ds-browse-by-metadata-page', @@ -26,6 +27,7 @@ import { StartsWithType } from '../../shared/starts-with/starts-with-decorator'; * A metadata definition is a short term used to describe one or multiple metadata fields. * An example would be 'author' for 'dc.contributor.*' */ +@rendersBrowseBy(BrowseByType.Metadata) export class BrowseByMetadataPageComponent implements OnInit { /** diff --git a/src/app/+browse-by/+browse-by-switcher/browse-by-decorator.ts b/src/app/+browse-by/+browse-by-switcher/browse-by-decorator.ts new file mode 100644 index 0000000000..fe61a2edd2 --- /dev/null +++ b/src/app/+browse-by/+browse-by-switcher/browse-by-decorator.ts @@ -0,0 +1,29 @@ +import { hasNoValue } from '../../shared/empty.util'; + +export enum BrowseByType { + Title = 'title', + Metadata = 'metadata', + Date = 'date' +} + +export const DEFAULT_BROWSE_BY_TYPE = BrowseByType.Metadata; + +const map = new Map(); + +export function rendersBrowseBy(browseByType: BrowseByType) { + return function decorator(component: any) { + if (hasNoValue(map.get(browseByType))) { + map.set(browseByType, component); + } else { + throw new Error(`There can't be more than one component to render Browse-By of type "${browseByType}"`); + } + }; +} + +export function getComponentByBrowseByType(browseByType) { + const comp = map.get(browseByType); + if (hasNoValue(comp)) { + map.get(DEFAULT_BROWSE_BY_TYPE); + } + return comp; +} diff --git a/src/app/+browse-by/+browse-by-switcher/browse-by-switcher.component.html b/src/app/+browse-by/+browse-by-switcher/browse-by-switcher.component.html new file mode 100644 index 0000000000..c7eab882d4 --- /dev/null +++ b/src/app/+browse-by/+browse-by-switcher/browse-by-switcher.component.html @@ -0,0 +1 @@ + diff --git a/src/app/+browse-by/+browse-by-switcher/browse-by-switcher.component.ts b/src/app/+browse-by/+browse-by-switcher/browse-by-switcher.component.ts new file mode 100644 index 0000000000..c3af4ddf52 --- /dev/null +++ b/src/app/+browse-by/+browse-by-switcher/browse-by-switcher.component.ts @@ -0,0 +1,36 @@ +import { Component, Inject, OnInit } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { Observable } from 'rxjs/internal/Observable'; +import { GLOBAL_CONFIG, GlobalConfig } from '../../../config'; +import { BrowseByTypeConfig } from '../../../config/browse-by-type-config.interface'; +import { map, tap } from 'rxjs/operators'; +import { getComponentByBrowseByType } from './browse-by-decorator'; + +@Component({ + selector: 'ds-browse-by-switcher', + templateUrl: './browse-by-switcher.component.html' +}) +export class BrowseBySwitcherComponent implements OnInit { + + browseByTypeConfig: Observable; + + public constructor(@Inject(GLOBAL_CONFIG) public config: GlobalConfig, + protected route: ActivatedRoute) { + } + + ngOnInit(): void { + this.browseByTypeConfig = this.route.params.pipe( + map((params) => { + const metadata = params.metadata; + return this.config.browseBy.types.find((config: BrowseByTypeConfig) => config.metadata === metadata); + }) + ); + } + + getComponent() { + return this.browseByTypeConfig.pipe( + map((config: BrowseByTypeConfig) => getComponentByBrowseByType(config.type)) + ); + } + +} 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 717275bf8b..92f9569d38 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,6 +1,5 @@ import { combineLatest as observableCombineLatest } from 'rxjs'; import { Component } from '@angular/core'; -import { ItemDataService } from '../../core/data/item-data.service'; import { ActivatedRoute, Router } from '@angular/router'; import { hasValue } from '../../shared/empty.util'; import { @@ -11,6 +10,7 @@ import { BrowseEntrySearchOptions } from '../../core/browse/browse-entry-search- import { DSpaceObjectDataService } from '../../core/data/dspace-object-data.service'; import { BrowseService } from '../../core/browse/browse.service'; import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model'; +import { BrowseByType, rendersBrowseBy } from '../+browse-by-switcher/browse-by-decorator'; @Component({ selector: 'ds-browse-by-title-page', @@ -20,6 +20,7 @@ import { SortDirection, SortOptions } from '../../core/cache/models/sort-options /** * Component for browsing items by title (dc.title) */ +@rendersBrowseBy(BrowseByType.Title) export class BrowseByTitlePageComponent extends BrowseByMetadataPageComponent { public constructor(protected route: ActivatedRoute, diff --git a/src/app/+browse-by/browse-by-guard.ts b/src/app/+browse-by/browse-by-guard.ts index 5d3dad2b0f..520d23c772 100644 --- a/src/app/+browse-by/browse-by-guard.ts +++ b/src/app/+browse-by/browse-by-guard.ts @@ -1,11 +1,12 @@ import { ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot } from '@angular/router'; -import { Injectable } from '@angular/core'; +import { Inject, Injectable } from '@angular/core'; import { DSpaceObjectDataService } from '../core/data/dspace-object-data.service'; -import { hasValue } from '../shared/empty.util'; +import { hasNoValue, hasValue } from '../shared/empty.util'; import { map } from 'rxjs/operators'; import { getSucceededRemoteData } from '../core/shared/operators'; import { TranslateService } from '@ngx-translate/core'; import { of as observableOf } from 'rxjs'; +import { GLOBAL_CONFIG, GlobalConfig } from '../../config'; @Injectable() /** @@ -13,14 +14,21 @@ import { of as observableOf } from 'rxjs'; */ export class BrowseByGuard implements CanActivate { - constructor(protected dsoService: DSpaceObjectDataService, + constructor(@Inject(GLOBAL_CONFIG) public config: GlobalConfig, + protected dsoService: DSpaceObjectDataService, protected translate: TranslateService) { } canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) { const title = route.data.title; const metadata = route.params.metadata || route.queryParams.metadata || route.data.metadata; - const metadataField = route.data.metadataField; + let metadataField = route.data.metadataField; + if (hasNoValue(metadataField) && hasValue(metadata)) { + const config = this.config.browseBy.types.find((conf) => conf.metadata === metadata); + if (hasValue(config) && hasValue(config.metadataField)) { + metadataField = config.metadataField; + } + } const scope = route.queryParams.scope; const value = route.queryParams.value; const metadataTranslated = this.translate.instant('browse.metadata.' + metadata); diff --git a/src/app/+browse-by/browse-by-routing.module.ts b/src/app/+browse-by/browse-by-routing.module.ts index 9ba15ecfe9..0b20cd4466 100644 --- a/src/app/+browse-by/browse-by-routing.module.ts +++ b/src/app/+browse-by/browse-by-routing.module.ts @@ -1,16 +1,12 @@ 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'; import { BrowseByGuard } from './browse-by-guard'; +import { BrowseBySwitcherComponent } from './+browse-by-switcher/browse-by-switcher.component'; @NgModule({ imports: [ RouterModule.forChild([ - { path: 'title', component: BrowseByTitlePageComponent, canActivate: [BrowseByGuard], data: { metadata: 'title', title: 'browse.title' } }, - { path: 'dateissued', component: BrowseByDatePageComponent, canActivate: [BrowseByGuard], data: { metadata: 'dateissued', metadataField: 'dc.date.issued', title: 'browse.title' } }, - { path: ':metadata', component: BrowseByMetadataPageComponent, canActivate: [BrowseByGuard], data: { title: 'browse.title' } } + { path: ':metadata', component: BrowseBySwitcherComponent, canActivate: [BrowseByGuard], data: { title: 'browse.title' } } ]) ] }) diff --git a/src/app/+browse-by/browse-by.module.ts b/src/app/+browse-by/browse-by.module.ts index 30d4617c16..4938b0df15 100644 --- a/src/app/+browse-by/browse-by.module.ts +++ b/src/app/+browse-by/browse-by.module.ts @@ -8,6 +8,7 @@ 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'; import { BrowseByGuard } from './browse-by-guard'; +import { BrowseBySwitcherComponent } from './+browse-by-switcher/browse-by-switcher.component'; @NgModule({ imports: [ @@ -18,12 +19,18 @@ import { BrowseByGuard } from './browse-by-guard'; declarations: [ BrowseByTitlePageComponent, BrowseByMetadataPageComponent, - BrowseByDatePageComponent + BrowseByDatePageComponent, + BrowseBySwitcherComponent ], providers: [ ItemDataService, BrowseService, BrowseByGuard + ], + entryComponents: [ + BrowseByTitlePageComponent, + BrowseByMetadataPageComponent, + BrowseByDatePageComponent ] }) export class BrowseByModule { diff --git a/src/config/browse-by-config.interface.ts b/src/config/browse-by-config.interface.ts index 6adba66b92..dec2f1bcf5 100644 --- a/src/config/browse-by-config.interface.ts +++ b/src/config/browse-by-config.interface.ts @@ -1,4 +1,5 @@ import { Config } from './config.interface'; +import { BrowseByTypeConfig } from './browse-by-type-config.interface'; /** * Config that determines how the dropdown list of years are created for browse-by-date components @@ -18,4 +19,6 @@ export interface BrowseByConfig extends Config { * The absolute lowest year to display in the dropdown when no lowest date can be found for all items */ defaultLowerLimit: number; + + types: BrowseByTypeConfig[]; } diff --git a/src/config/browse-by-type-config.interface.ts b/src/config/browse-by-type-config.interface.ts new file mode 100644 index 0000000000..d9ecc14704 --- /dev/null +++ b/src/config/browse-by-type-config.interface.ts @@ -0,0 +1,8 @@ +import { Config } from './config.interface'; +import { BrowseByType } from '../app/+browse-by/+browse-by-switcher/browse-by-decorator'; + +export interface BrowseByTypeConfig extends Config { + metadata: string; + type: BrowseByType; + metadataField: string; +} From 41e55d8d448f19871845f1f0a070b1c86d5e723a Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Tue, 30 Apr 2019 16:54:50 +0200 Subject: [PATCH 2/7] 62063: Configurable Browse-By menus --- resources/i18n/en.json | 2 +- src/app/navbar/navbar.component.ts | 66 ++++++------------- .../comcol-page-browse-by.component.html | 7 +- .../comcol-page-browse-by.component.ts | 16 ++++- 4 files changed, 37 insertions(+), 54 deletions(-) diff --git a/resources/i18n/en.json b/resources/i18n/en.json index 875a624c7e..3b6062fa91 100644 --- a/resources/i18n/en.json +++ b/resources/i18n/en.json @@ -555,7 +555,7 @@ "control_panel": "Control Panel", "browse_global": "All of DSpace", "browse_global_communities_and_collections": "Communities & Collections", - "browse_global_by_issue_date": "By Issue Date", + "browse_global_by_dateissued": "By Issue Date", "browse_global_by_author": "By Author", "browse_global_by_title": "By Title", "browse_global_by_subject": "By Subject", diff --git a/src/app/navbar/navbar.component.ts b/src/app/navbar/navbar.component.ts index 008a86599d..a8736fabda 100644 --- a/src/app/navbar/navbar.component.ts +++ b/src/app/navbar/navbar.component.ts @@ -1,4 +1,4 @@ -import { Component, Injector, OnInit } from '@angular/core'; +import { Component, Inject, Injector, OnInit } from '@angular/core'; import { slideMobileNav } from '../shared/animations/slide'; import { MenuComponent } from '../shared/menu/menu.component'; import { MenuService } from '../shared/menu/menu.service'; @@ -6,6 +6,7 @@ import { MenuID, MenuItemType } from '../shared/menu/initial-menus-state'; import { TextMenuItemModel } from '../shared/menu/menu-item/models/text.model'; import { LinkMenuItemModel } from '../shared/menu/menu-item/models/link.model'; import { HostWindowService } from '../shared/host-window.service'; +import { GLOBAL_CONFIG, GlobalConfig } from '../../config'; /** * Component representing the public navbar @@ -23,7 +24,8 @@ export class NavbarComponent extends MenuComponent implements OnInit { */ menuID = MenuID.PUBLIC; - constructor(protected menuService: MenuService, + constructor(@Inject(GLOBAL_CONFIG) public config: GlobalConfig, + protected menuService: MenuService, protected injector: Injector, public windowService: HostWindowService ) { @@ -39,7 +41,7 @@ export class NavbarComponent extends MenuComponent implements OnInit { * Initialize all menu sections and items for this menu */ createMenu() { - const menuList = [ + const menuList: any[] = [ /* News */ { id: 'browse_global', @@ -62,50 +64,6 @@ export class NavbarComponent extends MenuComponent implements OnInit { // link: '#' // } as LinkMenuItemModel, // }, - { - id: 'browse_global_global_by_title', - parentID: 'browse_global', - active: false, - visible: true, - model: { - type: MenuItemType.LINK, - text: 'menu.section.browse_global_by_title', - 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', - active: false, - visible: true, - model: { - type: MenuItemType.LINK, - text: 'menu.section.browse_global_by_author', - link: '/browse/author' - } as LinkMenuItemModel, - }, - { - id: 'browse_global_by_subject', - parentID: 'browse_global', - active: false, - visible: true, - model: { - type: MenuItemType.LINK, - text: 'menu.section.browse_global_by_subject', - link: '/browse/subject' - } as LinkMenuItemModel, - }, /* Statistics */ { @@ -120,6 +78,20 @@ export class NavbarComponent extends MenuComponent implements OnInit { index: 2 }, ]; + const types = this.config.browseBy.types; + types.forEach((typeConfig) => { + menuList.push({ + id: `browse_global_by_${typeConfig.metadata}`, + parentID: 'browse_global', + active: false, + visible: true, + model: { + type: MenuItemType.LINK, + text: `menu.section.browse_global_by_${typeConfig.metadata}`, + link: `/browse/${typeConfig.metadata}` + } as LinkMenuItemModel + }); + }); menuList.forEach((menuSection) => this.menuService.addSection(this.menuID, menuSection)); } 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 f9ef4e5232..9165471843 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,7 +1,6 @@

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

diff --git a/src/app/shared/comcol-page-browse-by/comcol-page-browse-by.component.ts b/src/app/shared/comcol-page-browse-by/comcol-page-browse-by.component.ts index 85d40a77e0..6b26d9f438 100644 --- a/src/app/shared/comcol-page-browse-by/comcol-page-browse-by.component.ts +++ b/src/app/shared/comcol-page-browse-by/comcol-page-browse-by.component.ts @@ -1,4 +1,6 @@ -import { Component, Input } from '@angular/core'; +import { Component, Inject, Input, OnInit } from '@angular/core'; +import { GLOBAL_CONFIG, GlobalConfig } from '../../../config'; +import { BrowseByTypeConfig } from '../../../config/browse-by-type-config.interface'; /** * A component to display the "Browse By" section of a Community or Collection page @@ -8,9 +10,19 @@ import { Component, Input } from '@angular/core'; selector: 'ds-comcol-page-browse-by', templateUrl: './comcol-page-browse-by.component.html', }) -export class ComcolPageBrowseByComponent { +export class ComcolPageBrowseByComponent implements OnInit { /** * The ID of the Community or Collection */ @Input() id: string; + + types: BrowseByTypeConfig[]; + + constructor(@Inject(GLOBAL_CONFIG) public config: GlobalConfig) { + } + + ngOnInit(): void { + this.types = this.config.browseBy.types; + } + } From 369a6dfaea920be93c3e69cd9dbabd22ef6d643f Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Tue, 30 Apr 2019 17:24:58 +0200 Subject: [PATCH 3/7] 62063: JSDocs and comments --- config/environment.default.js | 6 ++++++ .../+browse-by-switcher/browse-by-decorator.ts | 8 ++++++++ .../browse-by-switcher.component.ts | 12 ++++++++++++ src/app/navbar/navbar.component.ts | 1 + .../comcol-page-browse-by.component.ts | 3 +++ src/config/browse-by-config.interface.ts | 3 +++ src/config/browse-by-type-config.interface.ts | 15 +++++++++++++++ 7 files changed, 48 insertions(+) diff --git a/config/environment.default.js b/config/environment.default.js index 3013705a2e..4fccc92889 100644 --- a/config/environment.default.js +++ b/config/environment.default.js @@ -150,6 +150,12 @@ module.exports = { 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, + // List of all the active Browse-By types + // Adding a type will activate their Browse-By page and add them to the global navigation menu, as well as community and collection pages + // Allowed fields and their purpose: + // metadata: The browse id to use for fetching info from the rest api + // type: The type of Browse-By page to display + // metadataField: The metadata-field used to create starts-with options (only necessary when the type is set to 'date') types: [ { metadata: 'title', diff --git a/src/app/+browse-by/+browse-by-switcher/browse-by-decorator.ts b/src/app/+browse-by/+browse-by-switcher/browse-by-decorator.ts index fe61a2edd2..0143377922 100644 --- a/src/app/+browse-by/+browse-by-switcher/browse-by-decorator.ts +++ b/src/app/+browse-by/+browse-by-switcher/browse-by-decorator.ts @@ -10,6 +10,10 @@ export const DEFAULT_BROWSE_BY_TYPE = BrowseByType.Metadata; const map = new Map(); +/** + * Decorator used for rendering Browse-By pages by type + * @param browseByType The type of page + */ export function rendersBrowseBy(browseByType: BrowseByType) { return function decorator(component: any) { if (hasNoValue(map.get(browseByType))) { @@ -20,6 +24,10 @@ export function rendersBrowseBy(browseByType: BrowseByType) { }; } +/** + * Get the component used for rendering a Browse-By page by type + * @param browseByType The type of page + */ export function getComponentByBrowseByType(browseByType) { const comp = map.get(browseByType); if (hasNoValue(comp)) { diff --git a/src/app/+browse-by/+browse-by-switcher/browse-by-switcher.component.ts b/src/app/+browse-by/+browse-by-switcher/browse-by-switcher.component.ts index c3af4ddf52..b8652509b3 100644 --- a/src/app/+browse-by/+browse-by-switcher/browse-by-switcher.component.ts +++ b/src/app/+browse-by/+browse-by-switcher/browse-by-switcher.component.ts @@ -10,14 +10,23 @@ import { getComponentByBrowseByType } from './browse-by-decorator'; selector: 'ds-browse-by-switcher', templateUrl: './browse-by-switcher.component.html' }) +/** + * Component for determining what Browse-By component to use depending on the metadata (browse ID) provided + */ export class BrowseBySwitcherComponent implements OnInit { + /** + * Resolved browse config + */ browseByTypeConfig: Observable; public constructor(@Inject(GLOBAL_CONFIG) public config: GlobalConfig, protected route: ActivatedRoute) { } + /** + * Fetch the correct browse config from environment.js + */ ngOnInit(): void { this.browseByTypeConfig = this.route.params.pipe( map((params) => { @@ -27,6 +36,9 @@ export class BrowseBySwitcherComponent implements OnInit { ); } + /** + * Fetch the component depending on the browse type + */ getComponent() { return this.browseByTypeConfig.pipe( map((config: BrowseByTypeConfig) => getComponentByBrowseByType(config.type)) diff --git a/src/app/navbar/navbar.component.ts b/src/app/navbar/navbar.component.ts index a8736fabda..bd0a82e1e9 100644 --- a/src/app/navbar/navbar.component.ts +++ b/src/app/navbar/navbar.component.ts @@ -78,6 +78,7 @@ export class NavbarComponent extends MenuComponent implements OnInit { index: 2 }, ]; + // Read the different Browse-By types from config and add them to the browse menu const types = this.config.browseBy.types; types.forEach((typeConfig) => { menuList.push({ diff --git a/src/app/shared/comcol-page-browse-by/comcol-page-browse-by.component.ts b/src/app/shared/comcol-page-browse-by/comcol-page-browse-by.component.ts index 6b26d9f438..dcc7840bb4 100644 --- a/src/app/shared/comcol-page-browse-by/comcol-page-browse-by.component.ts +++ b/src/app/shared/comcol-page-browse-by/comcol-page-browse-by.component.ts @@ -16,6 +16,9 @@ export class ComcolPageBrowseByComponent implements OnInit { */ @Input() id: string; + /** + * List of currently active browse configurations + */ types: BrowseByTypeConfig[]; constructor(@Inject(GLOBAL_CONFIG) public config: GlobalConfig) { diff --git a/src/config/browse-by-config.interface.ts b/src/config/browse-by-config.interface.ts index dec2f1bcf5..719e127b4b 100644 --- a/src/config/browse-by-config.interface.ts +++ b/src/config/browse-by-config.interface.ts @@ -20,5 +20,8 @@ export interface BrowseByConfig extends Config { */ defaultLowerLimit: number; + /** + * A list of all the active Browse-By pages + */ types: BrowseByTypeConfig[]; } diff --git a/src/config/browse-by-type-config.interface.ts b/src/config/browse-by-type-config.interface.ts index d9ecc14704..92e3af20c0 100644 --- a/src/config/browse-by-type-config.interface.ts +++ b/src/config/browse-by-type-config.interface.ts @@ -1,8 +1,23 @@ import { Config } from './config.interface'; import { BrowseByType } from '../app/+browse-by/+browse-by-switcher/browse-by-decorator'; +/** + * Config used for rendering Browse-By pages and links + */ export interface BrowseByTypeConfig extends Config { + /** + * The browse id used for fetching browse data from the rest api + * e.g. author + */ metadata: string; + + /** + * The type of Browse-By page to render + */ type: BrowseByType; + + /** + * The metadata field to use for rendering starts-with options (only necessary when type is set to BrowseByType.Date) + */ metadataField: string; } From af2d376fb983a98c2309ee022fe7e8e38722f2a4 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Mon, 6 May 2019 14:08:55 +0200 Subject: [PATCH 4/7] 62063: Fixed existing tests --- .../+browse-by/+browse-by-switcher/browse-by-decorator.spec.ts | 0 src/app/+browse-by/browse-by-guard.spec.ts | 3 ++- src/app/navbar/navbar.component.spec.ts | 2 ++ 3 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 src/app/+browse-by/+browse-by-switcher/browse-by-decorator.spec.ts diff --git a/src/app/+browse-by/+browse-by-switcher/browse-by-decorator.spec.ts b/src/app/+browse-by/+browse-by-switcher/browse-by-decorator.spec.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/app/+browse-by/browse-by-guard.spec.ts b/src/app/+browse-by/browse-by-guard.spec.ts index 784f9ea22d..30dd7b7e0a 100644 --- a/src/app/+browse-by/browse-by-guard.spec.ts +++ b/src/app/+browse-by/browse-by-guard.spec.ts @@ -1,6 +1,7 @@ import { first } from 'rxjs/operators'; import { BrowseByGuard } from './browse-by-guard'; import { of as observableOf } from 'rxjs'; +import { ENV_CONFIG } from '../../config'; describe('BrowseByGuard', () => { describe('canActivate', () => { @@ -24,7 +25,7 @@ describe('BrowseByGuard', () => { translateService = { instant: () => field }; - guard = new BrowseByGuard(dsoService, translateService); + guard = new BrowseByGuard(ENV_CONFIG, dsoService, translateService); }); it('should return true, and sets up the data correctly, with a scope and value', () => { diff --git a/src/app/navbar/navbar.component.spec.ts b/src/app/navbar/navbar.component.spec.ts index 2d937fd84e..ca054a662b 100644 --- a/src/app/navbar/navbar.component.spec.ts +++ b/src/app/navbar/navbar.component.spec.ts @@ -11,6 +11,7 @@ import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { Injector, NO_ERRORS_SCHEMA } from '@angular/core'; import { MenuService } from '../shared/menu/menu.service'; import { MenuServiceStub } from '../shared/testing/menu-service-stub'; +import { ENV_CONFIG, GLOBAL_CONFIG } from '../../config'; let comp: NavbarComponent; let fixture: ComponentFixture; @@ -30,6 +31,7 @@ describe('NavbarComponent', () => { { provide: Injector, useValue: {} }, { provide: MenuService, useValue: menuService }, { provide: HostWindowService, useValue: new HostWindowServiceStub(800) }, + { provide: GLOBAL_CONFIG, useValue: ENV_CONFIG } ], schemas: [NO_ERRORS_SCHEMA] }) From 80c7b8608495c709ec69fff018ff0d6ba9ec1114 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Mon, 6 May 2019 15:27:16 +0200 Subject: [PATCH 5/7] 62063: browse-by-switcher + decorator tests --- .../browse-by-decorator.spec.ts | 12 ++++ .../browse-by-switcher.component.spec.ts | 55 +++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 src/app/+browse-by/+browse-by-switcher/browse-by-switcher.component.spec.ts diff --git a/src/app/+browse-by/+browse-by-switcher/browse-by-decorator.spec.ts b/src/app/+browse-by/+browse-by-switcher/browse-by-decorator.spec.ts index e69de29bb2..cd8d715287 100644 --- a/src/app/+browse-by/+browse-by-switcher/browse-by-decorator.spec.ts +++ b/src/app/+browse-by/+browse-by-switcher/browse-by-decorator.spec.ts @@ -0,0 +1,12 @@ +import { BrowseByType, rendersBrowseBy } from './browse-by-decorator'; + +describe('BrowseByDecorator', () => { + const titleDecorator = rendersBrowseBy(BrowseByType.Title); + const dateDecorator = rendersBrowseBy(BrowseByType.Date); + const metadataDecorator = rendersBrowseBy(BrowseByType.Metadata); + it('should have a decorator for all types', () => { + expect(titleDecorator.length).not.toBeNull(); + expect(dateDecorator.length).not.toBeNull(); + expect(metadataDecorator.length).not.toBeNull(); + }); +}); diff --git a/src/app/+browse-by/+browse-by-switcher/browse-by-switcher.component.spec.ts b/src/app/+browse-by/+browse-by-switcher/browse-by-switcher.component.spec.ts new file mode 100644 index 0000000000..35e8961e65 --- /dev/null +++ b/src/app/+browse-by/+browse-by-switcher/browse-by-switcher.component.spec.ts @@ -0,0 +1,55 @@ +import { BrowseBySwitcherComponent } from './browse-by-switcher.component'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { ENV_CONFIG, GLOBAL_CONFIG } from '../../../config'; +import { ActivatedRoute } from '@angular/router'; +import * as decorator from './browse-by-decorator'; +import createSpy = jasmine.createSpy; +import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject'; + +describe('BrowseBySwitcherComponent', () => { + let comp: BrowseBySwitcherComponent; + let fixture: ComponentFixture; + + const types = ENV_CONFIG.browseBy.types; + + const params = new BehaviorSubject(createParamsWithMetadata('initialValue')); + + const activatedRouteStub = { + params: params + }; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ BrowseBySwitcherComponent ], + providers: [ + { provide: GLOBAL_CONFIG, useValue: ENV_CONFIG }, + { provide: ActivatedRoute, useValue: activatedRouteStub } + ], + schemas: [ NO_ERRORS_SCHEMA ] + }).compileComponents(); + })); + + beforeEach(async(() => { + fixture = TestBed.createComponent(BrowseBySwitcherComponent); + comp = fixture.componentInstance; + spyOnProperty(decorator, 'getComponentByBrowseByType').and.returnValue(createSpy('getComponentByItemType')); + })); + + types.forEach((type) => { + describe(`when switching to a browse-by page for "${type.metadata}"`, () => { + beforeEach(() => { + params.next(createParamsWithMetadata(type.metadata)); + fixture.detectChanges(); + }); + + it(`should call getComponentByBrowseByType with type "${type.type}"`, () => { + expect(decorator.getComponentByBrowseByType).toHaveBeenCalledWith(type.type); + }); + }); + }); +}); + +export function createParamsWithMetadata(metadata) { + return { metadata: metadata }; +} From 1fb6b45560c055505843332581eeb3b4f47aab89 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Fri, 24 May 2019 14:12:43 +0200 Subject: [PATCH 6/7] 62063: 62063: Small Issue fixes --- .../browse-by-decorator.spec.ts | 6 +++--- .../browse-by-switcher.component.html | 2 +- .../browse-by-switcher.component.ts | 18 +++++------------- 3 files changed, 9 insertions(+), 17 deletions(-) diff --git a/src/app/+browse-by/+browse-by-switcher/browse-by-decorator.spec.ts b/src/app/+browse-by/+browse-by-switcher/browse-by-decorator.spec.ts index cd8d715287..f54efb9378 100644 --- a/src/app/+browse-by/+browse-by-switcher/browse-by-decorator.spec.ts +++ b/src/app/+browse-by/+browse-by-switcher/browse-by-decorator.spec.ts @@ -5,8 +5,8 @@ describe('BrowseByDecorator', () => { const dateDecorator = rendersBrowseBy(BrowseByType.Date); const metadataDecorator = rendersBrowseBy(BrowseByType.Metadata); it('should have a decorator for all types', () => { - expect(titleDecorator.length).not.toBeNull(); - expect(dateDecorator.length).not.toBeNull(); - expect(metadataDecorator.length).not.toBeNull(); + expect(titleDecorator.length).not.toEqual(0); + expect(dateDecorator.length).not.toEqual(0); + expect(metadataDecorator.length).not.toEqual(0); }); }); diff --git a/src/app/+browse-by/+browse-by-switcher/browse-by-switcher.component.html b/src/app/+browse-by/+browse-by-switcher/browse-by-switcher.component.html index c7eab882d4..afe79cf2b1 100644 --- a/src/app/+browse-by/+browse-by-switcher/browse-by-switcher.component.html +++ b/src/app/+browse-by/+browse-by-switcher/browse-by-switcher.component.html @@ -1 +1 @@ - + diff --git a/src/app/+browse-by/+browse-by-switcher/browse-by-switcher.component.ts b/src/app/+browse-by/+browse-by-switcher/browse-by-switcher.component.ts index b8652509b3..30a436d2d5 100644 --- a/src/app/+browse-by/+browse-by-switcher/browse-by-switcher.component.ts +++ b/src/app/+browse-by/+browse-by-switcher/browse-by-switcher.component.ts @@ -16,31 +16,23 @@ import { getComponentByBrowseByType } from './browse-by-decorator'; export class BrowseBySwitcherComponent implements OnInit { /** - * Resolved browse config + * Resolved browse-by component */ - browseByTypeConfig: Observable; + browseByComponent: Observable; public constructor(@Inject(GLOBAL_CONFIG) public config: GlobalConfig, protected route: ActivatedRoute) { } /** - * Fetch the correct browse config from environment.js + * Fetch the correct browse-by component by using the relevant config from environment.js */ ngOnInit(): void { - this.browseByTypeConfig = this.route.params.pipe( + this.browseByComponent = this.route.params.pipe( map((params) => { const metadata = params.metadata; return this.config.browseBy.types.find((config: BrowseByTypeConfig) => config.metadata === metadata); - }) - ); - } - - /** - * Fetch the component depending on the browse type - */ - getComponent() { - return this.browseByTypeConfig.pipe( + }), map((config: BrowseByTypeConfig) => getComponentByBrowseByType(config.type)) ); } From 1d98b8c29f42d9511ca12520ecfdfa8157372614 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Thu, 11 Jul 2019 13:59:23 +0200 Subject: [PATCH 7/7] 62063: Browse config refactoring property metadata to id --- config/environment.default.js | 10 +++++----- .../browse-by-date-page.component.ts | 8 ++++---- .../browse-by-metadata-page.component.html | 2 +- .../browse-by-metadata-page.component.ts | 14 +++++++------- .../browse-by-switcher.component.spec.ts | 10 +++++----- .../browse-by-switcher.component.ts | 4 ++-- .../browse-by-title-page.component.ts | 4 ++-- src/app/+browse-by/browse-by-guard.spec.ts | 16 ++++++++-------- src/app/+browse-by/browse-by-guard.ts | 16 ++++++++-------- src/app/+browse-by/browse-by-routing.module.ts | 2 +- src/app/navbar/navbar.component.ts | 6 +++--- .../comcol-page-browse-by.component.html | 2 +- src/config/browse-by-type-config.interface.ts | 2 +- 13 files changed, 48 insertions(+), 48 deletions(-) diff --git a/config/environment.default.js b/config/environment.default.js index 5f60335652..84fd7405e2 100644 --- a/config/environment.default.js +++ b/config/environment.default.js @@ -153,25 +153,25 @@ module.exports = { // List of all the active Browse-By types // Adding a type will activate their Browse-By page and add them to the global navigation menu, as well as community and collection pages // Allowed fields and their purpose: - // metadata: The browse id to use for fetching info from the rest api + // id: The browse id to use for fetching info from the rest api // type: The type of Browse-By page to display // metadataField: The metadata-field used to create starts-with options (only necessary when the type is set to 'date') types: [ { - metadata: 'title', + id: 'title', type: 'title' }, { - metadata: 'dateissued', + id: 'dateissued', type: 'date', metadataField: 'dc.date.issued' }, { - metadata: 'author', + id: 'author', type: 'metadata' }, { - metadata: 'subject', + id: 'subject', type: 'metadata' } ] 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 ab22216afa..98d0cc9cd1 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 @@ -22,7 +22,7 @@ import { BrowseByType, rendersBrowseBy } from '../+browse-by-switcher/browse-by- }) /** * 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. + * A metadata definition (a.k.a. browse id) is a short term used to describe one or multiple metadata fields. * An example would be 'dateissued' for 'dc.date.issued' */ @rendersBrowseBy(BrowseByType.Date) @@ -55,12 +55,12 @@ export class BrowseByDatePageComponent extends BrowseByMetadataPageComponent { }) .subscribe((params) => { const metadataField = params.metadataField || this.defaultMetadataField; - this.metadata = params.metadata || this.defaultMetadata; + this.browseId = params.id || this.defaultBrowseId; this.startsWith = +params.startsWith || params.startsWith; - const searchOptions = browseParamsToOptions(params, Object.assign({}), this.sortConfig, this.metadata); + const searchOptions = browseParamsToOptions(params, Object.assign({}), this.sortConfig, this.browseId); this.updatePageWithItems(searchOptions, this.value); this.updateParent(params.scope); - this.updateStartsWithOptions(this.metadata, metadataField, params.scope); + this.updateStartsWithOptions(this.browseId, metadataField, params.scope); })); } 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 cf43f74eb0..c589c543d4 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,7 +1,7 @@