diff --git a/src/app/process-page/form/process-form.component.html b/src/app/process-page/form/process-form.component.html index 3bb6a796c6c..5cc10b63ac1 100644 --- a/src/app/process-page/form/process-form.component.html +++ b/src/app/process-page/form/process-form.component.html @@ -5,10 +5,10 @@

- + {{ 'process.new.cancel' | translate }} - +
diff --git a/src/app/process-page/form/process-form.component.spec.ts b/src/app/process-page/form/process-form.component.spec.ts index 9d84f2774ca..c4acafd3e15 100644 --- a/src/app/process-page/form/process-form.component.spec.ts +++ b/src/app/process-page/form/process-form.component.spec.ts @@ -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; diff --git a/src/app/process-page/form/process-form.component.ts b/src/app/process-page/form/process-form.component.ts index 72f9e08d4a2..3dfcb8b2f01 100644 --- a/src/app/process-page/form/process-form.component.ts +++ b/src/app/process-page/form/process-form.component.ts @@ -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'; @@ -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, @@ -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, @@ -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; } @@ -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); diff --git a/src/app/process-page/form/scripts-select/scripts-select.component.html b/src/app/process-page/form/scripts-select/scripts-select.component.html index 2954e962716..86965046e63 100644 --- a/src/app/process-page/form/scripts-select/scripts-select.component.html +++ b/src/app/process-page/form/scripts-select/scripts-select.component.html @@ -39,7 +39,7 @@
- @if (script.invalid && (script.dirty || script.touched)) { + @if (script.invalid && (script.dirty || script.touched || submitted)) {
@if (script.errors.required) { diff --git a/src/app/process-page/form/scripts-select/scripts-select.component.spec.ts b/src/app/process-page/form/scripts-select/scripts-select.component.spec.ts index 2765edf3a11..191f1b19a10 100644 --- a/src/app/process-page/form/scripts-select/scripts-select.component.spec.ts +++ b/src/app/process-page/form/scripts-select/scripts-select.component.spec.ts @@ -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 = { diff --git a/src/app/process-page/form/scripts-select/scripts-select.component.ts b/src/app/process-page/form/scripts-select/scripts-select.component.ts index a3564e520aa..205503fbc22 100644 --- a/src/app/process-page/form/scripts-select/scripts-select.component.ts +++ b/src/app/process-page/form/scripts-select/scripts-select.component.ts @@ -67,6 +67,11 @@ export class ScriptsSelectComponent implements OnInit, OnDestroy { * Emits the selected script when the selection changes */ @Output() select: EventEmitter