summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJenkins <jenkins@review.openstack.org>2015-06-03 17:33:57 +0000
committerGerrit Code Review <review@openstack.org>2015-06-03 17:33:57 +0000
commitf0023b100f19a21b8fe78970ae23080ea76dbffe (patch)
tree54c2513aa1123a30142b2652de4655134c92ed85
parentb6367982a58d5aa0853da0c513d96415b8445f72 (diff)
parent2079195f7bbfe5a903b793c1a1f57c86aa436003 (diff)
Merge "Provide 'validatable-with' directive"
-rw-r--r--extensions/mistral/static/mistral/js/mistral.workbook.models.js7
-rw-r--r--merlin/static/merlin/js/merlin.directives.js30
-rw-r--r--merlin/static/merlin/js/merlin.field.models.js13
-rw-r--r--merlin/static/merlin/scss/merlin.scss9
-rw-r--r--merlin/static/merlin/templates/editable.html2
-rw-r--r--merlin/static/merlin/templates/fields/number.html3
-rw-r--r--merlin/static/merlin/templates/fields/string.html3
-rw-r--r--merlin/static/merlin/templates/fields/text.html3
-rw-r--r--merlin/test/js/directivesSpec.js79
9 files changed, 141 insertions, 8 deletions
diff --git a/extensions/mistral/static/mistral/js/mistral.workbook.models.js b/extensions/mistral/static/mistral/js/mistral.workbook.models.js
index 53715dd..1d0a103 100644
--- a/extensions/mistral/static/mistral/js/mistral.workbook.models.js
+++ b/extensions/mistral/static/mistral/js/mistral.workbook.models.js
@@ -559,7 +559,12 @@
559 'index': 0, 559 'index': 0,
560 'panelIndex': 0, 560 'panelIndex': 0,
561 'row': 0 561 'row': 0
562 } 562 },
563 '@constraints': [
564 function(value) {
565 return value !== 'workbook1' ? true : 'The sample validation failure.';
566 }
567 ]
563 }) 568 })
564 }, 569 },
565 'description': { 570 'description': {
diff --git a/merlin/static/merlin/js/merlin.directives.js b/merlin/static/merlin/js/merlin.directives.js
index 002e655..8d53ca8 100644
--- a/merlin/static/merlin/js/merlin.directives.js
+++ b/merlin/static/merlin/js/merlin.directives.js
@@ -112,6 +112,36 @@
112 } 112 }
113 } 113 }
114 }) 114 })
115 .directive('validatableWith', function($parse) {
116 return {
117 restrict: 'A',
118 require: 'ngModel',
119 link: function(scope, element, attrs, ctrl) {
120 var model;
121 if ( attrs.validatableWith ) {
122 model = $parse(attrs.validatableWith)(scope);
123 scope.error = '';
124 model.setValidatable && model.setValidatable(true);
125 model.on && model.on('validation', function(result) {
126 var isValid = (result == 'succeeded'),
127 baseMessage = '';
128 // (FIXME): hack until Barricade supports validation of empty required entries
129 if ( !model.get() && model.isRequired() ) {
130 isValid = false;
131 baseMessage = 'This field is required.'
132 }
133 ctrl.$setValidity('barricade', isValid);
134 scope.error = model.hasError() ? model.getError() : baseMessage;
135 });
136 ctrl.$formatters.push(function(modelValue) {
137 return modelValue === undefined ?
138 ( ctrl.$isEmpty(ctrl.$viewValue) ? undefined : ctrl.$viewValue ) :
139 modelValue;
140 });
141 }
142 }
143 }
144 })
115 .directive('typedField', ['$compile', 'merlin.templates', 145 .directive('typedField', ['$compile', 'merlin.templates',
116 function($compile, templates) { 146 function($compile, templates) {
117 function updateAutoCompletionDirective(template) { 147 function updateAutoCompletionDirective(template) {
diff --git a/merlin/static/merlin/js/merlin.field.models.js b/merlin/static/merlin/js/merlin.field.models.js
index 47ae295..dbc393b 100644
--- a/merlin/static/merlin/js/merlin.field.models.js
+++ b/merlin/static/merlin/js/merlin.field.models.js
@@ -32,11 +32,18 @@
32 }); 32 });
33 33
34 var modelMixin = Barricade.Blueprint.create(function(type) { 34 var modelMixin = Barricade.Blueprint.create(function(type) {
35 var isValid = true,
36 isValidatable = false;
35 this.value = function() { 37 this.value = function() {
36 if ( !arguments.length ) { 38 if ( !arguments.length ) {
37 return this.get(); 39 if ( isValidatable ) {
40 return isValid ? this.get() : undefined;
41 } else {
42 return this.get();
43 }
38 } else { 44 } else {
39 this.set(arguments[0]); 45 this.set(arguments[0]);
46 isValid = !this.hasError();
40 } 47 }
41 }; 48 };
42 this.id = utils.getNewId(); 49 this.id = utils.getNewId();
@@ -45,6 +52,10 @@
45 return type; 52 return type;
46 }; 53 };
47 54
55 this.setValidatable = function(validatable) {
56 isValidatable = validatable;
57 };
58
48 this.setType = function(_type) { 59 this.setType = function(_type) {
49 type = _type; 60 type = _type;
50 }; 61 };
diff --git a/merlin/static/merlin/scss/merlin.scss b/merlin/static/merlin/scss/merlin.scss
index b1b842a..b33a08c 100644
--- a/merlin/static/merlin/scss/merlin.scss
+++ b/merlin/static/merlin/scss/merlin.scss
@@ -54,8 +54,13 @@
54 } 54 }
55} 55}
56 56
57editable .fa-pencil { 57editable {
58 color: black; 58 .fa-pencil {
59 color: black;
60 }
61 button:disabled {
62 color: gray;
63 }
59} 64}
60 65
61.section { 66.section {
diff --git a/merlin/static/merlin/templates/editable.html b/merlin/static/merlin/templates/editable.html
index 06bdd31..7d8dd91 100644
--- a/merlin/static/merlin/templates/editable.html
+++ b/merlin/static/merlin/templates/editable.html
@@ -5,6 +5,6 @@
5</span> 5</span>
6<span ng-show="isEdited"> 6<span ng-show="isEdited">
7 <input type="text" ng-model="editableValue" show-focus="isEdited"> 7 <input type="text" ng-model="editableValue" show-focus="isEdited">
8 <button ng-click="accept()"><i class="fa fa-check"></i></button> 8 <button ng-click="accept()" ng-disabled="!editableValue"><i class="fa fa-check"></i></button>
9 <button ng-click="reject()"><i class="fa fa-close"></i></button> 9 <button ng-click="reject()"><i class="fa fa-close"></i></button>
10</span> 10</span>
diff --git a/merlin/static/merlin/templates/fields/number.html b/merlin/static/merlin/templates/fields/number.html
index 2b53f39..fbcc2b2 100644
--- a/merlin/static/merlin/templates/fields/number.html
+++ b/merlin/static/merlin/templates/fields/number.html
@@ -3,5 +3,6 @@
3 <label for="elem-{$ $id $}">{$ value.title() $}</label> 3 <label for="elem-{$ $id $}">{$ value.title() $}</label>
4 <input type="number" class="form-control" id="elem-{$ $id $}" 4 <input type="number" class="form-control" id="elem-{$ $id $}"
5 ng-model="value.value" ng-model-options="{ getterSetter: true }" 5 ng-model="value.value" ng-model-options="{ getterSetter: true }"
6 autocompletable="true"> 6 autocompletable="true" validatable-with="value">
7 <div ng-show="error" class="alert alert-danger">{$ error $}</div>
7</div> 8</div>
diff --git a/merlin/static/merlin/templates/fields/string.html b/merlin/static/merlin/templates/fields/string.html
index e738076..b526869 100644
--- a/merlin/static/merlin/templates/fields/string.html
+++ b/merlin/static/merlin/templates/fields/string.html
@@ -2,5 +2,6 @@
2 <label for="elem-{$ $id $}">{$ value.title() $}</label> 2 <label for="elem-{$ $id $}">{$ value.title() $}</label>
3 <input type="text" class="form-control" id="elem-{$ $id $}" 3 <input type="text" class="form-control" id="elem-{$ $id $}"
4 ng-model="value.value" ng-model-options="{ getterSetter: true }" 4 ng-model="value.value" ng-model-options="{ getterSetter: true }"
5 autocompletable="true"> 5 autocompletable="true" validatable-with="value">
6 <div ng-show="error" class="alert alert-danger">{$ error $}</div>
6</div> 7</div>
diff --git a/merlin/static/merlin/templates/fields/text.html b/merlin/static/merlin/templates/fields/text.html
index b961209..24173c3 100644
--- a/merlin/static/merlin/templates/fields/text.html
+++ b/merlin/static/merlin/templates/fields/text.html
@@ -2,5 +2,6 @@
2 <label for="elem-{$ $id $}">{$ value.title() $}</label> 2 <label for="elem-{$ $id $}">{$ value.title() $}</label>
3 <textarea class="form-control" id="elem-{$ $id $}" 3 <textarea class="form-control" id="elem-{$ $id $}"
4 ng-model="value.value" ng-model-options="{ getterSetter: true }" 4 ng-model="value.value" ng-model-options="{ getterSetter: true }"
5 autocompletable="true"></textarea> 5 autocompletable="true" validatable-with="value"></textarea>
6 <div ng-show="error" class="alert alert-danger">{$ error $}</div>
6</div> 7</div>
diff --git a/merlin/test/js/directivesSpec.js b/merlin/test/js/directivesSpec.js
index 3d8ca23..fd8fcc4 100644
--- a/merlin/test/js/directivesSpec.js
+++ b/merlin/test/js/directivesSpec.js
@@ -347,4 +347,83 @@ describe('merlin directives', function() {
347 }); 347 });
348 348
349 }); 349 });
350
351 describe("'validatable'", function() {
352 var fields;
353
354 beforeEach(inject(function($injector) {
355 fields = $injector.get('merlin.field.models');
356 }));
357
358 describe('working with the @constraints property:', function() {
359 var model, elt,
360 goodValue = 'allowedValue',
361 badValue = 'restrictedValue',
362 errorMessage = 'Wrong value provided';
363 beforeEach(function() {
364 var modelClass = fields.string.extend({}, {
365 '@constraints': [
366 function(value) {
367 return value !== badValue ? true : errorMessage;
368 }
369 ]
370 });
371 $scope.model = modelClass.create();
372 elt = $compile('<form name="form"><input name="model" type="text" ' +
373 'ng-model="model.value" ng-model-options="{ getterSetter: true }" ' +
374 'validatable-with="model"></form>')($scope);
375 });
376
377 describe('any valid value', function() {
378 beforeEach(function() {
379 $scope.form.model.$setViewValue(goodValue);
380 $scope.$digest();
381 });
382
383 it('is allowed to be entered', function() {
384 expect($scope.form.model.$viewValue).toEqual(goodValue);
385 });
386
387 it('is propagated into the model', function() {
388 expect($scope.model.value()).toEqual(goodValue);
389 });
390
391 it('does not cause the input to be marked as erroneous', function() {
392 expect(elt.find('input').hasClass('ng-valid')).toBe(true);
393 });
394
395 it('sets error message on scope to an empty string', function() {
396 expect($scope.error).toEqual('');
397 });
398 });
399
400 describe('any invalid value', function() {
401 beforeEach(function() {
402 $scope.form.model.$setViewValue(badValue);
403 $scope.$digest();
404 });
405
406 it('is allowed to be entered', function() {
407 expect($scope.form.model.$viewValue).toEqual(badValue);
408 });
409
410 it('is not propagated into the model', function() {
411 expect($scope.model.value()).toBe(undefined);
412 });
413
414 it('causes the input to be marked as erroneous', function() {
415 expect(elt.find('input').hasClass('ng-invalid')).toBe(true);
416 });
417
418 it('exposes error message in the parent scope', function() {
419 expect($scope.error).toEqual(errorMessage);
420 })
421 });
422 });
423
424 describe('working with the @required property', function() {
425 // TODO: fill in once validation of @required fields changes in Barricade
426 });
427 });
428
350}); 429});