HXIDP-6471 Provide start process variables to custom screen (#11712)

* HXIDP-6471 Provide start process variables to custom screen

* Apply recommendations

* Apply more recommendations

* Add appName property

* Fix input types and defaults
This commit is contained in:
Benn Snyder
2026-03-16 16:16:02 -04:00
committed by GitHub
parent a865372819
commit 759d206302
7 changed files with 64 additions and 7 deletions
@@ -113,6 +113,9 @@
} @else if (hasScreen) { } @else if (hasScreen) {
<adf-cloud-start-process-screen-cloud <adf-cloud-start-process-screen-cloud
[screenId]="processDefinitionCurrent.formKey" [screenId]="processDefinitionCurrent.formKey"
[appName]="appName"
[processDefinitionId]="processDefinitionCurrent?.id ?? ''"
[resolvedValues]="resolvedValues"
(screenStartProcessPayloadChange)="onScreenStartProcessPayloadChange($event)" (screenStartProcessPayloadChange)="onScreenStartProcessPayloadChange($event)"
(disableStartProcessButton)="onDisableStartProcessButtonForScreen($event)" (disableStartProcessButton)="onDisableStartProcessButtonForScreen($event)"
> >
@@ -310,6 +310,14 @@ describe('StartProcessCloudComponent', () => {
expect(contentElement.textContent).toBe('Mock Screen Component'); expect(contentElement.textContent).toBe('Mock Screen Component');
}); });
it('should inject screen input', () => {
formDefinitionSpy.and.returnValue(of(fakeStartForm));
const processDefinition = fakeProcessDefinitions[2];
component.processDefinitionCurrent = processDefinition;
fixture.detectChanges();
expect(screenComponent.processDefinitionId()).toEqual(processDefinition.id);
});
it('should toggle default process buttons', () => { it('should toggle default process buttons', () => {
screenComponent.defaultStartProcessButtonsConfigurationChange.emit({ show: false, disable: false }); screenComponent.defaultStartProcessButtonsConfigurationChange.emit({ show: false, disable: false });
fixture.detectChanges(); fixture.detectChanges();
@@ -15,7 +15,7 @@
* limitations under the License. * limitations under the License.
*/ */
import { Component, ComponentRef, inject, Input, OnInit, ViewChild, ViewContainerRef } from '@angular/core'; import { Component, ComponentRef, inject, Input, OnInit, signal, ViewChild, ViewContainerRef } from '@angular/core';
import { ScreenRenderingService } from '../../../services/screen-rendering.service'; import { ScreenRenderingService } from '../../../services/screen-rendering.service';
@Component({ @Component({
@@ -29,6 +29,8 @@ export abstract class BaseScreenCloudComponent<TScreenComponent = unknown> imple
container: ViewContainerRef; container: ViewContainerRef;
protected componentRef: ComponentRef<TScreenComponent>; protected componentRef: ComponentRef<TScreenComponent>;
private readonly _componentRefChanged = signal<ComponentRef<TScreenComponent> | undefined>(undefined);
protected readonly componentRefChanged = this._componentRefChanged.asReadonly();
protected readonly screenRenderingService = inject(ScreenRenderingService); protected readonly screenRenderingService = inject(ScreenRenderingService);
ngOnInit() { ngOnInit() {
@@ -39,12 +41,13 @@ export abstract class BaseScreenCloudComponent<TScreenComponent = unknown> imple
if (this.screenId) { if (this.screenId) {
const componentType = this.screenRenderingService.resolveComponentType({ type: this.screenId }); const componentType = this.screenRenderingService.resolveComponentType({ type: this.screenId });
this.componentRef = this.container.createComponent(componentType); this.componentRef = this.container.createComponent(componentType);
this._componentRefChanged.set(this.componentRef);
this.setInputsForDynamicComponent(); this.setInputsForDynamicComponent();
this.subscribeToOutputs(); this.subscribeToOutputs();
} }
} }
protected abstract setInputsForDynamicComponent(): void; protected setInputsForDynamicComponent(): void {}
protected abstract subscribeToOutputs(): void; protected abstract subscribeToOutputs(): void;
} }
@@ -21,6 +21,7 @@ import { MockedTaskScreenCloudComponent } from '../../../../testing/start-proces
import { provideScreen } from '../../../services/provide-screen'; import { provideScreen } from '../../../services/provide-screen';
import { By } from '@angular/platform-browser'; import { By } from '@angular/platform-browser';
import { StartProcessScreenCloud, StartProcessScreenDefaultButtons } from './start-process-screen.model'; import { StartProcessScreenCloud, StartProcessScreenDefaultButtons } from './start-process-screen.model';
import { TaskVariableCloud } from '../../../../form/models/task-variable-cloud.model';
describe('StartProcessScreenCloudComponent', () => { describe('StartProcessScreenCloudComponent', () => {
let component: StartProcessScreenCloudComponent; let component: StartProcessScreenCloudComponent;
@@ -63,8 +64,30 @@ describe('StartProcessScreenCloudComponent', () => {
expect(component.showStartProcessButtons()).toBe(startProcessScreenDefaultButtons.show); expect(component.showStartProcessButtons()).toBe(startProcessScreenDefaultButtons.show);
}); });
it('should set appName', () => {
const screenInstance: StartProcessScreenCloud = fixture.debugElement.query(By.directive(MockedTaskScreenCloudComponent)).componentInstance;
expect(screenInstance.appName()).toEqual('');
const newValue = 'new-app-name';
fixture.componentRef.setInput('appName', newValue);
fixture.detectChanges();
expect(screenInstance.appName()).toEqual(newValue);
});
it('should set process definition id', () => { it('should set process definition id', () => {
const screenInstance: StartProcessScreenCloud = fixture.debugElement.query(By.directive(MockedTaskScreenCloudComponent)).componentInstance; const screenInstance: StartProcessScreenCloud = fixture.debugElement.query(By.directive(MockedTaskScreenCloudComponent)).componentInstance;
expect(screenInstance.processDefinitionId()).toBe(processDefinitionId); expect(screenInstance.processDefinitionId()).toBe(processDefinitionId);
const newId = 'new-id';
fixture.componentRef.setInput('processDefinitionId', newId);
fixture.detectChanges();
expect(screenInstance.processDefinitionId()).toEqual(newId);
});
it('should set resolvedValues', () => {
const screenInstance: StartProcessScreenCloud = fixture.debugElement.query(By.directive(MockedTaskScreenCloudComponent)).componentInstance;
expect(screenInstance.resolvedValues()).toBeUndefined();
const newValues = [new TaskVariableCloud({ id: 'new-id', name: 'new-name' })];
fixture.componentRef.setInput('resolvedValues', newValues);
fixture.detectChanges();
expect(screenInstance.resolvedValues()).toEqual(newValues);
}); });
}); });
@@ -15,11 +15,12 @@
* limitations under the License. * limitations under the License.
*/ */
import { ChangeDetectionStrategy, Component, input, output, signal } from '@angular/core'; import { ChangeDetectionStrategy, Component, effect, input, output, signal } from '@angular/core';
import { BaseScreenCloudComponent } from '../base-screen/base-screen-cloud.component'; import { BaseScreenCloudComponent } from '../base-screen/base-screen-cloud.component';
import { MatCardModule } from '@angular/material/card'; import { MatCardModule } from '@angular/material/card';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { StartProcessScreenCloud } from './start-process-screen.model'; import { StartProcessScreenCloud } from './start-process-screen.model';
import { TaskVariableCloud } from '../../../../form/models/task-variable-cloud.model';
@Component({ @Component({
selector: 'adf-cloud-start-process-screen-cloud', selector: 'adf-cloud-start-process-screen-cloud',
@@ -30,16 +31,29 @@ import { StartProcessScreenCloud } from './start-process-screen.model';
changeDetection: ChangeDetectionStrategy.OnPush changeDetection: ChangeDetectionStrategy.OnPush
}) })
export class StartProcessScreenCloudComponent extends BaseScreenCloudComponent<StartProcessScreenCloud> { export class StartProcessScreenCloudComponent extends BaseScreenCloudComponent<StartProcessScreenCloud> {
readonly appName = input('');
processDefinitionId = input(''); processDefinitionId = input('');
readonly resolvedValues = input<TaskVariableCloud[]>();
screenStartProcessPayloadChange = output<unknown>(); screenStartProcessPayloadChange = output<unknown>();
disableStartProcessButton = output<boolean>(); disableStartProcessButton = output<boolean>();
showStartProcessButtons = signal(false); showStartProcessButtons = signal(false);
protected setInputsForDynamicComponent(): void { constructor() {
if (this.processDefinitionId()) { super();
this.componentRef.setInput('processDefinitionId', this.processDefinitionId()); effect(() => {
} const componentRef = this.componentRefChanged();
if (componentRef.instance && 'appName' in componentRef.instance) {
componentRef.setInput('appName', this.appName());
}
});
effect(() => this.componentRefChanged()?.setInput('processDefinitionId', this.processDefinitionId()));
effect(() => {
const componentRef = this.componentRefChanged();
if (componentRef?.instance && 'resolvedValues' in componentRef.instance) {
componentRef.setInput('resolvedValues', this.resolvedValues());
}
});
} }
protected subscribeToOutputs(): void { protected subscribeToOutputs(): void {
@@ -16,13 +16,16 @@
*/ */
import { InputSignal, OutputEmitterRef } from '@angular/core'; import { InputSignal, OutputEmitterRef } from '@angular/core';
import { TaskVariableCloud } from '../../../../form/models/task-variable-cloud.model';
export interface StartProcessScreenDefaultButtons { export interface StartProcessScreenDefaultButtons {
show: boolean; show: boolean;
disable: boolean; disable: boolean;
} }
export interface StartProcessScreenCloud { export interface StartProcessScreenCloud {
readonly appName?: InputSignal<string>;
processDefinitionId: InputSignal<string>; processDefinitionId: InputSignal<string>;
readonly resolvedValues?: InputSignal<TaskVariableCloud[] | undefined>;
defaultStartProcessButtonsConfigurationChange: OutputEmitterRef<StartProcessScreenDefaultButtons>; defaultStartProcessButtonsConfigurationChange: OutputEmitterRef<StartProcessScreenDefaultButtons>;
startProcessPayloadChanged: OutputEmitterRef<unknown>; startProcessPayloadChanged: OutputEmitterRef<unknown>;
} }
@@ -20,13 +20,16 @@ import {
StartProcessScreenCloud, StartProcessScreenCloud,
StartProcessScreenDefaultButtons StartProcessScreenDefaultButtons
} from '../screen/components/screen-cloud/start-process-event-screen/start-process-screen.model'; } from '../screen/components/screen-cloud/start-process-event-screen/start-process-screen.model';
import { TaskVariableCloud } from '../form/models/task-variable-cloud.model';
@Component({ @Component({
selector: 'adf-cloud-mock-screen-component', selector: 'adf-cloud-mock-screen-component',
template: `<div>Mock Screen Component</div>` template: `<div>Mock Screen Component</div>`
}) })
export class MockedTaskScreenCloudComponent implements StartProcessScreenCloud { export class MockedTaskScreenCloudComponent implements StartProcessScreenCloud {
readonly appName = input('');
processDefinitionId = input(''); processDefinitionId = input('');
readonly resolvedValues = input<TaskVariableCloud[] | undefined>();
defaultStartProcessButtonsConfigurationChange = output<StartProcessScreenDefaultButtons>(); defaultStartProcessButtonsConfigurationChange = output<StartProcessScreenDefaultButtons>();
startProcessPayloadChanged = output<unknown>(); startProcessPayloadChanged = output<unknown>();
} }