Skip to content

Commit

Permalink
Merge pull request #399 from himdel/validate-bz1720245
Browse files Browse the repository at this point in the history
Dialog User - stable validation, consistent refresh and initial load

(cherry picked from commit 55f80b8)

https://bugzilla.redhat.com/show_bug.cgi?id=1727989
https://bugzilla.redhat.com/show_bug.cgi?id=1727990
  • Loading branch information
h-kataria authored and simaishi committed Jul 8, 2019
1 parent 8fa0265 commit eb52db0
Show file tree
Hide file tree
Showing 8 changed files with 180 additions and 230 deletions.
11 changes: 4 additions & 7 deletions src/dialog-user/components/dialog-user/dialogField.html
@@ -1,6 +1,6 @@
<div ng-show="vm.dialogField.visible"
class="form-group"
ng-class="{'has-error': vm.dialogField.fieldValidation===false}">
ng-class="{'has-error': vm.validation.isValid === false}">

<label class=" col-sm-3 control-label">{{ ::vm.dialogField.label }}
<i class="fa fa-info-circle primary help-icon"
Expand All @@ -16,14 +16,13 @@
<input ng-model="vm.dialogField.default_value"
ng-disabled="vm.dialogField.read_only || vm.inputDisabled"
ng-change="vm.changesHappened()"
ng-blur="vm.validateField()"
ng-model-options="{debounce: {'default': 500}}"
class="form-control"
type="{{ vm.dialogField.options.protected ? 'password' : 'text' }}"
uib-tooltip="{{ ::inputTitle }}"
value="{{ vm.dialogField.values }}"
id="{{ vm.dialogField.name }}">
<div ng-if="vm.dialogField.fieldValidation===false">{{ vm.dialogField.errorMessage }}</div>
<div ng-if="vm.validation.isValid === false">{{ vm.validation.message }}</div>
</div>
<div class="col-sm-8" ng-switch-when="DialogFieldTextAreaBox">
<textarea ng-model="vm.dialogField.default_value"
Expand All @@ -46,7 +45,7 @@
type="checkbox"
uib-tooltip="{{ ::inputTitle }}"
id="{{ vm.dialogField.name }}">
<div ng-if="vm.dialogField.fieldValidation===false">{{vm.dialogField.errorMessage}}</div>
<div ng-if="vm.validation.isValid === false">{{ vm.validation.message }}</div>
</div>

