mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 01:54:15 +00:00
Merge pull request #1995 from mspalti/item-back-button
Back to results button in item view
This commit is contained in:
@@ -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 {
|
||||
|
@@ -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}))
|
||||
};
|
||||
}
|
||||
|
@@ -1,3 +1,4 @@
|
||||
<ds-themed-results-back-button *ngIf="showBackButton | async" [back]="back"></ds-themed-results-back-button>
|
||||
<div class="d-flex flex-row">
|
||||
<ds-item-page-title-field [item]="object" class="mr-auto">
|
||||
</ds-item-page-title-field>
|
||||
|
@@ -1 +1,2 @@
|
||||
@import '../../../../../styles/variables.scss';
|
||||
|
||||
|
@@ -1,3 +1,4 @@
|
||||
<ds-themed-results-back-button *ngIf="showBackButton | async" [back]="back"></ds-themed-results-back-button>
|
||||
<div class="d-flex flex-row">
|
||||
<ds-item-page-title-field [item]="object" class="mr-auto">
|
||||
</ds-item-page-title-field>
|
||||
|
@@ -1,3 +1,4 @@
|
||||
<ds-themed-results-back-button *ngIf="showBackButton | async" [back]="back"></ds-themed-results-back-button>
|
||||
<div class="d-flex flex-row">
|
||||
<ds-item-page-title-field [item]="object" class="mr-auto">
|
||||
</ds-item-page-title-field>
|
||||
|
@@ -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<JournalComponent>;
|
||||
@@ -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]
|
||||
|
@@ -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
|
||||
|
@@ -1,3 +1,4 @@
|
||||
<ds-themed-results-back-button *ngIf="showBackButton | async" [back]="back"></ds-themed-results-back-button>
|
||||
<div class="d-flex flex-row">
|
||||
<ds-item-page-title-field [item]="object" class="mr-auto">
|
||||
</ds-item-page-title-field>
|
||||
|
@@ -1,3 +1,4 @@
|
||||
<ds-themed-results-back-button *ngIf="showBackButton | async" [back]="back"></ds-themed-results-back-button>
|
||||
<div class="d-flex flex-row">
|
||||
<ds-item-page-title-field class="mr-auto" [item]="object">
|
||||
</ds-item-page-title-field>
|
||||
|
@@ -1,3 +1,4 @@
|
||||
<ds-themed-results-back-button *ngIf="showBackButton | async" [back]="back"></ds-themed-results-back-button>
|
||||
<div class="d-flex flex-row">
|
||||
<ds-item-page-title-field [item]="object" class="mr-auto">
|
||||
</ds-item-page-title-field>
|
||||
|
@@ -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,
|
||||
|
@@ -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,
|
||||
|
@@ -1,3 +1,4 @@
|
||||
<ds-themed-results-back-button *ngIf="showBackButton | async" [back]="back"></ds-themed-results-back-button>
|
||||
<div class="row" *ngIf="iiifEnabled">
|
||||
<div class="col-12">
|
||||
<ds-mirador-viewer id="iiif-viewer"
|
||||
|
@@ -1 +1,2 @@
|
||||
@import '../../../../../styles/variables.scss';
|
||||
|
||||
|
@@ -163,12 +163,12 @@ describe('PublicationComponent', () => {
|
||||
|
||||
describe('with IIIF viewer and search', () => {
|
||||
|
||||
const localMockRouteService = {
|
||||
getPreviousUrl(): Observable<string> {
|
||||
return of('/search?query=test%20query&fakeParam=true');
|
||||
}
|
||||
};
|
||||
beforeEach(waitForAsync(() => {
|
||||
const localMockRouteService = {
|
||||
getPreviousUrl(): Observable<string> {
|
||||
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<string> {
|
||||
return of('/item');
|
||||
}
|
||||
};
|
||||
beforeEach(waitForAsync(() => {
|
||||
const localMockRouteService = {
|
||||
getPreviousUrl(): Observable<string> {
|
||||
return of('/item');
|
||||
}
|
||||
};
|
||||
const iiifEnabledMap: MetadataMap = {
|
||||
'dspace.iiif.enabled': [getIIIFEnabled(true)],
|
||||
'iiif.search.enabled': [getIIIFSearchEnabled(true)],
|
||||
|
@@ -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<string> {
|
||||
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<any>;
|
||||
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();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
@@ -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<boolean>;
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
@@ -1,3 +1,4 @@
|
||||
<ds-themed-results-back-button *ngIf="showBackButton | async" [back]="back"></ds-themed-results-back-button>
|
||||
<div class="row" *ngIf="iiifEnabled">
|
||||
<div class="col-12">
|
||||
<ds-mirador-viewer id="iiif-viewer"
|
||||
@@ -7,6 +8,7 @@
|
||||
</ds-mirador-viewer>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex flex-row">
|
||||
<ds-item-page-title-field [item]="object" class="mr-auto">
|
||||
</ds-item-page-title-field>
|
||||
|
@@ -1 +1,2 @@
|
||||
@import '../../../../../styles/variables.scss';
|
||||
|
||||
|
@@ -169,13 +169,12 @@ describe('UntypedItemComponent', () => {
|
||||
});
|
||||
|
||||
describe('with IIIF viewer and search', () => {
|
||||
|
||||
const localMockRouteService = {
|
||||
getPreviousUrl(): Observable<string> {
|
||||
return of('/search?query=test%20query&fakeParam=true');
|
||||
}
|
||||
};
|
||||
beforeEach(waitForAsync(() => {
|
||||
const localMockRouteService = {
|
||||
getPreviousUrl(): Observable<string> {
|
||||
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<string> {
|
||||
return of('/item');
|
||||
}
|
||||
};
|
||||
beforeEach(waitForAsync(() => {
|
||||
const localMockRouteService = {
|
||||
getPreviousUrl(): Observable<string> {
|
||||
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();
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -3,7 +3,7 @@
|
||||
<ng-container *ngComponentOutlet="getStartsWithComponent(); injector: objectInjector;"></ng-container>
|
||||
<div *ngIf="objects?.hasSucceeded && !objects?.isLoading && objects?.payload?.page.length > 0" @fadeIn>
|
||||
<div *ngIf="shouldDisplayResetButton$ |async" class="mb-2 reset">
|
||||
<button class="btn btn-secondary" (click)="back()"><i class="fas fa-arrow-left"></i> {{'browse.back.all-results' | translate}}</button>
|
||||
<ds-themed-results-back-button [back]="back" [buttonLabel]="buttonLabel"></ds-themed-results-back-button>
|
||||
</div>
|
||||
<ds-viewable-collection
|
||||
[config]="paginationConfig"
|
||||
@@ -18,7 +18,7 @@
|
||||
<ds-error *ngIf="objects?.hasFailed" message="{{'error.browse-by' | translate}}"></ds-error>
|
||||
<div *ngIf="!objects?.isLoading && objects?.payload?.page.length === 0">
|
||||
<div *ngIf="shouldDisplayResetButton$ |async" class="d-inline-block mb-4 reset">
|
||||
<button class="btn btn-secondary" (click)="back()"><i class="fas fa-arrow-left"></i> {{'browse.back.all-results' | translate}}</button>
|
||||
<ds-themed-results-back-button [back]="back" [buttonLabel]="buttonLabel"></ds-themed-results-back-button>
|
||||
</div>
|
||||
<div class="alert alert-info w-100" role="alert">
|
||||
{{'browse.empty' | translate}}
|
||||
|
@@ -44,6 +44,9 @@ import { ConfigurationProperty } from '../../core/shared/configuration-property.
|
||||
import { SearchConfigurationServiceStub } from '../testing/search-configuration-service.stub';
|
||||
import { SearchConfigurationService } from '../../core/shared/search/search-configuration.service';
|
||||
import { getMockThemeService } from '../mocks/theme-service.mock';
|
||||
import { SharedModule } from '../shared.module';
|
||||
import { BrowseByRoutingModule } from '../../browse-by/browse-by-routing.module';
|
||||
import { AccessControlRoutingModule } from '../../access-control/access-control-routing.module';
|
||||
|
||||
@listableObjectComponent(BrowseEntry, ViewMode.ListElement, DEFAULT_CONTEXT, 'custom')
|
||||
@Component({
|
||||
@@ -112,7 +115,10 @@ describe('BrowseByComponent', () => {
|
||||
themeService = getMockThemeService('dspace');
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
BrowseByRoutingModule,
|
||||
AccessControlRoutingModule,
|
||||
CommonModule,
|
||||
SharedModule,
|
||||
NgbModule,
|
||||
TranslateModule.forRoot({
|
||||
loader: {
|
||||
@@ -123,7 +129,7 @@ describe('BrowseByComponent', () => {
|
||||
RouterTestingModule,
|
||||
NoopAnimationsModule
|
||||
],
|
||||
declarations: [],
|
||||
declarations: [BrowseByComponent],
|
||||
providers: [
|
||||
{ provide: SearchConfigurationService, useValue: new SearchConfigurationServiceStub() },
|
||||
{ provide: ConfigurationDataService, useValue: configurationDataService },
|
||||
@@ -242,16 +248,5 @@ describe('BrowseByComponent', () => {
|
||||
expect(button).toBeDefined();
|
||||
});
|
||||
});
|
||||
describe('back', () => {
|
||||
it('should navigate back to the main browse page', () => {
|
||||
const id = 'test-pagination';
|
||||
comp.back();
|
||||
expect(paginationService.updateRoute).toHaveBeenCalledWith(id, {page: 1}, {
|
||||
value: null,
|
||||
startsWith: null,
|
||||
[id + '.return']: null
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
@@ -12,6 +12,7 @@ import { ViewMode } from '../../core/shared/view-mode.model';
|
||||
import { RouteService } from '../../core/services/route.service';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { hasValue } from '../empty.util';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-browse-by',
|
||||
@@ -124,11 +125,25 @@ export class BrowseByComponent implements OnInit, OnDestroy {
|
||||
|
||||
public constructor(private injector: Injector,
|
||||
protected paginationService: PaginationService,
|
||||
protected translateService: TranslateService,
|
||||
private routeService: RouteService,
|
||||
) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* The label used by the back button.
|
||||
*/
|
||||
buttonLabel = this.translateService.get('browse.back.all-results');
|
||||
|
||||
/**
|
||||
* The function used for back navigation in metadata browse.
|
||||
*/
|
||||
back = () => {
|
||||
const page = +this.previousPage$.value > 1 ? +this.previousPage$.value : 1;
|
||||
this.paginationService.updateRoute(this.paginationConfig.id, {page: page}, {[this.paginationConfig.id + '.return']: null, value: null, startsWith: null});
|
||||
};
|
||||
|
||||
/**
|
||||
* Go to the previous page
|
||||
*/
|
||||
@@ -184,14 +199,6 @@ export class BrowseByComponent implements OnInit, OnDestroy {
|
||||
this.sub = this.routeService.getQueryParameterValue(this.paginationConfig.id + '.return').subscribe(this.previousPage$);
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigate back to the previous browse by page
|
||||
*/
|
||||
back() {
|
||||
const page = +this.previousPage$.value > 1 ? +this.previousPage$.value : 1;
|
||||
this.paginationService.updateRoute(this.paginationConfig.id, {page: page}, {[this.paginationConfig.id + '.return']: null, value: null, startsWith: null});
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
if (this.sub) {
|
||||
this.sub.unsubscribe();
|
||||
|
25
src/app/shared/browse-by/shared-browse-by.module.ts
Normal file
25
src/app/shared/browse-by/shared-browse-by.module.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { BrowseByComponent } from './browse-by.component';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { SharedModule } from '../shared.module';
|
||||
import { ResultsBackButtonModule } from '../results-back-button/results-back-button.module';
|
||||
import { BrowseByRoutingModule } from '../../browse-by/browse-by-routing.module';
|
||||
import { AccessControlRoutingModule } from '../../access-control/access-control-routing.module';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
BrowseByComponent,
|
||||
],
|
||||
imports: [
|
||||
ResultsBackButtonModule,
|
||||
BrowseByRoutingModule,
|
||||
AccessControlRoutingModule,
|
||||
CommonModule,
|
||||
SharedModule,
|
||||
],
|
||||
exports: [
|
||||
BrowseByComponent,
|
||||
SharedModule,
|
||||
]
|
||||
})
|
||||
export class SharedBrowseByModule { }
|
@@ -0,0 +1,5 @@
|
||||
<button class="btn btn-secondary btn-sm mb-2 ng-tns-c242-28" (click)="back()">
|
||||
<i _ngcontent-dspace-angular-c242="" class="fas fa-arrow-left ng-tns-c242-3"></i>
|
||||
{{this.buttonLabel | async}}
|
||||
</button>
|
||||
|
@@ -0,0 +1,73 @@
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
import { ResultsBackButtonComponent } from './results-back-button.component';
|
||||
import { TranslateModule, TranslateService } from '@ngx-translate/core';
|
||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { of } from 'rxjs';
|
||||
|
||||
describe('ResultsBackButtonComponent', () => {
|
||||
|
||||
let component: ResultsBackButtonComponent;
|
||||
let fixture: ComponentFixture<ResultsBackButtonComponent>;
|
||||
|
||||
const translate = {
|
||||
get: jasmine.createSpy('get').and.returnValue(of('item button')),
|
||||
};
|
||||
|
||||
describe('back to results', () => {
|
||||
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ResultsBackButtonComponent],
|
||||
imports: [TranslateModule.forRoot(),
|
||||
RouterTestingModule.withRoutes([])
|
||||
],
|
||||
providers: [
|
||||
{ provide: TranslateService, useValue: translate }
|
||||
],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
describe('from a metadata browse list', () => {
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
translate.get.calls.reset();
|
||||
fixture = TestBed.createComponent(ResultsBackButtonComponent);
|
||||
component = fixture.componentInstance;
|
||||
component.buttonLabel = of('browse button');
|
||||
component.ngOnInit();
|
||||
fixture.detectChanges();
|
||||
}));
|
||||
|
||||
it('should have back from browse label', () => {
|
||||
expect(translate.get).not.toHaveBeenCalled();
|
||||
const el = fixture.debugElement.nativeElement;
|
||||
expect(el.innerHTML).toContain('browse button');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('from an item', () => {
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
translate.get.calls.reset();
|
||||
fixture = TestBed.createComponent(ResultsBackButtonComponent);
|
||||
component = fixture.componentInstance;
|
||||
component.ngOnInit();
|
||||
fixture.detectChanges();
|
||||
}));
|
||||
|
||||
it('should set item label by default', () => {
|
||||
expect(translate.get).toHaveBeenCalledWith('search.browse.item-back');
|
||||
const el = fixture.debugElement.nativeElement;
|
||||
expect(el.innerHTML).toContain('item button');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
@@ -0,0 +1,37 @@
|
||||
import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-results-back-button',
|
||||
styleUrls: ['./results-back-button.component.scss'],
|
||||
templateUrl: './results-back-button.component.html',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
/**
|
||||
* Component for creating a back to result list button.
|
||||
*/
|
||||
export class ResultsBackButtonComponent implements OnInit {
|
||||
|
||||
/**
|
||||
* The function used for back navigation.
|
||||
*/
|
||||
@Input() back: () => void;
|
||||
|
||||
/**
|
||||
* The button label translation.
|
||||
*/
|
||||
@Input() buttonLabel?: Observable<any>;
|
||||
|
||||
constructor(private translateService: TranslateService) {
|
||||
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
if (!this.buttonLabel) {
|
||||
// Use the 'Back to Results' label as default.
|
||||
this.buttonLabel = this.translateService.get('search.browse.item-back');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,22 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { ResultsBackButtonComponent } from './results-back-button.component';
|
||||
import { ThemedResultsBackButtonComponent } from './themed-results-back-button.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
ResultsBackButtonComponent,
|
||||
ThemedResultsBackButtonComponent
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
TranslateModule
|
||||
],
|
||||
exports: [
|
||||
ThemedResultsBackButtonComponent
|
||||
]
|
||||
})
|
||||
export class ResultsBackButtonModule {
|
||||
|
||||
}
|
@@ -0,0 +1,31 @@
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { ThemedComponent } from '../theme-support/themed.component';
|
||||
import { ResultsBackButtonComponent } from './results-back-button.component';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-themed-results-back-button',
|
||||
styleUrls: [],
|
||||
templateUrl: '../theme-support/themed.component.html',
|
||||
})
|
||||
export class ThemedResultsBackButtonComponent extends ThemedComponent<ResultsBackButtonComponent> {
|
||||
|
||||
@Input() buttonLabel?: Observable<any>;
|
||||
|
||||
@Input() back: () => void;
|
||||
|
||||
protected inAndOutputNames: (keyof ResultsBackButtonComponent & keyof this)[] = ['back', 'buttonLabel'];
|
||||
|
||||
protected getComponentName(): string {
|
||||
return 'ResultsBackButtonComponent';
|
||||
}
|
||||
|
||||
protected importThemedComponent(themeName: string): Promise<any> {
|
||||
return import(`../../../themes/${themeName}/app/shared/results-back-button/results-back-button.component`);
|
||||
}
|
||||
|
||||
protected importUnthemedComponent(): Promise<any> {
|
||||
return import(`./results-back-button.component`);
|
||||
}
|
||||
|
||||
}
|
@@ -71,7 +71,6 @@ import {
|
||||
} from './object-detail/my-dspace-result-detail-element/search-result-detail-element.component';
|
||||
import { ObjectDetailComponent } from './object-detail/object-detail.component';
|
||||
import { ObjNgFor } from './utils/object-ngfor.pipe';
|
||||
import { BrowseByComponent } from './browse-by/browse-by.component';
|
||||
import {
|
||||
BrowseEntryListElementComponent
|
||||
} from './object-list/browse-entry-list-element/browse-entry-list-element.component';
|
||||
@@ -246,6 +245,7 @@ import { MetadataFieldWrapperComponent } from './metadata-field-wrapper/metadata
|
||||
import { LogInExternalProviderComponent } from './log-in/methods/log-in-external-provider/log-in-external-provider.component';
|
||||
|
||||
|
||||
|
||||
const MODULES = [
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
@@ -259,7 +259,7 @@ const MODULES = [
|
||||
RouterModule,
|
||||
DragDropModule,
|
||||
GoogleRecaptchaModule,
|
||||
MenuModule,
|
||||
MenuModule
|
||||
];
|
||||
|
||||
const ROOT_MODULES = [
|
||||
@@ -313,7 +313,6 @@ const COMPONENTS = [
|
||||
ViewModeSwitchComponent,
|
||||
TruncatableComponent,
|
||||
TruncatablePartComponent,
|
||||
BrowseByComponent,
|
||||
InputSuggestionsComponent,
|
||||
FilterInputSuggestionsComponent,
|
||||
ValidationSuggestionsComponent,
|
||||
|
@@ -780,6 +780,9 @@
|
||||
"browse.title.page": "Browsing {{ collection }} by {{ field }} {{ value }}",
|
||||
|
||||
|
||||
"search.browse.item-back": "Back to Results",
|
||||
|
||||
|
||||
"chips.remove": "Remove chip",
|
||||
|
||||
|
||||
|
@@ -0,0 +1,13 @@
|
||||
import {
|
||||
ResultsBackButtonComponent as BaseComponent
|
||||
} from '../../../../../app/shared/results-back-button/results-back-button.component';
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-results-back-button',
|
||||
// styleUrls: ['./results-back-button.component.scss'],
|
||||
styleUrls: ['../../../../../app/shared/results-back-button/results-back-button.component.scss'],
|
||||
//templateUrl: './results-back-button.component.html',
|
||||
templateUrl: '../../../../../app/shared/results-back-button/results-back-button.component.html'
|
||||
})
|
||||
export class ResultsBackButtonComponent extends BaseComponent {}
|
@@ -40,8 +40,10 @@ import {
|
||||
} from './app/shared/dso-selector/modal-wrappers/edit-item-selector/edit-item-selector.component';
|
||||
|
||||
import { CommunityListElementComponent } from './app/shared/object-list/community-list-element/community-list-element.component';
|
||||
import { CollectionListElementComponent } from './app/shared/object-list/collection-list-element/collection-list-element.component';
|
||||
import { CollectionListElementComponent} from './app/shared/object-list/collection-list-element/collection-list-element.component';
|
||||
import { CollectionDropdownComponent } from './app/shared/collection-dropdown/collection-dropdown.component';
|
||||
import { SharedBrowseByModule } from '../../app/shared/browse-by/shared-browse-by.module';
|
||||
import { ResultsBackButtonModule } from '../../app/shared/results-back-button/results-back-button.module';
|
||||
|
||||
|
||||
/**
|
||||
@@ -77,18 +79,20 @@ const DECLARATIONS = [
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
SharedModule,
|
||||
RootModule,
|
||||
NavbarModule,
|
||||
ItemPageModule,
|
||||
ItemSharedModule,
|
||||
],
|
||||
declarations: DECLARATIONS,
|
||||
providers: [
|
||||
...ENTRY_COMPONENTS.map((component) => ({ provide: component }))
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
SharedModule,
|
||||
RootModule,
|
||||
NavbarModule,
|
||||
SharedBrowseByModule,
|
||||
ResultsBackButtonModule,
|
||||
ItemPageModule,
|
||||
ItemSharedModule,
|
||||
],
|
||||
declarations: DECLARATIONS,
|
||||
providers: [
|
||||
...ENTRY_COMPONENTS.map((component) => ({provide: component}))
|
||||
],
|
||||
})
|
||||
/**
|
||||
* This module is included in the main bundle that gets downloaded at first page load. So it should
|
||||
|
@@ -117,8 +117,12 @@ import { BrowseByTitlePageComponent } from './app/browse-by/browse-by-title-page
|
||||
import {
|
||||
ExternalSourceEntryImportModalComponent
|
||||
} from './app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/external-source-entry-import-modal/external-source-entry-import-modal.component';
|
||||
import { SharedBrowseByModule } from '../../app/shared/browse-by/shared-browse-by.module';
|
||||
import { ResultsBackButtonModule } from '../../app/shared/results-back-button/results-back-button.module';
|
||||
import { ItemVersionsModule } from '../../app/item-page/versions/item-versions.module';
|
||||
import { ItemSharedModule } from '../../app/item-page/item-shared.module';
|
||||
import { ResultsBackButtonComponent } from './app/shared/results-back-button/results-back-button.component';
|
||||
|
||||
|
||||
const DECLARATIONS = [
|
||||
FileSectionComponent,
|
||||
@@ -174,7 +178,7 @@ const DECLARATIONS = [
|
||||
BrowseByDatePageComponent,
|
||||
BrowseByTitlePageComponent,
|
||||
ExternalSourceEntryImportModalComponent,
|
||||
|
||||
ResultsBackButtonComponent
|
||||
|
||||
];
|
||||
|
||||
@@ -216,6 +220,8 @@ const DECLARATIONS = [
|
||||
ScrollToModule,
|
||||
SearchPageModule,
|
||||
SharedModule,
|
||||
SharedBrowseByModule,
|
||||
ResultsBackButtonModule,
|
||||
StatisticsModule,
|
||||
StatisticsPageModule,
|
||||
StoreModule,
|
||||
|
@@ -7,6 +7,8 @@ import { HeaderComponent } from './app/header/header.component';
|
||||
import { HeaderNavbarWrapperComponent } from './app/header-nav-wrapper/header-navbar-wrapper.component';
|
||||
import { RootModule } from '../../app/root.module';
|
||||
import { NavbarModule } from '../../app/navbar/navbar.module';
|
||||
import { SharedBrowseByModule } from '../../app/shared/browse-by/shared-browse-by.module';
|
||||
import { ResultsBackButtonModule } from '../../app/shared/results-back-button/results-back-button.module';
|
||||
|
||||
/**
|
||||
* Add components that use a custom decorator to ENTRY_COMPONENTS as well as DECLARATIONS.
|
||||
@@ -27,6 +29,8 @@ const DECLARATIONS = [
|
||||
imports: [
|
||||
CommonModule,
|
||||
SharedModule,
|
||||
SharedBrowseByModule,
|
||||
ResultsBackButtonModule,
|
||||
RootModule,
|
||||
NavbarModule,
|
||||
],
|
||||
|
@@ -55,6 +55,9 @@ import {
|
||||
} from '../../app/shared/resource-policies/resource-policies.module';
|
||||
import { ComcolModule } from '../../app/shared/comcol/comcol.module';
|
||||
import { RootModule } from '../../app/root.module';
|
||||
import { BrowseByPageModule } from '../../app/browse-by/browse-by-page.module';
|
||||
import { ResultsBackButtonModule } from '../../app/shared/results-back-button/results-back-button.module';
|
||||
import { SharedBrowseByModule } from '../../app/shared/browse-by/shared-browse-by.module';
|
||||
import { ItemVersionsModule } from '../../app/item-page/versions/item-versions.module';
|
||||
import { ItemSharedModule } from 'src/app/item-page/item-shared.module';
|
||||
|
||||
@@ -70,6 +73,8 @@ const DECLARATIONS = [
|
||||
RootModule,
|
||||
BitstreamFormatsModule,
|
||||
BrowseByModule,
|
||||
BrowseByPageModule,
|
||||
ResultsBackButtonModule,
|
||||
CollectionFormModule,
|
||||
CollectionPageModule,
|
||||
CommonModule,
|
||||
@@ -99,6 +104,7 @@ const DECLARATIONS = [
|
||||
ScrollToModule,
|
||||
SearchPageModule,
|
||||
SharedModule,
|
||||
SharedBrowseByModule,
|
||||
StatisticsModule,
|
||||
StatisticsPageModule,
|
||||
StoreModule,
|
||||
|
Reference in New Issue
Block a user