mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-05-26 17:24:56 +00:00
[ADF-4488] Form - Add dropdown-cloud widget (#4695)
* [ADF-4488] Form - Add dropdown cloud widget * [ADF-4488] - add form style * [ADF-4488] - revert formFieldOption change * [ADF-4488] - PR changes * [ADF-4488] - move dropdown widget into cloud module * [ADF-4488] - PR changes and remove unused unit test * [ADF-4488] - PR changes * [ADF-4488] - PR changes * [ADF-4488] - add multilingual support * [ADF-4488] - PR changes
This commit is contained in:
parent
8985319f15
commit
96111f588a
@ -0,0 +1,20 @@
|
||||
<div class="adf-dropdown-widget {{field.className}}"
|
||||
[class.adf-invalid]="!field.isValid" [class.adf-readonly]="field.readOnly">
|
||||
<label class="adf-label" [attr.for]="field.id">{{field.name | translate }}<span *ngIf="isRequired()">*</span></label>
|
||||
<mat-form-field>
|
||||
<mat-select class="adf-select"
|
||||
[id]="field.id"
|
||||
[(ngModel)]="field.value"
|
||||
[disabled]="field.readOnly"
|
||||
(ngModelChange)="onFieldChanged(field)">
|
||||
<mat-option *ngFor="let opt of field.options"
|
||||
[value]="getOptionValue(opt, field.value)"
|
||||
[id]="opt.id">{{opt.name}}
|
||||
</mat-option>
|
||||
<mat-option id="readonlyOption" *ngIf="isReadOnlyType()" [value]="field.value">{{field.value}}</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<error-widget [error]="field.validationSummary"></error-widget>
|
||||
<error-widget class="adf-dropdown-required-message" *ngIf="isInvalidFieldRequired()"
|
||||
required="{{ 'FORM.FIELD.REQUIRED' | translate }}"></error-widget>
|
||||
</div>
|
@ -0,0 +1,25 @@
|
||||
.adf {
|
||||
&-dropdown-widget {
|
||||
width: 100%;
|
||||
margin-top: 13px;
|
||||
|
||||
.adf-select {
|
||||
padding-top: 0 !important;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.mat-select-value-text {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
&-select {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&-dropdown-required-message .adf-error-text-container {
|
||||
margin-top: 1px !important;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,191 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2019 Alfresco Software, Ltd.
|
||||
*
|
||||
* 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 { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { of } from 'rxjs';
|
||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { DropdownCloudWidgetComponent } from './dropdown-cloud.widget';
|
||||
import { FormService, WidgetVisibilityService, FormFieldOption, setupTestBed, FormFieldModel, FormModel, CoreModule } from '@alfresco/adf-core';
|
||||
import { FormCloudService } from '../../services/form-cloud.service';
|
||||
|
||||
describe('DropdownCloudWidgetComponent', () => {
|
||||
|
||||
let formService: FormService;
|
||||
let widget: DropdownCloudWidgetComponent;
|
||||
let visibilityService: WidgetVisibilityService;
|
||||
let formCloudService: FormCloudService;
|
||||
let fixture: ComponentFixture<DropdownCloudWidgetComponent>;
|
||||
let element: HTMLElement;
|
||||
|
||||
function openSelect() {
|
||||
const dropdown = fixture.debugElement.query(By.css('[class="mat-select-trigger"]'));
|
||||
dropdown.triggerEventHandler('click', null);
|
||||
fixture.detectChanges();
|
||||
}
|
||||
|
||||
const fakeOptionList: FormFieldOption[] = [
|
||||
{ id: 'opt_1', name: 'option_1' },
|
||||
{ id: 'opt_2', name: 'option_2' },
|
||||
{ id: 'opt_3', name: 'option_3' }];
|
||||
|
||||
setupTestBed({
|
||||
imports: [
|
||||
NoopAnimationsModule,
|
||||
CoreModule.forRoot()
|
||||
],
|
||||
declarations: [DropdownCloudWidgetComponent],
|
||||
providers: [FormCloudService]
|
||||
});
|
||||
|
||||
beforeEach(async(() => {
|
||||
fixture = TestBed.createComponent(DropdownCloudWidgetComponent);
|
||||
widget = fixture.componentInstance;
|
||||
element = fixture.nativeElement;
|
||||
formService = TestBed.get(FormService);
|
||||
visibilityService = TestBed.get(WidgetVisibilityService);
|
||||
formCloudService = TestBed.get(FormCloudService);
|
||||
|
||||
widget.field = new FormFieldModel(new FormModel());
|
||||
}));
|
||||
|
||||
it('should require field with restUrl', async(() => {
|
||||
spyOn(formService, 'getRestFieldValues').and.stub();
|
||||
|
||||
widget.field = null;
|
||||
widget.ngOnInit();
|
||||
expect(formService.getRestFieldValues).not.toHaveBeenCalled();
|
||||
|
||||
widget.field = new FormFieldModel(null, { restUrl: null });
|
||||
widget.ngOnInit();
|
||||
expect(formService.getRestFieldValues).not.toHaveBeenCalled();
|
||||
}));
|
||||
|
||||
describe('when template is ready', () => {
|
||||
|
||||
describe('and dropdown is populated', () => {
|
||||
|
||||
beforeEach(async(() => {
|
||||
spyOn(visibilityService, 'refreshVisibility').and.stub();
|
||||
spyOn(formService, 'getRestFieldValues').and.callFake(() => {
|
||||
return of(fakeOptionList);
|
||||
});
|
||||
widget.field = new FormFieldModel(new FormModel({ taskId: 'fake-task-id' }), {
|
||||
id: 'dropdown-id',
|
||||
name: 'date-name',
|
||||
type: 'dropdown',
|
||||
readOnly: 'false',
|
||||
restUrl: 'fake-rest-url'
|
||||
});
|
||||
widget.field.emptyOption = { id: 'empty', name: 'Choose one...' };
|
||||
widget.field.isVisible = true;
|
||||
fixture.detectChanges();
|
||||
}));
|
||||
|
||||
it('should show visible dropdown widget', async(() => {
|
||||
expect(element.querySelector('#dropdown-id')).toBeDefined();
|
||||
expect(element.querySelector('#dropdown-id')).not.toBeNull();
|
||||
|
||||
openSelect();
|
||||
|
||||
const optOne = fixture.debugElement.queryAll(By.css('[id="mat-option-1"]'));
|
||||
const optTwo = fixture.debugElement.queryAll(By.css('[id="mat-option-2"]'));
|
||||
const optThree = fixture.debugElement.queryAll(By.css('[id="mat-option-3"]'));
|
||||
|
||||
expect(optOne).not.toBeNull();
|
||||
expect(optTwo).not.toBeNull();
|
||||
expect(optThree).not.toBeNull();
|
||||
}));
|
||||
|
||||
it('should select the default value when an option is chosen as default', async(() => {
|
||||
widget.field.value = 'option_2';
|
||||
widget.ngOnInit();
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable()
|
||||
.then(() => {
|
||||
const dropDownElement: any = element.querySelector('#dropdown-id');
|
||||
expect(dropDownElement.attributes['ng-reflect-model'].value).toBe('option_2');
|
||||
expect(dropDownElement.attributes['ng-reflect-model'].textContent).toBe('option_2');
|
||||
});
|
||||
}));
|
||||
|
||||
it('should select the empty value when no default is chosen', async(() => {
|
||||
widget.field.value = 'empty';
|
||||
widget.ngOnInit();
|
||||
fixture.detectChanges();
|
||||
|
||||
openSelect();
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.whenStable()
|
||||
.then(() => {
|
||||
const dropDownElement: any = element.querySelector('#dropdown-id');
|
||||
expect(dropDownElement.attributes['ng-reflect-model'].value).toBe('empty');
|
||||
});
|
||||
}));
|
||||
|
||||
it('should load data from restUrl and populate options', async(() => {
|
||||
widget.field = new FormFieldModel(new FormModel({ taskId: 'fake-task-id' }), {
|
||||
id: 'dropdown-id',
|
||||
name: 'date-name',
|
||||
type: 'dropdown-cloud',
|
||||
readOnly: 'true',
|
||||
restUrl: 'fake-rest-url',
|
||||
optionType: 'rest',
|
||||
restIdProperty: 'name'
|
||||
});
|
||||
const jsonDataSpy = spyOn(formCloudService, 'getDropDownJsonData').and.returnValue(of(fakeOptionList));
|
||||
const optOne = fixture.debugElement.queryAll(By.css('[id="mat-option-1"]'));
|
||||
widget.ngOnInit();
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
expect(jsonDataSpy).toHaveBeenCalled();
|
||||
expect(optOne).not.toBeNull();
|
||||
});
|
||||
}));
|
||||
|
||||
it('shoud map properties if restResponsePath is set', async(() => {
|
||||
widget.field = new FormFieldModel(new FormModel({ taskId: 'fake-task-id' }), {
|
||||
id: 'dropdown-id',
|
||||
name: 'date-name',
|
||||
type: 'dropdown-cloud',
|
||||
readOnly: 'true',
|
||||
restUrl: 'fake-rest-url',
|
||||
optionType: 'rest',
|
||||
restResponsePath: 'path',
|
||||
restIdProperty: 'name'
|
||||
});
|
||||
|
||||
spyOn(formCloudService, 'getDropDownJsonData').and.returnValue(of([{
|
||||
id: 1,
|
||||
path: {
|
||||
name: 'test1'
|
||||
}
|
||||
}]));
|
||||
spyOn(widget, 'mapJsonData').and.returnValue([]);
|
||||
|
||||
widget.ngOnInit();
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
expect(widget.mapJsonData).toHaveBeenCalled();
|
||||
});
|
||||
}));
|
||||
|
||||
});
|
||||
});
|
||||
});
|
@ -0,0 +1,92 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2019 Alfresco Software, Ltd.
|
||||
*
|
||||
* 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 { Component, OnInit, ViewEncapsulation } from '@angular/core';
|
||||
import { baseHost, WidgetComponent, FormService, LogService, FormFieldOption } from '@alfresco/adf-core';
|
||||
import { FormCloudService } from '../../services/form-cloud.service';
|
||||
|
||||
/* tslint:disable:component-selector */
|
||||
|
||||
@Component({
|
||||
selector: 'dropdown-cloud-widget',
|
||||
templateUrl: './dropdown-cloud.widget.html',
|
||||
styleUrls: ['./dropdown-cloud.widget.scss'],
|
||||
host: baseHost,
|
||||
encapsulation: ViewEncapsulation.None
|
||||
})
|
||||
export class DropdownCloudWidgetComponent extends WidgetComponent implements OnInit {
|
||||
|
||||
constructor(public formService: FormService,
|
||||
private formCloudService: FormCloudService,
|
||||
private logService: LogService) {
|
||||
super(formService);
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
if (this.field && this.field.restUrl) {
|
||||
this.getValuesFromRestApi();
|
||||
}
|
||||
}
|
||||
|
||||
getValuesFromRestApi() {
|
||||
if (this.isValidRestType()) {
|
||||
this.formCloudService.getDropDownJsonData(this.field.restUrl).subscribe( (result: FormFieldOption[]) => {
|
||||
if (this.field.restResponsePath) {
|
||||
this.field.options = this.mapJsonData(result);
|
||||
} else {
|
||||
this.field.options = result;
|
||||
}
|
||||
},
|
||||
(err) => this.handleError(err));
|
||||
}
|
||||
}
|
||||
|
||||
mapJsonData(data: any[]): FormFieldOption[] {
|
||||
const path = this.field.restResponsePath;
|
||||
const idProperty = this.field.restIdProperty;
|
||||
|
||||
return data.map( (value: any) => {
|
||||
return {
|
||||
name: value[path][idProperty],
|
||||
id: value.id
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
getOptionValue(option: FormFieldOption, fieldValue: string): string {
|
||||
let optionValue: string = '';
|
||||
if (option.id === 'empty' || option.name !== fieldValue) {
|
||||
optionValue = option.id;
|
||||
} else {
|
||||
optionValue = option.name;
|
||||
}
|
||||
return optionValue;
|
||||
}
|
||||
|
||||
isValidRestType(): boolean {
|
||||
return this.field.optionType === 'rest' && !!this.field.restUrl;
|
||||
}
|
||||
|
||||
handleError(error: any) {
|
||||
this.logService.error(error);
|
||||
}
|
||||
|
||||
isReadOnlyType(): boolean {
|
||||
return this.field.type === 'readonly' ? true : false;
|
||||
}
|
||||
|
||||
}
|
@ -26,6 +26,7 @@ import { FormCloudComponent } from './components/form-cloud.component';
|
||||
import { FormDefinitionSelectorCloudComponent } from './components/form-definition-selector-cloud.component';
|
||||
import { FormDefinitionSelectorCloudService } from './services/form-definition-selector-cloud.service';
|
||||
import { FormCustomOutcomesComponent } from './components/form-cloud-custom-outcomes.component';
|
||||
import { DropdownCloudWidgetComponent } from './components/dropdown-cloud/dropdown-cloud.widget';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
@ -39,10 +40,13 @@ import { FormCustomOutcomesComponent } from './components/form-cloud-custom-outc
|
||||
FormBaseModule,
|
||||
CoreModule
|
||||
],
|
||||
declarations: [FormCloudComponent, UploadCloudWidgetComponent, FormDefinitionSelectorCloudComponent, FormCustomOutcomesComponent],
|
||||
providers: [FormDefinitionSelectorCloudService],
|
||||
declarations: [FormCloudComponent, UploadCloudWidgetComponent, FormDefinitionSelectorCloudComponent, FormCustomOutcomesComponent, DropdownCloudWidgetComponent],
|
||||
providers: [
|
||||
FormDefinitionSelectorCloudService
|
||||
],
|
||||
entryComponents: [
|
||||
UploadCloudWidgetComponent
|
||||
UploadCloudWidgetComponent,
|
||||
DropdownCloudWidgetComponent
|
||||
],
|
||||
exports: [
|
||||
FormCloudComponent, UploadCloudWidgetComponent, FormDefinitionSelectorCloudComponent, FormCustomOutcomesComponent
|
||||
|
@ -16,7 +16,7 @@
|
||||
*/
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { AlfrescoApiService, LogService, FormValues, AppConfigService, FormOutcomeModel } from '@alfresco/adf-core';
|
||||
import { AlfrescoApiService, LogService, FormValues, AppConfigService, FormOutcomeModel, FormFieldOption } from '@alfresco/adf-core';
|
||||
import { throwError, Observable, from } from 'rxjs';
|
||||
import { catchError, map, switchMap } from 'rxjs/operators';
|
||||
import { TaskDetailsCloudModel } from '../../task/start-task/models/task-details-cloud.model';
|
||||
@ -224,6 +224,26 @@ export class FormCloudService extends BaseCloudService {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses JSON data to create a corresponding form.
|
||||
* @param url String data to make the request
|
||||
* @returns Array of FormFieldOption object
|
||||
*/
|
||||
getDropDownJsonData(url: string): Observable<FormFieldOption[]> {
|
||||
return from(this.apiService.getInstance()
|
||||
.oauth2Auth.callCustomApi(url, 'GET',
|
||||
null, null, null,
|
||||
null, null,
|
||||
this.contentTypes, this.accepts,
|
||||
this.returnType, null, null)
|
||||
).pipe(
|
||||
map((res: any) => {
|
||||
return res;
|
||||
}),
|
||||
catchError((err) => this.handleError(err))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses JSON data to create a corresponding form.
|
||||
* @param json JSON data to create the form
|
||||
|
@ -24,6 +24,7 @@ import { TaskDetailsCloudModel } from '../../start-task/models/task-details-clou
|
||||
import { TaskCloudService } from '../../services/task-cloud.service';
|
||||
import { FormRenderingService } from '@alfresco/adf-core';
|
||||
import { UploadCloudWidgetComponent } from '../../../form/components/upload-cloud.widget';
|
||||
import { DropdownCloudWidgetComponent } from '../../../form/components/dropdown-cloud/dropdown-cloud.widget';
|
||||
|
||||
@Component({
|
||||
selector: 'adf-cloud-task-form',
|
||||
@ -94,6 +95,7 @@ export class TaskFormCloudComponent implements OnChanges {
|
||||
private taskCloudService: TaskCloudService,
|
||||
private formRenderingService: FormRenderingService) {
|
||||
this.formRenderingService.setComponentTypeResolver('upload', () => UploadCloudWidgetComponent, true);
|
||||
this.formRenderingService.setComponentTypeResolver('dropdown', () => DropdownCloudWidgetComponent, true);
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user