mirror of
				https://github.com/node-red/node-red.git
				synced 2025-03-01 10:36:34 +00:00 
			
		
		
		
	Merge pull request #4747 from GogoVega/tooltip-input-validation
Add tooltip and message validation to `typedInput`
This commit is contained in:
		| @@ -367,7 +367,8 @@ | ||||
|             valueLabel: contextLabel, | ||||
|             autoComplete: contextAutoComplete | ||||
|         }, | ||||
|         global: {value:"global",label:"global.",hasValue:true, | ||||
|         global: { | ||||
|             value: "global", label: "global.", hasValue: true, | ||||
|             options: [], | ||||
|             validate: RED.utils.validatePropertyExpression, | ||||
|             parse: contextParse, | ||||
| @@ -376,15 +377,17 @@ | ||||
|             autoComplete: contextAutoComplete | ||||
|         }, | ||||
|         str: { value: "str", label: "string", icon: "red/images/typedInput/az.svg" }, | ||||
|         num: {value:"num",label:"number",icon:"red/images/typedInput/09.svg",validate: function(v) { | ||||
|             return (true === RED.utils.validateTypedProperty(v, "num")); | ||||
|         num: { value: "num", label: "number", icon: "red/images/typedInput/09.svg", validate: function (v, o) { | ||||
|             return RED.utils.validateTypedProperty(v, "num", o); | ||||
|         } }, | ||||
|         bool: { value: "bool", label: "boolean", icon: "red/images/typedInput/bool.svg", options: ["true", "false"] }, | ||||
|         json: { | ||||
|             value: "json", | ||||
|             label: "JSON", | ||||
|             icon: "red/images/typedInput/json.svg", | ||||
|             validate: function(v) { try{JSON.parse(v);return true;}catch(e){return false;}}, | ||||
|             validate: function (v, o) { | ||||
|                 return RED.utils.validateTypedProperty(v, "json", o); | ||||
|             }, | ||||
|             expand: function () { | ||||
|                 var that = this; | ||||
|                 var value = this.value(); | ||||
| @@ -431,7 +434,9 @@ | ||||
|             value: "jsonata", | ||||
|             label: "expression", | ||||
|             icon: "red/images/typedInput/expr.svg", | ||||
|             validate: function(v) { try{jsonata(v);return true;}catch(e){return false;}}, | ||||
|             validate: function (v, o) { | ||||
|                 return RED.utils.validateTypedProperty(v, "jsonata", o); | ||||
|             }, | ||||
|             expand: function () { | ||||
|                 var that = this; | ||||
|                 RED.editor.editExpression({ | ||||
| @@ -1538,26 +1543,48 @@ | ||||
|                 } | ||||
|             } | ||||
|         }, | ||||
|         validate: function() { | ||||
|             var result; | ||||
|             var value = this.value(); | ||||
|             var type = this.type(); | ||||
|         validate: function(options) { | ||||
|             let valid = true; | ||||
|             const value = this.value(); | ||||
|             const type = this.type(); | ||||
|             if (this.typeMap[type] && this.typeMap[type].validate) { | ||||
|                 var val = this.typeMap[type].validate; | ||||
|                 if (typeof val === 'function') { | ||||
|                     result = val(value); | ||||
|                 const validate = this.typeMap[type].validate; | ||||
|                 if (typeof validate === 'function') { | ||||
|                     valid = validate(value, {}); | ||||
|                 } else { | ||||
|                     result = val.test(value); | ||||
|                     // Regex | ||||
|                     valid = validate.test(value); | ||||
|                     if (!valid) { | ||||
|                         valid = RED._("validator.errors.invalid-regexp"); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             if ((typeof valid === "string") || !valid) { | ||||
|                 this.element.addClass("input-error"); | ||||
|                 this.uiSelect.addClass("input-error"); | ||||
|                 if (typeof valid === "string") { | ||||
|                     let tooltip = this.element.data("tooltip"); | ||||
|                     if (tooltip) { | ||||
|                         tooltip.setContent(valid); | ||||
|                     } else { | ||||
|                         tooltip = RED.popover.tooltip(this.elementDiv, valid); | ||||
|                         this.element.data("tooltip", tooltip); | ||||
|                     } | ||||
|                 } | ||||
|             } else { | ||||
|                 result = true; | ||||
|                 this.element.removeClass("input-error"); | ||||
|                 this.uiSelect.removeClass("input-error"); | ||||
|                 const tooltip = this.element.data("tooltip"); | ||||
|                 if (tooltip) { | ||||
|                     this.element.data("tooltip", null); | ||||
|                     tooltip.delete(); | ||||
|                 } | ||||
|             if (result) { | ||||
|                 this.uiSelect.removeClass('input-error'); | ||||
|             } else { | ||||
|                 this.uiSelect.addClass('input-error'); | ||||
|             } | ||||
|             return result; | ||||
|             if (options?.returnErrorMessage === true) { | ||||
|                 return valid; | ||||
|             } | ||||
|             // Must return a boolean for no 3.x validator | ||||
|             return (typeof valid === "string") ? false : valid; | ||||
|         }, | ||||
|         show: function() { | ||||
|             this.uiSelect.show(); | ||||
|   | ||||
| @@ -190,7 +190,10 @@ RED.editor = (function() { | ||||
|                 const input = $("#"+prefix+"-"+property); | ||||
|                 const isTypedInput = input.length > 0 && input.next(".red-ui-typedInput-container").length > 0; | ||||
|                 if (isTypedInput) { | ||||
|                     valid = input.typedInput("validate"); | ||||
|                     valid = input.typedInput("validate", { returnErrorMessage: true }); | ||||
|                     if (typeof valid === "string") { | ||||
|                         return label ? label + ": " + valid : valid; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|   | ||||
| @@ -901,11 +901,25 @@ RED.utils = (function() { | ||||
|         return parts; | ||||
|     } | ||||
|  | ||||
|     function validatePropertyExpression(str) { | ||||
|     /** | ||||
|      * Validate a property expression | ||||
|      * @param {*} str - the property value | ||||
|      * @returns {boolean|string} whether the node proprty is valid. `true`: valid `false|String`: invalid | ||||
|      */ | ||||
|     function validatePropertyExpression(str, opt) { | ||||
|         try { | ||||
|             var parts = normalisePropertyExpression(str); | ||||
|             const parts = normalisePropertyExpression(str); | ||||
|             return true; | ||||
|         } catch(err) { | ||||
|             // If the validator has opt, it is a 3.x validator that | ||||
|             // can return a String to mean 'invalid' and provide a reason | ||||
|             if (opt) { | ||||
|                 if (opt.label) { | ||||
|                     return opt.label + ': ' + err.message; | ||||
|                 } | ||||
|                 return err.message; | ||||
|             } | ||||
|             // Otherwise, a 2.x returns a false value | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
| @@ -923,22 +937,24 @@ RED.utils = (function() { | ||||
|             // Allow ${ENV_VAR} value | ||||
|             return true | ||||
|         } | ||||
|         let error | ||||
|         let error; | ||||
|         if (propertyType === 'json') { | ||||
|             try { | ||||
|                 JSON.parse(propertyValue); | ||||
|             } catch(err) { | ||||
|                 error = RED._("validator.errors.invalid-json", { | ||||
|                     error: err.message | ||||
|                 }) | ||||
|                 }); | ||||
|             } | ||||
|         } else if (propertyType === 'msg' || propertyType === 'flow' || propertyType === 'global' ) { | ||||
|             if (!RED.utils.validatePropertyExpression(propertyValue)) { | ||||
|                 error = RED._("validator.errors.invalid-prop") | ||||
|             // To avoid double label | ||||
|             const valid = RED.utils.validatePropertyExpression(propertyValue, opt ? {} : null); | ||||
|             if (valid !== true) { | ||||
|                 error = opt ? valid : RED._("validator.errors.invalid-prop"); | ||||
|             } | ||||
|         } else if (propertyType === 'num') { | ||||
|             if (!/^NaN$|^[+-]?[0-9]*\.?[0-9]*([eE][-+]?[0-9]+)?$|^[+-]?(0b|0B)[01]+$|^[+-]?(0o|0O)[0-7]+$|^[+-]?(0x|0X)[0-9a-fA-F]+$/.test(propertyValue)) { | ||||
|                 error = RED._("validator.errors.invalid-num") | ||||
|                 error = RED._("validator.errors.invalid-num"); | ||||
|             } | ||||
|         } else if (propertyType === 'jsonata') { | ||||
|             try {  | ||||
| @@ -946,16 +962,16 @@ RED.utils = (function() { | ||||
|             } catch(err) { | ||||
|                 error = RED._("validator.errors.invalid-expr", { | ||||
|                     error: err.message | ||||
|                 }) | ||||
|                 }); | ||||
|             } | ||||
|         } | ||||
|         if (error) { | ||||
|             if (opt && opt.label) { | ||||
|                 return opt.label+': '+error | ||||
|                 return opt.label + ': ' + error; | ||||
|             } | ||||
|             return error | ||||
|             return error; | ||||
|         } | ||||
|         return true | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     function getMessageProperty(msg,expr) { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user