chore(Form): catch-up with v2 changes (#2165)

Co-authored-by: Benjamin Canac <canacb1@gmail.com>
This commit is contained in:
Romain Hamel
2024-09-10 14:09:42 +02:00
committed by GitHub
parent 9ddfec123e
commit 175229384f
8 changed files with 33 additions and 21 deletions

View File

@@ -157,15 +157,12 @@ async function _validate(opts: { name?: string | string[], silent?: boolean, nes
}
async function onSubmit(payload: Event) {
const event = payload as SubmitEvent
const event = payload as FormSubmitEvent<any>
try {
await _validate({ nested: true })
const submitEvent: FormSubmitEvent<any> = {
...event,
data: props.state
}
emits('submit', submitEvent)
event.data = props.state
emits('submit', event)
} catch (error) {
if (!(error instanceof FormValidationException)) {
throw error

View File

@@ -38,7 +38,7 @@ export interface FormFieldSlots {
<script setup lang="ts">
import { computed, ref, inject, provide, type Ref, useId } from 'vue'
import { Label } from 'radix-vue'
import { formFieldInjectionKey } from '../composables/useFormField'
import { formFieldInjectionKey, inputIdInjectionKey } from '../composables/useFormField'
import type { FormError, FormFieldInjectedOptions } from '../types/form'
const props = defineProps<FormFieldProps>()
@@ -55,8 +55,9 @@ const error = computed(() => props.error || formErrors?.value?.find(error => err
const id = ref(useId())
provide(inputIdInjectionKey, id)
provide(formFieldInjectionKey, computed(() => ({
id: id.value,
error: error.value,
name: props.name,
size: props.size,

View File

@@ -69,7 +69,7 @@ const slots = defineSlots<RadioGroupSlots<T>>()
const rootProps = useForwardPropsEmits(reactivePick(props, 'as', 'modelValue', 'defaultValue', 'orientation', 'loop', 'required'), emits)
const { emitFormChange, emitFormInput, color, name, size, id: _id, disabled } = useFormField<RadioGroupProps<T>>(props)
const { emitFormChange, emitFormInput, color, name, size, id: _id, disabled } = useFormField<RadioGroupProps<T>>(props, { bind: false })
const id = _id.value ?? useId()
const ui = computed(() => radioGroup({

View File

@@ -132,6 +132,8 @@ function autoResize() {
}
textareaRef.value.rows = props.rows
const overflow = textareaRef.value.style.overflow
textareaRef.value.style.overflow = 'hidden'
const styles = window.getComputedStyle(textareaRef.value)
const paddingTop = Number.parseInt(styles.paddingTop)
@@ -144,6 +146,8 @@ function autoResize() {
if (newRows > props.rows) {
textareaRef.value.rows = props.maxrows ? Math.min(newRows, props.maxrows) : newRows
}
textareaRef.value.style.overflow = overflow
}
}

View File

@@ -18,22 +18,26 @@ type Props<T> = {
export const formOptionsInjectionKey: InjectionKey<ComputedRef<FormInjectedOptions>> = Symbol('nuxt-ui.form-options')
export const formBusInjectionKey: InjectionKey<UseEventBusReturn<FormEvent, string>> = Symbol('nuxt-ui.form-events')
export const formFieldInjectionKey: InjectionKey<ComputedRef<FormFieldInjectedOptions<FormFieldProps>>> = Symbol('nuxt-ui.form-field')
export const inputIdInjectionKey: InjectionKey<Ref<string | undefined>> = Symbol('nuxt-ui.input-id')
export const formInputsInjectionKey: InjectionKey<Ref<Record<string, string>>> = Symbol('nuxt-ui.form-inputs')
export function useFormField<T>(props?: Props<T>) {
export function useFormField<T>(props?: Props<T>, opts?: { bind?: boolean }) {
const formOptions = inject(formOptionsInjectionKey, undefined)
const formBus = inject(formBusInjectionKey, undefined)
const formField = inject(formFieldInjectionKey, undefined)
const formInputs = inject(formInputsInjectionKey, undefined)
const inputId = inject(inputIdInjectionKey, undefined)
if (formField) {
if (props?.id) {
// Updates for="..." attribute on label if props.id is provided
formField.value.id = props?.id
if (formField && inputId) {
if (opts?.bind === false || props?.legend) {
// Removes for="..." attribute on label for RadioGroup and alike.
inputId.value = undefined
} else if (props?.id) {
// Updates for="..." attribute on label if props.id is provided.
inputId.value = props?.id
}
if (formInputs && formField.value.name) {
formInputs.value[formField.value.name] = formField.value.id
if (formInputs && formField.value.name && inputId.value) {
formInputs.value[formField.value.name] = inputId.value
}
}
@@ -62,7 +66,7 @@ export function useFormField<T>(props?: Props<T>) {
)
return {
id: computed(() => props?.id ?? formField?.value.id),
id: computed(() => props?.id ?? inputId?.value),
name: computed(() => props?.name ?? formField?.value.name),
size: computed(() => props?.size ?? formField?.value.size),
color: computed(() => formField?.value.error ? 'error' : props?.color),

View File

@@ -71,7 +71,6 @@ export interface FormInjectedOptions {
}
export interface FormFieldInjectedOptions<T> {
id: string
name?: string
size?: GetObjectField<T, 'size'>
error?: string | boolean

View File

@@ -228,7 +228,7 @@ describe('Form', () => {
expect(passwordField.text()).toBe('Required')
})
test('valid submit works', async () => {
test('validate on submit works', async () => {
state.email = 'bob@dylan.com'
state.password = 'strongpassword'
@@ -236,6 +236,7 @@ describe('Form', () => {
expect(wrapper.setupState.onSubmit).toHaveBeenCalledTimes(1)
expect(wrapper.setupState.onSubmit).toHaveBeenCalledWith(expect.objectContaining({
type: 'submit',
data: {
email: 'bob@dylan.com',
password: 'strongpassword'

View File

@@ -72,7 +72,7 @@ describe('RadioGroup', () => {
items: ['Option 1', 'Option 2']
},
slotTemplate: `
<UFormField name="value">
<UFormField name="value" label="Radio group">
<URadioGroup id="input" v-model="state.value" :items="items" />
</UFormField>
`
@@ -107,5 +107,11 @@ describe('RadioGroup', () => {
await flushPromises()
expect(wrapper.text()).not.toContain('Error message')
})
test('no label for=... on FormField', async () => {
const { wrapper } = await createForm()
const formFieldLabel = wrapper.findAll('label').map(label => label.attributes()).filter(label => !label.for?.includes('Option'))[0]
expect(formFieldLabel.for).toBeUndefined()
})
})
})