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
4 changes: 2 additions & 2 deletions src/app/process-page/form/process-form.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ <h1 class="col-12">
</h1>
<div class="col-12 col-md-6 mb-2">
<form #form="ngForm" (ngSubmit)="submitForm(form)">
<ds-scripts-select [script]="selectedScript" (select)="selectedScript = $event; parameters = undefined"></ds-scripts-select>
<ds-scripts-select [submitted]="submitted" [script]="selectedScript" (select)="selectedScript = $event; parameters = undefined"></ds-scripts-select>
<ds-process-parameters [initialParams]="parameters" [script]="selectedScript" (updateParameters)="parameters = $event"></ds-process-parameters>
<a [routerLink]="['/processes']" class="btn btn-danger float-start">{{ 'process.new.cancel' | translate }}</a>
<button type="submit" class="btn btn-primary float-end">{{ 'process.new.submit' | translate }}</button>
<button type="submit" class="btn btn-primary float-end" [dsBtnDisabled]="form.invalid || !isScriptSelected">{{ 'process.new.submit' | translate }}</button>
</form>
</div>
<div class="col-12 col-md-6">
Expand Down
25 changes: 25 additions & 0 deletions src/app/process-page/form/process-form.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,31 @@ describe('ProcessFormComponent', () => {
expect(scriptService.invoke).toHaveBeenCalled();
});

it('should mark the form as submitted when submit is attempted', () => {
expect(component.submitted).toBeFalse();
component.submitForm({ controls: {} } as any);
expect(component.submitted).toBeTrue();
});

describe('when no script is selected', () => {
beforeEach(() => {
component.selectedScript = undefined;
});

it('should not invoke the script on submit', () => {
component.submitForm({ controls: {} } as any);
expect(scriptService.invoke).not.toHaveBeenCalled();
});

it('should report that a script is not selected', () => {
expect(component.isScriptSelected).toBeFalse();
});
});

it('should report that a script is selected', () => {
expect(component.isScriptSelected).toBeTrue();
});

describe('when undefined parameters are provided', () => {
beforeEach(() => {
component.parameters = undefined;
Expand Down
26 changes: 24 additions & 2 deletions src/app/process-page/form/process-form.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,16 @@ import { ProcessParameter } from '@dspace/core/processes/process-parameter.model
import { getFirstCompletedRemoteData } from '@dspace/core/shared/operators';
import { Script } from '@dspace/core/shared/scripts/script.model';
import { ScriptParameter } from '@dspace/core/shared/scripts/script-parameter.model';
import { isEmpty } from '@dspace/shared/utils/empty.util';
import {
hasValue,
isEmpty,
} from '@dspace/shared/utils/empty.util';
import {
TranslateModule,
TranslateService,
} from '@ngx-translate/core';

import { BtnDisabledDirective } from '../../shared/btn-disabled.directive';
import { getProcessListRoute } from '../process-page-routing.paths';
import { ProcessParametersComponent } from './process-parameters/process-parameters.component';
import { ScriptHelpComponent } from './script-help/script-help.component';
Expand All @@ -40,6 +44,7 @@ import { ScriptsSelectComponent } from './scripts-select/scripts-select.componen
templateUrl: './process-form.component.html',
styleUrls: ['./process-form.component.scss'],
imports: [
BtnDisabledDirective,
FormsModule,
ProcessParametersComponent,
RouterLink,
Expand Down Expand Up @@ -79,6 +84,19 @@ export class ProcessFormComponent implements OnInit {
*/
public missingParameters = [];

/**
* Indicates whether the form has been submitted
* Used to surface validation errors on an interrupted submission
*/
public submitted = false;

/**
* Indicates whether a script has been selected
*/
get isScriptSelected(): boolean {
return hasValue(this.selectedScript);
}

constructor(
private scriptService: ScriptDataService,
private notificationsService: NotificationsService,
Expand All @@ -95,10 +113,11 @@ export class ProcessFormComponent implements OnInit {
* @param form
*/
submitForm(form: NgForm) {
this.submitted = true;
if (isEmpty(this.parameters)) {
this.parameters = [];
}
if (!this.validateForm(form) || this.isRequiredMissing()) {
if (!this.isScriptSelected || !this.validateForm(form) || this.isRequiredMissing()) {
return;
}

Expand Down Expand Up @@ -157,6 +176,9 @@ export class ProcessFormComponent implements OnInit {

private isRequiredMissing() {
this.missingParameters = [];
if (!this.isScriptSelected || isEmpty(this.selectedScript.parameters)) {
return false;
}
const setParams: string[] = this.parameters
.map((param) => param.name);
const requiredParams: ScriptParameter[] = this.selectedScript.parameters.filter((param) => param.mandatory);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
</div>
</div>
<div>
@if (script.invalid && (script.dirty || script.touched)) {
@if (script.invalid && (script.dirty || script.touched || submitted)) {
<div
class="alert alert-danger validation-error">
@if (script.errors.required) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,32 @@ describe('ScriptsSelectComponent', () => {
expect(validationError).toBeFalsy();
}));

it('should show a validation error if the form was submitted but the input was left empty', fakeAsync(() => {
component.submitted = true;
fixture.detectChanges();
tick();

const validationError = fixture.debugElement.query(By.css('.validation-error'));
expect(validationError).toBeTruthy();
}));

it('should not show a validation error if the form was submitted but the input was not left empty', fakeAsync(() => {
(component as any)._selectedScript.id = 'testValue';
fixture.detectChanges();
tick();

const select = fixture.debugElement.query(By.css('#process-script'));
select.triggerEventHandler('blur', null);
fixture.detectChanges();

component.submitted = true;
fixture.detectChanges();
tick();

const validationError = fixture.debugElement.query(By.css('.validation-error'));
expect(validationError).toBeFalsy();
}));

it('should load more scripts when scrolled to the bottom', fakeAsync(() => {
spyOn(component, 'loadScripts');
const event = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,11 @@ export class ScriptsSelectComponent implements OnInit, OnDestroy {
* Emits the selected script when the selection changes
*/
@Output() select: EventEmitter<Script> = new EventEmitter<Script>();
/**
* Indicates whether the parent form has been submitted
* Used to surface validation errors for an empty selection
*/
@Input() submitted = false;
/**
* All available scripts
*/
Expand Down
Loading