Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,27 +1,3 @@
<div class="container-fluid mb-2" *ngVar="(itemTemplateRD$ | async) as itemTemplateRD">
<span class="d-inline-block mb-2">{{ 'collection.edit.template.label' | translate}}</span>
<div class="button-row space-children-mr">
@if (!itemTemplateRD?.payload) {
<button class="btn btn-success" (click)="addItemTemplate()">
<i class="fas fa-plus" aria-hidden="true"></i>
<span class="d-none d-sm-inline">&nbsp;{{"collection.edit.template.add-button" | translate}}</span>
</button>
}
@if (itemTemplateRD?.payload) {
<button class="btn btn-danger" (click)="deleteItemTemplate()">
<i class="fas fa-trash-alt" aria-hidden="true"></i>
<span class="d-none d-sm-inline">&nbsp;{{"collection.edit.template.delete-button" | translate}}</span>
</button>
}
@if (itemTemplateRD?.payload) {
<button class="btn btn-primary"
[routerLink]="'/collections/' + (dsoRD$ | async)?.payload.uuid + '/itemtemplate'">
<i class="fas fa-edit" aria-hidden="true"></i>
<span class="d-none d-sm-inline">&nbsp;{{"collection.edit.template.edit-button" | translate}}</span>
</button>
}
</div>
</div>
<ds-collection-form [dso]="(dsoRD$ | async)?.payload"
[isCreation]="false"
(submitForm)="onSubmit($event)"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,44 +7,23 @@ import {
} from '@angular/core/testing';
import {
ActivatedRoute,
NavigationEnd,
Router,
} from '@angular/router';
import { RouterTestingModule } from '@angular/router/testing';
import { APP_CONFIG } from '@dspace/config/app-config.interface';
import { AuthService } from '@dspace/core/auth/auth.service';
import { ObjectCacheService } from '@dspace/core/cache/object-cache.service';
import { CollectionDataService } from '@dspace/core/data/collection-data.service';
import { CommunityDataService } from '@dspace/core/data/community-data.service';
import { ItemTemplateDataService } from '@dspace/core/data/item-template-data.service';
import { RequestService } from '@dspace/core/data/request.service';
import { APP_DATA_SERVICES_MAP } from '@dspace/core/data-services-map-type';
import { NotificationsService } from '@dspace/core/notification-system/notifications.service';
import { Collection } from '@dspace/core/shared/collection.model';
import { Item } from '@dspace/core/shared/item.model';
import { AuthServiceMock } from '@dspace/core/testing/auth.service.mock';
import {
createFailedRemoteDataObject$,
createSuccessfulRemoteDataObject,
createSuccessfulRemoteDataObject$,
} from '@dspace/core/utilities/remote-data.utils';
import { createSuccessfulRemoteDataObject } from '@dspace/core/utilities/remote-data.utils';
import { TranslateModule } from '@ngx-translate/core';
import { of } from 'rxjs';

import { getCollectionItemTemplateRoute } from '../../collection-page-routing-paths';
import { CollectionFormComponent } from '../../collection-form/collection-form.component';
import { CollectionMetadataComponent } from './collection-metadata.component';

describe('CollectionMetadataComponent', () => {
let comp: CollectionMetadataComponent;
let fixture: ComponentFixture<CollectionMetadataComponent>;
let router: Router;
let itemTemplateService: ItemTemplateDataService;

const template = Object.assign(new Item(), {
_links: {
self: { href: 'template-selflink' },
},
});
const collection = Object.assign(new Collection(), {
uuid: 'collection-id',
id: 'collection-id',
Expand All @@ -53,56 +32,27 @@ describe('CollectionMetadataComponent', () => {
self: { href: 'collection-selflink' },
},
});
const collectionTemplateHref = 'rest/api/test/collections/template';

const itemTemplateServiceStub = jasmine.createSpyObj('itemTemplateService', {
findByCollectionID: createSuccessfulRemoteDataObject$(template),
createByCollectionID: createSuccessfulRemoteDataObject$(template),
delete: of(true),
getCollectionEndpoint: of(collectionTemplateHref),
});

const notificationsService = jasmine.createSpyObj('notificationsService', {
success: {},
error: {},
});
const requestService = jasmine.createSpyObj('requestService', {
setStaleByHrefSubstring: {},
});

const routerMock = {
events: of(new NavigationEnd(1, 'url', 'url')),
navigate: jasmine.createSpy('navigate'),
};

beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
void TestBed.configureTestingModule({
imports: [TranslateModule.forRoot(), CommonModule, RouterTestingModule, CollectionMetadataComponent],
providers: [
{ provide: CollectionDataService, useValue: {} },
{ provide: ItemTemplateDataService, useValue: itemTemplateServiceStub },
{ provide: ActivatedRoute, useValue: { parent: { data: of({ dso: createSuccessfulRemoteDataObject(collection) }) } } },
{ provide: NotificationsService, useValue: notificationsService },
{ provide: RequestService, useValue: requestService },
{ provide: Router, useValue: routerMock },
{ provide: AuthService, useValue: new AuthServiceMock() },
{ provide: CommunityDataService, useValue: {} },
{ provide: ObjectCacheService, useValue: {} },
{ provide: APP_DATA_SERVICES_MAP, useValue: {} },
{ provide: APP_CONFIG, useValue: {} },
{ provide: NotificationsService, useValue: {} },
{ provide: Router, useValue: {} },
],
schemas: [NO_ERRORS_SCHEMA],
}).compileComponents();
})
.overrideComponent(CollectionMetadataComponent, {
remove: { imports: [CollectionFormComponent] },
})
.compileComponents();
}));

