1
0
mirror of synced 2024-11-22 05:16:05 +03:00

style: Addition of the eslint rules

This commit is contained in:
Zaytsev Kirill 2020-10-09 22:58:28 +03:00
parent 095f406fea
commit acb6357433
25 changed files with 754 additions and 555 deletions

View File

@ -1,22 +1,42 @@
module.exports = {
root: true,
parserOptions: {
parser: 'babel-eslint',
sourceType: 'module'
sourceType: 'module',
},
// https://github.com/feross/standard/blob/master/RULES.md#javascript-standard-style
extends: [
'standard',
'plugin:vue/recommended'
'@vue/standard',
'plugin:vue/recommended',
],
env: {
browser: true,
},
// add your custom rules here
'rules': {
rules: {
// allow paren-less arrow functions
'arrow-parens': 0,
'comma-dangle': ['error', 'only-multiline'],
'indent': ['error', 4],
'max-depth': ['error', 3],
'max-lines-per-function': ['error', 40],
'no-console': ['warn', {allow: ['warn', 'error']}],
// allow debugger during development
'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0
}
'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0,
'vue/html-closing-bracket-spacing': ['error', {
startTag: 'never',
endTag: 'never',
selfClosingTag: 'always',
}],
'vue/html-indent': ['error', 4, {
attribute: 1,
closeBracket: 0,
alignAttributesVertically: true,
ignores: [],
}],
},
}

View File

