Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(Form): improve form control and input validation trigger #487

Merged
merged 26 commits into from
Aug 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
2ee8a64
refactor(FormGroup): use inject instead of cloning slots
romhml Aug 3, 2023
1aa9e4e
refactor(FormGroup): use inject instead of cloning slots
romhml Aug 3, 2023
62acdd6
fix(FormGroup): fixed size value
romhml Aug 3, 2023
5c58e3a
refactor(FormGroup): use computed size in all input components
romhml Aug 3, 2023
7f0984a
feat(Form): give more control over Form API and trigger validation on…
romhml Aug 2, 2023
8360589
docs(Form): add example for backend validation
romhml Aug 3, 2023
d75b867
refactor(Form)!: trigger validation automatically on submit
romhml Aug 3, 2023
ae51fe5
docs(Form): update examples
romhml Aug 3, 2023
9718d92
feat(Form): reduce debounce time on input validation
romhml Aug 3, 2023
5f8c891
feat(Form): distinguish blur and change events
romhml Aug 4, 2023
272ef71
docs(Form): update examples
romhml Aug 4, 2023
effd03e
refactor(Form): extend SubmitEvent instead of overriding it
romhml Aug 4, 2023
fc25c7f
docs(Form): document validate-on prop
romhml Aug 4, 2023
83564cc
docs(Form): added api documentation
romhml Aug 4, 2023
4f623f8
fix(Inputs): fix invalid colors if error
romhml Aug 4, 2023
c95fcf4
chore(Form): apply suggestions
romhml Aug 11, 2023
2d5b3aa
chore(Form): remove useless refs in form examples
romhml Aug 11, 2023
a1256d9
docs(Form): restore custom component example
romhml Aug 11, 2023
65ce02f
Merge branch 'dev' into pr/487
benjamincanac Aug 11, 2023
1a949d4
docs(Form): improve API section
benjamincanac Aug 11, 2023
b1acea9
fix(FormGroup): handle `error` as boolean
benjamincanac Aug 11, 2023
92d297d
docs(ComponentCard): improve boolean rendering
benjamincanac Aug 11, 2023
c8469ba
docs(FormGroup): improve with `v-slot` example
benjamincanac Aug 11, 2023
caebf48
fix(useFormGroup): prevent err when no name provided
benjamincanac Aug 11, 2023
ae9cbbb
fix(FormGroup): only provide `props.size` to not override defaults
benjamincanac Aug 12, 2023
9d4620f
Merge branch 'dev' into form-improvements
benjamincanac Aug 12, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions docs/components/content/ComponentCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -164,9 +164,7 @@ const code = computed(() => {
continue
}

const prop = meta?.meta?.props?.find((prop: any) => prop.name === key)

code += ` ${(prop?.type === 'boolean' && value !== true) || typeof value === 'object' ? ':' : ''}${key === 'modelValue' ? 'value' : useKebabCase(key)}${prop?.type === 'boolean' && !!value && key !== 'modelValue' ? '' : `="${typeof value === 'object' ? renderObject(value) : value}"`}`
code += ` ${(typeof value === 'boolean' && value !== true) || typeof value === 'object' ? ':' : ''}${key === 'modelValue' ? 'value' : useKebabCase(key)}${typeof value === 'boolean' && !!value && key !== 'modelValue' ? '' : `="${typeof value === 'object' ? renderObject(value) : value}"`}`
}

