mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-07-24 17:32:15 +00:00
AAE-35981 Form button customization (#11047)
This commit is contained in:
@@ -27,12 +27,28 @@ import { isOutcomeButtonVisible } from './helpers/buttons-visibility';
|
||||
export abstract class FormBaseComponent {
|
||||
protected _form: FormModel;
|
||||
|
||||
static SAVE_OUTCOME_ID: string = '$save';
|
||||
static COMPLETE_OUTCOME_ID: string = '$complete';
|
||||
static START_PROCESS_OUTCOME_ID: string = '$startProcess';
|
||||
static CUSTOM_OUTCOME_ID: string = '$custom';
|
||||
static COMPLETE_BUTTON_COLOR: ThemePalette = 'primary';
|
||||
static COMPLETE_OUTCOME_NAME: string = 'COMPLETE';
|
||||
/**
|
||||
* @deprecated Use {@link FormModel.SAVE_OUTCOME} instead.
|
||||
*/
|
||||
static readonly SAVE_OUTCOME_ID: string = FormModel.SAVE_OUTCOME;
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link FormModel.COMPLETE_OUTCOME} instead.
|
||||
*/
|
||||
static readonly COMPLETE_OUTCOME_ID: string = FormModel.COMPLETE_OUTCOME;
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link FormModel.START_PROCESS_OUTCOME} instead.
|
||||
*/
|
||||
static readonly START_PROCESS_OUTCOME_ID: string = FormModel.START_PROCESS_OUTCOME;
|
||||
|
||||
static readonly CUSTOM_OUTCOME_ID: string = '$custom';
|
||||
static readonly COMPLETE_BUTTON_COLOR: ThemePalette = 'primary';
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link FormOutcomeModel.COMPLETE_ACTION} instead.
|
||||
*/
|
||||
static readonly COMPLETE_OUTCOME_NAME: string = FormOutcomeModel.COMPLETE_ACTION;
|
||||
|
||||
/** Path of the folder where the metadata will be stored. */
|
||||
@Input()
|
||||
@@ -50,7 +66,7 @@ export abstract class FormBaseComponent {
|
||||
@Input()
|
||||
showCompleteButton: boolean = true;
|
||||
|
||||
/** If true then the `Complete` outcome button is shown but it will be disabled. */
|
||||
/** If true then the `Complete` outcome button is shown, but it will be disabled. */
|
||||
@Input()
|
||||
disableCompleteButton: boolean = false;
|
||||
|
||||
@@ -138,7 +154,7 @@ export abstract class FormBaseComponent {
|
||||
}
|
||||
|
||||
getColorForOutcome(outcomeName: string): ThemePalette {
|
||||
return outcomeName === FormBaseComponent.COMPLETE_OUTCOME_NAME ? FormBaseComponent.COMPLETE_BUTTON_COLOR : null;
|
||||
return outcomeName === FormOutcomeModel.COMPLETE_ACTION ? FormBaseComponent.COMPLETE_BUTTON_COLOR : null;
|
||||
}
|
||||
|
||||
isOutcomeButtonEnabled(outcome?: FormOutcomeModel): boolean {
|
||||
@@ -182,20 +198,20 @@ export abstract class FormBaseComponent {
|
||||
}
|
||||
|
||||
if (outcome.isSystem) {
|
||||
if (outcome.id === FormBaseComponent.SAVE_OUTCOME_ID) {
|
||||
if (outcome.id === FormModel.SAVE_OUTCOME) {
|
||||
this.disableSaveButton = true;
|
||||
this.saveTaskForm();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (outcome.id === FormBaseComponent.COMPLETE_OUTCOME_ID) {
|
||||
if (outcome.id === FormModel.COMPLETE_OUTCOME) {
|
||||
this.disableSaveButton = true;
|
||||
this.disableCompleteButton = true;
|
||||
this.completeTaskForm();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (outcome.id === FormBaseComponent.START_PROCESS_OUTCOME_ID) {
|
||||
if (outcome.id === FormModel.START_PROCESS_OUTCOME) {
|
||||
this.completeTaskForm();
|
||||
return true;
|
||||
}
|
||||
|
@@ -21,9 +21,9 @@ import { FormWidgetModel } from './form-widget.model';
|
||||
import { WidgetVisibilityModel } from '../../../models/widget-visibility.model';
|
||||
|
||||
export class FormOutcomeModel extends FormWidgetModel {
|
||||
static SAVE_ACTION: string = 'SAVE'; // Activiti 'Save' action name
|
||||
static COMPLETE_ACTION: string = 'COMPLETE'; // Activiti 'Complete' action name
|
||||
static START_PROCESS_ACTION: string = 'START PROCESS'; // Activiti 'Start Process' action name
|
||||
static readonly SAVE_ACTION: string = 'SAVE'; // Activiti 'Save' action name
|
||||
static readonly COMPLETE_ACTION: string = 'COMPLETE'; // Activiti 'Complete' action name
|
||||
static readonly START_PROCESS_ACTION: string = 'START PROCESS'; // Activiti 'Start Process' action name
|
||||
|
||||
isSystem: boolean = false;
|
||||
isSelected: boolean = false;
|
||||
|
@@ -62,10 +62,10 @@ export interface FormRepresentationModel {
|
||||
theme?: ThemeModel;
|
||||
}
|
||||
export class FormModel implements ProcessFormModel {
|
||||
static UNSET_TASK_NAME: string = 'Nameless task';
|
||||
static SAVE_OUTCOME: string = '$save';
|
||||
static COMPLETE_OUTCOME: string = '$complete';
|
||||
static START_PROCESS_OUTCOME: string = '$startProcess';
|
||||
static readonly UNSET_TASK_NAME: string = 'Nameless task';
|
||||
static readonly SAVE_OUTCOME: string = '$save';
|
||||
static readonly COMPLETE_OUTCOME: string = '$complete';
|
||||
static readonly START_PROCESS_OUTCOME: string = '$startProcess';
|
||||
|
||||
readonly id: string | number;
|
||||
readonly name: string;
|
||||
@@ -410,27 +410,28 @@ export class FormModel implements ProcessFormModel {
|
||||
}
|
||||
|
||||
protected parseOutcomes() {
|
||||
if (this.json.fields) {
|
||||
const saveOutcome = new FormOutcomeModel(this, {
|
||||
id: FormModel.SAVE_OUTCOME,
|
||||
name: 'SAVE',
|
||||
isSystem: true
|
||||
});
|
||||
const completeOutcome = new FormOutcomeModel(this, {
|
||||
id: FormModel.COMPLETE_OUTCOME,
|
||||
name: 'COMPLETE',
|
||||
isSystem: true
|
||||
});
|
||||
const startProcessOutcome = new FormOutcomeModel(this, {
|
||||
id: FormModel.START_PROCESS_OUTCOME,
|
||||
name: 'START PROCESS',
|
||||
isSystem: true
|
||||
});
|
||||
if (!this.json.fields) return;
|
||||
|
||||
const customOutcomes = (this.json.outcomes || []).map((obj) => new FormOutcomeModel(this, obj));
|
||||
const saveOutcome = new FormOutcomeModel(this, {
|
||||
id: FormModel.SAVE_OUTCOME,
|
||||
name: FormOutcomeModel.SAVE_ACTION,
|
||||
isSystem: true
|
||||
});
|
||||
|
||||
this.outcomes = [saveOutcome].concat(customOutcomes.length > 0 ? customOutcomes : [completeOutcome, startProcessOutcome]);
|
||||
}
|
||||
const completeOutcome = new FormOutcomeModel(this, {
|
||||
id: FormModel.COMPLETE_OUTCOME,
|
||||
name: FormOutcomeModel.COMPLETE_ACTION,
|
||||
isSystem: true
|
||||
});
|
||||
|
||||
const startProcessOutcome = new FormOutcomeModel(this, {
|
||||
id: FormModel.START_PROCESS_OUTCOME,
|
||||
name: FormOutcomeModel.START_PROCESS_ACTION,
|
||||
isSystem: true
|
||||
});
|
||||
|
||||
const customOutcomes = (this.json.outcomes ?? ([] as FormModel[])).map((formModel: FormModel) => new FormOutcomeModel(this, formModel));
|
||||
this.outcomes = [saveOutcome].concat(customOutcomes.length > 0 ? customOutcomes : [completeOutcome, startProcessOutcome]);
|
||||
}
|
||||
|
||||
addValuesNotPresent(valuesToSetIfNotPresent: FormValues) {
|
||||
|
@@ -81,7 +81,7 @@ export class FormService implements FormValidationService {
|
||||
if (!json.fields) {
|
||||
form.outcomes = [
|
||||
new FormOutcomeModel(form, {
|
||||
id: '$save',
|
||||
id: FormModel.SAVE_OUTCOME,
|
||||
name: FormOutcomeModel.SAVE_ACTION,
|
||||
isSystem: true
|
||||
})
|
||||
|
@@ -91,7 +91,7 @@
|
||||
class="adf-cloud-form-custom-outcome-button"
|
||||
(click)="onOutcomeClicked(outcome)"
|
||||
>
|
||||
{{ outcome.name | translate | uppercase }}
|
||||
{{ getCustomOutcomeButtonText(outcome) || (outcome.name | translate | uppercase) }}
|
||||
</button>
|
||||
</ng-container>
|
||||
</mat-card-actions>
|
||||
|
@@ -1492,6 +1492,54 @@ describe('FormCloudComponent', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Custom outcome button text for default outcomes', () => {
|
||||
beforeEach(() => {
|
||||
formComponent.form = formComponent.parseForm(emptyFormRepresentationJSON);
|
||||
});
|
||||
|
||||
it('should display custom save button text when set', () => {
|
||||
const customText = 'Custom Save Text';
|
||||
formComponent.customSaveButtonText = customText;
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
const buttonSelector = '#adf-form-save';
|
||||
const outcomeButton = fixture.debugElement.query(By.css(buttonSelector));
|
||||
expect(outcomeButton).toBeTruthy();
|
||||
expect(outcomeButton.nativeElement.textContent.trim()).toBe(customText);
|
||||
});
|
||||
|
||||
it('should display default save button text when not set', () => {
|
||||
fixture.detectChanges();
|
||||
|
||||
const buttonSelector = '#adf-form-save';
|
||||
const outcomeButton = fixture.debugElement.query(By.css(buttonSelector));
|
||||
expect(outcomeButton).toBeTruthy();
|
||||
expect(outcomeButton.nativeElement.textContent.trim()).toBe('SAVE');
|
||||
});
|
||||
|
||||
it('should display custom complete button text when set', () => {
|
||||
const customText = 'Custom Complete Text';
|
||||
formComponent.customCompleteButtonText = customText;
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
const buttonSelector = '#adf-form-complete';
|
||||
const outcomeButton = fixture.debugElement.query(By.css(buttonSelector));
|
||||
expect(outcomeButton).toBeTruthy();
|
||||
expect(outcomeButton.nativeElement.textContent.trim()).toBe(customText);
|
||||
});
|
||||
|
||||
it('should display default complete button text when not set', () => {
|
||||
fixture.detectChanges();
|
||||
|
||||
const buttonSelector = '#adf-form-complete';
|
||||
const outcomeButton = fixture.debugElement.query(By.css(buttonSelector));
|
||||
expect(outcomeButton).toBeTruthy();
|
||||
expect(outcomeButton.nativeElement.textContent.trim()).toBe('COMPLETE');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Multilingual Form', () => {
|
||||
|
@@ -121,6 +121,20 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges,
|
||||
@Input()
|
||||
showCompleteButton = false;
|
||||
|
||||
/**
|
||||
* Custom text for the `Save` button.
|
||||
* If not provided, the default text will be used.
|
||||
*/
|
||||
@Input()
|
||||
customSaveButtonText: string = '';
|
||||
|
||||
/**
|
||||
* Custom text for the `Complete` button.
|
||||
* If not provided, the default text will be used.
|
||||
*/
|
||||
@Input()
|
||||
customCompleteButtonText: string = '';
|
||||
|
||||
/** Emitted when the form is submitted with the `Save` or custom outcomes. */
|
||||
@Output()
|
||||
formSaved = new EventEmitter<FormModel>();
|
||||
@@ -298,6 +312,15 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges,
|
||||
);
|
||||
}
|
||||
|
||||
getCustomOutcomeButtonText(outcome: FormOutcomeModel): string {
|
||||
if (outcome?.id === FormModel.SAVE_OUTCOME || outcome?.name === FormOutcomeModel.SAVE_ACTION) {
|
||||
return this.customSaveButtonText;
|
||||
} else if (outcome?.id === FormModel.COMPLETE_OUTCOME || outcome?.name === FormOutcomeModel.COMPLETE_ACTION) {
|
||||
return this.customCompleteButtonText;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
isAProcessTask(taskRepresentation: TaskDetailsCloudModel): boolean {
|
||||
return taskRepresentation.processDefinitionId && taskRepresentation.processDefinitionDeploymentId !== 'null';
|
||||
}
|
||||
@@ -430,7 +453,7 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges,
|
||||
* @returns list of form outcomes
|
||||
*/
|
||||
getFormDefinitionOutcomes(form: FormModel): FormOutcomeModel[] {
|
||||
return [new FormOutcomeModel(form, { id: '$save', name: FormOutcomeModel.SAVE_ACTION, isSystem: true })];
|
||||
return [new FormOutcomeModel(form, { id: FormModel.SAVE_OUTCOME, name: FormOutcomeModel.SAVE_ACTION, isSystem: true })];
|
||||
}
|
||||
|
||||
checkVisibility(field: FormFieldModel) {
|
||||
|
@@ -210,7 +210,7 @@ export class FormCloudService extends BaseCloudService implements FormCloudServi
|
||||
*
|
||||
* @param json JSON data to create the form
|
||||
* @param data Values for the form's fields
|
||||
* @param readOnly Toggles whether or not the form should be read-only
|
||||
* @param readOnly Toggles whether the form should be read-only
|
||||
* @returns Form created from the JSON specification
|
||||
*/
|
||||
parseForm(json: any, data?: TaskVariableCloud[], readOnly: boolean = false): FormModel {
|
||||
|
@@ -9,9 +9,12 @@
|
||||
[readOnly]="isReadOnly()"
|
||||
[showRefreshButton]="showRefreshButton"
|
||||
[showValidationIcon]="showValidationIcon"
|
||||
[showCompleteButton]="canCompleteTask()"
|
||||
[showSaveButton]="canCompleteTask()"
|
||||
[showCompleteButton]="showCompleteButton && canCompleteTask()"
|
||||
[showSaveButton]="showSaveButton && canCompleteTask()"
|
||||
[customSaveButtonText]="customSaveButtonText"
|
||||
[customCompleteButtonText]="customCompleteButtonText"
|
||||
[displayModeConfigurations]="displayModeConfigurations"
|
||||
(formLoaded)="onFormLoaded($event)"
|
||||
(formSaved)="onFormSaved($event)"
|
||||
(formCompleted)="onFormCompleted($event)"
|
||||
(formError)="onError($event)"
|
||||
@@ -27,6 +30,7 @@
|
||||
[canClaimTask]="canClaimTask()"
|
||||
[canUnclaimTask]="canUnclaimTask()"
|
||||
[showCancelButton]="showCancelButton"
|
||||
[customCancelButtonText]="customCancelButtonText"
|
||||
[taskId]="taskId"
|
||||
(cancelClick)="onCancelClick()"
|
||||
(claimTask)="onClaimTask()"
|
||||
|
@@ -33,6 +33,7 @@ import {
|
||||
import { UserTaskCloudButtonsComponent } from '../user-task-cloud-buttons/user-task-cloud-buttons.component';
|
||||
import { TaskFormCloudComponent } from './task-form-cloud.component';
|
||||
import { FormCustomOutcomesComponent } from '../../../../form/components/form-cloud-custom-outcomes.component';
|
||||
import { By } from '@angular/platform-browser';
|
||||
|
||||
const taskDetails: TaskDetailsCloudModel = {
|
||||
appName: 'simple-app',
|
||||
@@ -191,6 +192,90 @@ describe('TaskFormCloudComponent', () => {
|
||||
const canUnclaimTask = component.canUnclaimTask();
|
||||
expect(canUnclaimTask).toBe(false);
|
||||
});
|
||||
|
||||
it('should pass showCompleteButton to adf-cloud-form when task can be completed', () => {
|
||||
component.appName = 'app1';
|
||||
component.taskId = 'task1';
|
||||
component.showCompleteButton = true;
|
||||
spyOn(component, 'canCompleteTask').and.returnValue(true);
|
||||
fixture.detectChanges();
|
||||
|
||||
const cloudFormElement = fixture.debugElement.query(By.css('adf-cloud-form'));
|
||||
expect(cloudFormElement).toBeDefined();
|
||||
const cloudFormComponent = cloudFormElement.componentInstance as FormCloudComponent;
|
||||
expect(cloudFormComponent).toBeDefined();
|
||||
expect(cloudFormComponent.showCompleteButton).toBe(true);
|
||||
});
|
||||
|
||||
it('should not pass showCompleteButton to adf-cloud-form when task cannot be completed', () => {
|
||||
component.appName = 'app1';
|
||||
component.taskId = 'task1';
|
||||
component.showCompleteButton = true;
|
||||
spyOn(component, 'canCompleteTask').and.returnValue(false);
|
||||
fixture.detectChanges();
|
||||
|
||||
const cloudFormElement = fixture.debugElement.query(By.css('adf-cloud-form'));
|
||||
expect(cloudFormElement).toBeDefined();
|
||||
const cloudFormComponent = cloudFormElement.componentInstance as FormCloudComponent;
|
||||
expect(cloudFormComponent).toBeDefined();
|
||||
expect(cloudFormComponent.showCompleteButton).toBe(false);
|
||||
});
|
||||
|
||||
it('should pass showSaveButton to adf-cloud-form when task can be completed', () => {
|
||||
component.appName = 'app1';
|
||||
component.taskId = 'task1';
|
||||
component.showSaveButton = true;
|
||||
spyOn(component, 'canCompleteTask').and.returnValue(true);
|
||||
fixture.detectChanges();
|
||||
|
||||
const cloudFormElement = fixture.debugElement.query(By.css('adf-cloud-form'));
|
||||
expect(cloudFormElement).toBeDefined();
|
||||
const cloudFormComponent = cloudFormElement.componentInstance as FormCloudComponent;
|
||||
expect(cloudFormComponent).toBeDefined();
|
||||
expect(cloudFormComponent.showSaveButton).toBe(true);
|
||||
});
|
||||
|
||||
it('should not pass showSaveButton to adf-cloud-form when task cannot be completed', () => {
|
||||
component.appName = 'app1';
|
||||
component.taskId = 'task1';
|
||||
component.showSaveButton = true;
|
||||
spyOn(component, 'canCompleteTask').and.returnValue(false);
|
||||
fixture.detectChanges();
|
||||
|
||||
const cloudFormElement = fixture.debugElement.query(By.css('adf-cloud-form'));
|
||||
expect(cloudFormElement).toBeDefined();
|
||||
const cloudFormComponent = cloudFormElement.componentInstance as FormCloudComponent;
|
||||
expect(cloudFormComponent).toBeDefined();
|
||||
expect(cloudFormComponent.showSaveButton).toBe(false);
|
||||
});
|
||||
|
||||
it('should pass customSaveButtonText to adf-cloud-form', () => {
|
||||
const customText = 'Custom Save';
|
||||
component.appName = 'app1';
|
||||
component.taskId = 'task1';
|
||||
component.customSaveButtonText = customText;
|
||||
fixture.detectChanges();
|
||||
|
||||
const cloudFormElement = fixture.debugElement.query(By.css('adf-cloud-form'));
|
||||
expect(cloudFormElement).toBeDefined();
|
||||
const cloudFormComponent = cloudFormElement.componentInstance as FormCloudComponent;
|
||||
expect(cloudFormComponent).toBeDefined();
|
||||
expect(cloudFormComponent.customSaveButtonText).toBe(customText);
|
||||
});
|
||||
|
||||
it('should pass customCompleteButtonText to adf-cloud-form', () => {
|
||||
const customText = 'Custom Complete';
|
||||
component.appName = 'app1';
|
||||
component.taskId = 'task1';
|
||||
component.customCompleteButtonText = customText;
|
||||
fixture.detectChanges();
|
||||
|
||||
const cloudFormElement = fixture.debugElement.query(By.css('adf-cloud-form'));
|
||||
expect(cloudFormElement).toBeDefined();
|
||||
const cloudFormComponent = cloudFormElement.componentInstance as FormCloudComponent;
|
||||
expect(cloudFormComponent).toBeDefined();
|
||||
expect(cloudFormComponent.customCompleteButtonText).toBe(customText);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Events', () => {
|
||||
@@ -254,6 +339,25 @@ describe('TaskFormCloudComponent', () => {
|
||||
|
||||
expect(component.displayModeOff.emit).toHaveBeenCalledWith(DisplayModeService.DEFAULT_DISPLAY_MODE_CONFIGURATIONS[0]);
|
||||
});
|
||||
|
||||
it('should emit formLoaded when form is loaded', () => {
|
||||
const mockForm = new FormModel();
|
||||
spyOn(component.formLoaded, 'emit').and.stub();
|
||||
component.onFormLoaded(mockForm);
|
||||
|
||||
expect(component.formLoaded.emit).toHaveBeenCalledOnceWith(mockForm);
|
||||
});
|
||||
|
||||
it('should handle formLoaded event from adf-cloud-form and re-emit it', () => {
|
||||
const mockForm = new FormModel();
|
||||
spyOn(component.formLoaded, 'emit').and.stub();
|
||||
|
||||
// Trigger the formLoaded event from the child adf-cloud-form component
|
||||
const cloudFormElement = fixture.debugElement.query((sel) => sel.name === 'adf-cloud-form');
|
||||
cloudFormElement.triggerEventHandler('formLoaded', mockForm);
|
||||
|
||||
expect(component.formLoaded.emit).toHaveBeenCalledOnceWith(mockForm);
|
||||
});
|
||||
});
|
||||
|
||||
it('should call children cloud task form change display mode when changing the display mode', () => {
|
||||
|
@@ -72,6 +72,31 @@ export class TaskFormCloudComponent {
|
||||
@Input()
|
||||
showCompleteButton = true;
|
||||
|
||||
/** Toggle rendering of the `Save` button. */
|
||||
@Input()
|
||||
showSaveButton = true;
|
||||
|
||||
/**
|
||||
* Custom text for the `Cancel` button.
|
||||
* If not provided, the default text will be used.
|
||||
*/
|
||||
@Input()
|
||||
customCancelButtonText: string = '';
|
||||
|
||||
/**
|
||||
* Custom text for the `Complete` button.
|
||||
* If not provided, the default text will be used.
|
||||
*/
|
||||
@Input()
|
||||
customCompleteButtonText: string = '';
|
||||
|
||||
/**
|
||||
* Custom text for the `Save` button.
|
||||
* If not provided, the default text will be used.
|
||||
*/
|
||||
@Input()
|
||||
customSaveButtonText: string = '';
|
||||
|
||||
/** Toggle readonly state of the task. */
|
||||
@Input()
|
||||
readOnly = false;
|
||||
@@ -86,6 +111,10 @@ export class TaskFormCloudComponent {
|
||||
@Input()
|
||||
taskDetails: TaskDetailsCloudModel;
|
||||
|
||||
/** Emitted when the form is loaded or reloaded. */
|
||||
@Output()
|
||||
formLoaded = new EventEmitter<FormModel>();
|
||||
|
||||
/** Emitted when the form is saved. */
|
||||
@Output()
|
||||
formSaved = new EventEmitter<FormModel>();
|
||||
@@ -224,4 +253,8 @@ export class TaskFormCloudComponent {
|
||||
onDisplayModeOff(displayModeConfiguration: FormCloudDisplayModeConfiguration) {
|
||||
this.displayModeOff.emit(displayModeConfiguration);
|
||||
}
|
||||
|
||||
onFormLoaded(form: FormModel) {
|
||||
this.formLoaded.emit(form);
|
||||
}
|
||||
}
|
||||
|
@@ -1,10 +1,5 @@
|
||||
<button
|
||||
*ngIf="showCancelButton"
|
||||
mat-button
|
||||
id="adf-cloud-cancel-task"
|
||||
(click)="onCancelClick()"
|
||||
>
|
||||
{{'ADF_CLOUD_TASK_FORM.EMPTY_FORM.BUTTONS.CANCEL' | translate}}
|
||||
<button *ngIf="showCancelButton" mat-button id="adf-cloud-cancel-task" (click)="onCancelClick()">
|
||||
{{ customCancelButtonText || ('ADF_CLOUD_TASK_FORM.EMPTY_FORM.BUTTONS.CANCEL' | translate) }}
|
||||
</button>
|
||||
<button
|
||||
*ngIf="canClaimTask"
|
||||
@@ -16,7 +11,7 @@
|
||||
(success)="onClaimTask()"
|
||||
(error)="onError($event)"
|
||||
>
|
||||
{{'ADF_CLOUD_TASK_FORM.EMPTY_FORM.BUTTONS.CLAIM' | translate}}
|
||||
{{ 'ADF_CLOUD_TASK_FORM.EMPTY_FORM.BUTTONS.CLAIM' | translate }}
|
||||
</button>
|
||||
<button
|
||||
*ngIf="canUnclaimTask"
|
||||
@@ -28,5 +23,5 @@
|
||||
(success)="onUnclaimTask()"
|
||||
(error)="onError($event)"
|
||||
>
|
||||
{{'ADF_CLOUD_TASK_FORM.EMPTY_FORM.BUTTONS.UNCLAIM' | translate}}
|
||||
{{ 'ADF_CLOUD_TASK_FORM.EMPTY_FORM.BUTTONS.UNCLAIM' | translate }}
|
||||
</button>
|
||||
|
@@ -71,6 +71,29 @@ describe('UserTaskCloudButtonsComponent', () => {
|
||||
expect(cancelClickSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should display custom text when customCancelButtonText is provided', async () => {
|
||||
const customText = 'Custom Cancel Text';
|
||||
fixture.componentRef.setInput('showCancelButton', true);
|
||||
fixture.componentRef.setInput('customCancelButtonText', customText);
|
||||
fixture.detectChanges();
|
||||
|
||||
const cancelButton: MatButtonHarness = await loader.getHarnessOrNull(MatButtonHarness.with({ selector: '#adf-cloud-cancel-task' }));
|
||||
const buttonText = await cancelButton.getText();
|
||||
|
||||
expect(buttonText.trim()).toBe(customText);
|
||||
});
|
||||
|
||||
it('should display default text when customCancelButtonText is not provided', async () => {
|
||||
fixture.componentRef.setInput('showCancelButton', true);
|
||||
fixture.componentRef.setInput('customCancelButtonText', '');
|
||||
fixture.detectChanges();
|
||||
|
||||
const cancelButton: MatButtonHarness = await loader.getHarnessOrNull(MatButtonHarness.with({ selector: '#adf-cloud-cancel-task' }));
|
||||
const buttonText = await cancelButton.getText();
|
||||
|
||||
expect(buttonText.trim()).toBe('ADF_CLOUD_TASK_FORM.EMPTY_FORM.BUTTONS.CANCEL');
|
||||
});
|
||||
|
||||
it('should show claim button', async () => {
|
||||
let claimButton: MatButtonHarness = await loader.getHarnessOrNull(MatButtonHarness.with({ selector: '.adf-user-task-cloud-claim-btn' }));
|
||||
|
||||
|
@@ -49,6 +49,13 @@ export class UserTaskCloudButtonsComponent {
|
||||
@Input()
|
||||
showCancelButton = true;
|
||||
|
||||
/**
|
||||
* Custom text for the `Cancel` button.
|
||||
* If not provided, the default text will be used.
|
||||
*/
|
||||
@Input()
|
||||
customCancelButtonText: string = '';
|
||||
|
||||
/** Emitted when any error occurs. */
|
||||
@Output() error = new EventEmitter<any>();
|
||||
|
||||
|
@@ -1,4 +1,4 @@
|
||||
<div class="adf-user-task-cloud-container">
|
||||
<div class="adf-user-task-cloud-container">
|
||||
<div *ngIf="!loading; else loadingTemplate">
|
||||
<ng-container [ngSwitch]="taskType">
|
||||
<ng-container *ngSwitchCase="taskTypeEnum.Form">
|
||||
@@ -12,9 +12,16 @@
|
||||
[showTitle]="showTitle"
|
||||
[taskId]="taskId"
|
||||
[taskDetails]="taskDetails"
|
||||
[showCancelButton]="showCancelButton"
|
||||
[showSaveButton]="showSaveButton && canCompleteTask()"
|
||||
[showCompleteButton]="showCompleteButton && canCompleteTask()"
|
||||
[customCancelButtonText]="customCancelButtonText"
|
||||
[customSaveButtonText]="customSaveButtonText"
|
||||
[customCompleteButtonText]="customCompleteButtonText"
|
||||
(cancelClick)="onCancelForm()"
|
||||
(executeOutcome)="onExecuteOutcome($event)"
|
||||
(error)="onError($event)"
|
||||
(formLoaded)="onFormLoaded($event)"
|
||||
(formSaved)="onFormSaved()"
|
||||
(formContentClicked)="onFormContentClicked($event)"
|
||||
(taskCompleted)="onCompleteTaskForm()"
|
||||
@@ -37,7 +44,6 @@
|
||||
[taskId]="taskId"
|
||||
[showNextTaskCheckbox]="showNextTaskCheckbox && canCompleteTask()"
|
||||
[isNextTaskCheckboxChecked]="isNextTaskCheckboxChecked"
|
||||
|
||||
(cancelTask)="onCancelClick()"
|
||||
(claimTask)="onClaimTask()"
|
||||
(error)="onError($event)"
|
||||
@@ -79,7 +85,7 @@
|
||||
color="primary"
|
||||
id="adf-form-complete"
|
||||
>
|
||||
{{ 'ADF_CLOUD_TASK_FORM.EMPTY_FORM.BUTTONS.COMPLETE' | translate }}
|
||||
{{ customCompleteButtonText || ('ADF_CLOUD_TASK_FORM.EMPTY_FORM.BUTTONS.COMPLETE' | translate) }}
|
||||
</button>
|
||||
</mat-card-actions>
|
||||
</mat-card>
|
||||
@@ -98,6 +104,7 @@
|
||||
[canClaimTask]="canClaimTask()"
|
||||
[canUnclaimTask]="canUnclaimTask()"
|
||||
[showCancelButton]="showCancelButton"
|
||||
[customCancelButtonText]="customCancelButtonText"
|
||||
[taskId]="taskId"
|
||||
(cancelClick)="onCancelClick()"
|
||||
(claimTask)="onClaimTask()"
|
||||
|
@@ -39,6 +39,8 @@ import {
|
||||
} from '../../../models/task-details-cloud.model';
|
||||
import { TaskFormCloudComponent } from '../task-form-cloud/task-form-cloud.component';
|
||||
import { TaskCloudService } from '../../../services/task-cloud.service';
|
||||
import { FormModel } from '../../../../../../../core';
|
||||
import { UserTaskCloudButtonsComponent } from '../user-task-cloud-buttons/user-task-cloud-buttons.component';
|
||||
|
||||
const taskDetails: TaskDetailsCloudModel = {
|
||||
appName: 'simple-app',
|
||||
@@ -591,4 +593,168 @@ describe('UserTaskCloudComponent', () => {
|
||||
prepareTestCase({ showNextTaskCheckbox: true, showCompleteButton: true, readOnly: false, canCompleteTask: true });
|
||||
expect(isCheckboxShown()).toBeTrue();
|
||||
});
|
||||
|
||||
describe('Input Properties - Button Controls', () => {
|
||||
beforeEach(() => {
|
||||
taskDetails.formKey = 'my-form';
|
||||
component.taskDetails = { ...taskDetails };
|
||||
component.getTaskType();
|
||||
component.taskId = 'taskId';
|
||||
component.appName = 'app';
|
||||
});
|
||||
|
||||
describe('showSaveButton', () => {
|
||||
it('should pass showSaveButton to task form when task type is Form', () => {
|
||||
fixture.componentRef.setInput('showSaveButton', false);
|
||||
fixture.detectChanges();
|
||||
|
||||
const taskFormElement = fixture.debugElement.query(By.css('adf-cloud-task-form'));
|
||||
const taskFormComponent = taskFormElement?.componentInstance as TaskFormCloudComponent;
|
||||
|
||||
expect(taskFormComponent.showSaveButton).toBe(false);
|
||||
});
|
||||
|
||||
it('should pass showSaveButton as true by default to task form', () => {
|
||||
fixture.detectChanges();
|
||||
|
||||
const taskFormElement = fixture.debugElement.query(By.css('adf-cloud-task-form'));
|
||||
const taskFormComponent = taskFormElement?.componentInstance as TaskFormCloudComponent;
|
||||
|
||||
expect(taskFormComponent.showSaveButton).toBe(true);
|
||||
});
|
||||
|
||||
it('should only show save button when canCompleteTask returns true', () => {
|
||||
spyOn(component, 'canCompleteTask').and.returnValue(false);
|
||||
fixture.detectChanges();
|
||||
|
||||
const taskFormElement = fixture.debugElement.query(By.css('adf-cloud-task-form'));
|
||||
const taskFormComponent = taskFormElement?.componentInstance as TaskFormCloudComponent;
|
||||
|
||||
expect(taskFormComponent.showSaveButton).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('showCompleteButton', () => {
|
||||
it('should pass showCompleteButton to task form when task type is Form', () => {
|
||||
fixture.componentRef.setInput('showCompleteButton', false);
|
||||
fixture.detectChanges();
|
||||
|
||||
const taskFormElement = fixture.debugElement.query(By.css('adf-cloud-task-form'));
|
||||
const taskFormComponent = taskFormElement?.componentInstance as TaskFormCloudComponent;
|
||||
|
||||
expect(taskFormComponent.showCompleteButton).toBe(false);
|
||||
});
|
||||
|
||||
it('should only show complete button when canCompleteTask returns true', () => {
|
||||
spyOn(component, 'canCompleteTask').and.returnValue(false);
|
||||
fixture.detectChanges();
|
||||
|
||||
const taskFormElement = fixture.debugElement.query(By.css('adf-cloud-task-form'));
|
||||
const taskFormComponent = taskFormElement?.componentInstance as TaskFormCloudComponent;
|
||||
|
||||
expect(taskFormComponent.showCompleteButton).toBe(false);
|
||||
});
|
||||
|
||||
it('should display custom complete button text in no form template', async () => {
|
||||
component.taskDetails.formKey = '';
|
||||
component.getTaskType();
|
||||
|
||||
const customText = 'Custom Complete Text';
|
||||
fixture.componentRef.setInput('customCompleteButtonText', customText);
|
||||
fixture.detectChanges();
|
||||
|
||||
const completeButton = await loader.getHarnessOrNull(MatButtonHarness.with({ selector: '#adf-form-complete' }));
|
||||
expect(completeButton).not.toBeNull();
|
||||
|
||||
const buttonText = await completeButton.getText();
|
||||
expect(buttonText).toBe(customText);
|
||||
});
|
||||
|
||||
it('should display default complete button text when customCompleteButtonText is not provided', async () => {
|
||||
component.taskDetails.formKey = '';
|
||||
component.getTaskType();
|
||||
const spy = spyOn(taskCloudService, 'canCompleteTask');
|
||||
spy.and.returnValue(true);
|
||||
fixture.detectChanges();
|
||||
|
||||
const completeButton = await loader.getHarnessOrNull(MatButtonHarness.with({ selector: '#adf-form-complete' }));
|
||||
expect(completeButton).not.toBeNull();
|
||||
|
||||
const buttonText = await completeButton.getText();
|
||||
expect(buttonText).toBe('ADF_CLOUD_TASK_FORM.EMPTY_FORM.BUTTONS.COMPLETE');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Custom button text', () => {
|
||||
it('should pass customCancelButtonText to task form when task type is Form', () => {
|
||||
const customText = 'Custom Cancel Text';
|
||||
fixture.componentRef.setInput('customCancelButtonText', customText);
|
||||
component.ngOnChanges({ appName: new SimpleChange(null, 'app1', false) });
|
||||
fixture.detectChanges();
|
||||
|
||||
const taskFormElement = fixture.debugElement.query(By.css('adf-cloud-task-form'));
|
||||
const taskFormComponent = taskFormElement?.componentInstance as TaskFormCloudComponent;
|
||||
|
||||
expect(taskFormComponent.customCancelButtonText).toBe(customText);
|
||||
});
|
||||
|
||||
it('should pass customCancelButtonText to adf-cloud-user-task-cloud-buttons', () => {
|
||||
component.taskDetails.formKey = '';
|
||||
component.getTaskType();
|
||||
|
||||
const customText = 'Custom Cancel Text';
|
||||
fixture.componentRef.setInput('customCancelButtonText', customText);
|
||||
fixture.detectChanges();
|
||||
|
||||
const buttonsElement = fixture.debugElement.query(By.css('adf-cloud-user-task-cloud-buttons'));
|
||||
const buttonsComponent = buttonsElement?.componentInstance as UserTaskCloudButtonsComponent;
|
||||
|
||||
expect(buttonsComponent.customCancelButtonText).toBe(customText);
|
||||
});
|
||||
|
||||
it('should pass customSaveButtonText to task form when task type is Form', () => {
|
||||
const customText = 'Custom Save Text';
|
||||
fixture.componentRef.setInput('customSaveButtonText', customText);
|
||||
fixture.detectChanges();
|
||||
|
||||
const taskFormElement = fixture.debugElement.query(By.css('adf-cloud-task-form'));
|
||||
const taskFormComponent = taskFormElement?.componentInstance as TaskFormCloudComponent;
|
||||
|
||||
expect(taskFormComponent.customSaveButtonText).toBe(customText);
|
||||
});
|
||||
|
||||
it('should pass customCompleteButtonText to task form when task type is Form', () => {
|
||||
const customText = 'Custom Complete Text';
|
||||
fixture.componentRef.setInput('customCompleteButtonText', customText);
|
||||
fixture.detectChanges();
|
||||
|
||||
const taskFormElement = fixture.debugElement.query(By.css('adf-cloud-task-form'));
|
||||
const taskFormComponent = taskFormElement?.componentInstance as TaskFormCloudComponent;
|
||||
|
||||
expect(taskFormComponent.customCompleteButtonText).toBe(customText);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should emit formLoaded when task form emits formLoaded event', () => {
|
||||
const mockForm = new FormModel();
|
||||
|
||||
taskDetails.formKey = 'my-form';
|
||||
component.taskDetails = { ...taskDetails };
|
||||
component.getTaskType();
|
||||
component.taskId = 'taskId';
|
||||
component.appName = 'app';
|
||||
|
||||
spyOn(component.formLoaded, 'emit');
|
||||
|
||||
component.ngOnChanges({ appName: new SimpleChange(null, 'app1', false) });
|
||||
fixture.detectChanges();
|
||||
|
||||
const taskFormElement = fixture.debugElement.query(By.css('adf-cloud-task-form'));
|
||||
const taskFormComponent = taskFormElement?.componentInstance as TaskFormCloudComponent;
|
||||
|
||||
taskFormComponent.formLoaded.emit(mockForm);
|
||||
|
||||
expect(component.formLoaded.emit).toHaveBeenCalledWith(mockForm);
|
||||
});
|
||||
});
|
||||
|
@@ -86,6 +86,31 @@ export class UserTaskCloudComponent implements OnInit, OnChanges {
|
||||
@Input()
|
||||
showCompleteButton = true;
|
||||
|
||||
/** Toggle rendering of the `Save` button. */
|
||||
@Input()
|
||||
showSaveButton = true;
|
||||
|
||||
/**
|
||||
* Custom text for the `Cancel` button.
|
||||
* If not provided, the default text will be used.
|
||||
*/
|
||||
@Input()
|
||||
customCancelButtonText: string = '';
|
||||
|
||||
/**
|
||||
* Custom text for the `Complete` button.
|
||||
* If not provided, the default text will be used.
|
||||
*/
|
||||
@Input()
|
||||
customCompleteButtonText: string = '';
|
||||
|
||||
/**
|
||||
* Custom text for the `Save` button.
|
||||
* If not provided, the default text will be used.
|
||||
*/
|
||||
@Input()
|
||||
customSaveButtonText: string = '';
|
||||
|
||||
/** Toggle rendering of the `Open next task` checkbox (for screens only). */
|
||||
@Input()
|
||||
showNextTaskCheckbox = false;
|
||||
@@ -129,6 +154,10 @@ export class UserTaskCloudComponent implements OnInit, OnChanges {
|
||||
@Output()
|
||||
formContentClicked: EventEmitter<ContentLinkModel> = new EventEmitter();
|
||||
|
||||
/** Emitted when the form is loaded or reloaded. */
|
||||
@Output()
|
||||
formLoaded = new EventEmitter<FormModel>();
|
||||
|
||||
/** Emitted when the form is saved. */
|
||||
@Output()
|
||||
formSaved = new EventEmitter<FormModel>();
|
||||
@@ -248,6 +277,10 @@ export class UserTaskCloudComponent implements OnInit, OnChanges {
|
||||
this.error.emit(data);
|
||||
}
|
||||
|
||||
onFormLoaded(form: FormModel) {
|
||||
this.formLoaded.emit(form);
|
||||
}
|
||||
|
||||
onExecuteOutcome(outcome: FormOutcomeEvent): void {
|
||||
this.executeOutcome.emit(outcome);
|
||||
}
|
||||
|
@@ -323,7 +323,7 @@ export class FormComponent extends FormBaseComponent implements OnInit, OnChange
|
||||
* @returns list of form outcomes
|
||||
*/
|
||||
getFormDefinitionOutcomes(form: FormModel): FormOutcomeModel[] {
|
||||
return [new FormOutcomeModel(form, { id: '$save', name: FormOutcomeModel.SAVE_ACTION, isSystem: true })];
|
||||
return [new FormOutcomeModel(form, { id: FormModel.SAVE_OUTCOME, name: FormOutcomeModel.SAVE_ACTION, isSystem: true })];
|
||||
}
|
||||
|
||||
checkVisibility(field: FormFieldModel) {
|
||||
|
Reference in New Issue
Block a user