[AAE-11319] start process loading spinner (#7968)

This commit is contained in:
Robert Duda 2022-11-15 09:57:38 +01:00 committed by GitHub
parent 8d4549e01d
commit a83e837a96
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 123 additions and 53 deletions

View File

@ -1,11 +1,13 @@
<mat-card class="adf-start-process"> <mat-card class="adf-start-process" *ngIf="(loading$ | async) === false; else spinner">
<mat-card-title
*ngIf="showTitle"
class="adf-title">
{{'ADF_CLOUD_PROCESS_LIST.ADF_CLOUD_START_PROCESS.FORM.TITLE' | translate}}
</mat-card-title>
<mat-card-content> <mat-card-content>
<mat-card-title
*ngIf="showTitle"
class="adf-title">
{{'ADF_CLOUD_PROCESS_LIST.ADF_CLOUD_START_PROCESS.FORM.TITLE' | translate}}
</mat-card-title>
<mat-card-subtitle id="error-message" *ngIf="errorMessageId"> <mat-card-subtitle id="error-message" *ngIf="errorMessageId">
{{ errorMessageId | translate }} {{ errorMessageId | translate }}
</mat-card-subtitle> </mat-card-subtitle>
@ -16,28 +18,38 @@
class="adf-process-input-container" class="adf-process-input-container"
floatLabel="always" floatLabel="always"
*ngIf="showSelectProcessDropdown" *ngIf="showSelectProcessDropdown"
data-automation-id="adf-select-cloud-process-dropdown" data-automation-id="adf-select-cloud-process-dropdown">
> <mat-label>{{ 'ADF_CLOUD_PROCESS_LIST.ADF_CLOUD_START_PROCESS.FORM.LABEL.TYPE' | translate }}</mat-label>
<mat-label>{{ 'ADF_CLOUD_PROCESS_LIST.ADF_CLOUD_START_PROCESS.FORM.LABEL.TYPE' | translate }}</mat-label> <input
<input #inputAutocomplete
#inputAutocomplete matInput
matInput formControlName="processDefinition"
formControlName="processDefinition" [matAutocomplete]="auto"
[matAutocomplete]="auto" id="processDefinitionName">
id="processDefinitionName"> <div class="adf-process-input-autocomplete">
<div class="adf-process-input-autocomplete"> <mat-autocomplete
<mat-autocomplete #auto="matAutocomplete" id="processDefinitionOptions" [displayWith]="displayProcessNameOnDropdown" (optionSelected)="setProcessDefinitionOnForm($event.option.value)" > #auto="matAutocomplete"
<mat-option *ngFor="let processDef of filteredProcesses" [value]="getProcessDefinitionValue(processDef)" id="processDefinitionOptions"
[displayWith]="displayProcessNameOnDropdown"
(optionSelected)="setProcessDefinitionOnForm($event.option.value)" >
<mat-option
*ngFor="let processDef of filteredProcesses"
[value]="getProcessDefinitionValue(processDef)"
(click)="processDefinitionSelectionChanged(processDef)"> (click)="processDefinitionSelectionChanged(processDef)">
{{ getProcessDefinitionValue(processDef) }} {{ getProcessDefinitionValue(processDef) }}
</mat-option> </mat-option>
</mat-autocomplete> </mat-autocomplete>
<button id="adf-select-process-dropdown" mat-icon-button (click)="displayDropdown($event)"> <button
<mat-icon>arrow_drop_down</mat-icon> id="adf-select-process-dropdown"
</button> mat-icon-button
</div> (click)="displayDropdown($event)">
<mat-error *ngIf="processDefinition.hasError('required')" class="adf-error-pb"> <mat-icon>arrow_drop_down</mat-icon>
{{ 'ADF_CLOUD_PROCESS_LIST.ADF_CLOUD_START_PROCESS.ERROR.PROCESS_DEFINITION_REQUIRED' | translate }} </button>
</div>
<mat-error
*ngIf="processDefinition.hasError('required')"
class="adf-error-pb">
{{ 'ADF_CLOUD_PROCESS_LIST.ADF_CLOUD_START_PROCESS.ERROR.PROCESS_DEFINITION_REQUIRED' | translate }}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
@ -50,7 +62,7 @@
<span *ngIf="processInstanceName.hasError('required')"> <span *ngIf="processInstanceName.hasError('required')">
{{ 'ADF_CLOUD_PROCESS_LIST.ADF_CLOUD_START_PROCESS.ERROR.PROCESS_NAME_REQUIRED' | translate }} {{ 'ADF_CLOUD_PROCESS_LIST.ADF_CLOUD_START_PROCESS.ERROR.PROCESS_NAME_REQUIRED' | translate }}
</span> </span>
<span id="adf-start-process-maxlength-error" *ngIf="processInstanceName.hasError('maxlength')"> <span *ngIf="processInstanceName.hasError('maxlength')" id="adf-start-process-maxlength-error">
{{ 'ADF_CLOUD_PROCESS_LIST.ADF_CLOUD_START_PROCESS.ERROR.MAXIMUM_LENGTH' | translate : { characters : maxNameLength } }} {{ 'ADF_CLOUD_PROCESS_LIST.ADF_CLOUD_START_PROCESS.ERROR.MAXIMUM_LENGTH' | translate : { characters : maxNameLength } }}
</span> </span>
<span *ngIf="processInstanceName.hasError('pattern')"> <span *ngIf="processInstanceName.hasError('pattern')">
@ -73,33 +85,53 @@
[showTitle]="false" [showTitle]="false"
(formContentClicked)="onFormContentClicked($event)" (formContentClicked)="onFormContentClicked($event)"
(formLoaded)="onFormLoaded($event)"> (formLoaded)="onFormLoaded($event)">
<adf-cloud-form-custom-outcomes> <adf-cloud-form-custom-outcomes>
<ng-template [ngTemplateOutlet]="taskFormCloudButtons"> <ng-template [ngTemplateOutlet]="taskFormCloudButtons">
</ng-template> </ng-template>
</adf-cloud-form-custom-outcomes> </adf-cloud-form-custom-outcomes>
</adf-cloud-form> </adf-cloud-form>
</ng-container> </ng-container>
</div> </div>
</mat-card-content> </mat-card-content>
<ng-template #taskFormCloudButtons>
<div fxLayout="row" fxLayoutAlign="end end">
<button mat-button (click)="cancelStartProcess()" id="cancel_process">
{{ 'ADF_CLOUD_PROCESS_LIST.ADF_CLOUD_START_PROCESS.FORM.ACTION.CANCEL' | translate | uppercase}}
</button>
<button color="primary" mat-button [disabled]="disableStartButton || !isProcessFormValid()" (click)="startProcess()"
data-automation-id="btn-start" id="button-start" class="btn-start">
{{'ADF_CLOUD_PROCESS_LIST.ADF_CLOUD_START_PROCESS.FORM.ACTION.START' | translate | uppercase}}
</button>
</div>
</ng-template>
<ng-template #emptyProcessDefinitionsList>
<mat-card-content *ngIf="processDefinitionLoaded">
<mat-card-subtitle class="error-message" id="no-process-message">
{{ 'ADF_CLOUD_PROCESS_LIST.ADF_CLOUD_START_PROCESS.NO_PROCESS_DEFINITIONS' | translate | uppercase}}
</mat-card-subtitle>
</mat-card-content>
</ng-template>
</mat-card> </mat-card>
<ng-template #taskFormCloudButtons>
<div fxLayout="row" fxLayoutAlign="end end">
<button
mat-button
(click)="cancelStartProcess()"
id="cancel_process">
{{ 'ADF_CLOUD_PROCESS_LIST.ADF_CLOUD_START_PROCESS.FORM.ACTION.CANCEL' | translate | uppercase}}
</button>
<button
color="primary"
mat-button
[disabled]="disableStartButton || !isProcessFormValid()"
(click)="startProcess()"
data-automation-id="btn-start"
id="button-start"
class="btn-start">
{{'ADF_CLOUD_PROCESS_LIST.ADF_CLOUD_START_PROCESS.FORM.ACTION.START' | translate | uppercase}}
</button>
</div>
</ng-template>
<ng-template #emptyProcessDefinitionsList>
<mat-card-content *ngIf="processDefinitionLoaded">
<mat-card-subtitle class="error-message" id="no-process-message">
{{ 'ADF_CLOUD_PROCESS_LIST.ADF_CLOUD_START_PROCESS.NO_PROCESS_DEFINITIONS' | translate | uppercase}}
</mat-card-subtitle>
</mat-card-content>
</ng-template>
<ng-template #spinner>
<div class="adf-loading-container">
<mat-progress-spinner
class="adf-loading"
color="primary"
mode="indeterminate">
</mat-progress-spinner>
</div>
</ng-template>

View File

@ -46,4 +46,14 @@
&-start-form-actions { &-start-form-actions {
text-align: right !important; text-align: right !important;
} }
&-loading {
margin: auto;
}
&-loading-container {
min-height: 300px;
display: flex;
height: 100%;
}
} }

