AAE-41418 Add process instance id input to task header cloud component (#11897)

* AAE-41418 Add process instance id input to task header cloud component

* AAE-41418 Remove falsy early returns from task header process instance id helpers

* AAE-41418 Move process instance id fallback into task header refresh
This commit is contained in:
Wojciech Duda
2026-06-03 13:35:55 +01:00
committed by GitHub
parent a71cc84f11
commit 7d76d789b2
2 changed files with 61 additions and 5 deletions
@@ -20,7 +20,7 @@ import { of, throwError } from 'rxjs';
import { By } from '@angular/platform-browser';
import { ComponentFixture, TestBed, fakeAsync, flush, discardPeriodicTasks } from '@angular/core/testing';
import { AlfrescoApiService } from '@alfresco/adf-content-services';
import { AppConfigService, NoopAuthModule } from '@alfresco/adf-core';
import { AppConfigService, ClipboardService, NoopAuthModule } from '@alfresco/adf-core';
import { TaskCloudService } from '../../services/task-cloud.service';
import {
assignedTaskDetailsCloudMock,
@@ -106,7 +106,7 @@ describe('TaskHeaderCloudComponent', () => {
fixture.detectChanges();
await fixture.whenStable();
expect(getTaskByIdSpy).toHaveBeenCalled();
expect(component.taskDetails).toBe(assignedTaskDetailsCloudMock);
expect(component.taskDetails).toEqual(assignedTaskDetailsCloudMock);
});
it('should display assignee', async () => {
@@ -159,6 +159,35 @@ describe('TaskHeaderCloudComponent', () => {
expect(valueEl.nativeElement.value).toBe('67c4z2a8f-01f3-11e9-8e36-0a58646002ad');
});
it('should use processInstanceId input when task details omit it', async () => {
const taskWithoutProcessInstanceId = { ...assignedTaskDetailsCloudMock, processInstanceId: null };
getTaskByIdSpy.and.returnValue(of(taskWithoutProcessInstanceId));
component.processInstanceId = 'input-process-instance-id';
component.ngOnChanges();
fixture.detectChanges();
await fixture.whenStable();
fixture.detectChanges();
const valueEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-value-processInstanceId"]'));
expect(valueEl.nativeElement.value).toBe('input-process-instance-id');
expect(component.taskDetails.processInstanceId).toBe('input-process-instance-id');
});
it('should copy process instance id to clipboard when clicked', async () => {
const clipboardService = TestBed.inject(ClipboardService);
const copySpy = spyOn(clipboardService, 'copyContentToClipboard').and.stub();
fixture.detectChanges();
await fixture.whenStable();
fixture.detectChanges();
const processInstanceIdToggle = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-toggle-processInstanceId"]'));
processInstanceIdToggle.nativeElement.click();
expect(copySpy).toHaveBeenCalledWith('67c4z2a8f-01f3-11e9-8e36-0a58646002ad', 'CORE.METADATA.ACCESSIBILITY.COPY_TO_CLIPBOARD_MESSAGE');
});
it('should display placeholder if no due date', async () => {
component.taskDetails.dueDate = null;
component.refreshData();
@@ -30,6 +30,8 @@ import {
CardViewSelectItemModel,
CardViewTextItemModel,
CardViewUpdateService,
ClipboardService,
ClickNotification,
TranslationService,
UpdateNotification
} from '@alfresco/adf-core';
@@ -65,6 +67,10 @@ export class TaskHeaderCloudComponent implements OnInit, OnChanges {
@Input()
showTitle: boolean = true;
/** Process instance id used when task details do not include it. */
@Input()
processInstanceId?: string;
/** Emitted when the task is claimed. */
@Output()
claim: EventEmitter<any> = new EventEmitter<any>();
@@ -87,9 +93,9 @@ export class TaskHeaderCloudComponent implements OnInit, OnChanges {
dateLocale: string;
displayDateClearAction = false;
isLoading = true;
processInstanceId: string;
private readonly destroyRef = inject(DestroyRef);
private readonly clipboardService = inject(ClipboardService);
constructor() {
this.dateFormat = this.appConfig.get('adf-cloud-task-header.defaultDateFormat');
@@ -102,6 +108,7 @@ export class TaskHeaderCloudComponent implements OnInit, OnChanges {
});
this.cardViewUpdateService.itemUpdated$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(this.updateTaskDetails.bind(this));
this.cardViewUpdateService.itemClicked$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(this.onPropertyClicked.bind(this));
}
ngOnChanges() {
@@ -132,7 +139,6 @@ export class TaskHeaderCloudComponent implements OnInit, OnChanges {
this.taskDetails = taskDetails;
this.candidateGroups = candidateGroups.map((user) => ({ icon: 'group', value: user }) as CardViewArrayItem);
this.candidateUsers = candidateUsers.map((group) => ({ icon: 'person', value: group }) as CardViewArrayItem);
this.processInstanceId = taskDetails.processInstanceId;
if (this.taskDetails.parentTaskId) {
this.loadParentName(`${this.taskDetails.parentTaskId}`);
} else {
@@ -217,7 +223,7 @@ export class TaskHeaderCloudComponent implements OnInit, OnChanges {
}),
new CardViewTextItemModel({
label: 'ADF_CLOUD_TASK_HEADER.PROPERTIES.PROCESS_INSTANCE_ID',
value: this.processInstanceId,
value: this.taskDetails.processInstanceId,
default: this.translationService.instant('ADF_CLOUD_TASK_HEADER.PROPERTIES.PROCESS_INSTANCE_ID_DEFAULT'),
key: 'processInstanceId',
clickable: true
@@ -256,6 +262,7 @@ export class TaskHeaderCloudComponent implements OnInit, OnChanges {
*/
refreshData() {
if (this.taskDetails) {
this.taskDetails = this.applyProcessInstanceIdToTaskDetails(this.taskDetails);
const defaultProperties = this.initDefaultProperties();
const filteredProperties: string[] = this.appConfig.get('adf-cloud-task-header.presets.properties');
this.properties = defaultProperties.filter((cardItem) => this.isValidSelection(filteredProperties, cardItem));
@@ -279,10 +286,30 @@ export class TaskHeaderCloudComponent implements OnInit, OnChanges {
.subscribe((taskDetails) => {
if (taskDetails) {
this.taskDetails = taskDetails;
this.refreshData();
}
});
}
private applyProcessInstanceIdToTaskDetails(taskDetails: TaskDetailsCloudModel): TaskDetailsCloudModel {
const resolvedProcessInstanceId = taskDetails.processInstanceId ?? this.processInstanceId;
return resolvedProcessInstanceId === taskDetails.processInstanceId
? taskDetails
: { ...taskDetails, processInstanceId: resolvedProcessInstanceId };
}
private onPropertyClicked(clickNotification: ClickNotification): void {
if (clickNotification.target.key === 'processInstanceId') {
this.copyProcessInstanceIdToClipboard(clickNotification.target.value);
}
}
private copyProcessInstanceIdToClipboard(processInstanceId: string): void {
const clipboardMessage = this.translationService.instant('CORE.METADATA.ACCESSIBILITY.COPY_TO_CLIPBOARD_MESSAGE');
this.clipboardService.copyContentToClipboard(processInstanceId, clipboardMessage);
}
private loadParentName(taskId: string) {
this.taskCloudService.getTaskById(this.appName, taskId).subscribe((taskDetails) => {
this.parentTaskName = taskDetails.name;