AAE-28106 Make the header ( label + style ) optional with a checkbox [Preview/Runtime display] (#10408)

* AAE-28106 Update ContainerModel

* AAE-28106 Update HeaderWidgetComponent

* AAE-28106 Reorganize span element

* AAE-28106 Update tests
This commit is contained in:
Wiktor Danielewski 2024-11-20 16:50:52 +01:00 committed by GitHub
parent b4516bc998
commit beaf86d987
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 264 additions and 56 deletions

View File

@ -17,13 +17,154 @@
import { ContainerModel } from './container.model'; import { ContainerModel } from './container.model';
import { FormFieldModel } from './form-field.model'; import { FormFieldModel } from './form-field.model';
import { FormModel } from './form.model'; import { FormFieldTypes } from './form-field-types';
describe('ContainerModel', () => { describe('ContainerModel', () => {
let field: FormFieldModel;
it('should store the form reference', () => { beforeEach(() => {
const form = new FormModel(); field = new FormFieldModel(null, {
const model = new ContainerModel(new FormFieldModel(form)); id: 'group-id',
expect(model.form).toBe(form); name: 'group-name',
type: FormFieldTypes.GROUP,
params: {
allowCollapse: false,
collapseByDefault: false,
hideHeader: false
},
numberOfColumns: 1,
tab: null
});
});
it('should initialize with default values', () => {
const container = new ContainerModel(field);
expect(container.field).toBe(field);
expect(container.columns).toEqual([]);
expect(container.isExpanded).toBe(true);
expect(container.rowspan).toBe(1);
expect(container.colspan).toBe(1);
});
describe('isVisible getter', () => {
it('should return true when field is visible', () => {
const container = new ContainerModel(field);
expect(container.isVisible).toBe(true);
});
it('should return false when field is NOT visible', () => {
field.isVisible = false;
const container = new ContainerModel(field);
expect(container.isVisible).toBe(false);
});
});
describe('isTypeFieldGroup getter', () => {
it('should return true when field is a group', () => {
const container = new ContainerModel(field);
expect(container.isTypeFieldGroup).toBe(true);
});
it('should return false when field is NOT a group', () => {
const container = new ContainerModel(new FormFieldModel(null, { type: FormFieldTypes.CONTAINER }));
expect(container.isTypeFieldGroup).toBe(false);
});
});
describe('isCollapsible getter', () => {
it('should return false when field is NOT a group', () => {
const container = new ContainerModel(new FormFieldModel(null, { type: FormFieldTypes.CONTAINER }));
expect(container.isTypeFieldGroup).toBe(false);
expect(container.isCollapsible).toBe(false);
});
it('should return false when field is group and allowCollapse is false', () => {
const container = new ContainerModel(field);
expect(container.isTypeFieldGroup).toBe(true);
expect(container.isCollapsible).toBe(false);
});
it('should return true when field is a group and allowCollapse is true', () => {
field.params.allowCollapse = true;
const container = new ContainerModel(field);
expect(container.isTypeFieldGroup).toBe(true);
expect(container.isCollapsible).toBe(true);
});
it('should return false when field is a group and allowCollapse is NOT set', () => {
field.params.allowCollapse = undefined;
const container = new ContainerModel(field);
expect(container.isTypeFieldGroup).toBe(true);
expect(container.isCollapsible).toBe(false);
});
});
describe('isCollapsedByDefault getter', () => {
it('should return false when field is NOT a group', () => {
const container = new ContainerModel(new FormFieldModel(null, { type: FormFieldTypes.CONTAINER }));
expect(container.isTypeFieldGroup).toBe(false);
expect(container.isCollapsedByDefault).toBe(false);
});
it('should return false when field is group and collapseByDefault is false', () => {
const container = new ContainerModel(field);
expect(container.isTypeFieldGroup).toBe(true);
expect(container.isCollapsedByDefault).toBe(false);
});
it('should return true when field is a group and collapseByDefault is true', () => {
field.params.collapseByDefault = true;
const container = new ContainerModel(field);
expect(container.isTypeFieldGroup).toBe(true);
expect(container.isCollapsedByDefault).toBe(true);
});
it('should return false when field is a group and collapseByDefault is NOT set', () => {
field.params.collapseByDefault = undefined;
const container = new ContainerModel(field);
expect(container.isTypeFieldGroup).toBe(true);
expect(container.isCollapsedByDefault).toBe(false);
});
});
describe('hideHeader getter', () => {
it('should return false when field is NOT a group', () => {
const container = new ContainerModel(new FormFieldModel(null, { type: FormFieldTypes.CONTAINER }));
expect(container.hideHeader).toBe(false);
});
it('should return false when field is a group and hideHeader is false', () => {
const container = new ContainerModel(field);
expect(container.hideHeader).toBe(false);
});
it('should return true when field is a group and hideHeader is true', () => {
field.params.hideHeader = true;
const container = new ContainerModel(field);
expect(container.hideHeader).toBe(true);
});
it('should return false when field is a group and hideHeader is NOT set', () => {
field.params.hideHeader = undefined;
const container = new ContainerModel(field);
expect(container.hideHeader).toBe(false);
});
}); });
}); });

View File

@ -15,7 +15,7 @@
* limitations under the License. * limitations under the License.
*/ */
/* eslint-disable @angular-eslint/component-selector */ /* eslint-disable @angular-eslint/component-selector */
import { FormFieldModel } from './form-field.model'; import { FormFieldModel } from './form-field.model';
import { FormWidgetModel } from './form-widget.model'; import { FormWidgetModel } from './form-widget.model';
@ -23,7 +23,6 @@ import { ContainerColumnModel } from './container-column.model';
import { FormFieldTypes } from './form-field-types'; import { FormFieldTypes } from './form-field-types';
export class ContainerModel extends FormWidgetModel { export class ContainerModel extends FormWidgetModel {
field: FormFieldModel; field: FormFieldModel;
readonly columns: ContainerColumnModel[] = []; readonly columns: ContainerColumnModel[] = [];
@ -31,43 +30,35 @@ export class ContainerModel extends FormWidgetModel {
readonly rowspan: number = 1; readonly rowspan: number = 1;
readonly colspan: number = 1; readonly colspan: number = 1;
get isVisible(): boolean {
return this.field.isVisible;
}
constructor(field: FormFieldModel) { constructor(field: FormFieldModel) {
super(field.form, field.json); super(field.form, field.json);
if (field) { if (field) {
this.field = field; this.field = field;
this.columns = field.columns || []; this.columns = field.columns || [];
this.isExpanded = !this.isCollapsedByDefault(); this.isExpanded = !this.isCollapsedByDefault;
this.colspan = field.colspan; this.colspan = field.colspan;
this.rowspan = field.rowspan; this.rowspan = field.rowspan;
} }
} }
isGroup(): boolean { get isVisible(): boolean {
return this.field.isVisible;
}
get isTypeFieldGroup(): boolean {
return this.type === FormFieldTypes.GROUP; return this.type === FormFieldTypes.GROUP;
} }
isCollapsible(): boolean { get isCollapsible(): boolean {
let allowCollapse = false; return this.isTypeFieldGroup && (this.field.params?.allowCollapse ?? false);
if (this.isGroup() && this.field.params['allowCollapse']) {
allowCollapse = this.field.params['allowCollapse'];
}
return allowCollapse;
} }
isCollapsedByDefault(): boolean { get isCollapsedByDefault(): boolean {
let collapseByDefault = false; return this.isTypeFieldGroup && (this.field.params?.collapseByDefault ?? false);
}
if (this.isCollapsible() && this.field.params['collapseByDefault']) { get hideHeader(): boolean {
collapseByDefault = this.field.params['collapseByDefault']; return this.isTypeFieldGroup && (this.field.params?.hideHeader ?? false);
}
return collapseByDefault;
} }
} }

View File

@ -1,18 +1,37 @@
<div [hidden]="!element?.isGroup()" [style]="element | adfFieldStyle" class="adf-container-widget-header"> <div
<h4 class="adf-container-widget-header__text" id="container-header" *ngIf="element?.isTypeFieldGroup"
[class.adf-collapsible]="element?.isCollapsible()"> [style]="element | adfFieldStyle"
<button *ngIf="element?.isCollapsible()" class="adf-container-widget-header"
mat-icon-button >
class="mdl-button--icon" <div *ngIf="element?.hideHeader">
(click)="onExpanderClicked(element)"> <ng-container *ngTemplateOutlet="expandButton; context: { $implicit: element }" />
<mat-icon>{{ element?.isExpanded ? 'expand_more' : 'expand_less' }}</mat-icon> </div>
</button> <h4
<span (click)="onExpanderClicked(element)" *ngIf="!element?.hideHeader"
role="button" id="container-header"
tabindex="0" class="adf-container-widget-header__text"
(keyup.enter)="onExpanderClicked(element)" [class.adf-collapsible]="element?.isCollapsible"
[id]="'container-header-label-' + element?.id"> >
<ng-container *ngTemplateOutlet="expandButton; context: { $implicit: element }" />
<span
[id]="'container-header-label-' + element?.id"
role="button"
tabindex="0"
(keyup.enter)="onExpanderClicked(element)"
(click)="onExpanderClicked(element)"
>
{{ element.name | translate }} {{ element.name | translate }}
</span> </span>
</h4> </h4>
<ng-template #expandButton let-element>
<button
*ngIf="element?.isCollapsible"
mat-icon-button
class="mdl-button--icon"
(click)="onExpanderClicked(element)"
>
<mat-icon> {{ element?.isExpanded ? 'expand_more' : 'expand_less' }} </mat-icon>
</button>
</ng-template>
</div> </div>

View File

@ -16,11 +16,12 @@
*/ */
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { TranslateModule } from '@ngx-translate/core';
import { ContainerModel } from '../core/container.model'; import { ContainerModel } from '../core/container.model';
import { FormFieldTypes } from '../core/form-field-types'; import { FormFieldTypes } from '../core/form-field-types';
import { FormFieldModel } from '../core/form-field.model'; import { FormFieldModel } from '../core/form-field.model';
import { HeaderWidgetComponent } from './header.widget'; import { HeaderWidgetComponent } from './header.widget';
import { NoopTranslateModule } from '../../../../testing';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
describe('HeaderWidgetComponent', () => { describe('HeaderWidgetComponent', () => {
let component: HeaderWidgetComponent; let component: HeaderWidgetComponent;
@ -28,8 +29,8 @@ describe('HeaderWidgetComponent', () => {
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [HeaderWidgetComponent, TranslateModule.forRoot()] imports: [HeaderWidgetComponent, NoopTranslateModule, NoopAnimationsModule]
}).compileComponents(); });
}); });
beforeEach(() => { beforeEach(() => {
@ -46,24 +47,80 @@ describe('HeaderWidgetComponent', () => {
type: FormFieldTypes.GROUP, type: FormFieldTypes.GROUP,
name: 'test-name', name: 'test-name',
id: 'test-id', id: 'test-id',
params: { allowCollapse: true } params: {
hideHeader: false,
allowCollapse: false,
collapseByDefault: false
}
} }
) )
) )
); );
fixture.detectChanges();
}); });
it('should render the header widget template', () => { it('should render header widget template when type is group', () => {
const nativeElement = fixture.nativeElement; fixture.detectChanges();
expect(nativeElement.querySelector('.adf-container-widget-header')).toBeTruthy();
expect(nativeElement.querySelector('#container-header-label-test-id').textContent.trim()).toEqual('test-name'); expect(fixture.nativeElement.querySelector('.adf-container-widget-header')).not.toBe(null);
expect(fixture.nativeElement.querySelector('#container-header-label-test-id').textContent.trim()).toEqual('test-name');
});
it('should NOT render header widget template when type is different then group', () => {
spyOnProperty(component.element, 'isTypeFieldGroup').and.returnValue(false);
fixture.detectChanges();
expect(fixture.nativeElement.querySelector('.adf-container-widget-header')).toBe(null);
});
it('should display header text when hideHeader is set to false', () => {
fixture.detectChanges();
expect(fixture.nativeElement.querySelector('.adf-container-widget-header__text')).not.toBe(null);
});
it('should NOT display header text when hideHeader is set to true', () => {
component.element.json.params.hideHeader = true;
fixture.detectChanges();
expect(fixture.nativeElement.querySelector('.adf-container-widget-header__text')).toBe(null);
});
it('should display expander when allowCollapse is set to true', () => {
component.element.json.params.allowCollapse = true;
fixture.detectChanges();
expect(fixture.nativeElement.querySelector('.mdl-button--icon')).not.toBe(null);
});
it('should NOT display expander when allowCollapse is set to false', () => {
fixture.detectChanges();
expect(fixture.nativeElement.querySelector('.mdl-button--icon')).toBe(null);
}); });
it('should call onExpanderClicked method when expander is clicked', () => { it('should call onExpanderClicked method when expander is clicked', () => {
component.element.json.params.allowCollapse = true;
fixture.detectChanges();
spyOn(component, 'onExpanderClicked'); spyOn(component, 'onExpanderClicked');
const expander = fixture.nativeElement.querySelector('#container-header-label-test-id');
const expander = fixture.nativeElement.querySelector('.mdl-button--icon');
expander.click(); expander.click();
expect(component.onExpanderClicked).toHaveBeenCalled();
expect(component.onExpanderClicked).toHaveBeenCalledWith(component.element);
});
it('should call onExpanderClicked method when header text is clicked', () => {
fixture.detectChanges();
spyOn(component, 'onExpanderClicked');
const headerText = fixture.nativeElement.querySelector('#container-header-label-test-id');
headerText.click();
expect(component.onExpanderClicked).toHaveBeenCalledWith(component.element);
}); });
}); });