beforeEach(() => {
fixture = TestBed.createComponent(CollectionMetadataComponent);
comp = fixture.componentInstance;
itemTemplateService = (comp as any).itemTemplateService;
spyOn(comp, 'ngOnInit');
spyOn(comp, 'initTemplateItem');

routerMock.events = of(new NavigationEnd(1, 'url', 'url'));
fixture.detectChanges();
});

Expand All @@ -111,39 +61,4 @@ describe('CollectionMetadataComponent', () => {
expect((comp as any).frontendURL).toEqual('/collections/');
});
});

describe('addItemTemplate', () => {
it('should navigate to the collection\'s itemtemplate page', () => {
comp.addItemTemplate();
expect(routerMock.navigate).toHaveBeenCalledWith([getCollectionItemTemplateRoute(collection.uuid)]);
});
});

describe('deleteItemTemplate', () => {
beforeEach(() => {
(itemTemplateService.delete as jasmine.Spy).and.returnValue(createSuccessfulRemoteDataObject$({}));
comp.deleteItemTemplate();
});

it('should call ItemTemplateService.delete', () => {
expect(itemTemplateService.delete).toHaveBeenCalledWith(template.uuid);
});

describe('when delete returns a success', () => {
it('should display a success notification', () => {
expect(notificationsService.success).toHaveBeenCalled();
});
});

describe('when delete returns a failure', () => {
beforeEach(() => {
(itemTemplateService.delete as jasmine.Spy).and.returnValue(createFailedRemoteDataObject$());
comp.deleteItemTemplate();
});

it('should display an error notification', () => {
expect(notificationsService.error).toHaveBeenCalled();
});
});
});
});
Original file line number Diff line number Diff line change
@@ -1,46 +1,16 @@
import { AsyncPipe } from '@angular/common';
import {
ChangeDetectorRef,
Component,
OnInit,
} from '@angular/core';
import { Component } from '@angular/core';
import {
ActivatedRoute,
NavigationEnd,
Router,
RouterLink,
Scroll,
} from '@angular/router';
import { CollectionDataService } from '@dspace/core/data/collection-data.service';
import { ItemTemplateDataService } from '@dspace/core/data/item-template-data.service';
import { RemoteData } from '@dspace/core/data/remote-data';
import { RequestService } from '@dspace/core/data/request.service';
import { NotificationsService } from '@dspace/core/notification-system/notifications.service';
import { Collection } from '@dspace/core/shared/collection.model';
import { Item } from '@dspace/core/shared/item.model';
import { NoContent } from '@dspace/core/shared/NoContent.model';
import {
getFirstCompletedRemoteData,
getFirstSucceededRemoteDataPayload,
} from '@dspace/core/shared/operators';
import { hasValue } from '@dspace/shared/utils/empty.util';
import {
TranslateModule,
TranslateService,
} from '@ngx-translate/core';
import {
combineLatest as combineLatestObservable,
Observable,
} from 'rxjs';
import {
map,
switchMap,
} from 'rxjs/operators';
import { TranslateService } from '@ngx-translate/core';

import { ComcolMetadataComponent } from '../../../shared/comcol/comcol-forms/edit-comcol-page/comcol-metadata/comcol-metadata.component';
import { VarDirective } from '../../../shared/utils/var.directive';
import { CollectionFormComponent } from '../../collection-form/collection-form.component';
import { getCollectionItemTemplateRoute } from '../../collection-page-routing-paths';

/**
* Component for editing a collection's metadata
Expand All @@ -51,103 +21,19 @@ import { getCollectionItemTemplateRoute } from '../../collection-page-routing-pa
imports: [
AsyncPipe,
CollectionFormComponent,
RouterLink,
TranslateModule,
VarDirective,
],
})
export class CollectionMetadataComponent extends ComcolMetadataComponent<Collection> implements OnInit {
export class CollectionMetadataComponent extends ComcolMetadataComponent<Collection> {
protected frontendURL = '/collections/';
protected type = Collection.type;

/**
* The collection's item template
*/
itemTemplateRD$: Observable<RemoteData<Item>>;

