1
0
mirror of synced 2024-11-25 14:56:03 +03:00

Initial tests written for validation parsing and rules

This commit is contained in:
Justin Schroeder 2019-11-06 17:17:19 -05:00
parent 9522459600
commit a53edaa342
18 changed files with 1115 additions and 377 deletions

View File

@ -1,3 +1,6 @@
{ {
"jest.showCoverageOnLoad": true "jest.showCoverageOnLoad": true,
"cSpell.words": [
"arrayify"
]
} }

280
dist/formulate.esm.js vendored
View File

@ -140,6 +140,26 @@ function shallowEqualObjects (objA, objB) {
return true return true
} }
/**
* Given a string, object, falsey, or array - return an array.
* @param {mixed} item
*/
function arrayify (item) {
if (!item) {
return []
}
if (typeof item === 'string') {
return [item]
}
if (Array.isArray(item)) {
return item
}
if (typeof item === 'object') {
return Object.values(item)
}
return []
}
/** /**
* For a single instance of an input, export all of the context needed to fully * For a single instance of an input, export all of the context needed to fully
* render that element. * render that element.
@ -165,7 +185,8 @@ var context = {
typeContext: typeContext, typeContext: typeContext,
elementAttributes: elementAttributes, elementAttributes: elementAttributes,
logicalLabelPosition: logicalLabelPosition, logicalLabelPosition: logicalLabelPosition,
isVmodeled: isVmodeled isVmodeled: isVmodeled,
mergedErrors: mergedErrors
}; };
/** /**
@ -268,6 +289,16 @@ function createOptionList (options) {
return options return options
} }
/**
* The merged errors computed property.
*/
function mergedErrors () {
return arrayify(this.errors)
.concat(arrayify(this.error))
.concat(arrayify(this.validationErrors))
.reduce(function (errors, err) { return !errors.includes(err) ? errors.concat(err) : errors; }, [])
}
/** /**
* Defines the model used throughout the existing context. * Defines the model used throughout the existing context.
* @param {object} context * @param {object} context
@ -361,6 +392,14 @@ var script = {
debug: { debug: {
type: Boolean, type: Boolean,
default: false default: false
},
errors: {
type: [String, Array, Boolean],
default: false
},
error: {
type: [String, Boolean],
default: false
} }
}, },
data: function data () { data: function data () {
@ -560,8 +599,11 @@ var __vue_render__ = function() {
staticClass: "formulate-input-help", staticClass: "formulate-input-help",
domProps: { textContent: _vm._s(_vm.help) } domProps: { textContent: _vm._s(_vm.help) }
}) })
: _vm._e() : _vm._e(),
] _vm._v(" "),
_c("FormulateInputErrors", { attrs: { errors: _vm.mergedErrors } })
],
1
) )
}; };
var __vue_staticRenderFns__ = []; var __vue_staticRenderFns__ = [];
@ -742,6 +784,87 @@ __vue_render__$1._withStripped = true;
undefined undefined
); );
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
var script$2 = {
props: {
errors: {
type: [Boolean, Array],
required: true
}
}
};
/* script */
var __vue_script__$2 = script$2;
/* template */
var __vue_render__$2 = function() {
var _vm = this;
var _h = _vm.$createElement;
var _c = _vm._self._c || _h;
return _vm.errors.length
? _c(
"ul",
{ staticClass: "formulate-input-errors" },
_vm._l(_vm.errors, function(error) {
return _c("li", {
key: error,
staticClass: "formulate-input-error",
domProps: { innerHTML: _vm._s(error) }
})
}),
0
)
: _vm._e()
};
var __vue_staticRenderFns__$2 = [];
__vue_render__$2._withStripped = true;
/* style */
var __vue_inject_styles__$2 = undefined;
/* scoped */
var __vue_scope_id__$2 = undefined;
/* module identifier */
var __vue_module_identifier__$2 = undefined;
/* functional template */
var __vue_is_functional_template__$2 = false;
/* style inject */
/* style inject SSR */
/* style inject shadow dom */
var FormulateInputErrors = normalizeComponent(
{ render: __vue_render__$2, staticRenderFns: __vue_staticRenderFns__$2 },
__vue_inject_styles__$2,
__vue_script__$2,
__vue_scope_id__$2,
__vue_is_functional_template__$2,
__vue_module_identifier__$2,
false,
undefined,
undefined,
undefined
);
// //
// //
// //
@ -757,7 +880,7 @@ __vue_render__$1._withStripped = true;
// //
function objectWithoutProperties (obj, exclude) { var target = {}; for (var k in obj) if (Object.prototype.hasOwnProperty.call(obj, k) && exclude.indexOf(k) === -1) target[k] = obj[k]; return target; } function objectWithoutProperties (obj, exclude) { var target = {}; for (var k in obj) if (Object.prototype.hasOwnProperty.call(obj, k) && exclude.indexOf(k) === -1) target[k] = obj[k]; return target; }
var script$2 = { var script$3 = {
name: 'FormulateInputGroup', name: 'FormulateInputGroup',
props: { props: {
context: { context: {
@ -795,10 +918,10 @@ var script$2 = {
}; };
/* script */ /* script */
var __vue_script__$2 = script$2; var __vue_script__$3 = script$3;
/* template */ /* template */
var __vue_render__$2 = function() { var __vue_render__$3 = function() {
var _vm = this; var _vm = this;
var _h = _vm.$createElement; var _h = _vm.$createElement;
var _c = _vm._self._c || _h; var _c = _vm._self._c || _h;
@ -830,17 +953,17 @@ var __vue_render__$2 = function() {
1 1
) )
}; };
var __vue_staticRenderFns__$2 = []; var __vue_staticRenderFns__$3 = [];
__vue_render__$2._withStripped = true; __vue_render__$3._withStripped = true;
/* style */ /* style */
var __vue_inject_styles__$2 = undefined; var __vue_inject_styles__$3 = undefined;
/* scoped */ /* scoped */
var __vue_scope_id__$2 = undefined; var __vue_scope_id__$3 = undefined;
/* module identifier */ /* module identifier */
var __vue_module_identifier__$2 = undefined; var __vue_module_identifier__$3 = undefined;
/* functional template */ /* functional template */
var __vue_is_functional_template__$2 = false; var __vue_is_functional_template__$3 = false;
/* style inject */ /* style inject */
/* style inject SSR */ /* style inject SSR */
@ -850,12 +973,12 @@ __vue_render__$2._withStripped = true;
var FormulateInputGroup = normalizeComponent( var FormulateInputGroup = normalizeComponent(
{ render: __vue_render__$2, staticRenderFns: __vue_staticRenderFns__$2 }, { render: __vue_render__$3, staticRenderFns: __vue_staticRenderFns__$3 },
__vue_inject_styles__$2, __vue_inject_styles__$3,
__vue_script__$2, __vue_script__$3,
__vue_scope_id__$2, __vue_scope_id__$3,
__vue_is_functional_template__$2, __vue_is_functional_template__$3,
__vue_module_identifier__$2, __vue_module_identifier__$3,
false, false,
undefined, undefined,
undefined, undefined,
@ -890,16 +1013,16 @@ var FormulateInputMixin = {
// //
var script$3 = { var script$4 = {
name: 'FormulateInputBox', name: 'FormulateInputBox',
mixins: [FormulateInputMixin] mixins: [FormulateInputMixin]
}; };
/* script */ /* script */
var __vue_script__$3 = script$3; var __vue_script__$4 = script$4;
/* template */ /* template */
var __vue_render__$3 = function() { var __vue_render__$4 = function() {
var _vm = this; var _vm = this;
var _h = _vm.$createElement; var _h = _vm.$createElement;
var _c = _vm._self._c || _h; var _c = _vm._self._c || _h;
@ -1028,17 +1151,17 @@ var __vue_render__$3 = function() {
] ]
) )
}; };
var __vue_staticRenderFns__$3 = []; var __vue_staticRenderFns__$4 = [];
__vue_render__$3._withStripped = true; __vue_render__$4._withStripped = true;
/* style */ /* style */
var __vue_inject_styles__$3 = undefined; var __vue_inject_styles__$4 = undefined;
/* scoped */ /* scoped */
var __vue_scope_id__$3 = undefined; var __vue_scope_id__$4 = undefined;
/* module identifier */ /* module identifier */
var __vue_module_identifier__$3 = undefined; var __vue_module_identifier__$4 = undefined;
/* functional template */ /* functional template */
var __vue_is_functional_template__$3 = false; var __vue_is_functional_template__$4 = false;
/* style inject */ /* style inject */
/* style inject SSR */ /* style inject SSR */
@ -1048,12 +1171,12 @@ __vue_render__$3._withStripped = true;
var FormulateInputBox = normalizeComponent( var FormulateInputBox = normalizeComponent(
{ render: __vue_render__$3, staticRenderFns: __vue_staticRenderFns__$3 }, { render: __vue_render__$4, staticRenderFns: __vue_staticRenderFns__$4 },
__vue_inject_styles__$3, __vue_inject_styles__$4,
__vue_script__$3, __vue_script__$4,
__vue_scope_id__$3, __vue_scope_id__$4,
__vue_is_functional_template__$3, __vue_is_functional_template__$4,
__vue_module_identifier__$3, __vue_module_identifier__$4,
false, false,
undefined, undefined,
undefined, undefined,
@ -1062,16 +1185,16 @@ __vue_render__$3._withStripped = true;
// //
var script$4 = { var script$5 = {
name: 'FormulateInputText', name: 'FormulateInputText',
mixins: [FormulateInputMixin] mixins: [FormulateInputMixin]
}; };
/* script */ /* script */
var __vue_script__$4 = script$4; var __vue_script__$5 = script$5;
/* template */ /* template */
var __vue_render__$4 = function() { var __vue_render__$5 = function() {
var _vm = this; var _vm = this;
var _h = _vm.$createElement; var _h = _vm.$createElement;
var _c = _vm._self._c || _h; var _c = _vm._self._c || _h;
@ -1187,17 +1310,17 @@ var __vue_render__$4 = function() {
] ]
) )
}; };
var __vue_staticRenderFns__$4 = []; var __vue_staticRenderFns__$5 = [];
__vue_render__$4._withStripped = true; __vue_render__$5._withStripped = true;
/* style */ /* style */
var __vue_inject_styles__$4 = undefined; var __vue_inject_styles__$5 = undefined;
/* scoped */ /* scoped */
var __vue_scope_id__$4 = undefined; var __vue_scope_id__$5 = undefined;
/* module identifier */ /* module identifier */
var __vue_module_identifier__$4 = undefined; var __vue_module_identifier__$5 = undefined;
/* functional template */ /* functional template */
var __vue_is_functional_template__$4 = false; var __vue_is_functional_template__$5 = false;
/* style inject */ /* style inject */
/* style inject SSR */ /* style inject SSR */
@ -1207,12 +1330,12 @@ __vue_render__$4._withStripped = true;
var FormulateInputText = normalizeComponent( var FormulateInputText = normalizeComponent(
{ render: __vue_render__$4, staticRenderFns: __vue_staticRenderFns__$4 }, { render: __vue_render__$5, staticRenderFns: __vue_staticRenderFns__$5 },
__vue_inject_styles__$4, __vue_inject_styles__$5,
__vue_script__$4, __vue_script__$5,
__vue_scope_id__$4, __vue_scope_id__$5,
__vue_is_functional_template__$4, __vue_is_functional_template__$5,
__vue_module_identifier__$4, __vue_module_identifier__$5,
false, false,
undefined, undefined,
undefined, undefined,
@ -1221,7 +1344,7 @@ __vue_render__$4._withStripped = true;
// //
var script$5 = { var script$6 = {
name: 'FormulateInputSelect', name: 'FormulateInputSelect',
mixins: [FormulateInputMixin], mixins: [FormulateInputMixin],
computed: { computed: {
@ -1238,10 +1361,10 @@ var script$5 = {
}; };
/* script */ /* script */
var __vue_script__$5 = script$5; var __vue_script__$6 = script$6;
/* template */ /* template */
var __vue_render__$5 = function() { var __vue_render__$6 = function() {
var _vm = this; var _vm = this;
var _h = _vm.$createElement; var _h = _vm.$createElement;
var _c = _vm._self._c || _h; var _c = _vm._self._c || _h;
@ -1348,17 +1471,17 @@ var __vue_render__$5 = function() {
] ]
) )
}; };
var __vue_staticRenderFns__$5 = []; var __vue_staticRenderFns__$6 = [];
__vue_render__$5._withStripped = true; __vue_render__$6._withStripped = true;
/* style */ /* style */
var __vue_inject_styles__$5 = undefined; var __vue_inject_styles__$6 = undefined;
/* scoped */ /* scoped */
var __vue_scope_id__$5 = undefined; var __vue_scope_id__$6 = undefined;
/* module identifier */ /* module identifier */
var __vue_module_identifier__$5 = undefined; var __vue_module_identifier__$6 = undefined;
/* functional template */ /* functional template */
var __vue_is_functional_template__$5 = false; var __vue_is_functional_template__$6 = false;
/* style inject */ /* style inject */
/* style inject SSR */ /* style inject SSR */
@ -1368,12 +1491,12 @@ __vue_render__$5._withStripped = true;
var FormulateInputSelect = normalizeComponent( var FormulateInputSelect = normalizeComponent(
{ render: __vue_render__$5, staticRenderFns: __vue_staticRenderFns__$5 }, { render: __vue_render__$6, staticRenderFns: __vue_staticRenderFns__$6 },
__vue_inject_styles__$5, __vue_inject_styles__$6,
__vue_script__$5, __vue_script__$6,
__vue_scope_id__$5, __vue_scope_id__$6,
__vue_is_functional_template__$5, __vue_is_functional_template__$6,
__vue_module_identifier__$5, __vue_module_identifier__$6,
false, false,
undefined, undefined,
undefined, undefined,
@ -1382,16 +1505,16 @@ __vue_render__$5._withStripped = true;
// //
var script$6 = { var script$7 = {
name: 'FormulateInputTextArea', name: 'FormulateInputTextArea',
mixins: [FormulateInputMixin] mixins: [FormulateInputMixin]
}; };
/* script */ /* script */
var __vue_script__$6 = script$6; var __vue_script__$7 = script$7;
/* template */ /* template */
var __vue_render__$6 = function() { var __vue_render__$7 = function() {
var _vm = this; var _vm = this;
var _h = _vm.$createElement; var _h = _vm.$createElement;
var _c = _vm._self._c || _h; var _c = _vm._self._c || _h;
@ -1431,17 +1554,17 @@ var __vue_render__$6 = function() {
] ]
) )
}; };
var __vue_staticRenderFns__$6 = []; var __vue_staticRenderFns__$7 = [];
__vue_render__$6._withStripped = true; __vue_render__$7._withStripped = true;
/* style */ /* style */
var __vue_inject_styles__$6 = undefined; var __vue_inject_styles__$7 = undefined;
/* scoped */ /* scoped */
var __vue_scope_id__$6 = undefined; var __vue_scope_id__$7 = undefined;
/* module identifier */ /* module identifier */
var __vue_module_identifier__$6 = undefined; var __vue_module_identifier__$7 = undefined;
/* functional template */ /* functional template */
var __vue_is_functional_template__$6 = false; var __vue_is_functional_template__$7 = false;
/* style inject */ /* style inject */
/* style inject SSR */ /* style inject SSR */
@ -1451,12 +1574,12 @@ __vue_render__$6._withStripped = true;
var FormulateInputTextArea = normalizeComponent( var FormulateInputTextArea = normalizeComponent(
{ render: __vue_render__$6, staticRenderFns: __vue_staticRenderFns__$6 }, { render: __vue_render__$7, staticRenderFns: __vue_staticRenderFns__$7 },
__vue_inject_styles__$6, __vue_inject_styles__$7,
__vue_script__$6, __vue_script__$7,
__vue_scope_id__$6, __vue_scope_id__$7,
__vue_is_functional_template__$6, __vue_is_functional_template__$7,
__vue_module_identifier__$6, __vue_module_identifier__$7,
false, false,
undefined, undefined,
undefined, undefined,
@ -1471,6 +1594,7 @@ var Formulate = function Formulate () {
components: { components: {
FormulateForm: FormulateForm, FormulateForm: FormulateForm,
FormulateInput: FormulateInput, FormulateInput: FormulateInput,
FormulateInputErrors: FormulateInputErrors,
FormulateInputBox: FormulateInputBox, FormulateInputBox: FormulateInputBox,
FormulateInputText: FormulateInputText, FormulateInputText: FormulateInputText,
FormulateInputGroup: FormulateInputGroup, FormulateInputGroup: FormulateInputGroup,

280
dist/formulate.min.js vendored
View File

@ -143,6 +143,26 @@ var Formulate = (function (exports, isPlainObject, nanoid) {
return true return true
} }
/**
* Given a string, object, falsey, or array - return an array.
* @param {mixed} item
*/
function arrayify (item) {
if (!item) {
return []
}
if (typeof item === 'string') {
return [item]
}
if (Array.isArray(item)) {
return item
}
if (typeof item === 'object') {
return Object.values(item)
}
return []
}
/** /**
* For a single instance of an input, export all of the context needed to fully * For a single instance of an input, export all of the context needed to fully
* render that element. * render that element.
@ -168,7 +188,8 @@ var Formulate = (function (exports, isPlainObject, nanoid) {
typeContext: typeContext, typeContext: typeContext,
elementAttributes: elementAttributes, elementAttributes: elementAttributes,
logicalLabelPosition: logicalLabelPosition, logicalLabelPosition: logicalLabelPosition,
isVmodeled: isVmodeled isVmodeled: isVmodeled,
mergedErrors: mergedErrors
}; };
/** /**
@ -271,6 +292,16 @@ var Formulate = (function (exports, isPlainObject, nanoid) {
return options return options
} }
/**
* The merged errors computed property.
*/
function mergedErrors () {
return arrayify(this.errors)
.concat(arrayify(this.error))
.concat(arrayify(this.validationErrors))
.reduce(function (errors, err) { return !errors.includes(err) ? errors.concat(err) : errors; }, [])
}
/** /**
* Defines the model used throughout the existing context. * Defines the model used throughout the existing context.
* @param {object} context * @param {object} context
@ -364,6 +395,14 @@ var Formulate = (function (exports, isPlainObject, nanoid) {
debug: { debug: {
type: Boolean, type: Boolean,
default: false default: false
},
errors: {
type: [String, Array, Boolean],
default: false
},
error: {
type: [String, Boolean],
default: false
} }
}, },
data: function data () { data: function data () {
@ -563,8 +602,11 @@ var Formulate = (function (exports, isPlainObject, nanoid) {
staticClass: "formulate-input-help", staticClass: "formulate-input-help",
domProps: { textContent: _vm._s(_vm.help) } domProps: { textContent: _vm._s(_vm.help) }
}) })
: _vm._e() : _vm._e(),
] _vm._v(" "),
_c("FormulateInputErrors", { attrs: { errors: _vm.mergedErrors } })
],
1
) )
}; };
var __vue_staticRenderFns__ = []; var __vue_staticRenderFns__ = [];
@ -745,6 +787,87 @@ var Formulate = (function (exports, isPlainObject, nanoid) {
undefined undefined
); );
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
var script$2 = {
props: {
errors: {
type: [Boolean, Array],
required: true
}
}
};
/* script */
var __vue_script__$2 = script$2;
/* template */
var __vue_render__$2 = function() {
var _vm = this;
var _h = _vm.$createElement;
var _c = _vm._self._c || _h;
return _vm.errors.length
? _c(
"ul",
{ staticClass: "formulate-input-errors" },
_vm._l(_vm.errors, function(error) {
return _c("li", {
key: error,
staticClass: "formulate-input-error",
domProps: { innerHTML: _vm._s(error) }
})
}),
0
)
: _vm._e()
};
var __vue_staticRenderFns__$2 = [];
__vue_render__$2._withStripped = true;
/* style */
var __vue_inject_styles__$2 = undefined;
/* scoped */
var __vue_scope_id__$2 = undefined;
/* module identifier */
var __vue_module_identifier__$2 = undefined;
/* functional template */
var __vue_is_functional_template__$2 = false;
/* style inject */
/* style inject SSR */
/* style inject shadow dom */
var FormulateInputErrors = normalizeComponent(
{ render: __vue_render__$2, staticRenderFns: __vue_staticRenderFns__$2 },
__vue_inject_styles__$2,
__vue_script__$2,
__vue_scope_id__$2,
__vue_is_functional_template__$2,
__vue_module_identifier__$2,
false,
undefined,
undefined,
undefined
);
// //
// //
// //
@ -760,7 +883,7 @@ var Formulate = (function (exports, isPlainObject, nanoid) {
// //
function objectWithoutProperties (obj, exclude) { var target = {}; for (var k in obj) if (Object.prototype.hasOwnProperty.call(obj, k) && exclude.indexOf(k) === -1) target[k] = obj[k]; return target; } function objectWithoutProperties (obj, exclude) { var target = {}; for (var k in obj) if (Object.prototype.hasOwnProperty.call(obj, k) && exclude.indexOf(k) === -1) target[k] = obj[k]; return target; }
var script$2 = { var script$3 = {
name: 'FormulateInputGroup', name: 'FormulateInputGroup',
props: { props: {
context: { context: {
@ -798,10 +921,10 @@ var Formulate = (function (exports, isPlainObject, nanoid) {
}; };
/* script */ /* script */
var __vue_script__$2 = script$2; var __vue_script__$3 = script$3;
/* template */ /* template */
var __vue_render__$2 = function() { var __vue_render__$3 = function() {
var _vm = this; var _vm = this;
var _h = _vm.$createElement; var _h = _vm.$createElement;
var _c = _vm._self._c || _h; var _c = _vm._self._c || _h;
@ -833,17 +956,17 @@ var Formulate = (function (exports, isPlainObject, nanoid) {
1 1
) )
}; };
var __vue_staticRenderFns__$2 = []; var __vue_staticRenderFns__$3 = [];
__vue_render__$2._withStripped = true; __vue_render__$3._withStripped = true;
/* style */ /* style */
var __vue_inject_styles__$2 = undefined; var __vue_inject_styles__$3 = undefined;
/* scoped */ /* scoped */
var __vue_scope_id__$2 = undefined; var __vue_scope_id__$3 = undefined;
/* module identifier */ /* module identifier */
var __vue_module_identifier__$2 = undefined; var __vue_module_identifier__$3 = undefined;
/* functional template */ /* functional template */
var __vue_is_functional_template__$2 = false; var __vue_is_functional_template__$3 = false;
/* style inject */ /* style inject */
/* style inject SSR */ /* style inject SSR */
@ -853,12 +976,12 @@ var Formulate = (function (exports, isPlainObject, nanoid) {
var FormulateInputGroup = normalizeComponent( var FormulateInputGroup = normalizeComponent(
{ render: __vue_render__$2, staticRenderFns: __vue_staticRenderFns__$2 }, { render: __vue_render__$3, staticRenderFns: __vue_staticRenderFns__$3 },
__vue_inject_styles__$2, __vue_inject_styles__$3,
__vue_script__$2, __vue_script__$3,
__vue_scope_id__$2, __vue_scope_id__$3,
__vue_is_functional_template__$2, __vue_is_functional_template__$3,
__vue_module_identifier__$2, __vue_module_identifier__$3,
false, false,
undefined, undefined,
undefined, undefined,
@ -893,16 +1016,16 @@ var Formulate = (function (exports, isPlainObject, nanoid) {
// //
var script$3 = { var script$4 = {
name: 'FormulateInputBox', name: 'FormulateInputBox',
mixins: [FormulateInputMixin] mixins: [FormulateInputMixin]
}; };
/* script */ /* script */
var __vue_script__$3 = script$3; var __vue_script__$4 = script$4;
/* template */ /* template */
var __vue_render__$3 = function() { var __vue_render__$4 = function() {
var _vm = this; var _vm = this;
var _h = _vm.$createElement; var _h = _vm.$createElement;
var _c = _vm._self._c || _h; var _c = _vm._self._c || _h;
@ -1031,17 +1154,17 @@ var Formulate = (function (exports, isPlainObject, nanoid) {
] ]
) )
}; };
var __vue_staticRenderFns__$3 = []; var __vue_staticRenderFns__$4 = [];
__vue_render__$3._withStripped = true; __vue_render__$4._withStripped = true;
/* style */ /* style */
var __vue_inject_styles__$3 = undefined; var __vue_inject_styles__$4 = undefined;
/* scoped */ /* scoped */
var __vue_scope_id__$3 = undefined; var __vue_scope_id__$4 = undefined;
/* module identifier */ /* module identifier */
var __vue_module_identifier__$3 = undefined; var __vue_module_identifier__$4 = undefined;
/* functional template */ /* functional template */
var __vue_is_functional_template__$3 = false; var __vue_is_functional_template__$4 = false;
/* style inject */ /* style inject */
/* style inject SSR */ /* style inject SSR */
@ -1051,12 +1174,12 @@ var Formulate = (function (exports, isPlainObject, nanoid) {
var FormulateInputBox = normalizeComponent( var FormulateInputBox = normalizeComponent(
{ render: __vue_render__$3, staticRenderFns: __vue_staticRenderFns__$3 }, { render: __vue_render__$4, staticRenderFns: __vue_staticRenderFns__$4 },
__vue_inject_styles__$3, __vue_inject_styles__$4,
__vue_script__$3, __vue_script__$4,
__vue_scope_id__$3, __vue_scope_id__$4,
__vue_is_functional_template__$3, __vue_is_functional_template__$4,
__vue_module_identifier__$3, __vue_module_identifier__$4,
false, false,
undefined, undefined,
undefined, undefined,
@ -1065,16 +1188,16 @@ var Formulate = (function (exports, isPlainObject, nanoid) {
// //
var script$4 = { var script$5 = {
name: 'FormulateInputText', name: 'FormulateInputText',
mixins: [FormulateInputMixin] mixins: [FormulateInputMixin]
}; };
/* script */ /* script */
var __vue_script__$4 = script$4; var __vue_script__$5 = script$5;
/* template */ /* template */
var __vue_render__$4 = function() { var __vue_render__$5 = function() {
var _vm = this; var _vm = this;
var _h = _vm.$createElement; var _h = _vm.$createElement;
var _c = _vm._self._c || _h; var _c = _vm._self._c || _h;
@ -1190,17 +1313,17 @@ var Formulate = (function (exports, isPlainObject, nanoid) {
] ]
) )
}; };
var __vue_staticRenderFns__$4 = []; var __vue_staticRenderFns__$5 = [];
__vue_render__$4._withStripped = true; __vue_render__$5._withStripped = true;
/* style */ /* style */
var __vue_inject_styles__$4 = undefined; var __vue_inject_styles__$5 = undefined;
/* scoped */ /* scoped */
var __vue_scope_id__$4 = undefined; var __vue_scope_id__$5 = undefined;
/* module identifier */ /* module identifier */
var __vue_module_identifier__$4 = undefined; var __vue_module_identifier__$5 = undefined;
/* functional template */ /* functional template */
var __vue_is_functional_template__$4 = false; var __vue_is_functional_template__$5 = false;
/* style inject */ /* style inject */
/* style inject SSR */ /* style inject SSR */
@ -1210,12 +1333,12 @@ var Formulate = (function (exports, isPlainObject, nanoid) {
var FormulateInputText = normalizeComponent( var FormulateInputText = normalizeComponent(
{ render: __vue_render__$4, staticRenderFns: __vue_staticRenderFns__$4 }, { render: __vue_render__$5, staticRenderFns: __vue_staticRenderFns__$5 },
__vue_inject_styles__$4, __vue_inject_styles__$5,
__vue_script__$4, __vue_script__$5,
__vue_scope_id__$4, __vue_scope_id__$5,
__vue_is_functional_template__$4, __vue_is_functional_template__$5,
__vue_module_identifier__$4, __vue_module_identifier__$5,
false, false,
undefined, undefined,
undefined, undefined,
@ -1224,7 +1347,7 @@ var Formulate = (function (exports, isPlainObject, nanoid) {
// //
var script$5 = { var script$6 = {
name: 'FormulateInputSelect', name: 'FormulateInputSelect',
mixins: [FormulateInputMixin], mixins: [FormulateInputMixin],
computed: { computed: {
@ -1241,10 +1364,10 @@ var Formulate = (function (exports, isPlainObject, nanoid) {
}; };
/* script */ /* script */
var __vue_script__$5 = script$5; var __vue_script__$6 = script$6;
/* template */ /* template */
var __vue_render__$5 = function() { var __vue_render__$6 = function() {
var _vm = this; var _vm = this;
var _h = _vm.$createElement; var _h = _vm.$createElement;
var _c = _vm._self._c || _h; var _c = _vm._self._c || _h;
@ -1351,17 +1474,17 @@ var Formulate = (function (exports, isPlainObject, nanoid) {
] ]
) )
}; };
var __vue_staticRenderFns__$5 = []; var __vue_staticRenderFns__$6 = [];
__vue_render__$5._withStripped = true; __vue_render__$6._withStripped = true;
/* style */ /* style */
var __vue_inject_styles__$5 = undefined; var __vue_inject_styles__$6 = undefined;
/* scoped */ /* scoped */
var __vue_scope_id__$5 = undefined; var __vue_scope_id__$6 = undefined;
/* module identifier */ /* module identifier */
var __vue_module_identifier__$5 = undefined; var __vue_module_identifier__$6 = undefined;
/* functional template */ /* functional template */
var __vue_is_functional_template__$5 = false; var __vue_is_functional_template__$6 = false;
/* style inject */ /* style inject */
/* style inject SSR */ /* style inject SSR */
@ -1371,12 +1494,12 @@ var Formulate = (function (exports, isPlainObject, nanoid) {
var FormulateInputSelect = normalizeComponent( var FormulateInputSelect = normalizeComponent(
{ render: __vue_render__$5, staticRenderFns: __vue_staticRenderFns__$5 }, { render: __vue_render__$6, staticRenderFns: __vue_staticRenderFns__$6 },
__vue_inject_styles__$5, __vue_inject_styles__$6,
__vue_script__$5, __vue_script__$6,
__vue_scope_id__$5, __vue_scope_id__$6,
__vue_is_functional_template__$5, __vue_is_functional_template__$6,
__vue_module_identifier__$5, __vue_module_identifier__$6,
false, false,
undefined, undefined,
undefined, undefined,
@ -1385,16 +1508,16 @@ var Formulate = (function (exports, isPlainObject, nanoid) {
// //
var script$6 = { var script$7 = {
name: 'FormulateInputTextArea', name: 'FormulateInputTextArea',
mixins: [FormulateInputMixin] mixins: [FormulateInputMixin]
}; };
/* script */ /* script */
var __vue_script__$6 = script$6; var __vue_script__$7 = script$7;
/* template */ /* template */
var __vue_render__$6 = function() { var __vue_render__$7 = function() {
var _vm = this; var _vm = this;
var _h = _vm.$createElement; var _h = _vm.$createElement;
var _c = _vm._self._c || _h; var _c = _vm._self._c || _h;
@ -1434,17 +1557,17 @@ var Formulate = (function (exports, isPlainObject, nanoid) {
] ]
) )
}; };
var __vue_staticRenderFns__$6 = []; var __vue_staticRenderFns__$7 = [];
__vue_render__$6._withStripped = true; __vue_render__$7._withStripped = true;
/* style */ /* style */
var __vue_inject_styles__$6 = undefined; var __vue_inject_styles__$7 = undefined;
/* scoped */ /* scoped */
var __vue_scope_id__$6 = undefined; var __vue_scope_id__$7 = undefined;
/* module identifier */ /* module identifier */
var __vue_module_identifier__$6 = undefined; var __vue_module_identifier__$7 = undefined;
/* functional template */ /* functional template */
var __vue_is_functional_template__$6 = false; var __vue_is_functional_template__$7 = false;
/* style inject */ /* style inject */
/* style inject SSR */ /* style inject SSR */
@ -1454,12 +1577,12 @@ var Formulate = (function (exports, isPlainObject, nanoid) {
var FormulateInputTextArea = normalizeComponent( var FormulateInputTextArea = normalizeComponent(
{ render: __vue_render__$6, staticRenderFns: __vue_staticRenderFns__$6 }, { render: __vue_render__$7, staticRenderFns: __vue_staticRenderFns__$7 },
__vue_inject_styles__$6, __vue_inject_styles__$7,
__vue_script__$6, __vue_script__$7,
__vue_scope_id__$6, __vue_scope_id__$7,
__vue_is_functional_template__$6, __vue_is_functional_template__$7,
__vue_module_identifier__$6, __vue_module_identifier__$7,
false, false,
undefined, undefined,
undefined, undefined,
@ -1474,6 +1597,7 @@ var Formulate = (function (exports, isPlainObject, nanoid) {
components: { components: {
FormulateForm: FormulateForm, FormulateForm: FormulateForm,
FormulateInput: FormulateInput, FormulateInput: FormulateInput,
FormulateInputErrors: FormulateInputErrors,
FormulateInputBox: FormulateInputBox, FormulateInputBox: FormulateInputBox,
FormulateInputText: FormulateInputText, FormulateInputText: FormulateInputText,
FormulateInputGroup: FormulateInputGroup, FormulateInputGroup: FormulateInputGroup,

280
dist/formulate.umd.js vendored
View File

@ -146,6 +146,26 @@
return true return true
} }
/**
* Given a string, object, falsey, or array - return an array.
* @param {mixed} item
*/
function arrayify (item) {
if (!item) {
return []
}
if (typeof item === 'string') {
return [item]
}
if (Array.isArray(item)) {
return item
}
if (typeof item === 'object') {
return Object.values(item)
}
return []
}
/** /**
* For a single instance of an input, export all of the context needed to fully * For a single instance of an input, export all of the context needed to fully
* render that element. * render that element.
@ -171,7 +191,8 @@
typeContext: typeContext, typeContext: typeContext,
elementAttributes: elementAttributes, elementAttributes: elementAttributes,
logicalLabelPosition: logicalLabelPosition, logicalLabelPosition: logicalLabelPosition,
isVmodeled: isVmodeled isVmodeled: isVmodeled,
mergedErrors: mergedErrors
}; };
/** /**
@ -274,6 +295,16 @@
return options return options
} }
/**
* The merged errors computed property.
*/
function mergedErrors () {
return arrayify(this.errors)
.concat(arrayify(this.error))
.concat(arrayify(this.validationErrors))
.reduce(function (errors, err) { return !errors.includes(err) ? errors.concat(err) : errors; }, [])
}
/** /**
* Defines the model used throughout the existing context. * Defines the model used throughout the existing context.
* @param {object} context * @param {object} context
@ -367,6 +398,14 @@
debug: { debug: {
type: Boolean, type: Boolean,
default: false default: false
},
errors: {
type: [String, Array, Boolean],
default: false
},
error: {
type: [String, Boolean],
default: false
} }
}, },
data: function data () { data: function data () {
@ -566,8 +605,11 @@
staticClass: "formulate-input-help", staticClass: "formulate-input-help",
domProps: { textContent: _vm._s(_vm.help) } domProps: { textContent: _vm._s(_vm.help) }
}) })
: _vm._e() : _vm._e(),
] _vm._v(" "),
_c("FormulateInputErrors", { attrs: { errors: _vm.mergedErrors } })
],
1
) )
}; };
var __vue_staticRenderFns__ = []; var __vue_staticRenderFns__ = [];
@ -748,6 +790,87 @@
undefined undefined
); );
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
var script$2 = {
props: {
errors: {
type: [Boolean, Array],
required: true
}
}
};
/* script */
var __vue_script__$2 = script$2;
/* template */
var __vue_render__$2 = function() {
var _vm = this;
var _h = _vm.$createElement;
var _c = _vm._self._c || _h;
return _vm.errors.length
? _c(
"ul",
{ staticClass: "formulate-input-errors" },
_vm._l(_vm.errors, function(error) {
return _c("li", {
key: error,
staticClass: "formulate-input-error",
domProps: { innerHTML: _vm._s(error) }
})
}),
0
)
: _vm._e()
};
var __vue_staticRenderFns__$2 = [];
__vue_render__$2._withStripped = true;
/* style */
var __vue_inject_styles__$2 = undefined;
/* scoped */
var __vue_scope_id__$2 = undefined;
/* module identifier */
var __vue_module_identifier__$2 = undefined;
/* functional template */
var __vue_is_functional_template__$2 = false;
/* style inject */
/* style inject SSR */
/* style inject shadow dom */
var FormulateInputErrors = normalizeComponent(
{ render: __vue_render__$2, staticRenderFns: __vue_staticRenderFns__$2 },
__vue_inject_styles__$2,
__vue_script__$2,
__vue_scope_id__$2,
__vue_is_functional_template__$2,
__vue_module_identifier__$2,
false,
undefined,
undefined,
undefined
);
// //
// //
// //
@ -763,7 +886,7 @@
// //
function objectWithoutProperties (obj, exclude) { var target = {}; for (var k in obj) if (Object.prototype.hasOwnProperty.call(obj, k) && exclude.indexOf(k) === -1) target[k] = obj[k]; return target; } function objectWithoutProperties (obj, exclude) { var target = {}; for (var k in obj) if (Object.prototype.hasOwnProperty.call(obj, k) && exclude.indexOf(k) === -1) target[k] = obj[k]; return target; }
var script$2 = { var script$3 = {
name: 'FormulateInputGroup', name: 'FormulateInputGroup',
props: { props: {
context: { context: {
@ -801,10 +924,10 @@
}; };
/* script */ /* script */
var __vue_script__$2 = script$2; var __vue_script__$3 = script$3;
/* template */ /* template */
var __vue_render__$2 = function() { var __vue_render__$3 = function() {
var _vm = this; var _vm = this;
var _h = _vm.$createElement; var _h = _vm.$createElement;
var _c = _vm._self._c || _h; var _c = _vm._self._c || _h;
@ -836,17 +959,17 @@
1 1
) )
}; };
var __vue_staticRenderFns__$2 = []; var __vue_staticRenderFns__$3 = [];
__vue_render__$2._withStripped = true; __vue_render__$3._withStripped = true;
/* style */ /* style */
var __vue_inject_styles__$2 = undefined; var __vue_inject_styles__$3 = undefined;
/* scoped */ /* scoped */
var __vue_scope_id__$2 = undefined; var __vue_scope_id__$3 = undefined;
/* module identifier */ /* module identifier */
var __vue_module_identifier__$2 = undefined; var __vue_module_identifier__$3 = undefined;
/* functional template */ /* functional template */
var __vue_is_functional_template__$2 = false; var __vue_is_functional_template__$3 = false;
/* style inject */ /* style inject */
/* style inject SSR */ /* style inject SSR */
@ -856,12 +979,12 @@
var FormulateInputGroup = normalizeComponent( var FormulateInputGroup = normalizeComponent(
{ render: __vue_render__$2, staticRenderFns: __vue_staticRenderFns__$2 }, { render: __vue_render__$3, staticRenderFns: __vue_staticRenderFns__$3 },
__vue_inject_styles__$2, __vue_inject_styles__$3,
__vue_script__$2, __vue_script__$3,
__vue_scope_id__$2, __vue_scope_id__$3,
__vue_is_functional_template__$2, __vue_is_functional_template__$3,
__vue_module_identifier__$2, __vue_module_identifier__$3,
false, false,
undefined, undefined,
undefined, undefined,
@ -896,16 +1019,16 @@
// //
var script$3 = { var script$4 = {
name: 'FormulateInputBox', name: 'FormulateInputBox',
mixins: [FormulateInputMixin] mixins: [FormulateInputMixin]
}; };
/* script */ /* script */
var __vue_script__$3 = script$3; var __vue_script__$4 = script$4;
/* template */ /* template */
var __vue_render__$3 = function() { var __vue_render__$4 = function() {
var _vm = this; var _vm = this;
var _h = _vm.$createElement; var _h = _vm.$createElement;
var _c = _vm._self._c || _h; var _c = _vm._self._c || _h;
@ -1034,17 +1157,17 @@
] ]
) )
}; };
var __vue_staticRenderFns__$3 = []; var __vue_staticRenderFns__$4 = [];
__vue_render__$3._withStripped = true; __vue_render__$4._withStripped = true;
/* style */ /* style */
var __vue_inject_styles__$3 = undefined; var __vue_inject_styles__$4 = undefined;
/* scoped */ /* scoped */
var __vue_scope_id__$3 = undefined; var __vue_scope_id__$4 = undefined;
/* module identifier */ /* module identifier */
var __vue_module_identifier__$3 = undefined; var __vue_module_identifier__$4 = undefined;
/* functional template */ /* functional template */
var __vue_is_functional_template__$3 = false; var __vue_is_functional_template__$4 = false;
/* style inject */ /* style inject */
/* style inject SSR */ /* style inject SSR */
@ -1054,12 +1177,12 @@
var FormulateInputBox = normalizeComponent( var FormulateInputBox = normalizeComponent(
{ render: __vue_render__$3, staticRenderFns: __vue_staticRenderFns__$3 }, { render: __vue_render__$4, staticRenderFns: __vue_staticRenderFns__$4 },
__vue_inject_styles__$3, __vue_inject_styles__$4,
__vue_script__$3, __vue_script__$4,
__vue_scope_id__$3, __vue_scope_id__$4,
__vue_is_functional_template__$3, __vue_is_functional_template__$4,
__vue_module_identifier__$3, __vue_module_identifier__$4,
false, false,
undefined, undefined,
undefined, undefined,
@ -1068,16 +1191,16 @@
// //
var script$4 = { var script$5 = {
name: 'FormulateInputText', name: 'FormulateInputText',
mixins: [FormulateInputMixin] mixins: [FormulateInputMixin]
}; };
/* script */ /* script */
var __vue_script__$4 = script$4; var __vue_script__$5 = script$5;
/* template */ /* template */
var __vue_render__$4 = function() { var __vue_render__$5 = function() {
var _vm = this; var _vm = this;
var _h = _vm.$createElement; var _h = _vm.$createElement;
var _c = _vm._self._c || _h; var _c = _vm._self._c || _h;
@ -1193,17 +1316,17 @@
] ]
) )
}; };
var __vue_staticRenderFns__$4 = []; var __vue_staticRenderFns__$5 = [];
__vue_render__$4._withStripped = true; __vue_render__$5._withStripped = true;
/* style */ /* style */
var __vue_inject_styles__$4 = undefined; var __vue_inject_styles__$5 = undefined;
/* scoped */ /* scoped */
var __vue_scope_id__$4 = undefined; var __vue_scope_id__$5 = undefined;
/* module identifier */ /* module identifier */
var __vue_module_identifier__$4 = undefined; var __vue_module_identifier__$5 = undefined;
/* functional template */ /* functional template */
var __vue_is_functional_template__$4 = false; var __vue_is_functional_template__$5 = false;
/* style inject */ /* style inject */
/* style inject SSR */ /* style inject SSR */
@ -1213,12 +1336,12 @@
var FormulateInputText = normalizeComponent( var FormulateInputText = normalizeComponent(
{ render: __vue_render__$4, staticRenderFns: __vue_staticRenderFns__$4 }, { render: __vue_render__$5, staticRenderFns: __vue_staticRenderFns__$5 },
__vue_inject_styles__$4, __vue_inject_styles__$5,
__vue_script__$4, __vue_script__$5,
__vue_scope_id__$4, __vue_scope_id__$5,
__vue_is_functional_template__$4, __vue_is_functional_template__$5,
__vue_module_identifier__$4, __vue_module_identifier__$5,
false, false,
undefined, undefined,
undefined, undefined,
@ -1227,7 +1350,7 @@
// //
var script$5 = { var script$6 = {
name: 'FormulateInputSelect', name: 'FormulateInputSelect',
mixins: [FormulateInputMixin], mixins: [FormulateInputMixin],
computed: { computed: {
@ -1244,10 +1367,10 @@
}; };
/* script */ /* script */
var __vue_script__$5 = script$5; var __vue_script__$6 = script$6;
/* template */ /* template */
var __vue_render__$5 = function() { var __vue_render__$6 = function() {
var _vm = this; var _vm = this;
var _h = _vm.$createElement; var _h = _vm.$createElement;
var _c = _vm._self._c || _h; var _c = _vm._self._c || _h;
@ -1354,17 +1477,17 @@
] ]
) )
}; };
var __vue_staticRenderFns__$5 = []; var __vue_staticRenderFns__$6 = [];
__vue_render__$5._withStripped = true; __vue_render__$6._withStripped = true;
/* style */ /* style */
var __vue_inject_styles__$5 = undefined; var __vue_inject_styles__$6 = undefined;
/* scoped */ /* scoped */
var __vue_scope_id__$5 = undefined; var __vue_scope_id__$6 = undefined;
/* module identifier */ /* module identifier */
var __vue_module_identifier__$5 = undefined; var __vue_module_identifier__$6 = undefined;
/* functional template */ /* functional template */
var __vue_is_functional_template__$5 = false; var __vue_is_functional_template__$6 = false;
/* style inject */ /* style inject */
/* style inject SSR */ /* style inject SSR */
@ -1374,12 +1497,12 @@
var FormulateInputSelect = normalizeComponent( var FormulateInputSelect = normalizeComponent(
{ render: __vue_render__$5, staticRenderFns: __vue_staticRenderFns__$5 }, { render: __vue_render__$6, staticRenderFns: __vue_staticRenderFns__$6 },
__vue_inject_styles__$5, __vue_inject_styles__$6,
__vue_script__$5, __vue_script__$6,
__vue_scope_id__$5, __vue_scope_id__$6,
__vue_is_functional_template__$5, __vue_is_functional_template__$6,
__vue_module_identifier__$5, __vue_module_identifier__$6,
false, false,
undefined, undefined,
undefined, undefined,
@ -1388,16 +1511,16 @@
// //
var script$6 = { var script$7 = {
name: 'FormulateInputTextArea', name: 'FormulateInputTextArea',
mixins: [FormulateInputMixin] mixins: [FormulateInputMixin]
}; };
/* script */ /* script */
var __vue_script__$6 = script$6; var __vue_script__$7 = script$7;
/* template */ /* template */
var __vue_render__$6 = function() { var __vue_render__$7 = function() {
var _vm = this; var _vm = this;
var _h = _vm.$createElement; var _h = _vm.$createElement;
var _c = _vm._self._c || _h; var _c = _vm._self._c || _h;
@ -1437,17 +1560,17 @@
] ]
) )
}; };
var __vue_staticRenderFns__$6 = []; var __vue_staticRenderFns__$7 = [];
__vue_render__$6._withStripped = true; __vue_render__$7._withStripped = true;
/* style */ /* style */
var __vue_inject_styles__$6 = undefined; var __vue_inject_styles__$7 = undefined;
/* scoped */ /* scoped */
var __vue_scope_id__$6 = undefined; var __vue_scope_id__$7 = undefined;
/* module identifier */ /* module identifier */
var __vue_module_identifier__$6 = undefined; var __vue_module_identifier__$7 = undefined;
/* functional template */ /* functional template */
var __vue_is_functional_template__$6 = false; var __vue_is_functional_template__$7 = false;
/* style inject */ /* style inject */
/* style inject SSR */ /* style inject SSR */
@ -1457,12 +1580,12 @@
var FormulateInputTextArea = normalizeComponent( var FormulateInputTextArea = normalizeComponent(
{ render: __vue_render__$6, staticRenderFns: __vue_staticRenderFns__$6 }, { render: __vue_render__$7, staticRenderFns: __vue_staticRenderFns__$7 },
__vue_inject_styles__$6, __vue_inject_styles__$7,
__vue_script__$6, __vue_script__$7,
__vue_scope_id__$6, __vue_scope_id__$7,
__vue_is_functional_template__$6, __vue_is_functional_template__$7,
__vue_module_identifier__$6, __vue_module_identifier__$7,
false, false,
undefined, undefined,
undefined, undefined,
@ -1477,6 +1600,7 @@
components: { components: {
FormulateForm: FormulateForm, FormulateForm: FormulateForm,
FormulateInput: FormulateInput, FormulateInput: FormulateInput,
FormulateInputErrors: FormulateInputErrors,
FormulateInputBox: FormulateInputBox, FormulateInputBox: FormulateInputBox,
FormulateInputText: FormulateInputText, FormulateInputText: FormulateInputText,
FormulateInputGroup: FormulateInputGroup, FormulateInputGroup: FormulateInputGroup,

10
dist/snow.css vendored
View File

@ -15,6 +15,16 @@
font-weight: 300; font-weight: 300;
line-height: 1.5; line-height: 1.5;
margin-bottom: .25em; } margin-bottom: .25em; }
.formulate-input .formulate-input-errors {
list-style-type: none;
padding: 0;
margin: 0; }
.formulate-input .formulate-input-error {
color: #960505;
font-size: .8em;
font-weight: 300;
line-height: 1.5;
margin-bottom: .25em; }
.formulate-input .formulate-input-group-item { .formulate-input .formulate-input-group-item {
margin-bottom: .5em; } margin-bottom: .5em; }
.formulate-input:last-child { .formulate-input:last-child {

4
dist/snow.min.css vendored

File diff suppressed because one or more lines are too long

View File

@ -2,6 +2,7 @@ import library from './libs/library'
import isPlainObject from 'is-plain-object' import isPlainObject from 'is-plain-object'
import FormulateInput from './FormulateInput.vue' import FormulateInput from './FormulateInput.vue'
import FormulateForm from './FormulateForm.vue' import FormulateForm from './FormulateForm.vue'
import FormulateInputErrors from './FormulateInputErrors.vue'
import FormulateInputGroup from './FormulateInputGroup.vue' import FormulateInputGroup from './FormulateInputGroup.vue'
import FormulateInputBox from './inputs/FormulateInputBox.vue' import FormulateInputBox from './inputs/FormulateInputBox.vue'
import FormulateInputText from './inputs/FormulateInputText.vue' import FormulateInputText from './inputs/FormulateInputText.vue'
@ -19,6 +20,7 @@ class Formulate {
components: { components: {
FormulateForm, FormulateForm,
FormulateInput, FormulateInput,
FormulateInputErrors,
FormulateInputBox, FormulateInputBox,
FormulateInputText, FormulateInputText,
FormulateInputGroup, FormulateInputGroup,

View File

@ -2,6 +2,7 @@
<div <div
class="formulate-input" class="formulate-input"
:data-classification="classification" :data-classification="classification"
:data-has-errors="hasErrors"
:data-type="type" :data-type="type"
> >
<div class="formulate-input-wrapper"> <div class="formulate-input-wrapper">
@ -39,6 +40,9 @@
class="formulate-input-help" class="formulate-input-help"
v-text="help" v-text="help"
/> />
<FormulateInputErrors
:errors="mergedErrors"
/>
</div> </div>
</template> </template>
@ -102,6 +106,25 @@ export default {
debug: { debug: {
type: Boolean, type: Boolean,
default: false default: false
},
errors: {
type: [String, Array, Boolean],
default: false
},
validation: {
type: [String, Boolean, Array],
default: false
},
validationBehavior: {
type: String,
default: 'blur',
validator: function (value) {
return ['blur', 'live'].includes(value)
}
},
error: {
type: [String, Boolean],
default: false
} }
}, },
data () { data () {

View File

@ -0,0 +1,26 @@
<template>
<ul
v-if="errors.length"
class="formulate-input-errors"
>
<!-- eslint-disable -->
<li
v-for="error in errors"
:key="error"
v-html="error"
class="formulate-input-error"
/>
<!-- eslint-enable -->
</ul>
</template>
<script>
export default {
props: {
errors: {
type: [Boolean, Array],
required: true
}
}
}
</script>

View File

@ -1,5 +1,5 @@
import nanoid from 'nanoid' import nanoid from 'nanoid'
import { map } from './utils' import { map, arrayify, parseRules } from './utils'
/** /**
* For a single instance of an input, export all of the context needed to fully * For a single instance of an input, export all of the context needed to fully
@ -28,7 +28,10 @@ export default {
typeContext, typeContext,
elementAttributes, elementAttributes,
logicalLabelPosition, logicalLabelPosition,
isVmodeled isVmodeled,
mergedErrors,
hasErrors,
validationRules
} }
/** /**
@ -129,6 +132,30 @@ function createOptionList (options) {
return options return options
} }
/**
* The merged errors computed property.
*/
function mergedErrors () {
return arrayify(this.errors)
.concat(arrayify(this.error))
.concat(arrayify(this.validationErrors))
.reduce((errors, err) => !errors.includes(err) ? errors.concat(err) : errors, [])
}
/**
* Does this computed property have errors.
*/
function hasErrors () {
return !!this.mergedErrors.length
}
/**
* An array of validation rules to pass.
*/
function validationRules () {
return parseRules(this.validation)
}
/** /**
* Defines the model used throughout the existing context. * Defines the model used throughout the existing context.
* @param {object} context * @param {object} context

41
src/libs/rules.js Normal file
View File

@ -0,0 +1,41 @@
import { shallowEqualObjects } from './utils'
/**
* Library of rules
*/
export default {
/**
* Rule: must be a value
*/
required: async function (value) {
if (Array.isArray(value)) {
return !!value.length
}
if (typeof value === 'string') {
return !!value
}
if (typeof value === 'object') {
return (!value) ? false : !!Object.keys(value).length
}
return true
},
/**
* Rule: Value is in an array (stack).
*/
in: async function (value, ...stack) {
return !!stack.find(item => shallowEqualObjects(item, value))
},
/**
* Rule: Match the value against a (stack) of patterns or strings
*/
matches: async function (value, ...stack) {
return !!stack.find(pattern => {
if (pattern instanceof RegExp) {
return pattern.test(value)
}
return pattern === value
})
}
}

View File

@ -69,3 +69,68 @@ export function shallowEqualObjects (objA, objB) {
} }
return true return true
} }
/**
* Given a string, object, falsey, or array - return an array.
* @param {mixed} item
*/
export function arrayify (item) {
if (!item) {
return []
}
if (typeof item === 'string') {
return [item]
}
if (Array.isArray(item)) {
return item
}
if (typeof item === 'object') {
return Object.values(item)
}
return []
}
/**
* Given an array or string return an array of callables.
* @param {array|string} validation
* @param {array} rules and array of functions
* @return {array} an array of functions
*/
export function parseRules (validation, rules) {
if (typeof validation === 'string') {
return parseRules(validation.split('|'), rules)
}
if (!Array.isArray(validation)) {
return []
}
return validation.map(rule => parseRule(rule, rules)).filter(f => !!f)
}
/**
* Given a string or function, parse it and return the an array in the format
* [fn, [...arguments]]
* @param {string|function} rule
*/
function parseRule (rule, rules) {
if (typeof rule === 'function') {
return [rule, []]
}
if (Array.isArray(rule) && rule.length) {
if (typeof rule[0] === 'string' && rules.hasOwnProperty(rule[0])) {
return [rules[rule.shift()], rule]
}
if (typeof rule[0] === 'function') {
return [rule.shift(), rule]
}
}
if (typeof rule === 'string') {
const segments = rule.split(':')
const functionName = segments.shift()
if (rules.hasOwnProperty(functionName)) {
return [rules[functionName], segments.length ? segments.join(':').split(',') : []]
} else {
throw new Error(`Unknown validation rule ${rule}`)
}
}
return false
}

View File

@ -50,6 +50,7 @@ test('installs on vue instance', () => {
const components = [ const components = [
'FormulateForm', 'FormulateForm',
'FormulateInput', 'FormulateInput',
'FormulateInputErrors',
'FormulateInputBox', 'FormulateInputBox',
'FormulateInputText', 'FormulateInputText',
'FormulateInputGroup', 'FormulateInputGroup',

View File

@ -11,113 +11,114 @@ Vue.use(Formulate)
* Test each type of text element * Test each type of text element
*/ */
test('type "text" renders a text input', () => { describe('FormulateInputText', () => {
it('renders text input when type is "text"', () => {
const wrapper = mount(FormulateInput, { propsData: { type: 'text' } }) const wrapper = mount(FormulateInput, { propsData: { type: 'text' } })
expect(wrapper.contains(FormulateInputText)).toBe(true) expect(wrapper.contains(FormulateInputText)).toBe(true)
}) })
test('type "search" renders a text input', () => { it('renders search input when type is "search"', () => {
const wrapper = mount(FormulateInput, { propsData: { type: 'search' } }) const wrapper = mount(FormulateInput, { propsData: { type: 'search' } })
expect(wrapper.contains(FormulateInputText)).toBe(true) expect(wrapper.contains(FormulateInputText)).toBe(true)
}) })
test('type "email" renders a text input', () => { it('renders email input when type is "email"', () => {
const wrapper = mount(FormulateInput, { propsData: { type: 'email' } }) const wrapper = mount(FormulateInput, { propsData: { type: 'email' } })
expect(wrapper.contains(FormulateInputText)).toBe(true) expect(wrapper.contains(FormulateInputText)).toBe(true)
}) })
test('type "number" renders a text input', () => { it('renders number input when type is "number"', () => {
const wrapper = mount(FormulateInput, { propsData: { type: 'number' } }) const wrapper = mount(FormulateInput, { propsData: { type: 'number' } })
expect(wrapper.contains(FormulateInputText)).toBe(true) expect(wrapper.contains(FormulateInputText)).toBe(true)
}) })
test('type "color" renders a text input', () => { it('renders color input when type is "color"', () => {
const wrapper = mount(FormulateInput, { propsData: { type: 'color' } }) const wrapper = mount(FormulateInput, { propsData: { type: 'color' } })
expect(wrapper.contains(FormulateInputText)).toBe(true) expect(wrapper.contains(FormulateInputText)).toBe(true)
}) })
test('type "date" renders a text input', () => { it('renders date input when type is "date"', () => {
const wrapper = mount(FormulateInput, { propsData: { type: 'date' } }) const wrapper = mount(FormulateInput, { propsData: { type: 'date' } })
expect(wrapper.contains(FormulateInputText)).toBe(true) expect(wrapper.contains(FormulateInputText)).toBe(true)
}) })
test('type "month" renders a text input', () => { it('renders month input when type is "month"', () => {
const wrapper = mount(FormulateInput, { propsData: { type: 'month' } }) const wrapper = mount(FormulateInput, { propsData: { type: 'month' } })
expect(wrapper.contains(FormulateInputText)).toBe(true) expect(wrapper.contains(FormulateInputText)).toBe(true)
}) })
test('type "password" renders a text input', () => { it('renders password input when type is "password"', () => {
const wrapper = mount(FormulateInput, { propsData: { type: 'password' } }) const wrapper = mount(FormulateInput, { propsData: { type: 'password' } })
expect(wrapper.contains(FormulateInputText)).toBe(true) expect(wrapper.contains(FormulateInputText)).toBe(true)
}) })
test('type "range" renders a text input', () => { it('renders range input when type is "range"', () => {
const wrapper = mount(FormulateInput, { propsData: { type: 'range' } }) const wrapper = mount(FormulateInput, { propsData: { type: 'range' } })
expect(wrapper.contains(FormulateInputText)).toBe(true) expect(wrapper.contains(FormulateInputText)).toBe(true)
}) })
test('type "tel" renders a text input', () => { it('renders tel input when type is "tel"', () => {
const wrapper = mount(FormulateInput, { propsData: { type: 'tel' } }) const wrapper = mount(FormulateInput, { propsData: { type: 'tel' } })
expect(wrapper.contains(FormulateInputText)).toBe(true) expect(wrapper.contains(FormulateInputText)).toBe(true)
}) })
test('type "time" renders a text input', () => { it('renders time input when type is "time"', () => {
const wrapper = mount(FormulateInput, { propsData: { type: 'time' } }) const wrapper = mount(FormulateInput, { propsData: { type: 'time' } })
expect(wrapper.contains(FormulateInputText)).toBe(true) expect(wrapper.contains(FormulateInputText)).toBe(true)
}) })
test('type "url" renders a text input', () => { it('renders url input when type is "url"', () => {
const wrapper = mount(FormulateInput, { propsData: { type: 'url' } }) const wrapper = mount(FormulateInput, { propsData: { type: 'url' } })
expect(wrapper.contains(FormulateInputText)).toBe(true) expect(wrapper.contains(FormulateInputText)).toBe(true)
}) })
test('type "week" renders a text input', () => { it('renders week input when type is "week"', () => {
const wrapper = mount(FormulateInput, { propsData: { type: 'week' } }) const wrapper = mount(FormulateInput, { propsData: { type: 'week' } })
expect(wrapper.contains(FormulateInputText)).toBe(true) expect(wrapper.contains(FormulateInputText)).toBe(true)
}) })
/** /**
* Test rendering functionality to text inputs * Test rendering functionality to text inputs
*/ */
test('text inputs automatically have id assigned', () => { it('automatically assigns an id', () => {
const wrapper = mount(FormulateInput, { propsData: { type: 'text' } }) const wrapper = mount(FormulateInput, { propsData: { type: 'text' } })
expect(wrapper.vm.context).toHaveProperty('id') expect(wrapper.vm.context).toHaveProperty('id')
expect(wrapper.find(`input[id="${wrapper.vm.context.attributes.id}"]`).exists()).toBe(true) expect(wrapper.find(`input[id="${wrapper.vm.context.attributes.id}"]`).exists()).toBe(true)
}) })
test('text inputs dont have labels', () => { it('doesnt automatically add a label', () => {
const wrapper = mount(FormulateInput, { propsData: { type: 'text' } }) const wrapper = mount(FormulateInput, { propsData: { type: 'text' } })
expect(wrapper.find('label').exists()).toBe(false) expect(wrapper.find('label').exists()).toBe(false)
}) })
test('text inputs can have labels', () => { it('renders labels', () => {
const wrapper = mount(FormulateInput, { propsData: { type: 'text', label: 'Field label' } }) const wrapper = mount(FormulateInput, { propsData: { type: 'text', label: 'Field label' } })
expect(wrapper.find(`label[for="${wrapper.vm.context.attributes.id}"]`).exists()).toBe(true) expect(wrapper.find(`label[for="${wrapper.vm.context.attributes.id}"]`).exists()).toBe(true)
}) })
test('text inputs dont have help text', () => { it('doesnt automatically render help text', () => {
const wrapper = mount(FormulateInput, { propsData: { type: 'text' } }) const wrapper = mount(FormulateInput, { propsData: { type: 'text' } })
expect(wrapper.find(`.formulate-input-help`).exists()).toBe(false) expect(wrapper.find(`.formulate-input-help`).exists()).toBe(false)
}) })
test('text inputs dont have help text', () => { it('renders help text', () => {
const wrapper = mount(FormulateInput, { propsData: { type: 'text', help: 'This is some help text' } }) const wrapper = mount(FormulateInput, { propsData: { type: 'text', help: 'This is some help text' } })
expect(wrapper.find(`.formulate-input-help`).exists()).toBe(true) expect(wrapper.find(`.formulate-input-help`).exists()).toBe(true)
}) })
/** /**
* Test data binding * Test data binding
*/ */
test('text inputs emit input (vmodel) event with value when edited', () => { it('emits input (vmodel) event with value when edited', () => {
const wrapper = mount(FormulateInput, { propsData: { type: 'text' } }) const wrapper = mount(FormulateInput, { propsData: { type: 'text' } })
wrapper.find('input').setValue('Updated Value') wrapper.find('input').setValue('Updated Value')
expect(wrapper.emitted().input).toBeTruthy() expect(wrapper.emitted().input).toBeTruthy()
expect(wrapper.emitted().input[0]).toEqual(['Updated Value']) expect(wrapper.emitted().input[0]).toEqual(['Updated Value'])
}) })
test('test that inputs that arent updated dont re-context themselves', () => { it('doesnt re-context itself if there were no changes', () => {
const wrapper = mount({ const wrapper = mount({
data () { data () {
return { return {
@ -140,16 +141,46 @@ test('test that inputs that arent updated dont re-context themselves', () => {
expect(wrapper.vm.valueB === 'second value').toBe(true) expect(wrapper.vm.valueB === 'second value').toBe(true)
expect(wrapper.find({ref: "first"}).vm.context === firstContext).toBe(false) expect(wrapper.find({ref: "first"}).vm.context === firstContext).toBe(false)
expect(wrapper.find({ref: "second"}).vm.context === secondContext).toBe(true) expect(wrapper.find({ref: "second"}).vm.context === secondContext).toBe(true)
}) })
test('test that inputs contain their v-model value as the initial input', () => { it('uses the v-model value as the initial value', () => {
const wrapper = mount(FormulateInput, { propsData: { type: 'text', formulateValue: 'initial val' } }) const wrapper = mount(FormulateInput, { propsData: { type: 'text', formulateValue: 'initial val' } })
expect(wrapper.find('input').element.value).toBe('initial val') expect(wrapper.find('input').element.value).toBe('initial val')
}) })
test('test that inputs without v-model set a proxy model', () => { it('uses a proxy model internally if it doesnt have a v-model', () => {
const wrapper = mount(FormulateInput, { propsData: { type: 'textarea' } }) const wrapper = mount(FormulateInput, { propsData: { type: 'textarea' } })
const input = wrapper.find('textarea') const input = wrapper.find('textarea')
input.setValue('changed value') input.setValue('changed value')
expect(wrapper.vm.internalModelProxy).toBe('changed value') expect(wrapper.vm.internalModelProxy).toBe('changed value')
})
/**
* Test error handling
*/
it('doesnt automatically render errors', () => {
const wrapper = mount(FormulateInput, { propsData: { type: 'text' } })
expect(wrapper.find('.formulate-input-errors').exists()).toBe(false)
})
it('accepts a single string as an error prop', () => {
const wrapper = mount(FormulateInput, { propsData: { type: 'text', error: 'This is an error' } })
expect(wrapper.find('.formulate-input-errors').exists()).toBe(true)
})
it('accepts an array as errors prop', () => {
const wrapper = mount(FormulateInput, { propsData: { type: 'text', errors: ['This is an error', 'this is another'] } })
expect(wrapper.findAll('.formulate-input-error').length).toBe(2)
})
it('removes any duplicate errors', () => {
const wrapper = mount(FormulateInput, { propsData: { type: 'text', errors: ['This is an error', 'This is an error'] } })
expect(wrapper.findAll('.formulate-input-error').length).toBe(1)
})
it('adds data-has-errors when there are errors', () => {
const wrapper = mount(FormulateInput, { propsData: { type: 'text', errors: ['This is an error', 'This is an error'] } })
expect(wrapper.find('[data-has-errors]').exists()).toBe(true)
})
}) })

77
test/rules.test.js Normal file
View File

@ -0,0 +1,77 @@
import rules from '@/libs/rules'
/**
* Required rule
*/
describe('required', () => {
it('fails on empty string', async () => expect(await rules.required('')).toBe(false))
it('fails on empty array', async () => expect(await rules.required([])).toBe(false))
it('fails on empty object', async () => expect(await rules.required({})).toBe(false))
it('fails on null', async () => expect(await rules.required(null)).toBe(false))
it('passes with the number zero', async () => expect(await rules.required(0)).toBe(true))
it('passes with the boolean false', async () => expect(await rules.required(false)).toBe(true))
it('passes with a non empty array', async () => expect(await rules.required(['123'])).toBe(true))
it('passes with a non empty object', async () => expect(await rules.required({a: 'b'})).toBe(true))
})
/**
* In rule
*/
describe('in', () => {
it('fails when not in stack', async () => {
expect(await rules.in('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)
})
it('fails comparing dissimilar objects', async () => {
expect(await rules.in({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)
})
it('passes a shallow array compare', async () => {
expect(await rules.in(['abc'], ['cdf'], ['abc'])).toBe(true)
})
it('passes a shallow object compare', async () => {
expect(await rules.in({f: 'abc'}, {a: 'cdf'}, {f: 'abc'},)).toBe(true)
})
})
/**
* Matches rule
*/
describe('matches', () => {
it('simple strings fail if they arent equal', async () => {
expect(await rules.matches('third', 'first')).toBe(false)
})
it('fails on non matching regex', async () => {
expect(await rules.matches('third', /^thirds/)).toBe(false)
})
it('passes if simple strings match', async () => {
expect(await rules.matches('second', 'third', 'second')).toBe(true)
})
it('passes on matching regex', async () => {
expect(await rules.matches('third', /^third/)).toBe(true)
})
it('passes on matching mixed regex and string', async () => {
expect(await rules.matches('first-fourth', 'second', /^third/, /fourth$/)).toBe(true)
})
})

44
test/utils.test.js Normal file
View File

@ -0,0 +1,44 @@
import { parseRules } from '@/libs/utils'
import rules from '@/libs/rules'
describe('parseRules', () => {
it('parses single string rules, returning empty arguments array', () => {
expect(parseRules('required', rules)).toEqual([
[rules.required, []]
])
})
it('throws errors for invalid validation rules', () => {
expect(() => {
parseRules('required|notarule', rules)
}).toThrow()
})
it('parses arguments for a rule', () => {
expect(parseRules('in:foo,bar', rules)).toEqual([
[rules.in, ['foo', 'bar']]
])
})
it('parses multiple string rules and arguments', () => {
expect(parseRules('required|in:foo,bar', rules)).toEqual([
[rules.required, []],
[rules.in, ['foo', 'bar']]
])
})
it('parses multiple array rules and arguments', () => {
expect(parseRules(['required', 'in:foo,bar'], rules)).toEqual([
[rules.required, []],
[rules.in, ['foo', 'bar']]
])
})
it('parses array rules with expression arguments', () => {
expect(parseRules([
['matches', /^abc/, '1234']
], rules)).toEqual([
[rules.matches, [/^abc/, '1234']]
])
})
})

View File

@ -25,6 +25,20 @@
margin-bottom: .25em; margin-bottom: .25em;
} }
.formulate-input-errors {
list-style-type: none;
padding: 0;
margin: 0;
}
.formulate-input-error {
color: $formulate-error;
font-size: .8em;
font-weight: 300;
line-height: 1.5;
margin-bottom: .25em;
}
.formulate-input-group-item { .formulate-input-group-item {
margin-bottom: .5em; margin-bottom: .5em;
} }

View File

@ -15,6 +15,8 @@ $formulate-blue-l: #f3f4f4;
$formulate-green: #41b883; $formulate-green: #41b883;
$formulate-error: #960505;
$formulate-yellow-d: #6b5900; $formulate-yellow-d: #6b5900;
$formulate-yellow: #e6c000; $formulate-yellow: #e6c000;
$formulate-yellow-l: #fff8d2; $formulate-yellow-l: #fff8d2;