1
0
mirror of synced 2024-11-25 06:46:02 +03:00

Adds support for new named form methods

This commit is contained in:
Justin Schroeder 2020-05-15 23:20:29 -04:00
parent 1d869936c8
commit 2d097fadb4
12 changed files with 158 additions and 51 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

22
dist/snow.css vendored
View File

@ -330,22 +330,22 @@
.formulate-input[data-classification='group'] [data-is-repeatable] .formulate-input-group-repeatable-remove { .formulate-input[data-classification='group'] [data-is-repeatable] .formulate-input-group-repeatable-remove {
position: absolute; position: absolute;
display: block; display: block;
top: calc(50% - .75em); top: calc(50% - .65em + .5em);
width: 1.5em; width: 1.3em;
height: 1.5em; height: 1.3em;
background-color: #cecece; background-color: #cecece;
right: .75em; right: .85em;
border-radius: 1.5em; border-radius: 1.3em;
cursor: pointer; cursor: pointer;
transition: background-color .2s; } transition: background-color .2s; }
.formulate-input[data-classification='group'] [data-is-repeatable] .formulate-input-group-repeatable-remove::before, .formulate-input[data-classification='group'] [data-is-repeatable] .formulate-input-group-repeatable-remove::after { .formulate-input[data-classification='group'] [data-is-repeatable] .formulate-input-group-repeatable-remove::before, .formulate-input[data-classification='group'] [data-is-repeatable] .formulate-input-group-repeatable-remove::after {
content: ''; content: '';
position: absolute; position: absolute;
top: calc(50% - .125em); top: calc(50% - .1em);
left: 0.375em; left: .325em;
display: block; display: block;
width: .75em; width: .65em;
height: .25em; height: .2em;
background-color: white; background-color: white;
transform-origin: center center; transform-origin: center center;
transition: transform .25s; } transition: transform .25s; }
@ -354,8 +354,8 @@
background-color: #dc2c2c; } background-color: #dc2c2c; }
.formulate-input[data-classification='group'] [data-is-repeatable] .formulate-input-group-repeatable-remove:hover::after, .formulate-input[data-classification='group'] [data-is-repeatable] .formulate-input-group-repeatable-remove:hover::before { .formulate-input[data-classification='group'] [data-is-repeatable] .formulate-input-group-repeatable-remove:hover::after, .formulate-input[data-classification='group'] [data-is-repeatable] .formulate-input-group-repeatable-remove:hover::before {
height: .2em; height: .2em;
width: .9em; width: .75em;
left: .3em; left: .25em;
top: calc(50% - .075em); } top: calc(50% - .075em); }
.formulate-input[data-classification='group'] [data-is-repeatable] .formulate-input-group-repeatable-remove:hover::after { .formulate-input[data-classification='group'] [data-is-repeatable] .formulate-input-group-repeatable-remove:hover::after {
transform: rotate(45deg); } transform: rotate(45deg); }

2
dist/snow.min.css vendored

File diff suppressed because one or more lines are too long

View File

