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 { By } from '@angular/platform-browser';
import { ComponentFixture, TestBed, fakeAsync, flush, discardPeriodicTasks } from '@angular/core/testing'; import { ComponentFixture, TestBed, fakeAsync, flush, discardPeriodicTasks } from '@angular/core/testing';
import { AlfrescoApiService } from '@alfresco/adf-content-services'; 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 { TaskCloudService } from '../../services/task-cloud.service';
import { import {
assignedTaskDetailsCloudMock, assignedTaskDetailsCloudMock,
@@ -106,7 +106,7 @@ describe('TaskHeaderCloudComponent', () => {
fixture.detectChanges(); fixture.detectChanges();
await fixture.whenStable(); await fixture.whenStable();
expect(getTaskByIdSpy).toHaveBeenCalled(); expect(getTaskByIdSpy).toHaveBeenCalled();
expect(component.taskDetails).toBe(assignedTaskDetailsCloudMock); expect(component.taskDetails).toEqual(assignedTaskDetailsCloudMock);
}); });
it('should display assignee', async () => { it('should display assignee', async () => {
@@ -159,6 +159,35 @@ describe('TaskHeaderCloudComponent', () => {
expect(valueEl.nativeElement.value).toBe('67c4z2a8f-01f3-11e9-8e36-0a58646002ad'); 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 () => { it('should display placeholder if no due date', async () => {
component.taskDetails.dueDate = null; component.taskDetails.dueDate = null;
component.refreshData(); component.refreshData();
@@ -30,6 +30,8 @@ import {
CardViewSelectItemModel, CardViewSelectItemModel,
CardViewTextItemModel, CardViewTextItemModel,
CardViewUpdateService, CardViewUpdateService,
ClipboardService,
ClickNotification,
TranslationService, TranslationService,
UpdateNotification UpdateNotification
} from '@alfresco/adf-core'; } from '@alfresco/adf-core';
@@ -65,6 +67,10 @@ export class TaskHeaderCloudComponent implements OnInit, OnChanges {
@Input() @Input()
showTitle: boolean = true; showTitle: boolean = true;
/** Process instance id used when task details do not include it. */
@Input()
processInstanceId?: string;
/** Emitted when the task is claimed. */ /** Emitted when the task is claimed. */
@Output() @Output()
claim: EventEmitter<any> = new EventEmitter<any>(); claim: EventEmitter<any> = new EventEmitter<any>();
@@ -87,9 +93,9 @@ export class TaskHeaderCloudComponent implements OnInit, OnChanges {
dateLocale: string; dateLocale: string;
displayDateClearAction = false; displayDateClearAction = false;
isLoading = true; isLoading = true;
processInstanceId: string;
private readonly destroyRef = inject(DestroyRef); private readonly destroyRef = inject(DestroyRef);
private readonly clipboardService = inject(ClipboardService);
constructor() { constructor() {
this.dateFormat = this.appConfig.get('adf-cloud-task-header.defaultDateFormat'); 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.itemUpdated$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(this.updateTaskDetails.bind(this));
this.cardViewUpdateService.itemClicked$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(this.onPropertyClicked.bind(this));
} }
ngOnChanges() { ngOnChanges() {
@@ -132,7 +139,6 @@ export class TaskHeaderCloudComponent implements OnInit, OnChanges {
this.taskDetails = taskDetails; this.taskDetails = taskDetails;
this.candidateGroups = candidateGroups.map((user) => ({ icon: 'group', value: user }) as CardViewArrayItem); this.candidateGroups = candidateGroups.map((user) => ({ icon: 'group', value: user }) as CardViewArrayItem);
this.candidateUsers = candidateUsers.map((group) => ({ icon: 'person', value: group }) as CardViewArrayItem); this.candidateUsers = candidateUsers.map((group) => ({ icon: 'person', value: group }) as CardViewArrayItem);
this.processInstanceId = taskDetails.processInstanceId;
if (this.taskDetails.parentTaskId) { if (this.taskDetails.parentTaskId) {
this.loadParentName(`${this.taskDetails.parentTaskId}`); this.loadParentName(`${this.taskDetails.parentTaskId}`);
} else { } else {
@@ -217,7 +223,7 @@ export class TaskHeaderCloudComponent implements OnInit, OnChanges {
}), }),
new CardViewTextItemModel({ new CardViewTextItemModel({
label: 'ADF_CLOUD_TASK_HEADER.PROPERTIES.PROCESS_INSTANCE_ID', 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'), default: this.translationService.instant('ADF_CLOUD_TASK_HEADER.PROPERTIES.PROCESS_INSTANCE_ID_DEFAULT'),
key: 'processInstanceId', key: 'processInstanceId',
clickable: true clickable: true
@@ -256,6 +262,7 @@ export class TaskHeaderCloudComponent implements OnInit, OnChanges {
*/ */
refreshData() { refreshData() {
if (this.taskDetails) { if (this.taskDetails) {
this.taskDetails = this.applyProcessInstanceIdToTaskDetails(this.taskDetails);
const defaultProperties = this.initDefaultProperties(); const defaultProperties = this.initDefaultProperties();
const filteredProperties: string[] = this.appConfig.get('adf-cloud-task-header.presets.properties'); const filteredProperties: string[] = this.appConfig.get('adf-cloud-task-header.presets.properties');
this.properties = defaultProperties.filter((cardItem) => this.isValidSelection(filteredProperties, cardItem)); this.properties = defaultProperties.filter((cardItem) => this.isValidSelection(filteredProperties, cardItem));
@@ -279,10 +286,30 @@ export class TaskHeaderCloudComponent implements OnInit, OnChanges {
.subscribe((taskDetails) => { .subscribe((taskDetails) => {
if (taskDetails) { if (taskDetails) {
this.taskDetails = 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) { private loadParentName(taskId: string) {
this.taskCloudService.getTaskById(this.appName, taskId).subscribe((taskDetails) => { this.taskCloudService.getTaskById(this.appName, taskId).subscribe((taskDetails) => {
this.parentTaskName = taskDetails.name; this.parentTaskName = taskDetails.name;