mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-14 21:43:04 +00:00
94390: Replace DSO page edit buttons with a menu
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
<div class="sidebar-section">
|
||||
<a class="nav-item nav-link d-flex flex-row flex-nowrap"
|
||||
[ngClass]="{ disabled: !hasLink }"
|
||||
[attr.aria-disabled]="!hasLink"
|
||||
[ngClass]="{ disabled: isDisabled }"
|
||||
[attr.aria-disabled]="isDisabled"
|
||||
[attr.aria-labelledby]="'sidebarName-' + section.id"
|
||||
[title]="('menu.section.icon.' + section.id) | translate"
|
||||
[routerLink]="itemModel.link"
|
||||
|
@@ -17,6 +17,8 @@ describe('AdminSidebarSectionComponent', () => {
|
||||
const menuService = new MenuServiceStub();
|
||||
const iconString = 'test';
|
||||
|
||||
describe('when not disabled', () => {
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [NoopAnimationsModule, RouterTestingModule, TranslateModule.forRoot()],
|
||||
@@ -49,6 +51,52 @@ describe('AdminSidebarSectionComponent', () => {
|
||||
const icon = fixture.debugElement.query(By.css('.shortcut-icon')).query(By.css('i.fas'));
|
||||
expect(icon.nativeElement.getAttribute('class')).toContain('fa-' + iconString);
|
||||
});
|
||||
it('should not contain the disabled class', () => {
|
||||
const disabled = fixture.debugElement.query(By.css('.disabled'));
|
||||
expect(disabled).toBeFalsy();
|
||||
});
|
||||
|
||||
});
|
||||
describe('when disabled', () => {
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [NoopAnimationsModule, RouterTestingModule, TranslateModule.forRoot()],
|
||||
declarations: [AdminSidebarSectionComponent, TestComponent],
|
||||
providers: [
|
||||
{provide: 'sectionDataProvider', useValue: {model: {link: 'google.com', disabled: true}, icon: iconString}},
|
||||
{provide: MenuService, useValue: menuService},
|
||||
{provide: CSSVariableService, useClass: CSSVariableServiceStub},
|
||||
]
|
||||
}).overrideComponent(AdminSidebarSectionComponent, {
|
||||
set: {
|
||||
entryComponents: [TestComponent]
|
||||
}
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(AdminSidebarSectionComponent);
|
||||
component = fixture.componentInstance;
|
||||
spyOn(component as any, 'getMenuItemComponent').and.returnValue(TestComponent);
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should set the right icon', () => {
|
||||
const icon = fixture.debugElement.query(By.css('.shortcut-icon')).query(By.css('i.fas'));
|
||||
expect(icon.nativeElement.getAttribute('class')).toContain('fa-' + iconString);
|
||||
});
|
||||
it('should contain the disabled class', () => {
|
||||
const disabled = fixture.debugElement.query(By.css('.disabled'));
|
||||
expect(disabled).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
// declare a test component
|
||||
|
@@ -5,7 +5,7 @@ import { MenuService } from '../../../shared/menu/menu.service';
|
||||
import { rendersSectionForMenu } from '../../../shared/menu/menu-section.decorator';
|
||||
import { LinkMenuItemModel } from '../../../shared/menu/menu-item/models/link.model';
|
||||
import { MenuSection } from '../../../shared/menu/menu.reducer';
|
||||
import { isNotEmpty } from '../../../shared/empty.util';
|
||||
import { isEmpty } from '../../../shared/empty.util';
|
||||
import { Router } from '@angular/router';
|
||||
|
||||
/**
|
||||
@@ -26,7 +26,12 @@ export class AdminSidebarSectionComponent extends MenuSectionComponent implement
|
||||
*/
|
||||
menuID: MenuID = MenuID.ADMIN;
|
||||
itemModel;
|
||||
hasLink: boolean;
|
||||
|
||||
/**
|
||||
* Boolean to indicate whether this section is disabled
|
||||
*/
|
||||
isDisabled: boolean;
|
||||
|
||||
constructor(
|
||||
@Inject('sectionDataProvider') menuSection: MenuSection,
|
||||
protected menuService: MenuService,
|
||||
@@ -38,13 +43,13 @@ export class AdminSidebarSectionComponent extends MenuSectionComponent implement
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.hasLink = isNotEmpty(this.itemModel?.link);
|
||||
this.isDisabled = this.itemModel?.disabled || isEmpty(this.itemModel?.link);
|
||||
super.ngOnInit();
|
||||
}
|
||||
|
||||
navigate(event: any): void {
|
||||
event.preventDefault();
|
||||
if (this.hasLink) {
|
||||
if (this.isDisabled) {
|
||||
this.router.navigate(this.itemModel.link);
|
||||
}
|
||||
}
|
||||
|
@@ -7,6 +7,7 @@
|
||||
[attr.aria-labelledby]="'sidebarName-' + section.id"
|
||||
[attr.aria-expanded]="expanded | async"
|
||||
[title]="('menu.section.icon.' + section.id) | translate"
|
||||
[class.disabled]="section.model.disabled"
|
||||
(click)="toggleSection($event)"
|
||||
(keyup.space)="toggleSection($event)"
|
||||
(keyup.enter)="toggleSection($event)"
|
||||
|
@@ -21,6 +21,7 @@ import { CollectionPageAdministratorGuard } from './collection-page-administrato
|
||||
import { MenuItemType } from '../shared/menu/initial-menus-state';
|
||||
import { LinkMenuItemModel } from '../shared/menu/menu-item/models/link.model';
|
||||
import { ThemedCollectionPageComponent } from './themed-collection-page.component';
|
||||
import { DSOEditMenuResolver } from '../shared/dso-page/dso-edit-menu.resolver';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
@@ -34,7 +35,8 @@ import { ThemedCollectionPageComponent } from './themed-collection-page.componen
|
||||
path: ':id',
|
||||
resolve: {
|
||||
dso: CollectionPageResolver,
|
||||
breadcrumb: CollectionBreadcrumbResolver
|
||||
breadcrumb: CollectionBreadcrumbResolver,
|
||||
menu: DSOEditMenuResolver
|
||||
},
|
||||
runGuardsAndResolvers: 'always',
|
||||
children: [
|
||||
@@ -90,7 +92,8 @@ import { ThemedCollectionPageComponent } from './themed-collection-page.componen
|
||||
DSOBreadcrumbsService,
|
||||
LinkService,
|
||||
CreateCollectionPageGuard,
|
||||
CollectionPageAdministratorGuard
|
||||
CollectionPageAdministratorGuard,
|
||||
DSOEditMenuResolver
|
||||
]
|
||||
})
|
||||
export class CollectionPageRoutingModule {
|
||||
|
@@ -34,9 +34,7 @@
|
||||
[title]="'collection.page.news'">
|
||||
</ds-comcol-page-content>
|
||||
</header>
|
||||
<div class="pl-2">
|
||||
<ds-dso-page-edit-button *ngIf="isCollectionAdmin$ | async" [pageRoute]="collectionPageRoute$ | async" [dso]="collection" [tooltipMsg]="'collection.page.edit'"></ds-dso-page-edit-button>
|
||||
</div>
|
||||
<ds-dso-edit-menu></ds-dso-edit-menu>
|
||||
</div>
|
||||
<section class="comcol-page-browse-section">
|
||||
<!-- Browse-By Links -->
|
||||
|
@@ -14,6 +14,7 @@ import { CommunityPageAdministratorGuard } from './community-page-administrator.
|
||||
import { MenuItemType } from '../shared/menu/initial-menus-state';
|
||||
import { LinkMenuItemModel } from '../shared/menu/menu-item/models/link.model';
|
||||
import { ThemedCommunityPageComponent } from './themed-community-page.component';
|
||||
import { DSOEditMenuResolver } from '../shared/dso-page/dso-edit-menu.resolver';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
@@ -27,7 +28,8 @@ import { ThemedCommunityPageComponent } from './themed-community-page.component'
|
||||
path: ':id',
|
||||
resolve: {
|
||||
dso: CommunityPageResolver,
|
||||
breadcrumb: CommunityBreadcrumbResolver
|
||||
breadcrumb: CommunityBreadcrumbResolver,
|
||||
menu: DSOEditMenuResolver
|
||||
},
|
||||
runGuardsAndResolvers: 'always',
|
||||
children: [
|
||||
@@ -72,7 +74,8 @@ import { ThemedCommunityPageComponent } from './themed-community-page.component'
|
||||
DSOBreadcrumbsService,
|
||||
LinkService,
|
||||
CreateCommunityPageGuard,
|
||||
CommunityPageAdministratorGuard
|
||||
CommunityPageAdministratorGuard,
|
||||
DSOEditMenuResolver
|
||||
]
|
||||
})
|
||||
export class CommunityPageRoutingModule {
|
||||
|
@@ -20,9 +20,7 @@
|
||||
[title]="'community.page.news'">
|
||||
</ds-comcol-page-content>
|
||||
</header>
|
||||
<div class="pl-2">
|
||||
<ds-dso-page-edit-button *ngIf="isCommunityAdmin$ | async" [pageRoute]="communityPageRoute$ | async" [dso]="communityPayload" [tooltipMsg]="'community.page.edit'"></ds-dso-page-edit-button>
|
||||
</div>
|
||||
<ds-dso-edit-menu></ds-dso-edit-menu>
|
||||
</div>
|
||||
<section class="comcol-page-browse-section">
|
||||
<!-- Browse-By Links -->
|
||||
|
@@ -2,9 +2,8 @@
|
||||
<h2 class="item-page-title-field mr-auto">
|
||||
{{'journalissue.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="object?.allMetadata(['dc.title'])"></ds-metadata-values>
|
||||
</h2>
|
||||
<div class="pl-2">
|
||||
<ds-dso-page-edit-button [pageRoute]="itemPageRoute" [dso]="object" [tooltipMsg]="'journalissue.page.edit'"></ds-dso-page-edit-button>
|
||||
</div>
|
||||
<ds-dso-edit-menu></ds-dso-edit-menu>
|
||||
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-12 col-md-4">
|
||||
|
@@ -2,9 +2,7 @@
|
||||
<h2 class="item-page-title-field mr-auto">
|
||||
{{'journalvolume.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="object?.allMetadata(['dc.title'])"></ds-metadata-values>
|
||||
</h2>
|
||||
<div class="pl-2">
|
||||
<ds-dso-page-edit-button [pageRoute]="itemPageRoute" [dso]="object" [tooltipMsg]="'journalvolume.page.edit'"></ds-dso-page-edit-button>
|
||||
</div>
|
||||
<ds-dso-edit-menu></ds-dso-edit-menu>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-12 col-md-4">
|
||||
|
@@ -2,9 +2,8 @@
|
||||
<h2 class="item-page-title-field mr-auto">
|
||||
{{'journal.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="object?.allMetadata(['dc.title'])"></ds-metadata-values>
|
||||
</h2>
|
||||
<div class="pl-2">
|
||||
<ds-dso-page-edit-button [pageRoute]="itemPageRoute" [dso]="object" [tooltipMsg]="'journal.page.edit'"></ds-dso-page-edit-button>
|
||||
</div>
|
||||
<ds-dso-edit-menu></ds-dso-edit-menu>
|
||||
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-12 col-md-4">
|
||||
|
@@ -2,9 +2,8 @@
|
||||
<h2 class="item-page-title-field mr-auto">
|
||||
{{'orgunit.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="object?.allMetadata(['organization.legalName'])"></ds-metadata-values>
|
||||
</h2>
|
||||
<div class="pl-2">
|
||||
<ds-dso-page-edit-button [pageRoute]="itemPageRoute" [dso]="object" [tooltipMsg]="'orgunit.page.edit'"></ds-dso-page-edit-button>
|
||||
</div>
|
||||
<ds-dso-edit-menu></ds-dso-edit-menu>
|
||||
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-12 col-md-4">
|
||||
|
@@ -2,9 +2,8 @@
|
||||
<h2 class="item-page-title-field mr-auto">
|
||||
{{'person.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="[object?.firstMetadata('person.familyName'), object?.firstMetadata('person.givenName')]" [separator]="', '"></ds-metadata-values>
|
||||
</h2>
|
||||
<div class="pl-2">
|
||||
<ds-dso-page-edit-button [pageRoute]="itemPageRoute" [dso]="object" [tooltipMsg]="'person.page.edit'"></ds-dso-page-edit-button>
|
||||
</div>
|
||||
<ds-dso-edit-menu></ds-dso-edit-menu>
|
||||
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-12 col-md-4">
|
||||
|
@@ -2,9 +2,8 @@
|
||||
<h2 class="item-page-title-field mr-auto">
|
||||
{{'project.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="object?.allMetadata(['dc.title'])"></ds-metadata-values>
|
||||
</h2>
|
||||
<div class="pl-2">
|
||||
<ds-dso-page-edit-button [pageRoute]="itemPageRoute" [dso]="object" [tooltipMsg]="'project.page.edit'"></ds-dso-page-edit-button>
|
||||
</div>
|
||||
<ds-dso-edit-menu></ds-dso-edit-menu>
|
||||
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-12 col-md-4">
|
||||
|
@@ -6,9 +6,8 @@
|
||||
<ds-view-tracker [object]="item"></ds-view-tracker>
|
||||
<div class="d-flex flex-row">
|
||||
<ds-item-page-title-field class="mr-auto" [item]="item"></ds-item-page-title-field>
|
||||
<div class="pl-2">
|
||||
<ds-dso-page-edit-button [pageRoute]="itemPageRoute$ | async" [dso]="item" [tooltipMsg]="'item.page.edit'"></ds-dso-page-edit-button>
|
||||
</div>
|
||||
<ds-dso-edit-menu></ds-dso-edit-menu>
|
||||
|
||||
</div>
|
||||
<div class="simple-view-link my-3" *ngIf="!fromWfi">
|
||||
<a class="btn btn-outline-primary" [routerLink]="[(itemPageRoute$ | async)]">
|
||||
|
@@ -16,6 +16,7 @@ import { ThemedFullItemPageComponent } from './full/themed-full-item-page.compon
|
||||
import { VersionPageComponent } from './version-page/version-page/version-page.component';
|
||||
import { BitstreamRequestACopyPageComponent } from '../shared/bitstream-request-a-copy-page/bitstream-request-a-copy-page.component';
|
||||
import { REQUEST_COPY_MODULE_PATH } from '../app-routing-paths';
|
||||
import { DSOEditMenuResolver } from '../shared/dso-page/dso-edit-menu.resolver';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
@@ -24,7 +25,8 @@ import { REQUEST_COPY_MODULE_PATH } from '../app-routing-paths';
|
||||
path: ':id',
|
||||
resolve: {
|
||||
dso: ItemPageResolver,
|
||||
breadcrumb: ItemBreadcrumbResolver
|
||||
breadcrumb: ItemBreadcrumbResolver,
|
||||
menu: DSOEditMenuResolver
|
||||
},
|
||||
runGuardsAndResolvers: 'always',
|
||||
children: [
|
||||
@@ -88,6 +90,7 @@ import { REQUEST_COPY_MODULE_PATH } from '../app-routing-paths';
|
||||
LinkService,
|
||||
ItemPageAdministratorGuard,
|
||||
VersionResolver,
|
||||
DSOEditMenuResolver
|
||||
]
|
||||
|
||||
})
|
||||
|
@@ -32,7 +32,6 @@ import { MediaViewerImageComponent } from './media-viewer/media-viewer-image/med
|
||||
import { NgxGalleryModule } from '@kolkov/ngx-gallery';
|
||||
import { MiradorViewerComponent } from './mirador-viewer/mirador-viewer.component';
|
||||
import { VersionPageComponent } from './version-page/version-page/version-page.component';
|
||||
import { VersionedItemComponent } from './simple/item-types/versioned-item/versioned-item.component';
|
||||
import { ThemedFileSectionComponent } from './simple/field-components/file-section/themed-file-section.component';
|
||||
|
||||
|
||||
@@ -82,7 +81,6 @@ const DECLARATIONS = [
|
||||
],
|
||||
declarations: [
|
||||
...DECLARATIONS,
|
||||
VersionedItemComponent
|
||||
],
|
||||
exports: [
|
||||
...DECLARATIONS
|
||||
|
@@ -11,9 +11,7 @@
|
||||
<h2 class="item-page-title-field mr-auto">
|
||||
{{'publication.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="object?.allMetadata(['dc.title'])"></ds-metadata-values>
|
||||
</h2>
|
||||
<div class="pl-2">
|
||||
<ds-dso-page-edit-button [pageRoute]="itemPageRoute" [dso]="object" [tooltipMsg]="'publication.page.edit'"></ds-dso-page-edit-button>
|
||||
</div>
|
||||
<ds-dso-edit-menu></ds-dso-edit-menu>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-12 col-md-4">
|
||||
|
@@ -11,12 +11,8 @@
|
||||
<h2 class="item-page-title-field mr-auto">
|
||||
<ds-metadata-values [mdValues]="object?.allMetadata(['dc.title'])"></ds-metadata-values>
|
||||
</h2>
|
||||
<div class="pl-2">
|
||||
<ds-dso-page-version-button (newVersionEvent)="onCreateNewVersion()" [dso]="object"
|
||||
[tooltipMsgCreate]="'item.page.version.create'"
|
||||
[tooltipMsgHasDraft]="'item.page.version.hasDraft'"></ds-dso-page-version-button>
|
||||
<ds-dso-page-edit-button [pageRoute]="itemPageRoute" [dso]="object" [tooltipMsg]="'item.page.edit'"></ds-dso-page-edit-button>
|
||||
</div>
|
||||
<ds-dso-edit-menu></ds-dso-edit-menu>
|
||||
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-12 col-md-4">
|
||||
|
@@ -2,7 +2,7 @@ import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||
import { Item } from '../../../../core/shared/item.model';
|
||||
import { ViewMode } from '../../../../core/shared/view-mode.model';
|
||||
import { listableObjectComponent } from '../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
|
||||
import { VersionedItemComponent } from '../versioned-item/versioned-item.component';
|
||||
import { ItemComponent } from '../shared/item.component';
|
||||
|
||||
/**
|
||||
* Component that represents a publication Item page
|
||||
@@ -15,6 +15,6 @@ import { VersionedItemComponent } from '../versioned-item/versioned-item.compone
|
||||
templateUrl: './untyped-item.component.html',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class UntypedItemComponent extends VersionedItemComponent {
|
||||
export class UntypedItemComponent extends ItemComponent {
|
||||
|
||||
}
|
||||
|
@@ -1,95 +0,0 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { VersionedItemComponent } from './versioned-item.component';
|
||||
import { VersionHistoryDataService } from '../../../../core/data/version-history-data.service';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { VersionDataService } from '../../../../core/data/version-data.service';
|
||||
import { NotificationsService } from '../../../../shared/notifications/notifications.service';
|
||||
import { ItemVersionsSharedService } from '../../../../shared/item/item-versions/item-versions-shared.service';
|
||||
import { Item } from '../../../../core/shared/item.model';
|
||||
import { createSuccessfulRemoteDataObject$ } from '../../../../shared/remote-data.utils';
|
||||
import { buildPaginatedList } from '../../../../core/data/paginated-list.model';
|
||||
import { PageInfo } from '../../../../core/shared/page-info.model';
|
||||
import { MetadataMap } from '../../../../core/shared/metadata.models';
|
||||
import { createRelationshipsObservable, mockRouteService } from '../shared/item.component.spec';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { Component } from '@angular/core';
|
||||
import { WorkspaceitemDataService } from '../../../../core/submission/workspaceitem-data.service';
|
||||
import { SearchService } from '../../../../core/shared/search/search.service';
|
||||
import { ItemDataService } from '../../../../core/data/item-data.service';
|
||||
import { Version } from '../../../../core/shared/version.model';
|
||||
import { RouteService } from '../../../../core/services/route.service';
|
||||
|
||||
const mockItem: Item = Object.assign(new Item(), {
|
||||
bundles: createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), [])),
|
||||
metadata: new MetadataMap(),
|
||||
relationships: createRelationshipsObservable(),
|
||||
_links: {
|
||||
self: {
|
||||
href: 'item-href'
|
||||
},
|
||||
version: {
|
||||
href: 'version-href'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@Component({template: ''})
|
||||
class DummyComponent {
|
||||
}
|
||||
|
||||
describe('VersionedItemComponent', () => {
|
||||
let component: VersionedItemComponent;
|
||||
let fixture: ComponentFixture<VersionedItemComponent>;
|
||||
|
||||
let versionService: VersionDataService;
|
||||
let versionHistoryService: VersionHistoryDataService;
|
||||
|
||||
const versionServiceSpy = jasmine.createSpyObj('versionService', {
|
||||
findByHref: createSuccessfulRemoteDataObject$<Version>(new Version()),
|
||||
});
|
||||
|
||||
const versionHistoryServiceSpy = jasmine.createSpyObj('versionHistoryService', {
|
||||
createVersion: createSuccessfulRemoteDataObject$<Version>(new Version()),
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [VersionedItemComponent, DummyComponent],
|
||||
imports: [RouterTestingModule],
|
||||
providers: [
|
||||
{ provide: VersionHistoryDataService, useValue: versionHistoryServiceSpy },
|
||||
{ provide: TranslateService, useValue: {} },
|
||||
{ provide: VersionDataService, useValue: versionServiceSpy },
|
||||
{ provide: NotificationsService, useValue: {} },
|
||||
{ provide: ItemVersionsSharedService, useValue: {} },
|
||||
{ provide: WorkspaceitemDataService, useValue: {} },
|
||||
{ provide: SearchService, useValue: {} },
|
||||
{ provide: ItemDataService, useValue: {} },
|
||||
{ provide: RouteService, useValue: mockRouteService }
|
||||
]
|
||||
}).compileComponents();
|
||||
versionService = TestBed.inject(VersionDataService);
|
||||
versionHistoryService = TestBed.inject(VersionHistoryDataService);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(VersionedItemComponent);
|
||||
component = fixture.componentInstance;
|
||||
component.object = mockItem;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
describe('when onCreateNewVersion() is called', () => {
|
||||
it('should call versionService.findByHref', () => {
|
||||
component.onCreateNewVersion();
|
||||
expect(versionService.findByHref).toHaveBeenCalledWith('version-href');
|
||||
});
|
||||
});
|
||||
|
||||
});
|
@@ -1,80 +0,0 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { ItemComponent } from '../shared/item.component';
|
||||
import { ItemVersionsSummaryModalComponent } from '../../../../shared/item/item-versions/item-versions-summary-modal/item-versions-summary-modal.component';
|
||||
import { getFirstCompletedRemoteData, getFirstSucceededRemoteDataPayload } from '../../../../core/shared/operators';
|
||||
import { RemoteData } from '../../../../core/data/remote-data';
|
||||
import { Version } from '../../../../core/shared/version.model';
|
||||
import { switchMap, tap } from 'rxjs/operators';
|
||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { VersionHistoryDataService } from '../../../../core/data/version-history-data.service';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { VersionDataService } from '../../../../core/data/version-data.service';
|
||||
import { ItemVersionsSharedService } from '../../../../shared/item/item-versions/item-versions-shared.service';
|
||||
import { Router } from '@angular/router';
|
||||
import { WorkspaceitemDataService } from '../../../../core/submission/workspaceitem-data.service';
|
||||
import { SearchService } from '../../../../core/shared/search/search.service';
|
||||
import { Item } from '../../../../core/shared/item.model';
|
||||
import { ItemDataService } from '../../../../core/data/item-data.service';
|
||||
import { WorkspaceItem } from '../../../../core/submission/models/workspaceitem.model';
|
||||
import { RouteService } from '../../../../core/services/route.service';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-versioned-item',
|
||||
templateUrl: './versioned-item.component.html',
|
||||
styleUrls: ['./versioned-item.component.scss']
|
||||
})
|
||||
export class VersionedItemComponent extends ItemComponent {
|
||||
|
||||
constructor(
|
||||
private modalService: NgbModal,
|
||||
private versionHistoryService: VersionHistoryDataService,
|
||||
private translateService: TranslateService,
|
||||
private versionService: VersionDataService,
|
||||
private itemVersionShared: ItemVersionsSharedService,
|
||||
private router: Router,
|
||||
private workspaceItemDataService: WorkspaceitemDataService,
|
||||
private searchService: SearchService,
|
||||
private itemService: ItemDataService,
|
||||
protected routeService: RouteService
|
||||
) {
|
||||
super(routeService);
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a modal that allows to create a new version starting from the specified item, with optional summary
|
||||
*/
|
||||
onCreateNewVersion(): void {
|
||||
|
||||
const item = this.object;
|
||||
const versionHref = item._links.version.href;
|
||||
|
||||
// Open modal
|
||||
const activeModal = this.modalService.open(ItemVersionsSummaryModalComponent);
|
||||
|
||||
// Show current version in modal
|
||||
this.versionService.findByHref(versionHref).pipe(getFirstCompletedRemoteData()).subscribe((res: RemoteData<Version>) => {
|
||||
// if res.hasNoContent then the item is unversioned
|
||||
activeModal.componentInstance.firstVersion = res.hasNoContent;
|
||||
activeModal.componentInstance.versionNumber = (res.hasNoContent ? undefined : res.payload.version);
|
||||
});
|
||||
|
||||
// On createVersionEvent emitted create new version and notify
|
||||
activeModal.componentInstance.createVersionEvent.pipe(
|
||||
switchMap((summary: string) => this.versionHistoryService.createVersion(item._links.self.href, summary)),
|
||||
getFirstCompletedRemoteData(),
|
||||
// show success/failure notification
|
||||
tap((res: RemoteData<Version>) => { this.itemVersionShared.notifyCreateNewVersion(res); }),
|
||||
// get workspace item
|
||||
getFirstSucceededRemoteDataPayload<Version>(),
|
||||
switchMap((newVersion: Version) => this.itemService.findByHref(newVersion._links.item.href)),
|
||||
getFirstSucceededRemoteDataPayload<Item>(),
|
||||
switchMap((newVersionItem: Item) => this.workspaceItemDataService.findByItem(newVersionItem.uuid, true, false)),
|
||||
getFirstSucceededRemoteDataPayload<WorkspaceItem>(),
|
||||
).subscribe((wsItem) => {
|
||||
const wsiId = wsItem.id;
|
||||
const route = 'workspaceitems/' + wsiId + '/edit';
|
||||
this.router.navigateByUrl(route);
|
||||
});
|
||||
|
||||
}
|
||||
}
|
@@ -6,6 +6,7 @@
|
||||
(mouseenter)="activateSection($event)"
|
||||
(mouseleave)="deactivateSection($event)">
|
||||
<a href="javascript:void(0);" class="nav-link dropdown-toggle" routerLinkActive="active"
|
||||
[class.disabled]="section.model?.disabled"
|
||||
id="browseDropdown" (click)="toggleSection($event)"
|
||||
data-toggle="dropdown">
|
||||
<ng-container
|
||||
|
190
src/app/shared/dso-page/dso-edit-menu.resolver.spec.ts
Normal file
190
src/app/shared/dso-page/dso-edit-menu.resolver.spec.ts
Normal file
@@ -0,0 +1,190 @@
|
||||
import { TestBed, waitForAsync } from '@angular/core/testing';
|
||||
import { MenuServiceStub } from '../testing/menu-service.stub';
|
||||
import { of as observableOf } from 'rxjs';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { AdminSidebarComponent } from '../../admin/admin-sidebar/admin-sidebar.component';
|
||||
import { MenuService } from '../menu/menu.service';
|
||||
import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service';
|
||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { MenuID, MenuItemType } from '../menu/initial-menus-state';
|
||||
import { DSOEditMenuResolver } from './dso-edit-menu.resolver';
|
||||
import { DsoVersioningModalService } from './dso-versioning-modal-service/dso-versioning-modal.service';
|
||||
import { DSpaceObjectDataService } from '../../core/data/dspace-object-data.service';
|
||||
import { Item } from '../../core/shared/item.model';
|
||||
import { createFailedRemoteDataObject$, createSuccessfulRemoteDataObject$ } from '../remote-data.utils';
|
||||
|
||||
describe('DSOEditMenuResolver', () => {
|
||||
|
||||
const MENU_STATE = {
|
||||
id: 'some menu'
|
||||
};
|
||||
|
||||
let resolver: DSOEditMenuResolver;
|
||||
|
||||
let dSpaceObjectDataService;
|
||||
let menuService;
|
||||
let authorizationService;
|
||||
let dsoVersioningModalService;
|
||||
|
||||
const route = {
|
||||
data: {
|
||||
menu: {
|
||||
'statistics': [{
|
||||
id: 'statistics-dummy-1',
|
||||
active: false,
|
||||
visible: true,
|
||||
model: null
|
||||
}]
|
||||
}
|
||||
},
|
||||
params: {id: 'test-uuid'},
|
||||
};
|
||||
|
||||
const state = {
|
||||
url: 'test-url'
|
||||
};
|
||||
|
||||
const testObject = Object.assign(new Item(), {uuid: 'test-uuid', type: 'item', _links: {self: {href: 'self-link'}}});
|
||||
|
||||
const dummySections1 = [{
|
||||
id: 'dummy-1',
|
||||
active: false,
|
||||
visible: true,
|
||||
model: null
|
||||
},
|
||||
{
|
||||
id: 'dummy-2',
|
||||
active: false,
|
||||
visible: true,
|
||||
model: null
|
||||
}];
|
||||
|
||||
const dummySections2 = [{
|
||||
id: 'dummy-3',
|
||||
active: false,
|
||||
visible: true,
|
||||
model: null
|
||||
},
|
||||
{
|
||||
id: 'dummy-4',
|
||||
active: false,
|
||||
visible: true,
|
||||
model: null
|
||||
},
|
||||
{
|
||||
id: 'dummy-5',
|
||||
active: false,
|
||||
visible: true,
|
||||
model: null
|
||||
}];
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
menuService = new MenuServiceStub();
|
||||
spyOn(menuService, 'getMenu').and.returnValue(observableOf(MENU_STATE));
|
||||
|
||||
dSpaceObjectDataService = jasmine.createSpyObj('dSpaceObjectDataService', {
|
||||
findById: createSuccessfulRemoteDataObject$(testObject)
|
||||
});
|
||||
authorizationService = jasmine.createSpyObj('authorizationService', {
|
||||
isAuthorized: observableOf(true)
|
||||
});
|
||||
dsoVersioningModalService = jasmine.createSpyObj('dsoVersioningModalService', {
|
||||
isNewVersionButtonDisabled: observableOf(false),
|
||||
getVersioningTooltipMessage: observableOf('message'),
|
||||
openCreateVersionModal: {}
|
||||
});
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
imports: [TranslateModule.forRoot(), NoopAnimationsModule, RouterTestingModule],
|
||||
declarations: [AdminSidebarComponent],
|
||||
providers: [
|
||||
{provide: DSpaceObjectDataService, useValue: dSpaceObjectDataService},
|
||||
{provide: MenuService, useValue: menuService},
|
||||
{provide: AuthorizationDataService, useValue: authorizationService},
|
||||
{provide: DsoVersioningModalService, useValue: dsoVersioningModalService},
|
||||
{
|
||||
provide: NgbModal, useValue: {
|
||||
open: () => {/*comment*/
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
});
|
||||
resolver = TestBed.inject(DSOEditMenuResolver);
|
||||
|
||||
spyOn(menuService, 'addSection');
|
||||
}));
|
||||
|
||||
it('should be created', () => {
|
||||
expect(resolver).toBeTruthy();
|
||||
});
|
||||
|
||||
describe('resolve', () => {
|
||||
it('should create all menus when a dso is found', (done) => {
|
||||
spyOn(resolver, 'getDsoMenus').and.returnValue(
|
||||
[observableOf(dummySections1), observableOf(dummySections2)]
|
||||
);
|
||||
resolver.resolve(route as any, null).subscribe(resolved => {
|
||||
expect(resolved).toEqual(
|
||||
{
|
||||
...route.data.menu,
|
||||
[MenuID.DSO_EDIT]: [
|
||||
...dummySections1.map((menu) => Object.assign(menu, {id: menu.id + '-test-uuid'})),
|
||||
...dummySections2.map((menu) => Object.assign(menu, {id: menu.id + '-test-uuid'}))
|
||||
]
|
||||
}
|
||||
);
|
||||
expect(resolver.getDsoMenus).toHaveBeenCalled();
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('should return the statistics menu when no dso is found', (done) => {
|
||||
(dSpaceObjectDataService.findById as jasmine.Spy).and.returnValue(createFailedRemoteDataObject$());
|
||||
|
||||
resolver.resolve(route as any, null).subscribe(resolved => {
|
||||
expect(resolved).toEqual(
|
||||
{
|
||||
...route.data.menu
|
||||
}
|
||||
);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('getDsoMenus', () => {
|
||||
it('should return as first part the item version list ', (done) => {
|
||||
const result = resolver.getDsoMenus(testObject, route, state);
|
||||
result[0].subscribe((menuList) => {
|
||||
expect(menuList.length).toEqual(1);
|
||||
expect(menuList[0].id).toEqual('version-dso');
|
||||
expect(menuList[0].active).toEqual(false);
|
||||
expect(menuList[0].visible).toEqual(true);
|
||||
expect(menuList[0].model.type).toEqual(MenuItemType.ONCLICK);
|
||||
expect(menuList[0].model.text).toEqual('message');
|
||||
expect(menuList[0].model.disabled).toEqual(false);
|
||||
expect(menuList[0].icon).toEqual('code-branch');
|
||||
done();
|
||||
});
|
||||
|
||||
});
|
||||
it('should return as second part the common list ', (done) => {
|
||||
const result = resolver.getDsoMenus(testObject, route, state);
|
||||
result[1].subscribe((menuList) => {
|
||||
expect(menuList.length).toEqual(1);
|
||||
expect(menuList[0].id).toEqual('edit-dso');
|
||||
expect(menuList[0].active).toEqual(false);
|
||||
expect(menuList[0].visible).toEqual(true);
|
||||
expect(menuList[0].model.type).toEqual(MenuItemType.LINK);
|
||||
expect(menuList[0].model.text).toEqual('item.page.edit');
|
||||
expect(menuList[0].model.link).toEqual('test-url/edit/metadata');
|
||||
expect(menuList[0].icon).toEqual('pencil-alt');
|
||||
done();
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
});
|
164
src/app/shared/dso-page/dso-edit-menu.resolver.ts
Normal file
164
src/app/shared/dso-page/dso-edit-menu.resolver.ts
Normal file
@@ -0,0 +1,164 @@
|
||||
import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router';
|
||||
import { combineLatest, Observable, of as observableOf } from 'rxjs';
|
||||
import { FeatureID } from '../../core/data/feature-authorization/feature-id';
|
||||
import { MenuID, MenuItemType } from '../menu/initial-menus-state';
|
||||
import { MenuService } from '../menu/menu.service';
|
||||
import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { LinkMenuItemModel } from '../menu/menu-item/models/link.model';
|
||||
import { Item } from '../../core/shared/item.model';
|
||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { OnClickMenuItemModel } from '../menu/menu-item/models/onclick.model';
|
||||
import { getFirstCompletedRemoteData } from '../../core/shared/operators';
|
||||
import { map, switchMap } from 'rxjs/operators';
|
||||
import { DSpaceObjectDataService } from '../../core/data/dspace-object-data.service';
|
||||
import { URLCombiner } from '../../core/url-combiner/url-combiner';
|
||||
import { DsoVersioningModalService } from './dso-versioning-modal-service/dso-versioning-modal.service';
|
||||
import { hasValue } from '../empty.util';
|
||||
import { MenuSection } from '../menu/menu.reducer';
|
||||
|
||||
/**
|
||||
* Creates the menus for the dspace object pages
|
||||
*/
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class DSOEditMenuResolver implements Resolve<{ [key: string]: MenuSection[] }> {
|
||||
|
||||
constructor(
|
||||
protected dSpaceObjectDataService: DSpaceObjectDataService,
|
||||
protected menuService: MenuService,
|
||||
protected authorizationService: AuthorizationDataService,
|
||||
protected modalService: NgbModal,
|
||||
protected dsoVersioningModalService: DsoVersioningModalService,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise all dspace object related menus
|
||||
*/
|
||||
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<{ [key: string]: MenuSection[] }> {
|
||||
const uuid = route.params.id;
|
||||
return this.dSpaceObjectDataService.findById(uuid, true, false).pipe(
|
||||
getFirstCompletedRemoteData(),
|
||||
switchMap((dsoRD) => {
|
||||
if (dsoRD.hasSucceeded) {
|
||||
const dso = dsoRD.payload;
|
||||
return combineLatest(this.getDsoMenus(dso, route, state)).pipe(
|
||||
map((combinedMenus) => [].concat.apply([], combinedMenus)),
|
||||
map((menus) => this.addDsoUuidToMenuIDs(menus, dso)),
|
||||
map((menus) => {
|
||||
return {
|
||||
...route.data?.menu,
|
||||
[MenuID.DSO_EDIT]: menus
|
||||
};
|
||||
})
|
||||
);
|
||||
} else {
|
||||
return observableOf({...route.data?.menu});
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all the menus for a dso based on the route and state
|
||||
*/
|
||||
getDsoMenus(dso, route, state) {
|
||||
return [
|
||||
this.getItemMenu(dso),
|
||||
this.getCommonMenu(dso, state)
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the common menus between all dspace objects
|
||||
*/
|
||||
protected getCommonMenu(dso, state): Observable<any[]> {
|
||||
return combineLatest([
|
||||
this.authorizationService.isAuthorized(FeatureID.CanEditMetadata, dso.self),
|
||||
]).pipe(
|
||||
map(([canEditItem]) => {
|
||||
return [
|
||||
{
|
||||
id: 'edit-dso',
|
||||
active: false,
|
||||
visible: canEditItem,
|
||||
model: {
|
||||
type: MenuItemType.LINK,
|
||||
text: this.getDsoType(dso) + '.page.edit',
|
||||
link: new URLCombiner(state.url, 'edit', 'metadata').toString()
|
||||
} as LinkMenuItemModel,
|
||||
icon: 'pencil-alt',
|
||||
index: 1
|
||||
},
|
||||
];
|
||||
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get item sepcific menus
|
||||
*/
|
||||
protected getItemMenu(dso): Observable<any[]> {
|
||||
if (dso instanceof Item) {
|
||||
return combineLatest([
|
||||
this.authorizationService.isAuthorized(FeatureID.CanCreateVersion, dso.self),
|
||||
this.dsoVersioningModalService.isNewVersionButtonDisabled(dso),
|
||||
this.dsoVersioningModalService.getVersioningTooltipMessage(dso, 'item.page.version.hasDraft', 'item.page.version.create')
|
||||
]).pipe(
|
||||
map(([canCreateVersion, disableVersioning, versionTooltip]) => {
|
||||
return [
|
||||
{
|
||||
id: 'version-dso',
|
||||
active: false,
|
||||
visible: canCreateVersion,
|
||||
model: {
|
||||
type: MenuItemType.ONCLICK,
|
||||
text: versionTooltip,
|
||||
disabled: disableVersioning,
|
||||
function: () => {
|
||||
this.dsoVersioningModalService.openCreateVersionModal(dso);
|
||||
}
|
||||
} as OnClickMenuItemModel,
|
||||
icon: 'code-branch',
|
||||
index: 0
|
||||
},
|
||||
];
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
return observableOf([]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the dso or entity type for an object to be used in generic messages
|
||||
*/
|
||||
protected getDsoType(dso) {
|
||||
const renderType = dso.getRenderTypes()[0];
|
||||
if (typeof renderType === 'string' || renderType instanceof String) {
|
||||
return renderType.toLowerCase();
|
||||
} else {
|
||||
return dso.type.toString().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the dso uuid to all provided menu ids and parent ids
|
||||
*/
|
||||
protected addDsoUuidToMenuIDs(menus, dso) {
|
||||
return menus.map((menu) => {
|
||||
Object.assign(menu, {
|
||||
id: menu.id + '-' + dso.uuid
|
||||
});
|
||||
if (hasValue(menu.parentID)) {
|
||||
Object.assign(menu, {
|
||||
parentID: menu.parentID + '-' + dso.uuid
|
||||
});
|
||||
}
|
||||
return menu;
|
||||
});
|
||||
}
|
||||
}
|
@@ -0,0 +1,18 @@
|
||||
<div class="dso-button-menu mb-1" ngbDropdown placement="bottom-right">
|
||||
<div class="d-flex flex-row flex-nowrap"
|
||||
[ngbTooltip]="itemModel.text | translate">
|
||||
<button class="btn btn-dark btn-sm" ngbDropdownToggle [disabled]="section.model.disabled">
|
||||
<i class="fas fa-{{section.icon}} fa-fw"></i>
|
||||
</button>
|
||||
<ul ngbDropdownMenu>
|
||||
<ng-container *ngFor="let subSection of (subSections$ | async)">
|
||||
<ng-container
|
||||
*ngComponentOutlet="(sectionMap$ | async).get(subSection.id).component; injector: (sectionMap$ | async).get(subSection.id).injector;"></ng-container>
|
||||
</ng-container>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
@@ -0,0 +1,26 @@
|
||||
.btn-dark {
|
||||
background-color: var(--ds-admin-sidebar-bg);
|
||||
}
|
||||
|
||||
.dso-button-menu {
|
||||
.dropdown-toggle::after {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
ul.dropdown-menu {
|
||||
background-color: var(--ds-admin-sidebar-bg);
|
||||
color: white;
|
||||
|
||||
::ng-deep a {
|
||||
color: white;
|
||||
|
||||
&.disabled {
|
||||
color: $btn-link-disabled-color;
|
||||
}
|
||||
}
|
||||
|
||||
.disabled {
|
||||
color: $btn-link-disabled-color;
|
||||
}
|
||||
}
|
@@ -0,0 +1,75 @@
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
import { MenuServiceStub } from '../../../testing/menu-service.stub';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { MenuService } from '../../../menu/menu.service';
|
||||
import { CSSVariableService } from '../../../sass-helper/sass-helper.service';
|
||||
import { CSSVariableServiceStub } from '../../../testing/css-variable-service.stub';
|
||||
import { Router } from '@angular/router';
|
||||
import { RouterStub } from '../../../testing/router.stub';
|
||||
import { of as observableOf } from 'rxjs';
|
||||
import { Component } from '@angular/core';
|
||||
import { DsoEditMenuExpandableSectionComponent } from './dso-edit-menu-expandable-section.component';
|
||||
import { MenuItemType } from '../../../menu/initial-menus-state';
|
||||
import { By } from '@angular/platform-browser';
|
||||
|
||||
describe('DsoEditMenuExpandableSectionComponent', () => {
|
||||
let component: DsoEditMenuExpandableSectionComponent;
|
||||
let fixture: ComponentFixture<DsoEditMenuExpandableSectionComponent>;
|
||||
const menuService = new MenuServiceStub();
|
||||
const iconString = 'test';
|
||||
|
||||
const dummySection = {
|
||||
id: 'dummy',
|
||||
active: false,
|
||||
visible: true,
|
||||
model: {
|
||||
type: MenuItemType.TEXT,
|
||||
disabled: false,
|
||||
text: 'text'
|
||||
},
|
||||
icon: iconString
|
||||
};
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [TranslateModule.forRoot()],
|
||||
declarations: [DsoEditMenuExpandableSectionComponent, TestComponent],
|
||||
providers: [
|
||||
{provide: 'sectionDataProvider', useValue: dummySection},
|
||||
{provide: MenuService, useValue: menuService},
|
||||
{provide: CSSVariableService, useClass: CSSVariableServiceStub},
|
||||
{provide: Router, useValue: new RouterStub()},
|
||||
]
|
||||
}).overrideComponent(DsoEditMenuExpandableSectionComponent, {
|
||||
set: {
|
||||
entryComponents: [TestComponent]
|
||||
}
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
spyOn(menuService, 'getSubSectionsByParentID').and.returnValue(observableOf([]));
|
||||
fixture = TestBed.createComponent(DsoEditMenuExpandableSectionComponent);
|
||||
component = fixture.componentInstance;
|
||||
spyOn(component as any, 'getMenuItemComponent').and.returnValue(TestComponent);
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should show a button with the icon', () => {
|
||||
const button = fixture.debugElement.query(By.css('.btn-dark'));
|
||||
expect(button.nativeElement.innerHTML).toContain('fa-' + iconString);
|
||||
});
|
||||
});
|
||||
|
||||
// declare a test component
|
||||
@Component({
|
||||
selector: 'ds-test-cmp',
|
||||
template: ``
|
||||
})
|
||||
class TestComponent {
|
||||
}
|
@@ -0,0 +1,39 @@
|
||||
import { Component, Inject, Injector } from '@angular/core';
|
||||
import { MenuID } from 'src/app/shared/menu/initial-menus-state';
|
||||
import { rendersSectionForMenu } from 'src/app/shared/menu/menu-section.decorator';
|
||||
import { MenuSectionComponent } from 'src/app/shared/menu/menu-section/menu-section.component';
|
||||
import { MenuService } from '../../../menu/menu.service';
|
||||
import { MenuSection } from '../../../menu/menu.reducer';
|
||||
import { Router } from '@angular/router';
|
||||
|
||||
/**
|
||||
* Represents an expandable section in the dso edit menus
|
||||
*/
|
||||
@Component({
|
||||
/* tslint:disable:component-selector */
|
||||
selector: 'ds-dso-edit-menu-expandable-section',
|
||||
templateUrl: './dso-edit-menu-expandable-section.component.html',
|
||||
styleUrls: ['./dso-edit-menu-expandable-section.component.scss'],
|
||||
})
|
||||
@rendersSectionForMenu(MenuID.DSO_EDIT, true)
|
||||
export class DsoEditMenuExpandableSectionComponent extends MenuSectionComponent {
|
||||
|
||||
menuID: MenuID = MenuID.DSO_EDIT;
|
||||
itemModel;
|
||||
|
||||
constructor(
|
||||
@Inject('sectionDataProvider') menuSection: MenuSection,
|
||||
protected menuService: MenuService,
|
||||
protected injector: Injector,
|
||||
protected router: Router,
|
||||
) {
|
||||
super(menuSection, menuService, injector);
|
||||
this.itemModel = menuSection.model;
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.menuService.activateSection(this.menuID, this.section.id);
|
||||
super.ngOnInit();
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,25 @@
|
||||
<div *ngIf="!canActivate" class="dso-button-menu mb-1"
|
||||
[title]="itemModel.text | translate"
|
||||
[ngbTooltip]="itemModel.text | translate">
|
||||
<a *ngIf="!section.model.disabled" class="d-flex flex-row flex-nowrap"
|
||||
[routerLink]="itemModel.link"
|
||||
href="javascript:void(0);">
|
||||
<button class="btn btn-dark btn-sm" [disabled]="section.model.disabled">
|
||||
<i class="fas fa-{{section.icon}} fa-fw"></i>
|
||||
</button>
|
||||
</a>
|
||||
<div *ngIf="section.model.disabled" class="d-flex flex-row flex-nowrap">
|
||||
<button class="btn btn-dark btn-sm" [disabled]="section.model.disabled">
|
||||
<i class="fas fa-{{section.icon}} fa-fw"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="canActivate" class="dso-button-menu mb-1"
|
||||
[title]="itemModel.text | translate"
|
||||
[ngbTooltip]="itemModel.text | translate">
|
||||
<button class="btn btn-dark btn-sm" [disabled]="section.model.disabled"
|
||||
(click)="activate($event)">
|
||||
<i class="fas fa-{{section.icon}} fa-fw"></i>
|
||||
</button>
|
||||
</div>
|
@@ -0,0 +1,3 @@
|
||||
.btn-dark {
|
||||
background-color: var(--ds-admin-sidebar-bg);
|
||||
}
|
@@ -0,0 +1,173 @@
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
import { MenuServiceStub } from '../../../testing/menu-service.stub';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { MenuService } from '../../../menu/menu.service';
|
||||
import { CSSVariableService } from '../../../sass-helper/sass-helper.service';
|
||||
import { CSSVariableServiceStub } from '../../../testing/css-variable-service.stub';
|
||||
import { Router } from '@angular/router';
|
||||
import { RouterStub } from '../../../testing/router.stub';
|
||||
import { of as observableOf } from 'rxjs';
|
||||
import { Component } from '@angular/core';
|
||||
import { MenuItemType } from '../../../menu/initial-menus-state';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { DsoEditMenuSectionComponent } from './dso-edit-menu-section.component';
|
||||
import { OnClickMenuItemModel } from '../../../menu/menu-item/models/onclick.model';
|
||||
|
||||
function initAsync(dummySectionText: { visible: boolean; icon: string; active: boolean; model: { disabled: boolean; text: string; type: MenuItemType }; id: string }, menuService: MenuServiceStub) {
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [TranslateModule.forRoot()],
|
||||
declarations: [DsoEditMenuSectionComponent, TestComponent],
|
||||
providers: [
|
||||
{provide: 'sectionDataProvider', useValue: dummySectionText},
|
||||
{provide: MenuService, useValue: menuService},
|
||||
{provide: CSSVariableService, useClass: CSSVariableServiceStub},
|
||||
{provide: Router, useValue: new RouterStub()},
|
||||
]
|
||||
}).overrideComponent(DsoEditMenuSectionComponent, {
|
||||
set: {
|
||||
entryComponents: [TestComponent]
|
||||
}
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
}
|
||||
|
||||
describe('DsoEditMenuSectionComponent', () => {
|
||||
let component: DsoEditMenuSectionComponent;
|
||||
let fixture: ComponentFixture<DsoEditMenuSectionComponent>;
|
||||
const menuService = new MenuServiceStub();
|
||||
const iconString = 'test';
|
||||
|
||||
const dummySectionText = {
|
||||
id: 'dummy',
|
||||
active: false,
|
||||
visible: true,
|
||||
model: {
|
||||
type: MenuItemType.TEXT,
|
||||
disabled: false,
|
||||
text: 'text'
|
||||
},
|
||||
icon: iconString
|
||||
};
|
||||
const dummySectionLink = {
|
||||
id: 'dummy',
|
||||
active: false,
|
||||
visible: true,
|
||||
model: {
|
||||
type: MenuItemType.LINK,
|
||||
disabled: false,
|
||||
text: 'text',
|
||||
link: 'link'
|
||||
},
|
||||
icon: iconString
|
||||
};
|
||||
|
||||
const dummySectionClick = {
|
||||
id: 'dummy',
|
||||
active: false,
|
||||
visible: true,
|
||||
model: {
|
||||
type: MenuItemType.ONCLICK,
|
||||
disabled: false,
|
||||
text: 'text',
|
||||
function: () => 'test'
|
||||
},
|
||||
icon: iconString
|
||||
};
|
||||
|
||||
describe('text model', () => {
|
||||
initAsync(dummySectionText, menuService);
|
||||
beforeEach(() => {
|
||||
spyOn(menuService, 'getSubSectionsByParentID').and.returnValue(observableOf([]));
|
||||
fixture = TestBed.createComponent(DsoEditMenuSectionComponent);
|
||||
component = fixture.componentInstance;
|
||||
spyOn(component as any, 'getMenuItemComponent').and.returnValue(TestComponent);
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should show a button with the icon', () => {
|
||||
const button = fixture.debugElement.query(By.css('.btn-dark'));
|
||||
expect(button.nativeElement.innerHTML).toContain('fa-' + iconString);
|
||||
});
|
||||
describe('when the section model in a disabled link or text', () => {
|
||||
it('should show just the button', () => {
|
||||
const textButton = fixture.debugElement.query(By.css('div div button'));
|
||||
expect(textButton.nativeElement.innerHTML).toContain('fa-' + iconString);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('on click model', () => {
|
||||
initAsync(dummySectionClick, menuService);
|
||||
beforeEach(() => {
|
||||
spyOn(menuService, 'getSubSectionsByParentID').and.returnValue(observableOf([]));
|
||||
fixture = TestBed.createComponent(DsoEditMenuSectionComponent);
|
||||
component = fixture.componentInstance;
|
||||
spyOn(component as any, 'getMenuItemComponent').and.returnValue(TestComponent);
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
describe('when the section model in an on click menu', () => {
|
||||
it('should call the activate method when clicking the button', () => {
|
||||
spyOn(component, 'activate');
|
||||
|
||||
const button = fixture.debugElement.query(By.css('.btn-dark'));
|
||||
button.triggerEventHandler('click', null);
|
||||
|
||||
expect(component.activate).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
describe('activate', () => {
|
||||
const mockEvent = jasmine.createSpyObj('event', {
|
||||
preventDefault: jasmine.createSpy('preventDefault'),
|
||||
stopPropagation: jasmine.createSpy('stopPropagation'),
|
||||
});
|
||||
it('should call the item model function when not disabled', () => {
|
||||
spyOn(component.section.model as OnClickMenuItemModel, 'function');
|
||||
component.activate(mockEvent);
|
||||
|
||||
expect((component.section.model as OnClickMenuItemModel).function).toHaveBeenCalled();
|
||||
});
|
||||
it('should call not the item model function when disabled', () => {
|
||||
spyOn(component.section.model as OnClickMenuItemModel, 'function');
|
||||
component.itemModel.disabled = true;
|
||||
component.activate(mockEvent);
|
||||
|
||||
expect((component.section.model as OnClickMenuItemModel).function).not.toHaveBeenCalled();
|
||||
component.itemModel.disabled = false;
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('link model', () => {
|
||||
initAsync(dummySectionLink, menuService);
|
||||
beforeEach(() => {
|
||||
spyOn(menuService, 'getSubSectionsByParentID').and.returnValue(observableOf([]));
|
||||
fixture = TestBed.createComponent(DsoEditMenuSectionComponent);
|
||||
component = fixture.componentInstance;
|
||||
spyOn(component as any, 'getMenuItemComponent').and.returnValue(TestComponent);
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
describe('when the section model in a non disabled link', () => {
|
||||
it('should show a link element with the button in it', () => {
|
||||
const link = fixture.debugElement.query(By.css('a'));
|
||||
expect(link.nativeElement.innerHTML).toContain('button');
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
// declare a test component
|
||||
@Component({
|
||||
selector: 'ds-test-cmp',
|
||||
template: ``
|
||||
})
|
||||
class TestComponent {
|
||||
}
|
@@ -0,0 +1,51 @@
|
||||
import { Component, Inject, Injector, OnInit } from '@angular/core';
|
||||
import { MenuID } from 'src/app/shared/menu/initial-menus-state';
|
||||
import { rendersSectionForMenu } from 'src/app/shared/menu/menu-section.decorator';
|
||||
import { MenuSectionComponent } from 'src/app/shared/menu/menu-section/menu-section.component';
|
||||
import { MenuService } from '../../../menu/menu.service';
|
||||
import { MenuSection } from '../../../menu/menu.reducer';
|
||||
import { isNotEmpty } from '../../../empty.util';
|
||||
|
||||
/**
|
||||
* Represents a non-expandable section in the dso edit menus
|
||||
*/
|
||||
@Component({
|
||||
/* tslint:disable:component-selector */
|
||||
selector: 'ds-dso-edit-menu-section',
|
||||
templateUrl: './dso-edit-menu-section.component.html',
|
||||
styleUrls: ['./dso-edit-menu-section.component.scss']
|
||||
})
|
||||
@rendersSectionForMenu(MenuID.DSO_EDIT, false)
|
||||
export class DsoEditMenuSectionComponent extends MenuSectionComponent implements OnInit {
|
||||
|
||||
menuID: MenuID = MenuID.DSO_EDIT;
|
||||
itemModel;
|
||||
hasLink: boolean;
|
||||
canActivate: boolean;
|
||||
|
||||
constructor(
|
||||
@Inject('sectionDataProvider') menuSection: MenuSection,
|
||||
protected menuService: MenuService,
|
||||
protected injector: Injector,
|
||||
) {
|
||||
super(menuSection, menuService, injector);
|
||||
this.itemModel = menuSection.model;
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.hasLink = isNotEmpty(this.itemModel?.link);
|
||||
this.canActivate = isNotEmpty(this.itemModel?.function);
|
||||
super.ngOnInit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Activate the section's model funtion
|
||||
*/
|
||||
public activate(event: any) {
|
||||
event.preventDefault();
|
||||
if (!this.itemModel.disabled) {
|
||||
this.itemModel.function();
|
||||
}
|
||||
event.stopPropagation();
|
||||
}
|
||||
}
|
@@ -0,0 +1,6 @@
|
||||
<div class="dso-edit-menu d-flex flex-wrap">
|
||||
<div *ngFor="let section of (sections | async)" class="ml-1">
|
||||
<ng-container
|
||||
*ngComponentOutlet="(sectionMap$ | async).get(section.id)?.component; injector: (sectionMap$ | async).get(section.id)?.injector;"></ng-container>
|
||||
</div>
|
||||
</div>
|
@@ -0,0 +1,78 @@
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { ChangeDetectionStrategy, Injector, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { of as observableOf } from 'rxjs';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { DsoEditMenuComponent } from './dso-edit-menu.component';
|
||||
import { MenuServiceStub } from '../../testing/menu-service.stub';
|
||||
import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service';
|
||||
import { AuthService } from '../../../core/auth/auth.service';
|
||||
import { AuthServiceStub } from '../../testing/auth-service.stub';
|
||||
import { MenuService } from '../../menu/menu.service';
|
||||
import {
|
||||
ExpandableNavbarSectionComponent
|
||||
} from '../../../navbar/expandable-navbar-section/expandable-navbar-section.component';
|
||||
import { MenuItemModel } from '../../menu/menu-item/models/menu-item.model';
|
||||
|
||||
describe('DsoEditMenuComponent', () => {
|
||||
let comp: DsoEditMenuComponent;
|
||||
let fixture: ComponentFixture<DsoEditMenuComponent>;
|
||||
const menuService = new MenuServiceStub();
|
||||
let authorizationService: AuthorizationDataService;
|
||||
|
||||
const routeStub = {
|
||||
children: []
|
||||
};
|
||||
|
||||
const section = {
|
||||
id: 'edit-dso',
|
||||
active: false,
|
||||
visible: true,
|
||||
model: {
|
||||
type: null,
|
||||
disabled: false,
|
||||
} as MenuItemModel,
|
||||
icon: 'pencil-alt',
|
||||
index: 1
|
||||
};
|
||||
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
authorizationService = jasmine.createSpyObj('authorizationService', {
|
||||
isAuthorized: observableOf(true)
|
||||
});
|
||||
spyOn(menuService, 'getMenuTopSections').and.returnValue(observableOf([section]));
|
||||
TestBed.configureTestingModule({
|
||||
imports: [TranslateModule.forRoot(), RouterTestingModule],
|
||||
declarations: [DsoEditMenuComponent],
|
||||
providers: [
|
||||
Injector,
|
||||
{provide: MenuService, useValue: menuService},
|
||||
{provide: AuthService, useClass: AuthServiceStub},
|
||||
{provide: ActivatedRoute, useValue: routeStub},
|
||||
{provide: AuthorizationDataService, useValue: authorizationService},
|
||||
],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
}).overrideComponent(ExpandableNavbarSectionComponent, {
|
||||
set: {
|
||||
changeDetection: ChangeDetectionStrategy.Default,
|
||||
}
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(DsoEditMenuComponent);
|
||||
comp = fixture.componentInstance;
|
||||
comp.sections = observableOf([]);
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
describe('onInit', () => {
|
||||
it('should create', () => {
|
||||
expect(comp).toBeTruthy();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@@ -0,0 +1,36 @@
|
||||
import { Component, Injector } from '@angular/core';
|
||||
import { AuthorizationDataService } from 'src/app/core/data/feature-authorization/authorization-data.service';
|
||||
import { MenuID } from '../../menu/initial-menus-state';
|
||||
import { MenuComponent } from '../../menu/menu.component';
|
||||
import { MenuService } from '../../menu/menu.service';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { AuthService } from '../../../core/auth/auth.service';
|
||||
|
||||
/**
|
||||
* Component representing the edit menu and other menus on the dspace object pages
|
||||
*/
|
||||
@Component({
|
||||
selector: 'ds-dso-edit-menu',
|
||||
styleUrls: ['./dso-edit-menu.component.scss'],
|
||||
templateUrl: './dso-edit-menu.component.html',
|
||||
})
|
||||
export class DsoEditMenuComponent extends MenuComponent {
|
||||
/**
|
||||
* The menu ID of this component is DSO_EDIT
|
||||
* @type {MenuID.DSO_EDIT}
|
||||
*/
|
||||
menuID = MenuID.DSO_EDIT;
|
||||
|
||||
constructor(protected menuService: MenuService,
|
||||
protected injector: Injector,
|
||||
public authorizationService: AuthorizationDataService,
|
||||
public route: ActivatedRoute,
|
||||
private authService: AuthService,
|
||||
) {
|
||||
super(menuService, injector, authorizationService, route);
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
super.ngOnInit();
|
||||
}
|
||||
}
|
@@ -1,7 +0,0 @@
|
||||
<a *ngIf="isAuthorized$ | async"
|
||||
[routerLink]="[pageRoute, 'edit', 'metadata']"
|
||||
class="edit-button btn btn-dark btn-sm"
|
||||
[ngbTooltip]="tooltipMsg | translate"
|
||||
role="button" [title]="tooltipMsg |translate" [attr.aria-label]="tooltipMsg |translate">
|
||||
<i class="fas fa-pencil-alt fa-fw"></i>
|
||||
</a>
|
@@ -1,3 +0,0 @@
|
||||
.btn-dark {
|
||||
background-color: var(--ds-admin-sidebar-bg);
|
||||
}
|
@@ -1,76 +0,0 @@
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
import { DsoPageEditButtonComponent } from './dso-page-edit-button.component';
|
||||
import { DSpaceObject } from '../../../core/shared/dspace-object.model';
|
||||
import { Item } from '../../../core/shared/item.model';
|
||||
import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service';
|
||||
import { of as observableOf } from 'rxjs';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { FeatureID } from '../../../core/data/feature-authorization/feature-id';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
||||
|
||||
describe('DsoPageEditButtonComponent', () => {
|
||||
let component: DsoPageEditButtonComponent;
|
||||
let fixture: ComponentFixture<DsoPageEditButtonComponent>;
|
||||
|
||||
let authorizationService: AuthorizationDataService;
|
||||
let dso: DSpaceObject;
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
dso = Object.assign(new Item(), {
|
||||
id: 'test-item',
|
||||
_links: {
|
||||
self: { href: 'test-item-selflink' }
|
||||
}
|
||||
});
|
||||
authorizationService = jasmine.createSpyObj('authorizationService', {
|
||||
isAuthorized: observableOf(true)
|
||||
});
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [DsoPageEditButtonComponent],
|
||||
imports: [TranslateModule.forRoot(), RouterTestingModule.withRoutes([]), NgbModule],
|
||||
providers: [
|
||||
{ provide: AuthorizationDataService, useValue: authorizationService }
|
||||
]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(DsoPageEditButtonComponent);
|
||||
component = fixture.componentInstance;
|
||||
component.dso = dso;
|
||||
component.pageRoute = 'test';
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should check the authorization of the current user', () => {
|
||||
expect(authorizationService.isAuthorized).toHaveBeenCalledWith(FeatureID.CanEditMetadata, dso.self);
|
||||
});
|
||||
|
||||
describe('when the user is authorized', () => {
|
||||
beforeEach(() => {
|
||||
(authorizationService.isAuthorized as jasmine.Spy).and.returnValue(observableOf(true));
|
||||
component.ngOnInit();
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should render a link', () => {
|
||||
const link = fixture.debugElement.query(By.css('a'));
|
||||
expect(link).not.toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the user is not authorized', () => {
|
||||
beforeEach(() => {
|
||||
(authorizationService.isAuthorized as jasmine.Spy).and.returnValue(observableOf(false));
|
||||
component.ngOnInit();
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should not render a link', () => {
|
||||
const link = fixture.debugElement.query(By.css('a'));
|
||||
expect(link).toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
@@ -1,43 +0,0 @@
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
import { DSpaceObject } from '../../../core/shared/dspace-object.model';
|
||||
import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service';
|
||||
import { Observable } from 'rxjs';
|
||||
import { FeatureID } from '../../../core/data/feature-authorization/feature-id';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-dso-page-edit-button',
|
||||
templateUrl: './dso-page-edit-button.component.html',
|
||||
styleUrls: ['./dso-page-edit-button.component.scss']
|
||||
})
|
||||
/**
|
||||
* Display a button linking to the edit page of a DSpaceObject
|
||||
*/
|
||||
export class DsoPageEditButtonComponent implements OnInit {
|
||||
/**
|
||||
* The DSpaceObject to display a button to the edit page for
|
||||
*/
|
||||
@Input() dso: DSpaceObject;
|
||||
|
||||
/**
|
||||
* The prefix of the route to the edit page (before the object's UUID, e.g. "items")
|
||||
*/
|
||||
@Input() pageRoute: string;
|
||||
|
||||
/**
|
||||
* A message for the tooltip on the button
|
||||
* Supports i18n keys
|
||||
*/
|
||||
@Input() tooltipMsg: string;
|
||||
|
||||
/**
|
||||
* Whether or not the current user is authorized to edit the DSpaceObject
|
||||
*/
|
||||
isAuthorized$: Observable<boolean>;
|
||||
|
||||
constructor(protected authorizationService: AuthorizationDataService) { }
|
||||
|
||||
ngOnInit() {
|
||||
this.isAuthorized$ = this.authorizationService.isAuthorized(FeatureID.CanEditMetadata, this.dso.self);
|
||||
}
|
||||
|
||||
}
|
@@ -1,8 +0,0 @@
|
||||
<button *ngIf="isAuthorized$ | async"
|
||||
class="edit-button btn btn-dark btn-sm"
|
||||
(click)="createNewVersion()"
|
||||
[disabled]="disableNewVersionButton$ | async"
|
||||
[ngbTooltip]="tooltipMsg$ | async | translate"
|
||||
role="button" [title]="tooltipMsg$ | async |translate" [attr.aria-label]="tooltipMsg$ | async | translate">
|
||||
<i class="fas fa-code-branch fa-fw"></i>
|
||||
</button>
|
@@ -1,3 +0,0 @@
|
||||
.btn-dark {
|
||||
background-color: var(--ds-admin-sidebar-bg);
|
||||
}
|
@@ -1,96 +0,0 @@
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
import { DsoPageVersionButtonComponent } from './dso-page-version-button.component';
|
||||
import { Item } from '../../../core/shared/item.model';
|
||||
import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service';
|
||||
import { Observable, of, of as observableOf } from 'rxjs';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { FeatureID } from '../../../core/data/feature-authorization/feature-id';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { VersionHistoryDataService } from '../../../core/data/version-history-data.service';
|
||||
|
||||
describe('DsoPageVersionButtonComponent', () => {
|
||||
let component: DsoPageVersionButtonComponent;
|
||||
let fixture: ComponentFixture<DsoPageVersionButtonComponent>;
|
||||
|
||||
let authorizationService: AuthorizationDataService;
|
||||
let versionHistoryService: VersionHistoryDataService;
|
||||
|
||||
let dso: Item;
|
||||
let tooltipMsg: Observable<string>;
|
||||
|
||||
const authorizationServiceSpy = jasmine.createSpyObj('authorizationService', ['isAuthorized']);
|
||||
|
||||
const versionHistoryServiceSpy = jasmine.createSpyObj('versionHistoryService',
|
||||
['getVersions', 'getLatestVersionFromHistory$', 'isLatest$', 'hasDraftVersion$']
|
||||
);
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
dso = Object.assign(new Item(), {
|
||||
id: 'test-item',
|
||||
_links: {
|
||||
self: { href: 'test-item-selflink' },
|
||||
version: { href: 'test-item-version-selflink' },
|
||||
},
|
||||
});
|
||||
tooltipMsg = of('tooltip-msg');
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [DsoPageVersionButtonComponent],
|
||||
imports: [TranslateModule.forRoot(), RouterTestingModule.withRoutes([]), NgbModule],
|
||||
providers: [
|
||||
{ provide: AuthorizationDataService, useValue: authorizationServiceSpy },
|
||||
{ provide: VersionHistoryDataService, useValue: versionHistoryServiceSpy },
|
||||
]
|
||||
}).compileComponents();
|
||||
|
||||
authorizationService = TestBed.inject(AuthorizationDataService);
|
||||
versionHistoryService = TestBed.inject(VersionHistoryDataService);
|
||||
|
||||
versionHistoryServiceSpy.hasDraftVersion$.and.returnValue(observableOf(true));
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(DsoPageVersionButtonComponent);
|
||||
component = fixture.componentInstance;
|
||||
component.dso = dso;
|
||||
component.tooltipMsg$ = tooltipMsg;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should check the authorization of the current user', () => {
|
||||
expect(authorizationService.isAuthorized).toHaveBeenCalledWith(FeatureID.CanCreateVersion, dso.self);
|
||||
});
|
||||
|
||||
it('should check if the item has a draft version', () => {
|
||||
expect(versionHistoryServiceSpy.hasDraftVersion$).toHaveBeenCalledWith(dso._links.version.href);
|
||||
});
|
||||
|
||||
describe('when the user is authorized', () => {
|
||||
beforeEach(() => {
|
||||
authorizationServiceSpy.isAuthorized.and.returnValue(observableOf(true));
|
||||
component.ngOnInit();
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should render a button', () => {
|
||||
const button = fixture.debugElement.query(By.css('button'));
|
||||
expect(button).not.toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the user is not authorized', () => {
|
||||
beforeEach(() => {
|
||||
authorizationServiceSpy.isAuthorized.and.returnValue(observableOf(false));
|
||||
component.ngOnInit();
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should render a button', () => {
|
||||
const button = fixture.debugElement.query(By.css('button'));
|
||||
expect(button).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
@@ -1,77 +0,0 @@
|
||||
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
||||
import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service';
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { FeatureID } from '../../../core/data/feature-authorization/feature-id';
|
||||
import { VersionHistoryDataService } from '../../../core/data/version-history-data.service';
|
||||
import { Item } from '../../../core/shared/item.model';
|
||||
import { map, startWith, switchMap } from 'rxjs/operators';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-dso-page-version-button',
|
||||
templateUrl: './dso-page-version-button.component.html',
|
||||
styleUrls: ['./dso-page-version-button.component.scss']
|
||||
})
|
||||
/**
|
||||
* Display a button linking to the edit page of a DSpaceObject
|
||||
*/
|
||||
export class DsoPageVersionButtonComponent implements OnInit {
|
||||
/**
|
||||
* The item for which display a button to create a new version
|
||||
*/
|
||||
@Input() dso: Item;
|
||||
|
||||
/**
|
||||
* A message for the tooltip on the button
|
||||
* Supports i18n keys
|
||||
*/
|
||||
@Input() tooltipMsgCreate: string;
|
||||
|
||||
/**
|
||||
* A message for the tooltip on the button (when is disabled)
|
||||
* Supports i18n keys
|
||||
*/
|
||||
@Input() tooltipMsgHasDraft: string;
|
||||
|
||||
/**
|
||||
* Emits an event that triggers the creation of the new version
|
||||
*/
|
||||
@Output() newVersionEvent = new EventEmitter();
|
||||
|
||||
/**
|
||||
* Whether or not the current user is authorized to create a new version of the DSpaceObject
|
||||
*/
|
||||
isAuthorized$: Observable<boolean>;
|
||||
|
||||
disableNewVersionButton$: Observable<boolean>;
|
||||
|
||||
tooltipMsg$: Observable<string>;
|
||||
|
||||
constructor(
|
||||
protected authorizationService: AuthorizationDataService,
|
||||
protected versionHistoryService: VersionHistoryDataService,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new version for the current item
|
||||
*/
|
||||
createNewVersion() {
|
||||
this.newVersionEvent.emit();
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.isAuthorized$ = this.authorizationService.isAuthorized(FeatureID.CanCreateVersion, this.dso.self);
|
||||
|
||||
this.disableNewVersionButton$ = this.versionHistoryService.hasDraftVersion$(this.dso._links.version.href).pipe(
|
||||
// button is disabled if hasDraftVersion = true, and enabled if hasDraftVersion = false or null
|
||||
// (hasDraftVersion is null when a version history does not exist)
|
||||
map((res) => Boolean(res)),
|
||||
startWith(true),
|
||||
);
|
||||
|
||||
this.tooltipMsg$ = this.disableNewVersionButton$.pipe(
|
||||
switchMap((hasDraftVersion) => of(hasDraftVersion ? this.tooltipMsgHasDraft : this.tooltipMsgCreate)),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,92 @@
|
||||
import { DsoVersioningModalService } from './dso-versioning-modal.service';
|
||||
import { waitForAsync } from '@angular/core/testing';
|
||||
import { createSuccessfulRemoteDataObject$ } from '../../remote-data.utils';
|
||||
import { Version } from '../../../core/shared/version.model';
|
||||
import { Item } from '../../../core/shared/item.model';
|
||||
import { MetadataMap } from '../../../core/shared/metadata.models';
|
||||
import { createRelationshipsObservable } from '../../../item-page/simple/item-types/shared/item.component.spec';
|
||||
import { buildPaginatedList } from '../../../core/data/paginated-list.model';
|
||||
import { PageInfo } from '../../../core/shared/page-info.model';
|
||||
import { EMPTY, of as observableOf } from 'rxjs';
|
||||
|
||||
fdescribe('DsoVersioningModalService', () => {
|
||||
let service: DsoVersioningModalService;
|
||||
let modalService;
|
||||
let versionService;
|
||||
let versionHistoryService;
|
||||
let itemVersionShared;
|
||||
let router;
|
||||
let workspaceItemDataService;
|
||||
let itemService;
|
||||
|
||||
const mockItem: Item = Object.assign(new Item(), {
|
||||
bundles: createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), [])),
|
||||
metadata: new MetadataMap(),
|
||||
relationships: createRelationshipsObservable(),
|
||||
_links: {
|
||||
self: {
|
||||
href: 'item-href'
|
||||
},
|
||||
version: {
|
||||
href: 'version-href'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
modalService = jasmine.createSpyObj('modalService', {
|
||||
open: {componentInstance: {firstVersion: {}, versionNumber: {}, createVersionEvent: EMPTY}}
|
||||
});
|
||||
versionService = jasmine.createSpyObj('versionService', {
|
||||
findByHref: createSuccessfulRemoteDataObject$<Version>(new Version()),
|
||||
});
|
||||
versionHistoryService = jasmine.createSpyObj('versionHistoryService', {
|
||||
createVersion: createSuccessfulRemoteDataObject$<Version>(new Version()),
|
||||
hasDraftVersion$: observableOf(false)
|
||||
});
|
||||
itemVersionShared = jasmine.createSpyObj('itemVersionShared', ['notifyCreateNewVersion']);
|
||||
router = jasmine.createSpyObj('router', ['navigateByUrl']);
|
||||
workspaceItemDataService = jasmine.createSpyObj('workspaceItemDataService', ['findByItem']);
|
||||
itemService = jasmine.createSpyObj('itemService', ['findByHref']);
|
||||
|
||||
service = new DsoVersioningModalService(
|
||||
modalService,
|
||||
versionService,
|
||||
versionHistoryService,
|
||||
itemVersionShared,
|
||||
router,
|
||||
workspaceItemDataService,
|
||||
itemService
|
||||
);
|
||||
}));
|
||||
describe('when onCreateNewVersion() is called', () => {
|
||||
it('should call versionService.findByHref', () => {
|
||||
service.openCreateVersionModal(mockItem);
|
||||
expect(versionService.findByHref).toHaveBeenCalledWith('version-href');
|
||||
});
|
||||
});
|
||||
|
||||
describe('isNewVersionButtonDisabled', () => {
|
||||
it('should call versionHistoryService.hasDraftVersion$', () => {
|
||||
service.isNewVersionButtonDisabled(mockItem);
|
||||
expect(versionHistoryService.hasDraftVersion$).toHaveBeenCalledWith(mockItem._links.version.href);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getVersioningTooltipMessage', () => {
|
||||
it('should return the create message when isNewVersionButtonDisabled returns false', (done) => {
|
||||
spyOn(service, 'isNewVersionButtonDisabled').and.returnValue(observableOf(false));
|
||||
service.getVersioningTooltipMessage(mockItem, 'draft-message', 'create-message').subscribe((message) => {
|
||||
expect(message).toEqual('create-message');
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('should return the draft message when isNewVersionButtonDisabled returns true', (done) => {
|
||||
spyOn(service, 'isNewVersionButtonDisabled').and.returnValue(observableOf(true));
|
||||
service.getVersioningTooltipMessage(mockItem, 'draft-message', 'create-message').subscribe((message) => {
|
||||
expect(message).toEqual('draft-message');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@@ -0,0 +1,98 @@
|
||||
import {
|
||||
ItemVersionsSummaryModalComponent
|
||||
} from '../../item/item-versions/item-versions-summary-modal/item-versions-summary-modal.component';
|
||||
import { getFirstCompletedRemoteData, getFirstSucceededRemoteDataPayload } from '../../../core/shared/operators';
|
||||
import { RemoteData } from '../../../core/data/remote-data';
|
||||
import { Version } from '../../../core/shared/version.model';
|
||||
import { map, startWith, switchMap, tap } from 'rxjs/operators';
|
||||
import { Item } from '../../../core/shared/item.model';
|
||||
import { WorkspaceItem } from '../../../core/submission/models/workspaceitem.model';
|
||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { VersionDataService } from '../../../core/data/version-data.service';
|
||||
import { VersionHistoryDataService } from '../../../core/data/version-history-data.service';
|
||||
import { ItemVersionsSharedService } from '../../item/item-versions/item-versions-shared.service';
|
||||
import { Router } from '@angular/router';
|
||||
import { WorkspaceitemDataService } from '../../../core/submission/workspaceitem-data.service';
|
||||
import { ItemDataService } from '../../../core/data/item-data.service';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Observable, of } from 'rxjs';
|
||||
|
||||
/**
|
||||
* Service to take care of all the functionality related to the version creation modal
|
||||
*/
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class DsoVersioningModalService {
|
||||
|
||||
constructor(
|
||||
protected modalService: NgbModal,
|
||||
protected versionService: VersionDataService,
|
||||
protected versionHistoryService: VersionHistoryDataService,
|
||||
protected itemVersionShared: ItemVersionsSharedService,
|
||||
protected router: Router,
|
||||
protected workspaceItemDataService: WorkspaceitemDataService,
|
||||
protected itemService: ItemDataService,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Open the create version modal for the provided dso
|
||||
*/
|
||||
openCreateVersionModal(dso): void {
|
||||
|
||||
const item = dso;
|
||||
const versionHref = item._links.version.href;
|
||||
|
||||
// Open modal
|
||||
const activeModal = this.modalService.open(ItemVersionsSummaryModalComponent);
|
||||
|
||||
// Show current version in modal
|
||||
this.versionService.findByHref(versionHref).pipe(getFirstCompletedRemoteData()).subscribe((res: RemoteData<Version>) => {
|
||||
// if res.hasNoContent then the item is unversioned
|
||||
activeModal.componentInstance.firstVersion = res.hasNoContent;
|
||||
activeModal.componentInstance.versionNumber = (res.hasNoContent ? undefined : res.payload.version);
|
||||
});
|
||||
|
||||
// On createVersionEvent emitted create new version and notify
|
||||
activeModal.componentInstance.createVersionEvent.pipe(
|
||||
switchMap((summary: string) => this.versionHistoryService.createVersion(item._links.self.href, summary)),
|
||||
getFirstCompletedRemoteData(),
|
||||
// show success/failure notification
|
||||
tap((res: RemoteData<Version>) => {
|
||||
this.itemVersionShared.notifyCreateNewVersion(res);
|
||||
}),
|
||||
// get workspace item
|
||||
getFirstSucceededRemoteDataPayload<Version>(),
|
||||
switchMap((newVersion: Version) => this.itemService.findByHref(newVersion._links.item.href)),
|
||||
getFirstSucceededRemoteDataPayload<Item>(),
|
||||
switchMap((newVersionItem: Item) => this.workspaceItemDataService.findByItem(newVersionItem.uuid, true, false)),
|
||||
getFirstSucceededRemoteDataPayload<WorkspaceItem>(),
|
||||
).subscribe((wsItem) => {
|
||||
const wsiId = wsItem.id;
|
||||
const route = 'workspaceitems/' + wsiId + '/edit';
|
||||
this.router.navigateByUrl(route);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the new version button should be disabled for the provided dso
|
||||
*/
|
||||
isNewVersionButtonDisabled(dso): Observable<boolean> {
|
||||
return this.versionHistoryService.hasDraftVersion$(dso._links.version.href).pipe(
|
||||
// button is disabled if hasDraftVersion = true, and enabled if hasDraftVersion = false or null
|
||||
// (hasDraftVersion is null when a version history does not exist)
|
||||
map((res) => Boolean(res)),
|
||||
startWith(true),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks and returns the tooltip that needs to be used for the create version button tooltip
|
||||
*/
|
||||
getVersioningTooltipMessage(dso, tooltipMsgHasDraft, tooltipMsgCreate): Observable<string> {
|
||||
return this.isNewVersionButtonDisabled(dso).pipe(
|
||||
switchMap((hasDraftVersion) => of(hasDraftVersion ? tooltipMsgHasDraft : tooltipMsgCreate)),
|
||||
);
|
||||
}
|
||||
}
|
@@ -5,7 +5,8 @@ import { MenusState } from './menu.reducer';
|
||||
*/
|
||||
export enum MenuID {
|
||||
ADMIN = 'admin-sidebar',
|
||||
PUBLIC = 'public'
|
||||
PUBLIC = 'public',
|
||||
DSO_EDIT = 'dso-edit'
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -36,5 +37,14 @@ export const initialMenusState: MenusState = {
|
||||
visible: true,
|
||||
sections: {},
|
||||
sectionToSubsectionIndex: {}
|
||||
}
|
||||
},
|
||||
[MenuID.DSO_EDIT]:
|
||||
{
|
||||
id: MenuID.DSO_EDIT,
|
||||
collapsed: true,
|
||||
previewCollapsed: true,
|
||||
visible: false,
|
||||
sections: {},
|
||||
sectionToSubsectionIndex: {}
|
||||
},
|
||||
};
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<a class="nav-item nav-link"
|
||||
[ngClass]="{ 'disabled': !hasLink }"
|
||||
[attr.aria-disabled]="!hasLink"
|
||||
[ngClass]="{ 'disabled': !hasLink || item.disabled }"
|
||||
[attr.aria-disabled]="!hasLink || item.disabled"
|
||||
[title]="item.text | translate"
|
||||
[routerLink]="getRouterLink()"
|
||||
(click)="$event.stopPropagation()"
|
||||
|
@@ -37,7 +37,7 @@ export class LinkMenuItemComponent implements OnInit {
|
||||
|
||||
navigate(event: any) {
|
||||
event.preventDefault();
|
||||
if (this.getRouterLink()) {
|
||||
if (!this.item.disabled && this.getRouterLink()) {
|
||||
this.router.navigate([this.getRouterLink()]);
|
||||
}
|
||||
event.stopPropagation();
|
||||
|
@@ -6,5 +6,6 @@ import { MenuItemModel } from './menu-item.model';
|
||||
*/
|
||||
export class AltmetricMenuItemModel implements MenuItemModel {
|
||||
type = MenuItemType.ALTMETRIC;
|
||||
disabled: boolean;
|
||||
url: string;
|
||||
}
|
||||
|
@@ -6,6 +6,7 @@ import { MenuItemType } from '../../initial-menus-state';
|
||||
*/
|
||||
export class LinkMenuItemModel implements MenuItemModel {
|
||||
type = MenuItemType.LINK;
|
||||
disabled: boolean;
|
||||
text: string;
|
||||
link: string;
|
||||
}
|
||||
|
@@ -5,4 +5,5 @@ import { MenuItemType } from '../../initial-menus-state';
|
||||
*/
|
||||
export interface MenuItemModel {
|
||||
type: MenuItemType;
|
||||
disabled: boolean;
|
||||
}
|
||||
|
@@ -6,6 +6,7 @@ import { MenuItemType } from '../../initial-menus-state';
|
||||
*/
|
||||
export class OnClickMenuItemModel implements MenuItemModel {
|
||||
type = MenuItemType.ONCLICK;
|
||||
disabled: boolean;
|
||||
text: string;
|
||||
function: () => {};
|
||||
}
|
||||
|
@@ -6,6 +6,7 @@ import { MenuItemModel } from './menu-item.model';
|
||||
*/
|
||||
export class SearchMenuItemModel implements MenuItemModel {
|
||||
type = MenuItemType.SEARCH;
|
||||
disabled: boolean;
|
||||
placeholder: string;
|
||||
action: string;
|
||||
}
|
||||
|
@@ -6,5 +6,6 @@ import { MenuItemModel } from './menu-item.model';
|
||||
*/
|
||||
export class TextMenuItemModel implements MenuItemModel {
|
||||
type = MenuItemType.TEXT;
|
||||
disabled: boolean;
|
||||
text: string;
|
||||
}
|
||||
|
@@ -1,4 +1,5 @@
|
||||
<a href="javascript:void(0);"
|
||||
<a *ngIf="!item.disabled"
|
||||
href="javascript:void(0);"
|
||||
class="nav-item nav-link"
|
||||
role="button"
|
||||
[title]="item.text | translate"
|
||||
@@ -6,3 +7,4 @@
|
||||
(keyup.space)="activate($event)"
|
||||
(keyup.enter)="activate($event)"
|
||||
>{{item.text | translate}}</a>
|
||||
<span *ngIf="item.disabled" class="nav-item nav-link disabled">{{item.text | translate}}</span>
|
||||
|
@@ -14,13 +14,16 @@ import { OnClickMenuItemModel } from './models/onclick.model';
|
||||
@rendersMenuItemForType(MenuItemType.ONCLICK)
|
||||
export class OnClickMenuItemComponent {
|
||||
item: OnClickMenuItemModel;
|
||||
|
||||
constructor(@Inject('itemModelProvider') item: OnClickMenuItemModel) {
|
||||
this.item = item;
|
||||
}
|
||||
|
||||
public activate(event: any) {
|
||||
if (!this.item.disabled) {
|
||||
event.preventDefault();
|
||||
this.item.function();
|
||||
event.stopPropagation();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1 +1 @@
|
||||
<span>{{item.text | translate}}</span>
|
||||
<span [class.disabled]="item.disabled">{{item.text | translate}}</span>
|
||||
|
@@ -64,8 +64,10 @@ export class MenuSectionComponent implements OnInit, OnDestroy {
|
||||
*/
|
||||
toggleSection(event: Event) {
|
||||
event.preventDefault();
|
||||
if (!this.section.model?.disabled) {
|
||||
this.menuService.toggleActiveSection(this.menuID, this.section.id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Activate this section
|
||||
@@ -73,8 +75,10 @@ export class MenuSectionComponent implements OnInit, OnDestroy {
|
||||
*/
|
||||
activateSection(event: Event) {
|
||||
event.preventDefault();
|
||||
if (!this.section.model?.disabled) {
|
||||
this.menuService.activateSection(this.menuID, this.section.id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deactivate this section
|
||||
|
@@ -157,8 +157,6 @@ import { SidebarSearchListElementComponent } from './object-list/sidebar-search-
|
||||
import { CollectionSidebarSearchListElementComponent } from './object-list/sidebar-search-list-element/collection/collection-sidebar-search-list-element.component';
|
||||
import { CommunitySidebarSearchListElementComponent } from './object-list/sidebar-search-list-element/community/community-sidebar-search-list-element.component';
|
||||
import { AuthorizedCollectionSelectorComponent } from './dso-selector/dso-selector/authorized-collection-selector/authorized-collection-selector.component';
|
||||
import { DsoPageEditButtonComponent } from './dso-page/dso-page-edit-button/dso-page-edit-button.component';
|
||||
import { DsoPageVersionButtonComponent } from './dso-page/dso-page-version-button/dso-page-version-button.component';
|
||||
import { HoverClassDirective } from './hover-class.directive';
|
||||
import { ValidationSuggestionsComponent } from './input-suggestions/validation-suggestions/validation-suggestions.component';
|
||||
import { ItemAlertsComponent } from './item/item-alerts/item-alerts.component';
|
||||
@@ -177,6 +175,13 @@ import { ScopeSelectorModalComponent } from './search-form/scope-selector-modal/
|
||||
import { BitstreamRequestACopyPageComponent } from './bitstream-request-a-copy-page/bitstream-request-a-copy-page.component';
|
||||
import { DsSelectComponent } from './ds-select/ds-select.component';
|
||||
import { LogInOidcComponent } from './log-in/methods/oidc/log-in-oidc.component';
|
||||
import {
|
||||
DsoEditMenuSectionComponent
|
||||
} from './dso-page/dso-edit-menu/dso-edit-menu-section/dso-edit-menu-section.component';
|
||||
import { DsoEditMenuComponent } from './dso-page/dso-edit-menu/dso-edit-menu.component';
|
||||
import {
|
||||
DsoEditMenuExpandableSectionComponent
|
||||
} from './dso-page/dso-edit-menu/dso-edit-expandable-menu-section/dso-edit-menu-expandable-section.component';
|
||||
|
||||
const MODULES = [
|
||||
// Do NOT include UniversalModule, HttpModule, or JsonpModule here
|
||||
@@ -402,18 +407,20 @@ const ENTRY_COMPONENTS = [
|
||||
OnClickMenuItemComponent,
|
||||
TextMenuItemComponent,
|
||||
ScopeSelectorModalComponent,
|
||||
DsoEditMenuSectionComponent,
|
||||
DsoEditMenuExpandableSectionComponent,
|
||||
];
|
||||
|
||||
const SHARED_ITEM_PAGE_COMPONENTS = [
|
||||
MetadataFieldWrapperComponent,
|
||||
MetadataValuesComponent,
|
||||
DsoPageEditButtonComponent,
|
||||
DsoPageVersionButtonComponent,
|
||||
ItemAlertsComponent,
|
||||
GenericItemPageFieldComponent,
|
||||
MetadataRepresentationListComponent,
|
||||
RelatedItemsComponent,
|
||||
|
||||
DsoEditMenuSectionComponent,
|
||||
DsoEditMenuComponent,
|
||||
DsoEditMenuExpandableSectionComponent,
|
||||
];
|
||||
|
||||
const PROVIDERS = [
|
||||
|
Reference in New Issue
Block a user