View File

@ -19,7 +19,7 @@ import { Component, Input, ViewEncapsulation } from '@angular/core';
import { ContainerModel } from '../core/container.model'; import { ContainerModel } from '../core/container.model';
import { FieldStylePipe } from './../../../pipes/field-style.pipe'; import { FieldStylePipe } from './../../../pipes/field-style.pipe';
import { MatIconModule } from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon';
import { NgIf } from '@angular/common'; import { NgIf, NgTemplateOutlet } from '@angular/common';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
@ -29,13 +29,13 @@ import { MatButtonModule } from '@angular/material/button';
styleUrls: ['./header.widget.scss'], styleUrls: ['./header.widget.scss'],
standalone: true, standalone: true,
encapsulation: ViewEncapsulation.None, encapsulation: ViewEncapsulation.None,
imports: [FieldStylePipe, MatIconModule, MatButtonModule, TranslateModule, NgIf] imports: [FieldStylePipe, MatIconModule, MatButtonModule, TranslateModule, NgIf, NgTemplateOutlet]
}) })
export class HeaderWidgetComponent { export class HeaderWidgetComponent {
@Input() element: ContainerModel; @Input() element: ContainerModel;
onExpanderClicked(content: ContainerModel) { onExpanderClicked(content: ContainerModel) {
if (content?.isCollapsible()) { if (content?.isCollapsible) {
content.isExpanded = !content.isExpanded; content.isExpanded = !content.isExpanded;
} }
} }