View File

@ -882,6 +882,7 @@ describe('StartProcessCloudComponent', () => {
}); });
it('should hide title', () => { it('should hide title', () => {
component.loading$.next(false);
component.showTitle = false; component.showTitle = false;
fixture.detectChanges(); fixture.detectChanges();
@ -891,12 +892,16 @@ describe('StartProcessCloudComponent', () => {
}); });
it('should show title', () => { it('should show title', () => {
component.loading$.next(false);
fixture.detectChanges();
const title = fixture.debugElement.query(By.css('.adf-title')); const title = fixture.debugElement.query(By.css('.adf-title'));
expect(title).toBeTruthy(); expect(title).toBeTruthy();
}); });
it('should show process definition dropdown', () => { it('should show process definition dropdown', () => {
component.loading$.next(false);
component.processDefinitionList = fakeProcessDefinitions; component.processDefinitionList = fakeProcessDefinitions;
fixture.detectChanges(); fixture.detectChanges();
@ -906,6 +911,7 @@ describe('StartProcessCloudComponent', () => {
}); });
it('should hide process definition dropdown', () => { it('should hide process definition dropdown', () => {
component.loading$.next(false);
component.processDefinitionList = fakeProcessDefinitions; component.processDefinitionList = fakeProcessDefinitions;
component.showSelectProcessDropdown = false; component.showSelectProcessDropdown = false;
fixture.detectChanges(); fixture.detectChanges();
@ -914,5 +920,23 @@ describe('StartProcessCloudComponent', () => {
expect(processDropdown).toBeFalsy(); expect(processDropdown).toBeFalsy();
}); });
it('should show the loading spinner before process definitions loaded', () => {
component.loading$.next(true);
fixture.detectChanges();
const spinner = fixture.debugElement.query(By.css('.adf-loading'));
expect(spinner).toBeTruthy();
});
it('should show the process card after process definitions loaded', () => {
component.loading$.next(false);
fixture.detectChanges();
const card = fixture.debugElement.query(By.css('.adf-start-process'));
expect(card).toBeTruthy();
});
}); });
}); });

