From 1b212e88058b3578bfe47e4b5b5c96cbc42c78ac Mon Sep 17 00:00:00 2001 From: Michaela <85624192+mkrbr@users.noreply.github.com> Date: Wed, 17 Sep 2025 15:49:28 +0200 Subject: [PATCH] AAE-38063 Default buttons are temporarily visible before the custom buttons in workspace (#11198) * Bonus Content: Remove warnings from console * Fixes: Prevents display issues during form loading Ensures the process definition card and start button are not rendered until the form is fully loaded or an error occurs during the loading process. This prevents flickering and ensures a smoother user experience. * BONUS content: calm down sonar cube * Removes unnecessary error handling Removes the error handler from the `subscribe` block of the `startProcess` method in `StartProcessCloudComponent`. The error handling was redundant, as errors are already handled by other mechanisms. This simplifies the code and avoids potential duplicate error handling. * Uses `test` method for regex checks Replaces the `exec` method with the `test` method for regular expression checks to determine if a pattern exists within a string. This resolves potential issues where the return value of `exec` (an array or null) was not being properly evaluated as a boolean condition, leading to incorrect logic execution. --------- Co-authored-by: Fabian Kindgen <39992669+fkindgen@users.noreply.github.com> --- .../widgets/base-viewer/base-viewer.widget.ts | 3 +- .../app-details-cloud.component.ts | 3 +- .../widgets/file-viewer/file-viewer.widget.ts | 3 +- .../start-process-cloud.component.html | 2 +- .../start-process-cloud.component.spec.ts | 17 ++-- .../start-process-cloud.component.ts | 78 +++++++++++-------- 6 files changed, 54 insertions(+), 52 deletions(-) diff --git a/lib/core/src/lib/form/components/widgets/base-viewer/base-viewer.widget.ts b/lib/core/src/lib/form/components/widgets/base-viewer/base-viewer.widget.ts index c4dd66c337..0fdf0aefb4 100644 --- a/lib/core/src/lib/form/components/widgets/base-viewer/base-viewer.widget.ts +++ b/lib/core/src/lib/form/components/widgets/base-viewer/base-viewer.widget.ts @@ -15,7 +15,6 @@ * limitations under the License. */ -import { NgIf } from '@angular/common'; import { Component, OnInit, ViewEncapsulation } from '@angular/core'; import { TranslatePipe } from '@ngx-translate/core'; import { ViewerComponent } from '../../../../viewer'; @@ -40,7 +39,7 @@ import { WidgetComponent } from '../widget.component'; '(invalid)': 'event($event)', '(select)': 'event($event)' }, - imports: [NgIf, TranslatePipe, ViewerComponent, ErrorWidgetComponent], + imports: [TranslatePipe, ViewerComponent, ErrorWidgetComponent], encapsulation: ViewEncapsulation.None }) export class BaseViewerWidgetComponent extends WidgetComponent implements OnInit { diff --git a/lib/process-services-cloud/src/lib/app/components/app-details-cloud/app-details-cloud.component.ts b/lib/process-services-cloud/src/lib/app/components/app-details-cloud/app-details-cloud.component.ts index 6d6b1ea55d..fae39098e0 100644 --- a/lib/process-services-cloud/src/lib/app/components/app-details-cloud/app-details-cloud.component.ts +++ b/lib/process-services-cloud/src/lib/app/components/app-details-cloud/app-details-cloud.component.ts @@ -18,13 +18,12 @@ import { Component, EventEmitter, Input, Output, ViewEncapsulation } from '@angular/core'; import { ApplicationInstanceModel, DEFAULT_APP_INSTANCE_ICON, DEFAULT_APP_INSTANCE_THEME } from '../../models/application-instance.model'; import { CommonModule } from '@angular/common'; -import { TranslatePipe } from '@ngx-translate/core'; import { MatIconModule } from '@angular/material/icon'; import { MatCardModule } from '@angular/material/card'; @Component({ selector: 'adf-cloud-app-details', - imports: [CommonModule, TranslatePipe, MatIconModule, MatCardModule], + imports: [CommonModule, MatIconModule, MatCardModule], templateUrl: './app-details-cloud.component.html', styleUrls: ['./app-details-cloud.component.scss'], encapsulation: ViewEncapsulation.None diff --git a/lib/process-services-cloud/src/lib/form/components/widgets/file-viewer/file-viewer.widget.ts b/lib/process-services-cloud/src/lib/form/components/widgets/file-viewer/file-viewer.widget.ts index 20f497058d..3473f87b69 100644 --- a/lib/process-services-cloud/src/lib/form/components/widgets/file-viewer/file-viewer.widget.ts +++ b/lib/process-services-cloud/src/lib/form/components/widgets/file-viewer/file-viewer.widget.ts @@ -19,13 +19,12 @@ import { Component, ViewEncapsulation } from '@angular/core'; import { FormService, BaseViewerWidgetComponent, ErrorWidgetComponent } from '@alfresco/adf-core'; import { AlfrescoViewerComponent } from '@alfresco/adf-content-services'; import { TranslatePipe } from '@ngx-translate/core'; -import { NgIf } from '@angular/common'; /* eslint-disable @angular-eslint/component-selector */ @Component({ selector: 'file-viewer-widget', - imports: [NgIf, ErrorWidgetComponent, AlfrescoViewerComponent, TranslatePipe], + imports: [ErrorWidgetComponent, AlfrescoViewerComponent, TranslatePipe], templateUrl: './file-viewer.widget.html', styleUrls: ['./file-viewer.widget.scss'], host: { 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 d7f32700f8..a5c0f0b007 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 @@ -1,4 +1,4 @@ -@if (processDefinitionLoaded) { +@if (processDefinitionLoaded && !isFormCloudLoading) { @if (showTitle) { 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 5ca7fb185d..d5d0481f86 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 @@ -1150,15 +1150,14 @@ describe('StartProcessCloudComponent', () => { component.ngOnChanges({ appName: firstChange }); component.processDefinitionList = fakeProcessDefinitions; component.processDefinitionName = fakeProcessDefinitions[0].name; + fixture.detectChanges(); }); - it('start process button should be enabled when isProcessStarting is false', async () => { - fixture.detectChanges(); + it('start process button should be enabled when isProcessStarting is false', () => { component.processForm.controls['processInstanceName'].setValue(fakeProcessDefinitions[0].id); component.appName = 'test app name'; component.isProcessStarting = false; fixture.detectChanges(); - await fixture.whenStable(); const startButton = fixture.debugElement.query(By.css('#button-start')); expect(startButton).not.toBeNull(); @@ -1166,27 +1165,21 @@ describe('StartProcessCloudComponent', () => { expect((startButton.nativeElement as HTMLButtonElement).disabled).toBeFalse(); }); - it('start process button should be disabled when isFormCloudLoading is true', async () => { - fixture.detectChanges(); + it('start process button should be null when isFormCloudLoading is true', () => { component.processForm.controls['processInstanceName'].setValue(fakeProcessDefinitions[0].id); component.appName = 'test app name'; component.isFormCloudLoading = true; fixture.detectChanges(); - await fixture.whenStable(); const startButton = fixture.debugElement.query(By.css('#button-start')); - expect(startButton).not.toBeNull(); - expect(component.disableStartButton).toBeTrue(); - expect((startButton.nativeElement as HTMLButtonElement).disabled).toBeTrue(); + expect(startButton).toBeFalsy(); }); - it('start process button should be disabled when isLoading is true', async () => { - fixture.detectChanges(); + it('start process button should be disabled when isLoading is true', () => { component.processForm.controls['processInstanceName'].setValue(fakeProcessDefinitions[0].id); component.appName = 'test app name'; component.isProcessStarting = true; fixture.detectChanges(); - await fixture.whenStable(); const startButton = fixture.debugElement.query(By.css('#button-start')); expect(startButton).not.toBeNull(); 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 d06b9ae1f5..a01338ea7c 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 @@ -66,8 +66,8 @@ import { MatDialog } from '@angular/material/dialog'; const MAX_NAME_LENGTH: number = 255; const PROCESS_DEFINITION_DEBOUNCE: number = 300; -const DATE_TIME_IDENTIFIER_REG_EXP = new RegExp('%{datetime}', 'i'); -const PROCESS_DEFINITION_IDENTIFIER_REG_EXP = new RegExp('%{processdefinition}', 'i'); +const DATE_TIME_IDENTIFIER_REG_EXP = /%{datetime}/i; +const PROCESS_DEFINITION_IDENTIFIER_REG_EXP = /%{processdefinition}/i; @Component({ selector: 'adf-cloud-start-process', @@ -174,7 +174,7 @@ export class StartProcessCloudComponent implements OnChanges, OnInit { isProcessStarting = false; isFormCloudLoaded = false; - isFormCloudLoading = false; + isFormCloudLoading = true; processDefinitionLoaded = false; showStartProcessButton$: Observable; @@ -237,13 +237,18 @@ export class StartProcessCloudComponent implements OnChanges, OnInit { return this.translateService.instant('ADF_CLOUD_PROCESS_LIST.ADF_CLOUD_START_PROCESS.FORM.ACTION.CANCEL').toUpperCase(); } - constructor(private translateService: TranslationService) { + constructor(private readonly translateService: TranslationService) { this.startProcessButtonLabel = this.defaultStartProcessButtonLabel; this.cancelButtonLabel = this.defaultCancelProcessButtonLabel; } ngOnInit() { - this.processDefinition.setValue(this.processDefinitionName); + if (this.processDefinitionName) { + this.processDefinition.setValue(this.processDefinitionName); + } else { + this.isFormCloudLoading = false; + } + this.processDefinition.valueChanges .pipe(debounceTime(PROCESS_DEFINITION_DEBOUNCE)) .pipe(takeUntilDestroyed(this.destroyRef)) @@ -295,8 +300,8 @@ export class StartProcessCloudComponent implements OnChanges, OnInit { private selectProcessDefinitionByProcessDefinitionName(processDefinitionName: string): void { this.filteredProcesses = this.getProcessDefinitionListByNameOrKey(processDefinitionName); - - if (this.isProcessFormValid && this.filteredProcesses && this.filteredProcesses.length === 1) { + this.isFormCloudLoading = this.isProcessFormValid && this.filteredProcesses && this.filteredProcesses.length === 1; + if (this.isFormCloudLoading) { this.setProcessDefinitionOnForm(this.filteredProcesses[0].name); } } @@ -314,30 +319,34 @@ export class StartProcessCloudComponent implements OnChanges, OnInit { this.startProcessCloudService .getStartEventConstants(this.appName, processDefinitionCurrent.id) .pipe(catchError(() => of([] as TaskVariableCloud[]))) - ]).subscribe(([staticMappings, constants]) => { - this.staticMappings = staticMappings; - this.resolvedValues = this.staticMappings.concat(this.values || []); - this.processDefinitionCurrent = processDefinitionCurrent; - this.isFormCloudLoading = false; + ]).subscribe({ + next: ([staticMappings, constants]) => { + this.staticMappings = staticMappings; + this.resolvedValues = this.staticMappings.concat(this.values || []); + this.processDefinitionCurrent = processDefinitionCurrent; - const displayStart = constants?.find((constant) => constant.name === 'startEnabled'); - const startLabel = constants?.find((constant) => constant.name === 'startLabel'); + const displayStart = constants?.find((constant) => constant.name === 'startEnabled'); + const startLabel = constants?.find((constant) => constant.name === 'startLabel'); - const displayCancel = constants?.find((constant) => constant.name === 'cancelEnabled'); - const cancelLabel = constants?.find((constant) => constant.name === 'cancelLabel'); + const displayCancel = constants?.find((constant) => constant.name === 'cancelEnabled'); + const cancelLabel = constants?.find((constant) => constant.name === 'cancelLabel'); - if (displayStart) { - this.displayStartSubject.next(displayStart?.value); - } - if (startLabel) { - this.startProcessButtonLabel = startLabel?.value?.trim()?.length > 0 ? startLabel.value.trim() : this.defaultStartProcessButtonLabel; - } + if (displayStart) { + this.displayStartSubject.next(displayStart?.value); + } + if (startLabel) { + this.startProcessButtonLabel = + startLabel?.value?.trim()?.length > 0 ? startLabel.value.trim() : this.defaultStartProcessButtonLabel; + } - if (displayCancel) { - this.showCancelButton = displayCancel?.value === 'true' && this.showCancelButton; - } - if (cancelLabel) { - this.cancelButtonLabel = cancelLabel?.value?.trim()?.length > 0 ? cancelLabel.value.trim() : this.defaultCancelProcessButtonLabel; + if (displayCancel) { + this.showCancelButton = displayCancel?.value === 'true' && this.showCancelButton; + } + if (cancelLabel) { + this.cancelButtonLabel = cancelLabel?.value?.trim()?.length > 0 ? cancelLabel.value.trim() : this.defaultCancelProcessButtonLabel; + } + + this.isFormCloudLoading = false; } }); @@ -379,8 +388,8 @@ export class StartProcessCloudComponent implements OnChanges, OnInit { this.startProcessCloudService .getProcessDefinitions(this.appName) .pipe(takeUntilDestroyed(this.destroyRef)) - .subscribe( - (processDefinitionRepresentations: ProcessDefinitionCloud[]) => { + .subscribe({ + next: (processDefinitionRepresentations: ProcessDefinitionCloud[]) => { this.processDefinitionList = processDefinitionRepresentations; if (processDefinitionRepresentations.length === 1) { this.selectDefaultProcessDefinition(); @@ -393,14 +402,17 @@ export class StartProcessCloudComponent implements OnChanges, OnInit { this.setProcessDefinitionOnForm(processDefinition.name); this.processDefinitionSelectionChanged(processDefinition); } + } else { + this.isFormCloudLoading = false; } this.processDefinitionLoaded = true; }, - () => { + error: () => { this.errorMessageId = 'ADF_CLOUD_PROCESS_LIST.ADF_CLOUD_START_PROCESS.ERROR.LOAD_PROCESS_DEFS'; + this.isFormCloudLoading = false; } - ); + }); } private isValidName(name: string): boolean { @@ -564,12 +576,12 @@ export class StartProcessCloudComponent implements OnChanges, OnInit { getDefaultProcessName(processNameFormat: string, processInstance?: ProcessInstanceCloud): string { let processName = processNameFormat; - if (processName.match(DATE_TIME_IDENTIFIER_REG_EXP)) { + if (DATE_TIME_IDENTIFIER_REG_EXP.test(processName)) { const presentDateTime = getTime(new Date()); processName = processName.replace(DATE_TIME_IDENTIFIER_REG_EXP, this.localizedDatePipe.transform(presentDateTime, 'medium')); } - if (processName.match(PROCESS_DEFINITION_IDENTIFIER_REG_EXP)) { + if (PROCESS_DEFINITION_IDENTIFIER_REG_EXP.test(processName)) { const selectedProcessDefinitionName = processInstance ? processInstance.processDefinitionName : ''; processName = processName.replace(PROCESS_DEFINITION_IDENTIFIER_REG_EXP, selectedProcessDefinitionName); }