if (props.slots) {
Expand Down
13 changes: 5 additions & 8 deletions docs/components/content/examples/FormExampleBasic.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script setup lang="ts">
import { ref } from 'vue'
import type { FormError } from '@nuxthq/ui/dist/runtime/types'
import type { FormError, FormSubmitEvent } from '@nuxthq/ui/dist/runtime/types'

const state = ref({
email: undefined,
Expand All @@ -14,20 +14,17 @@ const validate = (state: any): FormError[] => {
return errors
}

const form = ref()

async function submit () {
await form.value!.validate()
// Do something with state.value
async function submit (event: FormSubmitEvent<any>) {
// Do something with data
console.log(event.data)
}
</script>

<template>
<UForm
ref="form"
:validate="validate"
:state="state"
@submit.prevent="submit"
@submit="submit"
>
<UFormGroup label="Email" name="email">
<UInput v-model="state.email" />
Expand Down
16 changes: 10 additions & 6 deletions docs/components/content/examples/FormExampleElements.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script setup lang="ts">
import { ref } from 'vue'
import { z } from 'zod'
import type { Form } from '@nuxthq/ui/dist/runtime/types'
import type { FormSubmitEvent } from '@nuxthq/ui/dist/runtime/types'

const options = [
{ label: 'Option 1', value: 'option-1' },
Expand Down Expand Up @@ -44,11 +44,11 @@ const schema = z.object({

type Schema = z.infer<typeof schema>

const form = ref<Form<Schema>>()
const form = ref()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why remove the type here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's no longer needed since the example does not rely on form.value.validate now that validation happens automatically on submit. It was required on previous examples to get the right return type.

The ref itself is also useless, I'll remove it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe to type the clear method?


async function submit () {
await form.value!.validate()
// Do something with state.value
async function submit (event: FormSubmitEvent<Schema>) {
// Do something with event.data
console.log(event.data)
}
</script>

Expand All @@ -57,7 +57,7 @@ async function submit () {
ref="form"
:schema="schema"
:state="state"
@submit.prevent="submit"
@submit="submit"
>
<UFormGroup name="input" label="Input">
<UInput v-model="state.input" />
Expand Down Expand Up @@ -96,5 +96,9 @@ async function submit () {
<UButton type="submit">
Submit
</UButton>

<UButton variant="outline" class="ml-2" @click="form.clear()">
Clear
</UButton>
</UForm>
</template>
28 changes: 13 additions & 15 deletions docs/components/content/examples/FormExampleJoi.vue
Original file line number Diff line number Diff line change
@@ -1,40 +1,38 @@
<script setup lang="ts">
import { ref } from 'vue'
import Joi from 'joi'
import type { FormSubmitEvent } from '@nuxthq/ui/dist/runtime/types'

const schema = Joi.object({
emailJoi: Joi.string().required(),
passwordJoi: Joi.string()
email: Joi.string().required(),
password: Joi.string()
.min(8)
.required()
})

const state = ref({
emailJoi: undefined,
passwordJoi: undefined
email: undefined,
password: undefined
})

const form = ref()

async function submit () {
await form.value!.validate()
// Do something with state.value
async function submit (event: FormSubmitEvent<any>) {
// Do something with event.data
console.log(event.data)
}
</script>

<template>
<UForm
ref="form"
:schema="schema"
:state="state"
@submit.prevent="submit"
@submit="submit"
>
<UFormGroup label="Email" name="emailJoi">
<UInput v-model="state.emailJoi" />
<UFormGroup label="Email" name="email">
<UInput v-model="state.email" />
</UFormGroup>

<UFormGroup label="Password" name="passwordJoi">
<UInput v-model="state.passwordJoi" type="password" />
<UFormGroup label="Password" name="password">
<UInput v-model="state.password" type="password" />
</UFormGroup>

<UButton type="submit">
Expand Down
29 changes: 13 additions & 16 deletions docs/components/content/examples/FormExampleYup.vue
Original file line number Diff line number Diff line change
@@ -1,43 +1,40 @@
<script setup lang="ts">
import { ref } from 'vue'
import { object, string, InferType } from 'yup'
import type { Form } from '@nuxthq/ui/dist/runtime/types'
import type { FormSubmitEvent } from '@nuxthq/ui/dist/runtime/types'

const schema = object({
emailYup: string().email('Invalid email').required('Required'),
passwordYup: string()
email: string().email('Invalid email').required('Required'),
password: string()
.min(8, 'Must be at least 8 characters')
.required('Required')
})

type Schema = InferType<typeof schema>

const state = ref({
emailYup: undefined,
passwordYup: undefined
email: undefined,
password: undefined
})

const form = ref<Form<Schema>>()

async function submit () {
await form.value!.validate()
// Do something with state.value
async function submit (event: FormSubmitEvent<Schema>) {
// Do something with event.data
console.log(event.data)
}
</script>

<template>
<UForm
ref="form"
:schema="schema"
:state="state"
@submit.prevent="submit"
@submit="submit"
>
<UFormGroup label="Email" name="emailYup">
<UInput v-model="state.emailYup" />
<UFormGroup label="Email" name="email">
<UInput v-model="state.email" />
</UFormGroup>

<UFormGroup label="Password" name="passwordYup">
<UInput v-model="state.passwordYup" type="password" />
<UFormGroup label="Password" name="password">
<UInput v-model="state.password" type="password" />
</UFormGroup>

<UButton type="submit">
Expand Down
29 changes: 13 additions & 16 deletions docs/components/content/examples/FormExampleZod.vue
Original file line number Diff line number Diff line change
@@ -1,41 +1,38 @@
<script setup lang="ts">
import { ref } from 'vue'
import { z } from 'zod'
import type { Form } from '@nuxthq/ui/dist/runtime/types'
import type { FormSubmitEvent } from '@nuxthq/ui/dist/runtime/types'

const schema = z.object({
emailZod: z.string().email('Invalid email'),
passwordZod: z.string().min(8, 'Must be at least 8 characters')
email: z.string().email('Invalid email'),
password: z.string().min(8, 'Must be at least 8 characters')
})

type Schema = z.output<typeof schema>

const state = ref({
emailZod: undefined,
passwordZod: undefined
email: undefined,
password: undefined
})

const form = ref<Form<Schema>>()

async function submit () {
await form.value!.validate()
// Do something with state.value
async function submit (event: FormSubmitEvent<Schema>) {
// Do something with data
console.log(event.data)
}
</script>

<template>
<UForm
ref="form"
:schema="schema"
:state="state"
@submit.prevent="submit"
@submit="submit"
>
<UFormGroup label="Email" name="emailZod">
<UInput v-model="state.emailZod" />
<UFormGroup label="Email" name="email">
<UInput v-model="state.email" />
</UFormGroup>

<UFormGroup label="Password" name="passwordZod">
<UInput v-model="state.passwordZod" type="password" />
<UFormGroup label="Password" name="password">
<UInput v-model="state.password" type="password" />
</UFormGroup>

<UButton type="submit">
Expand Down
9 changes: 9 additions & 0 deletions docs/components/content/examples/FormGroupErrorExample.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<template>
<UFormGroup v-slot="{ error }" label="Email" :error="!email && 'You must enter an email'" help="This is a nice email!">
<UInput v-model="email" type="email" placeholder="Enter email" :trailing-icon="error && 'i-heroicons-exclamation-triangle-20-solid'" />
</UFormGroup>
</template>

<script setup lang="ts">
const email = ref('')
</script>
Loading