public constructor(
protected collectionDataService: CollectionDataService,
protected itemTemplateService: ItemTemplateDataService,
protected router: Router,
protected route: ActivatedRoute,
protected notificationsService: NotificationsService,
protected translate: TranslateService,
protected requestService: RequestService,
protected chd: ChangeDetectorRef,
) {
super(collectionDataService, router, route, notificationsService, translate);
}

/**
* Checking if the navigation is done and if so, initialize the collection's item template,
* to ensure that the item template is always up to date.
* Check when a NavigationEnd event (URL change) or a Scroll event followed by a NavigationEnd event (refresh event), occurs
*/
ngOnInit(): void {
this.router.events.subscribe((event) => {
if (
event instanceof NavigationEnd ||
(event instanceof Scroll && event.routerEvent instanceof NavigationEnd)
) {
super.ngOnInit();
this.initTemplateItem();
this.chd.detectChanges();
}
});
}

/**
* Initialize the collection's item template
*/
initTemplateItem() {
this.itemTemplateRD$ = this.dsoRD$.pipe(
getFirstSucceededRemoteDataPayload(),
switchMap((collection: Collection) => this.itemTemplateService.findByCollectionID(collection.uuid)),
);
}

/**
* Add a new item template to the collection and redirect to the item template edit page
*/
addItemTemplate() {
const collection$ = this.dsoRD$.pipe(
getFirstSucceededRemoteDataPayload(),
);
const template$ = collection$.pipe(
switchMap((collection: Collection) => this.itemTemplateService.createByCollectionID(new Item(), collection.uuid).pipe(
getFirstSucceededRemoteDataPayload(),
)),
);
const templateHref$ = collection$.pipe(
switchMap((collection) => this.itemTemplateService.getCollectionEndpoint(collection.id)),
);

combineLatestObservable(collection$, template$, templateHref$).subscribe(([collection, template, templateHref]) => {
this.requestService.setStaleByHrefSubstring(templateHref);
this.router.navigate([getCollectionItemTemplateRoute(collection.uuid)]);
});
}

/**
* Delete the item template from the collection
*/
deleteItemTemplate() {
this.dsoRD$.pipe(
getFirstSucceededRemoteDataPayload(),
switchMap((collection: Collection) => this.itemTemplateService.findByCollectionID(collection.uuid)),
getFirstSucceededRemoteDataPayload(),
switchMap((template) => {
return this.itemTemplateService.delete(template.uuid);
}),
getFirstCompletedRemoteData(),
map((response: RemoteData<NoContent>) => hasValue(response) && response.hasSucceeded),
).subscribe((success: boolean) => {
if (success) {
this.notificationsService.success(null, this.translate.get('collection.edit.template.notifications.delete.success'));
} else {
this.notificationsService.error(null, this.translate.get('collection.edit.template.notifications.delete.error'));
}
this.initTemplateItem();
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<div class="container-fluid mb-2" *ngVar="(itemTemplateRD$ | async) as itemTemplateRD">
@if (!editing) {
<span class="d-inline-block mb-2">{{ 'collection.edit.template.label' | translate }}</span>
<div class="button-row space-children-mr">
@if (!itemTemplateRD?.payload) {
<button class="btn btn-success" (click)="addItemTemplate()">
<i class="fas fa-plus" aria-hidden="true"></i>
<span class="d-none d-sm-inline">&nbsp;{{ 'collection.edit.template.add-button' | translate }}</span>
</button>
}
@if (itemTemplateRD?.payload) {
<button class="btn btn-danger" (click)="deleteItemTemplate()">
<i class="fas fa-trash-alt" aria-hidden="true"></i>
<span class="d-none d-sm-inline">&nbsp;{{ 'collection.edit.template.delete-button' | translate }}</span>
</button>
}
@if (itemTemplateRD?.payload) {
<button class="btn btn-primary" (click)="editItemTemplate()">
<i class="fas fa-edit" aria-hidden="true"></i>
<span class="d-none d-sm-inline">&nbsp;{{ 'collection.edit.template.edit-button' | translate }}</span>
</button>
}
</div>
}

@if (editing && itemTemplateRD?.payload) {
<h2 class="border-bottom pb-2">
{{ 'collection.edit.template.head' | translate: { collection: dsoNameService.getName((dsoRD$ | async)?.payload) } }}
</h2>
<ds-dso-edit-metadata [updateDataService]="itemTemplateService"
[dso]="itemTemplateRD?.payload"></ds-dso-edit-metadata>
<button (click)="cancelEdit()" class="btn btn-outline-secondary">{{ 'collection.edit.template.cancel' | translate }}</button>
}
</div>
Loading
Loading