Initial stable build of vue-formulate
This commit is contained in:
parent
a705eced91
commit
9011888971
92
README.md
Normal file
92
README.md
Normal file
@ -0,0 +1,92 @@
|
||||
# Vue Formulate
|
||||
---------------
|
||||
|
||||
### What is it?
|
||||
|
||||
Vue Formulate is a (Vue)[https://vuejs.org/] plugin that exposes an elegant
|
||||
mechanism for building and validating forms with a centralized data store.
|
||||
|
||||
### Installation
|
||||
|
||||
First download the `vue-formulate` package from npm:
|
||||
|
||||
```sh
|
||||
npm install vue-formulate
|
||||
```
|
||||
|
||||
Install `vue-formulate` like any other vue plugin:
|
||||
|
||||
```js
|
||||
import Vue from 'vue
|
||||
import formulate from 'vue-formulate;
|
||||
|
||||
Vue.use(formulate)
|
||||
```
|
||||
Finally `vue-formulate` needs to access your vuex store. You can choose to.
|
||||
|
||||
### Usage
|
||||
|
||||
`vue-formulate` automatically registers two components `formulate` and
|
||||
`formulate-element`. These two elements are able to address most of your form
|
||||
building needs. Here's a simple example:
|
||||
|
||||
```html
|
||||
<template>
|
||||
<formulate name="registration">
|
||||
<formulate-element
|
||||
name="name"
|
||||
type="text"
|
||||
label="What is your name?"
|
||||
validation="required"
|
||||
/>
|
||||
<formulate-element
|
||||
name="email"
|
||||
type="email"
|
||||
label="What is your email address?"
|
||||
validation="required(Email address)|email"
|
||||
/>
|
||||
<formulate-element
|
||||
type="submit"
|
||||
name="Register"
|
||||
/>
|
||||
</formulate>
|
||||
</template>
|
||||
```
|
||||
|
||||
### Validation Rules
|
||||
|
||||
There are several built in validation methods and you are easily able to add
|
||||
your own.
|
||||
|
||||
Rule | Arguments
|
||||
----------|---------------
|
||||
required | label
|
||||
email | label
|
||||
confirmed | label, confirmation field
|
||||
|
||||
You can add as many validation rules as you want to each `formulate-element`,
|
||||
simply chain your rules with pipes `|'.
|
||||
|
||||
```
|
||||
validation="required(My Label)|confirmed(Password Field, confirmation_field)"
|
||||
```
|
||||
|
||||
Adding your own validation rules is simple, simply pass an additional object
|
||||
of rules in your installation:
|
||||
|
||||
```js
|
||||
Vue.use(formulate, {
|
||||
rules: {
|
||||
isPizza: ({field, value, error, values}, label) => value === 'pizza' ? false : `${label || field} is not pizza.`
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
Validation rules expect a return of `false` if there are no errors, or a error
|
||||
message string. Validation rules are all passed an object with the `field` name,
|
||||
`value` of the field, `error` function to generate an error message, and all the
|
||||
`values` of the entire form.
|
||||
|
||||
### Full Documentation
|
||||
|
||||
There are many more options available, more documentation coming soon.
|
44
dist/index.js
vendored
44
dist/index.js
vendored
File diff suppressed because one or more lines are too long
88
package-lock.json
generated
88
package-lock.json
generated
@ -1943,32 +1943,6 @@
|
||||
"wordwrap": "0.0.2"
|
||||
}
|
||||
},
|
||||
"clone-deep": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-3.0.1.tgz",
|
||||
"integrity": "sha512-kWn5hGUnIA4algk62xJIp9jxQZ8DxSPg9ktkkK1WxRGhU/0GKZBekYJHXAXaZKMpxoq/7R4eygeIl9Cf7si+bA==",
|
||||
"requires": {
|
||||
"for-own": "1.0.0",
|
||||
"is-plain-object": "2.0.4",
|
||||
"kind-of": "6.0.2",
|
||||
"shallow-clone": "2.0.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"for-own": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz",
|
||||
"integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=",
|
||||
"requires": {
|
||||
"for-in": "1.0.2"
|
||||
}
|
||||
},
|
||||
"kind-of": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
|
||||
"integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"co": {
|
||||
"version": "4.6.0",
|
||||
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
|
||||
@ -3079,7 +3053,8 @@
|
||||
"for-in": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
|
||||
"integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA="
|
||||
"integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=",
|
||||
"dev": true
|
||||
},
|
||||
"for-own": {
|
||||
"version": "0.1.5",
|
||||
@ -4612,21 +4587,6 @@
|
||||
"integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=",
|
||||
"dev": true
|
||||
},
|
||||
"is-plain-object": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
|
||||
"integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
|
||||
"requires": {
|
||||
"isobject": "3.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"isobject": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
|
||||
"integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8="
|
||||
}
|
||||
}
|
||||
},
|
||||
"is-posix-bracket": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz",
|
||||
@ -5203,25 +5163,6 @@
|
||||
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
|
||||
"dev": true
|
||||
},
|
||||
"mixin-object": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/mixin-object/-/mixin-object-3.0.0.tgz",
|
||||
"integrity": "sha512-RsUqTd3DyF9+UPqhLzJIWwGm4ZGIPYOu6WcQhQuBqqVBGhc6LOC8LrFk9KD7PvVwmqri45IJT88WLrNNrMWjxg==",
|
||||
"requires": {
|
||||
"for-in": "1.0.2",
|
||||
"is-extendable": "1.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"is-extendable": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
|
||||
"integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
|
||||
"requires": {
|
||||
"is-plain-object": "2.0.4"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"mkdirp": {
|
||||
"version": "0.5.1",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
|
||||
@ -6362,31 +6303,6 @@
|
||||
"safe-buffer": "5.1.1"
|
||||
}
|
||||
},
|
||||
"shallow-clone": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-2.0.2.tgz",
|
||||
"integrity": "sha512-2o81AG/RpLTAG/ZXQekPtH/6yTffzKlJ+i6UhtVTtnP6zWQaNo9vt6LI28bhZLSesB12VQSfJYtXopTogVBveg==",
|
||||
"requires": {
|
||||
"is-extendable": "1.0.1",
|
||||
"kind-of": "6.0.2",
|
||||
"mixin-object": "3.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"is-extendable": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
|
||||
"integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
|
||||
"requires": {
|
||||
"is-plain-object": "2.0.4"
|
||||
}
|
||||
},
|
||||
"kind-of": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
|
||||
"integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"shebang-command": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
|
||||
|
@ -59,7 +59,6 @@
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"clone-deep": "^3.0.1",
|
||||
"shortid": "^2.2.8"
|
||||
}
|
||||
}
|
||||
|
@ -25,11 +25,20 @@ export default {
|
||||
initial: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
behavior: {
|
||||
type: String,
|
||||
default: 'blur'
|
||||
},
|
||||
showErrors: {
|
||||
type: [Boolean, Object],
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
parentIdentifier: 'vue-formulate-wrapper-element'
|
||||
parentIdentifier: 'vue-formulate-wrapper-element',
|
||||
forceErrors: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@ -50,6 +59,15 @@ export default {
|
||||
},
|
||||
fields () {
|
||||
return this.$formulate.fields(this.$vnode)
|
||||
},
|
||||
shouldShowErrors () {
|
||||
if (this.forceErrors === false || this.forceErrors === true) {
|
||||
return this.forceErrors
|
||||
}
|
||||
if (this.showErrors === false || this.showErrors === true) {
|
||||
return this.showErrors
|
||||
}
|
||||
return this.behavior === 'live'
|
||||
}
|
||||
},
|
||||
created () {
|
||||
@ -87,8 +105,8 @@ export default {
|
||||
field,
|
||||
value: this.values[field]
|
||||
}, validation, this.values)
|
||||
if (!equals(errors, (this.validationErrors[field] || []))) {
|
||||
this.updateFieldValidationErrors({field, errors})
|
||||
if (!equals(errors || [], (this.validationErrors[field] || []))) {
|
||||
this.updateFieldValidationErrors({field, errors: errors || []})
|
||||
}
|
||||
return errors
|
||||
},
|
||||
@ -99,7 +117,11 @@ export default {
|
||||
}))
|
||||
},
|
||||
submit () {
|
||||
alert('submitting form')
|
||||
if (this.hasErrors) {
|
||||
this.forceErrors = true
|
||||
} else {
|
||||
alert('submitting form')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,39 +1,91 @@
|
||||
<template>
|
||||
<div class="formulate-element">
|
||||
<div :class="classes">
|
||||
<div class="formulate-element-input-wrapper">
|
||||
<!-- TEXT STYLE INPUTS -->
|
||||
<label
|
||||
:for="id"
|
||||
v-text="label"
|
||||
v-if="label && isTextInput"
|
||||
v-if="label && (!isBoxInput || optionList.length > 1)"
|
||||
/>
|
||||
<input
|
||||
ref="input"
|
||||
:type="type"
|
||||
:name="name"
|
||||
:id="id"
|
||||
v-model="val"
|
||||
v-bind="attributes"
|
||||
v-if="isTextInput"
|
||||
@blur="errorBlurState = true"
|
||||
>
|
||||
<!-- BUTTON INPUTS -->
|
||||
<button
|
||||
:type="type"
|
||||
v-text="label || name"
|
||||
v-if="isButtonInput"
|
||||
:disabled="type === 'submit' && form.hasErrors"
|
||||
:disabled="type === 'submit' && (form.hasErrors && form.behavior === 'live')"
|
||||
/>
|
||||
<!-- SELECT INPUTS -->
|
||||
|
||||
<!-- CHECKBOX INPUTS -->
|
||||
|
||||
<!-- RADIO INPUTS -->
|
||||
|
||||
<select
|
||||
v-bind="attributes"
|
||||
v-if="isSelectInput"
|
||||
:name="name"
|
||||
v-model="val"
|
||||
@blur="errorBlurState = true"
|
||||
>
|
||||
<option
|
||||
v-for="option in optionList"
|
||||
:value="option.value"
|
||||
:key="option.id"
|
||||
v-text="option.label"
|
||||
/>
|
||||
</select>
|
||||
<!-- BOX INPUTS -->
|
||||
<div
|
||||
class="formulate-element-box-input-group"
|
||||
v-if="isBoxInput"
|
||||
>
|
||||
<template v-for="option in optionList">
|
||||
<input
|
||||
type="radio"
|
||||
:name="name"
|
||||
:id="option.id"
|
||||
:value="option.value"
|
||||
:key="`${option.id}-input`"
|
||||
v-bind="attributes"
|
||||
v-model="val"
|
||||
v-if="type === 'radio'"
|
||||
@blur="errorBlurState = true"
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
:name="name"
|
||||
:id="option.id"
|
||||
:value="option.value"
|
||||
:key="`${option.id}-input`"
|
||||
v-bind="attributes"
|
||||
v-model="val"
|
||||
v-if="type === 'checkbox'"
|
||||
@blur="errorBlurState = true"
|
||||
>
|
||||
<label
|
||||
:for="option.id"
|
||||
:key="`${option.id}-label`"
|
||||
v-text="option.label"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
<!-- CUSTOM SLOT INPUTS -->
|
||||
<slot v-if="hasCustomInput" />
|
||||
|
||||
<!-- UNSUPORTED INPUT -->
|
||||
<div
|
||||
style="background-color: red; color: white"
|
||||
v-if="isUnsupportedInput"
|
||||
v-text="`Unsupported field type: “${type}”.`"
|
||||
/>
|
||||
</div>
|
||||
<ul
|
||||
class="formulate-errors"
|
||||
v-if="localAndValidationErrors.length"
|
||||
v-if="shouldShowErrors && localAndValidationErrors.length"
|
||||
>
|
||||
<li
|
||||
v-for="error in localAndValidationErrors"
|
||||
@ -45,7 +97,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {inputTypes} from '../utils'
|
||||
import {inputTypes, equals, reduce} from '../utils'
|
||||
import shortid from 'shortid'
|
||||
|
||||
export default {
|
||||
@ -77,6 +129,35 @@ export default {
|
||||
id: {
|
||||
type: [String],
|
||||
default: () => shortid.generate()
|
||||
},
|
||||
min: {
|
||||
type: [String, Number, Boolean],
|
||||
default: () => false
|
||||
},
|
||||
max: {
|
||||
type: [String, Number, Boolean],
|
||||
default: () => false
|
||||
},
|
||||
placeholder: {
|
||||
type: [String, Number, Boolean],
|
||||
default: () => false
|
||||
},
|
||||
options: {
|
||||
type: [Object, Array],
|
||||
default: () => []
|
||||
},
|
||||
multiple: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
showErrors: {
|
||||
type: [Object, Boolean],
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
errorBlurState: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@ -89,8 +170,14 @@ export default {
|
||||
isButtonInput () {
|
||||
return !this.hasCustomInput && inputTypes.button.includes(this.type)
|
||||
},
|
||||
isListInput () {
|
||||
return !this.hasCustomInput && inputTypes.list.includes(this.type)
|
||||
isSelectInput () {
|
||||
return !this.hasCustomInput && inputTypes.select.includes(this.type)
|
||||
},
|
||||
isBoxInput () {
|
||||
return !this.hasCustomInput && inputTypes.box.includes(this.type)
|
||||
},
|
||||
isUnsupportedInput () {
|
||||
return (!this.hasCustomInput && !this.isTextInput && !this.isButtonInput && !this.isSelectInput && !this.isBoxInput)
|
||||
},
|
||||
form () {
|
||||
let parent = this.$parent
|
||||
@ -106,7 +193,20 @@ export default {
|
||||
return this.form.values
|
||||
},
|
||||
value () {
|
||||
return this.values[this.name]
|
||||
let value = this.values[this.name]
|
||||
if (value === undefined) {
|
||||
switch (this.type) {
|
||||
case 'color':
|
||||
value = '#000000'
|
||||
break
|
||||
case 'checkbox':
|
||||
if (this.optionList.length > 1) {
|
||||
value = []
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
return value
|
||||
},
|
||||
module () {
|
||||
return this.form.$props['module']
|
||||
@ -114,6 +214,13 @@ export default {
|
||||
formName () {
|
||||
return this.form.$props['name']
|
||||
},
|
||||
classes () {
|
||||
return {
|
||||
'formulate-element': true,
|
||||
'formulate-element--has-value': !!this.value,
|
||||
'formulate-element--has-errors': this.localAndValidationErrors.length && this.shouldShowErrors
|
||||
}
|
||||
},
|
||||
validationErrors () {
|
||||
return this.form.validationErrors[this.name] || []
|
||||
},
|
||||
@ -123,13 +230,38 @@ export default {
|
||||
localAndValidationErrors () {
|
||||
return this.errors.concat(this.validationErrors)
|
||||
},
|
||||
shouldShowErrors () {
|
||||
let show = this.form.shouldShowErrors
|
||||
if (this.form.behavior === 'blur') {
|
||||
show = this.errorBlurState
|
||||
}
|
||||
if (this.showErrors === false || this.showErrors === true) {
|
||||
show = this.showErrors
|
||||
}
|
||||
return show
|
||||
},
|
||||
attributes () {
|
||||
return this.$props
|
||||
return ['min', 'max', 'placeholder', 'id', 'multiple']
|
||||
.filter(prop => this[prop] !== false)
|
||||
.reduce((attributes, attr) => {
|
||||
attributes[attr] = this[attr]
|
||||
return attributes
|
||||
}, {})
|
||||
},
|
||||
optionList () {
|
||||
if (!Array.isArray(this.options)) {
|
||||
return reduce(this.options, (options, value, label) => options.concat({value, label, id: shortid.generate()}), [])
|
||||
} else if (Array.isArray(this.options) && !this.options.length) {
|
||||
return [{value: this.name, label: (this.label || this.name), id: shortid.generate()}]
|
||||
}
|
||||
return this.options
|
||||
},
|
||||
val: {
|
||||
set (value) {
|
||||
this.form.update({field: this.name, value})
|
||||
this.$refs.input.value = value
|
||||
if (this.isTextInput) {
|
||||
this.$refs.input.value = value
|
||||
}
|
||||
},
|
||||
get () {
|
||||
return this.value
|
||||
@ -137,11 +269,13 @@ export default {
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
errors () {
|
||||
this.form.updateFieldErrors({
|
||||
field: this.name,
|
||||
errors: this.localAndValidationErrors
|
||||
})
|
||||
localAndValidationErrors () {
|
||||
if (!equals(this.localAndValidationErrors, this.storeErrors)) {
|
||||
this.form.updateFieldErrors({
|
||||
field: this.name,
|
||||
errors: this.localAndValidationErrors
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
created () {
|
||||
|
@ -5,7 +5,7 @@ export default {
|
||||
* @param {string} label
|
||||
*/
|
||||
async required ({field, value, error}, label) {
|
||||
return (!value) ? error(...arguments) : false
|
||||
return (!value || (Array.isArray(value) && !value.length)) ? error(...arguments) : false
|
||||
},
|
||||
|
||||
/**
|
||||
|
11
src/utils.js
11
src/utils.js
@ -1,5 +1,3 @@
|
||||
import cloneDeep from 'clone-deep'
|
||||
|
||||
/**
|
||||
* Compare the equality of two arrays.
|
||||
* @param {Array} arr1
|
||||
@ -22,7 +20,7 @@ export function equals (arr1, arr2) {
|
||||
* @param {Function} callback
|
||||
*/
|
||||
export function map (original, callback) {
|
||||
let obj = cloneDeep(original)
|
||||
let obj = Object.assign({}, original)
|
||||
for (let key in obj) {
|
||||
obj[key] = callback(key, obj[key])
|
||||
}
|
||||
@ -71,7 +69,6 @@ export const inputTypes = {
|
||||
'hidden',
|
||||
'month',
|
||||
'password',
|
||||
'radio',
|
||||
'range',
|
||||
'search',
|
||||
'tel',
|
||||
@ -83,7 +80,11 @@ export const inputTypes = {
|
||||
'submit',
|
||||
'button'
|
||||
],
|
||||
list: [
|
||||
select: [
|
||||
'select'
|
||||
],
|
||||
box: [
|
||||
'radio',
|
||||
'checkbox'
|
||||
]
|
||||
}
|
||||
|
@ -41,3 +41,15 @@ test('tests multiple validation errors', async t => {
|
||||
test('tests empty validation string', async t => {
|
||||
t.is(false, await formulate.validationErrors({field: 'email', value: 'pastaparty'}, false))
|
||||
})
|
||||
|
||||
test('can extend rules and errors', async t => {
|
||||
formulate.install(VueMock, {
|
||||
rules: {
|
||||
isPizza: ({field, value, error}, label) => value === 'pizza' ? false : error({field, value})
|
||||
},
|
||||
errors: {
|
||||
isPizza: ({field, value}) => `${field} is not a pizza`
|
||||
}
|
||||
})
|
||||
t.deepEqual(['pepperoni is not a pizza'], await formulate.validationErrors({field: 'pepperoni', value: 'meat'}, 'isPizza'))
|
||||
})
|
||||
|
@ -12,6 +12,11 @@ test('test required rule failure', async t => {
|
||||
t.is('namexyz', v)
|
||||
})
|
||||
|
||||
test('test required rule empty array failure', async t => {
|
||||
let v = await rules.required({field: 'name', value: [], error}, 'xyz')
|
||||
t.is('namexyz', v)
|
||||
})
|
||||
|
||||
test('test required rule passes', async t => {
|
||||
t.is(false, await rules.required({field: 'name', value: 'Justin'}))
|
||||
})
|
||||
|
147
tests/store.test.js
Normal file
147
tests/store.test.js
Normal file
@ -0,0 +1,147 @@
|
||||
import test from 'ava'
|
||||
import {formulateState, formulateGetters, formulateMutations} from '../dist'
|
||||
|
||||
test('initial store state', async t => {
|
||||
t.deepEqual({
|
||||
values: {},
|
||||
errors: {},
|
||||
validationErrors: {}
|
||||
}, formulateState()())
|
||||
})
|
||||
|
||||
test('extended initial store state', async t => {
|
||||
t.deepEqual({
|
||||
values: {},
|
||||
errors: {},
|
||||
validationErrors: {},
|
||||
additionalParam: 'test'
|
||||
}, formulateState({
|
||||
additionalParam: 'test'
|
||||
})())
|
||||
})
|
||||
|
||||
test('validationErrors getter', async t => {
|
||||
let state = {
|
||||
validationErrors: {
|
||||
form: {
|
||||
field: ['This is an error']
|
||||
}
|
||||
}
|
||||
}
|
||||
t.is(formulateGetters().formValidationErrors(state), state.validationErrors)
|
||||
})
|
||||
|
||||
test('errors getter', async t => {
|
||||
let state = {
|
||||
errors: {
|
||||
form: {
|
||||
field: ['This is an error', 'second error']
|
||||
}
|
||||
}
|
||||
}
|
||||
t.is(formulateGetters().formErrors(state), state.errors)
|
||||
})
|
||||
|
||||
test('values getter', async t => {
|
||||
let state = {
|
||||
values: {
|
||||
form: {
|
||||
name: 'Johan',
|
||||
field: 'Guttenberg'
|
||||
}
|
||||
}
|
||||
}
|
||||
t.is(formulateGetters().formValues(state), state.values)
|
||||
})
|
||||
|
||||
test('form has errors', async t => {
|
||||
let state = {
|
||||
errors: {
|
||||
form: {
|
||||
field: ['This is an error', 'second error']
|
||||
}
|
||||
}
|
||||
}
|
||||
t.is(true, formulateGetters().hasErrors(state).form)
|
||||
})
|
||||
|
||||
test('form has empty errors', async t => {
|
||||
let state = {
|
||||
errors: {
|
||||
form: {
|
||||
field: [],
|
||||
other: []
|
||||
}
|
||||
}
|
||||
}
|
||||
t.is(false, formulateGetters().hasErrors(state).form)
|
||||
})
|
||||
|
||||
test('form has no errors', async t => {
|
||||
let state = {
|
||||
errors: {
|
||||
form: {}
|
||||
}
|
||||
}
|
||||
t.is(false, formulateGetters().hasErrors(state).form)
|
||||
})
|
||||
|
||||
test('adds a new field value', async t => {
|
||||
let state = {values: {}}
|
||||
formulateMutations().setFieldValue(state, {
|
||||
form: 'form',
|
||||
field: 'name',
|
||||
value: 'test name'
|
||||
})
|
||||
t.deepEqual(state.values, {form: {name: 'test name'}})
|
||||
})
|
||||
|
||||
test('adds an existing field value', async t => {
|
||||
let state = {values: {form: {name: 'old name'}}}
|
||||
formulateMutations().setFieldValue(state, {
|
||||
form: 'form',
|
||||
field: 'name',
|
||||
value: 'new name'
|
||||
})
|
||||
t.deepEqual(state.values, {form: {name: 'new name'}})
|
||||
})
|
||||
|
||||
test('adds an error to new field', async t => {
|
||||
let state = {errors: {}}
|
||||
formulateMutations().setFieldErrors(state, {
|
||||
form: 'form',
|
||||
field: 'name',
|
||||
errors: ['i dislike this field']
|
||||
})
|
||||
t.deepEqual(state.errors, {form: {name: ['i dislike this field']}})
|
||||
})
|
||||
|
||||
test('adds an error to existing field', async t => {
|
||||
let state = {errors: {form: {name: ['i like this field']}}}
|
||||
formulateMutations().setFieldErrors(state, {
|
||||
form: 'form',
|
||||
field: 'name',
|
||||
errors: ['i dislike this field']
|
||||
})
|
||||
t.deepEqual(state.errors, {form: {name: ['i dislike this field']}})
|
||||
})
|
||||
|
||||
test('adds a validationError to new field', async t => {
|
||||
let state = {validationErrors: {}}
|
||||
formulateMutations().setFieldValidationErrors(state, {
|
||||
form: 'form',
|
||||
field: 'name',
|
||||
errors: ['i dislike this field']
|
||||
})
|
||||
t.deepEqual(state.validationErrors, {form: {name: ['i dislike this field']}})
|
||||
})
|
||||
|
||||
test('adds a validationError to existing field', async t => {
|
||||
let state = {validationErrors: {form: {name: ['i like this field']}}}
|
||||
formulateMutations().setFieldValidationErrors(state, {
|
||||
form: 'form',
|
||||
field: 'name',
|
||||
errors: ['i dislike this field']
|
||||
})
|
||||
t.deepEqual(state.validationErrors, {form: {name: ['i dislike this field']}})
|
||||
})
|
Loading…
Reference in New Issue
Block a user