@ -103,10 +103,10 @@ class Formulate {
* implementation is open to community review. * implementation is open to community review.
*/ */
nextId (vm) { nextId (vm) {
const path = vm.$route && vm.$route.path || false const path = vm.$route && vm.$route.path ? vm.$route.path : false
const pathPrefix = path ? vm.$route.path.replace(/[\/\\.\s]/g, '-') : 'global'; const pathPrefix = path ? vm.$route.path.replace(/[/\\.\s]/g, '-') : 'global'
if (!Object.prototype.hasOwnProperty.call(this.idRegistry, pathPrefix)) { if (!Object.prototype.hasOwnProperty.call(this.idRegistry, pathPrefix)) {
this.idRegistry[pathPrefix] = 0; this.idRegistry[pathPrefix] = 0
} }
return `${this.options.idPrefix}${pathPrefix}-${++this.idRegistry[pathPrefix]}` return `${this.options.idPrefix}${pathPrefix}-${++this.idRegistry[pathPrefix]}`
} }
@ -183,7 +183,7 @@ class Formulate {
*/ */
slotComponent (type, slot) { slotComponent (type, slot) {
const def = this.options.library[type] const def = this.options.library[type]
if (def.slotComponents && def.slotComponents[slot]) { if (def && def.slotComponents && def.slotComponents[slot]) {
return def.slotComponents[slot] return def.slotComponents[slot]
} }
return this.options.slotComponents[slot] return this.options.slotComponents[slot]
@ -297,6 +297,39 @@ class Formulate {
return e return e
} }
/**
* Reset a form.
* @param {string} formName
* @param {object} initialValue
*/
reset (formName, initialValue = {}) {
this.resetValidation(formName)
this.setValues(formName, initialValue)
}
/**
* Reset the form's validation messages.
* @param {string} formName
*/
resetValidation (formName) {
const form = this.registry.get(formName)
form.hideErrors(formName)
form.namedErrors = []
form.namedFieldErrors = {}
}
/**
* Set the form values.
* @param {string} formName
* @param {object} values
*/
setValues (formName, values) {
if (values && !Array.isArray(values) && typeof values === 'object') {
const form = this.registry.get(formName)
form.setValues({ ...values })
}
}
/** /**
* Get the file uploader. * Get the file uploader.
*/ */

View File

@ -12,7 +12,7 @@
</template> </template>
<script> <script>
import { shallowEqualObjects, arrayify, has } from './libs/utils' import { arrayify, has } from './libs/utils'
import useRegistry, { useRegistryComputed, useRegistryMethods, useRegistryProviders } from './libs/registry' import useRegistry, { useRegistryComputed, useRegistryMethods, useRegistryProviders } from './libs/registry'
import FormSubmission from './FormSubmission' import FormSubmission from './FormSubmission'
@ -96,20 +96,12 @@ export default {
}, },
watch: { watch: {
formulateValue: { formulateValue: {
handler (newValue, oldValue) { handler (values) {
if (this.isVmodeled && if (this.isVmodeled &&
newValue && values &&
typeof newValue === 'object' typeof values === 'object'
) { ) {
for (const field in newValue) { this.setValues(values)
if (this.registry.has(field) &&
!shallowEqualObjects(newValue[field], this.proxy[field]) &&
!shallowEqualObjects(newValue[field], this.registry.get(field).proxy[field])
) {
this.setFieldValue(field, newValue[field])
this.registry.get(field).context.model = newValue[field]
}
}
} }
}, },
deep: true deep: true

View File

@ -155,10 +155,6 @@ export default {
type: [String, Boolean], type: [String, Boolean],
default: false default: false
}, },
debug: {
type: Boolean,
default: false
},
errors: { errors: {
type: [String, Array, Boolean], type: [String, Array, Boolean],
default: false default: false

View File

@ -187,7 +187,12 @@ export function useRegistryMethods (without = []) {
} }
}, },
setFieldValue (field, value) { setFieldValue (field, value) {
if (value === undefined) {
const { [field]: value, ...proxy } = this.proxy
this.proxy = proxy
} else {
Object.assign(this.proxy, { [field]: value }) Object.assign(this.proxy, { [field]: value })
}
this.$emit('input', Object.assign({}, this.proxy)) this.$emit('input', Object.assign({}, this.proxy))
}, },
getFormValues () { getFormValues () {
@ -204,6 +209,26 @@ export function useRegistryMethods (without = []) {
this.registry.map(input => { this.registry.map(input => {
input.formShouldShowErrors = true input.formShouldShowErrors = true
}) })
},
hideErrors () {
this.childrenShouldShowErrors = false
this.registry.map(input => {
input.formShouldShowErrors = false
input.behavioralErrorVisibility = false
})
},
setValues (values) {
// Collect all keys, existing and incoming
const keys = Array.from(new Set(Object.keys(values).concat(Object.keys(this.proxy))))
keys.forEach(field => {
if (this.registry.has(field) &&
!shallowEqualObjects(values[field], this.proxy[field]) &&
!shallowEqualObjects(values[field], this.registry.get(field).proxy)
) {
this.setFieldValue(field, values[field])
this.registry.get(field).context.model = values[field]
}
})
} }
} }
return Object.keys(methods).reduce((withMethods, key) => { return Object.keys(methods).reduce((withMethods, key) => {

View File

@ -536,4 +536,65 @@ describe('FormulateForm', () => {
expect(wrapper.findComponent(FormulateForm).vm.proxy).toEqual({ foo: 'bar' }) expect(wrapper.findComponent(FormulateForm).vm.proxy).toEqual({ foo: 'bar' })
expect(wrapper.vm.formData).toEqual({ foo: 'bar' }) expect(wrapper.vm.formData).toEqual({ foo: 'bar' })
}) })
it('it allows the removal of properties in proxy.', async () => {
const wrapper = mount({
template: `
<FormulateForm
v-model="formData"
name="login"
ref="form"
>
<FormulateInput type="text" name="username" validation="required" v-model="username" />
<FormulateInput type="password" name="password" validation="required|min:4,length" />
</FormulateForm>
`,
data () {
return {
formData: {},
username: undefined
}
}
})
wrapper.find('input[type="text"]').setValue('foo')
await flushPromises()
expect(wrapper.vm.username).toEqual('foo')
expect(wrapper.vm.formData).toEqual({ username: 'foo' })
wrapper.vm.$refs.form.setValues({})
await flushPromises()
expect(wrapper.vm.formData).toEqual({ username: '' })
})
it('it allows resetting a form, hiding validation and clearing inputs.', async () => {
const wrapper = mount({
template: `
<FormulateForm
v-model="formData"
name="login"
>
<FormulateInput type="text" name="username" validation="required" />
<FormulateInput type="password" name="password" validation="required|min:4,length" />
</FormulateForm>
`,
data () {
return {
formData: {}
}
}
})
const password = wrapper.find('input[type="password"]')
password.setValue('foo')
password.trigger('blur')
wrapper.find('form').trigger('submit')
wrapper.vm.$formulate.handle({
inputErrors: { username: ['Failed'] }
}, 'login')
await flushPromises()
// First make sure we showed the errors
expect(wrapper.findAll('.formulate-input-error').length).toBe(3)
wrapper.vm.$formulate.reset('login')
await flushPromises()
expect(wrapper.findAll('.formulate-input-error').length).toBe(0)
expect(wrapper.vm.formData).toEqual({})
})
}) })

View File

@ -294,9 +294,9 @@ describe('FormulateInput', () => {
expect(wrapper.vm.context.visibleValidationErrors.length).toBe(2); expect(wrapper.vm.context.visibleValidationErrors.length).toBe(2);
}) })
it('doesnt show errors on blur when set error-behavior is submit', async () => { it('does not show errors on blur when set error-behavior is submit', async () => {
const wrapper = mount(FormulateInput, { propsData: { const wrapper = mount(FormulateInput, { propsData: {
type: 'special', type: 'text',
validation: 'required', validation: 'required',
errorBehavior: 'submit', errorBehavior: 'submit',
} }) } })

View File

@ -413,12 +413,12 @@
&-remove { &-remove {
position: absolute; position: absolute;
display: block; display: block;
top: calc(50% - .75em); top: calc(50% - .65em + .5em);
width: 1.5em; width: 1.3em;
height: 1.5em; height: 1.3em;
background-color: $formulate-gray-d; background-color: $formulate-gray-d;
right: .75em; right: .85em;
border-radius: 1.5em; border-radius: 1.3em;
cursor: pointer; cursor: pointer;
transition: background-color .2s; transition: background-color .2s;
@ -426,11 +426,11 @@
&::after { &::after {
content: ''; content: '';
position: absolute; position: absolute;
top: calc(50% - .125em); top: calc(50% - .1em);
left: 0.375em; left: .325em;
display: block; display: block;
width: .75em; width: .65em;
height: .25em; height: .2em;
background-color: white; background-color: white;
transform-origin: center center; transform-origin: center center;
transition: transform .25s; transition: transform .25s;
@ -443,8 +443,8 @@
&::after, &::after,
&::before { &::before {
height: .2em; height: .2em;
width: .9em; width: .75em;
left: .3em; left: .25em;
top: calc(50% - .075em); top: calc(50% - .075em);
} }