[AAE-8764] Enable left labels in text, number and dropdown cloud widget (#7628)

This commit is contained in:
Rubén Barroso 2022-05-16 10:14:45 +02:00 committed by GitHub
parent cec9297e14
commit 1762ba5af1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 315 additions and 74 deletions

View File

@ -106,6 +106,19 @@
font-weight: bold; font-weight: bold;
} }
} }
&-left-label-input-container {
display: flex;
div:nth-child(2) {
flex: 1;
}
}
&-left-label {
line-height: 64px;
margin-right: 15px;
}
} }
form-field { form-field {

View File

@ -76,6 +76,7 @@ export class FormFieldModel extends FormWidgetModel {
rule?: FormFieldRule; rule?: FormFieldRule;
selectLoggedUser: boolean; selectLoggedUser: boolean;
groupsRestriction: string[]; groupsRestriction: string[];
leftLabels: boolean = false;
// container model members // container model members
numberOfColumns: number = 1; numberOfColumns: number = 1;
@ -219,6 +220,10 @@ export class FormFieldModel extends FormWidgetModel {
} }
} }
if (form?.json) {
this.leftLabels = form.json.leftLabels || false;
}
const emptyOption = Array.isArray(this.options) ? this.options.find(({ id }) => id === 'empty') : undefined; const emptyOption = Array.isArray(this.options) ? this.options.find(({ id }) => id === 'empty') : undefined;
if (this.hasEmptyValue === undefined) { if (this.hasEmptyValue === undefined) {
this.hasEmptyValue = json?.hasEmptyValue ?? !!emptyOption; this.hasEmptyValue = json?.hasEmptyValue ?? !!emptyOption;

View File

@ -64,6 +64,10 @@ ul > li > form-field > .adf-focus {
border-color: var(--theme-warn-color); border-color: var(--theme-warn-color);
} }
} }
&-left-label {
color: var(--theme-secondary-text-color);
}
} }
/* query for Microsoft IE 11 */ /* query for Microsoft IE 11 */

View File

@ -1,23 +1,28 @@
<div class="adf-textfield adf-number-widget {{field.className}}" <div class="adf-textfield adf-number-widget {{field.className}}"
[class.adf-invalid]="!field.isValid && isTouched()" [class.adf-readonly]="field.readOnly"> [class.adf-invalid]="!field.isValid && isTouched()" [class.adf-readonly]="field.readOnly" [class.adf-left-label-input-container]="field.leftLabels">
<mat-form-field [hideRequiredMarker]="true"> <div *ngIf="field.leftLabels">
<label class="adf-label" [attr.for]="field.id">{{field.name | translate }}<span class="adf-asterisk" *ngIf="isRequired()">*</span></label> <label class="adf-label adf-left-label" [attr.for]="field.id">{{field.name | translate }}<span class="adf-asterisk" *ngIf="isRequired()">*</span></label>
<input matInput </div>
class="adf-input" <div>
type="text" <mat-form-field [hideRequiredMarker]="true">
pattern="-?[0-9]*(\.[0-9]+)?" <label class="adf-label" *ngIf="!field.leftLabels" [attr.for]="field.id">{{field.name | translate }}<span class="adf-asterisk" *ngIf="isRequired()">*</span></label>
[id]="field.id" <input matInput
[required]="isRequired()" class="adf-input"
[value]="displayValue" type="text"
[(ngModel)]="field.value" pattern="-?[0-9]*(\.[0-9]+)?"
(ngModelChange)="onFieldChanged(field)" [id]="field.id"
[disabled]="field.readOnly" [required]="isRequired()"
[placeholder]="field.placeholder" [value]="displayValue"
[matTooltip]="field.tooltip" [(ngModel)]="field.value"
(blur)="markAsTouched()" (ngModelChange)="onFieldChanged(field)"
matTooltipPosition="above" [disabled]="field.readOnly"
matTooltipShowDelay="1000"> [placeholder]="field.placeholder"
</mat-form-field> [matTooltip]="field.tooltip"
<error-widget [error]="field.validationSummary" ></error-widget> (blur)="markAsTouched()"
<error-widget *ngIf="isInvalidFieldRequired() && isTouched()" required="{{ 'FORM.FIELD.REQUIRED' | translate }}"></error-widget> matTooltipPosition="above"
matTooltipShowDelay="1000">
</mat-form-field>
<error-widget [error]="field.validationSummary" ></error-widget>
<error-widget *ngIf="isInvalidFieldRequired() && isTouched()" required="{{ 'FORM.FIELD.REQUIRED' | translate }}"></error-widget>
</div>
</div> </div>

