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

feat: Renamed FormularioInput => FormularioField, FormularioGrouping => FormularioFieldGroup, old names are preserved in plugin install method for compatibility

This commit is contained in:
Zaytsev Kirill 2021-05-22 16:50:53 +03:00
parent 6a86d1f7b7
commit 013931fbfc
10 changed files with 95 additions and 87 deletions

View File

@ -5,7 +5,7 @@ for working with forms and gives full control on the form presentation.
## Examples ## Examples
Every form control have to rendered inside FormularioInput component. This component provides `id` and `context` in Every form control have to rendered inside FormularioField component. This component provides `id` and `context` in
v-slot props. Control should use `context.model` as v-model and `context.runValidation` as handler for `blur` event v-slot props. Control should use `context.model` as v-model and `context.runValidation` as handler for `blur` event
(it is necessary for validation when property `validationBehavior` is `demand`). Errors list for a field can be (it is necessary for validation when property `validationBehavior` is `demand`). Errors list for a field can be
accessed through `context.allErrors`. accessed through `context.allErrors`.
@ -27,7 +27,7 @@ The example below creates the authorization form from data:
v-model="formData" v-model="formData"
name="formName" name="formName"
> >
<FormularioInput <FormularioField
v-slot="{ context }" v-slot="{ context }"
name="username" name="username"
validation="required|email" validation="required|email"
@ -46,9 +46,9 @@ The example below creates the authorization form from data:
{{ error }} {{ error }}
</span> </span>
</div> </div>
</FormularioInput> </FormularioField>
<FormularioInput <FormularioField
v-slot="{ context }" v-slot="{ context }"
name="password" name="password"
validation="required|min:4,length" validation="required|min:4,length"
@ -57,10 +57,10 @@ The example below creates the authorization form from data:
v-model="context.model" v-model="context.model"
type="password" type="password"
> >
</FormularioInput> </FormularioField>
<FormularioGrouping name="options"> <FormularioFieldGroup name="options">
<FormularioInput <FormularioField
v-slot="{ context }" v-slot="{ context }"
name="anonymous" name="anonymous"
> >
@ -72,10 +72,10 @@ The example below creates the authorization form from data:
> >
<label for="options-anonymous">As anonymous</label> <label for="options-anonymous">As anonymous</label>
</div> </div>
</FormularioInput> </FormularioField>
</FormularioGrouping> </FormularioFieldGroup>
<FormularioInput <FormularioField
v-slot="{ context }" v-slot="{ context }"
name="options.tags[0]" name="options.tags[0]"
> >
@ -83,7 +83,7 @@ The example below creates the authorization form from data:
v-model="context.model" v-model="context.model"
type="text" type="text"
> >
</FormularioInput> </FormularioField>
</FormularioForm> </FormularioForm>
``` ```

View File

