mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 01:54:15 +00:00
71712: confirmation modal for export + tests &
- request causing error because of issue #756, commented out for now & - drop event prevention in a HostListener like dragover event
This commit is contained in:
@@ -357,7 +357,7 @@ export class AdminSidebarComponent extends MenuComponent implements OnInit {
|
||||
|
||||
observableCombineLatest(
|
||||
this.authorizationService.isAuthorized(FeatureID.AdministratorOf),
|
||||
this.scriptDataService.scriptWithNameExistsAndCanExecute(METADATA_EXPORT_SCRIPT_NAME)
|
||||
// this.scriptDataService.scriptWithNameExistsAndCanExecute(METADATA_EXPORT_SCRIPT_NAME)
|
||||
).pipe(
|
||||
// TODO uncomment when #635 (https://github.com/DSpace/dspace-angular/issues/635) is fixed; otherwise even in production mode, the metadata export button is only available after a refresh (and not in dev mode)
|
||||
// filter(([authorized, metadataExportScriptExists]: boolean[]) => authorized && metadataExportScriptExists),
|
||||
@@ -416,7 +416,7 @@ export class AdminSidebarComponent extends MenuComponent implements OnInit {
|
||||
|
||||
observableCombineLatest(
|
||||
this.authorizationService.isAuthorized(FeatureID.AdministratorOf),
|
||||
this.scriptDataService.scriptWithNameExistsAndCanExecute(METADATA_IMPORT_SCRIPT_NAME)
|
||||
// this.scriptDataService.scriptWithNameExistsAndCanExecute(METADATA_IMPORT_SCRIPT_NAME)
|
||||
).pipe(
|
||||
// TODO uncomment when #635 (https://github.com/DSpace/dspace-angular/issues/635) is fixed
|
||||
// filter(([authorized, metadataImportScriptExists]: boolean[]) => authorized && metadataImportScriptExists),
|
||||
|
@@ -0,0 +1,16 @@
|
||||
<div>
|
||||
<div class="modal-header">{{ headerLabel | translate:{dsoName: dso?.name} }}
|
||||
<button type="button" class="close" (click)="close()" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>{{ infoLabel | translate:{dsoName: dso?.name} }}</p>
|
||||
<button type="button" class="cancel btn btn-secondary" (click)="cancelPressed()" aria-label="Cancel">
|
||||
{{ cancelLabel | translate:{dsoName: dso?.name} }}
|
||||
</button>
|
||||
<button type="button" class="confirm btn btn-primary" (click)="confirmPressed()" aria-label="Confirm" ngbAutofocus>
|
||||
{{ confirmLabel | translate:{dsoName: dso?.name} }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
@@ -0,0 +1,117 @@
|
||||
import { DebugElement, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { async, ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { ConfirmationModalComponent } from './confirmation-modal.component';
|
||||
|
||||
describe('ConfirmationModalComponent', () => {
|
||||
let component: ConfirmationModalComponent;
|
||||
let fixture: ComponentFixture<ConfirmationModalComponent>;
|
||||
let debugElement: DebugElement;
|
||||
|
||||
const modalStub = jasmine.createSpyObj('modalStub', ['close']);
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [TranslateModule.forRoot()],
|
||||
declarations: [ConfirmationModalComponent],
|
||||
providers: [
|
||||
{ provide: NgbActiveModal, useValue: modalStub }
|
||||
],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
}).compileComponents();
|
||||
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ConfirmationModalComponent);
|
||||
component = fixture.componentInstance;
|
||||
debugElement = fixture.debugElement;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
describe('close', () => {
|
||||
beforeEach(() => {
|
||||
component.close();
|
||||
});
|
||||
it('should call the close method on the active modal', () => {
|
||||
expect(modalStub.close).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('confirmPressed', () => {
|
||||
beforeEach(() => {
|
||||
spyOn(component.response, 'emit');
|
||||
component.confirmPressed();
|
||||
});
|
||||
it('should call the close method on the active modal', () => {
|
||||
expect(modalStub.close).toHaveBeenCalled();
|
||||
});
|
||||
it('event emitter should emit true', () => {
|
||||
expect(component.response.emit).toHaveBeenCalledWith(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('cancelPressed', () => {
|
||||
beforeEach(() => {
|
||||
spyOn(component.response, 'emit');
|
||||
component.cancelPressed();
|
||||
});
|
||||
it('should call the close method on the active modal', () => {
|
||||
expect(modalStub.close).toHaveBeenCalled();
|
||||
});
|
||||
it('event emitter should emit false', () => {
|
||||
expect(component.response.emit).toHaveBeenCalledWith(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the click method emits on close button', () => {
|
||||
beforeEach(fakeAsync(() => {
|
||||
spyOn(component, 'close');
|
||||
debugElement.query(By.css('button.close')).triggerEventHandler('click', {});
|
||||
tick();
|
||||
fixture.detectChanges();
|
||||
}));
|
||||
it('should call the close method on the component', () => {
|
||||
expect(component.close).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the click method emits on cancel button', () => {
|
||||
beforeEach(fakeAsync(() => {
|
||||
spyOn(component, 'close');
|
||||
spyOn(component.response, 'emit');
|
||||
debugElement.query(By.css('button.cancel')).triggerEventHandler('click', {});
|
||||
tick();
|
||||
fixture.detectChanges();
|
||||
}));
|
||||
it('should call the close method on the component', () => {
|
||||
expect(component.close).toHaveBeenCalled();
|
||||
});
|
||||
it('event emitter should emit false', () => {
|
||||
expect(component.response.emit).toHaveBeenCalledWith(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the click method emits on confirm button', () => {
|
||||
beforeEach(fakeAsync(() => {
|
||||
spyOn(component, 'close');
|
||||
spyOn(component.response, 'emit');
|
||||
debugElement.query(By.css('button.confirm')).triggerEventHandler('click', {});
|
||||
tick();
|
||||
fixture.detectChanges();
|
||||
}));
|
||||
it('should call the close method on the component', () => {
|
||||
expect(component.close).toHaveBeenCalled();
|
||||
});
|
||||
it('event emitter should emit true', () => {
|
||||
expect(component.response.emit).toHaveBeenCalledWith(true);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
@@ -0,0 +1,48 @@
|
||||
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { NgbActiveModal, NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { DSpaceObject } from '../../core/shared/dspace-object.model';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-confirmation-modal',
|
||||
templateUrl: 'confirmation-modal.component.html',
|
||||
})
|
||||
export class ConfirmationModalComponent {
|
||||
@Input() headerLabel: string;
|
||||
@Input() infoLabel: string;
|
||||
@Input() cancelLabel: string;
|
||||
@Input() confirmLabel: string;
|
||||
@Input() dso: DSpaceObject;
|
||||
|
||||
/**
|
||||
* An event fired when the cancel or confirm button is clicked, with respectively false or true
|
||||
*/
|
||||
@Output()
|
||||
response = new EventEmitter<boolean>();
|
||||
|
||||
constructor(protected activeModal: NgbActiveModal) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirm the action that led to the modal
|
||||
*/
|
||||
confirmPressed() {
|
||||
this.response.emit(true);
|
||||
this.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel the action that led to the modal and close modal
|
||||
*/
|
||||
cancelPressed() {
|
||||
this.response.emit(false);
|
||||
this.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the modal
|
||||
*/
|
||||
close() {
|
||||
this.activeModal.close();
|
||||
}
|
||||
}
|
@@ -4,16 +4,19 @@ import { TranslateService } from '@ngx-translate/core';
|
||||
import { Observable } from 'rxjs/internal/Observable';
|
||||
import { take, map } from 'rxjs/operators';
|
||||
import { of as observableOf } from 'rxjs';
|
||||
import { AuthService } from '../../../../core/auth/auth.service';
|
||||
import { METADATA_EXPORT_SCRIPT_NAME, ScriptDataService } from '../../../../core/data/processes/script-data.service';
|
||||
import { RequestEntry } from '../../../../core/data/request.reducer';
|
||||
import { Collection } from '../../../../core/shared/collection.model';
|
||||
import { Community } from '../../../../core/shared/community.model';
|
||||
import { DSpaceObjectType } from '../../../../core/shared/dspace-object-type.model';
|
||||
import { DSpaceObject } from '../../../../core/shared/dspace-object.model';
|
||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { NgbActiveModal, NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { ProcessParameter } from '../../../../process-page/processes/process-parameter.model';
|
||||
import { ConfirmationModalComponent } from '../../../confirmation-modal/confirmation-modal.component';
|
||||
import { isNotEmpty } from '../../../empty.util';
|
||||
import { NotificationsService } from '../../../notifications/notifications.service';
|
||||
import { createSuccessfulRemoteDataObject } from '../../../remote-data.utils';
|
||||
import { DSOSelectorModalWrapperComponent, SelectorActionType } from '../dso-selector-modal-wrapper.component';
|
||||
|
||||
/**
|
||||
@@ -21,7 +24,7 @@ import { DSOSelectorModalWrapperComponent, SelectorActionType } from '../dso-sel
|
||||
* Used to choose a dso from to export metadata of
|
||||
*/
|
||||
@Component({
|
||||
selector: 'ds-edit-item-selector',
|
||||
selector: 'ds-export-metadata-selector',
|
||||
templateUrl: '../dso-selector-modal-wrapper.component.html',
|
||||
})
|
||||
export class ExportMetadataSelectorComponent extends DSOSelectorModalWrapperComponent implements OnInit {
|
||||
@@ -31,7 +34,8 @@ export class ExportMetadataSelectorComponent extends DSOSelectorModalWrapperComp
|
||||
|
||||
constructor(protected activeModal: NgbActiveModal, protected route: ActivatedRoute, private router: Router,
|
||||
protected notificationsService: NotificationsService, protected translationService: TranslateService,
|
||||
protected scriptDataService: ScriptDataService) {
|
||||
protected scriptDataService: ScriptDataService,
|
||||
private modalService: NgbModal) {
|
||||
super(activeModal, route);
|
||||
}
|
||||
|
||||
@@ -41,9 +45,23 @@ export class ExportMetadataSelectorComponent extends DSOSelectorModalWrapperComp
|
||||
*/
|
||||
navigate(dso: DSpaceObject): Observable<boolean> {
|
||||
if (dso instanceof Collection || dso instanceof Community) {
|
||||
const startScriptSucceeded = this.startScriptNotifyAndRedirect(dso, dso.handle);
|
||||
startScriptSucceeded.pipe(take(1)).subscribe();
|
||||
return startScriptSucceeded;
|
||||
const modalRef = this.modalService.open(ConfirmationModalComponent);
|
||||
modalRef.componentInstance.dso = dso;
|
||||
modalRef.componentInstance.headerLabel = "confirmation-modal.export-metadata.header";
|
||||
modalRef.componentInstance.infoLabel = "confirmation-modal.export-metadata.info";
|
||||
modalRef.componentInstance.cancelLabel = "confirmation-modal.export-metadata.cancel";
|
||||
modalRef.componentInstance.confirmLabel = "confirmation-modal.export-metadata.confirm";
|
||||
|
||||
modalRef.componentInstance.response.subscribe((confirm: boolean) => {
|
||||
if (confirm) {
|
||||
const startScriptSucceeded = this.startScriptNotifyAndRedirect(dso, dso.handle);
|
||||
startScriptSucceeded.pipe(take(1)).subscribe();
|
||||
return startScriptSucceeded;
|
||||
} else {
|
||||
const modalRef = this.modalService.open(ExportMetadataSelectorComponent);
|
||||
modalRef.componentInstance.dsoRD = createSuccessfulRemoteDataObject(dso);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.notificationsService.error(this.translationService.get('dso-selector.export-metadata.notValidDSO'));
|
||||
return observableOf(false);
|
||||
|
@@ -51,15 +51,17 @@ export class FileDropzoneNoUploaderComponent implements OnInit {
|
||||
ngOnInit() {
|
||||
this.uploaderId = 'ds-drag-and-drop-uploader' + uniqueId();
|
||||
this.isOverDocumentDropZone = observableOf(false);
|
||||
window.addEventListener('drop', (e: DragEvent) => {
|
||||
return e && e.preventDefault();
|
||||
}, false);
|
||||
this.uploader = new FileUploader({
|
||||
// required, but using onFileDrop, not uploader
|
||||
url: 'placeholder',
|
||||
});
|
||||
}
|
||||
|
||||
@HostListener('window:drop', ['$event'])
|
||||
onDrop(event: any) {
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
@HostListener('window:dragover', ['$event'])
|
||||
onDragOver(event: any) {
|
||||
// Show drop area on the page
|
||||
|
@@ -10,6 +10,7 @@ import { MissingTranslationHandler, TranslateModule } from '@ngx-translate/core'
|
||||
|
||||
import { NgxPaginationModule } from 'ngx-pagination';
|
||||
import { ComcolRoleComponent } from './comcol-forms/edit-comcol-page/comcol-role/comcol-role.component';
|
||||
import { ConfirmationModalComponent } from './confirmation-modal/confirmation-modal.component';
|
||||
import { ExportMetadataSelectorComponent } from './dso-selector/modal-wrappers/export-metadata-selector/export-metadata-selector.component';
|
||||
import { FileDropzoneNoUploaderComponent } from './file-dropzone-no-uploader/file-dropzone-no-uploader.component';
|
||||
import { PublicationListElementComponent } from './object-list/item-list-element/item-types/publication/publication-list-element.component';
|
||||
@@ -401,7 +402,8 @@ const COMPONENTS = [
|
||||
GroupSearchBoxComponent,
|
||||
FileDownloadLinkComponent,
|
||||
CollectionDropdownComponent,
|
||||
ExportMetadataSelectorComponent
|
||||
ExportMetadataSelectorComponent,
|
||||
ConfirmationModalComponent
|
||||
];
|
||||
|
||||
const ENTRY_COMPONENTS = [
|
||||
@@ -478,7 +480,8 @@ const ENTRY_COMPONENTS = [
|
||||
ClaimedTaskActionsEditMetadataComponent,
|
||||
FileDownloadLinkComponent,
|
||||
CurationFormComponent,
|
||||
ExportMetadataSelectorComponent
|
||||
ExportMetadataSelectorComponent,
|
||||
ConfirmationModalComponent
|
||||
];
|
||||
|
||||
const SHARED_ITEM_PAGE_COMPONENTS = [
|
||||
|
@@ -995,6 +995,16 @@
|
||||
|
||||
|
||||
|
||||
"confirmation-modal.export-metadata.header": "Export metadata for {{ dsoName }}",
|
||||
|
||||
"confirmation-modal.export-metadata.info": "Are you sure you want to export metadata for {{ dsoName }}",
|
||||
|
||||
"confirmation-modal.export-metadata.cancel": "Cancel",
|
||||
|
||||
"confirmation-modal.export-metadata.confirm": "Export",
|
||||
|
||||
|
||||
|
||||
"error.bitstream": "Error fetching bitstream",
|
||||
|
||||
"error.browse-by": "Error fetching items",
|
||||
|
@@ -105,7 +105,7 @@ export const environment: Partial<GlobalConfig> = {
|
||||
},
|
||||
// Angular Universal settings
|
||||
universal: {
|
||||
preboot: true,
|
||||
preboot: false,
|
||||
async: true,
|
||||
time: false
|
||||
},
|
||||
|
Reference in New Issue
Block a user