View File

@ -28,7 +28,7 @@ import { MatAutocompleteTrigger } from '@angular/material/autocomplete';
import { ProcessPayloadCloud } from '../models/process-payload-cloud.model'; import { ProcessPayloadCloud } from '../models/process-payload-cloud.model';
import { debounceTime, takeUntil, switchMap, filter, distinctUntilChanged, tap } from 'rxjs/operators'; import { debounceTime, takeUntil, switchMap, filter, distinctUntilChanged, tap } from 'rxjs/operators';
import { ProcessDefinitionCloud } from '../../../models/process-definition-cloud.model'; import { ProcessDefinitionCloud } from '../../../models/process-definition-cloud.model';
import { Subject, Observable } from 'rxjs'; import { Subject, Observable, BehaviorSubject } from 'rxjs';
import { TaskVariableCloud } from '../../../form/models/task-variable-cloud.model'; import { TaskVariableCloud } from '../../../form/models/task-variable-cloud.model';
import { ProcessNameCloudPipe } from '../../../pipes/process-name-cloud.pipe'; import { ProcessNameCloudPipe } from '../../../pipes/process-name-cloud.pipe';
@ -114,6 +114,7 @@ export class StartProcessCloudComponent implements OnChanges, OnInit, OnDestroy
protected onDestroy$ = new Subject<boolean>(); protected onDestroy$ = new Subject<boolean>();
processDefinitionLoaded = false; processDefinitionLoaded = false;
loading$ = new BehaviorSubject<boolean>(!this.processDefinitionLoaded);
constructor(private startProcessCloudService: StartProcessCloudService, constructor(private startProcessCloudService: StartProcessCloudService,
private formBuilder: UntypedFormBuilder, private formBuilder: UntypedFormBuilder,
@ -253,7 +254,10 @@ export class StartProcessCloudComponent implements OnChanges, OnInit, OnDestroy
this.startProcessCloudService.getProcessDefinitions(this.appName) this.startProcessCloudService.getProcessDefinitions(this.appName)
.pipe( .pipe(
tap(() => this.processDefinitionLoaded = true), tap(() => {
this.processDefinitionLoaded = true;
this.loading$.next(false);
}),
takeUntil(this.onDestroy$)) takeUntil(this.onDestroy$))
.subscribe((processDefinitionRepresentations: ProcessDefinitionCloud[]) => { .subscribe((processDefinitionRepresentations: ProcessDefinitionCloud[]) => {
this.processDefinitionList = processDefinitionRepresentations; this.processDefinitionList = processDefinitionRepresentations;