@ -50,8 +50,8 @@ interface ModelSetConverter {
type Empty = null | undefined type Empty = null | undefined
@Component({ name: 'FormularioInput', inheritAttrs: false }) @Component({ name: 'FormularioField', inheritAttrs: false })
export default class FormularioInput extends Vue { export default class FormularioField extends Vue {
@Inject({ default: undefined }) formularioSetter!: Function|undefined @Inject({ default: undefined }) formularioSetter!: Function|undefined
@Inject({ default: () => (): void => {} }) onFormularioFieldValidation!: Function @Inject({ default: () => (): void => {} }) onFormularioFieldValidation!: Function
@Inject({ default: undefined }) formularioRegister!: Function|undefined @Inject({ default: undefined }) formularioRegister!: Function|undefined

View File

@ -13,8 +13,8 @@ import {
Provide, Provide,
} from 'vue-property-decorator' } from 'vue-property-decorator'
@Component({ name: 'FormularioGrouping' }) @Component({ name: 'FormularioFieldGroup' })
export default class FormularioGrouping extends Vue { export default class FormularioFieldGroup extends Vue {
@Inject({ default: '' }) path!: string @Inject({ default: '' }) path!: string
@Prop({ required: true }) @Prop({ required: true })

View File

@ -9,7 +9,7 @@ import Vue from 'vue'
import { Component, Model, Prop, Provide, Watch } from 'vue-property-decorator' import { Component, Model, Prop, Provide, Watch } from 'vue-property-decorator'
import { clone, getNested, has, merge, setNested, shallowEqualObjects } from '@/utils' import { clone, getNested, has, merge, setNested, shallowEqualObjects } from '@/utils'
import Registry from '@/form/registry' import Registry from '@/form/registry'
import FormularioInput from '@/FormularioInput.vue' import FormularioField from '@/FormularioField.vue'
import { import {
ErrorHandler, ErrorHandler,
@ -125,7 +125,7 @@ export default class FormularioForm extends Vue {
} }
@Provide('formularioRegister') @Provide('formularioRegister')
register (field: string, component: FormularioInput): void { register (field: string, component: FormularioField): void {
this.registry.add(field, component) this.registry.add(field, component)
} }
@ -149,7 +149,7 @@ export default class FormularioForm extends Vue {
} }
this.registry.getNested(field).forEach((registryField, registryKey) => { this.registry.getNested(field).forEach((registryField, registryKey) => {
const $input = this.registry.get(registryKey) as FormularioInput const $input = this.registry.get(registryKey) as FormularioField
const oldValue = getNested(this.proxy, registryKey) const oldValue = getNested(this.proxy, registryKey)
const newValue = getNested(values, registryKey) const newValue = getNested(values, registryKey)
@ -193,8 +193,8 @@ export default class FormularioForm extends Vue {
} }
hasValidationErrors (): Promise<boolean> { hasValidationErrors (): Promise<boolean> {
return Promise.all(this.registry.reduce((resolvers: Promise<boolean>[], input: FormularioInput) => { return Promise.all(this.registry.reduce((resolvers: Promise<boolean>[], field: FormularioField) => {
resolvers.push(input.runValidation() && input.hasValidationErrors()) resolvers.push(field.runValidation() && field.hasValidationErrors())
return resolvers return resolvers
}, [])).then(results => results.some(hasErrors => hasErrors)) }, [])).then(results => results.some(hasErrors => hasErrors))
} }
@ -202,8 +202,8 @@ export default class FormularioForm extends Vue {
resetValidation (): void { resetValidation (): void {
this.localFormErrors = [] this.localFormErrors = []
this.localFieldErrors = {} this.localFieldErrors = {}
this.registry.forEach((input: FormularioInput) => { this.registry.forEach((field: FormularioField) => {
input.resetValidation() field.resetValidation()
}) })
} }
} }

View File

@ -1,6 +1,6 @@
import { shallowEqualObjects, has, getNested } from '@/utils' import { shallowEqualObjects, has, getNested } from '@/utils'
import FormularioField from '@/FormularioField.vue'
import FormularioForm from '@/FormularioForm.vue' import FormularioForm from '@/FormularioForm.vue'
import FormularioInput from '@/FormularioInput.vue'
/** /**
* Component registry with inherent depth to handle complex nesting. This is * Component registry with inherent depth to handle complex nesting. This is
@ -8,7 +8,7 @@ import FormularioInput from '@/FormularioInput.vue'
*/ */
export default class Registry { export default class Registry {
private ctx: FormularioForm private ctx: FormularioForm
private registry: Map<string, FormularioInput> private registry: Map<string, FormularioField>
/** /**
* Create a new registry of components. * Create a new registry of components.
@ -24,7 +24,7 @@ export default class Registry {
* @param {string} field name of the field. * @param {string} field name of the field.
* @param {FormularioForm} component the actual component instance. * @param {FormularioForm} component the actual component instance.
*/ */
add (field: string, component: FormularioInput): void { add (field: string, component: FormularioField): void {
if (this.registry.has(field)) { if (this.registry.has(field)) {
return return
} }
@ -85,14 +85,14 @@ export default class Registry {
/** /**
* Get a particular registry value. * Get a particular registry value.
*/ */
get (key: string): FormularioInput | undefined { get (key: string): FormularioField | undefined {
return this.registry.get(key) return this.registry.get(key)
} }
/** /**
* Get registry value for key or nested to given key * Get registry value for key or nested to given key
*/ */
getNested (key: string): Map<string, FormularioInput> { getNested (key: string): Map<string, FormularioField> {
const result = new Map() const result = new Map()
for (const i of this.registry.keys()) { for (const i of this.registry.keys()) {

View File

@ -1,14 +1,21 @@
import Formulario, { FormularioOptions } from '@/Formulario.ts'
import { VueConstructor } from 'vue' import { VueConstructor } from 'vue'
import Formulario, { FormularioOptions } from '@/Formulario.ts'
import FormularioField from '@/FormularioField.vue'
import FormularioFieldGroup from '@/FormularioFieldGroup.vue'
import FormularioForm from '@/FormularioForm.vue' import FormularioForm from '@/FormularioForm.vue'
import FormularioGrouping from '@/FormularioGrouping.vue'
import FormularioInput from '@/FormularioInput.vue'
export default { export default {
install (Vue: VueConstructor, options?: FormularioOptions): void { install (Vue: VueConstructor, options?: FormularioOptions): void {
Vue.component('FormularioField', FormularioField)
Vue.component('FormularioFieldGroup', FormularioFieldGroup)
Vue.component('FormularioForm', FormularioForm) Vue.component('FormularioForm', FormularioForm)
Vue.component('FormularioGrouping', FormularioGrouping)
Vue.component('FormularioInput', FormularioInput) // @deprecated Use FormularioField instead
Vue.component('FormularioInput', FormularioField)
// @deprecated Use FormularioFieldGroup instead
Vue.component('FormularioGrouping', FormularioFieldGroup)
Vue.mixin({ Vue.mixin({
beforeCreate () { beforeCreate () {

View File

@ -6,7 +6,7 @@ export interface ErrorHandler {
export interface ErrorObserver { export interface ErrorObserver {
callback: ErrorHandler; callback: ErrorHandler;
type: 'form' | 'input'; type: 'form' | 'field';
field?: string; field?: string;
} }

View File

@ -2,19 +2,19 @@
<FormularioForm v-model="values"> <FormularioForm v-model="values">
<h1>Address list</h1> <h1>Address list</h1>
<FormularioInput <FormularioField
v-slot="addressList" v-slot="addressList"
name="addressList" name="addressList"
> >
<FormularioGrouping name="addressList"> <FormularioFieldGroup name="addressList">
<FormularioGrouping <FormularioFieldGroup
v-for="(address, addressIndex) in addressList.context.model" v-for="(address, addressIndex) in addressList.context.model"
:key="'address-' + addressIndex" :key="'address-' + addressIndex"
:name="addressIndex" :name="addressIndex"
:is-array-item="true" :is-array-item="true"
class="row mx-n2" class="row mx-n2"
> >
<FormularioInput <FormularioField
v-slot="{ context }" v-slot="{ context }"
class="col col-auto px-2 mb-3" class="col col-auto px-2 mb-3"
name="street" name="street"
@ -36,9 +36,9 @@
> >
{{ error }} {{ error }}
</div> </div>
</FormularioInput> </FormularioField>
<FormularioInput <FormularioField
v-slot="{ context }" v-slot="{ context }"
class="col col-auto px-2 mb-3" class="col col-auto px-2 mb-3"
name="building" name="building"
@ -60,7 +60,7 @@
> >
{{ error }} {{ error }}
</div> </div>
</FormularioInput> </FormularioField>
<div class="remove-btn-wrapper"> <div class="remove-btn-wrapper">
<button <button
@ -71,8 +71,8 @@
Remove Remove
</button> </button>
</div> </div>
</FormularioGrouping> </FormularioFieldGroup>
</FormularioGrouping> </FormularioFieldGroup>
<button <button
class="btn btn-primary" class="btn btn-primary"
@ -81,22 +81,22 @@
> >
Add address Add address
</button> </button>
</FormularioInput> </FormularioField>
</FormularioForm> </FormularioForm>
</template> </template>
<script> <script>
import FormularioField from '@/FormularioField'
import FormularioFieldGroup from '@/FormularioFieldGroup'
import FormularioForm from '@/FormularioForm' import FormularioForm from '@/FormularioForm'
import FormularioGrouping from '@/FormularioGrouping'
import FormularioInput from '@/FormularioInput'
export default { export default {
name: 'ExampleAddressListTale', name: 'ExampleAddressListTale',
components: { components: {
FormularioField,
FormularioForm, FormularioForm,
FormularioGrouping, FormularioFieldGroup,
FormularioInput,
}, },
data: () => ({ data: () => ({

View File

@ -4,7 +4,7 @@ import { mount } from '@vue/test-utils'
import Formulario from '@/index.ts' import Formulario from '@/index.ts'
import FormularioForm from '@/FormularioForm.vue' import FormularioForm from '@/FormularioForm.vue'
import FormularioInput from '@/FormularioInput.vue' import FormularioField from '@/FormularioField.vue'
const globalRule = jest.fn(() => false) const globalRule = jest.fn(() => false)
@ -18,9 +18,9 @@ Vue.use(Formulario, {
}, },
}) })
describe('FormularioInput', () => { describe('FormularioField', () => {
it('Allows custom field-rule level validation strings', async () => { it('Allows custom field-rule level validation strings', async () => {
const wrapper = mount(FormularioInput, { const wrapper = mount(FormularioField, {
propsData: { propsData: {
name: 'test', name: 'test',
value: 'other value', value: 'other value',
@ -37,7 +37,7 @@ describe('FormularioInput', () => {
}) })
it('No validation on created when validationBehavior is not live', async () => { it('No validation on created when validationBehavior is not live', async () => {
const wrapper = mount(FormularioInput, { const wrapper = mount(FormularioField, {
propsData: { propsData: {
name: 'test', name: 'test',
validation: 'required|in:abcdef', validation: 'required|in:abcdef',
@ -53,7 +53,7 @@ describe('FormularioInput', () => {
}) })
it('No validation on value change when validationBehavior is "submit"', async () => { it('No validation on value change when validationBehavior is "submit"', async () => {
const wrapper = mount(FormularioInput, { const wrapper = mount(FormularioField, {
propsData: { propsData: {
name: 'test', name: 'test',
validation: 'required|in:abcdef', validation: 'required|in:abcdef',
@ -83,7 +83,7 @@ describe('FormularioInput', () => {
}) })
it('Allows custom field-rule level validation functions', async () => { it('Allows custom field-rule level validation functions', async () => {
const wrapper = mount(FormularioInput, { const wrapper = mount(FormularioField, {
propsData: { propsData: {
name: 'test', name: 'test',
validation: 'required|in:abcdef', validation: 'required|in:abcdef',
@ -100,7 +100,7 @@ describe('FormularioInput', () => {
}) })
it('No validation on created when validationBehavior is default', async () => { it('No validation on created when validationBehavior is default', async () => {
const wrapper = mount(FormularioInput, { const wrapper = mount(FormularioField, {
propsData: { propsData: {
name: 'test', name: 'test',
validation: 'required|in:abcdef', validation: 'required|in:abcdef',
@ -116,7 +116,7 @@ describe('FormularioInput', () => {
}) })
it('Uses custom async validation rules on defined on the field', async () => { it('Uses custom async validation rules on defined on the field', async () => {
const wrapper = mount(FormularioInput, { const wrapper = mount(FormularioField, {
propsData: { propsData: {
name: 'test', name: 'test',
validation: 'required|foobar', validation: 'required|foobar',
@ -134,7 +134,7 @@ describe('FormularioInput', () => {
}) })
it('Uses custom sync validation rules on defined on the field', async () => { it('Uses custom sync validation rules on defined on the field', async () => {
const wrapper = mount(FormularioInput, { const wrapper = mount(FormularioField, {
propsData: { propsData: {
name: 'test', name: 'test',
value: 'bar', value: 'bar',
@ -152,7 +152,7 @@ describe('FormularioInput', () => {
}) })
it('Uses global custom validation rules', async () => { it('Uses global custom validation rules', async () => {
const wrapper = mount(FormularioInput, { const wrapper = mount(FormularioField, {
propsData: { propsData: {
name: 'test', name: 'test',
value: 'bar', value: 'bar',
@ -165,7 +165,7 @@ describe('FormularioInput', () => {
}) })
it('Emits correct validation event', async () => { it('Emits correct validation event', async () => {
const wrapper = mount(FormularioInput, { const wrapper = mount(FormularioField, {
propsData: { propsData: {
name: 'fieldName', name: 'fieldName',
value: '', value: '',
@ -188,7 +188,7 @@ describe('FormularioInput', () => {
}) })
it('Can bail on validation when encountering the bail rule', async () => { it('Can bail on validation when encountering the bail rule', async () => {
const wrapper = mount(FormularioInput, { const wrapper = mount(FormularioField, {
propsData: { propsData: {
name: 'test', name: 'test',
validation: 'bail|required|in:xyz', validation: 'bail|required|in:xyz',
@ -200,7 +200,7 @@ describe('FormularioInput', () => {
}) })
it('Can show multiple validation errors if they occur before the bail rule', async () => { it('Can show multiple validation errors if they occur before the bail rule', async () => {
const wrapper = mount(FormularioInput, { const wrapper = mount(FormularioField, {
propsData: { propsData: {
name: 'test', name: 'test',
validation: 'required|in:xyz|bail', validation: 'required|in:xyz|bail',
@ -212,7 +212,7 @@ describe('FormularioInput', () => {
}) })
it('Can avoid bail behavior by using modifier', async () => { it('Can avoid bail behavior by using modifier', async () => {
const wrapper = mount(FormularioInput, { const wrapper = mount(FormularioField, {
propsData: { propsData: {
name: 'test', name: 'test',
value: '123', value: '123',
@ -225,7 +225,7 @@ describe('FormularioInput', () => {
}) })
it('Prevents later error messages when modified rule fails', async () => { it('Prevents later error messages when modified rule fails', async () => {
const wrapper = mount(FormularioInput, { const wrapper = mount(FormularioField, {
propsData: { propsData: {
name: 'test', name: 'test',
validation: '^required|in:xyz|min:10,length', validation: '^required|in:xyz|min:10,length',
@ -237,7 +237,7 @@ describe('FormularioInput', () => {
}) })
it('can bail in the middle of the rule set with a modifier', async () => { it('can bail in the middle of the rule set with a modifier', async () => {
const wrapper = mount(FormularioInput, { const wrapper = mount(FormularioField, {
propsData: { propsData: {
name: 'test', name: 'test',
validation: 'required|^in:xyz|min:10,length', validation: 'required|^in:xyz|min:10,length',
@ -253,14 +253,14 @@ describe('FormularioInput', () => {
propsData: { name: 'test' }, propsData: { name: 'test' },
slots: { slots: {
default: ` default: `
<FormularioInput <FormularioField
v-slot="{ context }" v-slot="{ context }"
name="testinput" name="testinput"
validation="required" validation="required"
validation-behavior="submit" validation-behavior="submit"
> >
<span v-for="error in context.violations">{{ error.message }}</span> <span v-for="error in context.violations">{{ error.message }}</span>
</FormularioInput> </FormularioField>
` `
} }
}) })
@ -281,9 +281,9 @@ describe('FormularioInput', () => {
data: () => ({ values: { test: 'abcd' } }), data: () => ({ values: { test: 'abcd' } }),
template: ` template: `
<FormularioForm v-model="values"> <FormularioForm v-model="values">
<FormularioInput v-slot="{ context }" :model-get-converter="onGet" name="test" > <FormularioField v-slot="{ context }" :model-get-converter="onGet" name="test" >
<span>{{ context.model }}</span> <span>{{ context.model }}</span>
</FormularioInput> </FormularioField>
</FormularioForm> </FormularioForm>
`, `,
methods: { methods: {
@ -310,9 +310,9 @@ describe('FormularioInput', () => {
data: () => ({ values: { test: 'abcd' } }), data: () => ({ values: { test: 'abcd' } }),
template: ` template: `
<FormularioForm v-model="values"> <FormularioForm v-model="values">
<FormularioInput v-slot="{ context }" :model-get-converter="onGet" :model-set-converter="onSet" name="test" > <FormularioField v-slot="{ context }" :model-get-converter="onGet" :model-set-converter="onSet" name="test" >
<input type="text" v-model="context.model"> <input type="text" v-model="context.model">
</FormularioInput> </FormularioField>
</FormularioForm> </FormularioForm>
`, `,
methods: { methods: {

View File

@ -1,22 +1,24 @@
import Vue from 'vue' import Vue from 'vue'
import { mount } from '@vue/test-utils' import { mount } from '@vue/test-utils'
import flushPromises from 'flush-promises' import flushPromises from 'flush-promises'
import Formulario from '@/index.ts' import Formulario from '@/index.ts'
import FormularioFieldGroup from '@/FormularioFieldGroup.vue'
import FormularioForm from '@/FormularioForm.vue' import FormularioForm from '@/FormularioForm.vue'
import FormularioGrouping from '@/FormularioGrouping.vue'
Vue.use(Formulario) Vue.use(Formulario)
describe('FormularioGrouping', () => { describe('FormularioFieldGroup', () => {
it('Grouped fields to be set', async () => { it('Grouped fields to be set', async () => {
const wrapper = mount(FormularioForm, { const wrapper = mount(FormularioForm, {
slots: { slots: {
default: ` default: `
<FormularioGrouping name="group"> <FormularioFieldGroup name="group">
<FormularioInput name="text" v-slot="{ context }"> <FormularioField name="text" v-slot="{ context }">
<input type="text" v-model="context.model"> <input type="text" v-model="context.model">
</FormularioInput> </FormularioField>
</FormularioGrouping> </FormularioFieldGroup>
` `
} }
}) })
@ -31,8 +33,7 @@ describe('FormularioGrouping', () => {
const emitted = wrapper.emitted() const emitted = wrapper.emitted()
expect(emitted['submit']).toBeTruthy() expect(emitted['submit']).toBeTruthy()
expect(emitted['submit'].length).toBe(1) expect(emitted['submit']).toEqual([[{ group: { text: 'test' } }]])
expect(emitted['submit'][0]).toEqual([{ group: { text: 'test' } }])
}) })
it('Grouped fields to be got', async () => { it('Grouped fields to be got', async () => {
@ -45,11 +46,11 @@ describe('FormularioGrouping', () => {
}, },
slots: { slots: {
default: ` default: `
<FormularioGrouping name="group"> <FormularioFieldGroup name="group">
<FormularioInput name="text" v-slot="{ context }"> <FormularioField name="text" v-slot="{ context }">
<input type="text" v-model="context.model"> <input type="text" v-model="context.model">
</FormularioInput> </FormularioField>
</FormularioGrouping> </FormularioFieldGroup>
` `
} }
}) })
@ -61,12 +62,12 @@ describe('FormularioGrouping', () => {
data: () => ({ values: {} }), data: () => ({ values: {} }),
template: ` template: `
<FormularioForm name="form" v-model="values"> <FormularioForm name="form" v-model="values">
<FormularioGrouping name="group"> <FormularioFieldGroup name="group">
<FormularioInput name="text" v-slot="{ context }"> <FormularioField name="text" v-slot="{ context }">
<input type="text" v-model="context.model"> <input type="text" v-model="context.model">
<span>{{ values.group.text }}</span> <span>{{ values.group.text }}</span>
</FormularioInput> </FormularioField>
</FormularioGrouping> </FormularioFieldGroup>
</FormularioForm> </FormularioForm>
` `
}) })
@ -85,9 +86,9 @@ describe('FormularioGrouping', () => {
slots: { slots: {
default: ` default: `
<FormularioGrouping name="group"> <FormularioGrouping name="group">
<FormularioInput ref="input" name="text" v-slot="{ context }"> <FormularioField ref="input" name="text" v-slot="{ context }">
<span v-for="error in context.errors">{{ error }}</span> <span v-for="error in context.errors">{{ error }}</span>
</FormularioInput> </FormularioField>
</FormularioGrouping> </FormularioGrouping>
`, `,
}, },