View File

@ -15,17 +15,101 @@
* limitations under the License. * limitations under the License.
*/ */
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { FormsModule } from '@angular/forms';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { TranslateModule } from '@ngx-translate/core';
import { CoreTestingModule, setupTestBed } from 'core/testing';
import { FormFieldModel, FormFieldTypes, FormModel } from '../core';
import { NumberWidgetComponent } from './number.widget'; import { NumberWidgetComponent } from './number.widget';
describe('NumberWidgetComponent', () => { describe('NumberWidgetComponent', () => {
let widget: NumberWidgetComponent; let widget: NumberWidgetComponent;
let fixture: ComponentFixture<NumberWidgetComponent>;
let element: HTMLElement;
setupTestBed({
imports: [
TranslateModule.forRoot(),
CoreTestingModule,
MatInputModule,
FormsModule,
MatIconModule
]
});
beforeEach(() => { beforeEach(() => {
widget = new NumberWidgetComponent(null, null); fixture = TestBed.createComponent(NumberWidgetComponent);
widget = fixture.componentInstance;
element = fixture.nativeElement;
}); });
it('should exist', () => { it('should exist', () => {
expect(widget).toBeDefined(); expect(widget).toBeDefined();
}); });
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: 'number-id',
name: 'number-name',
value: '',
type: FormFieldTypes.NUMBER,
readOnly: false,
required: true
});
fixture.detectChanges();
await fixture.whenStable();
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', async () => {
widget.field = new FormFieldModel(new FormModel({ taskId: 'fake-task-id', leftLabels: false }), {
id: 'number-id',
name: 'number-name',
value: '',
type: FormFieldTypes.NUMBER,
readOnly: false,
required: true
});
fixture.detectChanges();
await fixture.whenStable();
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', async () => {
widget.field = new FormFieldModel(new FormModel({ taskId: 'fake-task-id' }), {
id: 'number-id',
name: 'number-name',
value: '',
type: FormFieldTypes.NUMBER,
readOnly: false,
required: true
});
fixture.detectChanges();
await fixture.whenStable();
const widgetContainer = element.querySelector('.adf-left-label-input-container');
expect(widgetContainer).toBeNull();
const adfLeftLabel = element.querySelector('.adf-left-label');
expect(adfLeftLabel).toBeNull();
});
});
}); });

View File

