diff --git a/src/app/browse-by/browse-by-page.module.ts b/src/app/browse-by/browse-by-page.module.ts index fea6668b3c..554a6c4f46 100644 --- a/src/app/browse-by/browse-by-page.module.ts +++ b/src/app/browse-by/browse-by-page.module.ts @@ -4,16 +4,21 @@ import { BrowseByModule } from './browse-by.module'; import { ItemDataService } from '../core/data/item-data.service'; import { BrowseService } from '../core/browse/browse.service'; import { BrowseByGuard } from './browse-by-guard'; +import { SharedBrowseByModule } from '../shared/browse-by/shared-browse-by.module'; @NgModule({ imports: [ + SharedBrowseByModule, BrowseByRoutingModule, - BrowseByModule.withEntryComponents() + BrowseByModule.withEntryComponents(), ], providers: [ ItemDataService, BrowseService, - BrowseByGuard + BrowseByGuard, + ], + declarations: [ + ] }) export class BrowseByPageModule { diff --git a/src/app/browse-by/browse-by.module.ts b/src/app/browse-by/browse-by.module.ts index 14e21f8b4c..a7643464dc 100644 --- a/src/app/browse-by/browse-by.module.ts +++ b/src/app/browse-by/browse-by.module.ts @@ -1,7 +1,6 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { BrowseByTitlePageComponent } from './browse-by-title-page/browse-by-title-page.component'; -import { SharedModule } from '../shared/shared.module'; 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 { BrowseBySwitcherComponent } from './browse-by-switcher/browse-by-switcher.component'; @@ -10,6 +9,7 @@ import { ComcolModule } from '../shared/comcol/comcol.module'; import { ThemedBrowseByMetadataPageComponent } from './browse-by-metadata-page/themed-browse-by-metadata-page.component'; import { ThemedBrowseByDatePageComponent } from './browse-by-date-page/themed-browse-by-date-page.component'; import { ThemedBrowseByTitlePageComponent } from './browse-by-title-page/themed-browse-by-title-page.component'; +import { SharedBrowseByModule } from '../shared/browse-by/shared-browse-by.module'; const ENTRY_COMPONENTS = [ // put only entry components that use custom decorator @@ -25,9 +25,9 @@ const ENTRY_COMPONENTS = [ @NgModule({ imports: [ + SharedBrowseByModule, CommonModule, ComcolModule, - SharedModule ], declarations: [ BrowseBySwitcherComponent, @@ -45,7 +45,7 @@ export class BrowseByModule { */ static withEntryComponents() { return { - ngModule: SharedModule, + ngModule: SharedBrowseByModule, providers: ENTRY_COMPONENTS.map((component) => ({provide: component})) }; } diff --git a/src/app/entity-groups/journal-entities/item-pages/journal-issue/journal-issue.component.html b/src/app/entity-groups/journal-entities/item-pages/journal-issue/journal-issue.component.html index bf73a62447..2d1f9b477e 100644 --- a/src/app/entity-groups/journal-entities/item-pages/journal-issue/journal-issue.component.html +++ b/src/app/entity-groups/journal-entities/item-pages/journal-issue/journal-issue.component.html @@ -1,3 +1,4 @@ +
diff --git a/src/app/entity-groups/journal-entities/item-pages/journal-issue/journal-issue.component.scss b/src/app/entity-groups/journal-entities/item-pages/journal-issue/journal-issue.component.scss index 3575cae797..b810d2ddd4 100644 --- a/src/app/entity-groups/journal-entities/item-pages/journal-issue/journal-issue.component.scss +++ b/src/app/entity-groups/journal-entities/item-pages/journal-issue/journal-issue.component.scss @@ -1 +1,2 @@ @import '../../../../../styles/variables.scss'; + diff --git a/src/app/entity-groups/journal-entities/item-pages/journal-volume/journal-volume.component.html b/src/app/entity-groups/journal-entities/item-pages/journal-volume/journal-volume.component.html index b370431a27..010b5ee5b5 100644 --- a/src/app/entity-groups/journal-entities/item-pages/journal-volume/journal-volume.component.html +++ b/src/app/entity-groups/journal-entities/item-pages/journal-volume/journal-volume.component.html @@ -1,3 +1,4 @@ +
diff --git a/src/app/entity-groups/journal-entities/item-pages/journal/journal.component.html b/src/app/entity-groups/journal-entities/item-pages/journal/journal.component.html index 27ee373237..88ed9e17dd 100644 --- a/src/app/entity-groups/journal-entities/item-pages/journal/journal.component.html +++ b/src/app/entity-groups/journal-entities/item-pages/journal/journal.component.html @@ -1,3 +1,4 @@ +
diff --git a/src/app/entity-groups/journal-entities/item-pages/journal/journal.component.spec.ts b/src/app/entity-groups/journal-entities/item-pages/journal/journal.component.spec.ts index 4469a2ac29..7e20edca6b 100644 --- a/src/app/entity-groups/journal-entities/item-pages/journal/journal.component.spec.ts +++ b/src/app/entity-groups/journal-entities/item-pages/journal/journal.component.spec.ts @@ -34,6 +34,7 @@ import { VersionHistoryDataService } from '../../../../core/data/version-history import { VersionDataService } from '../../../../core/data/version-data.service'; import { WorkspaceitemDataService } from '../../../../core/submission/workspaceitem-data.service'; import { SearchService } from '../../../../core/shared/search/search.service'; +import { mockRouteService } from '../../../../item-page/simple/item-types/shared/item.component.spec'; let comp: JournalComponent; let fixture: ComponentFixture; @@ -99,7 +100,7 @@ describe('JournalComponent', () => { { provide: BitstreamDataService, useValue: mockBitstreamDataService }, { provide: WorkspaceitemDataService, useValue: {} }, { provide: SearchService, useValue: {} }, - { provide: RouteService, useValue: {} } + { provide: RouteService, useValue: mockRouteService } ], schemas: [NO_ERRORS_SCHEMA] diff --git a/src/app/entity-groups/journal-entities/journal-entities.module.ts b/src/app/entity-groups/journal-entities/journal-entities.module.ts index 3bf861e10d..07585d4d16 100644 --- a/src/app/entity-groups/journal-entities/journal-entities.module.ts +++ b/src/app/entity-groups/journal-entities/journal-entities.module.ts @@ -20,6 +20,7 @@ import { JournalVolumeSidebarSearchListElementComponent } from './item-list-elem import { JournalIssueSidebarSearchListElementComponent } from './item-list-elements/sidebar-search-list-elements/journal-issue/journal-issue-sidebar-search-list-element.component'; import { JournalSidebarSearchListElementComponent } from './item-list-elements/sidebar-search-list-elements/journal/journal-sidebar-search-list-element.component'; import { ItemSharedModule } from '../../item-page/item-shared.module'; +import { ResultsBackButtonModule } from '../../shared/results-back-button/results-back-button.module'; const ENTRY_COMPONENTS = [ // put only entry components that use custom decorator @@ -47,7 +48,8 @@ const ENTRY_COMPONENTS = [ imports: [ CommonModule, ItemSharedModule, - SharedModule + SharedModule, + ResultsBackButtonModule ], declarations: [ ...ENTRY_COMPONENTS diff --git a/src/app/entity-groups/research-entities/item-pages/org-unit/org-unit.component.html b/src/app/entity-groups/research-entities/item-pages/org-unit/org-unit.component.html index ac92e4ad6d..d8b3815f6e 100644 --- a/src/app/entity-groups/research-entities/item-pages/org-unit/org-unit.component.html +++ b/src/app/entity-groups/research-entities/item-pages/org-unit/org-unit.component.html @@ -1,3 +1,4 @@ +
diff --git a/src/app/entity-groups/research-entities/item-pages/person/person.component.html b/src/app/entity-groups/research-entities/item-pages/person/person.component.html index a73236006b..20e458a906 100644 --- a/src/app/entity-groups/research-entities/item-pages/person/person.component.html +++ b/src/app/entity-groups/research-entities/item-pages/person/person.component.html @@ -1,3 +1,4 @@ +
diff --git a/src/app/entity-groups/research-entities/item-pages/project/project.component.html b/src/app/entity-groups/research-entities/item-pages/project/project.component.html index 3fb5e2d520..ee75abaae8 100644 --- a/src/app/entity-groups/research-entities/item-pages/project/project.component.html +++ b/src/app/entity-groups/research-entities/item-pages/project/project.component.html @@ -1,3 +1,4 @@ +
diff --git a/src/app/entity-groups/research-entities/research-entities.module.ts b/src/app/entity-groups/research-entities/research-entities.module.ts index 721a22be08..e3525cc881 100644 --- a/src/app/entity-groups/research-entities/research-entities.module.ts +++ b/src/app/entity-groups/research-entities/research-entities.module.ts @@ -29,6 +29,7 @@ import { OrgUnitSidebarSearchListElementComponent } from './item-list-elements/s import { PersonSidebarSearchListElementComponent } from './item-list-elements/sidebar-search-list-elements/person/person-sidebar-search-list-element.component'; import { ProjectSidebarSearchListElementComponent } from './item-list-elements/sidebar-search-list-elements/project/project-sidebar-search-list-element.component'; import { ItemSharedModule } from '../../item-page/item-shared.module'; +import { ResultsBackButtonModule } from '../../shared/results-back-button/results-back-button.module'; const ENTRY_COMPONENTS = [ // put only entry components that use custom decorator @@ -69,7 +70,8 @@ const COMPONENTS = [ CommonModule, ItemSharedModule, SharedModule, - NgbTooltipModule + NgbTooltipModule, + ResultsBackButtonModule ], declarations: [ ...COMPONENTS, diff --git a/src/app/item-page/item-page.module.ts b/src/app/item-page/item-page.module.ts index 39c2580921..53c679e596 100644 --- a/src/app/item-page/item-page.module.ts +++ b/src/app/item-page/item-page.module.ts @@ -47,6 +47,7 @@ import { NgbAccordionModule } from '@ng-bootstrap/ng-bootstrap'; import { OrcidSyncSettingsComponent } from './orcid-page/orcid-sync-settings/orcid-sync-settings.component'; import { OrcidQueueComponent } from './orcid-page/orcid-queue/orcid-queue.component'; import { UploadModule } from '../shared/upload/upload.module'; +import { ResultsBackButtonModule } from '../shared/results-back-button/results-back-button.module'; import { ItemAlertsComponent } from './alerts/item-alerts.component'; import { ItemVersionsModule } from './versions/item-versions.module'; import { BitstreamRequestACopyPageComponent } from './bitstreams/request-a-copy/bitstream-request-a-copy-page.component'; @@ -107,7 +108,8 @@ const DECLARATIONS = [ ResearchEntitiesModule.withEntryComponents(), NgxGalleryModule, NgbAccordionModule, - UploadModule, + ResultsBackButtonModule, + UploadModule ], declarations: [ ...DECLARATIONS, diff --git a/src/app/item-page/simple/item-types/publication/publication.component.html b/src/app/item-page/simple/item-types/publication/publication.component.html index 181480b789..9b7861c73c 100644 --- a/src/app/item-page/simple/item-types/publication/publication.component.html +++ b/src/app/item-page/simple/item-types/publication/publication.component.html @@ -1,3 +1,4 @@ +
{ describe('with IIIF viewer and search', () => { + const localMockRouteService = { + getPreviousUrl(): Observable { + return of('/search?query=test%20query&fakeParam=true'); + } + }; beforeEach(waitForAsync(() => { - const localMockRouteService = { - getPreviousUrl(): Observable { - return of('/search?query=test%20query&fakeParam=true'); - } - }; const iiifEnabledMap: MetadataMap = { 'dspace.iiif.enabled': [getIIIFEnabled(true)], 'iiif.search.enabled': [getIIIFSearchEnabled(true)], @@ -193,13 +193,12 @@ describe('PublicationComponent', () => { }); describe('with IIIF viewer and search but no previous search query', () => { - + const localMockRouteService = { + getPreviousUrl(): Observable { + return of('/item'); + } + }; beforeEach(waitForAsync(() => { - const localMockRouteService = { - getPreviousUrl(): Observable { - return of('/item'); - } - }; const iiifEnabledMap: MetadataMap = { 'dspace.iiif.enabled': [getIIIFEnabled(true)], 'iiif.search.enabled': [getIIIFSearchEnabled(true)], diff --git a/src/app/item-page/simple/item-types/shared/item.component.spec.ts b/src/app/item-page/simple/item-types/shared/item.component.spec.ts index c479f04809..cb91a31b06 100644 --- a/src/app/item-page/simple/item-types/shared/item.component.spec.ts +++ b/src/app/item-page/simple/item-types/shared/item.component.spec.ts @@ -39,6 +39,11 @@ import { RouterTestingModule } from '@angular/router/testing'; import { AuthorizationDataService } from '../../../../core/data/feature-authorization/authorization-data.service'; import { ResearcherProfileDataService } from '../../../../core/profile/researcher-profile-data.service'; +import { buildPaginatedList } from '../../../../core/data/paginated-list.model'; +import { PageInfo } from '../../../../core/shared/page-info.model'; +import { Router } from '@angular/router'; +import { ItemComponent } from './item.component'; + export function getIIIFSearchEnabled(enabled: boolean): MetadataValue { return Object.assign(new MetadataValue(), { 'value': enabled, @@ -59,7 +64,11 @@ export function getIIIFEnabled(enabled: boolean): MetadataValue { }); } -export const mockRouteService = jasmine.createSpyObj('RouteService', ['getPreviousUrl']); +export const mockRouteService = { + getPreviousUrl(): Observable { + return observableOf(''); + } +}; /** * Create a generic test for an item-page-fields component using a mockItem and the type of component @@ -114,7 +123,7 @@ export function getItemPageFieldsTest(mockItem: Item, component) { { provide: BitstreamDataService, useValue: mockBitstreamDataService }, { provide: WorkspaceitemDataService, useValue: {} }, { provide: SearchService, useValue: {} }, - { provide: RouteService, useValue: {} }, + { provide: RouteService, useValue: mockRouteService }, { provide: AuthorizationDataService, useValue: authorizationService }, { provide: ResearcherProfileDataService, useValue: {} } ], @@ -376,4 +385,110 @@ describe('ItemComponent', () => { }); }); + const mockItem: Item = Object.assign(new Item(), { + bundles: createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), [])), + metadata: { + 'publicationissue.issueNumber': [ + { + language: 'en_US', + value: '1234' + } + ], + 'dc.description': [ + { + language: 'en_US', + value: 'desc' + } + ] + }, + }); + describe('back to results', () => { + let comp: ItemComponent; + let fixture: ComponentFixture; + let router: Router; + + const searchUrl = '/search?query=test&spc.page=2'; + const browseUrl = '/browse/title?scope=0cc&bbm.page=3'; + const recentSubmissionsUrl = '/collections/be7b8430-77a5-4016-91c9-90863e50583a?cp.page=3'; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + imports: [ + TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: TranslateLoaderMock + } + }), + RouterTestingModule, + ], + declarations: [ItemComponent, GenericItemPageFieldComponent, TruncatePipe ], + providers: [ + { provide: ItemDataService, useValue: {} }, + { provide: TruncatableService, useValue: {} }, + { provide: RelationshipDataService, useValue: {} }, + { provide: ObjectCacheService, useValue: {} }, + { provide: UUIDService, useValue: {} }, + { provide: Store, useValue: {} }, + { provide: RemoteDataBuildService, useValue: {} }, + { provide: CommunityDataService, useValue: {} }, + { provide: HALEndpointService, useValue: {} }, + { provide: HttpClient, useValue: {} }, + { provide: DSOChangeAnalyzer, useValue: {} }, + { provide: VersionHistoryDataService, useValue: {} }, + { provide: VersionDataService, useValue: {} }, + { provide: NotificationsService, useValue: {} }, + { provide: DefaultChangeAnalyzer, useValue: {} }, + { provide: BitstreamDataService, useValue: {} }, + { provide: WorkspaceitemDataService, useValue: {} }, + { provide: SearchService, useValue: {} }, + { provide: RouteService, useValue: mockRouteService }, + { provide: AuthorizationDataService, useValue: {} }, + { provide: ResearcherProfileDataService, useValue: {} } + ], + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(ItemComponent, { + set: {changeDetection: ChangeDetectionStrategy.Default} + }); + })); + + beforeEach(waitForAsync(() => { + router = TestBed.inject(Router); + spyOn(router, 'navigateByUrl'); + TestBed.compileComponents(); + fixture = TestBed.createComponent(ItemComponent); + comp = fixture.componentInstance; + comp.object = mockItem; + fixture.detectChanges(); + })); + + it('should hide back button',() => { + spyOn(mockRouteService, 'getPreviousUrl').and.returnValue(observableOf('/item')); + comp.showBackButton.subscribe((val) => { + expect(val).toBeFalse(); + }); + }); + it('should show back button for search', () => { + spyOn(mockRouteService, 'getPreviousUrl').and.returnValue(observableOf(searchUrl)); + comp.ngOnInit(); + comp.showBackButton.subscribe((val) => { + expect(val).toBeTrue(); + }); + }); + it('should show back button for browse', () => { + spyOn(mockRouteService, 'getPreviousUrl').and.returnValue(observableOf(browseUrl)); + comp.ngOnInit(); + comp.showBackButton.subscribe((val) => { + expect(val).toBeTrue(); + }); + }); + it('should show back button for recent submissions', () => { + spyOn(mockRouteService, 'getPreviousUrl').and.returnValue(observableOf(recentSubmissionsUrl)); + comp.ngOnInit(); + comp.showBackButton.subscribe((val) => { + expect(val).toBeTrue(); + }); + }); + }); + }); diff --git a/src/app/item-page/simple/item-types/shared/item.component.ts b/src/app/item-page/simple/item-types/shared/item.component.ts index 71b0b5b678..93e6a0b346 100644 --- a/src/app/item-page/simple/item-types/shared/item.component.ts +++ b/src/app/item-page/simple/item-types/shared/item.component.ts @@ -5,6 +5,8 @@ import { getItemPageRoute } from '../../../item-page-routing-paths'; import { RouteService } from '../../../../core/services/route.service'; import { Observable } from 'rxjs'; import { getDSpaceQuery, isIiifEnabled, isIiifSearchEnabled } from './item-iiif-utils'; +import { filter, map, take } from 'rxjs/operators'; +import { Router } from '@angular/router'; @Component({ selector: 'ds-item', @@ -16,6 +18,17 @@ import { getDSpaceQuery, isIiifEnabled, isIiifSearchEnabled } from './item-iiif- export class ItemComponent implements OnInit { @Input() object: Item; + /** + * This regex matches previous routes. The button is shown + * for matching paths and hidden in other cases. + */ + previousRoute = /^(\/search|\/browse|\/collections|\/admin\/search|\/mydspace)/; + + /** + * Used to show or hide the back to results button in the view. + */ + showBackButton: Observable; + /** * Route to the item page */ @@ -38,12 +51,33 @@ export class ItemComponent implements OnInit { mediaViewer; - constructor(protected routeService: RouteService) { + constructor(protected routeService: RouteService, + protected router: Router) { this.mediaViewer = environment.mediaViewer; } + /** + * The function used to return to list from the item. + */ + back = () => { + this.routeService.getPreviousUrl().pipe( + take(1) + ).subscribe( + (url => { + this.router.navigateByUrl(url); + }) + ); + }; + ngOnInit(): void { + this.itemPageRoute = getItemPageRoute(this.object); + // hide/show the back button + this.showBackButton = this.routeService.getPreviousUrl().pipe( + filter(url => this.previousRoute.test(url)), + take(1), + map(() => true) + ); // check to see if iiif viewer is required. this.iiifEnabled = isIiifEnabled(this.object); this.iiifSearchEnabled = isIiifSearchEnabled(this.object); diff --git a/src/app/item-page/simple/item-types/untyped-item/untyped-item.component.html b/src/app/item-page/simple/item-types/untyped-item/untyped-item.component.html index 5c3e5e77b0..fcbc46422d 100644 --- a/src/app/item-page/simple/item-types/untyped-item/untyped-item.component.html +++ b/src/app/item-page/simple/item-types/untyped-item/untyped-item.component.html @@ -1,3 +1,4 @@ +
+
diff --git a/src/app/item-page/simple/item-types/untyped-item/untyped-item.component.scss b/src/app/item-page/simple/item-types/untyped-item/untyped-item.component.scss index 3575cae797..b810d2ddd4 100644 --- a/src/app/item-page/simple/item-types/untyped-item/untyped-item.component.scss +++ b/src/app/item-page/simple/item-types/untyped-item/untyped-item.component.scss @@ -1 +1,2 @@ @import '../../../../../styles/variables.scss'; + diff --git a/src/app/item-page/simple/item-types/untyped-item/untyped-item.component.spec.ts b/src/app/item-page/simple/item-types/untyped-item/untyped-item.component.spec.ts index 176a692f4b..3581694a5e 100644 --- a/src/app/item-page/simple/item-types/untyped-item/untyped-item.component.spec.ts +++ b/src/app/item-page/simple/item-types/untyped-item/untyped-item.component.spec.ts @@ -169,13 +169,12 @@ describe('UntypedItemComponent', () => { }); describe('with IIIF viewer and search', () => { - + const localMockRouteService = { + getPreviousUrl(): Observable { + return of('/search?query=test%20query&fakeParam=true'); + } + }; beforeEach(waitForAsync(() => { - const localMockRouteService = { - getPreviousUrl(): Observable { - return of('/search?query=test%20query&fakeParam=true'); - } - }; const iiifEnabledMap: MetadataMap = { 'dspace.iiif.enabled': [getIIIFEnabled(true)], 'iiif.search.enabled': [getIIIFSearchEnabled(true)], @@ -183,6 +182,7 @@ describe('UntypedItemComponent', () => { TestBed.overrideProvider(RouteService, {useValue: localMockRouteService}); TestBed.compileComponents(); fixture = TestBed.createComponent(UntypedItemComponent); + spyOn(localMockRouteService, 'getPreviousUrl').and.callThrough(); comp = fixture.componentInstance; comp.object = getItem(iiifEnabledMap); fixture.detectChanges(); @@ -196,17 +196,16 @@ describe('UntypedItemComponent', () => { it('should retrieve the query term for previous route', (): void => { expect(comp.iiifQuery$.subscribe(result => expect(result).toEqual('test query'))); }); - }); describe('with IIIF viewer and search but no previous search query', () => { + const localMockRouteService = { + getPreviousUrl(): Observable { + return of('/item'); + } + }; beforeEach(waitForAsync(() => { - const localMockRouteService = { - getPreviousUrl(): Observable { - return of('/item'); - } - }; const iiifEnabledMap: MetadataMap = { 'dspace.iiif.enabled': [getIIIFEnabled(true)], 'iiif.search.enabled': [getIIIFSearchEnabled(true)], @@ -214,6 +213,7 @@ describe('UntypedItemComponent', () => { TestBed.overrideProvider(RouteService, {useValue: localMockRouteService}); TestBed.compileComponents(); fixture = TestBed.createComponent(UntypedItemComponent); + comp = fixture.componentInstance; comp.object = getItem(iiifEnabledMap); fixture.detectChanges(); diff --git a/src/app/item-page/simple/item-types/versioned-item/versioned-item.component.ts b/src/app/item-page/simple/item-types/versioned-item/versioned-item.component.ts index 0cedc3bb10..a1f4cebd77 100644 --- a/src/app/item-page/simple/item-types/versioned-item/versioned-item.component.ts +++ b/src/app/item-page/simple/item-types/versioned-item/versioned-item.component.ts @@ -31,13 +31,13 @@ export class VersionedItemComponent extends ItemComponent { private translateService: TranslateService, private versionService: VersionDataService, private itemVersionShared: ItemVersionsSharedService, - private router: Router, + protected router: Router, private workspaceItemDataService: WorkspaceitemDataService, private searchService: SearchService, private itemService: ItemDataService, - protected routeService: RouteService + protected routeService: RouteService, ) { - super(routeService); + super(routeService, router); } /** diff --git a/src/app/shared/browse-by/browse-by.component.html b/src/app/shared/browse-by/browse-by.component.html index 7d9153b9f8..d6a0005173 100644 --- a/src/app/shared/browse-by/browse-by.component.html +++ b/src/app/shared/browse-by/browse-by.component.html @@ -3,7 +3,7 @@
- +
- +