<div class="col-sm-4" ng-switch-when="DialogFieldDropDownList">
Expand All @@ -56,7 +55,6 @@
ng-if="!vm.dialogField.options.force_multi_value"
ng-model="vm.dialogField.default_value"
watch-model="vm.dialogField.values"
ng-blur="vm.validateField()"
ng-change="vm.changesHappened()"
ng-disabled="vm.dialogField.read_only || vm.inputDisabled"
class="form-control"
Expand All @@ -72,8 +70,7 @@
ng-if="vm.dialogField.options.force_multi_value"
ng-model="vm.dialogField.default_value"
watch-model="vm.dialogField.values"
ng-change="vm.changesHappened(item)"
ng-blur="vm.validateField()"
ng-change="vm.changesHappened()"
ng-disabled="vm.dialogField.read_only || vm.inputDisabled"
input-id="{{ vm.dialogField.name }}"
ng-options="value[0] as value[1] for value in vm.dialogField.values"
Expand Down
17 changes: 8 additions & 9 deletions src/dialog-user/components/dialog-user/dialogField.spec.ts
Expand Up @@ -21,9 +21,7 @@ const dialogField = {
'dynamic': false,
'read_only': false,
'visible': true,
'fieldValidation': null,
'fieldBeingRefreshed': false,
'errorMessage': '',
'type': 'DialogFieldTextBox',
'resource_action': {
'resource_type': 'DialogField',
Expand Down Expand Up @@ -51,21 +49,23 @@ describe('Dialog field test', () => {
});

it('should have some default properties set', () => {
expect(dialogCtrl.dialogField.fieldValidation).toBeDefined();
expect(dialogCtrl.validation.isValid).toBeDefined();
expect(dialogCtrl.dialogField.fieldBeingRefreshed).toBe(false);
expect(dialogCtrl.dialogField.errorMessage).toBeDefined();
expect(dialogCtrl.validation.message).toBeDefined();
});

it('should allow a field to be validated', () => {
dialogCtrl.dialogField.default_value = 'Test';
const fieldValid = dialogCtrl.validateField();
dialogCtrl.validateField();

const fieldValid = dialogCtrl.validation;
const expectedValue = {
isValid: false,
field: 'Service Name',
label: 'Service Name',
message: 'Entered text should match the format: [0-9]'
};

expect(fieldValid).toEqual(expectedValue);
expect(fieldValid).toEqual(jasmine.objectContaining(expectedValue));
});

it('should check and update a field when the parent component field has changed', () => {
Expand Down Expand Up @@ -119,8 +119,7 @@ describe('Dialog field test', () => {
const dateTimeDialogField = {
'name': 'dateTest',
'type': 'DialogFieldDateTimeControl',
'dateField': testDate,
'timeField': testDate,
'default_value': testDate,
'options': {},
};

Expand Down
111 changes: 51 additions & 60 deletions src/dialog-user/components/dialog-user/dialogField.ts
@@ -1,25 +1,28 @@
import { DialogFieldClass } from '../../interfaces/abstractDialogFieldClass';
import * as _ from 'lodash';
import * as angular from 'angular';

/**
* This component deals with an individual dialog field
*
* @extends miqStaticAssets.dialog.DialogFieldClass
* @memberof miqStaticAssets.dialogUser
* @ngdoc controller
* @name DialogFieldController
*/
export class DialogFieldController {
public field: any;
public onUpdate: any;
public singleRefresh: any;
public options: any;
public inputDisabled: boolean;

export class DialogFieldController extends DialogFieldClass {
public service: any;
public dialogValue: any;
public dialogField: any;
public validation: any;
public minDate: any;
public clonedDialogField: any;
/*@ngInject*/
constructor(private DialogData: any, private $window: any) {
super();

/* @ngInject */
constructor(DialogData: any) {
this.service = DialogData;
}

/**
Expand All @@ -28,35 +31,43 @@ export class DialogFieldController extends DialogFieldClass {
* @function $onInit
*/
public $onInit() {
this.service = this.DialogData;
this.setup();
}

/**
* Checks to see if the dialog field has changed and re runs field setup if the field has changed
* @memberof DialogFieldController
* @function $doCheck
*/
public $doCheck() {
if (!_.isEqual(this.field, this.clonedDialogField)) {
this.setup();
}
}

// run field setup on field init or change
public setup() {
this.clonedDialogField = _.cloneDeep(this.field);
this.dialogField = this.field;
this.validation = null;
this.dialogField = this.service.setupField(this.field);

if ((this.dialogField.type === 'DialogFieldDateTimeControl') ||
(this.dialogField.type === 'DialogFieldDateControl')) {
this.setMinDate();
}

if (this.dialogField.type === 'DialogFieldDateTimeControl') {
this.dateTimeFieldChanged();
// dateTimeFieldChanged handles merging back to default_value
this.dialogField.dateField = new Date(this.dialogField.default_value);
this.dialogField.timeField = new Date(this.dialogField.default_value);
}

this.validateField();
}

/**
* Checks to see if the dialog field has changed and re runs field setup if the field has changed
* @memberof DialogFieldController
* @function $doCheck
*/
public $doCheck() {
if (!_.isEqual(this.field, this.clonedDialogField)) {
this.clonedDialogField = _.cloneDeep(this.field);
if (this.validation) {
this.field.fieldValidation = this.validation.isValid;
this.field.errorMessage = this.validation.message;
}
this.dialogField = this.service.setupField(this.field);
}
// validate field, set validation
private validateField() {
const field = this.dialogField;
this.validation = this.service.validateField(field, field.default_value);
}

/**
Expand All @@ -65,18 +76,14 @@ export class DialogFieldController extends DialogFieldClass {
* @memberof DialogFieldController
* @function changesHappened
*/
public changesHappened(value) {
const selectedValue = 0;
this.validation = this.validateField();
let fieldValue = (value ? value[selectedValue] : this.dialogField.default_value);
if ((this.dialogField.type === 'DialogFieldTagControl' ||
this.dialogField.type === 'DialogFieldDropDownList' ||
this.dialogField.type === 'DialogFieldRadioButton') &&
this.dialogField.default_value instanceof Array) {
// using `default_value` if field.type is a subclass of DialogFieldSortedItem
fieldValue = this.dialogField.default_value.join();
}
this.onUpdate({ dialogFieldName: this.field.name, value: fieldValue });
public changesHappened() {
this.validateField();

const field = this.dialogField;
this.onUpdate({
dialogFieldName: field.name,
value: field.default_value,
});
}

/**
Expand All @@ -87,19 +94,17 @@ export class DialogFieldController extends DialogFieldClass {
*/
public dateTimeFieldChanged() {
let dateField = this.dialogField.dateField;
let timeField = this.dialogField.timeField;

let fullYear = dateField.getFullYear();
let month = dateField.getMonth();
let date = dateField.getDate();

if (this.dialogField.timeField === undefined) {
this.dialogField.timeField = new Date();
}

let hours = this.dialogField.timeField.getHours();
let minutes = this.dialogField.timeField.getMinutes();
let hours = timeField.getHours();
let minutes = timeField.getMinutes();

let fullDate = new Date(fullYear, month, date, hours, minutes);
this.changesHappened([fullDate]);
this.dialogField.default_value = new Date(fullYear, month, date, hours, minutes);
return this.changesHappened();
}

/**
Expand All @@ -112,20 +117,6 @@ export class DialogFieldController extends DialogFieldClass {
this.minDate = this.dialogField.options.show_past_dates ? null : new Date();
}

/**
* This method validates a dialog field to ensure its current values are valid
* @memberof DialogFieldController
* @function validateField
*
*/
public validateField() {
let validation = { isValid: true, message: '' };
validation = this.service.validateField(this.dialogField);
this.dialogField.fieldValidation = validation.isValid;
this.dialogField.errorMessage = validation.message;
return validation;
}

public refreshSingleField() {
this.singleRefresh({ field: this.field.name });
}
Expand Down
27 changes: 18 additions & 9 deletions src/dialog-user/components/dialog-user/dialogUser.spec.ts
Expand Up @@ -80,12 +80,13 @@ describe('Dialog test', () => {

beforeEach(() => {
refreshField = {
callback: function(value) { }
callback: (value) => Promise.resolve({}),
};
spyOn(refreshField, 'callback');
spyOn(refreshField, 'callback').and.callThrough();

let bindings = {
dialog: dialogData,
refreshField: refreshField,
refreshField: refreshField.callback,
onUpdate: () => true,
inputDisabled: false
};
Expand All @@ -97,14 +98,22 @@ describe('Dialog test', () => {
});

it('returns a promise', () => {
let testPromise = new Promise((resolve, reject) => {});
expect(dialogCtrl.refreshSingleField('service_name')).toEqual(testPromise);
expect(dialogCtrl.refreshSingleField('service_name').then).toBeDefined();
});

it('makes the callback to refreshField', () => {
let promise = dialogCtrl.refreshSingleField('service_name');
promise.then(() => {
expect(refreshField.callback).toHaveBeenCalledWith({field: dialogData['service_name']});
it('makes the callback to refreshField', (done) => {
let expectedField = _.pick(dialogCtrl.dialogFields['service_name'], [
'created_at',
'href',
'name',
]);

dialogCtrl.refreshSingleField('service_name').then(() => {
expect(refreshField.callback).toHaveBeenCalledWith({
field: jasmine.objectContaining(expectedField),
});

done();
});
});
});
Expand Down

0 comments on commit eb52db0

Please sign in to comment.