Adds support for validation rules that are aware of other form elements
This commit is contained in:
parent
f30dc39927
commit
d7e3859951
@ -10,10 +10,11 @@ export default {
|
||||
globals: {
|
||||
'is-plain-object': 'isPlainObject',
|
||||
'nanoid': 'nanoid',
|
||||
'is-url': 'isUrl'
|
||||
'is-url': 'isUrl',
|
||||
'clone-deep': 'cloneDeep'
|
||||
}
|
||||
},
|
||||
external: ['is-plain-object', 'nanoid', 'is-url'],
|
||||
external: ['is-plain-object', 'nanoid', 'is-url', 'clone-deep'],
|
||||
plugins: [
|
||||
commonjs(),
|
||||
vue({
|
||||
|
240
dist/formulate.esm.js
vendored
240
dist/formulate.esm.js
vendored
@ -256,7 +256,6 @@ FileUpload.prototype.removeFile = function removeFile (uuid) {
|
||||
*/
|
||||
FileUpload.prototype.loadPreviews = function loadPreviews () {
|
||||
this.files.map(function (file) {
|
||||
console.log(file.type);
|
||||
if (!file.previewData && window && window.FileReader && /^image\//.test(file.file.type)) {
|
||||
var reader = new FileReader();
|
||||
reader.onload = function (e) { return Object.assign(file, { previewData: e.target.result }); };
|
||||
@ -279,6 +278,11 @@ FileUpload.prototype.getFiles = function getFiles () {
|
||||
return this.files
|
||||
};
|
||||
|
||||
FileUpload.prototype.toString = function toString () {
|
||||
var descriptor = this.files.length ? this.files.length + ' files' : 'empty';
|
||||
return ("FileUpload(" + descriptor + ")")
|
||||
};
|
||||
|
||||
/**
|
||||
* Function to map over an object.
|
||||
* @param {Object} obj An object to map over
|
||||
@ -426,6 +430,42 @@ function regexForFormat (format) {
|
||||
}, escaped))
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if
|
||||
* @param {mixed} data
|
||||
*/
|
||||
function isValueType (data) {
|
||||
switch (typeof data) {
|
||||
case 'symbol':
|
||||
case 'number':
|
||||
case 'string':
|
||||
case 'boolean':
|
||||
case 'undefined':
|
||||
return true
|
||||
default:
|
||||
if (data === null) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple (somewhat non-comprehensive) cloneDeep function, valid for our use
|
||||
* case of needing to unbind reactive watchers.
|
||||
*/
|
||||
function cloneDeep (obj) {
|
||||
var newObj = {};
|
||||
for (var key in obj) {
|
||||
if (obj[key] instanceof FileUpload || isValueType(obj[key])) {
|
||||
newObj[key] = obj;
|
||||
} else {
|
||||
newObj[key] = cloneDeep(obj[key]);
|
||||
}
|
||||
}
|
||||
return newObj
|
||||
}
|
||||
|
||||
/**
|
||||
* Library of rules
|
||||
*/
|
||||
@ -433,14 +473,17 @@ var rules = {
|
||||
/**
|
||||
* Rule: the value must be "yes", "on", "1", or true
|
||||
*/
|
||||
accepted: function (value) {
|
||||
accepted: function (ref) {
|
||||
var value = ref.value;
|
||||
|
||||
return Promise.resolve(['yes', 'on', '1', 1, true, 'true'].includes(value))
|
||||
},
|
||||
|
||||
/**
|
||||
* Rule: checks if a value is after a given date. Defaults to current time
|
||||
*/
|
||||
after: function (value, compare) {
|
||||
after: function (ref, compare) {
|
||||
var value = ref.value;
|
||||
if ( compare === void 0 ) compare = false;
|
||||
|
||||
var timestamp = Date.parse(compare || new Date());
|
||||
@ -451,7 +494,8 @@ var rules = {
|
||||
/**
|
||||
* Rule: checks if the value is only alpha
|
||||
*/
|
||||
alpha: function (value, set) {
|
||||
alpha: function (ref, set) {
|
||||
var value = ref.value;
|
||||
if ( set === void 0 ) set = 'default';
|
||||
|
||||
var sets = {
|
||||
@ -465,7 +509,8 @@ var rules = {
|
||||
/**
|
||||
* Rule: checks if the value is alpha numeric
|
||||
*/
|
||||
alphanumeric: function (value, set) {
|
||||
alphanumeric: function (ref, set) {
|
||||
var value = ref.value;
|
||||
if ( set === void 0 ) set = 'default';
|
||||
|
||||
var sets = {
|
||||
@ -479,7 +524,8 @@ var rules = {
|
||||
/**
|
||||
* Rule: checks if a value is after a given date. Defaults to current time
|
||||
*/
|
||||
before: function (value, compare) {
|
||||
before: function (ref, compare) {
|
||||
var value = ref.value;
|
||||
if ( compare === void 0 ) compare = false;
|
||||
|
||||
var timestamp = Date.parse(compare || new Date());
|
||||
@ -490,7 +536,8 @@ var rules = {
|
||||
/**
|
||||
* Rule: checks if the value is between two other values
|
||||
*/
|
||||
between: function (value, from, to) {
|
||||
between: function (ref, from, to) {
|
||||
var value = ref.value;
|
||||
if ( from === void 0 ) from = 0;
|
||||
if ( to === void 0 ) to = 10;
|
||||
|
||||
@ -511,11 +558,31 @@ var rules = {
|
||||
})())
|
||||
},
|
||||
|
||||
/**
|
||||
* Confirm that the value of one field is the same as another, mostly used
|
||||
* for password confirmations.
|
||||
*/
|
||||
confirm: function (ref, field) {
|
||||
var value = ref.value;
|
||||
var getFormValues = ref.getFormValues;
|
||||
var name = ref.name;
|
||||
|
||||
return Promise.resolve((function () {
|
||||
var formValues = getFormValues();
|
||||
var confirmationFieldName = field;
|
||||
if (!confirmationFieldName) {
|
||||
confirmationFieldName = /_confirm$/.test(name) ? name.substr(0, name.length - 8) : (name + "_confirm");
|
||||
}
|
||||
return formValues[confirmationFieldName] === value
|
||||
})())
|
||||
},
|
||||
|
||||
/**
|
||||
* Rule: ensures the value is a date according to Date.parse(), or a format
|
||||
* regex.
|
||||
*/
|
||||
date: function (value, format) {
|
||||
date: function (ref, format) {
|
||||
var value = ref.value;
|
||||
if ( format === void 0 ) format = false;
|
||||
|
||||
return Promise.resolve((function () {
|
||||
@ -529,7 +596,9 @@ var rules = {
|
||||
/**
|
||||
* Rule: tests
|
||||
*/
|
||||
email: function (value) {
|
||||
email: function (ref) {
|
||||
var value = ref.value;
|
||||
|
||||
// eslint-disable-next-line
|
||||
var isEmail = /^(([^<>()\[\]\.,;:\s@\"]+(\.[^<>()\[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i;
|
||||
return Promise.resolve(isEmail.test(value))
|
||||
@ -538,7 +607,8 @@ var rules = {
|
||||
/**
|
||||
* Rule: Value is in an array (stack).
|
||||
*/
|
||||
in: function (value) {
|
||||
in: function (ref) {
|
||||
var value = ref.value;
|
||||
var stack = [], len = arguments.length - 1;
|
||||
while ( len-- > 0 ) stack[ len ] = arguments[ len + 1 ];
|
||||
|
||||
@ -553,7 +623,8 @@ var rules = {
|
||||
/**
|
||||
* Rule: Match the value against a (stack) of patterns or strings
|
||||
*/
|
||||
matches: function (value) {
|
||||
matches: function (ref) {
|
||||
var value = ref.value;
|
||||
var stack = [], len = arguments.length - 1;
|
||||
while ( len-- > 0 ) stack[ len ] = arguments[ len + 1 ];
|
||||
|
||||
@ -568,7 +639,8 @@ var rules = {
|
||||
/**
|
||||
* Check the maximum value of a particular.
|
||||
*/
|
||||
max: function (value, minimum, force) {
|
||||
max: function (ref, minimum, force) {
|
||||
var value = ref.value;
|
||||
if ( minimum === void 0 ) minimum = 10;
|
||||
|
||||
return Promise.resolve((function () {
|
||||
@ -591,13 +663,14 @@ var rules = {
|
||||
/**
|
||||
* Check the file type is correct.
|
||||
*/
|
||||
mime: function (files) {
|
||||
mime: function (ref) {
|
||||
var value = ref.value;
|
||||
var types = [], len = arguments.length - 1;
|
||||
while ( len-- > 0 ) types[ len ] = arguments[ len + 1 ];
|
||||
|
||||
return Promise.resolve((function () {
|
||||
if (files instanceof FileUpload) {
|
||||
var fileList = files.getFileList();
|
||||
if (value instanceof FileUpload) {
|
||||
var fileList = value.getFileList();
|
||||
for (var i = 0; i < fileList.length; i++) {
|
||||
var file = fileList[i];
|
||||
if (!types.includes(file.type)) {
|
||||
@ -612,7 +685,8 @@ var rules = {
|
||||
/**
|
||||
* Check the minimum value of a particular.
|
||||
*/
|
||||
min: function (value, minimum, force) {
|
||||
min: function (ref, minimum, force) {
|
||||
var value = ref.value;
|
||||
if ( minimum === void 0 ) minimum = 1;
|
||||
|
||||
return Promise.resolve((function () {
|
||||
@ -635,7 +709,8 @@ var rules = {
|
||||
/**
|
||||
* Rule: Value is not in stack.
|
||||
*/
|
||||
not: function (value) {
|
||||
not: function (ref) {
|
||||
var value = ref.value;
|
||||
var stack = [], len = arguments.length - 1;
|
||||
while ( len-- > 0 ) stack[ len ] = arguments[ len + 1 ];
|
||||
|
||||
@ -650,14 +725,17 @@ var rules = {
|
||||
/**
|
||||
* Rule: checks if the value is only alpha numeric
|
||||
*/
|
||||
number: function (value) {
|
||||
number: function (ref) {
|
||||
var value = ref.value;
|
||||
|
||||
return Promise.resolve(!isNaN(value))
|
||||
},
|
||||
|
||||
/**
|
||||
* Rule: must be a value
|
||||
*/
|
||||
required: function (value, isRequired) {
|
||||
required: function (ref, isRequired) {
|
||||
var value = ref.value;
|
||||
if ( isRequired === void 0 ) isRequired = true;
|
||||
|
||||
return Promise.resolve((function () {
|
||||
@ -680,7 +758,9 @@ var rules = {
|
||||
/**
|
||||
* Rule: checks if a string is a valid url
|
||||
*/
|
||||
url: function (value) {
|
||||
url: function (ref) {
|
||||
var value = ref.value;
|
||||
|
||||
return Promise.resolve(isUrl(value))
|
||||
}
|
||||
};
|
||||
@ -757,6 +837,16 @@ var en = {
|
||||
return ((sentence(name)) + " must be between " + (args[0]) + " and " + (args[1]) + " characters long.")
|
||||
},
|
||||
|
||||
/**
|
||||
* The confirmation field does not match
|
||||
*/
|
||||
confirm: function (ref) {
|
||||
var name = ref.name;
|
||||
var args = ref.args;
|
||||
|
||||
return ((sentence(name)) + " does not match.")
|
||||
},
|
||||
|
||||
/**
|
||||
* Is not a valid date.
|
||||
*/
|
||||
@ -1328,7 +1418,11 @@ var script = {
|
||||
var rule = ref[0];
|
||||
var args = ref[1];
|
||||
|
||||
return rule.apply(void 0, [ this$1.context.model ].concat( args ))
|
||||
return rule.apply(void 0, [ {
|
||||
value: this$1.context.model,
|
||||
getFormValues: this$1.getFormValues.bind(this$1),
|
||||
name: this$1.context.name
|
||||
} ].concat( args ))
|
||||
.then(function (res) { return res ? false : this$1.$formulate.validationMessage(rule.name, {
|
||||
args: args,
|
||||
name: this$1.mergedValidationName,
|
||||
@ -1535,7 +1629,7 @@ __vue_render__._withStripped = true;
|
||||
|
||||
|
||||
|
||||
var FormulateInput = normalizeComponent(
|
||||
var __vue_component__ = normalizeComponent(
|
||||
{ render: __vue_render__, staticRenderFns: __vue_staticRenderFns__ },
|
||||
__vue_inject_styles__,
|
||||
__vue_script__,
|
||||
@ -1548,6 +1642,44 @@ __vue_render__._withStripped = true;
|
||||
undefined
|
||||
);
|
||||
|
||||
var FormSubmission = function FormSubmission (form) {
|
||||
this.form = form;
|
||||
};
|
||||
|
||||
/**
|
||||
* Determine if the form has any validation errors.
|
||||
*
|
||||
* @return {Promise} resolves a boolean
|
||||
*/
|
||||
FormSubmission.prototype.hasValidationErrors = function hasValidationErrors () {
|
||||
return this.form.hasValidationErrors()
|
||||
};
|
||||
|
||||
/**
|
||||
* Asynchronously generate the values payload of this form.
|
||||
* @return {Promise} resolves to json
|
||||
*/
|
||||
FormSubmission.prototype.values = function values () {
|
||||
var this$1 = this;
|
||||
|
||||
return new Promise(function (resolve, reject) {
|
||||
var pending = [];
|
||||
var values = cloneDeep(this$1.form.internalModelProxy);
|
||||
for (var key in values) {
|
||||
if (typeof this$1.form.internalModelProxy[key] === 'object' && this$1.form.internalModelProxy[key] instanceof FileUpload) {
|
||||
pending.push(this$1.form.internalModelProxy[key].upload());
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @todo - how do we get these uploaded path values back into our data?
|
||||
*/
|
||||
Promise.all(pending)
|
||||
// .then(file => file.path)
|
||||
.then(function () { return resolve(values); })
|
||||
.catch(function (err) { return reject(err); });
|
||||
})
|
||||
};
|
||||
|
||||
//
|
||||
|
||||
var script$1 = {
|
||||
@ -1636,9 +1768,15 @@ var script$1 = {
|
||||
}
|
||||
},
|
||||
formSubmitted: function formSubmitted () {
|
||||
var this$1 = this;
|
||||
|
||||
// perform validation here
|
||||
this.showErrors();
|
||||
this.$emit('submit', this.internalFormModelProxy);
|
||||
var submission = new FormSubmission(this);
|
||||
this.$emit('submit-raw', submission);
|
||||
submission.hasValidationErrors()
|
||||
.then(function (hasErrors) { return hasErrors ? false : submission.values(); })
|
||||
.then(function (json) { return this$1.$emit('submit', json); });
|
||||
},
|
||||
showErrors: function showErrors () {
|
||||
for (var fieldName in this.registry) {
|
||||
@ -1647,6 +1785,15 @@ var script$1 = {
|
||||
},
|
||||
getFormValues: function getFormValues () {
|
||||
return this.internalFormModelProxy
|
||||
},
|
||||
hasValidationErrors: function hasValidationErrors () {
|
||||
var resolvers = [];
|
||||
for (var fieldName in this.registry) {
|
||||
if (typeof this.registry[fieldName].hasValidationErrors === 'function') {
|
||||
resolvers.push(this.registry[fieldName].hasValidationErrors());
|
||||
}
|
||||
}
|
||||
return Promise.all(resolvers).then(function (fields) { return !!fields.find(function (hasErrors) { return hasErrors; }); })
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -1692,7 +1839,7 @@ __vue_render__$1._withStripped = true;
|
||||
|
||||
|
||||
|
||||
var FormulateForm = normalizeComponent(
|
||||
var __vue_component__$1 = normalizeComponent(
|
||||
{ render: __vue_render__$1, staticRenderFns: __vue_staticRenderFns__$1 },
|
||||
__vue_inject_styles__$1,
|
||||
__vue_script__$1,
|
||||
@ -1773,7 +1920,7 @@ __vue_render__$2._withStripped = true;
|
||||
|
||||
|
||||
|
||||
var FormulateInputErrors = normalizeComponent(
|
||||
var __vue_component__$2 = normalizeComponent(
|
||||
{ render: __vue_render__$2, staticRenderFns: __vue_staticRenderFns__$2 },
|
||||
__vue_inject_styles__$2,
|
||||
__vue_script__$2,
|
||||
@ -1893,7 +2040,7 @@ __vue_render__$3._withStripped = true;
|
||||
|
||||
|
||||
|
||||
var FormulateInputGroup = normalizeComponent(
|
||||
var __vue_component__$3 = normalizeComponent(
|
||||
{ render: __vue_render__$3, staticRenderFns: __vue_staticRenderFns__$3 },
|
||||
__vue_inject_styles__$3,
|
||||
__vue_script__$3,
|
||||
@ -2095,7 +2242,7 @@ __vue_render__$4._withStripped = true;
|
||||
|
||||
|
||||
|
||||
var FormulateInputBox = normalizeComponent(
|
||||
var __vue_component__$4 = normalizeComponent(
|
||||
{ render: __vue_render__$4, staticRenderFns: __vue_staticRenderFns__$4 },
|
||||
__vue_inject_styles__$4,
|
||||
__vue_script__$4,
|
||||
@ -2259,7 +2406,7 @@ __vue_render__$5._withStripped = true;
|
||||
|
||||
|
||||
|
||||
var FormulateInputText = normalizeComponent(
|
||||
var __vue_component__$5 = normalizeComponent(
|
||||
{ render: __vue_render__$5, staticRenderFns: __vue_staticRenderFns__$5 },
|
||||
__vue_inject_styles__$5,
|
||||
__vue_script__$5,
|
||||
@ -2400,7 +2547,7 @@ __vue_render__$6._withStripped = true;
|
||||
|
||||
|
||||
|
||||
var FormulateFiles = normalizeComponent(
|
||||
var __vue_component__$6 = normalizeComponent(
|
||||
{ render: __vue_render__$6, staticRenderFns: __vue_staticRenderFns__$6 },
|
||||
__vue_inject_styles__$6,
|
||||
__vue_script__$6,
|
||||
@ -2418,7 +2565,7 @@ __vue_render__$6._withStripped = true;
|
||||
var script$7 = {
|
||||
name: 'FormulateInputFile',
|
||||
components: {
|
||||
FormulateFiles: FormulateFiles
|
||||
FormulateFiles: __vue_component__$6
|
||||
},
|
||||
mixins: [FormulateInputMixin],
|
||||
data: function data () {
|
||||
@ -2572,7 +2719,7 @@ __vue_render__$7._withStripped = true;
|
||||
|
||||
|
||||
|
||||
var FormulateInputFile = normalizeComponent(
|
||||
var __vue_component__$7 = normalizeComponent(
|
||||
{ render: __vue_render__$7, staticRenderFns: __vue_staticRenderFns__$7 },
|
||||
__vue_inject_styles__$7,
|
||||
__vue_script__$7,
|
||||
@ -2650,7 +2797,7 @@ __vue_render__$8._withStripped = true;
|
||||
|
||||
|
||||
|
||||
var FormulateInputButton = normalizeComponent(
|
||||
var __vue_component__$8 = normalizeComponent(
|
||||
{ render: __vue_render__$8, staticRenderFns: __vue_staticRenderFns__$8 },
|
||||
__vue_inject_styles__$8,
|
||||
__vue_script__$8,
|
||||
@ -2816,7 +2963,7 @@ __vue_render__$9._withStripped = true;
|
||||
|
||||
|
||||
|
||||
var FormulateInputSelect = normalizeComponent(
|
||||
var __vue_component__$9 = normalizeComponent(
|
||||
{ render: __vue_render__$9, staticRenderFns: __vue_staticRenderFns__$9 },
|
||||
__vue_inject_styles__$9,
|
||||
__vue_script__$9,
|
||||
@ -2980,7 +3127,7 @@ __vue_render__$a._withStripped = true;
|
||||
|
||||
|
||||
|
||||
var FormulateInputSlider = normalizeComponent(
|
||||
var __vue_component__$a = normalizeComponent(
|
||||
{ render: __vue_render__$a, staticRenderFns: __vue_staticRenderFns__$a },
|
||||
__vue_inject_styles__$a,
|
||||
__vue_script__$a,
|
||||
@ -3065,7 +3212,7 @@ __vue_render__$b._withStripped = true;
|
||||
|
||||
|
||||
|
||||
var FormulateInputTextArea = normalizeComponent(
|
||||
var __vue_component__$b = normalizeComponent(
|
||||
{ render: __vue_render__$b, staticRenderFns: __vue_staticRenderFns__$b },
|
||||
__vue_inject_styles__$b,
|
||||
__vue_script__$b,
|
||||
@ -3084,17 +3231,17 @@ __vue_render__$b._withStripped = true;
|
||||
var Formulate = function Formulate () {
|
||||
this.defaults = {
|
||||
components: {
|
||||
FormulateForm: FormulateForm,
|
||||
FormulateInput: FormulateInput,
|
||||
FormulateInputErrors: FormulateInputErrors,
|
||||
FormulateInputBox: FormulateInputBox,
|
||||
FormulateInputText: FormulateInputText,
|
||||
FormulateInputFile: FormulateInputFile,
|
||||
FormulateInputGroup: FormulateInputGroup,
|
||||
FormulateInputButton: FormulateInputButton,
|
||||
FormulateInputSelect: FormulateInputSelect,
|
||||
FormulateInputSlider: FormulateInputSlider,
|
||||
FormulateInputTextArea: FormulateInputTextArea
|
||||
FormulateForm: __vue_component__$1,
|
||||
FormulateInput: __vue_component__,
|
||||
FormulateInputErrors: __vue_component__$2,
|
||||
FormulateInputBox: __vue_component__$4,
|
||||
FormulateInputText: __vue_component__$5,
|
||||
FormulateInputFile: __vue_component__$7,
|
||||
FormulateInputGroup: __vue_component__$3,
|
||||
FormulateInputButton: __vue_component__$8,
|
||||
FormulateInputSelect: __vue_component__$9,
|
||||
FormulateInputSlider: __vue_component__$a,
|
||||
FormulateInputTextArea: __vue_component__$b
|
||||
},
|
||||
library: library,
|
||||
rules: rules,
|
||||
@ -3206,3 +3353,4 @@ Formulate.prototype.createUpload = function createUpload (fileList, context) {
|
||||
var Formulate$1 = new Formulate();
|
||||
|
||||
export default Formulate$1;
|
||||
export { FileUpload };
|
||||
|
240
dist/formulate.min.js
vendored
240
dist/formulate.min.js
vendored
@ -259,7 +259,6 @@ var Formulate = (function (exports, isUrl, nanoid, isPlainObject) {
|
||||
*/
|
||||
FileUpload.prototype.loadPreviews = function loadPreviews () {
|
||||
this.files.map(function (file) {
|
||||
console.log(file.type);
|
||||
if (!file.previewData && window && window.FileReader && /^image\//.test(file.file.type)) {
|
||||
var reader = new FileReader();
|
||||
reader.onload = function (e) { return Object.assign(file, { previewData: e.target.result }); };
|
||||
@ -282,6 +281,11 @@ var Formulate = (function (exports, isUrl, nanoid, isPlainObject) {
|
||||
return this.files
|
||||
};
|
||||
|
||||
FileUpload.prototype.toString = function toString () {
|
||||
var descriptor = this.files.length ? this.files.length + ' files' : 'empty';
|
||||
return ("FileUpload(" + descriptor + ")")
|
||||
};
|
||||
|
||||
/**
|
||||
* Function to map over an object.
|
||||
* @param {Object} obj An object to map over
|
||||
@ -429,6 +433,42 @@ var Formulate = (function (exports, isUrl, nanoid, isPlainObject) {
|
||||
}, escaped))
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if
|
||||
* @param {mixed} data
|
||||
*/
|
||||
function isValueType (data) {
|
||||
switch (typeof data) {
|
||||
case 'symbol':
|
||||
case 'number':
|
||||
case 'string':
|
||||
case 'boolean':
|
||||
case 'undefined':
|
||||
return true
|
||||
default:
|
||||
if (data === null) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple (somewhat non-comprehensive) cloneDeep function, valid for our use
|
||||
* case of needing to unbind reactive watchers.
|
||||
*/
|
||||
function cloneDeep (obj) {
|
||||
var newObj = {};
|
||||
for (var key in obj) {
|
||||
if (obj[key] instanceof FileUpload || isValueType(obj[key])) {
|
||||
newObj[key] = obj;
|
||||
} else {
|
||||
newObj[key] = cloneDeep(obj[key]);
|
||||
}
|
||||
}
|
||||
return newObj
|
||||
}
|
||||
|
||||
/**
|
||||
* Library of rules
|
||||
*/
|
||||
@ -436,14 +476,17 @@ var Formulate = (function (exports, isUrl, nanoid, isPlainObject) {
|
||||
/**
|
||||
* Rule: the value must be "yes", "on", "1", or true
|
||||
*/
|
||||
accepted: function (value) {
|
||||
accepted: function (ref) {
|
||||
var value = ref.value;
|
||||
|
||||
return Promise.resolve(['yes', 'on', '1', 1, true, 'true'].includes(value))
|
||||
},
|
||||
|
||||
/**
|
||||
* Rule: checks if a value is after a given date. Defaults to current time
|
||||
*/
|
||||
after: function (value, compare) {
|
||||
after: function (ref, compare) {
|
||||
var value = ref.value;
|
||||
if ( compare === void 0 ) compare = false;
|
||||
|
||||
var timestamp = Date.parse(compare || new Date());
|
||||
@ -454,7 +497,8 @@ var Formulate = (function (exports, isUrl, nanoid, isPlainObject) {
|
||||
/**
|
||||
* Rule: checks if the value is only alpha
|
||||
*/
|
||||
alpha: function (value, set) {
|
||||
alpha: function (ref, set) {
|
||||
var value = ref.value;
|
||||
if ( set === void 0 ) set = 'default';
|
||||
|
||||
var sets = {
|
||||
@ -468,7 +512,8 @@ var Formulate = (function (exports, isUrl, nanoid, isPlainObject) {
|
||||
/**
|
||||
* Rule: checks if the value is alpha numeric
|
||||
*/
|
||||
alphanumeric: function (value, set) {
|
||||
alphanumeric: function (ref, set) {
|
||||
var value = ref.value;
|
||||
if ( set === void 0 ) set = 'default';
|
||||
|
||||
var sets = {
|
||||
@ -482,7 +527,8 @@ var Formulate = (function (exports, isUrl, nanoid, isPlainObject) {
|
||||
/**
|
||||
* Rule: checks if a value is after a given date. Defaults to current time
|
||||
*/
|
||||
before: function (value, compare) {
|
||||
before: function (ref, compare) {
|
||||
var value = ref.value;
|
||||
if ( compare === void 0 ) compare = false;
|
||||
|
||||
var timestamp = Date.parse(compare || new Date());
|
||||
@ -493,7 +539,8 @@ var Formulate = (function (exports, isUrl, nanoid, isPlainObject) {
|
||||
/**
|
||||
* Rule: checks if the value is between two other values
|
||||
*/
|
||||
between: function (value, from, to) {
|
||||
between: function (ref, from, to) {
|
||||
var value = ref.value;
|
||||
if ( from === void 0 ) from = 0;
|
||||
if ( to === void 0 ) to = 10;
|
||||
|
||||
@ -514,11 +561,31 @@ var Formulate = (function (exports, isUrl, nanoid, isPlainObject) {
|
||||
})())
|
||||
},
|
||||
|
||||
/**
|
||||
* Confirm that the value of one field is the same as another, mostly used
|
||||
* for password confirmations.
|
||||
*/
|
||||
confirm: function (ref, field) {
|
||||
var value = ref.value;
|
||||
var getFormValues = ref.getFormValues;
|
||||
var name = ref.name;
|
||||
|
||||
return Promise.resolve((function () {
|
||||
var formValues = getFormValues();
|
||||
var confirmationFieldName = field;
|
||||
if (!confirmationFieldName) {
|
||||
confirmationFieldName = /_confirm$/.test(name) ? name.substr(0, name.length - 8) : (name + "_confirm");
|
||||
}
|
||||
return formValues[confirmationFieldName] === value
|
||||
})())
|
||||
},
|
||||
|
||||
/**
|
||||
* Rule: ensures the value is a date according to Date.parse(), or a format
|
||||
* regex.
|
||||
*/
|
||||
date: function (value, format) {
|
||||
date: function (ref, format) {
|
||||
var value = ref.value;
|
||||
if ( format === void 0 ) format = false;
|
||||
|
||||
return Promise.resolve((function () {
|
||||
@ -532,7 +599,9 @@ var Formulate = (function (exports, isUrl, nanoid, isPlainObject) {
|
||||
/**
|
||||
* Rule: tests
|
||||
*/
|
||||
email: function (value) {
|
||||
email: function (ref) {
|
||||
var value = ref.value;
|
||||
|
||||
// eslint-disable-next-line
|
||||
var isEmail = /^(([^<>()\[\]\.,;:\s@\"]+(\.[^<>()\[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i;
|
||||
return Promise.resolve(isEmail.test(value))
|
||||
@ -541,7 +610,8 @@ var Formulate = (function (exports, isUrl, nanoid, isPlainObject) {
|
||||
/**
|
||||
* Rule: Value is in an array (stack).
|
||||
*/
|
||||
in: function (value) {
|
||||
in: function (ref) {
|
||||
var value = ref.value;
|
||||
var stack = [], len = arguments.length - 1;
|
||||
while ( len-- > 0 ) stack[ len ] = arguments[ len + 1 ];
|
||||
|
||||
@ -556,7 +626,8 @@ var Formulate = (function (exports, isUrl, nanoid, isPlainObject) {
|
||||
/**
|
||||
* Rule: Match the value against a (stack) of patterns or strings
|
||||
*/
|
||||
matches: function (value) {
|
||||
matches: function (ref) {
|
||||
var value = ref.value;
|
||||
var stack = [], len = arguments.length - 1;
|
||||
while ( len-- > 0 ) stack[ len ] = arguments[ len + 1 ];
|
||||
|
||||
@ -571,7 +642,8 @@ var Formulate = (function (exports, isUrl, nanoid, isPlainObject) {
|
||||
/**
|
||||
* Check the maximum value of a particular.
|
||||
*/
|
||||
max: function (value, minimum, force) {
|
||||
max: function (ref, minimum, force) {
|
||||
var value = ref.value;
|
||||
if ( minimum === void 0 ) minimum = 10;
|
||||
|
||||
return Promise.resolve((function () {
|
||||
@ -594,13 +666,14 @@ var Formulate = (function (exports, isUrl, nanoid, isPlainObject) {
|
||||
/**
|
||||
* Check the file type is correct.
|
||||
*/
|
||||
mime: function (files) {
|
||||
mime: function (ref) {
|
||||
var value = ref.value;
|
||||
var types = [], len = arguments.length - 1;
|
||||
while ( len-- > 0 ) types[ len ] = arguments[ len + 1 ];
|
||||
|
||||
return Promise.resolve((function () {
|
||||
if (files instanceof FileUpload) {
|
||||
var fileList = files.getFileList();
|
||||
if (value instanceof FileUpload) {
|
||||
var fileList = value.getFileList();
|
||||
for (var i = 0; i < fileList.length; i++) {
|
||||
var file = fileList[i];
|
||||
if (!types.includes(file.type)) {
|
||||
@ -615,7 +688,8 @@ var Formulate = (function (exports, isUrl, nanoid, isPlainObject) {
|
||||
/**
|
||||
* Check the minimum value of a particular.
|
||||
*/
|
||||
min: function (value, minimum, force) {
|
||||
min: function (ref, minimum, force) {
|
||||
var value = ref.value;
|
||||
if ( minimum === void 0 ) minimum = 1;
|
||||
|
||||
return Promise.resolve((function () {
|
||||
@ -638,7 +712,8 @@ var Formulate = (function (exports, isUrl, nanoid, isPlainObject) {
|
||||
/**
|
||||
* Rule: Value is not in stack.
|
||||
*/
|
||||
not: function (value) {
|
||||
not: function (ref) {
|
||||
var value = ref.value;
|
||||
var stack = [], len = arguments.length - 1;
|
||||
while ( len-- > 0 ) stack[ len ] = arguments[ len + 1 ];
|
||||
|
||||
@ -653,14 +728,17 @@ var Formulate = (function (exports, isUrl, nanoid, isPlainObject) {
|
||||
/**
|
||||
* Rule: checks if the value is only alpha numeric
|
||||
*/
|
||||
number: function (value) {
|
||||
number: function (ref) {
|
||||
var value = ref.value;
|
||||
|
||||
return Promise.resolve(!isNaN(value))
|
||||
},
|
||||
|
||||
/**
|
||||
* Rule: must be a value
|
||||
*/
|
||||
required: function (value, isRequired) {
|
||||
required: function (ref, isRequired) {
|
||||
var value = ref.value;
|
||||
if ( isRequired === void 0 ) isRequired = true;
|
||||
|
||||
return Promise.resolve((function () {
|
||||
@ -683,7 +761,9 @@ var Formulate = (function (exports, isUrl, nanoid, isPlainObject) {
|
||||
/**
|
||||
* Rule: checks if a string is a valid url
|
||||
*/
|
||||
url: function (value) {
|
||||
url: function (ref) {
|
||||
var value = ref.value;
|
||||
|
||||
return Promise.resolve(isUrl(value))
|
||||
}
|
||||
};
|
||||
@ -760,6 +840,16 @@ var Formulate = (function (exports, isUrl, nanoid, isPlainObject) {
|
||||
return ((sentence(name)) + " must be between " + (args[0]) + " and " + (args[1]) + " characters long.")
|
||||
},
|
||||
|
||||
/**
|
||||
* The confirmation field does not match
|
||||
*/
|
||||
confirm: function (ref) {
|
||||
var name = ref.name;
|
||||
var args = ref.args;
|
||||
|
||||
return ((sentence(name)) + " does not match.")
|
||||
},
|
||||
|
||||
/**
|
||||
* Is not a valid date.
|
||||
*/
|
||||
@ -1331,7 +1421,11 @@ var Formulate = (function (exports, isUrl, nanoid, isPlainObject) {
|
||||
var rule = ref[0];
|
||||
var args = ref[1];
|
||||
|
||||
return rule.apply(void 0, [ this$1.context.model ].concat( args ))
|
||||
return rule.apply(void 0, [ {
|
||||
value: this$1.context.model,
|
||||
getFormValues: this$1.getFormValues.bind(this$1),
|
||||
name: this$1.context.name
|
||||
} ].concat( args ))
|
||||
.then(function (res) { return res ? false : this$1.$formulate.validationMessage(rule.name, {
|
||||
args: args,
|
||||
name: this$1.mergedValidationName,
|
||||
@ -1538,7 +1632,7 @@ var Formulate = (function (exports, isUrl, nanoid, isPlainObject) {
|
||||
|
||||
|
||||
|
||||
var FormulateInput = normalizeComponent(
|
||||
var __vue_component__ = normalizeComponent(
|
||||
{ render: __vue_render__, staticRenderFns: __vue_staticRenderFns__ },
|
||||
__vue_inject_styles__,
|
||||
__vue_script__,
|
||||
@ -1551,6 +1645,44 @@ var Formulate = (function (exports, isUrl, nanoid, isPlainObject) {
|
||||
undefined
|
||||
);
|
||||
|
||||
var FormSubmission = function FormSubmission (form) {
|
||||
this.form = form;
|
||||
};
|
||||
|
||||
/**
|
||||
* Determine if the form has any validation errors.
|
||||
*
|
||||
* @return {Promise} resolves a boolean
|
||||
*/
|
||||
FormSubmission.prototype.hasValidationErrors = function hasValidationErrors () {
|
||||
return this.form.hasValidationErrors()
|
||||
};
|
||||
|
||||
/**
|
||||
* Asynchronously generate the values payload of this form.
|
||||
* @return {Promise} resolves to json
|
||||
*/
|
||||
FormSubmission.prototype.values = function values () {
|
||||
var this$1 = this;
|
||||
|
||||
return new Promise(function (resolve, reject) {
|
||||
var pending = [];
|
||||
var values = cloneDeep(this$1.form.internalModelProxy);
|
||||
for (var key in values) {
|
||||
if (typeof this$1.form.internalModelProxy[key] === 'object' && this$1.form.internalModelProxy[key] instanceof FileUpload) {
|
||||
pending.push(this$1.form.internalModelProxy[key].upload());
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @todo - how do we get these uploaded path values back into our data?
|
||||
*/
|
||||
Promise.all(pending)
|
||||
// .then(file => file.path)
|
||||
.then(function () { return resolve(values); })
|
||||
.catch(function (err) { return reject(err); });
|
||||
})
|
||||
};
|
||||
|
||||
//
|
||||
|
||||
var script$1 = {
|
||||
@ -1639,9 +1771,15 @@ var Formulate = (function (exports, isUrl, nanoid, isPlainObject) {
|
||||
}
|
||||
},
|
||||
formSubmitted: function formSubmitted () {
|
||||
var this$1 = this;
|
||||
|
||||
// perform validation here
|
||||
this.showErrors();
|
||||
this.$emit('submit', this.internalFormModelProxy);
|
||||
var submission = new FormSubmission(this);
|
||||
this.$emit('submit-raw', submission);
|
||||
submission.hasValidationErrors()
|
||||
.then(function (hasErrors) { return hasErrors ? false : submission.values(); })
|
||||
.then(function (json) { return this$1.$emit('submit', json); });
|
||||
},
|
||||
showErrors: function showErrors () {
|
||||
for (var fieldName in this.registry) {
|
||||
@ -1650,6 +1788,15 @@ var Formulate = (function (exports, isUrl, nanoid, isPlainObject) {
|
||||
},
|
||||
getFormValues: function getFormValues () {
|
||||
return this.internalFormModelProxy
|
||||
},
|
||||
hasValidationErrors: function hasValidationErrors () {
|
||||
var resolvers = [];
|
||||
for (var fieldName in this.registry) {
|
||||
if (typeof this.registry[fieldName].hasValidationErrors === 'function') {
|
||||
resolvers.push(this.registry[fieldName].hasValidationErrors());
|
||||
}
|
||||
}
|
||||
return Promise.all(resolvers).then(function (fields) { return !!fields.find(function (hasErrors) { return hasErrors; }); })
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -1695,7 +1842,7 @@ var Formulate = (function (exports, isUrl, nanoid, isPlainObject) {
|
||||
|
||||
|
||||
|
||||
var FormulateForm = normalizeComponent(
|
||||
var __vue_component__$1 = normalizeComponent(
|
||||
{ render: __vue_render__$1, staticRenderFns: __vue_staticRenderFns__$1 },
|
||||
__vue_inject_styles__$1,
|
||||
__vue_script__$1,
|
||||
@ -1776,7 +1923,7 @@ var Formulate = (function (exports, isUrl, nanoid, isPlainObject) {
|
||||
|
||||
|
||||
|
||||
var FormulateInputErrors = normalizeComponent(
|
||||
var __vue_component__$2 = normalizeComponent(
|
||||
{ render: __vue_render__$2, staticRenderFns: __vue_staticRenderFns__$2 },
|
||||
__vue_inject_styles__$2,
|
||||
__vue_script__$2,
|
||||
@ -1896,7 +2043,7 @@ var Formulate = (function (exports, isUrl, nanoid, isPlainObject) {
|
||||
|
||||
|
||||
|
||||
var FormulateInputGroup = normalizeComponent(
|
||||
var __vue_component__$3 = normalizeComponent(
|
||||
{ render: __vue_render__$3, staticRenderFns: __vue_staticRenderFns__$3 },
|
||||
__vue_inject_styles__$3,
|
||||
__vue_script__$3,
|
||||
@ -2098,7 +2245,7 @@ var Formulate = (function (exports, isUrl, nanoid, isPlainObject) {
|
||||
|
||||
|
||||
|
||||
var FormulateInputBox = normalizeComponent(
|
||||
var __vue_component__$4 = normalizeComponent(
|
||||
{ render: __vue_render__$4, staticRenderFns: __vue_staticRenderFns__$4 },
|
||||
__vue_inject_styles__$4,
|
||||
__vue_script__$4,
|
||||
@ -2262,7 +2409,7 @@ var Formulate = (function (exports, isUrl, nanoid, isPlainObject) {
|
||||
|
||||
|
||||
|
||||
var FormulateInputText = normalizeComponent(
|
||||
var __vue_component__$5 = normalizeComponent(
|
||||
{ render: __vue_render__$5, staticRenderFns: __vue_staticRenderFns__$5 },
|
||||
__vue_inject_styles__$5,
|
||||
__vue_script__$5,
|
||||
@ -2403,7 +2550,7 @@ var Formulate = (function (exports, isUrl, nanoid, isPlainObject) {
|
||||
|
||||
|
||||
|
||||
var FormulateFiles = normalizeComponent(
|
||||
var __vue_component__$6 = normalizeComponent(
|
||||
{ render: __vue_render__$6, staticRenderFns: __vue_staticRenderFns__$6 },
|
||||
__vue_inject_styles__$6,
|
||||
__vue_script__$6,
|
||||
@ -2421,7 +2568,7 @@ var Formulate = (function (exports, isUrl, nanoid, isPlainObject) {
|
||||
var script$7 = {
|
||||
name: 'FormulateInputFile',
|
||||
components: {
|
||||
FormulateFiles: FormulateFiles
|
||||
FormulateFiles: __vue_component__$6
|
||||
},
|
||||
mixins: [FormulateInputMixin],
|
||||
data: function data () {
|
||||
@ -2575,7 +2722,7 @@ var Formulate = (function (exports, isUrl, nanoid, isPlainObject) {
|
||||
|
||||
|
||||
|
||||
var FormulateInputFile = normalizeComponent(
|
||||
var __vue_component__$7 = normalizeComponent(
|
||||
{ render: __vue_render__$7, staticRenderFns: __vue_staticRenderFns__$7 },
|
||||
__vue_inject_styles__$7,
|
||||
__vue_script__$7,
|
||||
@ -2653,7 +2800,7 @@ var Formulate = (function (exports, isUrl, nanoid, isPlainObject) {
|
||||
|
||||
|
||||
|
||||
var FormulateInputButton = normalizeComponent(
|
||||
var __vue_component__$8 = normalizeComponent(
|
||||
{ render: __vue_render__$8, staticRenderFns: __vue_staticRenderFns__$8 },
|
||||
__vue_inject_styles__$8,
|
||||
__vue_script__$8,
|
||||
@ -2819,7 +2966,7 @@ var Formulate = (function (exports, isUrl, nanoid, isPlainObject) {
|
||||
|
||||
|
||||
|
||||
var FormulateInputSelect = normalizeComponent(
|
||||
var __vue_component__$9 = normalizeComponent(
|
||||
{ render: __vue_render__$9, staticRenderFns: __vue_staticRenderFns__$9 },
|
||||
__vue_inject_styles__$9,
|
||||
__vue_script__$9,
|
||||
@ -2983,7 +3130,7 @@ var Formulate = (function (exports, isUrl, nanoid, isPlainObject) {
|
||||
|
||||
|
||||
|
||||
var FormulateInputSlider = normalizeComponent(
|
||||
var __vue_component__$a = normalizeComponent(
|
||||
{ render: __vue_render__$a, staticRenderFns: __vue_staticRenderFns__$a },
|
||||
__vue_inject_styles__$a,
|
||||
__vue_script__$a,
|
||||
@ -3068,7 +3215,7 @@ var Formulate = (function (exports, isUrl, nanoid, isPlainObject) {
|
||||
|
||||
|
||||
|
||||
var FormulateInputTextArea = normalizeComponent(
|
||||
var __vue_component__$b = normalizeComponent(
|
||||
{ render: __vue_render__$b, staticRenderFns: __vue_staticRenderFns__$b },
|
||||
__vue_inject_styles__$b,
|
||||
__vue_script__$b,
|
||||
@ -3087,17 +3234,17 @@ var Formulate = (function (exports, isUrl, nanoid, isPlainObject) {
|
||||
var Formulate = function Formulate () {
|
||||
this.defaults = {
|
||||
components: {
|
||||
FormulateForm: FormulateForm,
|
||||
FormulateInput: FormulateInput,
|
||||
FormulateInputErrors: FormulateInputErrors,
|
||||
FormulateInputBox: FormulateInputBox,
|
||||
FormulateInputText: FormulateInputText,
|
||||
FormulateInputFile: FormulateInputFile,
|
||||
FormulateInputGroup: FormulateInputGroup,
|
||||
FormulateInputButton: FormulateInputButton,
|
||||
FormulateInputSelect: FormulateInputSelect,
|
||||
FormulateInputSlider: FormulateInputSlider,
|
||||
FormulateInputTextArea: FormulateInputTextArea
|
||||
FormulateForm: __vue_component__$1,
|
||||
FormulateInput: __vue_component__,
|
||||
FormulateInputErrors: __vue_component__$2,
|
||||
FormulateInputBox: __vue_component__$4,
|
||||
FormulateInputText: __vue_component__$5,
|
||||
FormulateInputFile: __vue_component__$7,
|
||||
FormulateInputGroup: __vue_component__$3,
|
||||
FormulateInputButton: __vue_component__$8,
|
||||
FormulateInputSelect: __vue_component__$9,
|
||||
FormulateInputSlider: __vue_component__$a,
|
||||
FormulateInputTextArea: __vue_component__$b
|
||||
},
|
||||
library: library,
|
||||
rules: rules,
|
||||
@ -3208,6 +3355,7 @@ var Formulate = (function (exports, isUrl, nanoid, isPlainObject) {
|
||||
|
||||
var Formulate$1 = new Formulate();
|
||||
|
||||
exports.FileUpload = FileUpload;
|
||||
exports.default = Formulate$1;
|
||||
|
||||
return exports;
|
||||
|
240
dist/formulate.umd.js
vendored
240
dist/formulate.umd.js
vendored
@ -262,7 +262,6 @@
|
||||
*/
|
||||
FileUpload.prototype.loadPreviews = function loadPreviews () {
|
||||
this.files.map(function (file) {
|
||||
console.log(file.type);
|
||||
if (!file.previewData && window && window.FileReader && /^image\//.test(file.file.type)) {
|
||||
var reader = new FileReader();
|
||||
reader.onload = function (e) { return Object.assign(file, { previewData: e.target.result }); };
|
||||
@ -285,6 +284,11 @@
|
||||
return this.files
|
||||
};
|
||||
|
||||
FileUpload.prototype.toString = function toString () {
|
||||
var descriptor = this.files.length ? this.files.length + ' files' : 'empty';
|
||||
return ("FileUpload(" + descriptor + ")")
|
||||
};
|
||||
|
||||
/**
|
||||
* Function to map over an object.
|
||||
* @param {Object} obj An object to map over
|
||||
@ -432,6 +436,42 @@
|
||||
}, escaped))
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if
|
||||
* @param {mixed} data
|
||||
*/
|
||||
function isValueType (data) {
|
||||
switch (typeof data) {
|
||||
case 'symbol':
|
||||
case 'number':
|
||||
case 'string':
|
||||
case 'boolean':
|
||||
case 'undefined':
|
||||
return true
|
||||
default:
|
||||
if (data === null) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple (somewhat non-comprehensive) cloneDeep function, valid for our use
|
||||
* case of needing to unbind reactive watchers.
|
||||
*/
|
||||
function cloneDeep (obj) {
|
||||
var newObj = {};
|
||||
for (var key in obj) {
|
||||
if (obj[key] instanceof FileUpload || isValueType(obj[key])) {
|
||||
newObj[key] = obj;
|
||||
} else {
|
||||
newObj[key] = cloneDeep(obj[key]);
|
||||
}
|
||||
}
|
||||
return newObj
|
||||
}
|
||||
|
||||
/**
|
||||
* Library of rules
|
||||
*/
|
||||
@ -439,14 +479,17 @@
|
||||
/**
|
||||
* Rule: the value must be "yes", "on", "1", or true
|
||||
*/
|
||||
accepted: function (value) {
|
||||
accepted: function (ref) {
|
||||
var value = ref.value;
|
||||
|
||||
return Promise.resolve(['yes', 'on', '1', 1, true, 'true'].includes(value))
|
||||
},
|
||||
|
||||
/**
|
||||
* Rule: checks if a value is after a given date. Defaults to current time
|
||||
*/
|
||||
after: function (value, compare) {
|
||||
after: function (ref, compare) {
|
||||
var value = ref.value;
|
||||
if ( compare === void 0 ) compare = false;
|
||||
|
||||
var timestamp = Date.parse(compare || new Date());
|
||||
@ -457,7 +500,8 @@
|
||||
/**
|
||||
* Rule: checks if the value is only alpha
|
||||
*/
|
||||
alpha: function (value, set) {
|
||||
alpha: function (ref, set) {
|
||||
var value = ref.value;
|
||||
if ( set === void 0 ) set = 'default';
|
||||
|
||||
var sets = {
|
||||
@ -471,7 +515,8 @@
|
||||
/**
|
||||
* Rule: checks if the value is alpha numeric
|
||||
*/
|
||||
alphanumeric: function (value, set) {
|
||||
alphanumeric: function (ref, set) {
|
||||
var value = ref.value;
|
||||
if ( set === void 0 ) set = 'default';
|
||||
|
||||
var sets = {
|
||||
@ -485,7 +530,8 @@
|
||||
/**
|
||||
* Rule: checks if a value is after a given date. Defaults to current time
|
||||
*/
|
||||
before: function (value, compare) {
|
||||
before: function (ref, compare) {
|
||||
var value = ref.value;
|
||||
if ( compare === void 0 ) compare = false;
|
||||
|
||||
var timestamp = Date.parse(compare || new Date());
|
||||
@ -496,7 +542,8 @@
|
||||
/**
|
||||
* Rule: checks if the value is between two other values
|
||||
*/
|
||||
between: function (value, from, to) {
|
||||
between: function (ref, from, to) {
|
||||
var value = ref.value;
|
||||
if ( from === void 0 ) from = 0;
|
||||
if ( to === void 0 ) to = 10;
|
||||
|
||||
@ -517,11 +564,31 @@
|
||||
})())
|
||||
},
|
||||
|
||||
/**
|
||||
* Confirm that the value of one field is the same as another, mostly used
|
||||
* for password confirmations.
|
||||
*/
|
||||
confirm: function (ref, field) {
|
||||
var value = ref.value;
|
||||
var getFormValues = ref.getFormValues;
|
||||
var name = ref.name;
|
||||
|
||||
return Promise.resolve((function () {
|
||||
var formValues = getFormValues();
|
||||
var confirmationFieldName = field;
|
||||
if (!confirmationFieldName) {
|
||||
confirmationFieldName = /_confirm$/.test(name) ? name.substr(0, name.length - 8) : (name + "_confirm");
|
||||
}
|
||||
return formValues[confirmationFieldName] === value
|
||||
})())
|
||||
},
|
||||
|
||||
/**
|
||||
* Rule: ensures the value is a date according to Date.parse(), or a format
|
||||
* regex.
|
||||
*/
|
||||
date: function (value, format) {
|
||||
date: function (ref, format) {
|
||||
var value = ref.value;
|
||||
if ( format === void 0 ) format = false;
|
||||
|
||||
return Promise.resolve((function () {
|
||||
@ -535,7 +602,9 @@
|
||||
/**
|
||||
* Rule: tests
|
||||
*/
|
||||
email: function (value) {
|
||||
email: function (ref) {
|
||||
var value = ref.value;
|
||||
|
||||
// eslint-disable-next-line
|
||||
var isEmail = /^(([^<>()\[\]\.,;:\s@\"]+(\.[^<>()\[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i;
|
||||
return Promise.resolve(isEmail.test(value))
|
||||
@ -544,7 +613,8 @@
|
||||
/**
|
||||
* Rule: Value is in an array (stack).
|
||||
*/
|
||||
in: function (value) {
|
||||
in: function (ref) {
|
||||
var value = ref.value;
|
||||
var stack = [], len = arguments.length - 1;
|
||||
while ( len-- > 0 ) stack[ len ] = arguments[ len + 1 ];
|
||||
|
||||
@ -559,7 +629,8 @@
|
||||
/**
|
||||
* Rule: Match the value against a (stack) of patterns or strings
|
||||
*/
|
||||
matches: function (value) {
|
||||
matches: function (ref) {
|
||||
var value = ref.value;
|
||||
var stack = [], len = arguments.length - 1;
|
||||
while ( len-- > 0 ) stack[ len ] = arguments[ len + 1 ];
|
||||
|
||||
@ -574,7 +645,8 @@
|
||||
/**
|
||||
* Check the maximum value of a particular.
|
||||
*/
|
||||
max: function (value, minimum, force) {
|
||||
max: function (ref, minimum, force) {
|
||||
var value = ref.value;
|
||||
if ( minimum === void 0 ) minimum = 10;
|
||||
|
||||
return Promise.resolve((function () {
|
||||
@ -597,13 +669,14 @@
|
||||
/**
|
||||
* Check the file type is correct.
|
||||
*/
|
||||
mime: function (files) {
|
||||
mime: function (ref) {
|
||||
var value = ref.value;
|
||||
var types = [], len = arguments.length - 1;
|
||||
while ( len-- > 0 ) types[ len ] = arguments[ len + 1 ];
|
||||
|
||||
return Promise.resolve((function () {
|
||||
if (files instanceof FileUpload) {
|
||||
var fileList = files.getFileList();
|
||||
if (value instanceof FileUpload) {
|
||||
var fileList = value.getFileList();
|
||||
for (var i = 0; i < fileList.length; i++) {
|
||||
var file = fileList[i];
|
||||
if (!types.includes(file.type)) {
|
||||
@ -618,7 +691,8 @@
|
||||
/**
|
||||
* Check the minimum value of a particular.
|
||||
*/
|
||||
min: function (value, minimum, force) {
|
||||
min: function (ref, minimum, force) {
|
||||
var value = ref.value;
|
||||
if ( minimum === void 0 ) minimum = 1;
|
||||
|
||||
return Promise.resolve((function () {
|
||||
@ -641,7 +715,8 @@
|
||||
/**
|
||||
* Rule: Value is not in stack.
|
||||
*/
|
||||
not: function (value) {
|
||||
not: function (ref) {
|
||||
var value = ref.value;
|
||||
var stack = [], len = arguments.length - 1;
|
||||
while ( len-- > 0 ) stack[ len ] = arguments[ len + 1 ];
|
||||
|
||||
@ -656,14 +731,17 @@
|
||||
/**
|
||||
* Rule: checks if the value is only alpha numeric
|
||||
*/
|
||||
number: function (value) {
|
||||
number: function (ref) {
|
||||
var value = ref.value;
|
||||
|
||||
return Promise.resolve(!isNaN(value))
|
||||
},
|
||||
|
||||
/**
|
||||
* Rule: must be a value
|
||||
*/
|
||||
required: function (value, isRequired) {
|
||||
required: function (ref, isRequired) {
|
||||
var value = ref.value;
|
||||
if ( isRequired === void 0 ) isRequired = true;
|
||||
|
||||
return Promise.resolve((function () {
|
||||
@ -686,7 +764,9 @@
|
||||
/**
|
||||
* Rule: checks if a string is a valid url
|
||||
*/
|
||||
url: function (value) {
|
||||
url: function (ref) {
|
||||
var value = ref.value;
|
||||
|
||||
return Promise.resolve(isUrl(value))
|
||||
}
|
||||
};
|
||||
@ -763,6 +843,16 @@
|
||||
return ((sentence(name)) + " must be between " + (args[0]) + " and " + (args[1]) + " characters long.")
|
||||
},
|
||||
|
||||
/**
|
||||
* The confirmation field does not match
|
||||
*/
|
||||
confirm: function (ref) {
|
||||
var name = ref.name;
|
||||
var args = ref.args;
|
||||
|
||||
return ((sentence(name)) + " does not match.")
|
||||
},
|
||||
|
||||
/**
|
||||
* Is not a valid date.
|
||||
*/
|
||||
@ -1334,7 +1424,11 @@
|
||||
var rule = ref[0];
|
||||
var args = ref[1];
|
||||
|
||||
return rule.apply(void 0, [ this$1.context.model ].concat( args ))
|
||||
return rule.apply(void 0, [ {
|
||||
value: this$1.context.model,
|
||||
getFormValues: this$1.getFormValues.bind(this$1),
|
||||
name: this$1.context.name
|
||||
} ].concat( args ))
|
||||
.then(function (res) { return res ? false : this$1.$formulate.validationMessage(rule.name, {
|
||||
args: args,
|
||||
name: this$1.mergedValidationName,
|
||||
@ -1541,7 +1635,7 @@
|
||||
|
||||
|
||||
|
||||
var FormulateInput = normalizeComponent(
|
||||
var __vue_component__ = normalizeComponent(
|
||||
{ render: __vue_render__, staticRenderFns: __vue_staticRenderFns__ },
|
||||
__vue_inject_styles__,
|
||||
__vue_script__,
|
||||
@ -1554,6 +1648,44 @@
|
||||
undefined
|
||||
);
|
||||
|
||||
var FormSubmission = function FormSubmission (form) {
|
||||
this.form = form;
|
||||
};
|
||||
|
||||
/**
|
||||
* Determine if the form has any validation errors.
|
||||
*
|
||||
* @return {Promise} resolves a boolean
|
||||
*/
|
||||
FormSubmission.prototype.hasValidationErrors = function hasValidationErrors () {
|
||||
return this.form.hasValidationErrors()
|
||||
};
|
||||
|
||||
/**
|
||||
* Asynchronously generate the values payload of this form.
|
||||
* @return {Promise} resolves to json
|
||||
*/
|
||||
FormSubmission.prototype.values = function values () {
|
||||
var this$1 = this;
|
||||
|
||||
return new Promise(function (resolve, reject) {
|
||||
var pending = [];
|
||||
var values = cloneDeep(this$1.form.internalModelProxy);
|
||||
for (var key in values) {
|
||||
if (typeof this$1.form.internalModelProxy[key] === 'object' && this$1.form.internalModelProxy[key] instanceof FileUpload) {
|
||||
pending.push(this$1.form.internalModelProxy[key].upload());
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @todo - how do we get these uploaded path values back into our data?
|
||||
*/
|
||||
Promise.all(pending)
|
||||
// .then(file => file.path)
|
||||
.then(function () { return resolve(values); })
|
||||
.catch(function (err) { return reject(err); });
|
||||
})
|
||||
};
|
||||
|
||||
//
|
||||
|
||||
var script$1 = {
|
||||
@ -1642,9 +1774,15 @@
|
||||
}
|
||||
},
|
||||
formSubmitted: function formSubmitted () {
|
||||
var this$1 = this;
|
||||
|
||||
// perform validation here
|
||||
this.showErrors();
|
||||
this.$emit('submit', this.internalFormModelProxy);
|
||||
var submission = new FormSubmission(this);
|
||||
this.$emit('submit-raw', submission);
|
||||
submission.hasValidationErrors()
|
||||
.then(function (hasErrors) { return hasErrors ? false : submission.values(); })
|
||||
.then(function (json) { return this$1.$emit('submit', json); });
|
||||
},
|
||||
showErrors: function showErrors () {
|
||||
for (var fieldName in this.registry) {
|
||||
@ -1653,6 +1791,15 @@
|
||||
},
|
||||
getFormValues: function getFormValues () {
|
||||
return this.internalFormModelProxy
|
||||
},
|
||||
hasValidationErrors: function hasValidationErrors () {
|
||||
var resolvers = [];
|
||||
for (var fieldName in this.registry) {
|
||||
if (typeof this.registry[fieldName].hasValidationErrors === 'function') {
|
||||
resolvers.push(this.registry[fieldName].hasValidationErrors());
|
||||
}
|
||||
}
|
||||
return Promise.all(resolvers).then(function (fields) { return !!fields.find(function (hasErrors) { return hasErrors; }); })
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -1698,7 +1845,7 @@
|
||||
|
||||
|
||||
|
||||
var FormulateForm = normalizeComponent(
|
||||
var __vue_component__$1 = normalizeComponent(
|
||||
{ render: __vue_render__$1, staticRenderFns: __vue_staticRenderFns__$1 },
|
||||
__vue_inject_styles__$1,
|
||||
__vue_script__$1,
|
||||
@ -1779,7 +1926,7 @@
|
||||
|
||||
|
||||
|
||||
var FormulateInputErrors = normalizeComponent(
|
||||
var __vue_component__$2 = normalizeComponent(
|
||||
{ render: __vue_render__$2, staticRenderFns: __vue_staticRenderFns__$2 },
|
||||
__vue_inject_styles__$2,
|
||||
__vue_script__$2,
|
||||
@ -1899,7 +2046,7 @@
|
||||
|
||||
|
||||
|
||||
var FormulateInputGroup = normalizeComponent(
|
||||
var __vue_component__$3 = normalizeComponent(
|
||||
{ render: __vue_render__$3, staticRenderFns: __vue_staticRenderFns__$3 },
|
||||
__vue_inject_styles__$3,
|
||||
__vue_script__$3,
|
||||
@ -2101,7 +2248,7 @@
|
||||
|
||||
|
||||
|
||||
var FormulateInputBox = normalizeComponent(
|
||||
var __vue_component__$4 = normalizeComponent(
|
||||
{ render: __vue_render__$4, staticRenderFns: __vue_staticRenderFns__$4 },
|
||||
__vue_inject_styles__$4,
|
||||
__vue_script__$4,
|
||||
@ -2265,7 +2412,7 @@
|
||||
|
||||
|
||||
|
||||
var FormulateInputText = normalizeComponent(
|
||||
var __vue_component__$5 = normalizeComponent(
|
||||
{ render: __vue_render__$5, staticRenderFns: __vue_staticRenderFns__$5 },
|
||||
__vue_inject_styles__$5,
|
||||
__vue_script__$5,
|
||||
@ -2406,7 +2553,7 @@
|
||||
|
||||
|
||||
|
||||
var FormulateFiles = normalizeComponent(
|
||||
var __vue_component__$6 = normalizeComponent(
|
||||
{ render: __vue_render__$6, staticRenderFns: __vue_staticRenderFns__$6 },
|
||||
__vue_inject_styles__$6,
|
||||
__vue_script__$6,
|
||||
@ -2424,7 +2571,7 @@
|
||||
var script$7 = {
|
||||
name: 'FormulateInputFile',
|
||||
components: {
|
||||
FormulateFiles: FormulateFiles
|
||||
FormulateFiles: __vue_component__$6
|
||||
},
|
||||
mixins: [FormulateInputMixin],
|
||||
data: function data () {
|
||||
@ -2578,7 +2725,7 @@
|
||||
|
||||
|
||||
|
||||
var FormulateInputFile = normalizeComponent(
|
||||
var __vue_component__$7 = normalizeComponent(
|
||||
{ render: __vue_render__$7, staticRenderFns: __vue_staticRenderFns__$7 },
|
||||
__vue_inject_styles__$7,
|
||||
__vue_script__$7,
|
||||
@ -2656,7 +2803,7 @@
|
||||
|
||||
|
||||
|
||||
var FormulateInputButton = normalizeComponent(
|
||||
var __vue_component__$8 = normalizeComponent(
|
||||
{ render: __vue_render__$8, staticRenderFns: __vue_staticRenderFns__$8 },
|
||||
__vue_inject_styles__$8,
|
||||
__vue_script__$8,
|
||||
@ -2822,7 +2969,7 @@
|
||||
|
||||
|
||||
|
||||
var FormulateInputSelect = normalizeComponent(
|
||||
var __vue_component__$9 = normalizeComponent(
|
||||
{ render: __vue_render__$9, staticRenderFns: __vue_staticRenderFns__$9 },
|
||||
__vue_inject_styles__$9,
|
||||
__vue_script__$9,
|
||||
@ -2986,7 +3133,7 @@
|
||||
|
||||
|
||||
|
||||
var FormulateInputSlider = normalizeComponent(
|
||||
var __vue_component__$a = normalizeComponent(
|
||||
{ render: __vue_render__$a, staticRenderFns: __vue_staticRenderFns__$a },
|
||||
__vue_inject_styles__$a,
|
||||
__vue_script__$a,
|
||||
@ -3071,7 +3218,7 @@
|
||||
|
||||
|
||||
|
||||
var FormulateInputTextArea = normalizeComponent(
|
||||
var __vue_component__$b = normalizeComponent(
|
||||
{ render: __vue_render__$b, staticRenderFns: __vue_staticRenderFns__$b },
|
||||
__vue_inject_styles__$b,
|
||||
__vue_script__$b,
|
||||
@ -3090,17 +3237,17 @@
|
||||
var Formulate = function Formulate () {
|
||||
this.defaults = {
|
||||
components: {
|
||||
FormulateForm: FormulateForm,
|
||||
FormulateInput: FormulateInput,
|
||||
FormulateInputErrors: FormulateInputErrors,
|
||||
FormulateInputBox: FormulateInputBox,
|
||||
FormulateInputText: FormulateInputText,
|
||||
FormulateInputFile: FormulateInputFile,
|
||||
FormulateInputGroup: FormulateInputGroup,
|
||||
FormulateInputButton: FormulateInputButton,
|
||||
FormulateInputSelect: FormulateInputSelect,
|
||||
FormulateInputSlider: FormulateInputSlider,
|
||||
FormulateInputTextArea: FormulateInputTextArea
|
||||
FormulateForm: __vue_component__$1,
|
||||
FormulateInput: __vue_component__,
|
||||
FormulateInputErrors: __vue_component__$2,
|
||||
FormulateInputBox: __vue_component__$4,
|
||||
FormulateInputText: __vue_component__$5,
|
||||
FormulateInputFile: __vue_component__$7,
|
||||
FormulateInputGroup: __vue_component__$3,
|
||||
FormulateInputButton: __vue_component__$8,
|
||||
FormulateInputSelect: __vue_component__$9,
|
||||
FormulateInputSlider: __vue_component__$a,
|
||||
FormulateInputTextArea: __vue_component__$b
|
||||
},
|
||||
library: library,
|
||||
rules: rules,
|
||||
@ -3211,6 +3358,7 @@
|
||||
|
||||
var Formulate$1 = new Formulate();
|
||||
|
||||
exports.FileUpload = FileUpload;
|
||||
exports.default = Formulate$1;
|
||||
|
||||
Object.defineProperty(exports, '__esModule', { value: true });
|
||||
|
4
dist/snow.min.css
vendored
4
dist/snow.min.css
vendored
File diff suppressed because one or more lines are too long
@ -1,6 +0,0 @@
|
||||
{
|
||||
"presets": [
|
||||
["env", { "modules": false }],
|
||||
"stage-2"
|
||||
]
|
||||
}
|
4
example/.gitignore
vendored
4
example/.gitignore
vendored
@ -1,4 +0,0 @@
|
||||
.DS_Store
|
||||
node_modules/
|
||||
dist/
|
||||
npm-debug.log
|
@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 Braid LLC
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
@ -1,13 +0,0 @@
|
||||
# Vue Formulate Example
|
||||
|
||||
A simple example of a registration form using `vue-formulate`. The store is
|
||||
set up using a *Root store* configuration (as opposed to using a vuex module).
|
||||
|
||||
### Installation
|
||||
|
||||
To install and run the example:
|
||||
|
||||
```sh
|
||||
npm install
|
||||
npm run dev
|
||||
```
|
@ -1,11 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Vue Formulate Example</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script src="/dist/build.js"></script>
|
||||
</body>
|
||||
</html>
|
8135
example/package-lock.json
generated
8135
example/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,26 +0,0 @@
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"author": "Justin Schroeder <justin@wearebraid.com>",
|
||||
"scripts": {
|
||||
"dev": "cross-env NODE_ENV=development webpack-dev-server --hot",
|
||||
"build": "cross-env NODE_ENV=production webpack -p"
|
||||
},
|
||||
"dependencies": {
|
||||
"vue": "^2.6.9",
|
||||
"vue-formulate": "^0.10.0",
|
||||
"vuex": "^2.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-core": "^6.26.3",
|
||||
"babel-loader": "^7.1.5",
|
||||
"babel-preset-env": "^1.7.0",
|
||||
"babel-preset-stage-2": "^6.24.1",
|
||||
"cross-env": "^3.0.0",
|
||||
"css-loader": "^0.25.0",
|
||||
"file-loader": "^0.9.0",
|
||||
"vue-loader": "^13.7.3",
|
||||
"vue-template-compiler": "^2.6.9",
|
||||
"webpack": "^3.12.0",
|
||||
"webpack-dev-server": "^2.11.3"
|
||||
}
|
||||
}
|
@ -1,196 +0,0 @@
|
||||
<template>
|
||||
<div id="app">
|
||||
<formulate
|
||||
name="registration"
|
||||
class="my-form"
|
||||
v-if="!values"
|
||||
@submit="useFormData"
|
||||
>
|
||||
<formulate-element
|
||||
name="email"
|
||||
type="email"
|
||||
label="Email address"
|
||||
placeholder="you@example.com"
|
||||
validation="required|email"
|
||||
/>
|
||||
<formulate-element
|
||||
name="password_confirmation"
|
||||
type="password"
|
||||
label="Password"
|
||||
placeholder="Choose a password"
|
||||
validation="required"
|
||||
/>
|
||||
<formulate-element
|
||||
name="password"
|
||||
type="password"
|
||||
label="Confirm Password"
|
||||
placeholder="Confirm your password"
|
||||
validation-label="Password"
|
||||
validation="required|confirmed"
|
||||
/>
|
||||
<formulate-element
|
||||
name="animal"
|
||||
label="Spirit Animal"
|
||||
>
|
||||
<custom-input
|
||||
v-model="animal"
|
||||
:options="{eagle: 'Eagle', lion: 'Lion', sloth: 'Sloth'}"
|
||||
/>
|
||||
<small>Example of a custom field using vue-formulate.</small>
|
||||
</formulate-element>
|
||||
<formulate-element
|
||||
type="submit"
|
||||
name="Save"
|
||||
/>
|
||||
</formulate>
|
||||
<code
|
||||
v-else
|
||||
class="my-form my-form--code"
|
||||
v-text="values"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CustomInput from './CustomInput.vue'
|
||||
import {mapModels} from 'vue-formulate'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
'custom-input': CustomInput
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
values: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapModels({
|
||||
animal: 'registration/animal'
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
useFormData (vals) {
|
||||
this.values = JSON.stringify(vals, null, 2)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
html,
|
||||
body {
|
||||
min-height: 100%;
|
||||
}
|
||||
body {
|
||||
background: linear-gradient(to top left, #35495e, #41b883);
|
||||
background-repeat: no-repeat;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
code {
|
||||
white-space: pre;
|
||||
}
|
||||
::-webkit-input-placeholder {
|
||||
color: lightgray;
|
||||
}
|
||||
::-moz-placeholder {
|
||||
color: lightgray;
|
||||
}
|
||||
:-ms-input-placeholder {
|
||||
color: lightgray;
|
||||
}
|
||||
:-moz-placeholder {
|
||||
color: lightgray;
|
||||
}
|
||||
|
||||
.my-form {
|
||||
box-sizing: border-box;
|
||||
box-shadow: 0 0 5px 0 rgba(0, 0, 0, .2);
|
||||
max-width: 500px;
|
||||
background-color: white;
|
||||
border-radius: .25em;
|
||||
padding: 2em;
|
||||
position: absolute;
|
||||
min-width: 500px;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
.formulate-element {
|
||||
font-size: 1em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.formulate-element:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.formulate-errors {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.formulate-errors li {
|
||||
margin: .25em 0;
|
||||
font-size: 14px;
|
||||
color: red;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
font-weight: normal;
|
||||
font-weight: bold;
|
||||
margin-bottom: .5em;
|
||||
}
|
||||
|
||||
input[type="text"],
|
||||
input[type="password"],
|
||||
input[type="email"] {
|
||||
border: 1px solid gainsboro;
|
||||
box-sizing: border-box;
|
||||
font-size: 1em;
|
||||
font-family: -apple-system, BlinkMacSystemFont, sans-serif;
|
||||
background-color: transparent;
|
||||
padding: .5em;
|
||||
width: 100%;
|
||||
}
|
||||
input[type="text"]:focus,
|
||||
input[type="password"]:focus,
|
||||
input[type="email"]:focus {
|
||||
outline: 0;
|
||||
border-color: #41b883;
|
||||
}
|
||||
|
||||
button {
|
||||
box-sizing: border-box;
|
||||
border: 1px solid #41b883;
|
||||
border-radius: 2em;
|
||||
background-color: #41b883;
|
||||
color: white;
|
||||
font-size: 1em;
|
||||
width: 100%;
|
||||
padding: .75em;
|
||||
text-transform: uppercase;
|
||||
text-align: center;
|
||||
min-width: 100%;
|
||||
}
|
||||
|
||||
button:focus {
|
||||
outline: 0;
|
||||
border-color: #35495e;
|
||||
}
|
||||
|
||||
small {
|
||||
color: grey;
|
||||
display: block;
|
||||
font-size: .8em;
|
||||
margin: .5em 0;
|
||||
}
|
||||
</style>
|
@ -1,63 +0,0 @@
|
||||
<template>
|
||||
<ul class="custom-input pill">
|
||||
<li
|
||||
v-for="(option, val) in options"
|
||||
:key="val"
|
||||
v-text="option"
|
||||
:data-selected="val === value"
|
||||
@click="$emit('input', val)"
|
||||
/>
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
options: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
value: {
|
||||
type: [String, Number, Boolean],
|
||||
default: false
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.pill {
|
||||
display: flex;
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.pill li {
|
||||
flex-grow: 1;
|
||||
padding: .75em 1em;
|
||||
text-align: center;
|
||||
border: 1px solid #41b883;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.pill li:hover {
|
||||
background-color: rgba(65, 184, 131, .15);
|
||||
}
|
||||
|
||||
.pill li[data-selected] {
|
||||
color: white;
|
||||
background-color: #41b883;
|
||||
}
|
||||
|
||||
.pill li:first-child {
|
||||
border-radius: .5em 0 0 .5em;
|
||||
border-right: 0;
|
||||
}
|
||||
|
||||
.pill li:last-child {
|
||||
border-radius: 0 .5em .5em 0;
|
||||
border-left: 0;
|
||||
}
|
||||
</style>
|
@ -1,12 +0,0 @@
|
||||
import Vue from 'vue'
|
||||
import App from './App.vue'
|
||||
import store from '../store'
|
||||
import formulate from 'vue-formulate'
|
||||
|
||||
Vue.use(formulate)
|
||||
|
||||
window.example = new Vue({
|
||||
el: '#app',
|
||||
store,
|
||||
render: h => h(App)
|
||||
})
|
@ -1,26 +0,0 @@
|
||||
import Vue from 'vue'
|
||||
import Vuex from 'vuex'
|
||||
import {formulateState, formulateGetters, formulateMutations} from 'vue-formulate'
|
||||
|
||||
Vue.use(Vuex)
|
||||
|
||||
const state = {
|
||||
...formulateState()()
|
||||
}
|
||||
|
||||
const getters = {
|
||||
...formulateGetters()
|
||||
}
|
||||
|
||||
const mutations = {
|
||||
...formulateMutations()
|
||||
}
|
||||
|
||||
const actions = {}
|
||||
|
||||
export default new Vuex.Store({
|
||||
state,
|
||||
getters,
|
||||
mutations,
|
||||
actions
|
||||
})
|
@ -1,67 +0,0 @@
|
||||
var path = require('path')
|
||||
var webpack = require('webpack')
|
||||
|
||||
module.exports = {
|
||||
entry: './src/main.js',
|
||||
output: {
|
||||
path: path.resolve(__dirname, './dist'),
|
||||
publicPath: '/dist/',
|
||||
filename: 'build.js'
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.vue$/,
|
||||
loader: 'vue-loader',
|
||||
options: {
|
||||
loaders: {
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
test: /\.js$/,
|
||||
loader: 'babel-loader',
|
||||
exclude: /node_modules/
|
||||
},
|
||||
{
|
||||
test: /\.(png|jpg|gif|svg)$/,
|
||||
loader: 'file-loader',
|
||||
options: {
|
||||
name: '[name].[ext]?[hash]'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
'vue$': 'vue/dist/vue.esm.js'
|
||||
},
|
||||
symlinks: false
|
||||
},
|
||||
devServer: {
|
||||
historyApiFallback: true,
|
||||
noInfo: true
|
||||
},
|
||||
performance: {
|
||||
hints: false
|
||||
}
|
||||
}
|
||||
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
module.exports.plugins = (module.exports.plugins || []).concat([
|
||||
new webpack.DefinePlugin({
|
||||
'process.env': {
|
||||
NODE_ENV: '"production"'
|
||||
}
|
||||
}),
|
||||
new webpack.optimize.UglifyJsPlugin({
|
||||
sourceMap: true,
|
||||
compress: {
|
||||
warnings: false
|
||||
}
|
||||
}),
|
||||
new webpack.LoaderOptionsPlugin({
|
||||
minimize: true
|
||||
})
|
||||
])
|
||||
}
|
3296
package-lock.json
generated
3296
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
33
package.json
33
package.json
@ -43,44 +43,45 @@
|
||||
},
|
||||
"homepage": "https://www.vueformulate.com",
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.6.4",
|
||||
"@babel/plugin-transform-modules-commonjs": "^7.6.0",
|
||||
"@babel/preset-env": "^7.6.3",
|
||||
"@vue/component-compiler-utils": "^3.0.0",
|
||||
"@vue/test-utils": "1.0.0-beta.29",
|
||||
"autoprefixer": "^9.7.0",
|
||||
"@babel/core": "^7.8.4",
|
||||
"@babel/plugin-transform-modules-commonjs": "^7.8.3",
|
||||
"@babel/preset-env": "^7.8.4",
|
||||
"@vue/component-compiler-utils": "^3.1.1",
|
||||
"@vue/test-utils": "^1.0.0-beta.31",
|
||||
"autoprefixer": "^9.7.4",
|
||||
"babel-core": "^7.0.0-bridge.0",
|
||||
"babel-eslint": "^10.0.1",
|
||||
"babel-jest": "^24.9.0",
|
||||
"cssnano": "^4.1.10",
|
||||
"eslint": "^5.16.0",
|
||||
"eslint-config-standard": "^12.0.0",
|
||||
"eslint-plugin-import": "^2.16.0",
|
||||
"eslint-plugin-import": "^2.20.1",
|
||||
"eslint-plugin-node": "^8.0.1",
|
||||
"eslint-plugin-promise": "^4.1.1",
|
||||
"eslint-plugin-standard": "^4.0.0",
|
||||
"eslint-plugin-vue": "^5.2.3",
|
||||
"flush-promises": "^1.0.2",
|
||||
"jest": "^24.9.0",
|
||||
"jest-vue-preprocessor": "^1.7.0",
|
||||
"node-sass": "^4.13.0",
|
||||
"postcss": "^7.0.21",
|
||||
"jest-vue-preprocessor": "^1.7.1",
|
||||
"node-sass": "^4.13.1",
|
||||
"postcss": "^7.0.27",
|
||||
"postcss-cli": "^6.1.3",
|
||||
"rollup": "^1.26.0",
|
||||
"rollup": "^1.31.1",
|
||||
"rollup-plugin-buble": "^0.19.8",
|
||||
"rollup-plugin-commonjs": "^10.1.0",
|
||||
"rollup-plugin-vue": "^5.1.1",
|
||||
"typescript": "^3.6.4",
|
||||
"vue": "^2.6.10",
|
||||
"rollup-plugin-vue": "^5.1.6",
|
||||
"typescript": "^3.8.2",
|
||||
"vue": "^2.6.11",
|
||||
"vue-jest": "^3.0.5",
|
||||
"vue-runtime-helpers": "^1.1.2",
|
||||
"vue-template-compiler": "^2.6.10",
|
||||
"vue-template-compiler": "^2.6.11",
|
||||
"vue-template-es2015-compiler": "^1.9.1",
|
||||
"watch": "^1.0.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"clone-deep": "^4.0.1",
|
||||
"is-plain-object": "^3.0.0",
|
||||
"is-url": "^1.2.4",
|
||||
"nanoid": "^2.1.6"
|
||||
"nanoid": "^2.1.11"
|
||||
}
|
||||
}
|
||||
|
@ -141,7 +141,6 @@ class FileUpload {
|
||||
*/
|
||||
loadPreviews () {
|
||||
this.files.map(file => {
|
||||
console.log(file.type)
|
||||
if (!file.previewData && window && window.FileReader && /^image\//.test(file.file.type)) {
|
||||
const reader = new FileReader()
|
||||
reader.onload = e => Object.assign(file, { previewData: e.target.result })
|
||||
@ -163,6 +162,11 @@ class FileUpload {
|
||||
getFiles () {
|
||||
return this.files
|
||||
}
|
||||
|
||||
toString () {
|
||||
const descriptor = this.files.length ? this.files.length + ' files' : 'empty'
|
||||
return `FileUpload(${descriptor})`
|
||||
}
|
||||
}
|
||||
|
||||
export default FileUpload
|
||||
|
44
src/FormSubmission.js
Normal file
44
src/FormSubmission.js
Normal file
@ -0,0 +1,44 @@
|
||||
import { cloneDeep } from './libs/utils'
|
||||
import FileUpload from './FileUpload'
|
||||
|
||||
export default class FormSubmission {
|
||||
/**
|
||||
* Initialize a formulate form.
|
||||
* @param {vm} form an instance of FormulateForm
|
||||
*/
|
||||
constructor (form) {
|
||||
this.form = form
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the form has any validation errors.
|
||||
*
|
||||
* @return {Promise} resolves a boolean
|
||||
*/
|
||||
hasValidationErrors () {
|
||||
return this.form.hasValidationErrors()
|
||||
}
|
||||
|
||||
/**
|
||||
* Asynchronously generate the values payload of this form.
|
||||
* @return {Promise} resolves to json
|
||||
*/
|
||||
values () {
|
||||
return new Promise((resolve, reject) => {
|
||||
const pending = []
|
||||
const values = cloneDeep(this.form.internalModelProxy)
|
||||
for (const key in values) {
|
||||
if (typeof this.form.internalModelProxy[key] === 'object' && this.form.internalModelProxy[key] instanceof FileUpload) {
|
||||
pending.push(this.form.internalModelProxy[key].upload())
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @todo - how do we get these uploaded path values back into our data?
|
||||
*/
|
||||
Promise.all(pending)
|
||||
// .then(file => file.path)
|
||||
.then(() => resolve(values))
|
||||
.catch(err => reject(err))
|
||||
})
|
||||
}
|
||||
}
|
@ -147,3 +147,6 @@ class Formulate {
|
||||
}
|
||||
|
||||
export default new Formulate()
|
||||
export {
|
||||
FileUpload
|
||||
}
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
<script>
|
||||
import { shallowEqualObjects } from './libs/utils'
|
||||
import FormSubmission from './FormSubmission'
|
||||
|
||||
export default {
|
||||
provide () {
|
||||
@ -95,7 +96,11 @@ export default {
|
||||
formSubmitted () {
|
||||
// perform validation here
|
||||
this.showErrors()
|
||||
this.$emit('submit', this.internalFormModelProxy)
|
||||
const submission = new FormSubmission(this)
|
||||
this.$emit('submit-raw', submission)
|
||||
submission.hasValidationErrors()
|
||||
.then(hasErrors => hasErrors ? false : submission.values())
|
||||
.then(json => this.$emit('submit', json))
|
||||
},
|
||||
showErrors () {
|
||||
for (const fieldName in this.registry) {
|
||||
@ -104,6 +109,15 @@ export default {
|
||||
},
|
||||
getFormValues () {
|
||||
return this.internalFormModelProxy
|
||||
},
|
||||
hasValidationErrors () {
|
||||
const resolvers = []
|
||||
for (const fieldName in this.registry) {
|
||||
if (typeof this.registry[fieldName].hasValidationErrors === 'function') {
|
||||
resolvers.push(this.registry[fieldName].hasValidationErrors())
|
||||
}
|
||||
}
|
||||
return Promise.all(resolvers).then(fields => !!fields.find(hasErrors => hasErrors))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -223,7 +223,11 @@ export default {
|
||||
const rules = parseRules(this.validation, this.$formulate.rules())
|
||||
this.pendingValidation = Promise.all(
|
||||
rules.map(([rule, args]) => {
|
||||
return rule(this.context.model, ...args)
|
||||
return rule({
|
||||
value: this.context.model,
|
||||
getFormValues: this.getFormValues.bind(this),
|
||||
name: this.context.name
|
||||
}, ...args)
|
||||
.then(res => res ? false : this.$formulate.validationMessage(rule.name, {
|
||||
args,
|
||||
name: this.mergedValidationName,
|
||||
|
@ -9,14 +9,14 @@ export default {
|
||||
/**
|
||||
* Rule: the value must be "yes", "on", "1", or true
|
||||
*/
|
||||
accepted: function (value) {
|
||||
accepted: function ({ value }) {
|
||||
return Promise.resolve(['yes', 'on', '1', 1, true, 'true'].includes(value))
|
||||
},
|
||||
|
||||
/**
|
||||
* Rule: checks if a value is after a given date. Defaults to current time
|
||||
*/
|
||||
after: function (value, compare = false) {
|
||||
after: function ({ value }, compare = false) {
|
||||
const timestamp = Date.parse(compare || new Date())
|
||||
const fieldValue = Date.parse(value)
|
||||
return Promise.resolve(isNaN(fieldValue) ? false : (fieldValue > timestamp))
|
||||
@ -25,7 +25,7 @@ export default {
|
||||
/**
|
||||
* Rule: checks if the value is only alpha
|
||||
*/
|
||||
alpha: function (value, set = 'default') {
|
||||
alpha: function ({ value }, set = 'default') {
|
||||
const sets = {
|
||||
default: /^[a-zA-ZÀ-ÖØ-öø-ÿ]+$/,
|
||||
latin: /^[a-zA-Z]+$/
|
||||
@ -37,7 +37,7 @@ export default {
|
||||
/**
|
||||
* Rule: checks if the value is alpha numeric
|
||||
*/
|
||||
alphanumeric: function (value, set = 'default') {
|
||||
alphanumeric: function ({ value }, set = 'default') {
|
||||
const sets = {
|
||||
default: /^[a-zA-Z0-9À-ÖØ-öø-ÿ]+$/,
|
||||
latin: /^[a-zA-Z0-9]+$/
|
||||
@ -49,7 +49,7 @@ export default {
|
||||
/**
|
||||
* Rule: checks if a value is after a given date. Defaults to current time
|
||||
*/
|
||||
before: function (value, compare = false) {
|
||||
before: function ({ value }, compare = false) {
|
||||
const timestamp = Date.parse(compare || new Date())
|
||||
const fieldValue = Date.parse(value)
|
||||
return Promise.resolve(isNaN(fieldValue) ? false : (fieldValue < timestamp))
|
||||
@ -58,7 +58,7 @@ export default {
|
||||
/**
|
||||
* Rule: checks if the value is between two other values
|
||||
*/
|
||||
between: function (value, from = 0, to = 10) {
|
||||
between: function ({ value }, from = 0, to = 10) {
|
||||
return Promise.resolve((() => {
|
||||
if (from === null || to === null || isNaN(from) || isNaN(to)) {
|
||||
return false
|
||||
@ -76,11 +76,26 @@ export default {
|
||||
})())
|
||||
},
|
||||
|
||||
/**
|
||||
* Confirm that the value of one field is the same as another, mostly used
|
||||
* for password confirmations.
|
||||
*/
|
||||
confirm: function ({ value, getFormValues, name }, field) {
|
||||
return Promise.resolve((() => {
|
||||
const formValues = getFormValues()
|
||||
var confirmationFieldName = field
|
||||
if (!confirmationFieldName) {
|
||||
confirmationFieldName = /_confirm$/.test(name) ? name.substr(0, name.length - 8) : `${name}_confirm`
|
||||
}
|
||||
return formValues[confirmationFieldName] === value
|
||||
})())
|
||||
},
|
||||
|
||||
/**
|
||||
* Rule: ensures the value is a date according to Date.parse(), or a format
|
||||
* regex.
|
||||
*/
|
||||
date: function (value, format = false) {
|
||||
date: function ({ value }, format = false) {
|
||||
return Promise.resolve((() => {
|
||||
if (format && typeof format === 'string') {
|
||||
return regexForFormat(format).test(value)
|
||||
@ -92,7 +107,7 @@ export default {
|
||||
/**
|
||||
* Rule: tests
|
||||
*/
|
||||
email: function (value) {
|
||||
email: function ({ value }) {
|
||||
// eslint-disable-next-line
|
||||
const isEmail = /^(([^<>()\[\]\.,;:\s@\"]+(\.[^<>()\[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i
|
||||
return Promise.resolve(isEmail.test(value))
|
||||
@ -101,7 +116,7 @@ export default {
|
||||
/**
|
||||
* Rule: Value is in an array (stack).
|
||||
*/
|
||||
in: function (value, ...stack) {
|
||||
in: function ({ value }, ...stack) {
|
||||
return Promise.resolve(stack.find(item => {
|
||||
if (typeof item === 'object') {
|
||||
return shallowEqualObjects(item, value)
|
||||
@ -113,7 +128,7 @@ export default {
|
||||
/**
|
||||
* Rule: Match the value against a (stack) of patterns or strings
|
||||
*/
|
||||
matches: function (value, ...stack) {
|
||||
matches: function ({ value }, ...stack) {
|
||||
return Promise.resolve(!!stack.find(pattern => {
|
||||
if (pattern instanceof RegExp) {
|
||||
return pattern.test(value)
|
||||
@ -125,7 +140,7 @@ export default {
|
||||
/**
|
||||
* Check the maximum value of a particular.
|
||||
*/
|
||||
max: function (value, minimum = 10, force) {
|
||||
max: function ({ value }, minimum = 10, force) {
|
||||
return Promise.resolve((() => {
|
||||
if (Array.isArray(value)) {
|
||||
minimum = !isNaN(minimum) ? Number(minimum) : minimum
|
||||
@ -146,10 +161,10 @@ export default {
|
||||
/**
|
||||
* Check the file type is correct.
|
||||
*/
|
||||
mime: function (files, ...types) {
|
||||
mime: function ({ value }, ...types) {
|
||||
return Promise.resolve((() => {
|
||||
if (files instanceof FileUpload) {
|
||||
const fileList = files.getFileList()
|
||||
if (value instanceof FileUpload) {
|
||||
const fileList = value.getFileList()
|
||||
for (let i = 0; i < fileList.length; i++) {
|
||||
const file = fileList[i]
|
||||
if (!types.includes(file.type)) {
|
||||
@ -164,7 +179,7 @@ export default {
|
||||
/**
|
||||
* Check the minimum value of a particular.
|
||||
*/
|
||||
min: function (value, minimum = 1, force) {
|
||||
min: function ({ value }, minimum = 1, force) {
|
||||
return Promise.resolve((() => {
|
||||
if (Array.isArray(value)) {
|
||||
minimum = !isNaN(minimum) ? Number(minimum) : minimum
|
||||
@ -185,7 +200,7 @@ export default {
|
||||
/**
|
||||
* Rule: Value is not in stack.
|
||||
*/
|
||||
not: function (value, ...stack) {
|
||||
not: function ({ value }, ...stack) {
|
||||
return Promise.resolve(stack.find(item => {
|
||||
if (typeof item === 'object') {
|
||||
return shallowEqualObjects(item, value)
|
||||
@ -197,14 +212,14 @@ export default {
|
||||
/**
|
||||
* Rule: checks if the value is only alpha numeric
|
||||
*/
|
||||
number: function (value) {
|
||||
number: function ({ value }) {
|
||||
return Promise.resolve(!isNaN(value))
|
||||
},
|
||||
|
||||
/**
|
||||
* Rule: must be a value
|
||||
*/
|
||||
required: function (value, isRequired = true) {
|
||||
required: function ({ value }, isRequired = true) {
|
||||
return Promise.resolve((() => {
|
||||
if (!isRequired || ['no', 'false'].includes(isRequired)) {
|
||||
return true
|
||||
@ -225,7 +240,7 @@ export default {
|
||||
/**
|
||||
* Rule: checks if a string is a valid url
|
||||
*/
|
||||
url: function (value) {
|
||||
url: function ({ value }) {
|
||||
return Promise.resolve(isUrl(value))
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
import FileUpload from '../FileUpload'
|
||||
|
||||
/**
|
||||
* Function to map over an object.
|
||||
* @param {Object} obj An object to map over
|
||||
@ -172,3 +174,39 @@ export function regexForFormat (format) {
|
||||
return regex.replace(format, formats[format])
|
||||
}, escaped))
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if
|
||||
* @param {mixed} data
|
||||
*/
|
||||
export function isValueType (data) {
|
||||
switch (typeof data) {
|
||||
case 'symbol':
|
||||
case 'number':
|
||||
case 'string':
|
||||
case 'boolean':
|
||||
case 'undefined':
|
||||
return true
|
||||
default:
|
||||
if (data === null) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple (somewhat non-comprehensive) cloneDeep function, valid for our use
|
||||
* case of needing to unbind reactive watchers.
|
||||
*/
|
||||
export function cloneDeep (obj) {
|
||||
const newObj = {}
|
||||
for (const key in obj) {
|
||||
if (obj[key] instanceof FileUpload || isValueType(obj[key])) {
|
||||
newObj[key] = obj
|
||||
} else {
|
||||
newObj[key] = cloneDeep(obj[key])
|
||||
}
|
||||
}
|
||||
return newObj
|
||||
}
|
||||
|
@ -55,6 +55,13 @@ export default {
|
||||
return `${s(name)} must be between ${args[0]} and ${args[1]} characters long.`
|
||||
},
|
||||
|
||||
/**
|
||||
* The confirmation field does not match
|
||||
*/
|
||||
confirm: function ({ name, args }) {
|
||||
return `${s(name)} does not match.`
|
||||
},
|
||||
|
||||
/**
|
||||
* Is not a valid date.
|
||||
*/
|
||||
|
@ -2,6 +2,7 @@ import Vue from 'vue'
|
||||
import { mount, shallowMount } from '@vue/test-utils'
|
||||
import flushPromises from 'flush-promises'
|
||||
import Formulate from '../src/Formulate.js'
|
||||
import FormSubmission from '../src/FormSubmission.js'
|
||||
import FormulateForm from '@/FormulateForm.vue'
|
||||
import FormulateInput from '@/FormulateInput.vue'
|
||||
|
||||
@ -86,30 +87,34 @@ describe('FormulateForm', () => {
|
||||
* @todo in vue-test-utils version 1.0.0-beta.29 has some bugs related to
|
||||
* synchronous updating. Some details are here:
|
||||
*
|
||||
* @update this test was re-implemented in version 1.0.0-beta.31 and seems to
|
||||
* be workign now with flushPromises(). Leaving these docs here for now.
|
||||
*
|
||||
* https://github.com/vuejs/vue-test-utils/issues/1130
|
||||
*
|
||||
* This test is being commented out until there is a resolution on this issue,
|
||||
* and instead being replaced with a mock call.
|
||||
*/
|
||||
|
||||
// it('updates initial form values when input contains a populated v-model', () => {
|
||||
// const wrapper = mount({
|
||||
// data () {
|
||||
// return {
|
||||
// formValues: {
|
||||
// testinput: '',
|
||||
// },
|
||||
// fieldValue: '123'
|
||||
// }
|
||||
// },
|
||||
// template: `
|
||||
// <FormulateForm v-model="formValues">
|
||||
// <FormulateInput type="text" name="testinput" v-model="fieldValue" />
|
||||
// </FormulateForm>
|
||||
// `
|
||||
// })
|
||||
// expect(wrapper.vm.formValues).toEqual({ testinput: '123' })
|
||||
// })
|
||||
it('updates initial form values when input contains a populated v-model', async () => {
|
||||
const wrapper = mount({
|
||||
data () {
|
||||
return {
|
||||
formValues: {
|
||||
testinput: '',
|
||||
},
|
||||
fieldValue: '123'
|
||||
}
|
||||
},
|
||||
template: `
|
||||
<FormulateForm v-model="formValues">
|
||||
<FormulateInput type="text" name="testinput" v-model="fieldValue" />
|
||||
</FormulateForm>
|
||||
`
|
||||
})
|
||||
await flushPromises()
|
||||
expect(wrapper.vm.formValues).toEqual({ testinput: '123' })
|
||||
})
|
||||
|
||||
// ===========================================================================
|
||||
|
||||
@ -125,4 +130,24 @@ describe('FormulateForm', () => {
|
||||
})
|
||||
expect(wrapper.emitted().input[wrapper.emitted().input.length - 1]).toEqual([{ testinput: 'override-data' }])
|
||||
})
|
||||
|
||||
|
||||
it('it emits an instance of FormSubmission', async () => {
|
||||
const wrapper = mount(FormulateForm, {
|
||||
slots: { default: '<FormulateInput type="text" formulate-value="123" name="testinput" />' }
|
||||
})
|
||||
wrapper.find('form').trigger('submit')
|
||||
await flushPromises()
|
||||
expect(wrapper.emitted('submit-raw')[0][0]).toBeInstanceOf(FormSubmission)
|
||||
})
|
||||
|
||||
it('it resolves hasValidationErrors to true', async () => {
|
||||
const wrapper = mount(FormulateForm, {
|
||||
slots: { default: '<FormulateInput type="text" validation="required" name="testinput" />' }
|
||||
})
|
||||
wrapper.find('form').trigger('submit')
|
||||
await flushPromises()
|
||||
const submission = wrapper.emitted('submit-raw')[0][0]
|
||||
expect(await submission.hasValidationErrors()).toBe(true)
|
||||
})
|
||||
})
|
||||
|
@ -1,4 +1,5 @@
|
||||
import Vue from 'vue'
|
||||
import flushPromises from 'flush-promises'
|
||||
import { mount } from '@vue/test-utils'
|
||||
import Formulate from '../src/Formulate.js'
|
||||
import FormulateInput from '@/FormulateInput.vue'
|
||||
@ -57,21 +58,25 @@ test('type "radio" auto generate ids if not provided', () => {
|
||||
* Test data binding
|
||||
*/
|
||||
|
||||
test('type "checkbox" sets array of values via v-model', () => {
|
||||
test('type "checkbox" sets array of values via v-model', async () => {
|
||||
const wrapper = mount({
|
||||
data () {
|
||||
return {
|
||||
checkboxValues: []
|
||||
checkboxValues: [],
|
||||
options: {foo: 'Foo', bar: 'Bar', fooey: 'Fooey'}
|
||||
}
|
||||
},
|
||||
template: `
|
||||
<div>
|
||||
<FormulateInput type="checkbox" v-model="checkboxValues" :options="{foo: 'Foo', bar: 'Bar', fooey: 'Fooey'}" />
|
||||
<FormulateInput type="checkbox" v-model="checkboxValues" :options="options" />
|
||||
</div>
|
||||
`
|
||||
})
|
||||
const fooInputs = wrapper.findAll('input[value^="foo"]')
|
||||
expect(fooInputs.length).toBe(2)
|
||||
fooInputs.setChecked(true)
|
||||
fooInputs.at(0).setChecked()
|
||||
await flushPromises()
|
||||
fooInputs.at(1).setChecked()
|
||||
await flushPromises()
|
||||
expect(wrapper.vm.checkboxValues).toEqual(['foo', 'fooey'])
|
||||
})
|
||||
|
@ -1,4 +1,5 @@
|
||||
import Vue from 'vue'
|
||||
import flushPromises from 'flush-promises'
|
||||
import { mount } from '@vue/test-utils'
|
||||
import Formulate from '../src/Formulate.js'
|
||||
import FormulateInput from '@/FormulateInput.vue'
|
||||
@ -113,7 +114,7 @@ describe('FormulateInputText', () => {
|
||||
})
|
||||
|
||||
|
||||
it('doesn’t re-context itself if there were no changes', () => {
|
||||
it('doesn’t re-context itself if there were no changes', async () => {
|
||||
const wrapper = mount({
|
||||
data () {
|
||||
return {
|
||||
@ -128,9 +129,11 @@ describe('FormulateInputText', () => {
|
||||
</div>
|
||||
`
|
||||
})
|
||||
await flushPromises()
|
||||
const firstContext = wrapper.find({ref: "first"}).vm.context
|
||||
const secondContext = wrapper.find({ref: "second"}).vm.context
|
||||
wrapper.find('input').setValue('new value')
|
||||
await flushPromises()
|
||||
expect(firstContext).toBeTruthy()
|
||||
expect(wrapper.vm.valueA === 'new value').toBe(true)
|
||||
expect(wrapper.vm.valueB === 'second value').toBe(true)
|
||||
@ -205,12 +208,13 @@ describe('FormulateInputText', () => {
|
||||
expect(wrapper.findAll('.formulate-input-error').length).toBe(1)
|
||||
})
|
||||
|
||||
it('shows errors on blur with error-behavior blur', () => {
|
||||
it('shows errors on blur with error-behavior blur', async () => {
|
||||
const wrapper = mount(FormulateInput, { propsData: { type: 'text', errorBehavior: 'blur', errors: ['Bad input'] } })
|
||||
expect(wrapper.find('[data-has-errors]').exists()).toBe(true)
|
||||
expect(wrapper.find('[data-is-showing-errors]').exists()).toBe(false)
|
||||
expect(wrapper.findAll('.formulate-input-errors').exists()).toBe(false)
|
||||
wrapper.find('input').trigger('blur')
|
||||
await flushPromises()
|
||||
expect(wrapper.find('[data-is-showing-errors]').exists()).toBe(true)
|
||||
expect(wrapper.findAll('.formulate-input-errors').exists()).toBe(true)
|
||||
expect(wrapper.findAll('.formulate-input-error').length).toBe(1)
|
||||
|
@ -5,25 +5,25 @@ import FileUpload from '../src/FileUpload'
|
||||
* Required rule
|
||||
*/
|
||||
describe('required', () => {
|
||||
it('fails on empty string', async () => expect(await rules.required('')).toBe(false))
|
||||
it('fails on empty string', async () => expect(await rules.required({ value: '' })).toBe(false))
|
||||
|
||||
it('fails on empty array', async () => expect(await rules.required([])).toBe(false))
|
||||
it('fails on empty array', async () => expect(await rules.required({ value: [] })).toBe(false))
|
||||
|
||||
it('fails on empty object', async () => expect(await rules.required({})).toBe(false))
|
||||
it('fails on empty object', async () => expect(await rules.required({ value: {} })).toBe(false))
|
||||
|
||||
it('fails on null', async () => expect(await rules.required(null)).toBe(false))
|
||||
it('fails on null', async () => expect(await rules.required({ value: null })).toBe(false))
|
||||
|
||||
it('passes with the number zero', async () => expect(await rules.required(0)).toBe(true))
|
||||
it('passes with the number zero', async () => expect(await rules.required({ value: 0 })).toBe(true))
|
||||
|
||||
it('passes with the boolean false', async () => expect(await rules.required(false)).toBe(true))
|
||||
it('passes with the boolean false', async () => expect(await rules.required({ value: false })).toBe(true))
|
||||
|
||||
it('passes with a non empty array', async () => expect(await rules.required(['123'])).toBe(true))
|
||||
it('passes with a non empty array', async () => expect(await rules.required({ value: ['123'] })).toBe(true))
|
||||
|
||||
it('passes with a non empty object', async () => expect(await rules.required({a: 'b'})).toBe(true))
|
||||
it('passes with a non empty object', async () => expect(await rules.required({ value: {a: 'b'} })).toBe(true))
|
||||
|
||||
it('passes with empty value if second argument is false', async () => expect(await rules.required('', false)).toBe(true))
|
||||
it('passes with empty value if second argument is false', async () => expect(await rules.required({ value: '' }, false)).toBe(true))
|
||||
|
||||
it('passes with empty value if second argument is false string', async () => expect(await rules.required('', 'false')).toBe(true))
|
||||
it('passes with empty value if second argument is false string', async () => expect(await rules.required({ value: '' }, 'false')).toBe(true))
|
||||
})
|
||||
|
||||
|
||||
@ -32,27 +32,27 @@ describe('required', () => {
|
||||
*/
|
||||
describe('in', () => {
|
||||
it('fails when not in stack', async () => {
|
||||
expect(await rules.in('third', 'first', 'second')).toBe(false)
|
||||
expect(await rules.in({ value: 'third' }, 'first', 'second')).toBe(false)
|
||||
})
|
||||
|
||||
it('fails when case sensitive mismatch is in stack', async () => {
|
||||
expect(await rules.in('third', 'first', 'second', 'Third')).toBe(false)
|
||||
expect(await rules.in({ value: 'third' }, 'first', 'second', 'Third')).toBe(false)
|
||||
})
|
||||
|
||||
it('fails comparing dissimilar objects', async () => {
|
||||
expect(await rules.in({f: 'abc'}, {a: 'cdf'}, {b: 'abc'})).toBe(false)
|
||||
expect(await rules.in({ value: {f: 'abc'} }, {a: 'cdf'}, {b: 'abc'})).toBe(false)
|
||||
})
|
||||
|
||||
it('passes when case sensitive match is in stack', async () => {
|
||||
expect(await rules.in('third', 'first', 'second', 'third')).toBe(true)
|
||||
expect(await rules.in({ value: 'third' }, 'first', 'second', 'third')).toBe(true)
|
||||
})
|
||||
|
||||
it('passes a shallow array compare', async () => {
|
||||
expect(await rules.in(['abc'], ['cdf'], ['abc'])).toBe(true)
|
||||
expect(await rules.in({ value: ['abc'] }, ['cdf'], ['abc'])).toBe(true)
|
||||
})
|
||||
|
||||
it('passes a shallow object compare', async () => {
|
||||
expect(await rules.in({f: 'abc'}, {a: 'cdf'}, {f: 'abc'},)).toBe(true)
|
||||
expect(await rules.in({ value: {f: 'abc'} }, {a: 'cdf'}, {f: 'abc'},)).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
@ -61,23 +61,23 @@ describe('in', () => {
|
||||
*/
|
||||
describe('matches', () => {
|
||||
it('simple strings fail if they aren’t equal', async () => {
|
||||
expect(await rules.matches('third', 'first')).toBe(false)
|
||||
expect(await rules.matches({ value: 'third' }, 'first')).toBe(false)
|
||||
})
|
||||
|
||||
it('fails on non matching regex', async () => {
|
||||
expect(await rules.matches('third', /^thirds/)).toBe(false)
|
||||
expect(await rules.matches({ value: 'third' }, /^thirds/)).toBe(false)
|
||||
})
|
||||
|
||||
it('passes if simple strings match', async () => {
|
||||
expect(await rules.matches('second', 'third', 'second')).toBe(true)
|
||||
expect(await rules.matches({ value: 'second' }, 'third', 'second')).toBe(true)
|
||||
})
|
||||
|
||||
it('passes on matching regex', async () => {
|
||||
expect(await rules.matches('third', /^third/)).toBe(true)
|
||||
expect(await rules.matches({ value: 'third' }, /^third/)).toBe(true)
|
||||
})
|
||||
|
||||
it('passes on matching mixed regex and string', async () => {
|
||||
expect(await rules.matches('first-fourth', 'second', /^third/, /fourth$/)).toBe(true)
|
||||
expect(await rules.matches({ value: 'first-fourth' }, 'second', /^third/, /fourth$/)).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
@ -85,19 +85,19 @@ describe('matches', () => {
|
||||
* Accepted rule
|
||||
*/
|
||||
describe('accepted', () => {
|
||||
it('passes with true', async () => expect(await rules.accepted('yes')).toBe(true))
|
||||
it('passes with true', async () => expect(await rules.accepted({ value: 'yes' })).toBe(true))
|
||||
|
||||
it('passes with on', async () => expect(await rules.accepted('on')).toBe(true))
|
||||
it('passes with on', async () => expect(await rules.accepted({ value: 'on' })).toBe(true))
|
||||
|
||||
it('passes with 1', async () => expect(await rules.accepted('1')).toBe(true))
|
||||
it('passes with 1', async () => expect(await rules.accepted({ value: '1' })).toBe(true))
|
||||
|
||||
it('passes with number 1', async () => expect(await rules.accepted(1)).toBe(true))
|
||||
it('passes with number 1', async () => expect(await rules.accepted({ value: 1 })).toBe(true))
|
||||
|
||||
it('passes with boolean true', async () => expect(await rules.accepted(true)).toBe(true))
|
||||
it('passes with boolean true', async () => expect(await rules.accepted({ value: true })).toBe(true))
|
||||
|
||||
it('fail with boolean false', async () => expect(await rules.accepted(false)).toBe(false))
|
||||
it('fail with boolean false', async () => expect(await rules.accepted({ value: false })).toBe(false))
|
||||
|
||||
it('fail with "false"', async () => expect(await rules.accepted('false')).toBe(false))
|
||||
it('fail with "false"', async () => expect(await rules.accepted({ value: 'false' })).toBe(false))
|
||||
})
|
||||
|
||||
|
||||
@ -108,36 +108,36 @@ describe('accepted', () => {
|
||||
* well tested: https://github.com/segmentio/is-url/blob/master/test/index.js
|
||||
*/
|
||||
describe('url', () => {
|
||||
it('passes with http://google.com', async () => expect(await rules.url('http://google.com')).toBe(true))
|
||||
it('passes with http://google.com', async () => expect(await rules.url({ value: 'http://google.com' })).toBe(true))
|
||||
|
||||
it('fails with google.com', async () => expect(await rules.url('google.com')).toBe(false))
|
||||
it('fails with google.com', async () => expect(await rules.url({ value: 'google.com' })).toBe(false))
|
||||
})
|
||||
|
||||
/**
|
||||
* Determines if the string is a date
|
||||
*/
|
||||
describe('date', () => {
|
||||
it('passes with month day year', async () => expect(await rules.date('December 17, 2020')).toBe(true))
|
||||
it('passes with month day year', async () => expect(await rules.date({ value: 'December 17, 2020' })).toBe(true))
|
||||
|
||||
it('passes with month day', async () => expect(await rules.date('December 17')).toBe(true))
|
||||
it('passes with month day', async () => expect(await rules.date({ value: 'December 17' })).toBe(true))
|
||||
|
||||
it('passes with short month day', async () => expect(await rules.date('Dec 17')).toBe(true))
|
||||
it('passes with short month day', async () => expect(await rules.date({ value: 'Dec 17' })).toBe(true))
|
||||
|
||||
it('passes with short month day', async () => expect(await rules.date('Dec 17 12:34:15')).toBe(true))
|
||||
it('passes with short month day', async () => expect(await rules.date({ value: 'Dec 17 12:34:15' })).toBe(true))
|
||||
|
||||
it('passes with out of bounds number', async () => expect(await rules.date('January 77')).toBe(true))
|
||||
it('passes with out of bounds number', async () => expect(await rules.date({ value: 'January 77' })).toBe(true))
|
||||
|
||||
it('passes with only month', async () => expect(await rules.date('January')).toBe(false))
|
||||
it('passes with only month', async () => expect(await rules.date({ value: 'January' })).toBe(false))
|
||||
|
||||
it('passes with valid date format', async () => expect(await rules.date('12/17/1987', 'MM/DD/YYYY')).toBe(true))
|
||||
it('passes with valid date format', async () => expect(await rules.date({ value: '12/17/1987' }, 'MM/DD/YYYY')).toBe(true))
|
||||
|
||||
it('fails with simple number and date format', async () => expect(await rules.date('1234', 'MM/DD/YYYY')).toBe(false))
|
||||
it('fails with simple number and date format', async () => expect(await rules.date({ value: '1234' }, 'MM/DD/YYYY')).toBe(false))
|
||||
|
||||
it('fails with only day of week', async () => expect(await rules.date('saturday')).toBe(false))
|
||||
it('fails with only day of week', async () => expect(await rules.date({ value: 'saturday' })).toBe(false))
|
||||
|
||||
it('fails with random string', async () => expect(await rules.date('Pepsi 17')).toBe(false))
|
||||
it('fails with random string', async () => expect(await rules.date({ value: 'Pepsi 17' })).toBe(false))
|
||||
|
||||
it('fails with random number', async () => expect(await rules.date('1872301237')).toBe(false))
|
||||
it('fails with random number', async () => expect(await rules.date({ value: '1872301237' })).toBe(false))
|
||||
|
||||
})
|
||||
|
||||
@ -152,17 +152,17 @@ describe('after', () => {
|
||||
tomorrow.setDate(today.getDate() + 1)
|
||||
yesterday.setDate(today.getDate() - 1)
|
||||
|
||||
it('passes with tomorrow’s date object', async () => expect(await rules.after(tomorrow)).toBe(true))
|
||||
it('passes with tomorrow’s date object', async () => expect(await rules.after({ value: tomorrow })).toBe(true))
|
||||
|
||||
it('passes with future date', async () => expect(await rules.after('January 15, 2999')).toBe(true))
|
||||
it('passes with future date', async () => expect(await rules.after({ value: 'January 15, 2999' })).toBe(true))
|
||||
|
||||
it('passes with long past date', async () => expect(await rules.after(yesterday, 'Jan 15, 2000')).toBe(true))
|
||||
it('passes with long past date', async () => expect(await rules.after({ value: yesterday }, 'Jan 15, 2000')).toBe(true))
|
||||
|
||||
it('fails with yesterday’s date', async () => expect(await rules.after(yesterday)).toBe(false))
|
||||
it('fails with yesterday’s date', async () => expect(await rules.after({ value: yesterday })).toBe(false))
|
||||
|
||||
it('fails with old date string', async () => expect(await rules.after('January, 2000')).toBe(false))
|
||||
it('fails with old date string', async () => expect(await rules.after({ value: 'January, 2000' })).toBe(false))
|
||||
|
||||
it('fails with invalid value', async () => expect(await rules.after('')).toBe(false))
|
||||
it('fails with invalid value', async () => expect(await rules.after({ value: '' })).toBe(false))
|
||||
})
|
||||
|
||||
/**
|
||||
@ -175,17 +175,17 @@ describe('before', () => {
|
||||
tomorrow.setDate(today.getDate() + 1)
|
||||
yesterday.setDate(today.getDate() - 1)
|
||||
|
||||
it('fails with tomorrow’s date object', async () => expect(await rules.before(tomorrow)).toBe(false))
|
||||
it('fails with tomorrow’s date object', async () => expect(await rules.before({ value: tomorrow })).toBe(false))
|
||||
|
||||
it('fails with future date', async () => expect(await rules.before('January 15, 2999')).toBe(false))
|
||||
it('fails with future date', async () => expect(await rules.before({ value: 'January 15, 2999' })).toBe(false))
|
||||
|
||||
it('fails with long past date', async () => expect(await rules.before(yesterday, 'Jan 15, 2000')).toBe(false))
|
||||
it('fails with long past date', async () => expect(await rules.before({ value: yesterday }, 'Jan 15, 2000')).toBe(false))
|
||||
|
||||
it('passes with yesterday’s date', async () => expect(await rules.before(yesterday)).toBe(true))
|
||||
it('passes with yesterday’s date', async () => expect(await rules.before({ value: yesterday })).toBe(true))
|
||||
|
||||
it('passes with old date string', async () => expect(await rules.before('January, 2000')).toBe(true))
|
||||
it('passes with old date string', async () => expect(await rules.before({ value: 'January, 2000' })).toBe(true))
|
||||
|
||||
it('fails with invalid value', async () => expect(await rules.after('')).toBe(false))
|
||||
it('fails with invalid value', async () => expect(await rules.after({ value: '' })).toBe(false))
|
||||
})
|
||||
|
||||
|
||||
@ -193,82 +193,82 @@ describe('before', () => {
|
||||
* Checks if a date is after another date
|
||||
*/
|
||||
describe('alpha', () => {
|
||||
it('passes with simple string', async () => expect(await rules.alpha('abc')).toBe(true))
|
||||
it('passes with simple string', async () => expect(await rules.alpha({ value: 'abc' })).toBe(true))
|
||||
|
||||
it('passes with long string', async () => expect(await rules.alpha('lkashdflaosuihdfaisudgflakjsdbflasidufg')).toBe(true))
|
||||
it('passes with long string', async () => expect(await rules.alpha({ value: 'lkashdflaosuihdfaisudgflakjsdbflasidufg' })).toBe(true))
|
||||
|
||||
it('passes with single character', async () => expect(await rules.alpha('z')).toBe(true))
|
||||
it('passes with single character', async () => expect(await rules.alpha({ value: 'z' })).toBe(true))
|
||||
|
||||
it('passes with accented character', async () => expect(await rules.alpha('jüstin')).toBe(true))
|
||||
it('passes with accented character', async () => expect(await rules.alpha({ value: 'jüstin' })).toBe(true))
|
||||
|
||||
it('passes with lots of accented characters', async () => expect(await rules.alpha('àáâäïíôöÆ')).toBe(true))
|
||||
it('passes with lots of accented characters', async () => expect(await rules.alpha({ value: 'àáâäïíôöÆ' })).toBe(true))
|
||||
|
||||
it('passes with lots of accented characters if invalid set', async () => expect(await rules.alpha('àáâäïíôöÆ', 'russian')).toBe(true))
|
||||
it('passes with lots of accented characters if invalid set', async () => expect(await rules.alpha({ value: 'àáâäïíôöÆ' }, 'russian')).toBe(true))
|
||||
|
||||
it('fails with lots of accented characters if latin', async () => expect(await rules.alpha('àáâäïíôöÆ', 'latin')).toBe(false))
|
||||
it('fails with lots of accented characters if latin', async () => expect(await rules.alpha({ value: 'àáâäïíôöÆ' }, 'latin')).toBe(false))
|
||||
|
||||
it('fails with numbers', async () => expect(await rules.alpha('justin83')).toBe(false))
|
||||
it('fails with numbers', async () => expect(await rules.alpha({ value: 'justin83' })).toBe(false))
|
||||
|
||||
it('fails with symbols', async () => expect(await rules.alpha('-justin')).toBe(false))
|
||||
it('fails with symbols', async () => expect(await rules.alpha({ value: '-justin' })).toBe(false))
|
||||
})
|
||||
|
||||
/**
|
||||
* Checks if a date is after another date
|
||||
*/
|
||||
describe('number', () => {
|
||||
it('passes with simple number string', async () => expect(await rules.number('123')).toBe(true))
|
||||
it('passes with simple number string', async () => expect(await rules.number({ value: '123' })).toBe(true))
|
||||
|
||||
it('passes with simple number', async () => expect(await rules.number(19832461234)).toBe(true))
|
||||
it('passes with simple number', async () => expect(await rules.number({ value: 19832461234 })).toBe(true))
|
||||
|
||||
it('passes with float', async () => expect(await rules.number(198.32464)).toBe(true))
|
||||
it('passes with float', async () => expect(await rules.number({ value: 198.32464 })).toBe(true))
|
||||
|
||||
it('passes with decimal in string', async () => expect(await rules.number('567.23')).toBe(true))
|
||||
it('passes with decimal in string', async () => expect(await rules.number({ value: '567.23' })).toBe(true))
|
||||
|
||||
it('fails with comma in number string', async () => expect(await rules.number('123,456')).toBe(false))
|
||||
it('fails with comma in number string', async () => expect(await rules.number({ value: '123,456' })).toBe(false))
|
||||
|
||||
it('fails with alpha', async () => expect(await rules.number('123sdf')).toBe(false))
|
||||
it('fails with alpha', async () => expect(await rules.number({ value: '123sdf' })).toBe(false))
|
||||
})
|
||||
|
||||
/**
|
||||
* Checks if a date alpha and numeric
|
||||
*/
|
||||
describe('alphanumeric', () => {
|
||||
it('passes with simple string', async () => expect(await rules.alphanumeric('567abc')).toBe(true))
|
||||
it('passes with simple string', async () => expect(await rules.alphanumeric({ value: '567abc' })).toBe(true))
|
||||
|
||||
it('passes with long string', async () => expect(await rules.alphanumeric('lkashdfla234osuihdfaisudgflakjsdbfla567sidufg')).toBe(true))
|
||||
it('passes with long string', async () => expect(await rules.alphanumeric({ value: 'lkashdfla234osuihdfaisudgflakjsdbfla567sidufg' })).toBe(true))
|
||||
|
||||
it('passes with single character', async () => expect(await rules.alphanumeric('z')).toBe(true))
|
||||
it('passes with single character', async () => expect(await rules.alphanumeric({ value: 'z' })).toBe(true))
|
||||
|
||||
it('passes with accented character', async () => expect(await rules.alphanumeric('jüst56in')).toBe(true))
|
||||
it('passes with accented character', async () => expect(await rules.alphanumeric({ value: 'jüst56in' })).toBe(true))
|
||||
|
||||
it('passes with lots of accented characters', async () => expect(await rules.alphanumeric('àáâ7567567äïíôöÆ')).toBe(true))
|
||||
it('passes with lots of accented characters', async () => expect(await rules.alphanumeric({ value: 'àáâ7567567äïíôöÆ' })).toBe(true))
|
||||
|
||||
it('passes with lots of accented characters if invalid set', async () => expect(await rules.alphanumeric('123123àáâäï67íôöÆ', 'russian')).toBe(true))
|
||||
it('passes with lots of accented characters if invalid set', async () => expect(await rules.alphanumeric({ value: '123123àáâäï67íôöÆ' }, 'russian')).toBe(true))
|
||||
|
||||
it('fails with lots of accented characters if latin', async () => expect(await rules.alphanumeric('àáâäï123123íôöÆ', 'latin')).toBe(false))
|
||||
it('fails with lots of accented characters if latin', async () => expect(await rules.alphanumeric({ value: 'àáâäï123123íôöÆ' }, 'latin')).toBe(false))
|
||||
|
||||
it('fails with decimals in', async () => expect(await rules.alphanumeric('abcABC99.123')).toBe(false))
|
||||
it('fails with decimals in', async () => expect(await rules.alphanumeric({ value: 'abcABC99.123' })).toBe(false))
|
||||
})
|
||||
|
||||
/**
|
||||
* Checks if between
|
||||
*/
|
||||
describe('between', () => {
|
||||
it('passes with simple number', async () => expect(await rules.between(5, 0, 10)).toBe(true))
|
||||
it('passes with simple number', async () => expect(await rules.between({ value: 5 }, 0, 10)).toBe(true))
|
||||
|
||||
it('passes with simple number string', async () => expect(await rules.between('5', '0', '10')).toBe(true))
|
||||
it('passes with simple number string', async () => expect(await rules.between({ value: '5' }, '0', '10')).toBe(true))
|
||||
|
||||
it('passes with decimal number string', async () => expect(await rules.between('0.5', '0', '1')).toBe(true))
|
||||
it('passes with decimal number string', async () => expect(await rules.between({ value: '0.5' }, '0', '1')).toBe(true))
|
||||
|
||||
it('passes with string length', async () => expect(await rules.between('abc', 2, 4)).toBe(true))
|
||||
it('passes with string length', async () => expect(await rules.between({ value: 'abc' }, 2, 4)).toBe(true))
|
||||
|
||||
it('fails with string length too long', async () => expect(await rules.between('abcdef', 2, 4)).toBe(false))
|
||||
it('fails with string length too long', async () => expect(await rules.between({ value: 'abcdef' }, 2, 4)).toBe(false))
|
||||
|
||||
it('fails with string length too short', async () => expect(await rules.between('abc', 3, 10)).toBe(false))
|
||||
it('fails with string length too short', async () => expect(await rules.between({ value: 'abc' }, 3, 10)).toBe(false))
|
||||
|
||||
it('fails with number to small', async () => expect(await rules.between(0, 3, 10)).toBe(false))
|
||||
it('fails with number to small', async () => expect(await rules.between({ value: 0 }, 3, 10)).toBe(false))
|
||||
|
||||
it('fails with number to large', async () => expect(await rules.between(15, 3, 10)).toBe(false))
|
||||
it('fails with number to large', async () => expect(await rules.between({ value: 15 }, 3, 10)).toBe(false))
|
||||
})
|
||||
|
||||
/**
|
||||
@ -277,19 +277,19 @@ describe('between', () => {
|
||||
* Note: testing is light, regular expression used is here: http://jsfiddle.net/ghvj4gy9/embedded/result,js/
|
||||
*/
|
||||
describe('email', () => {
|
||||
it('passes normal email', async () => expect(await rules.email('dev+123@wearebraid.com')).toBe(true))
|
||||
it('passes normal email', async () => expect(await rules.email({ value: 'dev+123@wearebraid.com' })).toBe(true))
|
||||
|
||||
it('passes numeric email', async () => expect(await rules.email('12345@google.com')).toBe(true))
|
||||
it('passes numeric email', async () => expect(await rules.email({ value: '12345@google.com' })).toBe(true))
|
||||
|
||||
it('passes unicode email', async () => expect(await rules.email('àlphä@❤️.ly')).toBe(true))
|
||||
it('passes unicode email', async () => expect(await rules.email({ value: 'àlphä@❤️.ly' })).toBe(true))
|
||||
|
||||
it('passes numeric with new tld', async () => expect(await rules.email('12345@google.photography')).toBe(true))
|
||||
it('passes numeric with new tld', async () => expect(await rules.email({ value: '12345@google.photography' })).toBe(true))
|
||||
|
||||
it('fails string without tld', async () => expect(await rules.email('12345@localhost')).toBe(false))
|
||||
it('fails string without tld', async () => expect(await rules.email({ value: '12345@localhost' })).toBe(false))
|
||||
|
||||
it('fails string without tld', async () => expect(await rules.email('12345@localhost')).toBe(false))
|
||||
it('fails string without tld', async () => expect(await rules.email({ value: '12345@localhost' })).toBe(false))
|
||||
|
||||
it('fails string without invalid name', async () => expect(await rules.email('1*(123)2345@localhost')).toBe(false))
|
||||
it('fails string without invalid name', async () => expect(await rules.email({ value: '1*(123)2345@localhost' })).toBe(false))
|
||||
})
|
||||
|
||||
/**
|
||||
@ -300,21 +300,21 @@ describe('mime', () => {
|
||||
const fileUpload = new FileUpload({
|
||||
files: [ { type: 'image/jpeg' } ]
|
||||
})
|
||||
expect(await rules.mime(fileUpload, 'image/png', 'image/jpeg')).toBe(true)
|
||||
expect(await rules.mime({ value: fileUpload }, 'image/png', 'image/jpeg')).toBe(true)
|
||||
})
|
||||
|
||||
it('passes when match is at begining of stack', async () => {
|
||||
const fileUpload = new FileUpload({
|
||||
files: [ { type: 'document/pdf' } ]
|
||||
})
|
||||
expect(await rules.mime(fileUpload, 'document/pdf')).toBe(true)
|
||||
expect(await rules.mime({ value: fileUpload }, 'document/pdf')).toBe(true)
|
||||
})
|
||||
|
||||
it('fails when not in stack', async () => {
|
||||
const fileUpload = new FileUpload({
|
||||
files: [ { type: 'application/json' } ]
|
||||
})
|
||||
expect(await rules.mime(fileUpload, 'image/png', 'image/jpeg')).toBe(false)
|
||||
expect(await rules.mime({ value: fileUpload }, 'image/png', 'image/jpeg')).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
@ -322,27 +322,27 @@ describe('mime', () => {
|
||||
* Minimum.
|
||||
*/
|
||||
describe('min', () => {
|
||||
it('passes when a number string', async () => expect(await rules.min('5', '5')).toBe(true))
|
||||
it('passes when a number string', async () => expect(await rules.min({ value: '5' }, '5')).toBe(true))
|
||||
|
||||
it('passes when a number', async () => expect(await rules.min(6, 5)).toBe(true))
|
||||
it('passes when a number', async () => expect(await rules.min({ value: 6 }, 5)).toBe(true))
|
||||
|
||||
it('passes when a string length', async () => expect(await rules.min('foobar', '6')).toBe(true))
|
||||
it('passes when a string length', async () => expect(await rules.min({ value: 'foobar' }, '6')).toBe(true))
|
||||
|
||||
it('passes when a array length', async () => expect(await rules.min(Array(6), '6')).toBe(true))
|
||||
it('passes when a array length', async () => expect(await rules.min({ value: Array(6) }, '6')).toBe(true))
|
||||
|
||||
it('passes when string is forced to value', async () => expect(await rules.min('bcd', 'aaa', 'value')).toBe(true))
|
||||
it('passes when string is forced to value', async () => expect(await rules.min({ value: 'bcd' }, 'aaa', 'value')).toBe(true))
|
||||
|
||||
it('fails when string is forced to lesser value', async () => expect(await rules.min('a', 'b', 'value')).toBe(false))
|
||||
it('fails when string is forced to lesser value', async () => expect(await rules.min({ value: 'a' }, 'b', 'value')).toBe(false))
|
||||
|
||||
it('passes when a number is forced to length', async () => expect(await rules.min('000', 3, 'length')).toBe(true))
|
||||
it('passes when a number is forced to length', async () => expect(await rules.min({ value: '000' }, 3, 'length')).toBe(true))
|
||||
|
||||
it('fails when a number is forced to length', async () => expect(await rules.min('44', 3, 'length')).toBe(false))
|
||||
it('fails when a number is forced to length', async () => expect(await rules.min({ value: '44' }, 3, 'length')).toBe(false))
|
||||
|
||||
it('fails when a array length', async () => expect(await rules.min(Array(6), '7')).toBe(false))
|
||||
it('fails when a array length', async () => expect(await rules.min({ value: Array(6) }, '7')).toBe(false))
|
||||
|
||||
it('fails when a string length', async () => expect(await rules.min('bar', 4)).toBe(false))
|
||||
it('fails when a string length', async () => expect(await rules.min({ value: 'bar' }, 4)).toBe(false))
|
||||
|
||||
it('fails when a number', async () => expect(await rules.min(3, '7')).toBe(false))
|
||||
it('fails when a number', async () => expect(await rules.min({ value: 3 }, '7')).toBe(false))
|
||||
|
||||
})
|
||||
|
||||
@ -350,42 +350,72 @@ describe('min', () => {
|
||||
* Maximum.
|
||||
*/
|
||||
describe('max', () => {
|
||||
it('passes when a number string', async () => expect(await rules.max('5', '5')).toBe(true))
|
||||
it('passes when a number string', async () => expect(await rules.max({ value: '5' }, '5')).toBe(true))
|
||||
|
||||
it('passes when a number', async () => expect(await rules.max(5, 6)).toBe(true))
|
||||
it('passes when a number', async () => expect(await rules.max({ value: 5 }, 6)).toBe(true))
|
||||
|
||||
it('passes when a string length', async () => expect(await rules.max('foobar', '6')).toBe(true))
|
||||
it('passes when a string length', async () => expect(await rules.max({ value: 'foobar' }, '6')).toBe(true))
|
||||
|
||||
it('passes when a array length', async () => expect(await rules.max(Array(6), '6')).toBe(true))
|
||||
it('passes when a array length', async () => expect(await rules.max({ value: Array(6) }, '6')).toBe(true))
|
||||
|
||||
it('passes when forced to validate on length', async () => expect(await rules.max(10, 3, 'length')).toBe(true))
|
||||
it('passes when forced to validate on length', async () => expect(await rules.max({ value: 10 }, 3, 'length')).toBe(true))
|
||||
|
||||
it('passes when forced to validate string on value', async () => expect(await rules.max('b', 'e', 'value')).toBe(true))
|
||||
it('passes when forced to validate string on value', async () => expect(await rules.max({ value: 'b' }, 'e', 'value')).toBe(true))
|
||||
|
||||
it('fails when a array length', async () => expect(await rules.max(Array(6), '5')).toBe(false))
|
||||
it('fails when a array length', async () => expect(await rules.max({ value: Array(6) }, '5')).toBe(false))
|
||||
|
||||
it('fails when a string length', async () => expect(await rules.max('bar', 2)).toBe(false))
|
||||
it('fails when a string length', async () => expect(await rules.max({ value: 'bar' }, 2)).toBe(false))
|
||||
|
||||
it('fails when a number', async () => expect(await rules.max(10, '7')).toBe(false))
|
||||
it('fails when a number', async () => expect(await rules.max({ value: 10 }, '7')).toBe(false))
|
||||
|
||||
it('fails when a number', async () => expect(await rules.max(10, '7')).toBe(false))
|
||||
it('fails when a number', async () => expect(await rules.max({ value: 10 }, '7')).toBe(false))
|
||||
|
||||
it('fails when forced to validate on length', async () => expect(await rules.max(-10, '1', 'length')).toBe(false))
|
||||
it('fails when forced to validate on length', async () => expect(await rules.max({ value: -10 }, '1', 'length')).toBe(false))
|
||||
})
|
||||
|
||||
/**
|
||||
* Maximum.
|
||||
*/
|
||||
describe('not', () => {
|
||||
it('passes when a number string', async () => expect(await rules.not('5', '6')).toBe(true))
|
||||
it('passes when a number string', async () => expect(await rules.not({ value: '5' }, '6')).toBe(true))
|
||||
|
||||
it('passes when a number', async () => expect(await rules.not(1, 30)).toBe(true))
|
||||
it('passes when a number', async () => expect(await rules.not({ value: 1 }, 30)).toBe(true))
|
||||
|
||||
it('passes when a string', async () => expect(await rules.not('abc', 'def')).toBe(true))
|
||||
it('passes when a string', async () => expect(await rules.not({ value: 'abc' }, 'def')).toBe(true))
|
||||
|
||||
it('fails when a shallow equal array', async () => expect(await rules.not(['abc'], ['abc'])).toBe(false))
|
||||
it('fails when a shallow equal array', async () => expect(await rules.not({ value: ['abc'] }, ['abc'])).toBe(false))
|
||||
|
||||
it('fails when a shallow equal object', async () => expect(await rules.not({a: 'abc'}, ['123'], {a: 'abc'})).toBe(false))
|
||||
it('fails when a shallow equal object', async () => expect(await rules.not({ value: {a: 'abc'} }, ['123'], {a: 'abc'})).toBe(false))
|
||||
|
||||
it('fails when string is in stack', async () => expect(await rules.not('a', 'b', 'c', 'd', 'a', 'f')).toBe(false))
|
||||
it('fails when string is in stack', async () => expect(await rules.not({ value: 'a' }, 'b', 'c', 'd', 'a', 'f')).toBe(false))
|
||||
})
|
||||
|
||||
/**
|
||||
* Confirm
|
||||
*/
|
||||
describe('confirm', () => {
|
||||
it('passes when the values are the same strings', async () => expect(await rules.confirm(
|
||||
{ value: 'abc', name: 'password', getFormValues: () => ({ password_confirm: 'abc' }) }
|
||||
)).toBe(true))
|
||||
|
||||
it('passes when the values are the same integers', async () => expect(await rules.confirm(
|
||||
{ value: 4422132, name: 'xyz', getFormValues: () => ({ xyz_confirm: 4422132 }) }
|
||||
)).toBe(true))
|
||||
|
||||
it('passes when using a custom field', async () => expect(await rules.confirm(
|
||||
{ value: 4422132, name: 'name', getFormValues: () => ({ other_field: 4422132 }) },
|
||||
'other_field'
|
||||
)).toBe(true))
|
||||
|
||||
it('passes when using a field ends in _confirm', async () => expect(await rules.confirm(
|
||||
{ value: '$ecret', name: 'password_confirm', getFormValues: () => ({ password: '$ecret' }) }
|
||||
)).toBe(true))
|
||||
|
||||
it('fails when using different strings', async () => expect(await rules.confirm(
|
||||
{ value: 'Justin', name: 'name', getFormValues: () => ({ name_confirm: 'Daniel' }) },
|
||||
)).toBe(false))
|
||||
|
||||
it('fails when the types are different', async () => expect(await rules.confirm(
|
||||
{ value: '1234', name: 'num', getFormValues: () => ({ num_confirm: 1234 }) },
|
||||
)).toBe(false))
|
||||
})
|
||||
|
Loading…
Reference in New Issue
Block a user