@@ -3182,16 +3182,14 @@ test('removes proper pathState when field is unmounting', async () => {
31823182 await flushPromises ( ) ;
31833183
31843184 expect ( form . meta . value . valid ) . toBe ( false ) ;
3185- expect ( form . getAllPathStates ( ) ) . toMatchObject ( [
3186- { id : 0 , path : 'foo' } ,
3187- { id : 1 , path : 'foo' } ,
3188- ] ) ;
3185+ // Both fields share the same pathState with an array of ids
3186+ expect ( form . getAllPathStates ( ) ) . toMatchObject ( [ { id : [ 0 , 1 ] , path : 'foo' , fieldsCount : 2 } ] ) ;
31893187
31903188 renderTemplateField . value = false ;
31913189 await flushPromises ( ) ;
31923190
31933191 expect ( form . meta . value . valid ) . toBe ( true ) ;
3194- expect ( form . getAllPathStates ( ) ) . toMatchObject ( [ { id : 0 , path : 'foo' } ] ) ;
3192+ expect ( form . getAllPathStates ( ) ) . toMatchObject ( [ { id : [ 0 ] , path : 'foo' , fieldsCount : 1 } ] ) ;
31953193} ) ;
31963194
31973195test ( 'handles onSubmit with generic object from zod schema' , async ( ) => {
@@ -3241,3 +3239,105 @@ test('handles onSubmit with generic object from zod schema', async () => {
32413239 expect . anything ( ) ,
32423240 ) ;
32433241} ) ;
3242+
3243+ // #5001 - radio buttons without explicit type on Field should share errors
3244+ test ( 'radio buttons with same name should all expose errors even without type=radio on Field' , async ( ) => {
3245+ const REQUIRED_MSG = 'This field is required' ;
3246+ defineRule ( 'required' , ( value : unknown ) => {
3247+ if ( ! value ) {
3248+ return REQUIRED_MSG ;
3249+ }
3250+ return true ;
3251+ } ) ;
3252+
3253+ const wrapper = mountWithHoc ( {
3254+ template : `
3255+ <VForm v-slot="{ errors: formErrors }">
3256+ <Field name="drink" rules="required" v-slot="{ errors, handleChange }">
3257+ <input type="radio" name="drink" value="" @change="handleChange" />
3258+ <span class="err-coffee">{{ errors[0] }}</span>
3259+ </Field>
3260+ <Field name="drink" rules="required" v-slot="{ errors, handleChange }">
3261+ <input type="radio" name="drink" value="Tea" @change="handleChange($event.target.value)" />
3262+ <span class="err-tea">{{ errors[0] }}</span>
3263+ </Field>
3264+ <Field name="drink" rules="required" v-slot="{ errors, handleChange }">
3265+ <input type="radio" name="drink" value="Coke" @change="handleChange($event.target.value)" />
3266+ <span class="err-coke">{{ errors[0] }}</span>
3267+ </Field>
3268+
3269+ <span id="form-err">{{ formErrors.drink }}</span>
3270+ <button>Submit</button>
3271+ </VForm>
3272+ ` ,
3273+ } ) ;
3274+
3275+ const errCoffee = wrapper . $el . querySelector ( '.err-coffee' ) ;
3276+ const errTea = wrapper . $el . querySelector ( '.err-tea' ) ;
3277+ const errCoke = wrapper . $el . querySelector ( '.err-coke' ) ;
3278+ const formErr = wrapper . $el . querySelector ( '#form-err' ) ;
3279+
3280+ // Submit without selecting a radio button to trigger required error
3281+ wrapper . $el . querySelector ( 'button' ) . click ( ) ;
3282+ await flushPromises ( ) ;
3283+
3284+ // All radio buttons should show the same error, not just the last one
3285+ expect ( formErr . textContent ) . toBe ( REQUIRED_MSG ) ;
3286+ expect ( errCoffee . textContent ) . toBe ( REQUIRED_MSG ) ;
3287+ expect ( errTea . textContent ) . toBe ( REQUIRED_MSG ) ;
3288+ expect ( errCoke . textContent ) . toBe ( REQUIRED_MSG ) ;
3289+ } ) ;
3290+
3291+ // #5001 - radio buttons with explicit type=radio on Field should share errors
3292+ test ( 'radio buttons with type=radio and same name should all expose errors' , async ( ) => {
3293+ const REQUIRED_MSG = 'This field is required' ;
3294+ defineRule ( 'required' , ( value : unknown ) => {
3295+ if ( ! value ) {
3296+ return REQUIRED_MSG ;
3297+ }
3298+ return true ;
3299+ } ) ;
3300+
3301+ const wrapper = mountWithHoc ( {
3302+ setup ( ) {
3303+ const schema = {
3304+ drink : 'required' ,
3305+ } ;
3306+
3307+ return {
3308+ schema,
3309+ } ;
3310+ } ,
3311+ template : `
3312+ <VForm :validation-schema="schema">
3313+ <Field name="drink" type="radio" value="" v-slot="{ errors, field }">
3314+ <input type="radio" v-bind="field" value="" />
3315+ <span class="err-coffee">{{ errors[0] }}</span>
3316+ </Field>
3317+ <Field name="drink" type="radio" value="Tea" v-slot="{ errors, field }">
3318+ <input type="radio" v-bind="field" value="Tea" />
3319+ <span class="err-tea">{{ errors[0] }}</span>
3320+ </Field>
3321+ <Field name="drink" type="radio" value="Coke" v-slot="{ errors, field }">
3322+ <input type="radio" v-bind="field" value="Coke" />
3323+ <span class="err-coke">{{ errors[0] }}</span>
3324+ </Field>
3325+
3326+ <button>Submit</button>
3327+ </VForm>
3328+ ` ,
3329+ } ) ;
3330+
3331+ const errCoffee = wrapper . $el . querySelector ( '.err-coffee' ) ;
3332+ const errTea = wrapper . $el . querySelector ( '.err-tea' ) ;
3333+ const errCoke = wrapper . $el . querySelector ( '.err-coke' ) ;
3334+
3335+ // Submit without selecting a radio button to trigger required error
3336+ wrapper . $el . querySelector ( 'button' ) . click ( ) ;
3337+ await flushPromises ( ) ;
3338+
3339+ // All radio buttons should show the same error
3340+ expect ( errCoffee . textContent ) . toBe ( REQUIRED_MSG ) ;
3341+ expect ( errTea . textContent ) . toBe ( REQUIRED_MSG ) ;
3342+ expect ( errCoke . textContent ) . toBe ( REQUIRED_MSG ) ;
3343+ } ) ;
0 commit comments