[AAE-3587] Use grid to allow using rowspan (#6151)

* AAE-3587 Use material grid to allow using rowspan

* AAE-3587 Fix column field rowspan

* AAE-3587 Add enableFixedSpace default value

* AAE-3587 Change material grid to CSS grid area

* AAE-3587 Review comments

* AAE-3587 Refactor for clarification of the complex method

* AAE-3587 Add types to method signatures
This commit is contained in:
Pablo Martinez Garcia
2020-10-16 15:36:58 +02:00
committed by GitHub
parent 04f5e35e05
commit b3e8ff27af
7 changed files with 201 additions and 58 deletions

View File

@@ -329,15 +329,15 @@ describe('Form Renderer Component', () => {
formRendererComponent.formDefinition = formService.parseForm(colspanForm.formRepresentation.formDefinition, null , false, false);
fixture.detectChanges();
await fixture.whenStable();
const formSizedElement = fixture.nativeElement.querySelector('#field-2bc275fb-e113-4d7d-885f-6e74a7332d40-container section.adf-grid-list-column-view');
const formSizedElement = fixture.nativeElement.querySelector('#field-2bc275fb-e113-4d7d-885f-6e74a7332d40-container div.adf-grid-list');
expectElementToBeVisible(formSizedElement);
const sectionGridElement: HTMLElement[] = fixture.nativeElement.querySelectorAll('#field-2bc275fb-e113-4d7d-885f-6e74a7332d40-container section .adf-grid-list-single-column');
const sectionGridElement: HTMLElement[] = fixture.nativeElement.querySelectorAll('#field-2bc275fb-e113-4d7d-885f-6e74a7332d40-container div .adf-grid-list-item');
sectionGridElement.forEach((element) => {
expect(element.style['width']).toBe('50%', 'Elemens is wrong sized for this section');
expect(element.style['grid-area']).toBe('auto / auto / span 1 / span 1', 'Elemens is wrong sized for this section');
});
const fullWidthElement = fixture.nativeElement.querySelector('#field-d52ada4e-cbdc-4f0c-a480-5b85fa00e4f8-container section.adf-grid-list-column-view .adf-grid-list-single-column');
expect(fullWidthElement.style['width']).toBe('100%');
const fullWidthElement = fixture.nativeElement.querySelector('#field-d52ada4e-cbdc-4f0c-a480-5b85fa00e4f8-container div.adf-grid-list .adf-grid-list-item');
expect(fullWidthElement.style['grid-area']).toBe('auto / auto / span 1 / span 2');
});
it('[C309655] - Should display validation error message when Number widget has invalid value', async () => {

View File

@@ -12,11 +12,13 @@
</div>
<div *ngIf="field?.form?.enableFixedSpace else fixingTemplate">
<section class="adf-grid-list" *ngIf="content?.isExpanded">
<div class="adf-grid-list-item" *ngFor="let field of fields" [style.width.%]="getColumnWith(field)">
<div class="adf-grid-list" [ngStyle]="{ 'grid-template-columns': 'repeat('+numberOfColumns+', 1fr)'}"
*ngIf="content?.isExpanded">
<div class="adf-grid-list-item" *ngFor="let field of fields"
[ngStyle]="{'grid-area': 'auto / auto / span '+(field?.rowspan || 1)+' / span '+(field?.colspan || 1)}">
<adf-form-field *ngIf="field" [field]="field"></adf-form-field>
</div>
</section>
</div>
</div>
<ng-template #fixingTemplate>
@@ -28,5 +30,3 @@
</div>
</section>
</ng-template>

View File

@@ -57,14 +57,10 @@
}
.adf-grid-list {
display: flex;
flex-wrap: wrap;
margin-left: -1%;
margin-right: -1%;
display: grid;
}
.adf-grid-list-item {
flex-grow: 1;
box-sizing: border-box;
padding-left: 1%;
padding-right: 1%;
@@ -119,5 +115,9 @@
width: 80%;
}
adf-form-field {
width: 100%;
}
}
}

View File