@ -1,23 +1,28 @@
<div class="adf-textfield adf-text-widget {{field.className}}" <div class="adf-textfield adf-text-widget {{field.className}}"
[class.adf-invalid]="!field.isValid && isTouched()" [class.adf-readonly]="field.readOnly"> [class.adf-invalid]="!field.isValid && isTouched()" [class.adf-readonly]="field.readOnly" [class.adf-left-label-input-container]="field.leftLabels">
<mat-form-field [hideRequiredMarker]="true"> <div *ngIf="field.leftLabels">
<label class="adf-label" [attr.for]="field.id">{{field.name | translate }}<span class="adf-asterisk" *ngIf="isRequired()">*</span></label> <label class="adf-label adf-left-label" [attr.for]="field.id">{{field.name | translate }}<span class="adf-asterisk" *ngIf="isRequired()">*</span></label>
<input matInput </div>
class="adf-input" <div>
type="text" <mat-form-field [hideRequiredMarker]="true">
[id]="field.id" <label class="adf-label" *ngIf="!field.leftLabels" [attr.for]="field.id">{{field.name | translate }}<span class="adf-asterisk" *ngIf="isRequired()">*</span></label>
[required]="isRequired()" <input matInput
[value]="field.value" class="adf-input"
[(ngModel)]="field.value" type="text"
(ngModelChange)="onFieldChanged(field)" [id]="field.id"
[disabled]="field.readOnly || readOnly" [required]="isRequired()"
[textMask]="{mask: mask, isReversed: isMaskReversed}" [value]="field.value"
[placeholder]="placeholder" [(ngModel)]="field.value"
[matTooltip]="field.tooltip" (ngModelChange)="onFieldChanged(field)"
(blur)="markAsTouched()" [disabled]="field.readOnly || readOnly"
matTooltipPosition="above" [textMask]="{mask: mask, isReversed: isMaskReversed}"
matTooltipShowDelay="1000"> [placeholder]="placeholder"
</mat-form-field> [matTooltip]="field.tooltip"
<error-widget [error]="field.validationSummary"></error-widget> (blur)="markAsTouched()"
<error-widget *ngIf="isInvalidFieldRequired() && isTouched()" required="{{ 'FORM.FIELD.REQUIRED' | translate }}"></error-widget> matTooltipPosition="above"
</div> matTooltipShowDelay="1000">
</mat-form-field>
<error-widget [error]="field.validationSummary"></error-widget>
<error-widget *ngIf="isInvalidFieldRequired() && isTouched()" required="{{ 'FORM.FIELD.REQUIRED' | translate }}"></error-widget>
</div>
</div>

View File

@ -442,5 +442,68 @@ describe('TextWidgetComponent', () => {
expect(label.innerText).toBe('Phone : (__) ___-___'); expect(label.innerText).toBe('Phone : (__) ___-___');
}); });
}); });
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: 'text-id',
name: 'text-name',
value: '',
type: FormFieldTypes.TEXT,
readOnly: false,
required: true
});
fixture.detectChanges();
await fixture.whenStable();
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', async () => {
widget.field = new FormFieldModel(new FormModel({ taskId: 'fake-task-id', leftLabels: false }), {
id: 'text-id',
name: 'text-name',
value: '',
type: FormFieldTypes.TEXT,
readOnly: false,
required: true
});
fixture.detectChanges();
await fixture.whenStable();
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', async () => {
widget.field = new FormFieldModel(new FormModel({ taskId: 'fake-task-id' }), {
id: 'text-id',
name: 'text-name',
value: '',
type: FormFieldTypes.TEXT,
readOnly: false,
required: true
});
fixture.detectChanges();
await fixture.whenStable();
const widgetContainer = element.querySelector('.adf-left-label-input-container');
expect(widgetContainer).toBeNull();
const adfLeftLabel = element.querySelector('.adf-left-label');
expect(adfLeftLabel).toBeNull();
});
});
}); });
}); });

View File

