diff --git a/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.html b/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.html
index a5c0f0b007..9f8fa48947 100755
--- a/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.html
+++ b/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.html
@@ -109,6 +109,16 @@
+ } @else if (hasScreen) {
+
+
+
+
+
} @else {
}
diff --git a/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.spec.ts b/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.spec.ts
index b06342326e..d14682df64 100755
--- a/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.spec.ts
+++ b/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.spec.ts
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-import { SimpleChange } from '@angular/core';
+import { DebugElement, SimpleChange } from '@angular/core';
import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing';
import { FormModel, FormOutcomeEvent, FormOutcomeModel } from '@alfresco/adf-core';
import { of, throwError } from 'rxjs';
@@ -49,6 +49,11 @@ import { MatButtonHarness } from '@angular/material/button/testing';
import { FormCloudDisplayMode } from '../../../services/form-fields.interfaces';
import { MatDialogHarness } from '@angular/material/dialog/testing';
import { MatDialog } from '@angular/material/dialog';
+import { ReactiveFormsModule } from '@angular/forms';
+import { StartProcessScreenCloudComponent } from '../../../screen/components/screen-cloud/start-process-event-screen/start-process-screen-cloud.component';
+import { provideScreen } from '../../../screen/services/provide-screen';
+import { StartProcessScreenCloud } from '../../../screen/components/screen-cloud/start-process-event-screen/start-process-screen.model';
+import { MockedTaskScreenCloudComponent } from '../../../testing/start-process-screen-mock.component';
describe('StartProcessCloudComponent', () => {
let loader: HarnessLoader;
@@ -57,7 +62,7 @@ describe('StartProcessCloudComponent', () => {
let fixture: ComponentFixture;
let processService: StartProcessCloudService;
let formCloudService: FormCloudService;
- let getDefinitionsSpy: jasmine.Spy;
+ let getProcessDefinitionsSpy: jasmine.Spy;
let startProcessSpy: jasmine.Spy;
let startProcessWithFormSpy: jasmine.Spy;
let formDefinitionSpy: jasmine.Spy;
@@ -65,6 +70,7 @@ describe('StartProcessCloudComponent', () => {
let getStartEventConstantSpy: jasmine.Spy;
const firstChange = new SimpleChange(undefined, 'myApp', true);
+ const screenId = 'screen-1234-5678-121212-123456';
const selectOptionByName = async (name: string) => {
const arrowButton = await loader.getHarness(MatButtonHarness.with({ selector: '#adf-select-process-dropdown' }));
@@ -84,14 +90,15 @@ describe('StartProcessCloudComponent', () => {
beforeEach(() => {
TestBed.configureTestingModule({
- imports: [StartProcessCloudComponent]
+ imports: [StartProcessCloudComponent, ReactiveFormsModule, StartProcessScreenCloudComponent],
+ providers: [provideScreen(screenId, MockedTaskScreenCloudComponent)]
});
processService = TestBed.inject(StartProcessCloudService);
formCloudService = TestBed.inject(FormCloudService);
fixture = TestBed.createComponent(StartProcessCloudComponent);
component = fixture.componentInstance;
- getDefinitionsSpy = spyOn(processService, 'getProcessDefinitions');
+ getProcessDefinitionsSpy = spyOn(processService, 'getProcessDefinitions');
formDefinitionSpy = spyOn(formCloudService, 'getForm');
spyOn(processService, 'updateProcess').and.returnValue(of());
startProcessSpy = spyOn(processService, 'startProcess').and.returnValue(of(fakeProcessInstance));
@@ -143,7 +150,7 @@ describe('StartProcessCloudComponent', () => {
component.name = 'My new process';
component.processDefinitionName = 'processwithoutform2';
- getDefinitionsSpy.and.returnValue(of(fakeSingleProcessDefinitionWithoutForm(component.processDefinitionName)));
+ getProcessDefinitionsSpy.and.returnValue(of(fakeSingleProcessDefinitionWithoutForm(component.processDefinitionName)));
fixture.detectChanges();
const change = new SimpleChange(null, 'MyApp', true);
@@ -162,7 +169,7 @@ describe('StartProcessCloudComponent', () => {
beforeEach(() => {
component.name = 'My formless new process';
component.appName = 'myApp';
- getDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions));
+ getProcessDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions));
fixture.detectChanges();
const change = new SimpleChange(null, 'MyApp', true);
component.ngOnChanges({ appName: change });
@@ -172,7 +179,7 @@ describe('StartProcessCloudComponent', () => {
it('should be able to start a process with a valid process name and process definition', fakeAsync(() => {
component.name = 'My new process';
component.processDefinitionName = 'processwithoutform2';
- getDefinitionsSpy.and.returnValue(of(fakeSingleProcessDefinitionWithoutForm(component.processDefinitionName)));
+ getProcessDefinitionsSpy.and.returnValue(of(fakeSingleProcessDefinitionWithoutForm(component.processDefinitionName)));
fixture.detectChanges();
const change = new SimpleChange(null, 'MyApp', true);
@@ -187,7 +194,7 @@ describe('StartProcessCloudComponent', () => {
}));
it('should create a process instance if the selection is valid', async () => {
- getDefinitionsSpy = getDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions));
+ getProcessDefinitionsSpy = getProcessDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions));
component.name = 'My new process';
component.processDefinitionName = 'process';
await selectOptionByName('processwithoutform2');
@@ -276,11 +283,81 @@ describe('StartProcessCloudComponent', () => {
});
});
+ describe('screen on start process event', () => {
+ let screenDebugComponent: DebugElement;
+ let screenComponent: StartProcessScreenCloud;
+
+ beforeEach(async () => {
+ fixture.detectChanges();
+
+ const processDefinitionWithScreen = fakeSingleProcessDefinition('processWithScreen', { formKey: screenId });
+ const someOtherProcess = fakeSingleProcessDefinition('otherProcess');
+ getProcessDefinitionsSpy.and.returnValue(of([...processDefinitionWithScreen, ...someOtherProcess]));
+ component.processDefinitionName = 'processWithScreen';
+
+ component.ngOnChanges({ appName: new SimpleChange(null, 'myAppName', true) });
+ fixture.detectChanges();
+
+ screenDebugComponent = fixture.debugElement.query(By.directive(MockedTaskScreenCloudComponent));
+ screenComponent = screenDebugComponent.componentInstance;
+ });
+
+ it('should show screen', () => {
+ expect(screenComponent).toBeDefined();
+
+ const contentElement = screenDebugComponent.nativeElement.querySelector('div');
+ expect(contentElement.textContent).toBe('Mock Screen Component');
+ });
+
+ it('should toggle default process buttons', () => {
+ screenComponent.defaultStartProcessButtonsConfigurationChange.emit({ show: false, disable: false });
+ fixture.detectChanges();
+
+ let startButton = fixture.nativeElement.querySelector('#button-start');
+ let cancelButton = fixture.nativeElement.querySelector('#cancel_process');
+ expect(startButton).toBeNull();
+ expect(cancelButton).toBeNull();
+
+ screenComponent.defaultStartProcessButtonsConfigurationChange.emit({ show: true, disable: false });
+ fixture.detectChanges();
+
+ startButton = fixture.nativeElement.querySelector('#button-start');
+ cancelButton = fixture.nativeElement.querySelector('#cancel_process');
+ expect(startButton).not.toBeNull();
+ expect(cancelButton).not.toBeNull();
+ });
+
+ it('should toggle disable start process button', () => {
+ screenComponent.defaultStartProcessButtonsConfigurationChange.emit({ show: true, disable: true });
+ fixture.detectChanges();
+ const startButton = fixture.nativeElement.querySelector('#button-start');
+
+ expect(startButton.disabled).toBe(true);
+
+ screenComponent.defaultStartProcessButtonsConfigurationChange.emit({ show: true, disable: false });
+ fixture.detectChanges();
+
+ expect(startButton.disabled).toBe(false);
+ });
+
+ it('should set payload for screen', () => {
+ const screenPayload = 'My payload';
+ screenComponent.defaultStartProcessButtonsConfigurationChange.emit({ show: true, disable: false });
+ screenComponent.startProcessPayloadChanged.emit(screenPayload);
+ fixture.detectChanges();
+
+ const startButton = fixture.debugElement.query(By.css('#button-start'));
+ startButton.triggerEventHandler('click', null);
+
+ expect(startProcessWithFormSpy).toHaveBeenCalledWith('myAppName', screenId, 0, jasmine.objectContaining({ values: screenPayload }));
+ });
+ });
+
describe('start a process with start form', () => {
beforeEach(() => {
component.name = 'My new process with form';
component.appName = 'startformwithoutupload';
- getDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions));
+ getProcessDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions));
fixture.detectChanges();
const change = new SimpleChange(null, 'startformwithoutupload', true);
component.ngOnChanges({ appName: change });
@@ -315,7 +392,7 @@ describe('StartProcessCloudComponent', () => {
it('should be able to start a process with a valid form', async () => {
formDefinitionSpy.and.returnValue(of(fakeStartForm));
- getDefinitionsSpy.and.returnValue(of(fakeSingleProcessDefinition('processwithform')));
+ getProcessDefinitionsSpy.and.returnValue(of(fakeSingleProcessDefinition('processwithform')));
typeValueInto('[data-automation-id="adf-inplace-input"]', 'My new process with form');
typeValueInto('#processDefinitionName', 'processwithform');
fixture.detectChanges();
@@ -353,7 +430,7 @@ describe('StartProcessCloudComponent', () => {
(fakeStartFormClone.formRepresentation as any).displayMode = FormCloudDisplayMode.fullScreen;
formDefinitionSpy.and.returnValue(of(fakeStartFormClone));
- getDefinitionsSpy.and.returnValue(of(fakeSingleProcessDefinition('processwithform')));
+ getProcessDefinitionsSpy.and.returnValue(of(fakeSingleProcessDefinition('processwithform')));
typeValueInto('[data-automation-id="adf-inplace-input"]', 'My new process with form');
typeValueInto('#processDefinitionName', 'processwithform');
fixture.detectChanges();
@@ -371,7 +448,7 @@ describe('StartProcessCloudComponent', () => {
it('should NOT be able to start a process with a form NOT valid', async () => {
formDefinitionSpy.and.returnValue(of(fakeStartFormNotValid));
- getDefinitionsSpy.and.returnValue(of(fakeSingleProcessDefinition('processwithform')));
+ getProcessDefinitionsSpy.and.returnValue(of(fakeSingleProcessDefinition('processwithform')));
typeValueInto('[data-automation-id="adf-inplace-input"]', 'My new process with form');
typeValueInto('#processDefinitionName', 'processwithform');
fixture.detectChanges();
@@ -388,7 +465,7 @@ describe('StartProcessCloudComponent', () => {
});
it('should be able to start a process with a prefilled valid form', async () => {
- getDefinitionsSpy.and.returnValue(of(fakeSingleProcessDefinition('processwithform')));
+ getProcessDefinitionsSpy.and.returnValue(of(fakeSingleProcessDefinition('processwithform')));
formDefinitionSpy.and.returnValue(of(fakeStartForm));
typeValueInto('[data-automation-id="adf-inplace-input"]', 'My new process with form');
typeValueInto('#processDefinitionName', 'processwithform');
@@ -409,7 +486,7 @@ describe('StartProcessCloudComponent', () => {
});
it('should NOT be able to start a process with a prefilled NOT valid form', async () => {
- getDefinitionsSpy.and.returnValue(of(fakeSingleProcessDefinition('processwithform')));
+ getProcessDefinitionsSpy.and.returnValue(of(fakeSingleProcessDefinition('processwithform')));
formDefinitionSpy.and.returnValue(of(fakeStartFormNotValid));
typeValueInto('[data-automation-id="adf-inplace-input"]', 'My new process with form');
typeValueInto('#processDefinitionName', 'processwithform');
@@ -431,7 +508,7 @@ describe('StartProcessCloudComponent', () => {
});
it('should display enabled start process button if the selection is valid', async () => {
- getDefinitionsSpy.and.returnValue(of(fakeSingleProcessDefinition('processwithoutform2')));
+ getProcessDefinitionsSpy.and.returnValue(of(fakeSingleProcessDefinition('processwithoutform2')));
formDefinitionSpy.and.returnValue(of(fakeStartForm));
typeValueInto('[data-automation-id="adf-inplace-input"]', 'My new process with form');
typeValueInto('#processDefinitionName', 'processwithoutform2');
@@ -444,7 +521,7 @@ describe('StartProcessCloudComponent', () => {
});
it('should have start button enabled when default values are set', async () => {
- getDefinitionsSpy.and.returnValue(of(fakeSingleProcessDefinition('processwithoutform2')));
+ getProcessDefinitionsSpy.and.returnValue(of(fakeSingleProcessDefinition('processwithoutform2')));
formDefinitionSpy.and.returnValue(of(fakeStartForm));
const change = new SimpleChange(null, 'MyApp', true);
@@ -461,7 +538,7 @@ describe('StartProcessCloudComponent', () => {
beforeEach(() => {
component.name = 'My new process';
component.appName = 'myApp';
- getDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions));
+ getProcessDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions));
fixture.detectChanges();
const change = new SimpleChange(null, 'MyApp', true);
component.ngOnChanges({ appName: change });
@@ -471,7 +548,7 @@ describe('StartProcessCloudComponent', () => {
it('should call service to fetch process definitions with appId', async () => {
await fixture.whenStable();
- expect(getDefinitionsSpy).toHaveBeenCalledWith('MyApp');
+ expect(getProcessDefinitionsSpy).toHaveBeenCalledWith('MyApp');
});
it('should display the correct number of processes in the select list', async () => {
@@ -497,7 +574,7 @@ describe('StartProcessCloudComponent', () => {
});
it('should indicate an error to the user if process defs cannot be loaded', async () => {
- getDefinitionsSpy.and.returnValue(throwError({}));
+ getProcessDefinitionsSpy.and.returnValue(throwError({}));
const change = new SimpleChange('myApp', 'myApp1', true);
component.ngOnChanges({ appName: change });
@@ -509,7 +586,7 @@ describe('StartProcessCloudComponent', () => {
});
it('should show no process available message when no process definition is loaded', async () => {
- getDefinitionsSpy.and.returnValue(of([]));
+ getProcessDefinitionsSpy.and.returnValue(of([]));
const change = new SimpleChange('myApp', 'myApp1', true);
component.ngOnChanges({ appName: change });
@@ -522,7 +599,7 @@ describe('StartProcessCloudComponent', () => {
});
it('should select automatically the processDefinition if the app contain only one', async () => {
- getDefinitionsSpy.and.returnValue(of([fakeProcessDefinitions[0]]));
+ getProcessDefinitionsSpy.and.returnValue(of([fakeProcessDefinitions[0]]));
const change = new SimpleChange('myApp', 'myApp1', true);
component.ngOnChanges({ appName: change });
@@ -533,7 +610,7 @@ describe('StartProcessCloudComponent', () => {
});
it('should select automatically the form when processDefinition is selected as default', async () => {
- getDefinitionsSpy.and.returnValue(of([fakeProcessDefinitions[2]]));
+ getProcessDefinitionsSpy.and.returnValue(of([fakeProcessDefinitions[2]]));
formDefinitionSpy.and.returnValue(of(fakeStartForm));
const change = new SimpleChange('myApp', 'myApp1', true);
component.ngOnChanges({ appName: change });
@@ -553,7 +630,7 @@ describe('StartProcessCloudComponent', () => {
});
it('should not select automatically any processDefinition if the app contain multiple process and does not have any processDefinition as input', async () => {
- getDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions));
+ getProcessDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions));
component.appName = 'myApp';
component.ngOnChanges({});
@@ -564,7 +641,7 @@ describe('StartProcessCloudComponent', () => {
});
it('should select the right process when the processKey begins with the name', async () => {
- getDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions));
+ getProcessDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions));
formDefinitionSpy.and.returnValue(of(fakeStartForm));
component.name = 'My new process';
component.processDefinitionName = 'process';
@@ -579,7 +656,7 @@ describe('StartProcessCloudComponent', () => {
describe('dropdown', () => {
it('should hide the process dropdown button if showSelectProcessDropdown is false', async () => {
fixture.detectChanges();
- getDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions));
+ getProcessDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions));
component.appName = 'myApp';
component.showSelectProcessDropdown = false;
component.ngOnChanges({});
@@ -593,7 +670,7 @@ describe('StartProcessCloudComponent', () => {
it('should show the process dropdown button if showSelectProcessDropdown is false', async () => {
fixture.detectChanges();
- getDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions));
+ getProcessDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions));
component.appName = 'myApp';
component.processDefinitionName = 'NewProcess 2';
component.showSelectProcessDropdown = true;
@@ -608,7 +685,7 @@ describe('StartProcessCloudComponent', () => {
it('should show the process dropdown button by default', async () => {
fixture.detectChanges();
- getDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions));
+ getProcessDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions));
component.appName = 'myApp';
component.processDefinitionName = 'NewProcess 2';
component.ngOnChanges({});
@@ -631,18 +708,18 @@ describe('StartProcessCloudComponent', () => {
});
it('should reload processes when appName input changed', async () => {
- getDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions));
+ getProcessDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions));
component.ngOnChanges({ appName: firstChange });
component.ngOnChanges({ appName: change });
fixture.detectChanges();
await fixture.whenStable();
- expect(getDefinitionsSpy).toHaveBeenCalledWith('myApp1');
+ expect(getProcessDefinitionsSpy).toHaveBeenCalledWith('myApp1');
});
it('should reload processes ONLY when appName input changed', async () => {
- getDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions));
+ getProcessDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions));
component.ngOnChanges({ appName: firstChange });
fixture.detectChanges();
@@ -650,19 +727,19 @@ describe('StartProcessCloudComponent', () => {
fixture.detectChanges();
await fixture.whenStable();
- expect(getDefinitionsSpy).toHaveBeenCalledTimes(1);
+ expect(getProcessDefinitionsSpy).toHaveBeenCalledTimes(1);
});
it('should get current processDef', () => {
- getDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions));
+ getProcessDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions));
component.ngOnChanges({ appName: change });
fixture.detectChanges();
- expect(getDefinitionsSpy).toHaveBeenCalled();
+ expect(getProcessDefinitionsSpy).toHaveBeenCalled();
expect(component.processDefinitionList).toBe(fakeProcessDefinitions);
});
it('should display the matching results in the dropdown as the user types down', async () => {
- getDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions));
+ getProcessDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions));
component.ngOnChanges({ appName: change });
fixture.detectChanges();
@@ -680,7 +757,7 @@ describe('StartProcessCloudComponent', () => {
});
it('should display the process definion field as empty if are more than one process definition in the list', async () => {
- getDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions));
+ getProcessDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions));
component.ngOnChanges({ appName: change });
fixture.detectChanges();
@@ -696,7 +773,7 @@ describe('StartProcessCloudComponent', () => {
fixture.detectChanges();
component.name = 'NewProcess 1';
component.appName = 'myApp';
- getDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions));
+ getProcessDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions));
fixture.detectChanges();
});
@@ -836,7 +913,7 @@ describe('StartProcessCloudComponent', () => {
});
it('should call service with the correct parameters when variables are undefined and formCloud is defined', async () => {
- getDefinitionsSpy.and.returnValue(of([fakeProcessDefinitions[2]]));
+ getProcessDefinitionsSpy.and.returnValue(of([fakeProcessDefinitions[2]]));
formDefinitionSpy.and.returnValue(of(fakeStartForm));
const change = new SimpleChange('myApp', 'myApp1', true);
component.ngOnChanges({ appName: change });
@@ -894,7 +971,7 @@ describe('StartProcessCloudComponent', () => {
});
it('should indicate an error to the user if process cannot be started', async () => {
- getDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions));
+ getProcessDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions));
const change = new SimpleChange('myApp', 'myApp1', true);
component.ngOnChanges({ appName: change });
const error = {
@@ -915,7 +992,7 @@ describe('StartProcessCloudComponent', () => {
});
it('should use fallback error message when no specific error message is provided', async () => {
- getDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions));
+ getProcessDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions));
const change = new SimpleChange('myApp', 'myApp1', true);
component.ngOnChanges({ appName: change });
const errorWithoutMessage = {
@@ -1040,7 +1117,7 @@ describe('StartProcessCloudComponent', () => {
fixture.detectChanges();
let noProcessElement = fixture.nativeElement.querySelector('#no-process-message');
expect(noProcessElement).toBeNull();
- getDefinitionsSpy.and.returnValue(of([]));
+ getProcessDefinitionsSpy.and.returnValue(of([]));
component.loadProcessDefinitions();
fixture.detectChanges();
@@ -1051,7 +1128,7 @@ describe('StartProcessCloudComponent', () => {
it('should set the process name using the processName cloud pipe when a process definition gets selected', async () => {
const getDefaultProcessNameSpy = spyOn(component, 'getDefaultProcessName').and.returnValue('fake-transformed-name');
const expectedProcessInstanceDetails: ProcessInstanceCloud = { processDefinitionName: fakeProcessDefinitions[0].name };
- getDefinitionsSpy.and.returnValue(of([fakeProcessDefinitions[0]]));
+ getProcessDefinitionsSpy.and.returnValue(of([fakeProcessDefinitions[0]]));
formDefinitionSpy.and.stub();
component.appName = 'myApp';
@@ -1085,7 +1162,7 @@ describe('StartProcessCloudComponent', () => {
done();
});
- getDefinitionsSpy.and.returnValue(of(definitions));
+ getProcessDefinitionsSpy.and.returnValue(of(definitions));
const fakeTransformedName = 'fake-transformed-name';
spyOn(component, 'getDefaultProcessName').and.returnValue(fakeTransformedName);
@@ -1166,7 +1243,7 @@ describe('StartProcessCloudComponent', () => {
beforeEach(() => {
component.name = 'NewProcess 1';
component.appName = 'myApp';
- getDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions));
+ getProcessDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions));
component.ngOnChanges({ appName: firstChange });
component.processDefinitionList = fakeProcessDefinitions;
component.processDefinitionName = fakeProcessDefinitions[0].name;
@@ -1212,7 +1289,7 @@ describe('StartProcessCloudComponent', () => {
beforeEach(() => {
component.name = 'NewProcess 1';
component.appName = 'myApp';
- getDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions));
+ getProcessDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions));
component.ngOnChanges({ appName: firstChange });
fixture.detectChanges();
});
@@ -1235,7 +1312,7 @@ describe('StartProcessCloudComponent', () => {
const customOutcomeSelectedSpy = spyOn(component.customOutcomeSelected, 'emit');
const successSpy = spyOn(component.success, 'emit');
- getDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions));
+ getProcessDefinitionsSpy.and.returnValue(of(fakeProcessDefinitions));
formDefinitionSpy.and.returnValue(of(fakeFormModelJson));
startProcessWithFormSpy.and.returnValue(of(fakeProcessInstance));
diff --git a/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.ts b/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.ts
index b739478687..61bfaaed6a 100755
--- a/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.ts
+++ b/lib/process-services-cloud/src/lib/process/start-process/components/start-process-cloud.component.ts
@@ -63,6 +63,8 @@ import { MatOptionModule } from '@angular/material/core';
import { FormCloudComponent } from '../../../form/components/form-cloud.component';
import { FormCustomOutcomesComponent } from '../../../form/components/form-cloud-custom-outcomes.component';
import { MatDialog } from '@angular/material/dialog';
+import { StartProcessScreenCloudComponent } from '../../../screen/components/screen-cloud/start-process-event-screen/start-process-screen-cloud.component';
+import { TaskTypeResolverService } from '../../../services/task-type-resolver/task-type-resolver.service';
const MAX_NAME_LENGTH: number = 255;
const PROCESS_DEFINITION_DEBOUNCE: number = 300;
@@ -85,7 +87,8 @@ const PROCESS_DEFINITION_IDENTIFIER_REG_EXP = /%{processdefinition}/i;
MatAutocompleteModule,
ReactiveFormsModule,
FormCloudComponent,
- FormCustomOutcomesComponent
+ FormCustomOutcomesComponent,
+ StartProcessScreenCloudComponent
],
providers: [LocalizedDatePipe],
templateUrl: './start-process-cloud.component.html',
@@ -177,6 +180,7 @@ export class StartProcessCloudComponent implements OnChanges, OnInit {
isFormCloudLoading = true;
processDefinitionLoaded = false;
+ disableStartProcessForScreen = false;
showStartProcessButton$: Observable;
startProcessButtonLabel: string;
cancelButtonLabel: string;
@@ -197,6 +201,9 @@ export class StartProcessCloudComponent implements OnChanges, OnInit {
private readonly displayStartSubject = new BehaviorSubject(null);
private readonly hasVisibleOutcomesSubject = new BehaviorSubject(false);
private readonly dialog = inject(MatDialog);
+ private readonly taskTypeResolverService = inject(TaskTypeResolverService);
+
+ private screenSubmitPayload: unknown;
showSaveButton = false;
showCompleteButton = false;
@@ -204,13 +211,17 @@ export class StartProcessCloudComponent implements OnChanges, OnInit {
get isProcessFormValid(): boolean {
if (this.hasForm && this.isFormCloudLoaded) {
return (this.formCloud ? !Object.keys(this.formCloud.values).length : false) || this.formCloud?.isValid || this.isProcessStarting;
+ } else if (this.hasScreen) {
+ return true;
} else {
return this.processForm.valid || this.isProcessStarting;
}
}
get disableStartButton(): boolean {
- return !this.appName || !this.processDefinition.valid || this.isProcessStarting || this.isFormCloudLoading;
+ return (
+ !this.appName || !this.processDefinition.valid || this.isProcessStarting || this.isFormCloudLoading || this.disableStartProcessForScreen
+ );
}
get isProcessDefinitionsEmpty(): boolean {
@@ -226,7 +237,15 @@ export class StartProcessCloudComponent implements OnChanges, OnInit {
}
get hasForm(): boolean {
- return !!this.processDefinitionCurrent?.formKey;
+ return this.taskTypeResolverService.isFormTask(this.processDefinitionCurrent?.formKey);
+ }
+
+ get hasScreen(): boolean {
+ return this.taskTypeResolverService.isScreenTask(this.processDefinitionCurrent?.formKey);
+ }
+
+ get screenId(): string {
+ return this.taskTypeResolverService.getScreenId(this.processDefinitionCurrent?.formKey);
}
get defaultStartProcessButtonLabel(): string {
@@ -294,6 +313,14 @@ export class StartProcessCloudComponent implements OnChanges, OnInit {
this.hasVisibleOutcomesSubject.next(anyOutcomeVisible);
}
+ onDisableStartProcessButtonForScreen(disable: boolean): void {
+ this.disableStartProcessForScreen = disable;
+ }
+
+ onScreenStartProcessPayloadChange(payload: unknown): void {
+ this.screenSubmitPayload = payload;
+ }
+
private getMaxNameLength(): number {
return this.maxNameLength > MAX_NAME_LENGTH ? MAX_NAME_LENGTH : this.maxNameLength;
}
@@ -301,6 +328,7 @@ export class StartProcessCloudComponent implements OnChanges, OnInit {
private selectProcessDefinitionByProcessDefinitionName(processDefinitionName: string): void {
this.filteredProcesses = this.getProcessDefinitionListByNameOrKey(processDefinitionName);
this.isFormCloudLoading = this.isProcessFormValid && this.filteredProcesses && this.filteredProcesses.length === 1;
+
if (this.isFormCloudLoading) {
this.setProcessDefinitionOnForm(this.filteredProcesses[0].name);
}
@@ -447,27 +475,31 @@ export class StartProcessCloudComponent implements OnChanges, OnInit {
startProcessWithoutConfirmation() {
this.isProcessStarting = true;
- const action = this.hasForm
- ? this.startProcessCloudService.startProcessWithForm(
- this.appName,
- this.processDefinitionCurrent.formKey,
- this.processDefinitionCurrent.version,
- new ProcessWithFormPayloadCloud({
- processName: this.processInstanceName.value,
- processDefinitionKey: this.processPayloadCloud.processDefinitionKey,
- variables: this.variables ?? {},
- values: this.formCloud.values,
- outcome: this.customOutcomeName
- })
- )
- : this.startProcessCloudService.startProcess(
- this.appName,
- new ProcessPayloadCloud({
- name: this.processInstanceName.value,
- processDefinitionKey: this.processPayloadCloud.processDefinitionKey,
- variables: this.variables ?? {}
- })
- );
+ let action: Observable;
+
+ if (this.hasForm || this.hasScreen) {
+ action = this.startProcessCloudService.startProcessWithForm(
+ this.appName,
+ this.processDefinitionCurrent.formKey,
+ this.processDefinitionCurrent.version,
+ new ProcessWithFormPayloadCloud({
+ processName: this.processInstanceName.value,
+ processDefinitionKey: this.processPayloadCloud.processDefinitionKey,
+ variables: this.variables ?? {},
+ values: this.hasForm ? this.formCloud.values : this.screenSubmitPayload,
+ outcome: this.customOutcomeName
+ })
+ );
+ } else {
+ action = this.startProcessCloudService.startProcess(
+ this.appName,
+ new ProcessPayloadCloud({
+ name: this.processInstanceName.value,
+ processDefinitionKey: this.processPayloadCloud.processDefinitionKey,
+ variables: this.variables ?? {}
+ })
+ );
+ }
action.subscribe({
next: (res) => {
diff --git a/lib/process-services-cloud/src/lib/process/start-process/mock/start-process.component.mock.ts b/lib/process-services-cloud/src/lib/process/start-process/mock/start-process.component.mock.ts
index cbce42f06d..bf47b00134 100755
--- a/lib/process-services-cloud/src/lib/process/start-process/mock/start-process.component.mock.ts
+++ b/lib/process-services-cloud/src/lib/process/start-process/mock/start-process.component.mock.ts
@@ -81,13 +81,14 @@ export const fakeProcessDefinitions: ProcessDefinitionCloud[] = [
})
];
-export const fakeSingleProcessDefinition = (name: string): ProcessDefinitionCloud[] => [
+export const fakeSingleProcessDefinition = (name: string, processDefinitionData?: Partial): ProcessDefinitionCloud[] => [
new ProcessDefinitionCloud({
appName: 'startformwithoutupload',
formKey: 'form-a5d50817-5183-4850-802d-17af54b2632f',
id: 'd00c0237-8772-11e9-859a-428f83d5904f',
key: 'process-5151ad1d-f992-4ee6-9742-3a04617469fe',
- name
+ name,
+ ...(processDefinitionData ?? {})
})
];
diff --git a/lib/process-services-cloud/src/lib/screen/components/screen-cloud/base-screen/base-screen-cloud.component.ts b/lib/process-services-cloud/src/lib/screen/components/screen-cloud/base-screen/base-screen-cloud.component.ts
new file mode 100644
index 0000000000..a8ec356011
--- /dev/null
+++ b/lib/process-services-cloud/src/lib/screen/components/screen-cloud/base-screen/base-screen-cloud.component.ts
@@ -0,0 +1,50 @@
+/*!
+ * @license
+ * Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { Component, ComponentRef, inject, Input, OnInit, ViewChild, ViewContainerRef } from '@angular/core';
+import { ScreenRenderingService } from '../../../services/screen-rendering.service';
+
+@Component({
+ template: ''
+})
+export abstract class BaseScreenCloudComponent implements OnInit {
+ @Input()
+ screenId: string = '';
+
+ @ViewChild('container', { read: ViewContainerRef, static: true })
+ container: ViewContainerRef;
+
+ protected componentRef: ComponentRef;
+ protected readonly screenRenderingService = inject(ScreenRenderingService);
+
+ ngOnInit() {
+ this.createDynamicComponent();
+ }
+
+ private createDynamicComponent(): void {
+ if (this.screenId) {
+ const componentType = this.screenRenderingService.resolveComponentType({ type: this.screenId });
+ this.componentRef = this.container.createComponent(componentType);
+ this.setInputsForDynamicComponent();
+ this.subscribeToOutputs();
+ }
+ }
+
+ protected abstract setInputsForDynamicComponent(): void;
+
+ protected abstract subscribeToOutputs(): void;
+}
diff --git a/lib/process-services-cloud/src/lib/screen/components/screen-cloud/start-process-event-screen/start-process-screen-cloud.component.html b/lib/process-services-cloud/src/lib/screen/components/screen-cloud/start-process-event-screen/start-process-screen-cloud.component.html
new file mode 100644
index 0000000000..21f5a25370
--- /dev/null
+++ b/lib/process-services-cloud/src/lib/screen/components/screen-cloud/start-process-event-screen/start-process-screen-cloud.component.html
@@ -0,0 +1,9 @@
+
+
+
+
+
+ @if(showStartProcessButtons()) {
+
+ }
+
diff --git a/lib/process-services-cloud/src/lib/screen/components/screen-cloud/start-process-event-screen/start-process-screen-cloud.component.scss b/lib/process-services-cloud/src/lib/screen/components/screen-cloud/start-process-event-screen/start-process-screen-cloud.component.scss
new file mode 100644
index 0000000000..5a60318c78
--- /dev/null
+++ b/lib/process-services-cloud/src/lib/screen/components/screen-cloud/start-process-event-screen/start-process-screen-cloud.component.scss
@@ -0,0 +1,8 @@
+.adf-cloud-start-process-screen-cloud {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ z-index: 100;
+}
diff --git a/lib/process-services-cloud/src/lib/screen/components/screen-cloud/start-process-event-screen/start-process-screen-cloud.component.spec.ts b/lib/process-services-cloud/src/lib/screen/components/screen-cloud/start-process-event-screen/start-process-screen-cloud.component.spec.ts
new file mode 100644
index 0000000000..595a7d4bef
--- /dev/null
+++ b/lib/process-services-cloud/src/lib/screen/components/screen-cloud/start-process-event-screen/start-process-screen-cloud.component.spec.ts
@@ -0,0 +1,70 @@
+/*!
+ * @license
+ * Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { StartProcessScreenCloudComponent } from './start-process-screen-cloud.component';
+import { MockedTaskScreenCloudComponent } from '../../../../testing/start-process-screen-mock.component';
+import { provideScreen } from '../../../services/provide-screen';
+import { By } from '@angular/platform-browser';
+import { StartProcessScreenCloud, StartProcessScreenDefaultButtons } from './start-process-screen.model';
+
+describe('StartProcessScreenCloudComponent', () => {
+ let component: StartProcessScreenCloudComponent;
+ let fixture: ComponentFixture;
+ const screenId = 'screen-1234-5678-121212-123456';
+ const processDefinitionId = 'definition-id';
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [StartProcessScreenCloudComponent],
+ providers: [provideScreen(screenId, MockedTaskScreenCloudComponent)]
+ }).compileComponents();
+
+ fixture = TestBed.createComponent(StartProcessScreenCloudComponent);
+ component = fixture.componentInstance;
+ fixture.componentRef.setInput('processDefinitionId', processDefinitionId);
+ fixture.componentRef.setInput('screenId', screenId);
+
+ fixture.detectChanges();
+ });
+
+ it('should create provided screen component', () => {
+ const screenDebugComponent = fixture.debugElement.query(By.directive(MockedTaskScreenCloudComponent));
+ expect(screenDebugComponent).toBeTruthy();
+ });
+
+ it('should subscribe to outputs', () => {
+ const screenStartProcessPayloadChangeSpy = spyOn(component.screenStartProcessPayloadChange, 'emit');
+ const disableStartProcessButtonSpy = spyOn(component.disableStartProcessButton, 'emit');
+
+ const screenInstance: StartProcessScreenCloud = fixture.debugElement.query(By.directive(MockedTaskScreenCloudComponent)).componentInstance;
+
+ const startProcessScreenDefaultButtons: StartProcessScreenDefaultButtons = { show: true, disable: false };
+ const startProcessPayload = { payload: 'TEST' };
+ screenInstance.defaultStartProcessButtonsConfigurationChange.emit(startProcessScreenDefaultButtons);
+ screenInstance.startProcessPayloadChanged.emit(startProcessPayload);
+
+ expect(screenStartProcessPayloadChangeSpy).toHaveBeenCalledWith(startProcessPayload);
+ expect(disableStartProcessButtonSpy).toHaveBeenCalledWith(startProcessScreenDefaultButtons.disable);
+ expect(component.showStartProcessButtons()).toBe(startProcessScreenDefaultButtons.show);
+ });
+
+ it('should set process definition id', () => {
+ const screenInstance: StartProcessScreenCloud = fixture.debugElement.query(By.directive(MockedTaskScreenCloudComponent)).componentInstance;
+ expect(screenInstance.processDefinitionId()).toBe(processDefinitionId);
+ });
+});
diff --git a/lib/process-services-cloud/src/lib/screen/components/screen-cloud/start-process-event-screen/start-process-screen-cloud.component.ts b/lib/process-services-cloud/src/lib/screen/components/screen-cloud/start-process-event-screen/start-process-screen-cloud.component.ts
new file mode 100644
index 0000000000..d066c2ee49
--- /dev/null
+++ b/lib/process-services-cloud/src/lib/screen/components/screen-cloud/start-process-event-screen/start-process-screen-cloud.component.ts
@@ -0,0 +1,52 @@
+/*!
+ * @license
+ * Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { ChangeDetectionStrategy, Component, input, output, signal } from '@angular/core';
+import { BaseScreenCloudComponent } from '../base-screen/base-screen-cloud.component';
+import { MatCardModule } from '@angular/material/card';
+import { CommonModule } from '@angular/common';
+import { StartProcessScreenCloud } from './start-process-screen.model';
+
+@Component({
+ selector: 'adf-cloud-start-process-screen-cloud',
+ imports: [CommonModule, MatCardModule],
+ templateUrl: './start-process-screen-cloud.component.html',
+ host: { class: 'adf-cloud-start-process-screen-cloud' },
+ styleUrls: ['./start-process-screen-cloud.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class StartProcessScreenCloudComponent extends BaseScreenCloudComponent {
+ processDefinitionId = input('');
+ screenStartProcessPayloadChange = output();
+ disableStartProcessButton = output();
+
+ showStartProcessButtons = signal(false);
+
+ protected setInputsForDynamicComponent(): void {
+ if (this.processDefinitionId()) {
+ this.componentRef.setInput('processDefinitionId', this.processDefinitionId());
+ }
+ }
+
+ protected subscribeToOutputs(): void {
+ this.componentRef.instance.startProcessPayloadChanged.subscribe((payload) => this.screenStartProcessPayloadChange.emit(payload));
+ this.componentRef.instance.defaultStartProcessButtonsConfigurationChange.subscribe((config) => {
+ this.showStartProcessButtons.set(config.show);
+ this.disableStartProcessButton.emit(config.disable);
+ });
+ }
+}
diff --git a/lib/process-services-cloud/src/lib/screen/components/screen-cloud/start-process-event-screen/start-process-screen.model.ts b/lib/process-services-cloud/src/lib/screen/components/screen-cloud/start-process-event-screen/start-process-screen.model.ts
new file mode 100644
index 0000000000..51a9964d04
--- /dev/null
+++ b/lib/process-services-cloud/src/lib/screen/components/screen-cloud/start-process-event-screen/start-process-screen.model.ts
@@ -0,0 +1,28 @@
+/*!
+ * @license
+ * Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { InputSignal, OutputEmitterRef } from '@angular/core';
+
+export interface StartProcessScreenDefaultButtons {
+ show: boolean;
+ disable: boolean;
+}
+export interface StartProcessScreenCloud {
+ processDefinitionId: InputSignal;
+ defaultStartProcessButtonsConfigurationChange: OutputEmitterRef;
+ startProcessPayloadChanged: OutputEmitterRef;
+}
diff --git a/lib/process-services-cloud/src/lib/screen/components/screen-cloud/screen-cloud.component.html b/lib/process-services-cloud/src/lib/screen/components/screen-cloud/user-task-screen/screen-cloud.component.html
similarity index 100%
rename from lib/process-services-cloud/src/lib/screen/components/screen-cloud/screen-cloud.component.html
rename to lib/process-services-cloud/src/lib/screen/components/screen-cloud/user-task-screen/screen-cloud.component.html
diff --git a/lib/process-services-cloud/src/lib/screen/components/screen-cloud/screen-cloud.component.spec.ts b/lib/process-services-cloud/src/lib/screen/components/screen-cloud/user-task-screen/screen-cloud.component.spec.ts
similarity index 98%
rename from lib/process-services-cloud/src/lib/screen/components/screen-cloud/screen-cloud.component.spec.ts
rename to lib/process-services-cloud/src/lib/screen/components/screen-cloud/user-task-screen/screen-cloud.component.spec.ts
index d55281891b..a78eea608b 100644
--- a/lib/process-services-cloud/src/lib/screen/components/screen-cloud/screen-cloud.component.spec.ts
+++ b/lib/process-services-cloud/src/lib/screen/components/screen-cloud/user-task-screen/screen-cloud.component.spec.ts
@@ -18,7 +18,7 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { Component, EventEmitter, Input, Output, ViewChild } from '@angular/core';
import { By } from '@angular/platform-browser';
-import { ScreenRenderingService } from '../../services/screen-rendering.service';
+import { ScreenRenderingService } from '../../../services/screen-rendering.service';
import { TaskScreenCloudComponent } from './screen-cloud.component';
@Component({
diff --git a/lib/process-services-cloud/src/lib/screen/components/screen-cloud/screen-cloud.component.ts b/lib/process-services-cloud/src/lib/screen/components/screen-cloud/user-task-screen/screen-cloud.component.ts
similarity index 85%
rename from lib/process-services-cloud/src/lib/screen/components/screen-cloud/screen-cloud.component.ts
rename to lib/process-services-cloud/src/lib/screen/components/screen-cloud/user-task-screen/screen-cloud.component.ts
index eca14fcec7..5542954777 100644
--- a/lib/process-services-cloud/src/lib/screen/components/screen-cloud/screen-cloud.component.ts
+++ b/lib/process-services-cloud/src/lib/screen/components/screen-cloud/user-task-screen/screen-cloud.component.ts
@@ -16,19 +16,19 @@
*/
import { CommonModule } from '@angular/common';
-import { Component, ComponentRef, DestroyRef, EventEmitter, inject, Input, OnInit, Output, ViewChild, ViewContainerRef } from '@angular/core';
-import { ScreenRenderingService } from '../../services/screen-rendering.service';
+import { Component, DestroyRef, EventEmitter, inject, Input, Output } from '@angular/core';
import { MatCardModule } from '@angular/material/card';
import { UserTaskCustomUi } from './screen-cloud.model';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { MatCheckboxChange } from '@angular/material/checkbox';
+import { BaseScreenCloudComponent } from '../base-screen/base-screen-cloud.component';
@Component({
selector: 'adf-cloud-task-screen',
imports: [CommonModule, MatCardModule],
templateUrl: './screen-cloud.component.html'
})
-export class TaskScreenCloudComponent implements OnInit {
+export class TaskScreenCloudComponent extends BaseScreenCloudComponent {
/** Task id to fetch corresponding form and values. */
@Input()
taskId: string;
@@ -48,10 +48,6 @@ export class TaskScreenCloudComponent implements OnInit {
@Input()
showCancelButton: boolean;
- /** Screen id to create dynamic component. */
- @Input()
- screenId: string = '';
-
/** Process Instance Id to fetch corresponding data. */
@Input()
processInstanceId: string = '';
@@ -104,27 +100,9 @@ export class TaskScreenCloudComponent implements OnInit {
@Output()
nextTaskCheckboxCheckedChanged = new EventEmitter();
- @ViewChild('container', { read: ViewContainerRef, static: true })
- container: ViewContainerRef;
+ private readonly destroyRef = inject(DestroyRef);
- private destroyRef = inject(DestroyRef);
- componentRef: ComponentRef;
- private readonly screenRenderingService = inject(ScreenRenderingService);
-
- ngOnInit() {
- this.createDynamicComponent();
- }
-
- createDynamicComponent() {
- if (this.screenId) {
- const componentType = this.screenRenderingService.resolveComponentType({ type: this.screenId });
- this.componentRef = this.container.createComponent(componentType);
- this.setInputsForDynamicComponent();
- this.subscribeToOutputs();
- }
- }
-
- setInputsForDynamicComponent(): void {
+ protected override setInputsForDynamicComponent(): void {
if (this.taskId && Object.prototype.hasOwnProperty.call(this.componentRef.instance, 'taskId')) {
this.componentRef.setInput('taskId', this.taskId);
}
@@ -160,7 +138,7 @@ export class TaskScreenCloudComponent implements OnInit {
}
}
- subscribeToOutputs() {
+ protected override subscribeToOutputs(): void {
if (this.componentRef.instance?.taskSaved) {
this.componentRef.instance.taskSaved.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => this.taskSaved.emit());
}
@@ -172,7 +150,6 @@ export class TaskScreenCloudComponent implements OnInit {
if (this.componentRef.instance?.error) {
this.componentRef.instance.error.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((data) => this.error.emit(data));
}
-
if (this.componentRef.instance?.claimTask) {
this.componentRef.instance.claimTask.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((data) => this.claimTask.emit(data));
}
@@ -189,7 +166,7 @@ export class TaskScreenCloudComponent implements OnInit {
}
}
- switchToDisplayMode(newDisplayMode?: string) {
+ switchToDisplayMode(newDisplayMode?: string): void {
if (this.componentRef?.instance?.switchToDisplayMode) {
this.componentRef.instance.switchToDisplayMode(newDisplayMode);
}
diff --git a/lib/process-services-cloud/src/lib/screen/components/screen-cloud/screen-cloud.model.ts b/lib/process-services-cloud/src/lib/screen/components/screen-cloud/user-task-screen/screen-cloud.model.ts
similarity index 100%
rename from lib/process-services-cloud/src/lib/screen/components/screen-cloud/screen-cloud.model.ts
rename to lib/process-services-cloud/src/lib/screen/components/screen-cloud/user-task-screen/screen-cloud.model.ts
diff --git a/lib/process-services-cloud/src/lib/screen/public-api.ts b/lib/process-services-cloud/src/lib/screen/public-api.ts
index c8decd7901..2068895975 100644
--- a/lib/process-services-cloud/src/lib/screen/public-api.ts
+++ b/lib/process-services-cloud/src/lib/screen/public-api.ts
@@ -15,6 +15,7 @@
* limitations under the License.
*/
-export * from './components/screen-cloud/screen-cloud.model';
+export * from './components/screen-cloud/user-task-screen/screen-cloud.model';
+export * from './components/screen-cloud/start-process-event-screen/start-process-screen-cloud.component';
export * from './services/screen-rendering.service';
export * from './services/provide-screen';
diff --git a/lib/process-services-cloud/src/lib/services/task-type-resolver/task-type-resolver.service.spec.ts b/lib/process-services-cloud/src/lib/services/task-type-resolver/task-type-resolver.service.spec.ts
new file mode 100644
index 0000000000..385c760db7
--- /dev/null
+++ b/lib/process-services-cloud/src/lib/services/task-type-resolver/task-type-resolver.service.spec.ts
@@ -0,0 +1,63 @@
+/*!
+ * @license
+ * Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { TestBed } from '@angular/core/testing';
+import { TaskTypeResolverService, UserTaskContentType } from './task-type-resolver.service';
+
+describe('TaskTypeResolverService', () => {
+ let service: TaskTypeResolverService;
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({});
+ service = TestBed.inject(TaskTypeResolverService);
+ });
+
+ it('should return proper user task type', () => {
+ expect(service.getUserTaskType('form-1234')).toBe(UserTaskContentType.Form);
+ expect(service.getUserTaskType('screen-1234')).toBe(UserTaskContentType.Screen);
+
+ expect(service.getUserTaskType('other-1234')).toBe(UserTaskContentType.None);
+ expect(service.getUserTaskType('')).toBe(UserTaskContentType.None);
+ expect(service.getUserTaskType()).toBe(UserTaskContentType.None);
+ });
+
+ it('should detect form task', () => {
+ expect(service.isFormTask('form-1234')).toBeTrue();
+
+ expect(service.isFormTask('screen-1234')).toBeFalse();
+ expect(service.isFormTask('other-1234')).toBeFalse();
+ expect(service.isFormTask('')).toBeFalse();
+ expect(service.isFormTask()).toBeFalse();
+ });
+
+ it('should detect screen task', () => {
+ expect(service.isScreenTask('screen-1234')).toBeTrue();
+
+ expect(service.isScreenTask('form-1234')).toBeFalse();
+ expect(service.isScreenTask('other-1234')).toBeFalse();
+ expect(service.isScreenTask('')).toBeFalse();
+ expect(service.isScreenTask()).toBeFalse();
+ });
+
+ it('should extract screen id from form key', () => {
+ expect(service.getScreenId('screen-1234-5678-9121')).toBe('1234-5678-9121');
+ expect(service.getScreenId('form-1234')).toBe('');
+ expect(service.getScreenId('other-1234')).toBe('');
+ expect(service.getScreenId('')).toBe('');
+ expect(service.getScreenId()).toBe('');
+ });
+});
diff --git a/lib/process-services-cloud/src/lib/services/task-type-resolver/task-type-resolver.service.ts b/lib/process-services-cloud/src/lib/services/task-type-resolver/task-type-resolver.service.ts
new file mode 100644
index 0000000000..232f71c410
--- /dev/null
+++ b/lib/process-services-cloud/src/lib/services/task-type-resolver/task-type-resolver.service.ts
@@ -0,0 +1,61 @@
+/*!
+ * @license
+ * Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { Injectable } from '@angular/core';
+
+export const UserTaskContentType = {
+ Form: 'form',
+ Screen: 'screen',
+ None: ''
+} as const;
+
+export type UserTaskType = (typeof UserTaskContentType)[keyof typeof UserTaskContentType];
+
+@Injectable({
+ providedIn: 'root'
+})
+export class TaskTypeResolverService {
+ isScreenTask(formKey?: string): boolean {
+ return this.getUserTaskType(formKey) === UserTaskContentType.Screen;
+ }
+
+ isFormTask(formKey?: string): boolean {
+ return this.getUserTaskType(formKey) === UserTaskContentType.Form;
+ }
+
+ getScreenId(formKey?: string): string {
+ const taskType = this.getUserTaskType(formKey);
+
+ if (taskType === UserTaskContentType.Screen) {
+ return formKey.replace(`${taskType}-`, '');
+ }
+
+ return '';
+ }
+
+ getUserTaskType(formKey?: string): UserTaskType {
+ if (formKey?.includes(UserTaskContentType.Form)) {
+ return UserTaskContentType.Form;
+ }
+
+ if (formKey?.includes(UserTaskContentType.Screen)) {
+ return UserTaskContentType.Screen;
+ }
+
+ return UserTaskContentType.None;
+ }
+}
diff --git a/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/user-task-cloud.component.spec.ts b/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/user-task-cloud.component.spec.ts
index 864e99364c..988ad2e29b 100644
--- a/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/user-task-cloud.component.spec.ts
+++ b/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/user-task-cloud.component.spec.ts
@@ -26,7 +26,7 @@ import { of, throwError } from 'rxjs';
import { IdentityUserService } from '../../../../people/services/identity-user.service';
import { UserTaskCloudComponent } from './user-task-cloud.component';
import { By } from '@angular/platform-browser';
-import { TaskScreenCloudComponent } from '../../../../screen/components/screen-cloud/screen-cloud.component';
+import { TaskScreenCloudComponent } from '../../../../screen/components/screen-cloud/user-task-screen/screen-cloud.component';
import { MatCheckboxChange } from '@angular/material/checkbox';
import { NoopAuthModule, NoopTranslateModule } from '@alfresco/adf-core';
import {
@@ -42,7 +42,7 @@ import { TaskCloudService } from '../../../services/task-cloud.service';
import { FormModel } from '../../../../../../../core';
import { UserTaskCloudButtonsComponent } from '../user-task-cloud-buttons/user-task-cloud-buttons.component';
-const taskDetails: TaskDetailsCloudModel = {
+const createTaskDetailsCloudModel = (): TaskDetailsCloudModel => ({
appName: 'simple-app',
assignee: 'admin.adf',
completedDate: null,
@@ -55,7 +55,7 @@ const taskDetails: TaskDetailsCloudModel = {
standalone: false,
status: TASK_ASSIGNED_STATE,
permissions: [TASK_VIEW_PERMISSION]
-};
+});
@Component({
selector: 'adf-cloud-task-screen',
@@ -94,8 +94,11 @@ describe('UserTaskCloudComponent', () => {
let loader: HarnessLoader;
let identityUserService: IdentityUserService;
let errorEmitSpy: jasmine.Spy;
+ let taskDetails: TaskDetailsCloudModel;
beforeEach(() => {
+ taskDetails = createTaskDetailsCloudModel();
+
TestBed.configureTestingModule({
imports: [NoopTranslateModule, NoopAuthModule, UserTaskCloudComponent, TaskFormCloudComponent]
}).overrideComponent(UserTaskCloudComponent, {
diff --git a/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/user-task-cloud.component.ts b/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/user-task-cloud.component.ts
index 32167fec94..602f8c2cdb 100644
--- a/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/user-task-cloud.component.ts
+++ b/lib/process-services-cloud/src/lib/task/task-form/components/user-task-cloud/user-task-cloud.component.ts
@@ -28,18 +28,11 @@ import { UserTaskCloudButtonsComponent } from '../user-task-cloud-buttons/user-t
import { TranslatePipe } from '@ngx-translate/core';
import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card';
-import { TaskScreenCloudComponent } from '../../../../screen/components/screen-cloud/screen-cloud.component';
+import { TaskScreenCloudComponent } from '../../../../screen/components/screen-cloud/user-task-screen/screen-cloud.component';
import { CompleteTaskDirective } from './complete-task/complete-task.directive';
import { catchError, EMPTY, forkJoin } from 'rxjs';
import { MatCheckboxChange, MatCheckboxModule } from '@angular/material/checkbox';
-
-const TaskTypes = {
- Form: 'form',
- Screen: 'screen',
- None: ''
-} as const;
-
-type TaskTypesType = (typeof TaskTypes)[keyof typeof TaskTypes];
+import { UserTaskContentType, TaskTypeResolverService, UserTaskType } from '../../../../services/task-type-resolver/task-type-resolver.service';
@Component({
selector: 'adf-cloud-user-task',
@@ -183,12 +176,13 @@ export class UserTaskCloudComponent implements OnInit, OnChanges {
candidateUsers: string[] = [];
candidateGroups: string[] = [];
loading: boolean = false;
- screenId: string;
taskDetails: TaskDetailsCloudModel;
- taskType: TaskTypesType;
- taskTypeEnum = TaskTypes;
+ taskType: UserTaskType;
+ taskTypeEnum = UserTaskContentType;
+ screenId: string;
private taskCloudService: TaskCloudService = inject(TaskCloudService);
+ private readonly taskTypeResolverService = inject(TaskTypeResolverService);
private readonly destroyRef = inject(DestroyRef);
ngOnChanges(changes: SimpleChanges) {
@@ -224,19 +218,8 @@ export class UserTaskCloudComponent implements OnInit, OnChanges {
}
getTaskType(): void {
- if (this.taskDetails && !!this.taskDetails.formKey) {
- if (this.taskDetails.formKey.includes(this.taskTypeEnum.Form)) {
- this.taskType = this.taskTypeEnum.Form;
- return;
- } else if (this.taskDetails.formKey.includes(this.taskTypeEnum.Screen)) {
- this.taskType = this.taskTypeEnum.Screen;
- const screenId = this.taskDetails.formKey.replace(this.taskTypeEnum.Screen + '-', '');
- this.screenId = screenId;
- return;
- }
- }
-
- this.taskType = this.taskTypeEnum.None;
+ this.taskType = this.taskTypeResolverService.getUserTaskType(this.taskDetails?.formKey);
+ this.screenId = this.taskTypeResolverService.getScreenId(this.taskDetails?.formKey);
}
hasCandidateUsers(): boolean {
@@ -284,6 +267,7 @@ export class UserTaskCloudComponent implements OnInit, OnChanges {
onExecuteOutcome(outcome: FormOutcomeEvent): void {
this.executeOutcome.emit(outcome);
}
+
onFormContentClicked(content: ContentLinkModel): void {
this.formContentClicked.emit(content);
}
@@ -309,6 +293,7 @@ export class UserTaskCloudComponent implements OnInit, OnChanges {
const tasks$ = this.taskCloudService.getTaskById(this.appName, this.taskId);
const candidateUsers$ = this.taskCloudService.getCandidateUsers(this.appName, this.taskId);
const candidateGroups$ = this.taskCloudService.getCandidateGroups(this.appName, this.taskId);
+
forkJoin({
tasks: tasks$,
candidateUsers: candidateUsers$,
diff --git a/lib/process-services-cloud/src/lib/testing/start-process-screen-mock.component.ts b/lib/process-services-cloud/src/lib/testing/start-process-screen-mock.component.ts
new file mode 100644
index 0000000000..92138774fa
--- /dev/null
+++ b/lib/process-services-cloud/src/lib/testing/start-process-screen-mock.component.ts
@@ -0,0 +1,32 @@
+/*!
+ * @license
+ * Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { Component, input, output } from '@angular/core';
+import {
+ StartProcessScreenCloud,
+ StartProcessScreenDefaultButtons
+} from '../screen/components/screen-cloud/start-process-event-screen/start-process-screen.model';
+
+@Component({
+ selector: 'adf-cloud-mock-screen-component',
+ template: `Mock Screen Component
`
+})
+export class MockedTaskScreenCloudComponent implements StartProcessScreenCloud {
+ processDefinitionId = input('');
+ defaultStartProcessButtonsConfigurationChange = output();
+ startProcessPayloadChanged = output();
+}