From ff41b80a3382d0b50afc2d28dc1194a57efcec90 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Tue, 3 Jul 2018 17:51:26 +0200 Subject: [PATCH] Renamed form.reducers.ts to form.reducer.ts Created custom sass variable Removed use of instanceof operator --- src/app/app.reducer.ts | 2 +- .../ds-dynamic-form-control.component.ts | 4 +- .../models/ds-dynamic-concat.model.ts | 1 + .../models/ds-dynamic-qualdrop.model.ts | 1 + .../models/ds-dynamic-row-array-model.ts | 3 + .../models/ds-dynamic-row-group-model.ts | 2 +- .../dynamic-group/dynamic-group.components.ts | 4 +- .../dynamic-group/dynamic-group.model.ts | 4 +- .../list/dynamic-list-checkbox-group.model.ts | 1 + .../list/dynamic-list-radio-group.model.ts | 1 + .../lookup/dynamic-lookup.component.scss | 27 ++-- ...dynamic-scrollable-dropdown.component.scss | 8 +- .../models/tag/dynamic-tag.component.html | 4 +- .../models/tag/dynamic-tag.component.scss | 5 +- .../dynamic-typeahead.component.scss | 2 +- .../form/builder/form-builder.service.spec.ts | 73 ++++++++- .../form/builder/form-builder.service.ts | 138 ++++++++++-------- .../shared/form/builder/parsers/row-parser.ts | 16 +- src/app/shared/form/form.component.spec.ts | 2 +- src/app/shared/form/form.component.ts | 2 +- src/app/shared/form/form.reducer.spec.ts | 2 +- .../{form.reducers.ts => form.reducer.ts} | 0 src/app/shared/form/form.service.spec.ts | 2 +- src/app/shared/form/selectors.ts | 4 +- .../number-picker.component.scss | 4 - src/app/shared/object.util.spec.ts | 16 +- src/app/shared/object.util.ts | 20 +-- .../shared/uploader/uploader.component.scss | 6 +- src/styles/_custom_variables.scss | 3 + 29 files changed, 220 insertions(+), 137 deletions(-) rename src/app/shared/form/{form.reducers.ts => form.reducer.ts} (100%) diff --git a/src/app/app.reducer.ts b/src/app/app.reducer.ts index c0fcb56954..77c8efcab2 100644 --- a/src/app/app.reducer.ts +++ b/src/app/app.reducer.ts @@ -3,7 +3,7 @@ import * as fromRouter from '@ngrx/router-store'; import { headerReducer, HeaderState } from './header/header.reducer'; import { hostWindowReducer, HostWindowState } from './shared/host-window.reducer'; -import { formReducer, FormState } from './shared/form/form.reducers'; +import { formReducer, FormState } from './shared/form/form.reducer'; import { SearchSidebarState, sidebarReducer diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control.component.ts index 660ae9c244..3a39d22bef 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control.component.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control.component.ts @@ -33,7 +33,7 @@ import { import { DYNAMIC_FORM_CONTROL_TYPE_TYPEAHEAD } from './models/typeahead/dynamic-typeahead.model'; import { DYNAMIC_FORM_CONTROL_TYPE_SCROLLABLE_DROPDOWN } from './models/scrollable-dropdown/dynamic-scrollable-dropdown.model'; import { DYNAMIC_FORM_CONTROL_TYPE_TAG } from './models/tag/dynamic-tag.model'; -import { DYNAMIC_FORM_CONTROL_TYPE_RELATION } from './models/dynamic-group/dynamic-group.model'; +import { DYNAMIC_FORM_CONTROL_TYPE_RELATION_GROUP } from './models/dynamic-group/dynamic-group.model'; import { DYNAMIC_FORM_CONTROL_TYPE_DSDATEPICKER } from './models/date-picker/date-picker.model'; import { DYNAMIC_FORM_CONTROL_TYPE_LOOKUP } from './models/lookup/dynamic-lookup.model'; import { DynamicListCheckboxGroupModel } from './models/list/dynamic-list-checkbox-group.model'; @@ -137,7 +137,7 @@ export class DsDynamicFormControlComponent extends DynamicFormControlComponent i case DYNAMIC_FORM_CONTROL_TYPE_TAG: return NGBootstrapFormControlType.Tag; - case DYNAMIC_FORM_CONTROL_TYPE_RELATION: + case DYNAMIC_FORM_CONTROL_TYPE_RELATION_GROUP: return NGBootstrapFormControlType.Relation; case DYNAMIC_FORM_CONTROL_TYPE_DSDATEPICKER: diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/ds-dynamic-concat.model.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/ds-dynamic-concat.model.ts index 47d410f2ed..81f570b14c 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/ds-dynamic-concat.model.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/ds-dynamic-concat.model.ts @@ -16,6 +16,7 @@ export class DynamicConcatModel extends DynamicFormGroupModel { @serializable() separator: string; @serializable() hasLanguages = false; + isCustomGroup = true; constructor(config: DynamicConcatModelConfig, layout?: DynamicFormControlLayout) { diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/ds-dynamic-qualdrop.model.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/ds-dynamic-qualdrop.model.ts index fa91c0ad6b..bae79cc348 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/ds-dynamic-qualdrop.model.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/ds-dynamic-qualdrop.model.ts @@ -20,6 +20,7 @@ export class DynamicQualdropModel extends DynamicFormGroupModel { @serializable() languageUpdates: Subject; @serializable() hasLanguages = false; @serializable() readOnly: boolean; + isCustomGroup = true; constructor(config: DsDynamicQualdropModelConfig, layout?: DynamicFormControlLayout) { super(config, layout); diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/ds-dynamic-row-array-model.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/ds-dynamic-row-array-model.ts index 0a056b7d32..b38ea142f0 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/ds-dynamic-row-array-model.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/ds-dynamic-row-array-model.ts @@ -1,7 +1,9 @@ import { + DYNAMIC_FORM_CONTROL_TYPE_ARRAY, DynamicFormArrayModel, DynamicFormArrayModelConfig, DynamicFormControlLayout, serializable } from '@ng-dynamic-forms/core'; +import { DYNAMIC_FORM_CONTROL_TYPE_TAG } from './tag/dynamic-tag.model'; export interface DynamicRowArrayModelConfig extends DynamicFormArrayModelConfig { notRepeteable: boolean; @@ -9,6 +11,7 @@ export interface DynamicRowArrayModelConfig extends DynamicFormArrayModelConfig export class DynamicRowArrayModel extends DynamicFormArrayModel { @serializable() notRepeteable = false; + isRowArray = true; constructor(config: DynamicRowArrayModelConfig, layout?: DynamicFormControlLayout) { super(config, layout); diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/ds-dynamic-row-group-model.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/ds-dynamic-row-group-model.ts index 3e163f1f13..b6d76b4c55 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/ds-dynamic-row-group-model.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/ds-dynamic-row-group-model.ts @@ -1,5 +1,5 @@ import { DynamicFormGroupModel } from '@ng-dynamic-forms/core'; export class DynamicRowGroupModel extends DynamicFormGroupModel { - + isRowGroup = true; } diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/dynamic-group/dynamic-group.components.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/dynamic-group/dynamic-group.components.ts index acb146c98c..92141a0077 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/dynamic-group/dynamic-group.components.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/dynamic-group/dynamic-group.components.ts @@ -27,7 +27,7 @@ import { GlobalConfig } from '../../../../../../../config/global-config.interfac import { GLOBAL_CONFIG } from '../../../../../../../config'; import { FormGroup } from '@angular/forms'; import { Subscription } from 'rxjs/Subscription'; -import { isObjectEmpty } from '../../../../../object.util'; +import { hasOnlyEmptyProperties } from '../../../../../object.util'; import { FormFieldMetadataValueObject } from '../../../models/form-field-metadata-value.model'; import { AuthorityValueModel } from '../../../../../../core/integration/models/authority-value.model'; @@ -70,7 +70,7 @@ export class DsDynamicGroupComponent implements OnDestroy, OnInit { this.formCollapsed = Observable.of(true); } this.model.valueUpdates.subscribe((value: any[]) => { - this.formCollapsed = (isNotEmpty(value) && !(value.length === 1 && isObjectEmpty(value[0]))) ? Observable.of(true) : Observable.of(false); + this.formCollapsed = (isNotEmpty(value) && !(value.length === 1 && hasOnlyEmptyProperties(value[0]))) ? Observable.of(true) : Observable.of(false); }); this.formId = this.formService.getUniqueId(this.model.id); diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/dynamic-group/dynamic-group.model.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/dynamic-group/dynamic-group.model.ts index f8f4c80385..570e4bd7ea 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/dynamic-group/dynamic-group.model.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/dynamic-group/dynamic-group.model.ts @@ -4,7 +4,7 @@ import { DsDynamicInputModel, DsDynamicInputModelConfig } from '../ds-dynamic-in import { AuthorityValueModel } from '../../../../../../core/integration/models/authority-value.model'; import { isEmpty, isNull } from '../../../../../empty.util'; -export const DYNAMIC_FORM_CONTROL_TYPE_RELATION = 'RELATION'; +export const DYNAMIC_FORM_CONTROL_TYPE_RELATION_GROUP = 'RELATION'; export const PLACEHOLDER_PARENT_METADATA = '#PLACEHOLDER_PARENT_METADATA_VALUE#'; /** @@ -28,7 +28,7 @@ export class DynamicGroupModel extends DsDynamicInputModel { @serializable() scopeUUID: string; @serializable() submissionScope: string; @serializable() _value: any[]; - @serializable() readonly type: string = DYNAMIC_FORM_CONTROL_TYPE_RELATION; + @serializable() readonly type: string = DYNAMIC_FORM_CONTROL_TYPE_RELATION_GROUP; constructor(config: DynamicGroupModelConfig, layout?: DynamicFormControlLayout) { super(config, layout); diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/list/dynamic-list-checkbox-group.model.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/list/dynamic-list-checkbox-group.model.ts index 1537a02cd1..5fdc530ebd 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/list/dynamic-list-checkbox-group.model.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/list/dynamic-list-checkbox-group.model.ts @@ -22,6 +22,7 @@ export class DynamicListCheckboxGroupModel extends DynamicCheckboxGroupModel { @serializable() repeatable: boolean; @serializable() groupLength: number; @serializable() _value: AuthorityValueModel[]; + isListGroup = true; valueUpdates: Subject; constructor(config: DynamicListCheckboxGroupModelConfig, layout?: DynamicFormControlLayout) { diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/list/dynamic-list-radio-group.model.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/list/dynamic-list-radio-group.model.ts index 984e3b3880..287c10f3fe 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/list/dynamic-list-radio-group.model.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/list/dynamic-list-radio-group.model.ts @@ -19,6 +19,7 @@ export class DynamicListRadioGroupModel extends DynamicRadioGroupModel { @serializable() authorityOptions: AuthorityOptions; @serializable() repeatable: boolean; @serializable() groupLength: number; + isListGroup = true; constructor(config: DynamicListModelConfig, layout?: DynamicFormControlLayout) { super(config, layout); diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup/dynamic-lookup.component.scss b/src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup/dynamic-lookup.component.scss index a697772020..6dec8c1f45 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup/dynamic-lookup.component.scss +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup/dynamic-lookup.component.scss @@ -29,16 +29,16 @@ /* add padding */ .left-addon input { - padding-left: 30px; + padding-left: $spacer * 2; } .right-addon input { - padding-right: 30px; + padding-right: $spacer * 2; } :host /deep/ .dropdown-menu { width: 100% !important; - max-height: 200px; + max-height: $dropdown-menu-max-height; overflow-y: auto !important; overflow-x: hidden; } @@ -50,23 +50,16 @@ color: $dropdown-link-hover-color !important; background-color: $dropdown-link-hover-bg !important; } - -.dropdown-menu { - margin-top: -10px; -} +// +//.dropdown-menu { +// margin-top: -($spacer * 0.625); +//} div { overflow: visible; //padding-right: 0 !important; } - -button { - margin-right: 10px; -} - -//@media (min-width: $screen-sm-min) { -// .firstName { -// padding-left: 0 !important; -// margin-left: -5px !important; -// } +// +//button { +// margin-right: $spacer * 0.625; //} diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/scrollable-dropdown/dynamic-scrollable-dropdown.component.scss b/src/app/shared/form/builder/ds-dynamic-form-ui/models/scrollable-dropdown/dynamic-scrollable-dropdown.component.scss index 5b278312d3..34c5a84220 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/scrollable-dropdown/dynamic-scrollable-dropdown.component.scss +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/scrollable-dropdown/dynamic-scrollable-dropdown.component.scss @@ -2,7 +2,7 @@ .scrollable-menu { height: auto; - max-height: 200px; + max-height: $dropdown-menu-max-height; overflow-x: hidden; } @@ -13,14 +13,14 @@ .scrollable-dropdown-loading { background-color: map-get($theme-colors, primary); color: white; - height: 2rem !important; - line-height: 2rem; + height: $spacer * 2 !important; + line-height: $spacer * 2; position: sticky; bottom: 0; } .scrollable-dropdown-menu { left: 0 !important; - margin-bottom: 1rem; + margin-bottom: $spacer; z-index: 1000; } diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/tag/dynamic-tag.component.html b/src/app/shared/form/builder/ds-dynamic-form-ui/models/tag/dynamic-tag.component.html index 9a6ec43a40..5522e0b43a 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/tag/dynamic-tag.component.html +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/tag/dynamic-tag.component.html @@ -6,7 +6,7 @@ { @@ -248,7 +249,20 @@ describe('FormBuilderService test suite', () => { new DynamicLookupNameModel({id: 'testLookupName'}), - new DynamicQualdropModel({id: 'testCombobox', readOnly: false}) + new DynamicQualdropModel({id: 'testCombobox', readOnly: false}), + + new DynamicRowArrayModel( + { + id: 'testFormRowArray', + initialCount: 5, + notRepeteable: false, + groupFactory: () => { + return [ + new DynamicInputModel({id: 'testFormRowArrayGroupInput'}) + ]; + }, + } + ), ]; testFormConfiguration = { @@ -388,6 +402,8 @@ describe('FormBuilderService test suite', () => { expect(service.findById('testCheckboxGroup1', testModel) instanceof DynamicFormControlModel).toBe(true); expect(service.findById('testCheckboxGroup2', testModel) instanceof DynamicFormControlModel).toBe(true); expect(service.findById('nestedTestInput', testModel) instanceof DynamicFormControlModel).toBe(true); + expect(service.findById('testFormRowArrayGroupInput', testModel) instanceof DynamicFormControlModel).toBe(true); + expect(service.findById('testFormRowArrayGroupInput', testModel, 2) instanceof DynamicFormControlModel).toBe(true); }); it('should create an array of form models', () => { @@ -484,9 +500,32 @@ describe('FormBuilderService test suite', () => { it('should return true when model is a Qualdrop Group', () => { const formModel = service.modelFromConfiguration(testFormConfiguration, 'testScopeUUID'); - const model = service.findById('dc_identifier_QUALDROP_GROUP', formModel); + let model = service.findById('dc_identifier_QUALDROP_GROUP', formModel); expect(service.isQualdropGroup(model)).toBe(true); + + model = service.findById('name_CONCAT_GROUP', formModel); + + expect(service.isQualdropGroup(model)).toBe(false); + }); + + it('should return true when model is a Custom or List Group', () => { + const formModel = service.modelFromConfiguration(testFormConfiguration, 'testScopeUUID'); + let model = service.findById('dc_identifier_QUALDROP_GROUP', formModel); + + expect(service.isCustomOrListGroup(model)).toBe(true); + + model = service.findById('name_CONCAT_GROUP', formModel); + + expect(service.isCustomOrListGroup(model)).toBe(true); + + model = service.findById('testCheckboxList', testModel); + + expect(service.isCustomOrListGroup(model)).toBe(true); + + model = service.findById('testRadioList', testModel); + + expect(service.isCustomOrListGroup(model)).toBe(true); }); it('should return true when model is a Custom Group', () => { @@ -498,6 +537,14 @@ describe('FormBuilderService test suite', () => { model = service.findById('name_CONCAT_GROUP', formModel); expect(service.isCustomGroup(model)).toBe(true); + + model = service.findById('testCheckboxList', testModel); + + expect(service.isCustomGroup(model)).toBe(false); + + model = service.findById('testRadioList', testModel); + + expect(service.isCustomGroup(model)).toBe(false); }); it('should return true when model is a List Group', () => { @@ -516,6 +563,26 @@ describe('FormBuilderService test suite', () => { expect(service.isRelationGroup(model)).toBe(true); }); + it('should return true when model is a Array Row Group', () => { + let model = service.findById('testFormRowArray', testModel, null); + + expect(service.isRowArrayGroup(model)).toBe(true); + + model = service.findById('testFormArray', testModel); + + expect(service.isRowArrayGroup(model)).toBe(false); + }); + + it('should return true when model is a Array Group', () => { + let model = service.findById('testFormRowArray', testModel) as DynamicFormArrayModel; + + expect(service.isArrayGroup(model)).toBe(true); + + model = service.findById('testFormArray', testModel) as DynamicFormArrayModel; + + expect(service.isArrayGroup(model)).toBe(true); + }); + it('should return properly form control by field id', () => { const group = service.createFormGroup(testModel); const control = group.controls.testLookup; diff --git a/src/app/shared/form/builder/form-builder.service.ts b/src/app/shared/form/builder/form-builder.service.ts index a58061e448..0fb8f9ef11 100644 --- a/src/app/shared/form/builder/form-builder.service.ts +++ b/src/app/shared/form/builder/form-builder.service.ts @@ -1,8 +1,11 @@ import { Injectable } from '@angular/core'; -import { FormGroup } from '@angular/forms'; +import { AbstractControl, FormGroup } from '@angular/forms'; import { - DynamicFormArrayGroupModel, + DYNAMIC_FORM_CONTROL_TYPE_ARRAY, + DYNAMIC_FORM_CONTROL_TYPE_CHECKBOX_GROUP, + DYNAMIC_FORM_CONTROL_TYPE_GROUP, + DYNAMIC_FORM_CONTROL_TYPE_RADIO_GROUP, DynamicFormArrayModel, DynamicFormControlModel, DynamicFormGroupModel, @@ -10,26 +13,18 @@ import { DynamicPathable, JSONUtils, } from '@ng-dynamic-forms/core'; -import { mergeWith, isObject } from 'lodash'; +import { isObject, isString, mergeWith } from 'lodash'; -import { isEmpty, isNotEmpty, isNotNull, isNotUndefined, isNull } from '../../empty.util'; +import { hasValue, isEmpty, isNotEmpty, isNotNull, isNotUndefined, isNull } from '../../empty.util'; import { DynamicQualdropModel } from './ds-dynamic-form-ui/models/ds-dynamic-qualdrop.model'; import { SubmissionFormsModel } from '../../../core/shared/config/config-submission-forms.model'; -import { DynamicConcatModel } from './ds-dynamic-form-ui/models/ds-dynamic-concat.model'; -import { DynamicListCheckboxGroupModel } from './ds-dynamic-form-ui/models/list/dynamic-list-checkbox-group.model'; -import { - DYNAMIC_FORM_CONTROL_TYPE_RELATION, - DynamicGroupModel -} from './ds-dynamic-form-ui/models/dynamic-group/dynamic-group.model'; -import { DYNAMIC_FORM_CONTROL_TYPE_TAG, DynamicTagModel } from './ds-dynamic-form-ui/models/tag/dynamic-tag.model'; -import { DynamicListRadioGroupModel } from './ds-dynamic-form-ui/models/list/dynamic-list-radio-group.model'; +import { DYNAMIC_FORM_CONTROL_TYPE_RELATION_GROUP } from './ds-dynamic-form-ui/models/dynamic-group/dynamic-group.model'; +import { DYNAMIC_FORM_CONTROL_TYPE_TAG } from './ds-dynamic-form-ui/models/tag/dynamic-tag.model'; import { RowParser } from './parsers/row-parser'; import { DynamicRowArrayModel } from './ds-dynamic-form-ui/models/ds-dynamic-row-array-model'; -import { DynamicRowGroupModel } from './ds-dynamic-form-ui/models/ds-dynamic-row-group-model'; import { DsDynamicInputModel } from './ds-dynamic-form-ui/models/ds-dynamic-input.model'; import { FormFieldMetadataValueObject } from './models/form-field-metadata-value.model'; -import { AuthorityValueModel } from '../../../core/integration/models/authority-value.model'; @Injectable() export class FormBuilderService extends DynamicFormService { @@ -37,31 +32,33 @@ export class FormBuilderService extends DynamicFormService { findById(id: string, groupModel: DynamicFormControlModel[], arrayIndex = null): DynamicFormControlModel | null { let result = null; - const findByIdFn = (findId: string, findGroupModel: DynamicFormControlModel[]): void => { + const findByIdFn = (findId: string, findGroupModel: DynamicFormControlModel[], findArrayIndex): void => { for (const controlModel of findGroupModel) { if (controlModel.id === findId) { - if (controlModel instanceof DynamicFormArrayModel && isNotNull(arrayIndex)) { - result = controlModel.get(arrayIndex); + + if (this.isArrayGroup(controlModel) && isNotNull(findArrayIndex)) { + result = (controlModel as DynamicFormArrayModel).get(findArrayIndex); } else { result = controlModel; } break; } - if (controlModel instanceof DynamicFormGroupModel) { - findByIdFn(findId, (controlModel as DynamicFormGroupModel).group); + if (this.isGroup(controlModel)) { + findByIdFn(findId, (controlModel as DynamicFormGroupModel).group, findArrayIndex); } - if (controlModel instanceof DynamicFormArrayModel && (isNull(arrayIndex) || controlModel.size > (arrayIndex))) { - arrayIndex = (isNull(arrayIndex)) ? 0 : arrayIndex; - findByIdFn(findId, controlModel.get(arrayIndex).group); + if (this.isArrayGroup(controlModel) + && (isNull(findArrayIndex) || (controlModel as DynamicFormArrayModel).size > (findArrayIndex))) { + const index = (isNull(findArrayIndex)) ? 0 : findArrayIndex; + findByIdFn(findId, (controlModel as DynamicFormArrayModel).get(index).group, index); } } }; - findByIdFn(id, groupModel); + findByIdFn(id, groupModel, arrayIndex); return result; } @@ -72,13 +69,13 @@ export class FormBuilderService extends DynamicFormService { for (const controlModel of findGroupModel) { - if (controlModel instanceof DynamicFormGroupModel) { + if (this.isGroup(controlModel)) { iterateControlModels((controlModel as DynamicFormGroupModel).group); continue; } - if (controlModel instanceof DynamicFormArrayModel) { - iterateControlModels(controlModel.groupFactory()); + if (this.isArrayGroup(controlModel)) { + iterateControlModels((controlModel as DynamicFormArrayModel).groupFactory()); continue; } @@ -103,13 +100,12 @@ export class FormBuilderService extends DynamicFormService { const normalizeValue = (controlModel, controlValue, controlModelIndex) => { const controlLanguage = (controlModel as DsDynamicInputModel).hasLanguages ? (controlModel as DsDynamicInputModel).language : null; - if (((isObject(controlValue) && controlValue.id) || controlValue instanceof AuthorityValueModel)) { - return new FormFieldMetadataValueObject(controlValue.value, controlLanguage, controlValue.id, controlValue.display, controlModelIndex); - } else if (!(controlValue instanceof FormFieldMetadataValueObject)) { + if (isString(controlValue)) { return new FormFieldMetadataValueObject(controlValue, controlLanguage, null, null, controlModelIndex); - } else { + } else if (isObject(controlValue)) { + const authority = controlValue.authority || controlValue.id || null; const place = controlModelIndex || controlValue.place; - return Object.assign(new FormFieldMetadataValueObject(), controlValue, {place}); + return new FormFieldMetadataValueObject(controlValue.value, controlLanguage, authority, controlValue.display, place); } }; @@ -118,29 +114,27 @@ export class FormBuilderService extends DynamicFormService { // Iterate over all group's controls for (const controlModel of findGroupModel) { - /* tslint:disable-next-line:no-shadowed-variable */ - // for (const {controlModel, controlModelIndex} of findGroupModel.map((controlModel, controlModelIndex) => ({ controlModel, controlModelIndex }))) { - if (controlModel instanceof DynamicRowGroupModel && !this.isCustomGroup(controlModel)) { + if (this.isRowGroup(controlModel) && !this.isCustomOrListGroup(controlModel)) { iterateResult = mergeWith(iterateResult, iterateControlModels((controlModel as DynamicFormGroupModel).group), customizer); continue; } - if (controlModel instanceof DynamicFormGroupModel && !this.isCustomGroup(controlModel)) { + if (this.isGroup(controlModel) && !this.isCustomOrListGroup(controlModel)) { iterateResult[controlModel.name] = iterateControlModels((controlModel as DynamicFormGroupModel).group); continue; } - if (controlModel instanceof DynamicRowArrayModel) { - for (const arrayItemModel of controlModel.groups) { + if (this.isRowArrayGroup(controlModel)) { + for (const arrayItemModel of (controlModel as DynamicRowArrayModel).groups) { iterateResult = mergeWith(iterateResult, iterateControlModels(arrayItemModel.group, arrayItemModel.index), customizer); } continue; } - if (controlModel instanceof DynamicFormArrayModel) { + if (this.isArrayGroup(controlModel)) { iterateResult[controlModel.name] = []; - for (const arrayItemModel of controlModel.groups) { + for (const arrayItemModel of (controlModel as DynamicFormArrayModel).groups) { iterateResult[controlModel.name].push(iterateControlModels(arrayItemModel.group, arrayItemModel.index)); } continue; @@ -148,14 +142,14 @@ export class FormBuilderService extends DynamicFormService { let controlId; // Get the field's name - if (controlModel instanceof DynamicQualdropModel) { + if (this.isQualdropGroup(controlModel)) { // If is instance of DynamicQualdropModel take the qualdrop id as field's name - controlId = controlModel.qualdropId; + controlId = (controlModel as DynamicQualdropModel).qualdropId; } else { controlId = controlModel.name; } - if (controlModel instanceof DynamicGroupModel) { + if (this.isRelationGroup(controlModel)) { const values = (controlModel as any).value; values.forEach((groupValue, groupIndex) => { const newGroupValue = Object.create({}); @@ -170,7 +164,6 @@ export class FormBuilderService extends DynamicFormService { } } }); - // controlArrayValue.push(newGroupValue); }) } else if (isNotUndefined((controlModel as any).value) && isNotEmpty((controlModel as any).value)) { const controlArrayValue = []; @@ -217,49 +210,66 @@ export class FormBuilderService extends DynamicFormService { return rows; } - isModelInCustomGroup(model: DynamicFormControlModel) { - return model.parent && - (model.parent instanceof DynamicConcatModel - || model.parent instanceof DynamicQualdropModel); + isModelInCustomGroup(model: DynamicFormControlModel): boolean { + return this.isCustomGroup((model as any).parent); } - hasArrayGroupValue(model: DynamicFormControlModel) { + hasArrayGroupValue(model: DynamicFormControlModel): boolean { return model && (this.isListGroup(model) || model.type === DYNAMIC_FORM_CONTROL_TYPE_TAG); } - hasMappedGroupValue(model: DynamicFormControlModel) { - return ((model.parent && model.parent instanceof DynamicQualdropModel) - || model.parent instanceof DynamicGroupModel); + hasMappedGroupValue(model: DynamicFormControlModel): boolean { + return (this.isQualdropGroup((model as any).parent) + || this.isRelationGroup((model as any).parent)); } - isQualdropGroup(model: DynamicFormControlModel) { - return model && model instanceof DynamicQualdropModel; + isGroup(model: DynamicFormControlModel): boolean { + return model && (model.type === DYNAMIC_FORM_CONTROL_TYPE_GROUP || model.type === DYNAMIC_FORM_CONTROL_TYPE_CHECKBOX_GROUP); } - isCustomGroup(model: DynamicFormControlModel) { + isQualdropGroup(model: DynamicFormControlModel): boolean { + return (model && model.type === DYNAMIC_FORM_CONTROL_TYPE_GROUP && hasValue((model as any).qualdropId)); + } + + isCustomGroup(model: DynamicFormControlModel): boolean { + return model && ((model as any).type === DYNAMIC_FORM_CONTROL_TYPE_GROUP && (model as any).isCustomGroup === true); + } + + isRowGroup(model: DynamicFormControlModel): boolean { + return model && ((model as any).type === DYNAMIC_FORM_CONTROL_TYPE_GROUP && (model as any).isRowGroup === true); + } + + isCustomOrListGroup(model: DynamicFormControlModel): boolean { return model && - (model instanceof DynamicConcatModel - || this.isQualdropGroup(model) + (this.isCustomGroup(model) || this.isListGroup(model)); } - isListGroup(model: DynamicFormControlModel) { + isListGroup(model: DynamicFormControlModel): boolean { return model && - (model instanceof DynamicListCheckboxGroupModel - || model instanceof DynamicListRadioGroupModel); + ((model.type === DYNAMIC_FORM_CONTROL_TYPE_CHECKBOX_GROUP && (model as any).isListGroup === true) + || (model.type === DYNAMIC_FORM_CONTROL_TYPE_RADIO_GROUP && (model as any).isListGroup === true)); } - isRelationGroup(model: DynamicFormControlModel) { - return model && model.type === DYNAMIC_FORM_CONTROL_TYPE_RELATION; + isRelationGroup(model: DynamicFormControlModel): boolean { + return model && model.type === DYNAMIC_FORM_CONTROL_TYPE_RELATION_GROUP; } - getFormControlById(id: string, formGroup: FormGroup, groupModel: DynamicFormControlModel[], index = 0) { + isRowArrayGroup(model: DynamicFormControlModel): boolean { + return model.type === DYNAMIC_FORM_CONTROL_TYPE_ARRAY && (model as any).isRowArray === true; + } + + isArrayGroup(model: DynamicFormControlModel): boolean { + return model.type === DYNAMIC_FORM_CONTROL_TYPE_ARRAY; + } + + getFormControlById(id: string, formGroup: FormGroup, groupModel: DynamicFormControlModel[], index = 0): AbstractControl { const fieldModel = this.findById(id, groupModel, index); return isNotEmpty(fieldModel) ? formGroup.get(this.getPath(fieldModel)) : null; } - getId(model: DynamicPathable) { - if (model instanceof DynamicFormArrayGroupModel) { + getId(model: DynamicPathable): string { + if (this.isArrayGroup(model as DynamicFormControlModel)) { return model.index.toString(); } else { return ((model as DynamicFormControlModel).id !== (model as DynamicFormControlModel).name) ? diff --git a/src/app/shared/form/builder/parsers/row-parser.ts b/src/app/shared/form/builder/parsers/row-parser.ts index d21799c584..5d707e1cd4 100644 --- a/src/app/shared/form/builder/parsers/row-parser.ts +++ b/src/app/shared/form/builder/parsers/row-parser.ts @@ -1,4 +1,9 @@ -import { DynamicFormArrayModel, DynamicFormControlModel, DynamicFormGroupModelConfig } from '@ng-dynamic-forms/core'; +import { + DYNAMIC_FORM_CONTROL_TYPE_ARRAY, + DynamicFormArrayModel, + DynamicFormControlModel, + DynamicFormGroupModelConfig +} from '@ng-dynamic-forms/core'; import { uniqueId } from 'lodash'; import { DateFieldParser } from './date-field-parser'; @@ -11,7 +16,10 @@ import { TagFieldParser } from './tag-field-parser'; import { TextareaFieldParser } from './textarea-field-parser'; import { GroupFieldParser } from './group-field-parser'; import { IntegrationSearchOptions } from '../../../../core/integration/models/integration-options.model'; -import { DynamicGroupModel } from '../ds-dynamic-form-ui/models/dynamic-group/dynamic-group.model'; +import { + DYNAMIC_FORM_CONTROL_TYPE_RELATION_GROUP, + DynamicGroupModel +} from '../ds-dynamic-form-ui/models/dynamic-group/dynamic-group.model'; import { DynamicRowGroupModel } from '../ds-dynamic-form-ui/models/ds-dynamic-row-group-model'; import { isEmpty } from '../../../empty.util'; import { LookupFieldParser } from './lookup-field-parser'; @@ -98,7 +106,7 @@ export class RowParser { } if (fieldModel) { - if (fieldModel instanceof DynamicFormArrayModel || fieldModel instanceof DynamicGroupModel) { + if (fieldModel.type === DYNAMIC_FORM_CONTROL_TYPE_ARRAY || fieldModel.type === DYNAMIC_FORM_CONTROL_TYPE_RELATION_GROUP) { if (this.rowData.fields.length > 1) { setLayout(fieldModel, 'grid', 'host', layoutGridClass); config.group.push(fieldModel); @@ -111,7 +119,7 @@ export class RowParser { } return; } else { - if (fieldModel instanceof Array) { + if (Array.isArray(fieldModel)) { fieldModel.forEach((model) => { parsedResult = model; return; diff --git a/src/app/shared/form/form.component.spec.ts b/src/app/shared/form/form.component.spec.ts index 155e009fb1..ccffe920d1 100644 --- a/src/app/shared/form/form.component.spec.ts +++ b/src/app/shared/form/form.component.spec.ts @@ -18,7 +18,7 @@ import { TranslateModule } from '@ngx-translate/core'; import { FormComponent } from './form.component'; import { FormService } from './form.service'; import { FormBuilderService } from './builder/form-builder.service'; -import { FormState } from './form.reducers'; +import { FormState } from './form.reducer'; import { FormChangeAction, FormStatusChangeAction } from './form.actions'; import { MockStore } from '../testing/mock-store'; import { FormFieldMetadataValueObject } from './builder/models/form-field-metadata-value.model'; diff --git a/src/app/shared/form/form.component.ts b/src/app/shared/form/form.component.ts index 922f2e40f8..5958bfeeba 100644 --- a/src/app/shared/form/form.component.ts +++ b/src/app/shared/form/form.component.ts @@ -24,7 +24,7 @@ import { Subscription } from 'rxjs/Subscription'; import { hasValue, isNotEmpty, isNotNull, isNull } from '../empty.util'; import { FormService } from './form.service'; import { formObjectFromIdSelector } from './selectors'; -import { FormEntry, FormError } from './form.reducers'; +import { FormEntry, FormError } from './form.reducer'; import { isEmpty } from 'lodash'; /** diff --git a/src/app/shared/form/form.reducer.spec.ts b/src/app/shared/form/form.reducer.spec.ts index a76bfa5c03..ddd5f96ecd 100644 --- a/src/app/shared/form/form.reducer.spec.ts +++ b/src/app/shared/form/form.reducer.spec.ts @@ -1,4 +1,4 @@ -import { FormEntry, formReducer } from './form.reducers'; +import { FormEntry, formReducer } from './form.reducer'; import { FormAddError, FormChangeAction, FormClearErrorsAction, diff --git a/src/app/shared/form/form.reducers.ts b/src/app/shared/form/form.reducer.ts similarity index 100% rename from src/app/shared/form/form.reducers.ts rename to src/app/shared/form/form.reducer.ts diff --git a/src/app/shared/form/form.service.spec.ts b/src/app/shared/form/form.service.spec.ts index 4c7e84b4f2..dda6e018c6 100644 --- a/src/app/shared/form/form.service.spec.ts +++ b/src/app/shared/form/form.service.spec.ts @@ -12,7 +12,7 @@ import { import { FormService } from './form.service'; import { FormBuilderService } from './builder/form-builder.service'; import { AppState } from '../../app.reducer'; -import { formReducer } from './form.reducers'; +import { formReducer } from './form.reducer'; describe('FormService test suite', () => { const formId = 'testForm'; diff --git a/src/app/shared/form/selectors.ts b/src/app/shared/form/selectors.ts index a4f27945ef..aeefb5f984 100644 --- a/src/app/shared/form/selectors.ts +++ b/src/app/shared/form/selectors.ts @@ -1,7 +1,7 @@ -import { createSelector, MemoizedSelector, Selector } from '@ngrx/store'; +import { createSelector, MemoizedSelector } from '@ngrx/store'; import { AppState } from '../../app.reducer'; -import { FormEntry, FormState } from './form.reducers'; +import { FormEntry, FormState } from './form.reducer'; export const formStateSelector = (state: AppState) => state.forms; diff --git a/src/app/shared/number-picker/number-picker.component.scss b/src/app/shared/number-picker/number-picker.component.scss index 4fa2f3ac21..0f7c4067ac 100644 --- a/src/app/shared/number-picker/number-picker.component.scss +++ b/src/app/shared/number-picker/number-picker.component.scss @@ -12,15 +12,11 @@ position: relative; top: 0.15em; transform: rotate(-45deg); - -webkit-transform: rotate(-45deg); - -ms-transform: rotate(-45deg); vertical-align: middle; width: 0.71em; } .chevron.bottom:before { top: -.3em; - -webkit-transform: rotate(135deg); - -ms-transform: rotate(135deg); transform: rotate(135deg); } diff --git a/src/app/shared/object.util.spec.ts b/src/app/shared/object.util.spec.ts index 9f0e687dc1..c910e59e97 100644 --- a/src/app/shared/object.util.spec.ts +++ b/src/app/shared/object.util.spec.ts @@ -1,4 +1,4 @@ -import { deleteProperty, difference, isObjectEmpty } from './object.util'; +import { deleteProperty, difference, hasOnlyEmptyProperties } from './object.util'; describe('Object Utils', () => { let object: any = {}; @@ -19,36 +19,36 @@ describe('Object Utils', () => { }); - describe('isObjectEmpty', () => { + describe('hasOnlyEmptyProperties', () => { it('should return true when object is empty', () => { object = {}; - expect(isObjectEmpty(object)).toBe(true); + expect(hasOnlyEmptyProperties(object)).toBe(true); }); it('should return true when object has a null property', () => { object = {a: null}; - expect(isObjectEmpty(object)).toBe(true); + expect(hasOnlyEmptyProperties(object)).toBe(true); }); it('should return true when object property has an empty array as value', () => { object = {a: []}; - expect(isObjectEmpty(object)).toBe(true); + expect(hasOnlyEmptyProperties(object)).toBe(true); }); it('should return true when object property has an empty object as value', () => { object = {a: {}}; - expect(isObjectEmpty(object)).toBe(true); + expect(hasOnlyEmptyProperties(object)).toBe(true); }); it('should return false when object is not empty', () => { object = {a: 'a', b: 'b'}; - expect(isObjectEmpty(object)).toBe(false); + expect(hasOnlyEmptyProperties(object)).toBe(false); }); it('should return false when object has at least a valued property', () => { object = {a: [], b: 'b'}; - expect(isObjectEmpty(object)).toBe(false); + expect(hasOnlyEmptyProperties(object)).toBe(false); }); }); diff --git a/src/app/shared/object.util.ts b/src/app/shared/object.util.ts index 1dccd2070a..60ed71096a 100644 --- a/src/app/shared/object.util.ts +++ b/src/app/shared/object.util.ts @@ -4,21 +4,21 @@ import { isEqual, isObject, transform } from 'lodash'; /** * Returns passed object without specified property */ -export function deleteProperty(object, key): object { +export function deleteProperty(object: object, key: string): object { const {[key]: deletedKey, ...otherKeys} = object; return otherKeys; } /** * Returns true if the passed object is empty or has only empty property. - * isObjectEmpty({}); // true - * isObjectEmpty({a: null}); // true - * isObjectEmpty({a: []}); // true - * isObjectEmpty({a: [], b: {}); // true - * isObjectEmpty({a: 'a', b: 'b'}); // false - * isObjectEmpty({a: [], b: 'b'}); // false + * hasOnlyEmptyProperties({}); // true + * hasOnlyEmptyProperties({a: null}); // true + * hasOnlyEmptyProperties({a: []}); // true + * hasOnlyEmptyProperties({a: [], b: {}); // true + * hasOnlyEmptyProperties({a: 'a', b: 'b'}); // false + * hasOnlyEmptyProperties({a: [], b: 'b'}); // false */ -export function isObjectEmpty(obj: any): boolean { +export function hasOnlyEmptyProperties(obj: object): boolean { const objectType = typeof obj; if (objectType === 'object') { if (Object.keys(obj).length === 0) { @@ -43,12 +43,12 @@ export function isObjectEmpty(obj: any): boolean { * difference({a: 'a', b: {}}, {a: 'a'}); // {} * difference({a: 'a'}, {a: 'a', b: 'b'}); // {} */ -export function difference(object, base) { +export function difference(object: object, base: object) { const changes = (o, b) => { return transform(o, (result, value, key) => { if (!isEqual(value, b[key]) && isNotEmpty(value)) { const resultValue = (isObject(value) && isObject(b[key])) ? changes(value, b[key]) : value; - if (!isObjectEmpty(resultValue)) { + if (!hasOnlyEmptyProperties(resultValue)) { result[key] = resultValue; } } diff --git a/src/app/shared/uploader/uploader.component.scss b/src/app/shared/uploader/uploader.component.scss index f0b54bfe15..7e0f6fdd23 100644 --- a/src/app/shared/uploader/uploader.component.scss +++ b/src/app/shared/uploader/uploader.component.scss @@ -20,19 +20,19 @@ } .ds-document-drop-zone-active { - z-index: 1025 !important; + z-index: $drop-zone-area-z-index !important; } .ds-document-drop-zone-inner { background-color: rgba($white, 0.7); - z-index: 1021; + z-index: $drop-zone-area-inner-z-index; top: 0; left: 0; } .ds-document-drop-zone-inner-content { border: 4px dashed map-get($theme-colors, primary); - z-index: 1021; + z-index: $drop-zone-area-inner-z-index; } .ds-document-drop-zone-inner-content p { diff --git a/src/styles/_custom_variables.scss b/src/styles/_custom_variables.scss index 4dcc161cc4..8372aa934d 100644 --- a/src/styles/_custom_variables.scss +++ b/src/styles/_custom_variables.scss @@ -3,4 +3,7 @@ $content-spacing: $spacer * 1.5; $button-height: $input-btn-padding-y * 2 + $input-btn-line-height + calculateRem($input-btn-border-width*2); $card-height-percentage:98%; $card-thumbnail-height:240px; +$dropdown-menu-max-height: 200px; +$drop-zone-area-z-index: 1025; +$drop-zone-area-inner-z-index: 1021;