mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-07-24 17:32:15 +00:00
AAE-20848 Add display external property widget (#9429)
* AAE-20848 Add display external property widget * revert css changes * AAE-20848 validate validatable types * AAE-20848 implement suggestions * fix lint
This commit is contained in:
@@ -27,6 +27,7 @@ import { RadioButtonsCloudWidgetComponent } from './widgets/radio-buttons/radio-
|
||||
import { FileViewerWidgetComponent } from './widgets/file-viewer/file-viewer.widget';
|
||||
import { DisplayRichTextWidgetComponent } from './widgets/display-rich-text/display-rich-text.widget';
|
||||
import { DataTableWidgetComponent } from './widgets/data-table/data-table.widget';
|
||||
import { DisplayExternalPropertyWidgetComponent } from './widgets/display-external-property/display-external-property.widget';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
@@ -45,7 +46,8 @@ export class CloudFormRenderingService extends FormRenderingService {
|
||||
[FormFieldTypes.RADIO_BUTTONS]: () => RadioButtonsCloudWidgetComponent,
|
||||
[FormFieldTypes.ALFRESCO_FILE_VIEWER]: () => FileViewerWidgetComponent,
|
||||
[FormFieldTypes.DISPLAY_RICH_TEXT]: () => DisplayRichTextWidgetComponent,
|
||||
[FormFieldTypes.DATA_TABLE]: () => DataTableWidgetComponent
|
||||
[FormFieldTypes.DATA_TABLE]: () => DataTableWidgetComponent,
|
||||
[FormFieldTypes.DISPLAY_EXTERNAL_PROPERTY]: () => DisplayExternalPropertyWidgetComponent
|
||||
}, true);
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,30 @@
|
||||
<div
|
||||
class="adf-textfield adf-display-external-property-widget {{ field.className }}"
|
||||
[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>
|
||||
|
||||
<div>
|
||||
<mat-form-field [hideRequiredMarker]="true">
|
||||
<label class="adf-label" *ngIf="!field.leftLabels" [attr.for]="field.id">
|
||||
{{ field.name | translate }}<span class="adf-asterisk" *ngIf="isRequired()">*</span>
|
||||
</label>
|
||||
<input matInput
|
||||
class="adf-input"
|
||||
type="text"
|
||||
data-automation-id="adf-display-external-property-widget"
|
||||
[id]="field.id"
|
||||
[formControl]="propertyControl"
|
||||
>
|
||||
</mat-form-field>
|
||||
|
||||
<ng-container *ngIf="!previewState">
|
||||
<error-widget *ngIf="propertyLoadFailed" [required]="'FORM.FIELD.EXTERNAL_PROPERTY_LOAD_FAILED' | translate"></error-widget>
|
||||
</ng-container>
|
||||
</div>
|
@@ -0,0 +1,9 @@
|
||||
.adf {
|
||||
&-display-external-property-widget {
|
||||
width: 100%;
|
||||
|
||||
.adf-label {
|
||||
top: 20px;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,187 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright © 2005-2023 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 { FormService, FormFieldModel, FormModel, FormFieldTypes, LogService } from '@alfresco/adf-core';
|
||||
import { HarnessLoader } from '@angular/cdk/testing';
|
||||
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
import { MatInputHarness } from '@angular/material/input/testing';
|
||||
import { DisplayExternalPropertyWidgetComponent } from './display-external-property.widget';
|
||||
import { FormCloudService } from '../../../services/form-cloud.service';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||
|
||||
describe('DisplayExternalPropertyWidgetComponent', () => {
|
||||
let loader: HarnessLoader;
|
||||
let widget: DisplayExternalPropertyWidgetComponent;
|
||||
let fixture: ComponentFixture<DisplayExternalPropertyWidgetComponent>;
|
||||
let element: HTMLElement;
|
||||
let logService: LogService;
|
||||
let logServiceSpy: jasmine.Spy;
|
||||
let formCloudService: FormCloudService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
TranslateModule.forRoot(),
|
||||
NoopAnimationsModule,
|
||||
ReactiveFormsModule,
|
||||
DisplayExternalPropertyWidgetComponent
|
||||
],
|
||||
providers: [FormService]
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(DisplayExternalPropertyWidgetComponent);
|
||||
widget = fixture.componentInstance;
|
||||
element = fixture.nativeElement;
|
||||
loader = TestbedHarnessEnvironment.loader(fixture);
|
||||
logService = TestBed.inject(LogService);
|
||||
formCloudService = TestBed.inject(FormCloudService);
|
||||
|
||||
logServiceSpy = spyOn(logService, 'error');
|
||||
});
|
||||
|
||||
it('should display initial value', async () => {
|
||||
widget.field = new FormFieldModel(new FormModel({ taskId: '<id>' }), {
|
||||
type: FormFieldTypes.DISPLAY_EXTERNAL_PROPERTY,
|
||||
readOnly: true,
|
||||
externalProperty: 'fruitName',
|
||||
value: 'banana'
|
||||
});
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
const input = await loader.getHarness(MatInputHarness);
|
||||
expect(fixture.nativeElement.querySelector('.adf-invalid')).toBeFalsy();
|
||||
expect(await input.getValue()).toBe('banana');
|
||||
});
|
||||
|
||||
describe('when property load fails', () => {
|
||||
beforeEach(() => {
|
||||
widget.field = new FormFieldModel(new FormModel({ taskId: '<id>' }), {
|
||||
type: FormFieldTypes.DISPLAY_EXTERNAL_PROPERTY,
|
||||
externalProperty: 'fruitName',
|
||||
value: null
|
||||
});
|
||||
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should display the error message', () => {
|
||||
const errorElement = element.querySelector('error-widget');
|
||||
expect(errorElement.textContent.trim()).toContain('FORM.FIELD.EXTERNAL_PROPERTY_LOAD_FAILED');
|
||||
});
|
||||
|
||||
it('should log the error', () => {
|
||||
expect(logServiceSpy).toHaveBeenCalledWith('External property not found');
|
||||
});
|
||||
});
|
||||
|
||||
describe('when property is in preview state', () => {
|
||||
beforeEach(() => {
|
||||
widget.field = new FormFieldModel(new FormModel({ taskId: '<id>' }), {
|
||||
type: FormFieldTypes.DISPLAY_EXTERNAL_PROPERTY,
|
||||
externalProperty: true,
|
||||
value: null
|
||||
});
|
||||
|
||||
spyOn(formCloudService, 'getPreviewState').and.returnValue(true);
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should NOT display the error message', () => {
|
||||
const errorElement = element.querySelector('error-widget');
|
||||
expect(errorElement).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should NOT log the error', () => {
|
||||
expect(logServiceSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when is required', () => {
|
||||
beforeEach(() => {
|
||||
widget.field = new FormFieldModel(new FormModel({ taskId: '<id>' }), {
|
||||
type: FormFieldTypes.DISPLAY_EXTERNAL_PROPERTY,
|
||||
required: true
|
||||
});
|
||||
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should be able to display label with asterisk', () => {
|
||||
const asterisk = element.querySelector('.adf-asterisk');
|
||||
|
||||
expect(asterisk).toBeTruthy();
|
||||
expect(asterisk?.textContent).toEqual('*');
|
||||
});
|
||||
});
|
||||
|
||||
describe('when form model has left labels', () => {
|
||||
it('should have left labels classes on leftLabels true', async () => {
|
||||
widget.field = new FormFieldModel(new FormModel({ taskId: 'fake-task-id', leftLabels: true }), {
|
||||
id: 'external-property-id',
|
||||
name: 'external-property-name',
|
||||
value: '',
|
||||
type: FormFieldTypes.DISPLAY_EXTERNAL_PROPERTY
|
||||
});
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
const widgetContainer = element.querySelector('.adf-left-label-input-container');
|
||||
expect(widgetContainer).not.toBeNull();
|
||||
|
||||
const adfLeftLabel = element.querySelector('.adf-left-label');
|
||||
expect(adfLeftLabel).not.toBeNull();
|
||||
});
|
||||
|
||||
it('should not have left labels classes on leftLabels false', () => {
|
||||
widget.field = new FormFieldModel(new FormModel({ taskId: 'fake-task-id', leftLabels: false }), {
|
||||
id: 'external-property-id',
|
||||
name: 'external-property-name',
|
||||
value: '',
|
||||
type: FormFieldTypes.DISPLAY_EXTERNAL_PROPERTY
|
||||
});
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
const widgetContainer = element.querySelector('.adf-left-label-input-container');
|
||||
expect(widgetContainer).toBeNull();
|
||||
|
||||
const adfLeftLabel = element.querySelector('.adf-left-label');
|
||||
expect(adfLeftLabel).toBeNull();
|
||||
});
|
||||
|
||||
it('should not have left labels classes on leftLabels not present', () => {
|
||||
widget.field = new FormFieldModel(new FormModel({ taskId: 'fake-task-id' }), {
|
||||
id: 'external-property-id',
|
||||
name: 'external-property-name',
|
||||
value: '',
|
||||
type: FormFieldTypes.DISPLAY_EXTERNAL_PROPERTY
|
||||
});
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
const widgetContainer = element.querySelector('.adf-left-label-input-container');
|
||||
expect(widgetContainer).toBeNull();
|
||||
|
||||
const adfLeftLabel = element.querySelector('.adf-left-label');
|
||||
expect(adfLeftLabel).toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
@@ -0,0 +1,109 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright © 2005-2023 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 { ChangeDetectionStrategy, Component, OnInit, ViewEncapsulation } from '@angular/core';
|
||||
import {
|
||||
WidgetComponent,
|
||||
FormService,
|
||||
LogService,
|
||||
FormBaseModule
|
||||
} from '@alfresco/adf-core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { FormCloudService } from '../../../services/form-cloud.service';
|
||||
import { FormControl, ReactiveFormsModule, Validators } from '@angular/forms';
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
imports: [
|
||||
CommonModule,
|
||||
TranslateModule,
|
||||
ReactiveFormsModule,
|
||||
MatFormFieldModule,
|
||||
MatInputModule,
|
||||
FormBaseModule
|
||||
],
|
||||
selector: 'adf-cloud-display-external-property',
|
||||
templateUrl: './display-external-property.widget.html',
|
||||
styleUrls: ['./display-external-property.widget.scss'],
|
||||
host: {
|
||||
'(click)': 'event($event)',
|
||||
'(blur)': 'event($event)',
|
||||
'(change)': 'event($event)',
|
||||
'(focus)': 'event($event)',
|
||||
'(focusin)': 'event($event)',
|
||||
'(focusout)': 'event($event)',
|
||||
'(input)': 'event($event)',
|
||||
'(invalid)': 'event($event)',
|
||||
'(select)': 'event($event)'
|
||||
},
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class DisplayExternalPropertyWidgetComponent extends WidgetComponent implements OnInit {
|
||||
|
||||
propertyLoadFailed = false;
|
||||
previewState = false;
|
||||
propertyControl: FormControl;
|
||||
|
||||
constructor(
|
||||
public readonly formService: FormService,
|
||||
private readonly formCloudService: FormCloudService,
|
||||
private readonly logService: LogService
|
||||
) {
|
||||
super(formService);
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.initFormControl();
|
||||
this.initPreviewState();
|
||||
this.handleFailedPropertyLoad();
|
||||
}
|
||||
|
||||
private initFormControl(): void {
|
||||
this.propertyControl = new FormControl(
|
||||
{
|
||||
value: this.field?.value,
|
||||
disabled: this.field?.readOnly || this.readOnly
|
||||
},
|
||||
this.isRequired() ? [Validators.required] : []
|
||||
);
|
||||
}
|
||||
|
||||
private isPropertyLoadFailed(): boolean {
|
||||
return this.field.externalProperty && !this.field.value;
|
||||
}
|
||||
|
||||
private handleFailedPropertyLoad(): void {
|
||||
if (this.isPropertyLoadFailed()) {
|
||||
this.handleError('External property not found');
|
||||
}
|
||||
}
|
||||
|
||||
private initPreviewState(): void {
|
||||
this.previewState = this.formCloudService.getPreviewState();
|
||||
}
|
||||
|
||||
private handleError(error: any): void {
|
||||
if (!this.previewState) {
|
||||
this.propertyLoadFailed = true;
|
||||
this.logService.error(error);
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user