mirror of
https://github.com/ArthurDanjou/ui.git
synced 2026-01-14 12:14:41 +01:00
chore(Form): catch-up with v2 changes (#2165)
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -71,7 +71,6 @@ export interface FormInjectedOptions {
|
||||
}
|
||||
|
||||
export interface FormFieldInjectedOptions<T> {
|
||||
id: string
|
||||
name?: string
|
||||
size?: GetObjectField<T, 'size'>
|
||||
error?: string | boolean
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user