AAE-26215 standalone form (cloud) (#10535)

This commit is contained in:
Denys Vuika 2025-01-08 07:53:56 -05:00 committed by GitHub
parent 27faf1d2ba
commit b7881db56b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
67 changed files with 453 additions and 642 deletions

View File

@ -34,10 +34,6 @@ describe('AvatarComponent', () => {
const getAvatarImageElement = (): HTMLImageElement => fixture.nativeElement.querySelector('.adf-avatar__image');
it('should create', () => {
expect(component).toBeTruthy();
});
it('should display initials when src is not provided', () => {
component.src = '';
fixture.detectChanges();

View File

@ -24,6 +24,8 @@ import { FormFieldModel, FormFieldValidator, FormModel, FormOutcomeEvent, FormOu
})
// eslint-disable-next-line @angular-eslint/directive-class-suffix
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';
@ -96,7 +98,27 @@ export abstract class FormBaseComponent {
@Output()
error = new EventEmitter<any>();
form: FormModel;
/**
* Custom style that is backed by the form.theme.
*/
formStyle: string = '';
get form(): FormModel {
return this._form;
}
/** Underlying form model instance. */
@Input()
set form(form: FormModel) {
this._form = form;
if (form) {
const theme = form.theme?.form;
this.formStyle = theme ? this.flattenStyles(theme) : '';
} else {
this.formStyle = '';
}
}
getParsedFormDefinition(): FormBaseComponent {
return this;
@ -226,4 +248,10 @@ export abstract class FormBaseComponent {
protected abstract storeFormAsMetadata(): void;
protected abstract onExecuteOutcome(outcome: FormOutcomeModel): boolean;
private flattenStyles(styles: { [key: string]: string }): string {
return Object.entries(styles)
.map(([key, value]) => `${key}: ${value}`)
.join(';');
}
}

View File

@ -47,3 +47,4 @@ export * from './form-field-variable-options';
export * from './widget-schema.model';
export * from './theme.model';
export * from './predefined-theme';
export * from './displayable-cm-properties.model';

View File

@ -57,7 +57,7 @@ export * from './text/text-mask.component';
export * from './display-text';
export * from './header';
export const WIDGET_DIRECTIVES: any[] = [
export const WIDGET_DIRECTIVES = [
UnknownWidgetComponent,
TextWidgetComponent,
NumberWidgetComponent,
@ -72,6 +72,6 @@ export const WIDGET_DIRECTIVES: any[] = [
DateTimeWidgetComponent,
JsonWidgetComponent,
BaseViewerWidgetComponent
];
] as const;
export const MASK_DIRECTIVE: any[] = [InputMaskDirective];
export const MASK_DIRECTIVE = [InputMaskDirective] as const;

View File

@ -1,47 +0,0 @@
/*!
* @license
* Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { FormStylePipe } from './form-style.pipe';
import { ThemeModel } from '../components/widgets/core/theme.model';
describe('FormStylePipe', () => {
let pipe: FormStylePipe;
beforeEach(() => {
pipe = new FormStylePipe();
});
it('should transform form theme into styles', () => {
const formTheme: ThemeModel = {
form: {
'--adf-form-label-font-size': '16px',
'--adf-form-label-color': 'black',
'--adf-form-label-font-weight': 'bold'
}
};
const result = pipe.transform(formTheme);
expect(result).toEqual('--adf-form-label-font-size: 16px;--adf-form-label-color: black;--adf-form-label-font-weight: bold');
});
it('should return an empty string if form theme is undefined', () => {
const result = pipe.transform(undefined);
expect(result).toEqual('');
});
});

View File

@ -1,36 +0,0 @@
/*!
* @license
* Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Pipe, PipeTransform } from '@angular/core';
import { ThemeModel } from '../components/widgets/core/theme.model';
@Pipe({
name: 'adfFormStyle',
standalone: true
})
export class FormStylePipe implements PipeTransform {
transform(formTheme?: ThemeModel): string {
const theme = formTheme?.form;
return theme ? this.flattenStyles(theme) : '';
}
private flattenStyles(styles: { [key: string]: string }): string {
return Object.entries(styles)
.map(([key, value]) => `${key}: ${value}`)
.join(';');
}
}

View File

@ -16,4 +16,3 @@
*/
export * from './field-style.pipe';
export * from './form-style.pipe';

View File

@ -38,10 +38,6 @@ describe('NavbarItemComponent', () => {
button = fixture.nativeElement.querySelector('.adf-navbar-item-btn');
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should display label', () => {
fixture.detectChanges();
expect(button.textContent).toContain('Test Label');

View File

@ -40,10 +40,6 @@ describe('ProgressComponent', () => {
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should default to bar variant and indeterminate mode', () => {
expect(component.variant).toBe('bar');
expect(component.mode).toBe('indeterminate');

View File

@ -36,18 +36,21 @@ export class CloudFormRenderingService extends FormRenderingService {
constructor() {
super();
this.register({
[FormFieldTypes.UPLOAD]: () => AttachFileCloudWidgetComponent,
[FormFieldTypes.DROPDOWN]: () => DropdownCloudWidgetComponent,
[FormFieldTypes.DATE]: () => DateCloudWidgetComponent,
[FormFieldTypes.PEOPLE]: () => PeopleCloudWidgetComponent,
[FormFieldTypes.FUNCTIONAL_GROUP]: () => GroupCloudWidgetComponent,
[FormFieldTypes.PROPERTIES_VIEWER]: () => PropertiesViewerWidgetComponent,
[FormFieldTypes.RADIO_BUTTONS]: () => RadioButtonsCloudWidgetComponent,
[FormFieldTypes.ALFRESCO_FILE_VIEWER]: () => FileViewerWidgetComponent,
[FormFieldTypes.DISPLAY_RICH_TEXT]: () => DisplayRichTextWidgetComponent,
[FormFieldTypes.DATA_TABLE]: () => DataTableWidgetComponent,
[FormFieldTypes.DISPLAY_EXTERNAL_PROPERTY]: () => DisplayExternalPropertyWidgetComponent
}, true);
this.register(
{
[FormFieldTypes.UPLOAD]: () => AttachFileCloudWidgetComponent,
[FormFieldTypes.DROPDOWN]: () => DropdownCloudWidgetComponent,
[FormFieldTypes.DATE]: () => DateCloudWidgetComponent,
[FormFieldTypes.PEOPLE]: () => PeopleCloudWidgetComponent,
[FormFieldTypes.FUNCTIONAL_GROUP]: () => GroupCloudWidgetComponent,
[FormFieldTypes.PROPERTIES_VIEWER]: () => PropertiesViewerWidgetComponent,
[FormFieldTypes.RADIO_BUTTONS]: () => RadioButtonsCloudWidgetComponent,
[FormFieldTypes.ALFRESCO_FILE_VIEWER]: () => FileViewerWidgetComponent,
[FormFieldTypes.DISPLAY_RICH_TEXT]: () => DisplayRichTextWidgetComponent,
[FormFieldTypes.DATA_TABLE]: () => DataTableWidgetComponent,
[FormFieldTypes.DISPLAY_EXTERNAL_PROPERTY]: () => DisplayExternalPropertyWidgetComponent
},
true
);
}
}

View File

@ -21,10 +21,14 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { ProcessServiceCloudTestingModule } from '../../testing/process-service-cloud.testing.module';
import { FormCloudComponent } from './form-cloud.component';
import { FormCustomOutcomesComponent } from './form-cloud-custom-outcomes.component';
import { MatButtonModule } from '@angular/material/button';
@Component({
selector: 'adf-cloud-form-with-custom-outcomes',
template: ` <adf-cloud-form #adfCloudForm>
standalone: true,
imports: [FormCustomOutcomesComponent, FormCloudComponent, MatButtonModule],
template: `<adf-cloud-form #adfCloudForm>
<adf-cloud-form-custom-outcomes>
<button mat-button id="adf-custom-outcome-1" (click)="onCustomButtonOneClick()">CUSTOM-BUTTON-1</button>
<button mat-button id="adf-custom-outcome-2" (click)="onCustomButtonTwoClick()">CUSTOM-BUTTON-2</button>
@ -36,7 +40,6 @@ class FormCloudWithCustomOutComesComponent {
adfCloudForm: FormCloudComponent;
onCustomButtonOneClick() {}
onCustomButtonTwoClick() {}
}
@ -47,8 +50,7 @@ describe('FormCloudWithCustomOutComesComponent', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [ProcessServiceCloudTestingModule],
declarations: [FormCloudWithCustomOutComesComponent]
imports: [ProcessServiceCloudTestingModule, FormCloudWithCustomOutComesComponent]
});
fixture = TestBed.createComponent(FormCloudWithCustomOutComesComponent);
customComponent = fixture.componentInstance;
@ -58,8 +60,7 @@ describe('FormCloudWithCustomOutComesComponent', () => {
outcomes: [{ id: 'outcome-1', name: 'outcome 1' }]
};
const form = new FormModel(formRepresentation);
customComponent.adfCloudForm.form = form;
customComponent.adfCloudForm.form = new FormModel(formRepresentation);
fixture.detectChanges();
});

View File

@ -19,6 +19,7 @@ import { Component } from '@angular/core';
@Component({
selector: 'adf-cloud-form-custom-outcomes',
standalone: true,
template: '<ng-content></ng-content>'
})
export class FormCustomOutcomesComponent {}

View File

@ -6,7 +6,7 @@
<div
*ngIf="hasForm()"
class="adf-cloud-form-container adf-cloud-form-{{displayConfiguration?.options?.fullscreen ? 'fullscreen' : 'inline'}}-container"
[style]="form.theme | adfFormStyle">
[style]="formStyle">
<div class="adf-cloud-form-content"
[cdkTrapFocus]="displayConfiguration?.options?.trapFocus"
cdkTrapFocusAutoCapture>
@ -22,18 +22,18 @@
</div>
<adf-toolbar-divider *ngIf="displayConfiguration?.options?.displayCloseButton" />
<button
*ngIf="displayConfiguration?.options?.displayCloseButton"
class="adf-cloud-form-close-button"
data-automation-id="adf-toolbar-right-back"
[attr.aria-label]="'ADF_VIEWER.ACTIONS.CLOSE' | translate"
[attr.data-automation-id]="'adf-cloud-form-close-button'"
[title]="'ADF_VIEWER.ACTIONS.CLOSE' | translate"
mat-icon-button
title="{{ 'ADF_VIEWER.ACTIONS.CLOSE' | translate }}"
(click)="switchToDisplayMode()">
<mat-icon>close</mat-icon>
</button>
<button
*ngIf="displayConfiguration?.options?.displayCloseButton"
class="adf-cloud-form-close-button"
data-automation-id="adf-toolbar-right-back"
[attr.aria-label]="'ADF_VIEWER.ACTIONS.CLOSE' | translate"
[attr.data-automation-id]="'adf-cloud-form-close-button'"
[title]="'ADF_VIEWER.ACTIONS.CLOSE' | translate"
mat-icon-button
title="{{ 'ADF_VIEWER.ACTIONS.CLOSE' | translate }}"
(click)="switchToDisplayMode()">
<mat-icon>close</mat-icon>
</button>
</adf-toolbar>
<mat-card
@ -42,57 +42,54 @@
[class.adf-cloud-form-content-card-fullscreen]="displayMode === 'fullScreen'"
>
<div class="adf-cloud-form-content-card-container">
<mat-card-header *ngIf="showTitle || showRefreshButton || showValidationIcon">
<mat-card-title>
<h4>
<div *ngIf="showValidationIcon" class="adf-form-validation-button">
<i id="adf-valid-form-icon" class="material-icons"
*ngIf="form.isValid; else no_valid_form">check_circle</i>
<ng-template #no_valid_form>
<i id="adf-invalid-form-icon" class="material-icons adf-invalid-color">error</i>
</ng-template>
</div>
<div *ngIf="!displayConfiguration?.options?.fullscreen && findDisplayConfiguration('fullScreen')" class="adf-cloud-form-fullscreen-button">
<button mat-icon-button (click)="switchToDisplayMode('fullScreen')" [attr.data-automation-id]="'adf-cloud-form-fullscreen-button'">
<mat-icon>fullscreen</mat-icon>
</button>
</div>
<div *ngIf="showRefreshButton" class="adf-cloud-form-reload-button" [title]="'ADF_VIEWER.ACTIONS.FULLSCREEN' | translate">
<button mat-icon-button (click)="onRefreshClicked()" [attr.aria-label]="'ADF_VIEWER.ACTIONS.FULLSCREEN' | translate">
<mat-icon>refresh</mat-icon>
</button>
</div>
<span *ngIf="isTitleEnabled()" class="adf-cloud-form-title" [title]="form.taskName"
>{{form.taskName}}
<ng-container *ngIf="!form.taskName">
{{'FORM.FORM_RENDERER.NAMELESS_TASK' | translate}}
</ng-container>
</span>
</h4>
</mat-card-title>
</mat-card-header>
<mat-card-content class="adf-form-container-card-content">
<adf-form-renderer
[formDefinition]="form"
[readOnly]="readOnly"
/>
</mat-card-content>
<mat-card-actions *ngIf="form.hasOutcomes()" class="adf-form-mat-card-actions" align="end">
<ng-content select="adf-cloud-form-custom-outcomes"></ng-content>
<ng-container *ngFor="let outcome of form.outcomes">
<button
*ngIf="outcome.isVisible"
[id]="'adf-form-'+ outcome.name | formatSpace"
[color]="getColorForOutcome(outcome.name)"
mat-button
[disabled]="!isOutcomeButtonEnabled(outcome)"
[class.adf-form-hide-button]="!isOutcomeButtonVisible(outcome, form.readOnly)"
(click)="onOutcomeClicked(outcome)"
>
{{outcome.name | translate | uppercase }}
</button>
</ng-container>
</mat-card-actions>
<mat-card-header *ngIf="showTitle || showRefreshButton || showValidationIcon">
<mat-card-title>
<h4>
<div *ngIf="showValidationIcon" class="adf-form-validation-button">
<i id="adf-valid-form-icon" class="material-icons"
*ngIf="form.isValid; else no_valid_form">check_circle</i>
<ng-template #no_valid_form>
<i id="adf-invalid-form-icon" class="material-icons adf-invalid-color">error</i>
</ng-template>
</div>
<div *ngIf="!displayConfiguration?.options?.fullscreen && findDisplayConfiguration('fullScreen')" class="adf-cloud-form-fullscreen-button">
<button mat-icon-button (click)="switchToDisplayMode('fullScreen')" [attr.data-automation-id]="'adf-cloud-form-fullscreen-button'">
<mat-icon>fullscreen</mat-icon>
</button>
</div>
<div *ngIf="showRefreshButton" class="adf-cloud-form-reload-button" [title]="'ADF_VIEWER.ACTIONS.FULLSCREEN' | translate">
<button mat-icon-button (click)="onRefreshClicked()" [attr.aria-label]="'ADF_VIEWER.ACTIONS.FULLSCREEN' | translate">
<mat-icon>refresh</mat-icon>
</button>
</div>
<span *ngIf="isTitleEnabled()" class="adf-cloud-form-title" [title]="form.taskName"
>{{form.taskName}}
<ng-container *ngIf="!form.taskName">
{{'FORM.FORM_RENDERER.NAMELESS_TASK' | translate}}
</ng-container>
</span>
</h4>
</mat-card-title>
</mat-card-header>
<mat-card-content class="adf-form-container-card-content">
<adf-form-renderer [formDefinition]="form" [readOnly]="readOnly" />
</mat-card-content>
<mat-card-actions *ngIf="form.hasOutcomes()" class="adf-form-mat-card-actions" align="end">
<ng-content select="adf-cloud-form-custom-outcomes"></ng-content>
<ng-container *ngFor="let outcome of form.outcomes">
<button
*ngIf="outcome.isVisible"
[id]="'adf-form-'+ outcome.name | formatSpace"
[color]="getColorForOutcome(outcome.name)"
mat-button
[disabled]="!isOutcomeButtonEnabled(outcome)"
[class.adf-form-hide-button]="!isOutcomeButtonVisible(outcome, form.readOnly)"
(click)="onOutcomeClicked(outcome)"
>
{{outcome.name | translate | uppercase }}
</button>
</ng-container>
</mat-card-actions>
</div>
</mat-card>
</div>

View File

@ -32,7 +32,8 @@ import {
WidgetVisibilityService,
provideTranslations,
AuthModule,
FormFieldEvent
FormFieldEvent,
NoopTranslateModule
} from '@alfresco/adf-core';
import { Node } from '@alfresco/js-api';
import { ESCAPE } from '@angular/cdk/keycodes';
@ -46,7 +47,6 @@ import { By } from '@angular/platform-browser';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { TranslateModule, TranslateService } from '@ngx-translate/core';
import { Observable, of, throwError } from 'rxjs';
import { FormCloudModule } from '../form-cloud.module';
import {
cloudFormMock,
conditionalUploadWidgetsMock,
@ -98,7 +98,7 @@ describe('FormCloudComponent', () => {
const resolver = formRenderingService.getComponentTypeResolver(type);
const widgetType = resolver(null);
const factoryResolver: ComponentFactoryResolver = TestBed.inject(ComponentFactoryResolver);
const factoryResolver = TestBed.inject(ComponentFactoryResolver);
const factory = factoryResolver.resolveComponentFactory(widgetType);
const componentRef = factory.create(injector);
@ -107,7 +107,7 @@ describe('FormCloudComponent', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [ProcessServiceCloudTestingModule],
imports: [ProcessServiceCloudTestingModule, FormCloudComponent],
providers: [
{
provide: VersionCompatibilityService,
@ -1539,7 +1539,7 @@ describe('retrieve metadata on submit', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [AuthModule.forRoot({ useHash: true }), NoopAnimationsModule, TranslateModule.forRoot(), CoreModule.forRoot(), FormCloudModule],
imports: [AuthModule.forRoot({ useHash: true }), NoopAnimationsModule, NoopTranslateModule, CoreModule.forRoot(), FormCloudComponent],
providers: [
provideTranslations('app', 'resources'),
{

View File

@ -34,6 +34,7 @@ import {
ConfirmDialogComponent,
ContentLinkModel,
FORM_FIELD_VALIDATORS,
FormatSpacePipe,
FormBaseComponent,
FormEvent,
FormFieldModel,
@ -41,8 +42,11 @@ import {
FormModel,
FormOutcomeEvent,
FormOutcomeModel,
FormRendererComponent,
FormService,
FormValues,
ToolbarComponent,
ToolbarDividerComponent,
UploadWidgetContentLinkModel,
WidgetVisibilityService
} from '@alfresco/adf-core';
@ -55,9 +59,29 @@ import { FormCloudDisplayMode, FormCloudDisplayModeConfiguration } from '../../s
import { FormCloudSpinnerService } from '../services/spinner/form-cloud-spinner.service';
import { DisplayModeService } from '../services/display-mode.service';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { CommonModule } from '@angular/common';
import { TranslateModule } from '@ngx-translate/core';
import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card';
import { MatIconModule } from '@angular/material/icon';
import { A11yModule } from '@angular/cdk/a11y';
@Component({
selector: 'adf-cloud-form',
standalone: true,
imports: [
CommonModule,
TranslateModule,
FormatSpacePipe,
MatButtonModule,
MatCardModule,
FormRendererComponent,
MatIconModule,
ToolbarDividerComponent,
ToolbarComponent,
A11yModule
],
providers: [FormCloudSpinnerService],
templateUrl: './form-cloud.component.html',
styleUrls: ['./form-cloud.component.scss']
})
@ -78,10 +102,6 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges,
@Input()
processInstanceId: string;
/** Underlying form model instance. */
@Input()
form: FormModel;
/** Task id to fetch corresponding form and values. */
@Input()
taskId: string;

View File

@ -1,7 +1,7 @@
<mat-form-field class="adf-form-definition-selector">
<mat-label>{{'ADF_CLOUD_TASK_LIST.START_TASK.FORM.LABEL.FORM'|translate}}</mat-label>
<mat-label>{{'ADF_CLOUD_TASK_LIST.START_TASK.FORM.LABEL.FORM' | translate}}</mat-label>
<mat-select class="adf-form-selector-dropdown" (selectionChange)="onSelect($event)">
<mat-option [value]="''">{{'ADF_CLOUD_TASK_LIST.START_TASK.FORM.LABEL.NONE'|translate}}</mat-option>
<mat-option [value]="''">{{'ADF_CLOUD_TASK_LIST.START_TASK.FORM.LABEL.NONE' | translate}}</mat-option>
<mat-option *ngFor="let form of forms$ | async" [value]="form.id">{{ form.name }}</mat-option>
</mat-select>
</mat-form-field>

View File

@ -17,7 +17,6 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ProcessServiceCloudTestingModule } from '../../testing/process-service-cloud.testing.module';
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { FormDefinitionSelectorCloudComponent } from './form-definition-selector-cloud.component';
import { of } from 'rxjs';
import { FormDefinitionSelectorCloudService } from '../services/form-definition-selector-cloud.service';
@ -33,8 +32,7 @@ describe('FormDefinitionCloudComponent', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [ProcessServiceCloudTestingModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA]
imports: [ProcessServiceCloudTestingModule, FormDefinitionSelectorCloudComponent]
});
fixture = TestBed.createComponent(FormDefinitionSelectorCloudComponent);
service = TestBed.inject(FormDefinitionSelectorCloudService);

View File

@ -16,7 +16,6 @@
*/
import { applicationConfig, Meta, moduleMetadata, StoryFn } from '@storybook/angular';
import { FormCloudModule } from '../form-cloud.module';
import { FormDefinitionSelectorCloudComponent } from './form-definition-selector-cloud.component';
import { ProcessServicesCloudStoryModule } from '../../testing/process-services-cloud-story.module';
import { FormDefinitionSelectorCloudService } from '../services/form-definition-selector-cloud.service';
@ -28,7 +27,7 @@ export default {
title: 'Process Services Cloud/Form Cloud/Form Definition Selector Cloud',
decorators: [
moduleMetadata({
imports: [FormCloudModule],
imports: [FormDefinitionSelectorCloudComponent],
providers: [{ provide: FormDefinitionSelectorCloudService, useClass: FormDefinitionSelectorCloudServiceMock }]
}),
applicationConfig({

View File

@ -18,17 +18,19 @@
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { Observable } from 'rxjs';
import { FormDefinitionSelectorCloudService } from '../services/form-definition-selector-cloud.service';
import { MatSelectChange } from '@angular/material/select';
import { MatSelectChange, MatSelectModule } from '@angular/material/select';
import { FormRepresentation } from '../../services/form-fields.interfaces';
import { CommonModule } from '@angular/common';
import { TranslateModule } from '@ngx-translate/core';
@Component({
selector: 'adf-cloud-form-definition-selector',
standalone: true,
imports: [CommonModule, TranslateModule, MatSelectModule],
templateUrl: './form-definition-selector-cloud.component.html',
styleUrls: ['./form-definition-selector-cloud.component.scss']
})
export class FormDefinitionSelectorCloudComponent implements OnInit {
/** Name of the application. If specified, this shows the users who have access to the app. */
@Input()
appName: string = '';
@ -39,8 +41,7 @@ export class FormDefinitionSelectorCloudComponent implements OnInit {
forms$: Observable<FormRepresentation[]>;
constructor(private formDefinitionCloudService: FormDefinitionSelectorCloudService) {
}
constructor(private formDefinitionCloudService: FormDefinitionSelectorCloudService) {}
ngOnInit(): void {
this.forms$ = this.formDefinitionCloudService.getStandAloneTaskForms(this.appName);
@ -49,5 +50,4 @@ export class FormDefinitionSelectorCloudComponent implements OnInit {
onSelect(event: MatSelectChange) {
this.selectForm.emit(event.value);
}
}

View File

@ -16,8 +16,12 @@
*/
import { Component, Input } from '@angular/core';
import { TranslateModule } from '@ngx-translate/core';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
@Component({
standalone: true,
imports: [TranslateModule, MatProgressSpinnerModule],
templateUrl: './form-spinner.component.html',
styleUrls: ['./form-spinner.component.scss']
})

View File

@ -16,7 +16,6 @@
*/
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ContentCloudNodeSelectorService } from '../../../services/content-cloud-node-selector.service';
import { ProcessCloudContentService } from '../../../services/process-cloud-content.service';
import { AttachFileCloudWidgetComponent } from './attach-file-cloud-widget.component';
@ -61,16 +60,10 @@ import {
processVariables
} from '../../../mocks/attach-file-cloud-widget.mock';
import { ProcessServiceCloudTestingModule } from '../../../../testing/process-service-cloud.testing.module';
import { CUSTOM_ELEMENTS_SCHEMA, Injector, runInInjectionContext } from '@angular/core';
import {
ContentModule,
ContentNodeSelectorPanelService,
NewVersionUploaderDataAction,
NewVersionUploaderService
} from '@alfresco/adf-content-services';
import { Injector, runInInjectionContext } from '@angular/core';
import { ContentNodeSelectorPanelService, NewVersionUploaderDataAction, NewVersionUploaderService } from '@alfresco/adf-content-services';
import { By } from '@angular/platform-browser';
import { of, throwError } from 'rxjs';
import { FormCloudModule } from '../../../form-cloud.module';
const mockNodeToBeVersioned: any = {
isFile: true,
@ -150,8 +143,7 @@ describe('AttachFileCloudWidgetComponent', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [ProcessServiceCloudTestingModule, FormCloudModule, ContentModule.forRoot()],
schemas: [CUSTOM_ELEMENTS_SCHEMA]
imports: [ProcessServiceCloudTestingModule, AttachFileCloudWidgetComponent]
});
notificationService = TestBed.inject(NotificationService);
downloadService = TestBed.inject(DownloadService);

View File

@ -17,21 +17,19 @@
/* eslint-disable @angular-eslint/component-selector */
import { Component, EventEmitter, OnDestroy, OnInit, Output, ViewEncapsulation } from '@angular/core';
import { Component, EventEmitter, inject, OnDestroy, OnInit, Output, ViewEncapsulation } from '@angular/core';
import {
FormService,
ThumbnailService,
NotificationService,
FormValues,
ContentLinkModel,
AppConfigService,
UploadWidgetContentLinkModel,
DestinationFolderPath
DestinationFolderPath,
ErrorWidgetComponent
} from '@alfresco/adf-core';
import { Node, NodesApi, RelatedContentRepresentation } from '@alfresco/js-api';
import { ContentCloudNodeSelectorService } from '../../../services/content-cloud-node-selector.service';
import { ProcessCloudContentService } from '../../../services/process-cloud-content.service';
import { UploadCloudWidgetComponent } from './upload-cloud.widget';
import { UploadCloudWidgetComponent } from '../upload/upload-cloud.widget';
import { DestinationFolderPathModel, DestinationFolderPathType } from '../../../models/form-cloud-representation.model';
import {
AlfrescoApiService,
@ -41,15 +39,22 @@ import {
NewVersionUploaderService,
VersionManagerUploadData
} from '@alfresco/adf-content-services';
import { CommonModule } from '@angular/common';
import { TranslateModule } from '@ngx-translate/core';
import { MatIconModule } from '@angular/material/icon';
import { FilePropertiesTableCloudComponent } from './file-properties-table/file-properties-table-cloud.component';
import { MatButtonModule } from '@angular/material/button';
export const RETRIEVE_METADATA_OPTION = 'retrieveMetadata';
export const ALIAS_ROOT_FOLDER = '-root-';
export const ALIAS_USER_FOLDER = '-my-';
export const APP_NAME = '-appname-';
export const VALID_ALIAS = [ALIAS_ROOT_FOLDER, ALIAS_USER_FOLDER, '-shared-'];
const RETRIEVE_METADATA_OPTION = 'retrieveMetadata';
const ALIAS_ROOT_FOLDER = '-root-';
const ALIAS_USER_FOLDER = '-my-';
const APP_NAME = '-appname-';
const VALID_ALIAS = [ALIAS_ROOT_FOLDER, ALIAS_USER_FOLDER, '-shared-'];
@Component({
selector: 'adf-cloud-attach-file-cloud-widget',
standalone: true,
imports: [CommonModule, ErrorWidgetComponent, TranslateModule, MatIconModule, FilePropertiesTableCloudComponent, MatButtonModule],
templateUrl: './attach-file-cloud-widget.component.html',
styleUrls: ['./attach-file-cloud-widget.component.scss'],
host: {
@ -66,6 +71,12 @@ export const VALID_ALIAS = [ALIAS_ROOT_FOLDER, ALIAS_USER_FOLDER, '-shared-'];
encapsulation: ViewEncapsulation.None
})
export class AttachFileCloudWidgetComponent extends UploadCloudWidgetComponent implements OnInit, OnDestroy {
private contentNodeSelectorService = inject(ContentCloudNodeSelectorService);
private appConfigService = inject(AppConfigService);
private apiService = inject(AlfrescoApiService);
private contentNodeSelectorPanelService = inject(ContentNodeSelectorPanelService);
private newVersionUploaderService = inject(NewVersionUploaderService);
typeId = 'AttachFileCloudWidgetComponent';
rootNodeId = ALIAS_USER_FOLDER;
selectedNode: Node;
@ -81,18 +92,8 @@ export class AttachFileCloudWidgetComponent extends UploadCloudWidgetComponent i
}
displayedColumns = ['icon', 'fileName', 'action'];
constructor(
formService: FormService,
thumbnails: ThumbnailService,
processCloudContentService: ProcessCloudContentService,
notificationService: NotificationService,
private contentNodeSelectorService: ContentCloudNodeSelectorService,
private appConfigService: AppConfigService,
private apiService: AlfrescoApiService,
private contentNodeSelectorPanelService: ContentNodeSelectorPanelService,
private newVersionUploaderService: NewVersionUploaderService
) {
super(formService, thumbnails, processCloudContentService, notificationService);
constructor(formService: FormService) {
super(formService);
}
ngOnInit() {

View File

@ -29,16 +29,12 @@
</td>
</ng-container>
<ng-container *ngFor="let columnName of field?.params?.displayableCMProperties"
[matColumnDef]="columnName.name">
<th mat-header-cell *matHeaderCellDef>{{
columnName.title ? columnName.title : columnName.name | titlecase
}}
</th>
<ng-container *ngFor="let prop of field?.params?.displayableCMProperties" [matColumnDef]="prop.name">
<th mat-header-cell *matHeaderCellDef>{{prop.title ? prop.title : prop.name | titlecase }}</th>
<td mat-cell class="adf-file-properties-table-cell" *matCellDef="let row">
<span matLine id="{{'fileProperty-'+row?.id+'-'+columnName?.name}}" role="button" tabindex="0"
<span matLine id="{{'fileProperty-'+row?.id+'-'+prop?.name}}" role="button" tabindex="0"
(keyup.enter)="onRowClicked(row)"
(click)="onRowClicked(row)">{{ getColumnValue(row, columnName) }}</span>
(click)="onRowClicked(row)">{{ getColumnValue(row, prop) }}</span>
</td>
</ng-container>
@ -89,6 +85,5 @@
</ng-container>
<tr mat-header-row class="adf-file-properties-table-header-row" *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns"></tr>
</table>
</div>

View File

@ -17,21 +17,18 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ProcessServiceCloudTestingModule } from '../../../../testing/process-service-cloud.testing.module';
import { ProcessServiceCloudTestingModule } from '../../../../../testing/process-service-cloud.testing.module';
import { FilePropertiesTableCloudComponent } from './file-properties-table-cloud.component';
import { By } from '@angular/platform-browser';
import { MatTableModule } from '@angular/material/table';
import { MatIconModule } from '@angular/material/icon';
describe('FilePropertiesTableCloudComponent', () => {
let widget: FilePropertiesTableCloudComponent;
let fixture: ComponentFixture<FilePropertiesTableCloudComponent>;
beforeEach(async () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [ProcessServiceCloudTestingModule, MatTableModule, MatIconModule],
declarations: [FilePropertiesTableCloudComponent]
}).compileComponents();
imports: [ProcessServiceCloudTestingModule, FilePropertiesTableCloudComponent]
});
});
beforeEach(() => {

View File

@ -17,19 +17,43 @@
/* eslint-disable @angular-eslint/component-selector */
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { LocalizedDatePipe, ThumbnailService } from '@alfresco/adf-core';
import { Component, EventEmitter, inject, Input, Output } from '@angular/core';
import { LocalizedDatePipe, ThumbnailService, UploadDirective, DisplayableCMProperties } from '@alfresco/adf-core';
import { Node } from '@alfresco/js-api';
import { NewVersionUploaderDialogData } from '@alfresco/adf-content-services';
import { CommonModule } from '@angular/common';
import { MatIconModule } from '@angular/material/icon';
import { TranslateModule } from '@ngx-translate/core';
import { MatMenuModule } from '@angular/material/menu';
import { MatButtonModule } from '@angular/material/button';
import { MatTableModule } from '@angular/material/table';
import { MatLineModule } from '@angular/material/core';
import { MatListModule } from '@angular/material/list';
export const RETRIEVE_METADATA_OPTION = 'retrieveMetadata';
const RETRIEVE_METADATA_OPTION = 'retrieveMetadata';
@Component({
selector: 'adf-cloud-file-properties-table',
standalone: true,
imports: [
CommonModule,
MatIconModule,
TranslateModule,
MatMenuModule,
UploadDirective,
MatButtonModule,
MatTableModule,
MatLineModule,
MatListModule
],
providers: [LocalizedDatePipe],
templateUrl: './file-properties-table-cloud.component.html',
styleUrls: ['./file-properties-table-cloud.component.scss']
})
export class FilePropertiesTableCloudComponent {
private localizedDatePipe = inject(LocalizedDatePipe);
private thumbnailService = inject(ThumbnailService);
@Input()
uploadedFiles;
@ -61,12 +85,10 @@ export class FilePropertiesTableCloudComponent {
uploadNewFileVersion = new EventEmitter<NewVersionUploaderDialogData>();
@Output()
contentModelFileHandler: EventEmitter<any> = new EventEmitter<Node>();
contentModelFileHandler = new EventEmitter<Node>();
@Output()
removeAttachFile: EventEmitter<any> = new EventEmitter<any>();
constructor(private localizedDatePipe: LocalizedDatePipe, private thumbnailService: ThumbnailService) {}
removeAttachFile = new EventEmitter<Node>();
onRowClicked(file?: Node) {
this.rowClick.emit(file);
@ -90,11 +112,11 @@ export class FilePropertiesTableCloudComponent {
this.uploadNewFileVersion.emit(newVersionUploaderDialogData);
}
contentModelFormFileHandler(file?: any) {
contentModelFormFileHandler(file?: Node) {
this.contentModelFileHandler.emit(file);
}
onRemoveAttachFile(file: any) {
onRemoveAttachFile(file: Node) {
this.removeAttachFile.emit(file);
}
@ -102,17 +124,15 @@ export class FilePropertiesTableCloudComponent {
return this.thumbnailService.getMimeTypeIcon(mimeType);
}
getColumnValue(file, displayableCMProperty): string {
if (!file.properties[displayableCMProperty.prefixedName]) {
const fieldProperty = this.field.params.displayableCMProperties?.find((property) => property.name === displayableCMProperty.name);
return fieldProperty.defaultValue ? this.checkDateTypeAndTransform(displayableCMProperty.dataType, fieldProperty.defaultValue) : '--';
getColumnValue(file, prop: DisplayableCMProperties): string {
if (!file.properties[prop.prefixedName]) {
const fieldProperty = this.field.params.displayableCMProperties?.find((property) => property.name === prop.name);
return fieldProperty.defaultValue ? this.checkDateTypeAndTransform(prop.dataType, fieldProperty.defaultValue) : '--';
}
return file.properties[displayableCMProperty.prefixedName]
? this.checkDateTypeAndTransform(displayableCMProperty.dataType, file.properties[displayableCMProperty.prefixedName])
: '--';
return file.properties[prop.prefixedName] ? this.checkDateTypeAndTransform(prop.dataType, file.properties[prop.prefixedName]) : '--';
}
checkDateTypeAndTransform(dataType, value): string {
private checkDateTypeAndTransform(dataType: string, value: any): string {
if (dataType === 'd:date') {
return this.localizedDatePipe.transform(value);
} else if (dataType === 'd:datetime') {

View File

@ -81,7 +81,7 @@ describe('DisplayRichTextWidgetComponent', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [CoreTestingModule]
imports: [CoreTestingModule, DisplayRichTextWidgetComponent]
});
fixture = TestBed.createComponent(DisplayRichTextWidgetComponent);
widget = fixture.componentInstance;
@ -89,11 +89,6 @@ describe('DisplayRichTextWidgetComponent', () => {
widget.field = fakeFormField;
});
it('should create', () => {
fixture.detectChanges();
expect(widget).toBeTruthy();
});
it('should parse editorjs data to html', async () => {
const expectedHtml =
'<h1>Editor.js</h1><p class="ce-tune-alignment--left">Display some <font color="#ff1300">formatted</font> <mark class="cdx-marker">text</mark></p>';

View File

@ -24,6 +24,7 @@ import { DomSanitizer } from '@angular/platform-browser';
@Component({
selector: 'display-rich-text',
standalone: true,
templateUrl: './display-rich-text.widget.html',
styleUrls: ['./display-rich-text.widget.scss'],
host: {

View File

@ -15,11 +15,9 @@
* limitations under the License.
*/
import { TranslateModule } from '@ngx-translate/core';
import { FileViewerWidgetComponent } from './file-viewer.widget';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { FormModel, FormService, FormFieldModel } from '@alfresco/adf-core';
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { FormFieldModel, FormModel, FormService, NoopAuthModule, NoopTranslateModule } from '@alfresco/adf-core';
describe('FileViewerWidgetComponent', () => {
const fakeForm = new FormModel();
@ -44,10 +42,8 @@ describe('FileViewerWidgetComponent', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [TranslateModule.forRoot()],
declarations: [FileViewerWidgetComponent],
providers: [{ provide: FormService, useValue: formServiceStub }],
schemas: [CUSTOM_ELEMENTS_SCHEMA]
imports: [NoopTranslateModule, NoopAuthModule, FileViewerWidgetComponent],
providers: [{ provide: FormService, useValue: formServiceStub }]
});
formServiceStub = TestBed.inject(FormService);
@ -55,27 +51,21 @@ describe('FileViewerWidgetComponent', () => {
widget = fixture.componentInstance;
});
it('should set the file id corretly when the field value is an array', (done) => {
const fakeField = new FormFieldModel(fakeForm, { id: 'fakeField', value: [fakePngAnswer] });
widget.field = fakeField;
it('should set the file id corretly when the field value is an array', async () => {
widget.field = new FormFieldModel(fakeForm, { id: 'fakeField', value: [fakePngAnswer] });
fixture.detectChanges();
await fixture.whenStable();
fixture.whenStable().then(() => {
expect(widget.field.value).toBe('1933');
done();
});
expect(widget.field.value).toBe('1933');
});
it('should set the file id corretly when the field value is a string', (done) => {
const fakeField = new FormFieldModel(fakeForm, { id: 'fakeField', value: 'fakeValue' });
widget.field = fakeField;
it('should set the file id corretly when the field value is a string', async () => {
widget.field = new FormFieldModel(fakeForm, { id: 'fakeField', value: 'fakeValue' });
fixture.detectChanges();
await fixture.whenStable();
fixture.whenStable().then(() => {
expect(widget.field.value).toBe('fakeValue');
done();
});
expect(widget.field.value).toBe('fakeValue');
});
});

View File

@ -16,12 +16,16 @@
*/
import { Component, ViewEncapsulation } from '@angular/core';
import { FormService, BaseViewerWidgetComponent } from '@alfresco/adf-core';
import { FormService, BaseViewerWidgetComponent, ErrorWidgetComponent } from '@alfresco/adf-core';
import { AlfrescoViewerComponent } from '@alfresco/adf-content-services';
import { TranslateModule } from '@ngx-translate/core';
/* eslint-disable @angular-eslint/component-selector */
@Component({
selector: 'file-viewer-widget',
standalone: true,
imports: [ErrorWidgetComponent, AlfrescoViewerComponent, TranslateModule],
templateUrl: './file-viewer.widget.html',
styleUrls: ['./file-viewer.widget.scss'],
host: {

View File

@ -18,8 +18,9 @@
<label class="adf-label" *ngIf="!field.leftLabels" [attr.for]="field.id" label>{{field.name | translate }}<span class="adf-asterisk" *ngIf="isRequired()">*</span></label>
</adf-cloud-group>
<error-widget [error]="field.validationSummary" />
<error-widget class="adf-dropdown-required-message" *ngIf="isInvalidFieldRequired() && isTouched()"
required="{{ 'FORM.FIELD.REQUIRED' | translate }}" />
<error-widget
class="adf-dropdown-required-message" *ngIf="isInvalidFieldRequired() && isTouched()"
required="{{ 'FORM.FIELD.REQUIRED' | translate }}" />
</div>
</div>

View File

@ -19,7 +19,6 @@ import { FormFieldModel, FormFieldTypes, FormModel, IdentityGroupModel } from '@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { GroupCloudWidgetComponent } from './group-cloud.widget';
import { ProcessServiceCloudTestingModule } from '../../../../testing/process-service-cloud.testing.module';
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { HarnessLoader } from '@angular/cdk/testing';
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { MatChipHarness } from '@angular/material/chips/testing';
@ -32,9 +31,7 @@ describe('GroupCloudWidgetComponent', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [ProcessServiceCloudTestingModule],
declarations: [GroupCloudWidgetComponent],
schemas: [CUSTOM_ELEMENTS_SCHEMA]
imports: [ProcessServiceCloudTestingModule, GroupCloudWidgetComponent]
});
fixture = TestBed.createComponent(GroupCloudWidgetComponent);
widget = fixture.componentInstance;

View File

@ -16,17 +16,22 @@
*/
import { Component, DestroyRef, inject, OnInit, ViewEncapsulation } from '@angular/core';
import { FormService, WidgetComponent } from '@alfresco/adf-core';
import { ErrorWidgetComponent, FormService, WidgetComponent } from '@alfresco/adf-core';
import { UntypedFormControl } from '@angular/forms';
import { filter } from 'rxjs/operators';
import { ComponentSelectionMode } from '../../../../types';
import { IdentityGroupModel } from '../../../../group/models/identity-group.model';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { CommonModule } from '@angular/common';
import { TranslateModule } from '@ngx-translate/core';
import { GroupCloudModule } from '../../../../group/group-cloud.module';
/* eslint-disable @angular-eslint/component-selector */
@Component({
selector: 'group-cloud-widget',
standalone: true,
imports: [CommonModule, TranslateModule, ErrorWidgetComponent, GroupCloudModule],
templateUrl: './group-cloud.widget.html',
host: {
'(click)': 'event($event)',
@ -42,7 +47,6 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
encapsulation: ViewEncapsulation.None
})
export class GroupCloudWidgetComponent extends WidgetComponent implements OnInit {
typeId = 'GroupCloudWidgetComponent';
roles: string[];
mode: ComponentSelectionMode;
@ -65,9 +69,7 @@ export class GroupCloudWidgetComponent extends WidgetComponent implements OnInit
this.preSelectGroup = this.field.value ? this.field.value : [];
this.validate = this.field.readOnly ? false : true;
}
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
this.search = new UntypedFormControl({value: '', disabled: this.field.readOnly}, []),
this.search = new UntypedFormControl({ value: '', disabled: this.field.readOnly }, []);
this.search.statusChanges
.pipe(
filter((value: string) => value === 'INVALID'),

View File

@ -1,5 +1,7 @@
<div class="adf-dropdown-widget {{field.className}}"
[class.adf-invalid]="!field.isValid && isTouched()" [class.adf-readonly]="field.readOnly" [class.adf-left-label-input-container]="field.leftLabels">
[class.adf-invalid]="!field.isValid && isTouched()"
[class.adf-readonly]="field.readOnly"
[class.adf-left-label-input-container]="field.leftLabels">
<div *ngIf="field.leftLabels">
<label class="adf-label adf-left-label" [attr.for]="field.id">{{field.name | translate }}<span class="adf-asterisk" *ngIf="isRequired()">*</span></label>
</div>
@ -18,11 +20,16 @@
(blur)="markAsTouched()"
[attr.title]="field.tooltip"
>
<label class="adf-label" *ngIf="!field.leftLabels" [attr.for]="field.id" label>{{field.name | translate }}<span class="adf-asterisk" *ngIf="isRequired()">*</span></label>
<label class="adf-label"
*ngIf="!field.leftLabels"
[attr.for]="field.id" label
>{{field.name | translate }}<span class="adf-asterisk" *ngIf="isRequired()">*</span></label>
</adf-cloud-people>
<error-widget [error]="field.validationSummary" />
<error-widget class="adf-dropdown-required-message" *ngIf="isInvalidFieldRequired() && isTouched()"
required="{{ 'FORM.FIELD.REQUIRED' | translate }}" />
<error-widget
class="adf-dropdown-required-message"
*ngIf="isInvalidFieldRequired() && isTouched()"
required="{{ 'FORM.FIELD.REQUIRED' | translate }}" />
</div>
</div>

View File

@ -18,7 +18,6 @@
import { FormFieldModel, FormFieldTypes, FormModel, IdentityUserModel } from '@alfresco/adf-core';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { PeopleCloudWidgetComponent } from './people-cloud.widget';
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { ProcessServiceCloudTestingModule } from '../../../../testing/process-service-cloud.testing.module';
import { IdentityUserService } from '../../../../people/services/identity-user.service';
import { mockShepherdsPie, mockYorkshirePudding } from '../../../../people/mock/people-cloud.mock';
@ -35,9 +34,7 @@ describe('PeopleCloudWidgetComponent', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [ProcessServiceCloudTestingModule],
declarations: [PeopleCloudWidgetComponent],
schemas: [CUSTOM_ELEMENTS_SCHEMA]
imports: [ProcessServiceCloudTestingModule, PeopleCloudWidgetComponent]
});
identityUserService = TestBed.inject(IdentityUserService);
fixture = TestBed.createComponent(PeopleCloudWidgetComponent);

View File

@ -16,18 +16,23 @@
*/
import { Component, DestroyRef, inject, OnInit, ViewEncapsulation } from '@angular/core';
import { FormService, WidgetComponent } from '@alfresco/adf-core';
import { ErrorWidgetComponent, FormService, WidgetComponent } from '@alfresco/adf-core';
import { UntypedFormControl } from '@angular/forms';
import { filter } from 'rxjs/operators';
import { ComponentSelectionMode } from '../../../../types';
import { IdentityUserModel } from '../../../../people/models/identity-user.model';
import { IdentityUserService } from '../../../../people/services/identity-user.service';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { CommonModule } from '@angular/common';
import { TranslateModule } from '@ngx-translate/core';
import { PeopleCloudModule } from '../../../../people/people-cloud.module';
/* eslint-disable @angular-eslint/component-selector */
@Component({
selector: 'people-cloud-widget',
standalone: true,
imports: [CommonModule, TranslateModule, ErrorWidgetComponent, PeopleCloudModule],
templateUrl: './people-cloud.widget.html',
host: {
'(click)': 'event($event)',
@ -43,6 +48,7 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
encapsulation: ViewEncapsulation.None
})
export class PeopleCloudWidgetComponent extends WidgetComponent implements OnInit {
private identityUserService = inject(IdentityUserService);
typeId = 'PeopleCloudWidgetComponent';
appName: string;
@ -56,7 +62,7 @@ export class PeopleCloudWidgetComponent extends WidgetComponent implements OnIni
private readonly destroyRef = inject(DestroyRef);
constructor(formService: FormService, private identityUserService: IdentityUserService) {
constructor(formService: FormService) {
super(formService);
}
@ -70,7 +76,7 @@ export class PeopleCloudWidgetComponent extends WidgetComponent implements OnIni
this.validate = this.field.readOnly ? false : true;
}
this.search = new UntypedFormControl({value: '', disabled: this.field.readOnly}, []);
this.search = new UntypedFormControl({ value: '', disabled: this.field.readOnly }, []);
this.search.statusChanges
.pipe(
@ -94,7 +100,7 @@ export class PeopleCloudWidgetComponent extends WidgetComponent implements OnIni
if (this.field.selectLoggedUser && !this.field.value) {
const userInfo = this.identityUserService.getCurrentUserInfo();
this.preSelectUsers = [ userInfo ];
this.preSelectUsers = [userInfo];
this.onChangedUser(this.preSelectUsers);
}
}

View File

@ -16,13 +16,17 @@
*/
import { Component, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges, ViewEncapsulation } from '@angular/core';
import { PresetConfig, NodesApiService } from '@alfresco/adf-content-services';
import { PresetConfig, NodesApiService, ContentMetadataComponent } from '@alfresco/adf-content-services';
import { Node } from '@alfresco/js-api';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { CommonModule } from '@angular/common';
/* eslint-disable @angular-eslint/component-selector */
@Component({
selector: 'adf-properties-viewer-wrapper',
standalone: true,
imports: [CommonModule, MatProgressSpinnerModule, ContentMetadataComponent],
templateUrl: './properties-viewer-wrapper.component.html',
encapsulation: ViewEncapsulation.None
})

View File

@ -29,7 +29,7 @@ describe('PropertiesViewerWidgetComponent', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [ProcessServiceCloudTestingModule],
imports: [ProcessServiceCloudTestingModule, PropertiesViewerWrapperComponent],
providers: [NodesApiService, { provide: BasicPropertiesService, useValue: { getProperties: () => [] } }]
});
fixture = TestBed.createComponent(PropertiesViewerWrapperComponent);

View File

@ -20,7 +20,6 @@ import { FormFieldModel, FormModel } from '@alfresco/adf-core';
import { PropertiesViewerWidgetComponent } from './properties-viewer.widget';
import { ProcessServiceCloudTestingModule } from '../../../../testing/process-service-cloud.testing.module';
import { fakeNodeWithProperties } from '../../../mocks/attach-file-cloud-widget.mock';
import { PropertiesViewerWrapperComponent } from './properties-viewer-wrapper/properties-viewer-wrapper.component';
import { NodesApiService, BasicPropertiesService } from '@alfresco/adf-content-services';
import { of } from 'rxjs';
@ -47,8 +46,7 @@ describe('PropertiesViewerWidgetComponent', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [ProcessServiceCloudTestingModule],
declarations: [PropertiesViewerWrapperComponent],
imports: [ProcessServiceCloudTestingModule, PropertiesViewerWidgetComponent],
providers: [NodesApiService, { provide: BasicPropertiesService, useValue: { getProperties: () => [] } }]
});
fixture = TestBed.createComponent(PropertiesViewerWidgetComponent);

View File

@ -16,13 +16,18 @@
*/
import { Component, EventEmitter, Output, ViewEncapsulation } from '@angular/core';
import { BaseViewerWidgetComponent, FormService } from '@alfresco/adf-core';
import { BaseViewerWidgetComponent, ErrorWidgetComponent, FormService } from '@alfresco/adf-core';
import { Node } from '@alfresco/js-api';
import { PropertiesViewerWrapperComponent } from './properties-viewer-wrapper/properties-viewer-wrapper.component';
import { TranslateModule } from '@ngx-translate/core';
import { CommonModule } from '@angular/common';
/* eslint-disable @angular-eslint/component-selector */
@Component({
selector: 'adf-properties-viewer-widget',
standalone: true,
imports: [CommonModule, ErrorWidgetComponent, PropertiesViewerWrapperComponent, TranslateModule],
templateUrl: './properties-viewer.widget.html',
styleUrls: ['./properties-viewer.widget.scss'],
host: {
@ -39,7 +44,6 @@ import { Node } from '@alfresco/js-api';
encapsulation: ViewEncapsulation.None
})
export class PropertiesViewerWidgetComponent extends BaseViewerWidgetComponent {
@Output()
nodeContentLoaded: EventEmitter<Node> = new EventEmitter();

View File

@ -1,19 +0,0 @@
/*!
* @license
* Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export * from './radio-buttons-cloud.schema';
export * from './radio-buttons-cloud.widget';

View File

@ -1,8 +1,14 @@
<div class="adf-radio-buttons-widget-cloud {{field.className}}"
[class.adf-readonly]="field.readOnly" [id]="field.id">
<div [ngClass]="(field.alignmentType === 'vertical') ? 'adf-radio-button-container': 'adf-radio-button-container-horizontal'">
<label class="adf-label" [attr.for]="field.id">{{field.name | translate }}<span class="adf-asterisk" *ngIf="isRequired()">*</span></label>
<mat-radio-group [ngClass]="(field.alignmentType === 'vertical') ? 'adf-radio-group': 'adf-radio-group-horizontal'" class="adf-radio-group" [(ngModel)]="field.value" [disabled]="field.readOnly">
<label class="adf-label"
[attr.for]="field.id">{{field.name | translate }}<span class="adf-asterisk" *ngIf="isRequired()">*</span>
</label>
<mat-radio-group
[ngClass]="(field.alignmentType === 'vertical') ? 'adf-radio-group': 'adf-radio-group-horizontal'"
class="adf-radio-group"
[(ngModel)]="field.value"
[disabled]="field.readOnly">
<mat-radio-button
[title]="field.tooltip"
[id]="field.id + '-' + opt.id"

View File

@ -46,7 +46,7 @@ describe('RadioButtonsCloudWidgetComponent', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [ProcessServiceCloudTestingModule]
imports: [ProcessServiceCloudTestingModule, RadioButtonsCloudWidgetComponent]
});
formCloudService = TestBed.inject(FormCloudService);
formUtilsService = TestBed.inject(FormUtilsService);

View File

@ -18,14 +18,19 @@
/* eslint-disable @angular-eslint/component-selector */
import { Component, DestroyRef, inject, OnInit, ViewEncapsulation } from '@angular/core';
import { ErrorMessageModel, FormFieldOption, FormService, WidgetComponent } from '@alfresco/adf-core';
import { ErrorMessageModel, ErrorWidgetComponent, FormFieldOption, FormService, WidgetComponent } from '@alfresco/adf-core';
import { FormCloudService } from '../../../services/form-cloud.service';
import { TranslateService } from '@ngx-translate/core';
import { TranslateModule, TranslateService } from '@ngx-translate/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { FormUtilsService } from '../../../services/form-utils.service';
import { MatRadioModule } from '@angular/material/radio';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
@Component({
selector: 'radio-buttons-cloud-widget',
standalone: true,
imports: [CommonModule, ErrorWidgetComponent, MatRadioModule, TranslateModule, FormsModule],
templateUrl: './radio-buttons-cloud.widget.html',
styleUrls: ['./radio-buttons-cloud.widget.scss'],
host: {
@ -42,17 +47,16 @@ import { FormUtilsService } from '../../../services/form-utils.service';
encapsulation: ViewEncapsulation.None
})
export class RadioButtonsCloudWidgetComponent extends WidgetComponent implements OnInit {
private formCloudService = inject(FormCloudService);
private translateService = inject(TranslateService);
private formUtilsService = inject(FormUtilsService);
typeId = 'RadioButtonsCloudWidgetComponent';
restApiError: ErrorMessageModel;
private readonly destroyRef = inject(DestroyRef);
constructor(
public formService: FormService,
private readonly formCloudService: FormCloudService,
private readonly translateService: TranslateService,
private readonly formUtilsService: FormUtilsService
) {
constructor(formService: FormService) {
super(formService);
}
@ -68,16 +72,16 @@ export class RadioButtonsCloudWidgetComponent extends WidgetComponent implements
this.formCloudService
.getRestWidgetData(this.field.form.id, this.field.id, body)
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(
(result: FormFieldOption[]) => {
.subscribe({
next: (result) => {
this.field.options = result;
this.field.updateForm();
},
(err) => {
error: (err) => {
this.resetRestApiOptions();
this.handleError(err);
}
);
});
}
onOptionClick(optionSelected: any) {

View File

@ -9,8 +9,8 @@
<img matListItemLine class="adf-upload-widget__icon" [id]="'file-'+file.id+'-icon'"
[src]="getIcon(file.content.mimeType)" [alt]="mimeTypeIcon" (click)="fileClicked(file)"
(keyup.enter)="fileClicked(file)" role="button" tabindex="0" />
<span class="adf-upload-widget__button" matLine id="{{'file-'+file.id}}" (click)="fileClicked(file)" (keyup.enter)="fileClicked(file)"
role="button" tabindex="0" class="adf-file">{{file.name}}</span>
<span class="adf-upload-widget__button adf-file" matLine id="{{'file-'+file.id}}" (click)="fileClicked(file)" (keyup.enter)="fileClicked(file)"
role="button" tabindex="0">{{file.name}}</span>
<button *ngIf="!field.readOnly" mat-icon-button [id]="'file-'+file.id+'-remove'"
(click)="removeFile(file);" (keyup.enter)="removeFile(file);">
<mat-icon class="mat-24">highlight_off</mat-icon>

View File

@ -17,17 +17,25 @@
/* eslint-disable @angular-eslint/component-selector */
import { Component, ElementRef, EventEmitter, OnInit, Output, ViewChild, ViewEncapsulation } from '@angular/core';
import { Component, ElementRef, EventEmitter, inject, OnInit, Output, ViewChild, ViewEncapsulation } from '@angular/core';
import { Node } from '@alfresco/js-api';
import { Observable, from } from 'rxjs';
import { mergeMap } from 'rxjs/operators';
import { WidgetComponent, FormService, ThumbnailService, NotificationService } from '@alfresco/adf-core';
import { WidgetComponent, FormService, ThumbnailService, NotificationService, ErrorWidgetComponent } from '@alfresco/adf-core';
import { ProcessCloudContentService } from '../../../services/process-cloud-content.service';
import { FileSourceTypes, DestinationFolderPathType } from '../../../models/form-cloud-representation.model';
import { VersionManagerUploadData } from '@alfresco/adf-content-services';
import { CommonModule } from '@angular/common';
import { TranslateModule } from '@ngx-translate/core';
import { MatIconModule } from '@angular/material/icon';
import { MatButtonModule } from '@angular/material/button';
import { MatListModule } from '@angular/material/list';
import { MatLineModule } from '@angular/material/core';
@Component({
selector: 'upload-cloud-widget',
standalone: true,
imports: [CommonModule, TranslateModule, ErrorWidgetComponent, MatIconModule, MatButtonModule, MatListModule, MatLineModule],
templateUrl: './upload-cloud.widget.html',
styleUrls: ['./upload-cloud.widget.scss'],
host: {
@ -44,6 +52,10 @@ import { VersionManagerUploadData } from '@alfresco/adf-content-services';
encapsulation: ViewEncapsulation.None
})
export class UploadCloudWidgetComponent extends WidgetComponent implements OnInit {
protected thumbnailService = inject(ThumbnailService);
protected processCloudContentService = inject(ProcessCloudContentService);
protected notificationService = inject(NotificationService);
hasFile: boolean;
displayText: string;
multipleOption: string = '';
@ -55,12 +67,7 @@ export class UploadCloudWidgetComponent extends WidgetComponent implements OnIni
@ViewChild('uploadFiles')
fileInput: ElementRef;
constructor(
formService: FormService,
private thumbnailService: ThumbnailService,
protected processCloudContentService: ProcessCloudContentService,
protected notificationService: NotificationService
) {
constructor(formService: FormService) {
super(formService);
}
@ -101,16 +108,16 @@ export class UploadCloudWidgetComponent extends WidgetComponent implements OnIni
if (files && files.length > 0) {
from(files)
.pipe(mergeMap((file) => this.uploadRawContent(file)))
.subscribe(
(res) => {
.subscribe({
next: (res) => {
filesSaved.push(res);
},
(error) => this.widgetError.emit(`Error uploading file. See console output for more details. ${error}`),
() => {
error: (error) => this.widgetError.emit(`Error uploading file. See console output for more details. ${error}`),
complete: () => {
this.fixIncompatibilityFromPreviousAndNewForm(filesSaved);
this.hasFile = true;
}
);
});
}
}

View File

@ -16,86 +16,43 @@
*/
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { CoreModule, FormatSpacePipe, TOOLBAR_DIRECTIVES, FormStylePipe } from '@alfresco/adf-core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MaterialModule } from '../material.module';
import { FormCloudComponent } from './components/form-cloud.component';
import { FormDefinitionSelectorCloudComponent } from './components/form-definition-selector-cloud.component';
import { FormCustomOutcomesComponent } from './components/form-cloud-custom-outcomes.component';
import {
AlfrescoViewerComponent,
CONTENT_METADATA_DIRECTIVES,
CONTENT_UPLOAD_DIRECTIVES,
ContentNodeSelectorModule
} from '@alfresco/adf-content-services';
import { GroupCloudWidgetComponent } from './components/widgets/group/group-cloud.widget';
import { PeopleCloudWidgetComponent } from './components/widgets/people/people-cloud.widget';
import { AttachFileCloudWidgetComponent } from './components/widgets/attach-file/attach-file-cloud-widget.component';
import { UploadCloudWidgetComponent } from './components/widgets/attach-file/upload-cloud.widget';
import { PeopleCloudModule } from '../people/people-cloud.module';
import { GroupCloudModule } from '../group/group-cloud.module';
import { UploadCloudWidgetComponent } from './components/widgets/upload/upload-cloud.widget';
import { PropertiesViewerWidgetComponent } from './components/widgets/properties-viewer/properties-viewer.widget';
import { PropertiesViewerWrapperComponent } from './components/widgets/properties-viewer/properties-viewer-wrapper/properties-viewer-wrapper.component';
import { RadioButtonsCloudWidgetComponent } from './components/widgets/radio-buttons/radio-buttons-cloud.widget';
import { FilePropertiesTableCloudComponent } from './components/widgets/attach-file/file-properties-table-cloud.component';
import { FilePropertiesTableCloudComponent } from './components/widgets/attach-file/file-properties-table/file-properties-table-cloud.component';
import { FileViewerWidgetComponent } from './components/widgets/file-viewer/file-viewer.widget';
import { DisplayRichTextWidgetComponent } from './components/widgets/display-rich-text/display-rich-text.widget';
import { RichTextEditorComponent } from '../rich-text-editor';
import { A11yModule } from '@angular/cdk/a11y';
import { OverlayModule } from '@angular/cdk/overlay';
import { FormSpinnerComponent } from './components/spinner/form-spinner.component';
import { FormCloudSpinnerService } from './services/spinner/form-cloud-spinner.service';
export const FORM_CLOUD_DIRECTIVES = [
RichTextEditorComponent,
FormSpinnerComponent,
PropertiesViewerWrapperComponent,
PropertiesViewerWidgetComponent,
DisplayRichTextWidgetComponent,
FileViewerWidgetComponent,
FilePropertiesTableCloudComponent,
FormCustomOutcomesComponent,
FormDefinitionSelectorCloudComponent,
RadioButtonsCloudWidgetComponent,
AttachFileCloudWidgetComponent,
UploadCloudWidgetComponent,
PeopleCloudWidgetComponent,
GroupCloudWidgetComponent,
FormCloudComponent
] as const;
/** @deprecated use ...FORM_CLOUD_DIRECTIVES instead */
@NgModule({
imports: [
CommonModule,
OverlayModule,
MaterialModule,
FormsModule,
ReactiveFormsModule,
CoreModule,
ContentNodeSelectorModule,
PeopleCloudModule,
GroupCloudModule,
RichTextEditorComponent,
...TOOLBAR_DIRECTIVES,
A11yModule,
FormatSpacePipe,
AlfrescoViewerComponent,
...CONTENT_UPLOAD_DIRECTIVES,
...CONTENT_METADATA_DIRECTIVES,
FormStylePipe
],
declarations: [
FormCloudComponent,
UploadCloudWidgetComponent,
FormDefinitionSelectorCloudComponent,
FormCustomOutcomesComponent,
RadioButtonsCloudWidgetComponent,
AttachFileCloudWidgetComponent,
PeopleCloudWidgetComponent,
GroupCloudWidgetComponent,
PropertiesViewerWrapperComponent,
PropertiesViewerWidgetComponent,
FilePropertiesTableCloudComponent,
FileViewerWidgetComponent,
DisplayRichTextWidgetComponent,
FormSpinnerComponent
],
exports: [
FormCloudComponent,
UploadCloudWidgetComponent,
FormDefinitionSelectorCloudComponent,
FormCustomOutcomesComponent,
RadioButtonsCloudWidgetComponent,
AttachFileCloudWidgetComponent,
PeopleCloudWidgetComponent,
GroupCloudWidgetComponent,
PropertiesViewerWidgetComponent,
FileViewerWidgetComponent,
DisplayRichTextWidgetComponent
],
providers: [FormCloudSpinnerService]
imports: [...FORM_CLOUD_DIRECTIVES],
exports: [...FORM_CLOUD_DIRECTIVES]
})
export class FormCloudModule {}

View File

@ -1318,99 +1318,3 @@ export const fakeMetadataForm = {
variables: []
}
};
export const fakeViewerForm = {
id: 'form-de8895be-d0d7-4434-beef-559b15305d72',
name: 'StartEventForm',
description: '',
version: 0,
formDefinition: {
tabs: [],
fields: [
{
type: 'container',
id: '5a6b24c1-db2b-45e9-9aff-142395433d23',
name: 'Label',
tab: null,
fields: {
1: [
{
id: 'content_form_nodes',
name: 'Nodes',
type: 'upload',
readOnly: false,
required: true,
colspan: 1,
visibilityCondition: null,
params: {
existingColspan: 1,
maxColspan: 2,
fileSource: {
serviceId: 'alfresco-content',
name: 'Alfresco Content',
metadataAllowed: true
},
multiple: true,
menuOptions: {
show: true,
download: true,
retrieveMetadata: true,
remove: true
},
link: false
}
}
],
2: [
{
id: 'upload_widget',
name: 'Nodes',
type: 'upload',
readOnly: false,
required: true,
colspan: 1,
visibilityCondition: null,
params: {
existingColspan: 1,
maxColspan: 2,
fileSource: {
serviceId: 'alfresco-content',
name: 'Alfresco Content',
metadataAllowed: true
},
multiple: true,
menuOptions: {
show: true,
download: true,
retrieveMetadata: true,
remove: true
},
link: false
}
}
],
3: [
{
id: 'cmfb85b2a7295ba41209750bca176ccaf9a',
name: 'File viewer',
type: 'file-viewer',
readOnly: false,
required: false,
colspan: 1,
visibilityCondition: null,
params: {
existingColspan: 1,
maxColspan: 2,
uploadWidget: 'content_form_nodes'
}
}
]
},
numberOfColumns: 2
}
],
outcomes: [],
metadata: {},
variables: []
}
};

View File

@ -17,23 +17,27 @@
export * from './models/task-variable-cloud.model';
export * from './components/spinner/form-spinner.component';
export * from './components/form-cloud-custom-outcomes.component';
export * from './components/form-cloud.component';
export * from './components/form-definition-selector-cloud.component';
export * from './components/cloud-form-rendering.service';
export * from './components/widgets/attach-file/attach-file-cloud-widget.component';
export * from './components/widgets/attach-file/upload-cloud.widget';
export * from './components/widgets/attach-file/file-properties-table/file-properties-table-cloud.component';
export * from './components/widgets/upload/upload-cloud.widget';
export * from './components/widgets/date/date-cloud.widget';
export * from './components/widgets/dropdown/dropdown-cloud.widget';
export * from './components/widgets/group/group-cloud.widget';
export * from './components/widgets/people/people-cloud.widget';
export * from './components/widgets/properties-viewer/properties-viewer.widget';
export * from './components/widgets/properties-viewer/properties-viewer-wrapper/properties-viewer-wrapper.component';
export * from './components/widgets/file-viewer/file-viewer.widget';
export * from './components/widgets/display-rich-text/display-rich-text.widget';
// widgets with schema
export * from './components/widgets/radio-buttons';
export * from './components/widgets/radio-buttons/radio-buttons-cloud.widget';
export * from './components/widgets/radio-buttons/radio-buttons-cloud.schema';
export * from './services/content-cloud-node-selector.service';
export * from './services/form-cloud.service';

View File

@ -34,7 +34,7 @@ import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
template: `<div>adf-cloud-overlay-test</div>`
})
class SpinnerTestComponent {
destroyRef = inject(DestroyRef)
destroyRef = inject(DestroyRef);
}
describe('FormCloudSpinnerService', () => {
@ -47,10 +47,10 @@ describe('FormCloudSpinnerService', () => {
const showSpinnerEvent = new FormSpinnerEvent('toggle-spinner', { showSpinner: true, message: 'LOAD_SPINNER_MESSAGE' });
const hideSpinnerEvent = new FormSpinnerEvent('toggle-spinner', { showSpinner: false });
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [FormSpinnerComponent, SpinnerTestComponent],
imports: [OverlayModule, PortalModule, MatProgressSpinnerModule, TranslateModule.forRoot(), FormSpinnerComponent],
declarations: [SpinnerTestComponent],
providers: [
FormCloudSpinnerService,
{
@ -59,15 +59,14 @@ describe('FormCloudSpinnerService', () => {
toggleFormSpinner: new Subject()
}
}
],
imports: [OverlayModule, PortalModule, MatProgressSpinnerModule, TranslateModule.forRoot()]
]
});
fixture = TestBed.createComponent(SpinnerTestComponent);
rootLoader = TestbedHarnessEnvironment.documentRootLoader(fixture);
spinnerService = TestBed.inject(FormCloudSpinnerService);
formService = TestBed.inject(FormService);
destroyRef = fixture.componentInstance.destroyRef
destroyRef = fixture.componentInstance.destroyRef;
});
it('should toggle spinner', async () => {

View File

@ -21,7 +21,7 @@ import { APP_LIST_CLOUD_DIRECTIVES } from './app/app-list-cloud.module';
import { TaskCloudModule } from './task/task-cloud.module';
import { ProcessCloudModule } from './process/process-cloud.module';
import { GroupCloudModule } from './group/group-cloud.module';
import { FormCloudModule } from './form/form-cloud.module';
import { FORM_CLOUD_DIRECTIVES } from './form/form-cloud.module';
import { TaskFormModule } from './task/task-form/task-form.module';
import {
LocalPreferenceCloudService,
@ -44,7 +44,7 @@ import { RichTextEditorComponent } from './rich-text-editor';
TaskCloudModule,
GroupCloudModule,
PeopleCloudModule,
FormCloudModule,
...FORM_CLOUD_DIRECTIVES,
TaskFormModule,
ApolloModule,
RichTextEditorComponent
@ -55,7 +55,7 @@ import { RichTextEditorComponent } from './rich-text-editor';
ProcessCloudModule,
TaskCloudModule,
GroupCloudModule,
FormCloudModule,
...FORM_CLOUD_DIRECTIVES,
TaskFormModule,
PeopleCloudModule,
RichTextEditorComponent

View File

@ -18,13 +18,12 @@
import { NgModule } from '@angular/core';
import { ProcessFiltersCloudModule } from './process-filters/process-filters-cloud.module';
import { ProcessListCloudModule } from './process-list/process-list-cloud.module';
import { CoreModule, LocalizedDatePipe } from '@alfresco/adf-core';
import { CoreModule } from '@alfresco/adf-core';
import { StartProcessCloudComponent } from './start-process/components/start-process-cloud.component';
import { ProcessHeaderCloudComponent } from './process-header/components/process-header-cloud.component';
@NgModule({
imports: [CoreModule, ProcessFiltersCloudModule, ProcessListCloudModule, StartProcessCloudComponent, ProcessHeaderCloudComponent],
exports: [ProcessFiltersCloudModule, ProcessListCloudModule, StartProcessCloudComponent, ProcessHeaderCloudComponent],
providers: [LocalizedDatePipe]
exports: [ProcessFiltersCloudModule, ProcessListCloudModule, StartProcessCloudComponent, ProcessHeaderCloudComponent]
})
export class ProcessCloudModule {}

View File

@ -24,17 +24,14 @@ import { LocalPreferenceCloudService } from '../../services/local-preference-clo
import { PROCESS_LISTS_PREFERENCES_SERVICE_TOKEN } from '../../services/cloud-token.service';
@NgModule({
imports: [
CommonModule,
MaterialModule,
CoreModule
imports: [CommonModule, MaterialModule, CoreModule],
providers: [
{
provide: PROCESS_LISTS_PREFERENCES_SERVICE_TOKEN,
useClass: LocalPreferenceCloudService
}
],
providers: [{
provide: PROCESS_LISTS_PREFERENCES_SERVICE_TOKEN,
useClass: LocalPreferenceCloudService
}],
declarations: [ProcessListCloudComponent],
exports: [ProcessListCloudComponent]
})
export class ProcessListCloudModule { }
export class ProcessListCloudModule {}

View File

@ -22,14 +22,6 @@ import { of, throwError } from 'rxjs';
import { StartProcessCloudService } from '../services/start-process-cloud.service';
import { FormCloudService } from '../../../form/services/form-cloud.service';
import { StartProcessCloudComponent } from './start-process-cloud.component';
import { MatAutocompleteModule } from '@angular/material/autocomplete';
import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card';
import { MatRippleModule, MatCommonModule, MatOptionModule } from '@angular/material/core';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import {
fakeProcessDefinitions,
fakeStartForm,
@ -88,20 +80,7 @@ describe('StartProcessCloudComponent', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
ProcessServiceCloudTestingModule,
FormsModule,
MatCommonModule,
ReactiveFormsModule,
MatCardModule,
MatIconModule,
MatAutocompleteModule,
MatOptionModule,
MatButtonModule,
MatFormFieldModule,
MatInputModule,
MatRippleModule
]
imports: [ProcessServiceCloudTestingModule, StartProcessCloudComponent]
});
processService = TestBed.inject(StartProcessCloudService);
formCloudService = TestBed.inject(FormCloudService);

View File

@ -56,10 +56,11 @@ import { TranslateModule } from '@ngx-translate/core';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { MatCardModule } from '@angular/material/card';
import { MatButtonModule } from '@angular/material/button';
import { FormCloudModule } from '../../../form/form-cloud.module';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatOptionModule } from '@angular/material/core';
import { FormCloudComponent } from '../../../form/components/form-cloud.component';
import { FormCustomOutcomesComponent } from '../../../form/components/form-cloud-custom-outcomes.component';
const MAX_NAME_LENGTH: number = 255;
const PROCESS_DEFINITION_DEBOUNCE: number = 300;
@ -75,14 +76,16 @@ const PROCESS_DEFINITION_IDENTIFIER_REG_EXP = new RegExp('%{processdefinition}',
MatProgressSpinnerModule,
MatCardModule,
MatButtonModule,
FormCloudModule,
InplaceFormInputComponent,
MatIconModule,
MatInputModule,
MatOptionModule,
MatAutocompleteModule,
ReactiveFormsModule
ReactiveFormsModule,
FormCloudComponent,
FormCustomOutcomesComponent
],
providers: [LocalizedDatePipe],
templateUrl: './start-process-cloud.component.html',
styleUrls: ['./start-process-cloud.component.scss'],
encapsulation: ViewEncapsulation.None

View File

@ -16,9 +16,6 @@
*/
export * from './components/start-process-cloud.component';
export * from './models/process-instance-cloud.model';
export * from './models/process-payload-cloud.model';
export * from './services/start-process-cloud.service';
export * from './start-process-cloud.module';

View File

@ -1,26 +0,0 @@
/*!
* @license
* Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { NgModule } from '@angular/core';
import { StartProcessCloudComponent } from './components/start-process-cloud.component';
/** @deprecated use StartProcessCloudComponent instead */
@NgModule({
imports: [StartProcessCloudComponent],
exports: [StartProcessCloudComponent]
})
export class StartProcessCloudModule {}

View File

@ -70,8 +70,13 @@ describe('TaskFormCloudComponent', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [ProcessServiceCloudTestingModule],
declarations: [FormCloudComponent, UserTaskCloudButtonsComponent, FormCustomOutcomesComponent]
imports: [
ProcessServiceCloudTestingModule,
FormCloudComponent,
FormCustomOutcomesComponent,
UserTaskCloudButtonsComponent,
TaskFormCloudComponent
]
});
taskDetails.status = TASK_ASSIGNED_STATE;
taskDetails.permissions = [TASK_VIEW_PERMISSION];

View File

@ -18,7 +18,6 @@
import { applicationConfig, Meta, moduleMetadata, StoryFn } from '@storybook/angular';
import { FormCloudService } from '../../../../form/public-api';
import { TaskCloudService } from '../../../services/task-cloud.service';
import { TaskFormModule } from '../../task-form.module';
import { TaskFormCloudComponent } from './task-form-cloud.component';
import { TaskCloudServiceMock } from '../../../mock/task-cloud.service.mock';
import { FormCloudServiceMock } from '../../../../form/mocks/form-cloud.service.mock';
@ -30,7 +29,7 @@ export default {
title: 'Process Services Cloud/Task Cloud/Task Form/Task Form Cloud',
decorators: [
moduleMetadata({
imports: [TaskFormModule],
imports: [TaskFormCloudComponent],
providers: [
{ provide: TaskCloudService, useClass: TaskCloudServiceMock },
{ provide: FormCloudService, useClass: FormCloudServiceMock }

View File

@ -24,9 +24,14 @@ import { DropdownCloudWidgetComponent } from '../../../../form/components/widget
import { FormCloudDisplayModeConfiguration } from '../../../../services/form-fields.interfaces';
import { TaskCloudService } from '../../../services/task-cloud.service';
import { TaskDetailsCloudModel } from '../../../start-task/models/task-details-cloud.model';
import { CommonModule } from '@angular/common';
import { UserTaskCloudButtonsComponent } from '../user-task-cloud-buttons/user-task-cloud-buttons.component';
import { FormCustomOutcomesComponent } from '../../../../form/components/form-cloud-custom-outcomes.component';
@Component({
selector: 'adf-cloud-task-form',
standalone: true,
imports: [CommonModule, UserTaskCloudButtonsComponent, FormCustomOutcomesComponent, FormCloudComponent],
templateUrl: './task-form-cloud.component.html',
styleUrls: ['./task-form-cloud.component.scss'],
encapsulation: ViewEncapsulation.None

View File

@ -20,7 +20,6 @@ import { UserTaskCloudButtonsComponent } from './user-task-cloud-buttons.compone
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { HarnessLoader } from '@angular/cdk/testing';
import { MatButtonHarness } from '@angular/material/button/testing';
import { NoopTranslateModule } from '@alfresco/adf-core';
import { By } from '@angular/platform-browser';
import { DebugElement } from '@angular/core';
import { ProcessServiceCloudTestingModule } from 'lib/process-services-cloud/src/lib/testing/process-service-cloud.testing.module';
@ -36,8 +35,7 @@ describe('UserTaskCloudButtonsComponent', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [NoopTranslateModule, ProcessServiceCloudTestingModule],
declarations: [UserTaskCloudButtonsComponent]
imports: [ProcessServiceCloudTestingModule, UserTaskCloudButtonsComponent]
});
fixture = TestBed.createComponent(UserTaskCloudButtonsComponent);
debugElement = fixture.debugElement;

View File

@ -16,9 +16,16 @@
*/
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { CommonModule } from '@angular/common';
import { TranslateModule } from '@ngx-translate/core';
import { MatButtonModule } from '@angular/material/button';
import { UnClaimTaskCloudDirective } from '../../../directives/unclaim-task-cloud.directive';
import { ClaimTaskCloudDirective } from '../../../directives/claim-task-cloud.directive';
@Component({
selector: 'adf-cloud-user-task-cloud-buttons',
standalone: true,
imports: [CommonModule, TranslateModule, UnClaimTaskCloudDirective, ClaimTaskCloudDirective, MatButtonModule],
styles: ['button { margin-right: 8px; }'],
templateUrl: './user-task-cloud-buttons.component.html'
})

View File

@ -15,7 +15,6 @@
* limitations under the License.
*/
import { NoopTranslateModule } from '@alfresco/adf-core';
import {
TASK_ASSIGNED_STATE,
TASK_CLAIM_PERMISSION,
@ -64,8 +63,7 @@ describe('UserTaskCloudComponent', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [NoopTranslateModule, ProcessServiceCloudTestingModule],
declarations: [UserTaskCloudComponent, TaskFormCloudComponent]
imports: [ProcessServiceCloudTestingModule, UserTaskCloudComponent, TaskFormCloudComponent]
});
fixture = TestBed.createComponent(UserTaskCloudComponent);
component = fixture.componentInstance;

View File

@ -15,13 +15,21 @@
* limitations under the License.
*/
import { ContentLinkModel, FormFieldValidator, FormModel, FormOutcomeEvent } from '@alfresco/adf-core';
import { ContentLinkModel, EmptyContentComponent, FormFieldValidator, FormModel, FormOutcomeEvent } from '@alfresco/adf-core';
import { Component, DestroyRef, EventEmitter, inject, Input, OnChanges, OnInit, Output, SimpleChanges, ViewChild } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { FormCloudDisplayModeConfiguration } from '../../../../services/form-fields.interfaces';
import { TaskCloudService } from '../../../services/task-cloud.service';
import { TaskDetailsCloudModel } from '../../../start-task/models/task-details-cloud.model';
import { TaskFormCloudComponent } from '../task-form-cloud/task-form-cloud.component';
import { CommonModule } from '@angular/common';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { UserTaskCloudButtonsComponent } from '../user-task-cloud-buttons/user-task-cloud-buttons.component';
import { TranslateModule } from '@ngx-translate/core';
import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card';
import { TaskScreenCloudComponent } from '../../../../screen/components/screen-cloud/screen-cloud.component';
import { CompleteTaskDirective } from '../../../directives/complete-task.directive';
const TaskTypes = {
Form: 'form',
@ -33,6 +41,19 @@ type TaskTypesType = (typeof TaskTypes)[keyof typeof TaskTypes];
@Component({
selector: 'adf-cloud-user-task',
standalone: true,
imports: [
CommonModule,
MatProgressSpinnerModule,
UserTaskCloudButtonsComponent,
TranslateModule,
MatButtonModule,
MatCardModule,
EmptyContentComponent,
TaskScreenCloudComponent,
TaskFormCloudComponent,
CompleteTaskDirective
],
templateUrl: './user-task-cloud.component.html',
styleUrls: ['./user-task-cloud.component.scss']
})

View File

@ -16,19 +16,23 @@
*/
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { MaterialModule } from '../../material.module';
import { FormCloudModule } from '../../form/form-cloud.module';
import { FORM_CLOUD_DIRECTIVES } from '../../form/form-cloud.module';
import { TASK_DIRECTIVES } from '../directives/task-directive.module';
import { TaskFormCloudComponent } from './components/task-form-cloud/task-form-cloud.component';
import { CoreModule } from '@alfresco/adf-core';
import { TaskScreenCloudComponent } from '../../screen/components/screen-cloud/screen-cloud.component';
import { UserTaskCloudComponent } from './components/user-task-cloud/user-task-cloud.component';
import { UserTaskCloudButtonsComponent } from './components/user-task-cloud-buttons/user-task-cloud-buttons.component';
/** @deprecated use standalone component imports instead */
@NgModule({
imports: [CoreModule, CommonModule, MaterialModule, FormCloudModule, ...TASK_DIRECTIVES, TaskScreenCloudComponent],
declarations: [TaskFormCloudComponent, UserTaskCloudComponent, UserTaskCloudButtonsComponent],
imports: [
...FORM_CLOUD_DIRECTIVES,
...TASK_DIRECTIVES,
TaskScreenCloudComponent,
UserTaskCloudButtonsComponent,
TaskFormCloudComponent,
UserTaskCloudComponent
],
exports: [TaskFormCloudComponent, UserTaskCloudComponent]
})
export class TaskFormModule {}

View File

@ -79,10 +79,6 @@ export class FormComponent extends FormBaseComponent implements OnInit, OnChange
protected nodeService = inject(NodesApiService);
private cdRef = inject(ChangeDetectorRef);
/** Underlying form model instance. */
@Input()
form: FormModel;
/** Task id to fetch corresponding form and values. */
@Input()
taskId: string;