Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Revert "AAE-23521 Fix improve dropdown reactive form" #10068

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export class FormFieldTypes {

static VALIDATABLE_TYPES: string[] = [FormFieldTypes.DISPLAY_EXTERNAL_PROPERTY];

static REACTIVE_TYPES: string[] = [FormFieldTypes.DATE, FormFieldTypes.DATETIME, FormFieldTypes.DROPDOWN];
static REACTIVE_TYPES: string[] = [FormFieldTypes.DATE, FormFieldTypes.DATETIME];

static CONSTANT_VALUE_TYPES: string[] = [FormFieldTypes.DISPLAY_EXTERNAL_PROPERTY];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,51 @@ describe('FormFieldValidator', () => {
expect(validator.validate(field)).toBe(true);
});

it('should fail (display error) for multiple type dropdown with zero selection', () => {
const field = new FormFieldModel(new FormModel(), {
type: FormFieldTypes.DROPDOWN,
value: [{ id: 'id_cat', name: 'Cat' }],
required: true,
selectionType: 'multiple',
hasEmptyValue: false,
options: [
{ id: 'id_cat', name: 'Cat' },
{ id: 'id_dog', name: 'Dog' }
]
});

const validateBeforeUnselect = validator.validate(field);
field.value = [];
const validateAfterUnselect = validator.validate(field);

expect(validateBeforeUnselect).toBe(true);
expect(validateAfterUnselect).toBe(false);
});

it('should fail (display error) for dropdown with null value', () => {
const field = new FormFieldModel(new FormModel(), {
type: FormFieldTypes.DROPDOWN,
value: null,
required: true,
options: [{ id: 'one', name: 'one' }],
selectionType: 'multiple'
});

expect(validator.validate(field)).toBe(false);
});

it('should fail (display error) for dropdown with empty object', () => {
const field = new FormFieldModel(new FormModel(), {
type: FormFieldTypes.DROPDOWN,
value: {},
required: true,
options: [{ id: 'one', name: 'one' }],
selectionType: 'multiple'
});

expect(validator.validate(field)).toBe(false);
});

it('should fail (display error) for radio buttons', () => {
const field = new FormFieldModel(new FormModel(), {
type: FormFieldTypes.RADIO_BUTTONS,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export class RequiredFieldValidator implements FormFieldValidator {
FormFieldTypes.NUMBER,
FormFieldTypes.BOOLEAN,
FormFieldTypes.TYPEAHEAD,
FormFieldTypes.DROPDOWN,
FormFieldTypes.PEOPLE,
FormFieldTypes.FUNCTIONAL_GROUP,
FormFieldTypes.RADIO_BUTTONS,
Expand All @@ -50,6 +51,22 @@ export class RequiredFieldValidator implements FormFieldValidator {

validate(field: FormFieldModel): boolean {
if (this.isSupported(field) && field.isVisible) {
if (field.type === FormFieldTypes.DROPDOWN) {
if (field.hasMultipleValues) {
return Array.isArray(field.value) && !!field.value.length;
}

if (field.hasEmptyValue && field.emptyOption) {
if (field.value === field.emptyOption.id) {
return false;
}
}

if (field.required && field.value && typeof field.value === 'object' && !Object.keys(field.value).length) {
return false;
}
}

if (field.type === FormFieldTypes.RADIO_BUTTONS) {
const option = field.options.find((opt) => opt.id === field.value);
return !!option;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
.adf-error {
display: flex;
align-items: center;

&-widget-container {
height: auto;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,50 +1,43 @@
<div
class="adf-dropdown-widget {{field.className}}"
[class.adf-invalid]="dropdownControl.invalid && dropdownControl.touched"
[class.adf-readonly]="field.readOnly"
[class.adf-left-label-input-container]="field.leftLabels"
>
<div *ngIf="field.leftLabels">
<label class="adf-label adf-left-label" [attr.for]="field.id">
{{ field.name | translate }}<span class="adf-asterisk" *ngIf="isRequired()">*</span>
<div class="adf-dropdown-widget {{field.className}}"
[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">
<label class="adf-label" [attr.for]="field.id" [class.adf-left-label]="field.leftLabels">{{field.name | translate }}<span class="adf-asterisk"
*ngIf="isRequired()">*</span>
</label>
</div>
<div>
<mat-form-field>
<label *ngIf="!field.leftLabels" class="adf-label" [attr.for]="field.id">
{{ field.name | translate }}<span class="adf-asterisk" *ngIf="isRequired()">*</span>
</label>
<mat-select
class="adf-select"
[formControl]="dropdownControl"
[id]="field.id"
[compareWith]="compareDropdownValues"
[title]="field.tooltip"
panelClass="adf-select-filter"
[multiple]="field.hasMultipleValues"
>
<mat-label *ngIf="getDefaultOption(list$ | async) as defaultOption">
{{ defaultOption.name }}
</mat-label>
<mat-select class="adf-select"
[id]="field.id"
[(ngModel)]="field.value"
[disabled]="field.readOnly"
[compareWith]="compareDropdownValues"
(ngModelChange)="selectionChangedForField(field)"
[title]="field.tooltip"
[required]="isRequired()"
panelClass="adf-select-filter"
(blur)="markAsTouched()"
[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]="opt" [id]="opt.id">{{opt.name}}</mat-option>
<mat-option id="readonlyOption" *ngIf="isReadOnlyType" [value]="field.value">{{field.value}}</mat-option>
<mat-option *ngFor="let opt of list$ | async"
[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>
<div *ngIf="!previewState && !field.readOnly">
<error-widget
class="adf-dropdown-required-message"
*ngIf="showRequiredMessage"
required="{{ 'FORM.FIELD.REQUIRED' | translate }}"
></error-widget>
<error-widget
class="adf-dropdown-failed-message"
*ngIf="isRestApiFailed"
required="{{ 'FORM.FIELD.REST_API_FAILED' | translate: { hostname: restApiHostName } }}"
></error-widget>
<error-widget
class="adf-dropdown-failed-message"
*ngIf="variableOptionsFailed"
required="{{ 'FORM.FIELD.VARIABLE_DROPDOWN_OPTIONS_FAILED' | translate }}"
></error-widget>
<div *ngIf="!previewState && !isReadOnlyField">
<error-widget [error]="field.validationSummary"></error-widget>
<error-widget class="adf-dropdown-required-message" *ngIf="showRequiredMessage()"
required="{{ 'FORM.FIELD.REQUIRED' | translate }}"></error-widget>
<error-widget class="adf-dropdown-failed-message" *ngIf="isRestApiFailed"
required="{{ 'FORM.FIELD.REST_API_FAILED' | translate: { hostname: restApiHostName } }}"></error-widget>
<error-widget class="adf-dropdown-failed-message" *ngIf="variableOptionsFailed"
required="{{ 'FORM.FIELD.VARIABLE_DROPDOWN_OPTIONS_FAILED' | translate }}"></error-widget>
</div>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
@import 'styles/mat-selectors';

.adf {
&-dropdown-widget {
width: 100%;
Expand All @@ -26,9 +24,5 @@
&-dropdown-failed-message .adf-error-container {
margin-top: 1px;
}

#{$mat-select-arrow-wrapper} {
height: auto;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import { TaskVariableCloud } from '../../../models/task-variable-cloud.model';
import { HarnessLoader } from '@angular/cdk/testing';
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { MatSelectHarness } from '@angular/material/select/testing';
import { MatFormFieldHarness } from '@angular/material/form-field/testing';
import { DebugElement } from '@angular/core';

describe('DropdownCloudWidgetComponent', () => {
Expand Down Expand Up @@ -94,15 +95,15 @@ describe('DropdownCloudWidgetComponent', () => {
it('should select the default value when an option is chosen as default', async () => {
widget.field.value = 'option_2';

expect(widget.field.value).toEqual('option_2');
expect(widget.fieldValue).toEqual('option_2');
});

it('should select the empty value when no default is chosen', async () => {
widget.field.value = 'empty';
const dropdown = await loader.getHarness(MatSelectHarness.with({ selector: '.adf-select' }));
await dropdown.open();

expect(widget.field.value).toEqual('empty');
expect(widget.fieldValue).toEqual('empty');
});

it('should load data from restUrl and populate options', async () => {
Expand Down Expand Up @@ -294,14 +295,17 @@ describe('DropdownCloudWidgetComponent', () => {
await dropdown.clickOptions({ selector: '[id="opt_1"]' });

expect(await dropdown.getValueText()).toEqual('option_1');
expect(widget.field.value).toEqual('opt_1');
expect(widget.fieldValue).toEqual('opt_1');

await dropdown.open();
await dropdown.clickOptions({ selector: '[id="empty"]' });

expect(widget.field.value).toEqual(undefined);
const formField = await loader.getHarness(MatFormFieldHarness);
const dropdownLabel = await formField.getLabel();

expect(await dropdown.getValueText()).toEqual('This is a mock none option');
expect(dropdownLabel).toEqual('This is a mock none option');
expect(widget.fieldValue).toEqual(undefined);
expect(await dropdown.getValueText()).toEqual('');
});
});

Expand Down Expand Up @@ -524,58 +528,6 @@ describe('DropdownCloudWidgetComponent', () => {
{ id: 'opt_4', name: 'option_4' }
]);
});

it('should fail (display error) for multiple type dropdown with zero selection', async () => {
widget.field = new FormFieldModel(new FormModel({ taskId: 'fake-task-id', readOnly: 'false' }), {
type: FormFieldTypes.DROPDOWN,
value: [{ id: 'id_cat', name: 'Cat' }],
required: true,
selectionType: 'multiple',
hasEmptyValue: false,
options: [
{ id: 'id_cat', name: 'Cat' },
{ id: 'id_dog', name: 'Dog' }
]
});

const validateBeforeUnselect = widget.dropdownControl.valid;

const dropdown = await loader.getHarness(MatSelectHarness.with({ selector: '.adf-select' }));
await dropdown.clickOptions({ selector: '[id="id_cat"]' });

const validateAfterUnselect = widget.dropdownControl.valid;

expect(validateBeforeUnselect).toBe(true);
expect(validateAfterUnselect).toBe(false);
});

it('should fail (display error) for dropdown with null value', () => {
widget.field = new FormFieldModel(new FormModel(), {
type: FormFieldTypes.DROPDOWN,
value: null,
required: true,
options: [{ id: 'one', name: 'one' }],
selectionType: 'multiple'
});

widget.ngOnInit();

expect(widget.dropdownControl.valid).toBe(false);
});

it('should fail (display error) for dropdown with empty object', () => {
widget.field = new FormFieldModel(new FormModel(), {
type: FormFieldTypes.DROPDOWN,
value: {},
required: true,
options: [{ id: 'one', name: 'one' }],
selectionType: 'multiple'
});

widget.ngOnInit();

expect(widget.dropdownControl.valid).toBe(false);
});
});

describe('Linked Dropdown', () => {
Expand Down Expand Up @@ -745,7 +697,7 @@ describe('DropdownCloudWidgetComponent', () => {
fixture.detectChanges();

expect(widget.field.options).toEqual(mockConditionalEntries[0].options);
expect(widget.field.value).toEqual('');
expect(widget.fieldValue).toEqual('');
});

it('should not reset the current value when it is part of the available options', () => {
Expand All @@ -758,7 +710,7 @@ describe('DropdownCloudWidgetComponent', () => {
fixture.detectChanges();

expect(widget.field.options).toEqual(mockConditionalEntries[0].options);
expect(widget.field.value).toEqual('ATH');
expect(widget.fieldValue).toEqual('ATH');
});

it('should fire a form field value changed event when the value gets reset (notify children on the chain to reset)', () => {
Expand Down
Loading
Loading