mirror of
https://github.com/ArthurDanjou/ui.git
synced 2026-01-20 15:01:46 +01:00
feat(Form): add superstruct validation (#2357)
This commit is contained in:
36
docs/components/content/examples/FormExampleSuperstruct.vue
Normal file
36
docs/components/content/examples/FormExampleSuperstruct.vue
Normal file
@@ -0,0 +1,36 @@
|
||||
<script setup lang="ts">
|
||||
import { object, string, nonempty, type Infer } from 'superstruct'
|
||||
import type { FormSubmitEvent } from '#ui/types'
|
||||
|
||||
const schema = object({
|
||||
email: nonempty(string()),
|
||||
password: nonempty(string())
|
||||
})
|
||||
|
||||
const state = reactive({
|
||||
email: '',
|
||||
password: ''
|
||||
})
|
||||
|
||||
type Schema = Infer<typeof schema>
|
||||
|
||||
async function onSubmit (event: FormSubmitEvent<Schema>) {
|
||||
console.log(event.data)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UForm :schema="schema" :state="state" class="space-y-4" @submit="onSubmit">
|
||||
<UFormGroup label="Email" name="email">
|
||||
<UInput v-model="state.email" />
|
||||
</UFormGroup>
|
||||
|
||||
<UFormGroup label="Password" name="password">
|
||||
<UInput v-model="state.password" type="password" />
|
||||
</UFormGroup>
|
||||
|
||||
<UButton type="submit">
|
||||
Submit
|
||||
</UButton>
|
||||
</UForm>
|
||||
</template>
|
||||
@@ -8,13 +8,13 @@ links:
|
||||
|
||||
## Usage
|
||||
|
||||
Use the Form component to validate form data using schema libraries such as [Yup](https://github.com/jquense/yup), [Zod](https://github.com/colinhacks/zod), [Joi](https://github.com/hapijs/joi), [Valibot](https://github.com/fabian-hiller/valibot), or your own validation logic.
|
||||
Use the Form component to validate form data using schema libraries such as [Yup](https://github.com/jquense/yup), [Zod](https://github.com/colinhacks/zod), [Joi](https://github.com/hapijs/joi), [Valibot](https://github.com/fabian-hiller/valibot), [Superstruct](https://github.com/ianstormtaylor/superstruct), or your own validation logic.
|
||||
|
||||
It works with the [FormGroup](/components/form-group) component to display error messages around form elements automatically.
|
||||
|
||||
The form component requires two props:
|
||||
- `state` - a reactive object holding the form's state.
|
||||
- `schema` - a schema object from a validation library like [Yup](https://github.com/jquense/yup), [Zod](https://github.com/colinhacks/zod), [Joi](https://github.com/hapijs/joi) or [Valibot](https://github.com/fabian-hiller/valibot).
|
||||
- `schema` - a schema object from a validation library like [Yup](https://github.com/jquense/yup), [Zod](https://github.com/colinhacks/zod), [Joi](https://github.com/hapijs/joi), [Valibot](https://github.com/fabian-hiller/valibot) or [Superstruct](https://github.com/ianstormtaylor/superstruct).
|
||||
|
||||
::callout{icon="i-heroicons-light-bulb"}
|
||||
Note that **no validation library is included** by default, so ensure you **install the one you need**.
|
||||
@@ -52,6 +52,13 @@ Note that **no validation library is included** by default, so ensure you **inst
|
||||
class: 'w-60'
|
||||
---
|
||||
::
|
||||
::component-example{label="Superstruct"}
|
||||
---
|
||||
component: 'form-example-superstruct'
|
||||
componentProps:
|
||||
class: 'w-60'
|
||||
---
|
||||
::
|
||||
::
|
||||
|
||||
## Custom validation
|
||||
|
||||
@@ -66,6 +66,7 @@
|
||||
"joi": "^17.13.3",
|
||||
"nuxt": "^3.13.2",
|
||||
"release-it": "^17.7.0",
|
||||
"superstruct": "^2.0.2",
|
||||
"unbuild": "^2.0.0",
|
||||
"valibot": "^0.42.1",
|
||||
"valibot30": "npm:valibot@0.30.0",
|
||||
|
||||
9
pnpm-lock.yaml
generated
9
pnpm-lock.yaml
generated
@@ -109,6 +109,9 @@ importers:
|
||||
release-it:
|
||||
specifier: ^17.7.0
|
||||
version: 17.7.0(typescript@5.6.2)
|
||||
superstruct:
|
||||
specifier: ^2.0.2
|
||||
version: 2.0.2
|
||||
unbuild:
|
||||
specifier: ^2.0.0
|
||||
version: 2.0.0(typescript@5.6.2)(vue-tsc@2.1.6(typescript@5.6.2))
|
||||
@@ -6078,6 +6081,10 @@ packages:
|
||||
resolution: {integrity: sha512-8iGv75BYOa0xRJHK5vRLEjE2H/i4lulTjzpUXic3Eg8akftYjkmQDa8JARQ42rlczXyFR3IeRoeFCc7RxHsYZA==}
|
||||
engines: {node: '>=16'}
|
||||
|
||||
superstruct@2.0.2:
|
||||
resolution: {integrity: sha512-uV+TFRZdXsqXTL2pRvujROjdZQ4RAlBUS5BTh9IGm+jTqQntYThciG/qu57Gs69yjnVUSqdxF9YLmSnpupBW9A==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
|
||||
supports-color@5.5.0:
|
||||
resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==}
|
||||
engines: {node: '>=4'}
|
||||
@@ -14318,6 +14325,8 @@ snapshots:
|
||||
dependencies:
|
||||
copy-anything: 3.0.5
|
||||
|
||||
superstruct@2.0.2: {}
|
||||
|
||||
supports-color@5.5.0:
|
||||
dependencies:
|
||||
has-flag: 3.0.0
|
||||
|
||||
@@ -13,6 +13,7 @@ import type { ObjectSchema as YupObjectSchema, ValidationError as YupError } fro
|
||||
import type { BaseSchema as ValibotSchema30, BaseSchemaAsync as ValibotSchemaAsync30 } from 'valibot30'
|
||||
import type { GenericSchema as ValibotSchema31, GenericSchemaAsync as ValibotSchemaAsync31, SafeParser as ValibotSafeParser31, SafeParserAsync as ValibotSafeParserAsync31 } from 'valibot31'
|
||||
import type { GenericSchema as ValibotSchema, GenericSchemaAsync as ValibotSchemaAsync, SafeParser as ValibotSafeParser, SafeParserAsync as ValibotSafeParserAsync } from 'valibot'
|
||||
import type { Struct } from 'superstruct'
|
||||
import type { FormError, FormEvent, FormEventType, FormSubmitEvent, FormErrorEvent, Form } from '../../types/form'
|
||||
import { useId } from '#imports'
|
||||
|
||||
@@ -35,7 +36,7 @@ export default defineComponent({
|
||||
| PropType<ValibotSchema31 | ValibotSchemaAsync31>
|
||||
| PropType<ValibotSafeParser31<any, any> | ValibotSafeParserAsync31<any, any>>
|
||||
| PropType<ValibotSchema | ValibotSchemaAsync>
|
||||
| PropType<ValibotSafeParser<any, any> | ValibotSafeParserAsync<any, any>>,
|
||||
| PropType<ValibotSafeParser<any, any> | ValibotSafeParserAsync<any, any>> | PropType<Struct<any, any>>,
|
||||
default: undefined
|
||||
},
|
||||
state: {
|
||||
@@ -88,6 +89,8 @@ export default defineComponent({
|
||||
errs = errs.concat(await getJoiErrors(props.state, props.schema))
|
||||
} else if (isValibotSchema(props.schema)) {
|
||||
errs = errs.concat(await getValibotError(props.state, props.schema))
|
||||
} else if (isSuperStructSchema(props.schema)) {
|
||||
errs = errs.concat(await getSuperStructErrors(props.state, props.schema))
|
||||
} else {
|
||||
throw new Error('Form validation failed: Unsupported form schema')
|
||||
}
|
||||
@@ -195,6 +198,15 @@ function isYupError (error: any): error is YupError {
|
||||
return error.inner !== undefined
|
||||
}
|
||||
|
||||
function isSuperStructSchema (schema: any): schema is Struct<any, any> {
|
||||
return (
|
||||
'schema' in schema &&
|
||||
typeof schema.coercer === 'function' &&
|
||||
typeof schema.validator === 'function' &&
|
||||
typeof schema.refiner === 'function'
|
||||
)
|
||||
}
|
||||
|
||||
async function getYupErrors (
|
||||
state: any,
|
||||
schema: YupObjectSchema<any>
|
||||
@@ -218,6 +230,18 @@ function isZodSchema (schema: any): schema is ZodSchema {
|
||||
return schema.parse !== undefined
|
||||
}
|
||||
|
||||
async function getSuperStructErrors (state: any, schema: Struct<any, any>): Promise<FormError[]> {
|
||||
const [err] = schema.validate(state)
|
||||
if (err) {
|
||||
const errors = err.failures()
|
||||
return errors.map((error) => ({
|
||||
message: error.message,
|
||||
path: error.path.join('.')
|
||||
}))
|
||||
}
|
||||
return []
|
||||
}
|
||||
|
||||
async function getZodErrors (
|
||||
state: any,
|
||||
schema: ZodSchema
|
||||
@@ -259,6 +283,7 @@ async function getJoiErrors (
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function isValibotSchema (schema: any): schema is ValibotSchema30 | ValibotSchemaAsync30 | ValibotSchema31 | ValibotSchemaAsync31 | ValibotSafeParser31<any, any> | ValibotSafeParserAsync31<any, any> | ValibotSchema | ValibotSchemaAsync | ValibotSafeParser<any, any> | ValibotSafeParserAsync<any, any> {
|
||||
return '_parse' in schema || '_run' in schema || (typeof schema === 'function' && 'schema' in schema)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user