@ -1,36 +1,38 @@
<div class="adf-dropdown-widget {{field.className}}" <div class="adf-dropdown-widget {{field.className}}"
[class.adf-invalid]="(!field.isValid && isTouched()) || isRestApiFailed" [class.adf-readonly]="field.readOnly"> [class.adf-invalid]="(!field.isValid && isTouched()) || isRestApiFailed" [class.adf-readonly]="field.readOnly" [class.adf-left-label-input-container]="field.leftLabels">
<div class="adf-dropdown-widget-top-labels"> <div class="adf-dropdown-widget-top-labels">
<label class="adf-label" [attr.for]="field.id">{{field.name | translate }}<span class="adf-asterisk" <label class="adf-label" [attr.for]="field.id" [class.adf-left-label]="field.leftLabels">{{field.name | translate }}<span class="adf-asterisk"
*ngIf="isRequired()">*</span> *ngIf="isRequired()">*</span>
</label> </label>
</div> </div>
<mat-form-field> <div>
<mat-select class="adf-select" <mat-form-field>
[id]="field.id" <mat-select class="adf-select"
[(ngModel)]="field.value" [id]="field.id"
[disabled]="field.readOnly" [(ngModel)]="field.value"
[compareWith]="compareDropdownValues" [disabled]="field.readOnly"
(ngModelChange)="selectionChangedForField(field)" [compareWith]="compareDropdownValues"
[matTooltip]="field.tooltip" (ngModelChange)="selectionChangedForField(field)"
[required]="isRequired()" [matTooltip]="field.tooltip"
panelClass="adf-select-filter" [required]="isRequired()"
(blur)="markAsTouched()" panelClass="adf-select-filter"
matTooltipPosition="above" (blur)="markAsTouched()"
matTooltipShowDelay="1000" matTooltipPosition="above"
[multiple]="field.hasMultipleValues"> matTooltipShowDelay="1000"
<adf-select-filter-input *ngIf="showInputFilter" (change)="filter$.next($event)"></adf-select-filter-input> [multiple]="field.hasMultipleValues">
<adf-select-filter-input *ngIf="showInputFilter" (change)="filter$.next($event)"></adf-select-filter-input>
<mat-option *ngFor="let opt of list$ | async"
[value]="getOptionValue(opt, field.value)" <mat-option *ngFor="let opt of list$ | async"
[id]="opt.id">{{opt.name}} [value]="getOptionValue(opt, field.value)"
</mat-option> [id]="opt.id">{{opt.name}}
<mat-option id="readonlyOption" *ngIf="isReadOnlyType()" [value]="field.value">{{field.value}}</mat-option> </mat-option>
</mat-select> <mat-option id="readonlyOption" *ngIf="isReadOnlyType()" [value]="field.value">{{field.value}}</mat-option>
</mat-form-field> </mat-select>
<error-widget [error]="field.validationSummary"></error-widget> </mat-form-field>
<error-widget class="adf-dropdown-required-message" *ngIf="showRequiredMessage()" <error-widget [error]="field.validationSummary"></error-widget>
required="{{ 'FORM.FIELD.REQUIRED' | translate }}"></error-widget> <error-widget class="adf-dropdown-required-message" *ngIf="showRequiredMessage()"
<error-widget class="adf-dropdown-failed-message" *ngIf="isRestApiFailed" required="{{ 'FORM.FIELD.REQUIRED' | translate }}"></error-widget>
required="{{ 'FORM.FIELD.REST_API_FAILED' | translate: { hostname: restApiHostName } }}"></error-widget> <error-widget class="adf-dropdown-failed-message" *ngIf="isRestApiFailed"
required="{{ 'FORM.FIELD.REST_API_FAILED' | translate: { hostname: restApiHostName } }}"></error-widget>
</div>
</div> </div>

View File

@ -628,5 +628,65 @@ describe('DropdownCloudWidgetComponent', () => {
expect(widget.field.options).toEqual(mockRestDropdownOptions); expect(widget.field.options).toEqual(mockRestDropdownOptions);
}); });
}); });
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: 'dropdown-id',
name: 'option list',
type: FormFieldTypes.DROPDOWN,
readOnly: false,
options: filterOptionList
});
fixture.detectChanges();
await fixture.whenStable();
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', async () => {
widget.field = new FormFieldModel(new FormModel({ taskId: 'fake-task-id', leftLabels: false }), {
id: 'dropdown-id',
name: 'option list',
type: FormFieldTypes.DROPDOWN,
readOnly: false,
options: filterOptionList
});
fixture.detectChanges();
await fixture.whenStable();
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', async () => {
widget.field = new FormFieldModel(new FormModel({ taskId: 'fake-task-id' }), {
id: 'dropdown-id',
name: 'option list',
type: FormFieldTypes.DROPDOWN,
readOnly: false,
options: filterOptionList
});
fixture.detectChanges();
await fixture.whenStable();
const widgetContainer = element.querySelector('.adf-left-label-input-container');
expect(widgetContainer).toBeNull();
const adfLeftLabel = element.querySelector('.adf-left-label');
expect(adfLeftLabel).toBeNull();
});
});
}); });
}); });