@ -26,6 +26,7 @@ import SpecimenSelect from './specimens/SpecimenSelect'
export default {
name: 'App',
components: {
SpecimenButton,
SpecimenBox,
@ -34,21 +35,22 @@ export default {
SpecimenGroup,
SpecimenFile,
SpecimenSlider,
SpecimenSelect
},
data () {
return {
}
SpecimenSelect,
}
}
</script>
<style lang="scss">
$formulate-font-stack: Arial, sans-serif;
$formulate-gray: gray;
$formulate-green: green;
body {
margin: 0;
padding: 0;
font-family: $formulate-font-stack;
}
h2 {
display: block;
width: 100%;
@ -61,6 +63,7 @@ h2 {
margin: 2em 0 0 0;
z-index: 10;
}
.specimen-list {
padding: 1em;
max-width: 1200px;
@ -68,8 +71,8 @@ h2 {
@media (min-width: 756px) {
padding: 2em;
}
}
.specimens {
@media (min-width: 500px) {
display: flex;

View File

@ -1,10 +1,9 @@
import Vue from 'vue'
import VueFormulate from '../src/Formulate'
import VueFormulario from '../src/Formulario'
import FormulateSpecimens from './FormulateSpecimens.vue'
Vue.config.productionTip = false
Vue.use(VueFormulate)
Vue.use(VueFormulario)
new Vue({
render: h => h(FormulateSpecimens)

View File

@ -47,9 +47,3 @@
</div>
</div>
</template>
<script>
export default {
}
</script>

View File

@ -51,6 +51,7 @@
"@vue/cli-plugin-eslint": "^4.3.1",
"@vue/cli-service": "^4.5.4",
"@vue/component-compiler-utils": "^3.1.2",
"@vue/eslint-config-standard": "^5.1.2",
"@vue/test-utils": "^1.0.2",
"autoprefixer": "^9.7.6",
"babel-core": "^7.0.0-bridge.0",

View File

@ -1,5 +1,6 @@
import nanoid from 'nanoid/non-secure'
// noinspection JSUnusedGlobalSymbols
/**
* The file upload class holds and represents a files upload state durring
* the upload flow.
@ -7,8 +8,9 @@ import nanoid from 'nanoid/non-secure'
class FileUpload {
/**
* Create a file upload object.
* @param {FileList} fileList
* @param {FileList} input
* @param {object} context
* @param {object} options
*/
constructor (input, context, options = {}) {
this.input = input
@ -27,7 +29,6 @@ class FileUpload {
/**
* Given a pre-existing array of files, create a faux FileList.
* @param {array} items expects an array of objects [{ url: '/uploads/file.pdf' }]
* @param {string} pathKey the object-key to access the url (defaults to "url")
*/
rehydrateFileList (items) {
const fauxFileList = items.reduce((fileList, item) => {
@ -48,15 +49,12 @@ class FileUpload {
/**
* Produce an array of files and alert the callback.
* @param {FileList}
* @param {FileList} fileList
*/
addFileList (fileList) {
for (let i = 0; i < fileList.length; i++) {
const file = fileList[i]
const uuid = nanoid()
const removeFile = function () {
this.removeFile(uuid)
}
this.files.push({
progress: false,
error: false,
@ -66,7 +64,7 @@ class FileUpload {
file,
uuid,
path: false,
removeFile: removeFile.bind(this),
removeFile: () => this.removeFile(uuid),
previewData: file.previewData || false
})
}
@ -86,16 +84,11 @@ class FileUpload {
* https://github.com/axios/axios/issues/737
*/
uploaderIsAxios () {
if (
this.hasUploader &&
return this.hasUploader &&
typeof this.context.uploader.request === 'function' &&
typeof this.context.uploader.get === 'function' &&
typeof this.context.uploader.delete === 'function' &&
typeof this.context.uploader.post === 'function'
) {
return true
}
return false
}
/**

View File

@ -3,13 +3,15 @@ import rules from './libs/rules'
import mimes from './libs/mimes'
import FileUpload from './FileUpload'
import RuleValidationMessages from './RuleValidationMessages'
import { arrayify, parseLocale, has } from './libs/utils'
import { arrayify } from './libs/utils'
import isPlainObject from 'is-plain-object'
import fauxUploader from './libs/faux-uploader'
import FormularioForm from './FormularioForm.vue'
import FormularioInput from './FormularioInput.vue'
import FormularioGrouping from './FormularioGrouping.vue'
// noinspection JSUnusedGlobalSymbols
/**
* The base formulario library.
*/
@ -23,7 +25,7 @@ class Formulario {
components: {
FormularioForm,
FormularioInput,
FormularioGrouping,
FormularioGrouping
},
library,
rules,
@ -34,7 +36,7 @@ class Formulario {
fileUrlKey: 'url',
uploadJustCompleteDuration: 1000,
errorHandler: (err) => err,
plugins: [ RuleValidationMessages ],
plugins: [RuleValidationMessages],
validationMessages: {},
idPrefix: 'formulario-'
}
@ -48,16 +50,18 @@ class Formulario {
install (Vue, options) {
Vue.prototype.$formulario = this
this.options = this.defaults
var plugins = this.defaults.plugins
let plugins = this.defaults.plugins
if (options && Array.isArray(options.plugins) && options.plugins.length) {
plugins = plugins.concat(options.plugins)
}
plugins.forEach(plugin => (typeof plugin === 'function') ? plugin(this) : null)
this.extend(options || {})
for (var componentName in this.options.components) {
for (const componentName in this.options.components) {
if (Object.prototype.hasOwnProperty.call(this.options.components, componentName)) {
Vue.component(componentName, this.options.components[componentName])
}
}
}
/**
* Produce a deterministically generated id based on the sequence by which it
@ -95,9 +99,10 @@ class Formulario {
* @param {boolean} concatArrays
*/
merge (base, mergeWith, concatArrays = true) {
var merged = {}
for (var key in base) {
if (mergeWith.hasOwnProperty(key)) {
const merged = {}
for (const key in base) {
if (Object.prototype.hasOwnProperty.call(mergeWith, key)) {
if (isPlainObject(mergeWith[key]) && isPlainObject(base[key])) {
merged[key] = this.merge(base[key], mergeWith[key], concatArrays)
} else if (concatArrays && Array.isArray(base[key]) && Array.isArray(mergeWith[key])) {
@ -109,11 +114,13 @@ class Formulario {
merged[key] = base[key]
}
}
for (var prop in mergeWith) {
if (!merged.hasOwnProperty(prop)) {
for (const prop in mergeWith) {
if (!Object.prototype.hasOwnProperty.call(merged, prop)) {
merged[prop] = mergeWith[prop]
}
}
return merged
}
@ -122,9 +129,10 @@ class Formulario {
* @param {string} type
*/
classify (type) {
if (this.options.library.hasOwnProperty(type)) {
if (Object.prototype.hasOwnProperty.call(this.options.library, type)) {
return this.options.library[type].classification
}
return 'unknown'
}
@ -133,9 +141,10 @@ class Formulario {
* @param {string} type
*/
component (type) {
if (this.options.library.hasOwnProperty(type)) {
if (Object.prototype.hasOwnProperty.call(this.options.library, type)) {
return this.options.library[type].component
}
return false
}
@ -151,16 +160,16 @@ class Formulario {
* Get the validation message for a particular error.
*/
validationMessage (rule, validationContext, vm) {
if (this.options.validationMessages.hasOwnProperty(rule)) {
if (Object.prototype.hasOwnProperty.call(this.options.validationMessages, rule)) {
return this.options.validationMessages[rule](vm, validationContext)
} else {
return this.options.validationMessages['default'](vm, validationContext)
return this.options.validationMessages.default(vm, validationContext)
}
}
/**
* Given an instance of a FormularioForm register it.
* @param {vm} form
* @param {Vue} form
*/
register (form) {
if (form.$options.name === 'FormularioForm' && form.name) {
@ -170,7 +179,7 @@ class Formulario {
/**
* Given an instance of a form, remove it from the registry.
* @param {vm} form
* @param {Vue} form
*/
deregister (form) {
if (
@ -188,7 +197,7 @@ class Formulario {
*
* @param {error} err
* @param {string} formName
* @param {error}
* @param {boolean} skip
*/
handle (err, formName, skip = false) {
const e = skip ? err : this.options.errorHandler(err, formName)

View File

@ -16,13 +16,16 @@
>
<img
:src="file.previewData"
alt=""
>
</div>
<div
class="formulario-file-name"
:title="file.name"
v-text="file.name"
/>
<div
v-if="file.progress !== false"
:data-just-finished="file.justFinished"
@ -34,12 +37,14 @@
:style="{width: file.progress + '%'}"
/>
</div>
<div
v-if="(file.complete && !file.justFinished) || file.progress === false"
class="formulario-file-remove"
@click="file.removeFile"
/>
</div>
<div
v-if="file.error"
class="formulario-file-upload-error"
@ -54,21 +59,25 @@ import FileUpload from './FileUpload'
export default {
name: 'FormularioFiles',
props: {
files: {
type: FileUpload,
required: true
},
imagePreview: {
type: Boolean,
default: false
}
},
computed: {
fileUploads () {
return this.files.files || []
}
},
watch: {
files () {
if (this.imagePreview) {
@ -76,6 +85,7 @@ export default {
}
}
},
mounted () {
if (this.imagePreview) {
this.files.loadPreviews()

View File

@ -13,6 +13,8 @@ import useRegistry, { useRegistryComputed, useRegistryMethods, useRegistryProvid
import FormSubmission from './FormSubmission'
export default {
name: 'FormularioForm',
provide () {
return {
...useRegistryProviders(this),
@ -22,33 +24,39 @@ export default {
path: ''
}
},
name: 'FormularioForm',
model: {
prop: 'formularioValue',
event: 'input'
},
props: {
name: {
type: [String, Boolean],
default: false
},
formularioValue: {
type: Object,
default: () => ({})
},
values: {
type: [Object, Boolean],
default: false
},
errors: {
type: [Object, Boolean],
default: false
},
formErrors: {
type: Array,
default: () => ([])
}
},
data () {
return {
...useRegistry(this),
@ -58,51 +66,58 @@ export default {
namedFieldErrors: {}
}
},
computed: {
...useRegistryComputed(),
classes () {
const classes = { 'formulario-form': true }
if (this.name) {
classes[`formulario-form--${this.name}`] = true
return {
'formulario-form': true,
[`formulario-form--${this.name}`]: !!this.name
}
return classes
},
mergedFormErrors () {
return this.formErrors.concat(this.namedErrors)
},
mergedFieldErrors () {
const errors = {}
if (this.errors) {
for (const fieldName in this.errors) {
errors[fieldName] = arrayify(this.errors[fieldName])
}
}
for (const fieldName in this.namedFieldErrors) {
errors[fieldName] = arrayify(this.namedFieldErrors[fieldName])
}
return errors
},
hasFormErrorObservers () {
return !!this.errorObservers.filter(o => o.type === 'form').length
}
},
watch: {
formularioValue: {
handler (values) {
if (this.isVmodeled &&
values &&
typeof values === 'object'
) {
if (this.isVmodeled && values && typeof values === 'object') {
this.setValues(values)
}
},
deep: true
},
mergedFormErrors (errors) {
this.errorObservers
.filter(o => o.type === 'form')
.forEach(o => o.callback(errors))
},
mergedFieldErrors: {
handler (errors) {
this.errorObservers
@ -112,20 +127,25 @@ export default {
immediate: true
}
},
created () {
this.$formulario.register(this)
this.applyInitialValues()
},
destroyed () {
this.$formulario.deregister(this)
},
methods: {
...useRegistryMethods(),
applyErrors ({ formErrors, inputErrors }) {
// given an object of errors, apply them to this form
this.namedErrors = formErrors
this.namedFieldErrors = inputErrors
},
addErrorObserver (observer) {
if (!this.errorObservers.find(obs => observer.callback === obs.callback)) {
this.errorObservers.push(observer)
@ -136,14 +156,17 @@ export default {
}
}
},
removeErrorObserver (observer) {
this.errorObservers = this.errorObservers.filter(obs => obs.callback !== observer)
},
registerErrorComponent (component) {
if (!this.errorComponents.includes(component)) {
this.errorComponents.push(component)
}
},
formSubmitted () {
// perform validation here
this.showErrors()
@ -159,6 +182,7 @@ export default {
return undefined
})
},
formularioFieldValidation (errorObject) {
this.$emit('validation', errorObject)
}

View File

@ -10,33 +10,38 @@
<script>
export default {
name: 'FormularioGrouping',
props: {
name: {
type: String,
required: true
},
isArrayItem: {
type: Boolean,
default: false
},
},
provide () {
return {
path: this.groupPath
}
},
inject: ['path'],
props: {
name: {
type: String,
required: true
},
isArrayItem: {
type: Boolean,
default: false
}
},
computed: {
groupPath () {
if (this.isArrayItem) {
return this.path + '[' + this.name + ']';
} else {
if (this.path === '') {
return this.name;
return `${this.path}[${this.name}]`
}
return this.path + '.' + this.name;
if (this.path === '') {
return this.name
}
return `${this.path}.${this.name}`
}
}
}

View File

@ -5,7 +5,12 @@
:data-is-showing-errors="hasVisibleErrors"
:data-type="type"
>
<slot :id="id" :context="context" :errors="errors" :validationErrors="validationErrors" />
<slot
:id="id"
:context="context"
:errors="errors"
:validationErrors="validationErrors"
/>
</div>
</template>
@ -15,7 +20,9 @@ import { shallowEqualObjects, parseRules, snakeToCamel, has, arrayify, groupBail
export default {
name: 'FormularioInput',
inheritAttrs: false,
provide () {
return {
// Allows sub-components of this input to register arbitrary rules.
@ -23,6 +30,7 @@ export default {
formularioRemoveRule: this.removeRule
}
},
inject: {
formularioSetter: { default: undefined },
formularioFieldValidation: { default: () => () => ({}) },
@ -33,87 +41,107 @@ export default {
removeErrorObserver: { default: undefined },
path: { default: '' }
},
model: {
prop: 'formularioValue',
event: 'input'
},
props: {
type: {
type: String,
default: 'text'
},
name: {
type: String,
required: true
},
/* eslint-disable */
formularioValue: {
default: ''
},
value: {
default: false
},
/* eslint-enable */
id: {
type: [String, Boolean, Number],
default: false
},
errors: {
type: [String, Array, Boolean],
default: false
},
validation: {
type: [String, Boolean, Array],
default: false
},
validationName: {
type: [String, Boolean],
default: false
},
errorBehavior: {
type: String,
default: 'blur',
validator: function (value) {
validator (value) {
return ['blur', 'live', 'submit'].includes(value)
}
},
showErrors: {
type: Boolean,
default: false
},
imageBehavior: {
type: String,
default: 'preview'
},
uploadUrl: {
type: [String, Boolean],
default: false
},
uploader: {
type: [Function, Object, Boolean],
default: false
},
uploadBehavior: {
type: String,
default: 'live'
},
preventWindowDrops: {
type: Boolean,
default: true
},
validationMessages: {
type: Object,
default: () => ({})
},
validationRules: {
type: Object,
default: () => ({})
},
disableErrors: {
type: Boolean,
default: false
}
},
data () {
return {
defaultId: this.$formulario.nextId(this),
@ -129,8 +157,10 @@ export default {
messageRegistry: {}
}
},
computed: {
...context,
parsedValidationRules () {
const parsedValidationRules = {}
Object.keys(this.validationRules).forEach(key => {
@ -138,6 +168,7 @@ export default {
})
return parsedValidationRules
},
messages () {
const messages = {}
Object.keys(this.validationMessages).forEach((key) => {
@ -149,24 +180,28 @@ export default {
return messages
}
},
watch: {
'$attrs': {
$attrs: {
handler (value) {
this.updateLocalAttributes(value)
},
deep: true
},
proxy (newValue, oldValue) {
this.performValidation()
if (!this.isVmodeled && !shallowEqualObjects(newValue, oldValue)) {
this.context.model = newValue
}
},
formularioValue (newValue, oldValue) {
if (this.isVmodeled && !shallowEqualObjects(newValue, oldValue)) {
this.context.model = newValue
}
},
showValidationErrors: {
handler (val) {
this.$emit('error-visibility', val)
@ -174,6 +209,7 @@ export default {
immediate: true
}
},
created () {
this.applyInitialValue()
if (this.formularioRegister && typeof this.formularioRegister === 'function') {
@ -187,6 +223,7 @@ export default {
this.performValidation()
}
},
beforeDestroy () {
if (!this.disableErrors && typeof this.removeErrorObserver === 'function') {
this.removeErrorObserver(this.setErrors)
@ -195,6 +232,7 @@ export default {
this.formularioDeregister(this.nameOrFallback)
}
},
methods: {
getInitialValue () {
if (has(this.$options.propsData, 'value')) {
@ -204,6 +242,7 @@ export default {
}
return ''
},
applyInitialValue () {
// This should only be run immediately on created and ensures that the
// proxy and the model are both the same before any additional registration.
@ -211,11 +250,13 @@ export default {
this.context.model = this.proxy
}
},
updateLocalAttributes (value) {
if (!shallowEqualObjects(value, this.localAttributes)) {
this.localAttributes = value
}
},
performValidation () {
let rules = parseRules(this.validation, this.$formulario.rules(this.parsedValidationRules))
// Add in ruleRegistry rules. These are added directly via injection from
@ -225,6 +266,7 @@ export default {
.then(messages => this.didValidate(messages))
return this.pendingValidation
},
runRules (rules) {
const run = ([rule, args, ruleName, modifier]) => {
var res = rule({
@ -257,6 +299,7 @@ export default {
resolveGroups(groupBails(rules))
})
},
didValidate (messages) {
const validationChanged = !shallowEqualObjects(messages, this.validationErrors)
this.validationErrors = messages
@ -268,15 +311,16 @@ export default {
}
}
},
getMessageObject (ruleName, args) {
let context = {
const context = {
args,
name: this.mergedValidationName,
value: this.context.model,
vm: this,
formValues: this.getFormValues()
};
let message = this.getMessageFunc(ruleName)(context);
}
const message = this.getMessageFunc(ruleName)(context)
return {
message: message,
@ -284,6 +328,7 @@ export default {
context: context
}
},
getMessageFunc (ruleName) {
ruleName = snakeToCamel(ruleName)
if (this.messages && typeof this.messages[ruleName] !== 'undefined') {
@ -297,6 +342,7 @@ export default {
}
return (context) => this.$formulario.validationMessage(ruleName, context, this)
},
hasValidationErrors () {
return new Promise(resolve => {
this.$nextTick(() => {
@ -304,11 +350,13 @@ export default {
})
})
},
getValidationErrors () {
return new Promise(resolve => {
this.$nextTick(() => this.pendingValidation.then(() => resolve(this.getErrorObject())))
})
},
getErrorObject () {
return {
name: this.context.nameOrFallback || this.context.name,
@ -316,9 +364,11 @@ export default {
hasErrors: !!this.validationErrors.length
}
},
setErrors (errors) {
this.localErrors = arrayify(errors)
},
registerRule (rule, args, ruleName, message = null) {
if (!this.ruleRegistry.some(r => r[2] === ruleName)) {
// These are the raw rule format since they will be used directly.
@ -328,6 +378,7 @@ export default {
}
}
},
removeRule (key) {
const ruleIndex = this.ruleRegistry.findIndex(r => r[2] === key)
if (ruleIndex >= 0) {

View File

@ -18,14 +18,14 @@ const validationMessages = {
* The default render method for error messages.
*/
default: function (vm, context) {
return vm.$t(`validation.default`, context)
return vm.$t('validation.default', context)
},
/**
* Valid accepted value.
*/
accepted: function (vm, context) {
return vm.$t(`validation.accepted`, context)
return vm.$t('validation.accepted', context)
},
/**
@ -33,25 +33,25 @@ const validationMessages = {
*/
after: function (vm, context) {
if (Array.isArray(context.args) && context.args.length) {
context['compare'] = context.args[0]
return vm.$t(`validation.after.compare`, context)
context.compare = context.args[0]
return vm.$t('validation.after.compare', context)
}
return vm.$t(`validation.after.default`, context)
return vm.$t('validation.after.default', context)
},
/**
* The value is not a letter.
*/
alpha: function (vm, context) {
return vm.$t(`validation.alpha`, context)
return vm.$t('validation.alpha', context)
},
/**
* Rule: checks if the value is alpha numeric
*/
alphanumeric: function (vm, context) {
return vm.$t(`validation.alphanumeric`, context)
return vm.$t('validation.alphanumeric', context)
},
/**
@ -59,33 +59,33 @@ const validationMessages = {
*/
before: function (vm, context) {
if (Array.isArray(context.args) && context.args.length) {
context['compare'] = context.args[0]
return vm.$t(`validation.before.compare`, context)
context.compare = context.args[0]
return vm.$t('validation.before.compare', context)
}
return vm.$t(`validation.before.default`, context)
return vm.$t('validation.before.default', context)
},
/**
* The value is not between two numbers or lengths
*/
between: function (vm, context) {
context['from'] = context.args[0]
context['to'] = context.args[1]
context.from = context.args[0]
context.to = context.args[1]
const force = Array.isArray(context.args) && context.args[2] ? context.args[2] : false
if ((!isNaN(value) && force !== 'length') || force === 'value') {
return vm.$t(`validation.between.force`, context)
return vm.$t('validation.between.force', context)
}
return vm.$t(`validation.between.default`, context)
return vm.$t('validation.between.default', context)
},
/**
* The confirmation field does not match
*/
confirm: function (vm, context) {
return vm.$t(`validation.confirm`, context)
return vm.$t('validation.confirm', context)
},
/**
@ -93,25 +93,25 @@ const validationMessages = {
*/
date: function (vm, context) {
if (Array.isArray(context.args) && context.args.length) {
context['format'] = context.args[0]
return vm.$t(`validation.date.format`, context)
context.format = context.args[0]
return vm.$t('validation.date.format', context)
}
return vm.$t(`validation.date.default`, context)
return vm.$t('validation.date.default', context)
},
/**
* Is not a valid email address.
*/
email: function (vm, context) {
return vm.$t(`validation.email.default`, context)
return vm.$t('validation.email.default', context)
},
/**
* Ends with specified value
*/
endsWith: function (vm, context) {
return vm.$t(`validation.endsWith.default`, context)
return vm.$t('validation.endsWith.default', context)
},
/**
@ -119,44 +119,44 @@ const validationMessages = {
*/
in: function (vm, context) {
if (typeof context.value === 'string' && context.value) {
return vm.$t(`validation.in.string`, context)
return vm.$t('validation.in.string', context)
}
return vm.$t(`validation.in.default`, context)
return vm.$t('validation.in.default', context)
},
/**
* Value is not a match.
*/
matches: function (vm, context) {
return vm.$t(`validation.matches.default`, context)
return vm.$t('validation.matches.default', context)
},
/**
* The maximum value allowed.
*/
max: function (vm, context) {
context['maximum'] = context.args[0]
context.maximum = context.args[0]
if (Array.isArray(context.value)) {
return vm.$tc(`validation.max.array`, context['maximum'], context)
return vm.$tc('validation.max.array', context.maximum, context)
}
const force = Array.isArray(context.args) && context.args[1] ? context.args[1] : false
if ((!isNaN(context.value) && force !== 'length') || force === 'value') {
return vm.$tc(`validation.max.force`, context['maximum'], context)
return vm.$tc('validation.max.force', context.maximum, context)
}
return vm.$tc(`validation.max.default`, context['maximum'], context)
return vm.$tc('validation.max.default', context.maximum, context)
},
/**
* The (field-level) error message for mime errors.
*/
mime: function (vm, context) {
context['types'] = context.args[0]
if (context['types']) {
return vm.$t(`validation.mime.default`, context)
context.types = context.args[0]
if (context.types) {
return vm.$t('validation.mime.default', context)
} else {
return vm.$t(`validation.mime.no_formats_allowed`, context)
return vm.$t('validation.mime.no_formats_allowed', context)
}
},
@ -164,51 +164,51 @@ const validationMessages = {
* The maximum value allowed.
*/
min: function (vm, context) {
context['minimum'] = context.args[0]
context.minimum = context.args[0]
if (Array.isArray(context.value)) {
return vm.$tc(`validation.min.array`, context['minimum'], context)
return vm.$tc('validation.min.array', context.minimum, context)
}
const force = Array.isArray(context.args) && context.args[1] ? context.args[1] : false
if ((!isNaN(context.value) && force !== 'length') || force === 'value') {
return vm.$tc(`validation.min.force`, context['minimum'], context)
return vm.$tc('validation.min.force', context.minimum, context)
}
return vm.$tc(`validation.min.default`, context['minimum'], context)
return vm.$tc('validation.min.default', context.minimum, context)
},
/**
* The field is not an allowed value
*/
not: function (vm, context) {
return vm.$t(`validation.not.default`, context)
return vm.$t('validation.not.default', context)
},
/**
* The field is not a number
*/
number: function (vm, context) {
return vm.$t(`validation.number.default`, context)
return vm.$t('validation.number.default', context)
},
/**
* Required field.
*/
required: function (vm, context) {
return vm.$t(`validation.required.default`, context)
return vm.$t('validation.required.default', context)
},
/**
* Starts with specified value
*/
startsWith: function (vm, context) {
return vm.$t(`validation.startsWith.default`, context)
return vm.$t('validation.startsWith.default', context)
},
/**
* Value is not a url.
*/
url: function (vm, context) {
return vm.$t(`validation.url.default`, context)
return vm.$t('validation.url.default', context)
}
}
@ -218,6 +218,6 @@ const validationMessages = {
*/
export default function (instance) {
instance.extend({
validationMessages: validationMessages
validationMessages
})
}

View File

@ -1,10 +1,10 @@
const i = 'image/'
export default {
'csv': 'text/csv',
'gif': i + 'gif',
'jpg': i + 'jpeg',
'jpeg': i + 'jpeg',
'png': i + 'png',
'pdf': 'application/pdf',
'svg': i + 'svg+xml'
csv: 'text/csv',
gif: i + 'gif',
jpg: i + 'jpeg',
jpeg: i + 'jpeg',
png: i + 'png',
pdf: 'application/pdf',
svg: i + 'svg+xml'
}

View File

@ -16,7 +16,7 @@ class Registry {
/**
* Add an item to the registry.
* @param {string|array} key
* @param {string|array} name
* @param {vue} component
*/
add (name, component) {
@ -104,6 +104,7 @@ class Registry {
/**
* Reduce the registry.
* @param {function} callback
* @param accumulator
*/
reduce (callback, accumulator) {
this.registry.forEach((component, field) => {
@ -148,7 +149,7 @@ export function useRegistryComputed () {
)
},
isVmodeled () {
return !!(this.$options.propsData.hasOwnProperty('formularioValue') &&
return !!(Object.prototype.hasOwnProperty.call(this.$options.propsData, 'formularioValue') &&
this._events &&
Array.isArray(this._events.input) &&
this._events.input.length)
@ -191,7 +192,7 @@ export function useRegistryMethods (without = []) {
const { [field]: value, ...proxy } = this.proxy
this.proxy = proxy
} else {
setNested(this.proxy, field, value);
setNested(this.proxy, field, value)
}
this.$emit('input', Object.assign({}, this.proxy))
},

View File

@ -1,7 +1,6 @@
import Vue from 'vue'
import VueI18n from 'vue-i18n'
import flushPromises from 'flush-promises'
import { mount, createLocalVue } from '@vue/test-utils'
import Formulario from '@/Formulario.js'
import FormularioForm from '@/FormularioForm.vue'
import FormularioInput from '@/FormularioInput.vue'
@ -19,7 +18,9 @@ function validationMessages (instance) {
})
}
Vue.use(Formulario, {
const localVue = createLocalVue()
localVue.use(Formulario, {
plugins: [validationMessages],
rules: {
globalRule
@ -29,6 +30,7 @@ Vue.use(Formulario, {
describe('FormularioInput', () => {
it('allows custom field-rule level validation strings', async () => {
const wrapper = mount(FormularioInput, {
localVue,
propsData: {
name: 'test',
validation: 'required|in:abcdef',
@ -38,7 +40,7 @@ describe('FormularioInput', () => {
},
scopedSlots: {
default: `<div><span v-for="error in props.context.allErrors">{{ error.message }}</span></div>`
}
},
})
await flushPromises()
expect(wrapper.find('span').text()).toBe('the value was different than expected')
@ -62,6 +64,7 @@ describe('FormularioInput', () => {
it('allows custom field-rule level validation functions', async () => {
const wrapper = mount(FormularioInput, {
localVue,
propsData: {
name: 'test',
validation: 'required|in:abcdef',
@ -79,6 +82,7 @@ describe('FormularioInput', () => {
it('uses custom async validation rules on defined on the field', async () => {
const wrapper = mount(FormularioInput, {
localVue,
propsData: {
name: 'test',
validation: 'required|foobar',
@ -101,6 +105,7 @@ describe('FormularioInput', () => {
it('uses custom sync validation rules on defined on the field', async () => {
const wrapper = mount(FormularioInput, {
localVue,
propsData: {
name: 'test',
validation: 'required|foobar',
@ -123,6 +128,7 @@ describe('FormularioInput', () => {
it('uses global custom validation rules', async () => {
const wrapper = mount(FormularioInput, {
localVue,
propsData: {
name: 'test',
validation: 'required|globalRule',
@ -135,13 +141,15 @@ describe('FormularioInput', () => {
})
it('emits correct validation event', async () => {
const wrapper = mount(FormularioInput, { propsData: {
name: 'test',
const wrapper = mount(FormularioInput, {
localVue,
propsData: {
validation: 'required',
errorBehavior: 'live',
value: '',
name: 'testinput',
} })
}
})
await flushPromises()
const errorObject = wrapper.emitted('validation')[0][0]
expect(errorObject).toEqual({
@ -159,8 +167,8 @@ describe('FormularioInput', () => {
it('emits a error-visibility event on blur', async () => {
const wrapper = mount(FormularioInput, {
localVue,
propsData: {
name: 'test',
validation: 'required',
errorBehavior: 'blur',
value: '',
@ -178,21 +186,23 @@ describe('FormularioInput', () => {
})
it('emits error-visibility event immediately when live', async () => {
const wrapper = mount(FormularioInput, { propsData: {
name: 'test',
const wrapper = mount(FormularioInput, {
localVue,
propsData: {
validation: 'required',
errorBehavior: 'live',
value: '',
name: 'testinput',
} })
}
})
await flushPromises()
expect(wrapper.emitted('error-visibility').length).toBe(1)
})
it('Does not emit an error-visibility event if visibility did not change', async () => {
const wrapper = mount(FormularioInput, {
localVue,
propsData: {
name: 'test',
validation: 'in:xyz',
errorBehavior: 'live',
value: 'bar',
@ -211,6 +221,7 @@ describe('FormularioInput', () => {
it('can bail on validation when encountering the bail rule', async () => {
const wrapper = mount(FormularioInput, {
localVue,
propsData: { name: 'test', validation: 'bail|required|in:xyz', errorBehavior: 'live' }
})
await flushPromises();
@ -219,6 +230,7 @@ describe('FormularioInput', () => {
it('can show multiple validation errors if they occur before the bail rule', async () => {
const wrapper = mount(FormularioInput, {
localVue,
propsData: { name: 'test', validation: 'required|in:xyz|bail', errorBehavior: 'live' }
})
await flushPromises();
@ -227,6 +239,7 @@ describe('FormularioInput', () => {
it('can avoid bail behavior by using modifier', async () => {
const wrapper = mount(FormularioInput, {
localVue,
propsData: { name: 'test', validation: '^required|in:xyz|min:10,length', errorBehavior: 'live', value: '123' }
})
await flushPromises();
@ -235,6 +248,7 @@ describe('FormularioInput', () => {
it('prevents later error messages when modified rule fails', async () => {
const wrapper = mount(FormularioInput, {
localVue,
propsData: { name: 'test', validation: '^required|in:xyz|min:10,length', errorBehavior: 'live' }
})
await flushPromises();
@ -243,6 +257,7 @@ describe('FormularioInput', () => {
it('can bail in the middle of the rule set with a modifier', async () => {
const wrapper = mount(FormularioInput, {
localVue,
propsData: { name: 'test', validation: 'required|^in:xyz|min:10,length', errorBehavior: 'live' }
})
await flushPromises();
@ -251,6 +266,7 @@ describe('FormularioInput', () => {
it('does not show errors on blur when set error-behavior is submit', async () => {
const wrapper = mount(FormularioInput, {
localVue,
propsData: {
validation: 'required',
errorBehavior: 'submit',
@ -275,6 +291,7 @@ describe('FormularioInput', () => {
it('displays errors when error-behavior is submit and form is submitted', async () => {
const wrapper = mount(FormularioForm, {
localVue,
propsData: {name: 'test'},
slots: {
default: `

View File

@ -1729,6 +1729,15 @@
sass "^1.18.0"
stylus "^0.54.5"
"@vue/eslint-config-standard@^5.1.2":
version "5.1.2"
resolved "https://registry.yarnpkg.com/@vue/eslint-config-standard/-/eslint-config-standard-5.1.2.tgz#c5d55af894a3ae23b65b1af4a425777ac0170b42"
integrity sha512-FTz0k77dIrj9r3xskt9jsZyL/YprrLiPRf4m3k7G6dZ5PKuD6OPqYrHR9eduUmHDFpTlRgFpTVQrq+1el9k3QQ==
dependencies:
eslint-config-standard "^14.1.0"
eslint-import-resolver-node "^0.3.3"
eslint-import-resolver-webpack "^0.12.1"
"@vue/preload-webpack-plugin@^1.1.0":
version "1.1.1"
resolved "https://registry.yarnpkg.com/@vue/preload-webpack-plugin/-/preload-webpack-plugin-1.1.1.tgz#18723530d304f443021da2292d6ec9502826104a"
@ -2158,6 +2167,11 @@ array-equal@^1.0.0:
resolved "https://registry.yarnpkg.com/array-equal/-/array-equal-1.0.0.tgz#8c2a5ef2472fd9ea742b04c77a75093ba2757c93"
integrity sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=
array-find@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/array-find/-/array-find-1.0.0.tgz#6c8e286d11ed768327f8e62ecee87353ca3e78b8"
integrity sha1-bI4obRHtdoMn+OYuzuhzU8o+eLg=
array-flatten@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2"
@ -4186,6 +4200,15 @@ end-of-stream@^1.0.0, end-of-stream@^1.1.0:
dependencies:
once "^1.4.0"
enhanced-resolve@^0.9.1:
version "0.9.1"
resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-0.9.1.tgz#4d6e689b3725f86090927ccc86cd9f1635b89e2e"
integrity sha1-TW5omzcl+GCQknzMhs2fFjW4ni4=
dependencies:
graceful-fs "^4.1.2"
memory-fs "^0.2.0"
tapable "^0.1.8"
enhanced-resolve@^4.1.0:
version "4.1.1"
resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.1.1.tgz#2937e2b8066cd0fe7ce0990a98f0d71a35189f66"
@ -4279,6 +4302,11 @@ eslint-config-standard@^12.0.0:
resolved "https://registry.yarnpkg.com/eslint-config-standard/-/eslint-config-standard-12.0.0.tgz#638b4c65db0bd5a41319f96bba1f15ddad2107d9"
integrity sha512-COUz8FnXhqFitYj4DTqHzidjIL/t4mumGZto5c7DrBpvWoie+Sn3P4sLEzUGeYhRElWuFEf8K1S1EfvD1vixCQ==
eslint-config-standard@^14.1.0:
version "14.1.1"
resolved "https://registry.yarnpkg.com/eslint-config-standard/-/eslint-config-standard-14.1.1.tgz#830a8e44e7aef7de67464979ad06b406026c56ea"
integrity sha512-Z9B+VR+JIXRxz21udPTL9HpFMyoMUEeX1G251EQ6e05WD9aPVtVBn09XUmZ259wCMlCDmYDSZG62Hhm+ZTJcUg==
eslint-import-resolver-node@^0.3.2:
version "0.3.3"
resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.3.tgz#dbaa52b6b2816b50bc6711af75422de808e98404"
@ -4287,6 +4315,30 @@ eslint-import-resolver-node@^0.3.2:
debug "^2.6.9"
resolve "^1.13.1"
eslint-import-resolver-node@^0.3.3:
version "0.3.4"
resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz#85ffa81942c25012d8231096ddf679c03042c717"
integrity sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA==
dependencies:
debug "^2.6.9"
resolve "^1.13.1"
eslint-import-resolver-webpack@^0.12.1:
version "0.12.2"
resolved "https://registry.yarnpkg.com/eslint-import-resolver-webpack/-/eslint-import-resolver-webpack-0.12.2.tgz#769e86cd0c752a1536c19855ebd90aa14ce384ee"
integrity sha512-7Jnm4YAoNNkvqPaZkKdIHsKGmv8/uNnYC5QsXkiSodvX4XEEfH2AKOna98FK52fCDXm3q4HzuX+7pRMKkJ64EQ==
dependencies:
array-find "^1.0.0"
debug "^2.6.9"
enhanced-resolve "^0.9.1"
find-root "^1.1.0"
has "^1.0.3"
interpret "^1.2.0"
lodash "^4.17.15"
node-libs-browser "^1.0.0 || ^2.0.0"
resolve "^1.13.1"
semver "^5.7.1"
eslint-loader@^2.2.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/eslint-loader/-/eslint-loader-2.2.1.tgz#28b9c12da54057af0845e2a6112701a2f6bf8337"
@ -4881,6 +4933,11 @@ find-cache-dir@^3.0.0, find-cache-dir@^3.3.1:
make-dir "^3.0.2"
pkg-dir "^4.1.0"
find-root@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/find-root/-/find-root-1.1.0.tgz#abcfc8ba76f708c42a97b3d685b7e9450bfb9ce4"
integrity sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==
find-up@^1.0.0:
version "1.1.2"
resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f"
@ -5691,6 +5748,11 @@ internal-ip@^4.3.0:
default-gateway "^4.2.0"
ipaddr.js "^1.9.0"
interpret@^1.2.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e"
integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==
invariant@^2.2.2, invariant@^2.2.4:
version "2.2.4"
resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6"
@ -7006,6 +7068,11 @@ media-typer@0.3.0:
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=
memory-fs@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.2.0.tgz#f2bb25368bc121e391c2520de92969caee0a0290"
integrity sha1-8rslNovBIeORwlIN6Slpyu4KApA=
memory-fs@^0.4.1:
version "0.4.1"
resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552"
@ -7357,7 +7424,7 @@ node-ipc@^9.1.1:
js-message "1.0.5"
js-queue "2.0.0"
node-libs-browser@^2.2.1:
"node-libs-browser@^1.0.0 || ^2.0.0", node-libs-browser@^2.2.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.2.1.tgz#b64f513d18338625f90346d27b0d235e631f6425"
integrity sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==
@ -9361,7 +9428,7 @@ selfsigned@^1.10.7:
dependencies:
node-forge "0.9.0"
"semver@2 || 3 || 4 || 5", semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0:
"semver@2 || 3 || 4 || 5", semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.1:
version "5.7.1"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
@ -10064,6 +10131,11 @@ table@^5.2.3:
slice-ansi "^2.1.0"
string-width "^3.0.0"
tapable@^0.1.8:
version "0.1.10"
resolved "https://registry.yarnpkg.com/tapable/-/tapable-0.1.10.tgz#29c35707c2b70e50d07482b5d202e8ed446dafd4"
integrity sha1-KcNXB8K3DlDQdIK10gLo7URtr9Q=
tapable@^1.0.0, tapable@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2"