mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-17 23:13:04 +00:00
added create-from functionality
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
<div class="form-row mb-2 mx-0">
|
||||
<select id="process-parameters"
|
||||
class="form-control col"
|
||||
name="parameter-{{index}}"
|
||||
[(ngModel)]="selectedParameter"
|
||||
#param="ngModel">
|
||||
<option [ngValue]="undefined">Add a parameter...</option>
|
||||
<option *ngFor="let param of parameters" [ngValue]="param.name">
|
||||
{{param.nameLong || param.name}}
|
||||
</option>
|
||||
</select>
|
||||
<ds-parameter-value-input [initialValue]="parameterValue.value" [parameter]="selectedScriptParameter" (updateValue)="selectedParameterValue = $event" class="d-block col" [index]="index"></ds-parameter-value-input>
|
||||
<button *ngIf="removable" class="btn btn-light col-1 remove-button" (click)="removeParameter.emit(parameterValue);"><span class="fas fa-trash"></span></button>
|
||||
<span *ngIf="!removable" class="col-1"></span>
|
||||
</div>
|
||||
|
@@ -0,0 +1,71 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ParameterSelectComponent } from './parameter-select.component';
|
||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { ScriptParameter } from '../../../scripts/script-parameter.model';
|
||||
import { ScriptParameterType } from '../../../scripts/script-parameter-type.model';
|
||||
import { By } from '@angular/platform-browser';
|
||||
|
||||
describe('ParameterSelectComponent', () => {
|
||||
let component: ParameterSelectComponent;
|
||||
let fixture: ComponentFixture<ParameterSelectComponent>;
|
||||
let scriptParams: ScriptParameter[];
|
||||
|
||||
function init() {
|
||||
scriptParams = [
|
||||
Object.assign(
|
||||
new ScriptParameter(),
|
||||
{
|
||||
name: '-a',
|
||||
type: ScriptParameterType.BOOLEAN
|
||||
}
|
||||
),
|
||||
Object.assign(
|
||||
new ScriptParameter(),
|
||||
{
|
||||
name: '-f',
|
||||
type: ScriptParameterType.FILE
|
||||
}
|
||||
),
|
||||
]
|
||||
}
|
||||
beforeEach(async(() => {
|
||||
init();
|
||||
TestBed.configureTestingModule({
|
||||
imports: [FormsModule],
|
||||
declarations: [ParameterSelectComponent],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ParameterSelectComponent);
|
||||
component = fixture.componentInstance;
|
||||
|
||||
component.parameters = scriptParams;
|
||||
component.removable = false;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should show the remove button when removable', () => {
|
||||
component.removable = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
const button = fixture.debugElement.query(By.css('button.remove-button'));
|
||||
expect(button).not.toBeNull();
|
||||
});
|
||||
|
||||
it('should hide the remove button when not removable', () => {
|
||||
component.removable = false;
|
||||
fixture.detectChanges();
|
||||
|
||||
const button = fixture.debugElement.query(By.css('button.remove-button'));
|
||||
expect(button).toBeNull();
|
||||
});
|
||||
});
|
@@ -0,0 +1,90 @@
|
||||
import { Component, EventEmitter, Input, OnInit, Output, Optional } from '@angular/core';
|
||||
import { ProcessParameter } from '../../../processes/process-parameter.model';
|
||||
import { ScriptParameter } from '../../../scripts/script-parameter.model';
|
||||
import { hasNoValue } from '../../../../shared/empty.util';
|
||||
import { ControlContainer, NgForm } from '@angular/forms';
|
||||
import { controlContainerFactory } from '../../process-form.component';
|
||||
|
||||
/**
|
||||
* Component to select a single parameter for a process
|
||||
*/
|
||||
@Component({
|
||||
selector: 'ds-parameter-select',
|
||||
templateUrl: './parameter-select.component.html',
|
||||
styleUrls: ['./parameter-select.component.scss'],
|
||||
viewProviders: [{
|
||||
provide: ControlContainer,
|
||||
useFactory: controlContainerFactory,
|
||||
deps: [[new Optional(), NgForm]]
|
||||
}]
|
||||
})
|
||||
export class ParameterSelectComponent {
|
||||
@Input() index: number;
|
||||
|
||||
/**
|
||||
* The current parameter value of the selected parameter
|
||||
*/
|
||||
@Input() parameterValue: ProcessParameter = new ProcessParameter();
|
||||
|
||||
/**
|
||||
* The available script parameters for the script
|
||||
*/
|
||||
@Input() parameters: ScriptParameter[];
|
||||
|
||||
/**
|
||||
* Whether or not this selected parameter can be removed from the list
|
||||
*/
|
||||
@Input() removable: boolean;
|
||||
|
||||
/**
|
||||
* Emits the parameter value when it's removed
|
||||
*/
|
||||
@Output() removeParameter: EventEmitter<ProcessParameter> = new EventEmitter<ProcessParameter>();
|
||||
|
||||
/**
|
||||
* Emits the updated parameter value when it changes
|
||||
*/
|
||||
@Output() changeParameter: EventEmitter<ProcessParameter> = new EventEmitter<ProcessParameter>();
|
||||
|
||||
/**
|
||||
* Returns the script parameter based on the currently selected name
|
||||
*/
|
||||
get selectedScriptParameter(): ScriptParameter {
|
||||
return this.parameters.find((parameter: ScriptParameter) => parameter.name === this.selectedParameter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the currently selected parameter name
|
||||
*/
|
||||
get selectedParameter(): string {
|
||||
return this.parameterValue ? this.parameterValue.name : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the currently selected parameter based on the provided parameter name
|
||||
* Emits the new value from the changeParameter output
|
||||
* @param value The parameter name to set
|
||||
*/
|
||||
set selectedParameter(value: string) {
|
||||
this.parameterValue.name = value;
|
||||
this.selectedParameterValue = undefined;
|
||||
this.changeParameter.emit(this.parameterValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the currently selected parameter value
|
||||
*/
|
||||
get selectedParameterValue(): any {
|
||||
return this.parameterValue ? this.parameterValue.value : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the currently selected value for the parameter
|
||||
* Emits the new value from the changeParameter output
|
||||
* @param value The parameter value to set
|
||||
*/
|
||||
set selectedParameterValue(value: any) {
|
||||
this.parameterValue.value = value;
|
||||
this.changeParameter.emit(this.parameterValue);
|
||||
}
|
||||
}
|
@@ -0,0 +1 @@
|
||||
<input type="hidden" value="true" name="boolean-value-{{index}}" id="boolean-value-{{index}}"/>
|
@@ -0,0 +1,32 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { BooleanValueInputComponent } from './boolean-value-input.component';
|
||||
import { DebugElement } from '@angular/core';
|
||||
import { By } from '@angular/platform-browser';
|
||||
|
||||
describe('BooleanValueInputComponent', () => {
|
||||
let component: BooleanValueInputComponent;
|
||||
let fixture: ComponentFixture<BooleanValueInputComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [BooleanValueInputComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(BooleanValueInputComponent);
|
||||
component = fixture.componentInstance;
|
||||
spyOn(component.updateValue, 'emit');
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should emit true onInit', () => {
|
||||
expect(component.updateValue.emit).toHaveBeenCalledWith(true);
|
||||
});
|
||||
});
|
@@ -0,0 +1,21 @@
|
||||
import { Component, OnInit, Optional } from '@angular/core';
|
||||
import { ValueInputComponent } from '../value-input.component';
|
||||
import { ControlContainer, NgForm } from '@angular/forms';
|
||||
import { controlContainerFactory } from '../../../process-form.component';
|
||||
|
||||
/**
|
||||
* Represents the value of a boolean parameter
|
||||
*/
|
||||
@Component({
|
||||
selector: 'ds-boolean-value-input',
|
||||
templateUrl: './boolean-value-input.component.html',
|
||||
styleUrls: ['./boolean-value-input.component.scss'],
|
||||
viewProviders: [ { provide: ControlContainer,
|
||||
useFactory: controlContainerFactory,
|
||||
deps: [[new Optional(), NgForm]] } ]
|
||||
})
|
||||
export class BooleanValueInputComponent extends ValueInputComponent<boolean> implements OnInit {
|
||||
ngOnInit() {
|
||||
this.updateValue.emit(true)
|
||||
}
|
||||
}
|
@@ -0,0 +1,7 @@
|
||||
<input required #string="ngModel" type="text" class="form-control" name="date-value-{{index}}" id="date-value-{{index}}" [ngModel]="value" (ngModelChange)="setValue($event)"/>
|
||||
<div *ngIf="string.invalid && (string.dirty || string.touched)"
|
||||
class="alert alert-danger validation-error">
|
||||
<div *ngIf="string.errors.required">
|
||||
{{'process.new.parameter.string.required' | translate}}
|
||||
</div>
|
||||
</div>
|
@@ -0,0 +1,70 @@
|
||||
import { async, ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
|
||||
|
||||
import { DateValueInputComponent } from './date-value-input.component';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { TranslateLoaderMock } from '../../../../../shared/mocks/translate-loader.mock';
|
||||
|
||||
describe('DateValueInputComponent', () => {
|
||||
let component: DateValueInputComponent;
|
||||
let fixture: ComponentFixture<DateValueInputComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
FormsModule,
|
||||
TranslateModule.forRoot({
|
||||
loader: {
|
||||
provide: TranslateLoader,
|
||||
useClass: TranslateLoaderMock
|
||||
}
|
||||
})],
|
||||
declarations: [DateValueInputComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(DateValueInputComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should not show a validation error if the input field was left untouched but left empty', () => {
|
||||
const validationError = fixture.debugElement.query(By.css('.validation-error'));
|
||||
expect(validationError).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should show a validation error if the input field was touched but left empty', fakeAsync(() => {
|
||||
component.value = '';
|
||||
fixture.detectChanges();
|
||||
tick();
|
||||
|
||||
const input = fixture.debugElement.query(By.css('input'));
|
||||
input.triggerEventHandler('blur', null);
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
const validationError = fixture.debugElement.query(By.css('.validation-error'));
|
||||
expect(validationError).toBeTruthy();
|
||||
}));
|
||||
|
||||
it('should not show a validation error if the input field was touched but not left empty', fakeAsync(() => {
|
||||
component.value = 'testValue';
|
||||
fixture.detectChanges();
|
||||
tick();
|
||||
|
||||
const input = fixture.debugElement.query(By.css('input'));
|
||||
input.triggerEventHandler('blur', null);
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
const validationError = fixture.debugElement.query(By.css('.validation-error'));
|
||||
expect(validationError).toBeFalsy();
|
||||
}));
|
||||
});
|
@@ -0,0 +1,32 @@
|
||||
import { Component, OnInit, Optional, Input } from '@angular/core';
|
||||
import { ValueInputComponent } from '../value-input.component';
|
||||
import { ControlContainer, NgForm } from '@angular/forms';
|
||||
import { controlContainerFactory } from '../../../process-form.component';
|
||||
|
||||
/**
|
||||
* Represents the user inputted value of a date parameter
|
||||
*/
|
||||
@Component({
|
||||
selector: 'ds-date-value-input',
|
||||
templateUrl: './date-value-input.component.html',
|
||||
styleUrls: ['./date-value-input.component.scss'],
|
||||
viewProviders: [ { provide: ControlContainer,
|
||||
useFactory: controlContainerFactory,
|
||||
deps: [[new Optional(), NgForm]] } ]
|
||||
})
|
||||
export class DateValueInputComponent extends ValueInputComponent<string> {
|
||||
/**
|
||||
* The current value of the date string
|
||||
*/
|
||||
value: string;
|
||||
@Input() initialValue;
|
||||
|
||||
ngOnInit() {
|
||||
this.value = this.initialValue;
|
||||
}
|
||||
|
||||
setValue(value) {
|
||||
this.value = value;
|
||||
this.updateValue.emit(value)
|
||||
}
|
||||
}
|
@@ -0,0 +1,13 @@
|
||||
<label for="file-upload-{{index}}" class="d-flex align-items-center m-0">
|
||||
<span class="btn btn-light">
|
||||
{{'process.new.parameter.file.upload-button' | translate}}
|
||||
</span>
|
||||
<span class="file-name ml-1">{{fileObject?.name}}</span>
|
||||
</label>
|
||||
<input requireFile #file="ngModel" type="file" name="file-upload-{{index}}" id="file-upload-{{index}}" class="form-control-file d-none" [ngModel]="fileObject" (ngModelChange)="setFile($event)"/>
|
||||
<div *ngIf="file.invalid && (file.dirty || file.touched)"
|
||||
class="alert alert-danger validation-error">
|
||||
<div *ngIf="file.errors.required">
|
||||
{{'process.new.parameter.file.required' | translate}}
|
||||
</div>
|
||||
</div>
|
@@ -0,0 +1,6 @@
|
||||
.file-name {
|
||||
flex: 1;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
@@ -0,0 +1,57 @@
|
||||
import { async, ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
|
||||
|
||||
import { FormsModule, NgForm, ReactiveFormsModule } from '@angular/forms';
|
||||
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { FileValueInputComponent } from './file-value-input.component';
|
||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { FileValueAccessorDirective } from '../../../../../shared/utils/file-value-accessor.directive';
|
||||
import { FileValidator } from '../../../../../shared/utils/require-file.validator';
|
||||
import { TranslateLoaderMock } from '../../../../../shared/mocks/translate-loader.mock';
|
||||
|
||||
describe('FileValueInputComponent', () => {
|
||||
let component: FileValueInputComponent;
|
||||
let fixture: ComponentFixture<FileValueInputComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
FormsModule,
|
||||
TranslateModule.forRoot({
|
||||
loader: {
|
||||
provide: TranslateLoader,
|
||||
useClass: TranslateLoaderMock
|
||||
}
|
||||
})],
|
||||
declarations: [FileValueInputComponent, FileValueAccessorDirective, FileValidator],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(FileValueInputComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should not show a validation error if the input field was left untouched but left empty', () => {
|
||||
const validationError = fixture.debugElement.query(By.css('.validation-error'));
|
||||
expect(validationError).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should show a validation error if the input field was touched but left empty', () => {
|
||||
const input = fixture.debugElement.query(By.css('input'));
|
||||
input.triggerEventHandler('blur', null);
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
const validationError = fixture.debugElement.query(By.css('.validation-error'));
|
||||
expect(validationError).toBeTruthy();
|
||||
});
|
||||
});
|
@@ -0,0 +1,26 @@
|
||||
import { Component, Optional } from '@angular/core';
|
||||
import { ValueInputComponent } from '../value-input.component';
|
||||
import { ControlContainer, NgForm } from '@angular/forms';
|
||||
import { controlContainerFactory } from '../../../process-form.component';
|
||||
|
||||
/**
|
||||
* Represents the user inputted value of a file parameter
|
||||
*/
|
||||
@Component({
|
||||
selector: 'ds-file-value-input',
|
||||
templateUrl: './file-value-input.component.html',
|
||||
styleUrls: ['./file-value-input.component.scss'],
|
||||
viewProviders: [ { provide: ControlContainer,
|
||||
useFactory: controlContainerFactory,
|
||||
deps: [[new Optional(), NgForm]] } ]
|
||||
})
|
||||
export class FileValueInputComponent extends ValueInputComponent<File> {
|
||||
/**
|
||||
* The current value of the file
|
||||
*/
|
||||
fileObject: File;
|
||||
setFile(files) {
|
||||
this.fileObject = files.length > 0 ? files[0] : undefined;
|
||||
this.updateValue.emit(this.fileObject);
|
||||
}
|
||||
}
|
@@ -0,0 +1,7 @@
|
||||
<div [ngSwitch]="parameter?.type">
|
||||
<ds-string-value-input *ngSwitchCase="parameterTypes.STRING" [initialValue]="initialValue" (updateValue)="updateValue.emit($event)" [index]="index"></ds-string-value-input>
|
||||
<ds-string-value-input *ngSwitchCase="parameterTypes.OUTPUT" [initialValue]="initialValue" (updateValue)="updateValue.emit($event)" [index]="index"></ds-string-value-input>
|
||||
<ds-date-value-input *ngSwitchCase="parameterTypes.DATE" [initialValue]="initialValue" (updateValue)="updateValue.emit($event)" [index]="index"></ds-date-value-input>
|
||||
<ds-file-value-input *ngSwitchCase="parameterTypes.FILE" (updateValue)="updateValue.emit($event)" [index]="index"></ds-file-value-input>
|
||||
<ds-boolean-value-input *ngSwitchCase="parameterTypes.BOOLEAN" (updateValue)="updateValue.emit($event)" [index]="index"></ds-boolean-value-input>
|
||||
</div>
|
@@ -0,0 +1,105 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ParameterValueInputComponent } from './parameter-value-input.component';
|
||||
import { ScriptParameter } from '../../../scripts/script-parameter.model';
|
||||
import { ScriptParameterType } from '../../../scripts/script-parameter-type.model';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { BooleanValueInputComponent } from './boolean-value-input/boolean-value-input.component';
|
||||
import { StringValueInputComponent } from './string-value-input/string-value-input.component';
|
||||
import { FileValueInputComponent } from './file-value-input/file-value-input.component';
|
||||
import { DateValueInputComponent } from './date-value-input/date-value-input.component';
|
||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
||||
import { FileValueAccessorDirective } from '../../../../shared/utils/file-value-accessor.directive';
|
||||
import { FileValidator } from '../../../../shared/utils/require-file.validator';
|
||||
import { TranslateLoaderMock } from '../../../../shared/mocks/translate-loader.mock';
|
||||
|
||||
describe('ParameterValueInputComponent', () => {
|
||||
let component: ParameterValueInputComponent;
|
||||
let fixture: ComponentFixture<ParameterValueInputComponent>;
|
||||
let booleanParameter;
|
||||
let stringParameter;
|
||||
let fileParameter;
|
||||
let dateParameter;
|
||||
let outputParameter;
|
||||
|
||||
function init() {
|
||||
booleanParameter = Object.assign(new ScriptParameter(), { type: ScriptParameterType.BOOLEAN });
|
||||
stringParameter = Object.assign(new ScriptParameter(), { type: ScriptParameterType.STRING });
|
||||
fileParameter = Object.assign(new ScriptParameter(), { type: ScriptParameterType.FILE });
|
||||
dateParameter = Object.assign(new ScriptParameter(), { type: ScriptParameterType.DATE });
|
||||
outputParameter = Object.assign(new ScriptParameter(), { type: ScriptParameterType.OUTPUT });
|
||||
}
|
||||
|
||||
beforeEach(async(() => {
|
||||
init();
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
FormsModule,
|
||||
TranslateModule.forRoot({
|
||||
loader: {
|
||||
provide: TranslateLoader,
|
||||
useClass: TranslateLoaderMock
|
||||
}
|
||||
})],
|
||||
declarations: [
|
||||
ParameterValueInputComponent,
|
||||
BooleanValueInputComponent,
|
||||
StringValueInputComponent,
|
||||
FileValueInputComponent,
|
||||
DateValueInputComponent,
|
||||
FileValueAccessorDirective,
|
||||
FileValidator
|
||||
],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ParameterValueInputComponent);
|
||||
component = fixture.componentInstance;
|
||||
component.parameter = stringParameter;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should show a BooleanValueInputComponent when the parameter type is boolean', () => {
|
||||
component.parameter = booleanParameter;
|
||||
fixture.detectChanges();
|
||||
const valueInput = fixture.debugElement.query(By.directive(BooleanValueInputComponent));
|
||||
expect(valueInput).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should show a StringValueInputComponent when the parameter type is string', () => {
|
||||
component.parameter = stringParameter;
|
||||
fixture.detectChanges();
|
||||
const valueInput = fixture.debugElement.query(By.directive(StringValueInputComponent));
|
||||
expect(valueInput).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should show a FileValueInputComponent when the parameter type is file', () => {
|
||||
component.parameter = fileParameter;
|
||||
fixture.detectChanges();
|
||||
const valueInput = fixture.debugElement.query(By.directive(FileValueInputComponent));
|
||||
expect(valueInput).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should show a DateValueInputComponent when the parameter type is date', () => {
|
||||
component.parameter = dateParameter;
|
||||
fixture.detectChanges();
|
||||
const valueInput = fixture.debugElement.query(By.directive(DateValueInputComponent));
|
||||
expect(valueInput).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should show a StringValueInputComponent when the parameter type is output', () => {
|
||||
component.parameter = outputParameter;
|
||||
fixture.detectChanges();
|
||||
const valueInput = fixture.debugElement.query(By.directive(StringValueInputComponent));
|
||||
expect(valueInput).toBeTruthy();
|
||||
});
|
||||
});
|
@@ -0,0 +1,37 @@
|
||||
import { Component, EventEmitter, Input, OnChanges, OnInit, Optional, Output } from '@angular/core';
|
||||
import { ScriptParameterType } from '../../../scripts/script-parameter-type.model';
|
||||
import { ScriptParameter } from '../../../scripts/script-parameter.model';
|
||||
import { ControlContainer, NgForm } from '@angular/forms';
|
||||
import { controlContainerFactory } from '../../process-form.component';
|
||||
|
||||
/**
|
||||
* Component that renders the correct parameter value input based the script parameter's type
|
||||
*/
|
||||
@Component({
|
||||
selector: 'ds-parameter-value-input',
|
||||
templateUrl: './parameter-value-input.component.html',
|
||||
styleUrls: ['./parameter-value-input.component.scss'],
|
||||
viewProviders: [ { provide: ControlContainer,
|
||||
useFactory: controlContainerFactory,
|
||||
deps: [[new Optional(), NgForm]] } ]
|
||||
})
|
||||
export class ParameterValueInputComponent {
|
||||
@Input() index: number;
|
||||
|
||||
/**
|
||||
* The current script parameter
|
||||
*/
|
||||
@Input() parameter: ScriptParameter;
|
||||
|
||||
|
||||
@Input() initialValue: any;
|
||||
/**
|
||||
* Emits the value of the input when its updated
|
||||
*/
|
||||
@Output() updateValue: EventEmitter<any> = new EventEmitter();
|
||||
|
||||
/**
|
||||
* The available script parameter types
|
||||
*/
|
||||
parameterTypes = ScriptParameterType;
|
||||
}
|
@@ -0,0 +1,7 @@
|
||||
<input required #string="ngModel" type="text" name="string-value-{{index}}" class="form-control" id="string-value-{{index}}" [ngModel]="value" (ngModelChange)="setValue($event)"/>
|
||||
<div *ngIf="string.invalid && (string.dirty || string.touched)"
|
||||
class="alert alert-danger validation-error">
|
||||
<div *ngIf="string.errors.required">
|
||||
{{'process.new.parameter.string.required' | translate}}
|
||||
</div>
|
||||
</div>
|
@@ -0,0 +1,72 @@
|
||||
import { async, ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
|
||||
|
||||
import { FormsModule, NgForm } from '@angular/forms';
|
||||
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { StringValueInputComponent } from './string-value-input.component';
|
||||
import { TranslateLoaderMock } from '../../../../../shared/mocks/translate-loader.mock';
|
||||
|
||||
describe('StringValueInputComponent', () => {
|
||||
let component: StringValueInputComponent;
|
||||
let fixture: ComponentFixture<StringValueInputComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
FormsModule,
|
||||
TranslateModule.forRoot({
|
||||
loader: {
|
||||
provide: TranslateLoader,
|
||||
useClass: TranslateLoaderMock
|
||||
}
|
||||
})],
|
||||
declarations: [StringValueInputComponent],
|
||||
providers: [
|
||||
]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(StringValueInputComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should not show a validation error if the input field was left untouched but left empty', () => {
|
||||
const validationError = fixture.debugElement.query(By.css('.validation-error'));
|
||||
expect(validationError).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should show a validation error if the input field was touched but left empty', fakeAsync(() => {
|
||||
component.value = '';
|
||||
fixture.detectChanges();
|
||||
tick();
|
||||
|
||||
const input = fixture.debugElement.query(By.css('input'));
|
||||
input.triggerEventHandler('blur', null);
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
const validationError = fixture.debugElement.query(By.css('.validation-error'));
|
||||
expect(validationError).toBeTruthy();
|
||||
}));
|
||||
|
||||
it('should not show a validation error if the input field was touched but not left empty', fakeAsync(() => {
|
||||
component.value = 'testValue';
|
||||
fixture.detectChanges();
|
||||
tick();
|
||||
|
||||
const input = fixture.debugElement.query(By.css('input'));
|
||||
input.triggerEventHandler('blur', null);
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
const validationError = fixture.debugElement.query(By.css('.validation-error'));
|
||||
expect(validationError).toBeFalsy();
|
||||
}));
|
||||
});
|
@@ -0,0 +1,32 @@
|
||||
import { Component, Optional, Input } from '@angular/core';
|
||||
import { ValueInputComponent } from '../value-input.component';
|
||||
import { ControlContainer, NgForm } from '@angular/forms';
|
||||
import { controlContainerFactory } from '../../../process-form.component';
|
||||
|
||||
/**
|
||||
* Represents the user inputted value of a string parameter
|
||||
*/
|
||||
@Component({
|
||||
selector: 'ds-string-value-input',
|
||||
templateUrl: './string-value-input.component.html',
|
||||
styleUrls: ['./string-value-input.component.scss'],
|
||||
viewProviders: [ { provide: ControlContainer,
|
||||
useFactory: controlContainerFactory,
|
||||
deps: [[new Optional(), NgForm]] } ]
|
||||
})
|
||||
export class StringValueInputComponent extends ValueInputComponent<string> {
|
||||
/**
|
||||
* The current value of the string
|
||||
*/
|
||||
value: string;
|
||||
@Input() initialValue;
|
||||
|
||||
ngOnInit() {
|
||||
this.value = this.initialValue;
|
||||
}
|
||||
|
||||
setValue(value) {
|
||||
this.value = value;
|
||||
this.updateValue.emit(value)
|
||||
}
|
||||
}
|
@@ -0,0 +1,12 @@
|
||||
import { EventEmitter, Input, Output } from '@angular/core';
|
||||
|
||||
/**
|
||||
* Abstract class that represents value input components
|
||||
*/
|
||||
export abstract class ValueInputComponent<T> {
|
||||
@Input() index: number;
|
||||
/**
|
||||
* Used by the subclasses to emit the value when it's updated
|
||||
*/
|
||||
@Output() updateValue: EventEmitter<T> = new EventEmitter<T>()
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
<div class="form-group" *ngIf="script">
|
||||
<label>{{'process.new.select-parameters' | translate}}</label>
|
||||
<ds-parameter-select
|
||||
*ngFor="let value of parameterValues; let i = index; let last = last"
|
||||
[parameters]="script.parameters"
|
||||
[parameterValue]="value"
|
||||
[removable]="!last"
|
||||
[index]="i"
|
||||
(removeParameter)="removeParameter(i)"
|
||||
(changeParameter)="updateParameter($event, i)"></ds-parameter-select>
|
||||
</div>
|
@@ -0,0 +1,64 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ProcessParametersComponent } from './process-parameters.component';
|
||||
import { ProcessParameter } from '../../processes/process-parameter.model';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { ParameterSelectComponent } from './parameter-select/parameter-select.component';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { Script } from '../../scripts/script.model';
|
||||
import { ScriptParameter } from '../../scripts/script-parameter.model';
|
||||
import { TranslateLoaderMock } from '../../../shared/mocks/translate-loader.mock';
|
||||
|
||||
describe('ProcessParametersComponent', () => {
|
||||
let component: ProcessParametersComponent;
|
||||
let fixture: ComponentFixture<ProcessParametersComponent>;
|
||||
let parameterValues;
|
||||
let script;
|
||||
|
||||
function init() {
|
||||
const param1 = new ScriptParameter();
|
||||
const param2 = new ScriptParameter();
|
||||
script = Object.assign(new Script(), { parameters: [param1, param2] });
|
||||
parameterValues = [
|
||||
Object.assign(new ProcessParameter(), { name: '-a', value: 'bla' }),
|
||||
Object.assign(new ProcessParameter(), { name: '-b', value: '123' }),
|
||||
Object.assign(new ProcessParameter(), { name: '-c', value: 'value' }),
|
||||
]
|
||||
}
|
||||
|
||||
beforeEach(async(() => {
|
||||
init();
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
FormsModule,
|
||||
TranslateModule.forRoot({
|
||||
loader: {
|
||||
provide: TranslateLoader,
|
||||
useClass: TranslateLoaderMock
|
||||
}
|
||||
})],
|
||||
declarations: [ProcessParametersComponent, ParameterSelectComponent],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ProcessParametersComponent);
|
||||
component = fixture.componentInstance;
|
||||
component.script = script;
|
||||
component.parameterValues = parameterValues;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should render a ParameterSelectComponent for each parameter value of the component', () => {
|
||||
const selectComponents = fixture.debugElement.queryAll(By.directive(ParameterSelectComponent));
|
||||
expect(selectComponents.length).toBe(parameterValues.length);
|
||||
});
|
||||
});
|
@@ -0,0 +1,109 @@
|
||||
import { Component, EventEmitter, Input, OnChanges, Optional, Output, SimpleChanges } from '@angular/core';
|
||||
import { Script } from '../../scripts/script.model';
|
||||
import { ProcessParameter } from '../../processes/process-parameter.model';
|
||||
import { hasValue } from '../../../shared/empty.util';
|
||||
import { ControlContainer, NgForm } from '@angular/forms';
|
||||
import { ScriptParameter } from '../../scripts/script-parameter.model';
|
||||
import { controlContainerFactory } from '../process-form.component';
|
||||
|
||||
/**
|
||||
* Component that represents the selected list of parameters for a script
|
||||
*/
|
||||
@Component({
|
||||
selector: 'ds-process-parameters',
|
||||
templateUrl: './process-parameters.component.html',
|
||||
styleUrls: ['./process-parameters.component.scss'],
|
||||
viewProviders: [{
|
||||
provide: ControlContainer,
|
||||
useFactory: controlContainerFactory,
|
||||
deps: [[new Optional(), NgForm]]
|
||||
}]
|
||||
})
|
||||
export class ProcessParametersComponent implements OnChanges {
|
||||
/**
|
||||
* The currently selected script
|
||||
*/
|
||||
@Input() script: Script;
|
||||
@Input() initialParams: ProcessParameter[];
|
||||
/**
|
||||
* Emits the parameter values when they're updated
|
||||
*/
|
||||
@Output() updateParameters: EventEmitter<ProcessParameter[]> = new EventEmitter();
|
||||
|
||||
/**
|
||||
* The current parameter values
|
||||
*/
|
||||
parameterValues: ProcessParameter[];
|
||||
|
||||
ngOnInit() {
|
||||
if (hasValue(this.initialParams)) {
|
||||
this.parameterValues = this.initialParams;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes sure the parameters are reset when the script changes
|
||||
* @param changes
|
||||
*/
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
if (changes.script) {
|
||||
this.initParameters()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Empties the parameter values
|
||||
* Initializes the first parameter value
|
||||
*/
|
||||
initParameters() {
|
||||
if (hasValue(this.initialParams)) {
|
||||
this.parameterValues = this.initialParams;
|
||||
} else {
|
||||
this.parameterValues = [];
|
||||
this.initializeParameter();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a single parameter value using its new value and index
|
||||
* Adds a new parameter when the last of the parameter values is changed
|
||||
* @param processParameter The new value of the parameter
|
||||
* @param index The index of the parameter
|
||||
*/
|
||||
updateParameter(processParameter: ProcessParameter, index: number) {
|
||||
this.parameterValues[index] = processParameter;
|
||||
if (index === this.parameterValues.length - 1) {
|
||||
this.addParameter();
|
||||
}
|
||||
this.updateParameters.emit(this.parameterValues.filter((param: ProcessParameter) => hasValue(param.name)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a parameter value from the list
|
||||
* @param index The index of the parameter to remove
|
||||
*/
|
||||
removeParameter(index: number) {
|
||||
this.parameterValues = this.parameterValues.filter((value, i) => i !== index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes parameter values based on the selected script
|
||||
*/
|
||||
initializeParameter() {
|
||||
if (hasValue(this.script)) {
|
||||
this.parameterValues = this.script.parameters
|
||||
.filter((param) => param.mandatory)
|
||||
.map(
|
||||
(parameter: ScriptParameter) => Object.assign(new ProcessParameter(), { name: parameter.name })
|
||||
);
|
||||
}
|
||||
this.addParameter();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an empty parameter value to the end of the list
|
||||
*/
|
||||
addParameter() {
|
||||
this.parameterValues = [...this.parameterValues, new ProcessParameter()];
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user