@@ -17,7 +17,6 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ContainerColumnModel } from './../core/container-column.model';
import { FormFieldTypes } from './../core/form-field-types';
import { FormFieldModel } from './../core/form-field.model';
import { FormModel } from './../core/form.model';
@@ -103,7 +102,7 @@ describe('ContainerWidgetComponent', () => {
it('should send an event when a value is changed in the form', (done) => {
const fakeForm = new FormModel();
const fakeField = new FormFieldModel(fakeForm, {id: 'fakeField', value: 'fakeValue'});
const fakeField = new FormFieldModel(fakeForm, { id: 'fakeField', value: 'fakeValue' });
widget.fieldChanged.subscribe((field) => {
expect(field).not.toBe(null);
expect(field.id).toBe('fakeField');
@@ -117,30 +116,22 @@ describe('ContainerWidgetComponent', () => {
describe('fields', () => {
it('should serializes the content fields', () => {
const field1 = <FormFieldModel> {id: '1'},
field2 = <FormFieldModel> {id: '2'},
field3 = <FormFieldModel> {id: '3'},
field4 = <FormFieldModel> {id: '4'},
field5 = <FormFieldModel> {id: '5'},
field6 = <FormFieldModel> {id: '6'};
const form = new FormModel();
const json = {
id: 'test',
name: 'test',
type: 'container',
tab: null,
fields: {
'1' : [{ id: '1' }, { id: '2' }, { id: '3' }],
'2' : [{ id: '4' }, { id: '5' }],
'3' : [{ id: '6' }]
}
};
const container = new ContainerWidgetComponentModel(new FormFieldModel(new FormModel()));
container.columns = [
<ContainerColumnModel> { fields: [
field1,
field2,
field3
] },
<ContainerColumnModel> { fields: [
field4,
field5
] },
<ContainerColumnModel> { fields: [
field6
] }
];
widget.content = container;
const field = new FormFieldModel(form, json);
widget.field = field;
widget.ngOnInit();
expect(widget.fields[0].id).toEqual('1');
expect(widget.fields[1].id).toEqual('4');
@@ -152,6 +143,99 @@ describe('ContainerWidgetComponent', () => {
expect(widget.fields[7]).toEqual(undefined);
expect(widget.fields[8]).toEqual(undefined);
});
it('should serializes the content fields with rowspan', () => {
const form = new FormModel();
const json = {
id: 'test',
name: 'test',
type: 'container',
tab: null,
fields: {
'1': [
{
id: 'a',
colspan: 2,
rowspan: 1
},
{
id: 'b'
},
{
id: 'c'
},
{
id: 'd'
},
{
id: 'e',
colspan: 3
},
{
id: 'f'
},
{
id: 'g'
},
{
id: 'h',
colspan: 2
}
],
'2': [
{
id: '1',
rowspan: 3
},
{
id: '2',
rowspan: 2,
colspan: 2
},
{
id: '3'
}
],
'3': [
{
id: 'white'
},
{
id: 'black'
},
{
id: 'green',
rowspan: 2
},
{
id: 'yellow'
}
]
}
};
const field = new FormFieldModel(form, json);
widget.field = field;
widget.ngOnInit();
expect(widget.fields.length).toEqual(17);
expect(widget.fields[0].id).toEqual('a');
expect(widget.fields[1].id).toEqual('white');
expect(widget.fields[2].id).toEqual('b');
expect(widget.fields[3].id).toEqual('1');
expect(widget.fields[4].id).toEqual('black');
expect(widget.fields[5].id).toEqual('c');
expect(widget.fields[6].id).toEqual('green');
expect(widget.fields[7].id).toEqual('d');
expect(widget.fields[8].id).toEqual('e');
expect(widget.fields[9].id).toEqual('f');
expect(widget.fields[10].id).toEqual('2');
expect(widget.fields[11].id).toEqual('g');
expect(widget.fields[12].id).toEqual('h');
expect(widget.fields[13].id).toEqual('yellow');
expect(widget.fields[14]).toEqual(undefined);
expect(widget.fields[15].id).toEqual('3');
expect(widget.fields[16]).toEqual(undefined);
});
});
describe('getColumnWith', () => {

View File

@@ -15,7 +15,7 @@
* limitations under the License.
*/
/* tslint:disable:component-selector */
/* tslint:disable:component-selector */
import { AfterViewInit, Component, OnInit, ViewEncapsulation } from '@angular/core';
import { FormService } from './../../../services/form.service';
@@ -43,9 +43,11 @@ import { ContainerWidgetComponentModel } from './container.widget.model';
export class ContainerWidgetComponent extends WidgetComponent implements OnInit, AfterViewInit {
content: ContainerWidgetComponentModel;
numberOfColumns: number;
fields: FormFieldModel[];
constructor(public formService: FormService) {
super(formService);
super(formService);
}
onExpanderClicked() {
@@ -57,34 +59,90 @@ export class ContainerWidgetComponent extends WidgetComponent implements OnInit,
ngOnInit() {
if (this.field) {
this.content = new ContainerWidgetComponentModel(this.field);
this.getNumberOfColumnsFromTheBiggestBetweenJsonAndColumnsLengthOrOne();
this.fields = this.getFields();
}
}
private getNumberOfColumnsFromTheBiggestBetweenJsonAndColumnsLengthOrOne() {
this.numberOfColumns = (this.content.json?.numberOfColumns || 1) > (this.content.columns?.length || 1) ?
(this.content.json?.numberOfColumns || 1) :
(this.content.columns?.length || 1);
}
/**
* Serializes column fields
*/
get fields(): FormFieldModel[] {
const fields = [];
private getFields(): FormFieldModel[] {
const { size, rowspanOffset, numberOfColumnElementsToBeProcessedRemaining , fields } = this.initializeHelpers();
let rowContainsElement = true,
rowIndex = 0;
while (rowContainsElement) {
rowContainsElement = false;
for (let i = 0; i < this.content.columns.length; i++ ) {
const field = this.content.columns[i].fields[rowIndex];
if (field) {
rowContainsElement = true;
for (let i = 0; i < size; i++) {
let fieldExist = false;
let columnIndex = 0;
while (columnIndex < this.numberOfColumns) {
let field: FormFieldModel;
if (rowspanOffset[columnIndex] > 0) {
this.decreaseRowspanOffsetForColumn(rowspanOffset, columnIndex);
} else {
field = this.getNextFieldToAdd(columnIndex, numberOfColumnElementsToBeProcessedRemaining, field);
fields.push(field);
if (field) {
fieldExist = true;
}
this.updateColumnsRowspanOffsetWithFieldRowspan(field, rowspanOffset, columnIndex);
numberOfColumnElementsToBeProcessedRemaining[columnIndex] = numberOfColumnElementsToBeProcessedRemaining[columnIndex] - 1;
}
fields.push(field);
columnIndex = columnIndex + (field?.colspan || 1);
}
if (!fieldExist) {
i = this.deleteLastEmptyRowAndExit(fields, i, size);
}
rowIndex++;
}
return fields;
}
private updateColumnsRowspanOffsetWithFieldRowspan(field: FormFieldModel, rowspanOffset: any[], columnIndex: number) {
for (let k = 0; k < (field?.colspan || 1); k++) {
rowspanOffset[columnIndex + k] = field?.rowspan > 0 ? field?.rowspan - 1 : 0;
}
}
private getNextFieldToAdd(columnIndex: number, numberOfColumnElementsToBeProcessedRemaining: any[], field: FormFieldModel): FormFieldModel {
const rowToCompute = (this.content.columns[columnIndex]?.fields?.length || 0) - numberOfColumnElementsToBeProcessedRemaining[columnIndex];
field = this.content.columns[columnIndex]?.fields[rowToCompute];
return field;
}
private decreaseRowspanOffsetForColumn(rowspanOffset: any[], columnIndex: number) {
rowspanOffset[columnIndex] = rowspanOffset[columnIndex] - 1;
}
private initializeHelpers(): {
size: number;
rowspanOffset: number[];
numberOfColumnElementsToBeProcessedRemaining: number[];
fields: FormFieldModel[];
} {
const fields = [];
const numberOfColumnElementsToBeProcessedRemaining: number[] = [];
const rowspanOffset: number[] = [];
let size = 0;
for (let i = 0; i < this.numberOfColumns; i++) {
numberOfColumnElementsToBeProcessedRemaining.push(this.content.columns[i]?.fields?.length || 0);
rowspanOffset[i] = 0;
size += (this.content.columns[i]?.fields?.length || 0);
}
return { size, rowspanOffset, numberOfColumnElementsToBeProcessedRemaining, fields };
}
private deleteLastEmptyRowAndExit(fields: FormFieldModel[], i: number, size: number) {
for (let j = 0; j < this.numberOfColumns; j++) {
fields.pop();
}
i = size;
return i;
}
/**
* Calculate the column width based on the numberOfColumns and current field's colspan property
*

View File

@@ -152,6 +152,7 @@ export class FormFieldModel extends FormWidgetModel {
this.restIdProperty = json.restIdProperty;
this.restLabelProperty = json.restLabelProperty;
this.colspan = <number> json.colspan;
this.rowspan = <number> json.rowspan;
this.minLength = <number> json.minLength || 0;
this.maxLength = <number> json.maxLength || 0;
this.minValue = json.minValue;

View File

@@ -99,7 +99,7 @@ export class FormModel {
this.className = json.className || '';
this.variables = json.variables || [];
this.processVariables = json.processVariables || [];
this.enableFixedSpace = enableFixedSpace;
this.enableFixedSpace = enableFixedSpace || true;
const tabCache: FormWidgetModelCache<TabModel> = {};