Skip to content

Commit 9803aa2

Browse files
fix: valibot intersections (#5014)
* fix: valibot intersections * Create famous-wombats-tan.md
1 parent 57e4193 commit 9803aa2

File tree

3 files changed

+112
-0
lines changed

3 files changed

+112
-0
lines changed

.changeset/famous-wombats-tan.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@vee-validate/valibot": patch
3+
---
4+
5+
fix: valibot intersections

packages/valibot/src/index.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ import {
2525
LooseObjectSchema,
2626
getDotPath,
2727
Config,
28+
IntersectSchema,
29+
IntersectIssue,
2830
} from 'valibot';
2931
import { isIndex, isObject, merge, normalizeFormPath } from '../../shared';
3032

@@ -134,6 +136,10 @@ function getSchemaForPath(
134136
path: string,
135137
schema: BaseSchema<unknown, unknown, BaseIssue<unknown>> | BaseSchemaAsync<unknown, unknown, BaseIssue<unknown>>,
136138
): BaseSchema<unknown, unknown, BaseIssue<unknown>> | null {
139+
if (isIntersectSchema(schema)) {
140+
return schema.options.map(o => getSchemaForPath(path, o)).find(Boolean) ?? null;
141+
}
142+
137143
if (!isObjectSchema(schema)) {
138144
return null;
139145
}
@@ -151,6 +157,10 @@ function getSchemaForPath(
151157
return currentSchema;
152158
}
153159

160+
if (isIntersectSchema(currentSchema)) {
161+
currentSchema = currentSchema.options.find(o => isObjectSchema(o) && o.entries[p]) ?? currentSchema;
162+
}
163+
154164
if (isObjectSchema(currentSchema)) {
155165
currentSchema = currentSchema.entries[p] || null;
156166
continue;
@@ -189,3 +199,12 @@ function isObjectSchema(
189199
| StrictObjectSchema<ObjectEntries, ErrorMessage<StrictObjectIssue> | undefined> {
190200
return isObject(schema) && 'entries' in schema;
191201
}
202+
203+
function isIntersectSchema(
204+
schema: BaseSchema<unknown, unknown, BaseIssue<unknown>> | BaseSchemaAsync<unknown, unknown, BaseIssue<unknown>>,
205+
): schema is IntersectSchema<
206+
BaseSchema<unknown, unknown, BaseIssue<unknown>>[],
207+
ErrorMessage<IntersectIssue> | undefined
208+
> {
209+
return schema.type === 'intersect';
210+
}

packages/valibot/tests/valibot.spec.ts

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -568,6 +568,94 @@ test('reports required state for field-level schemas without a form context', as
568568
);
569569
});
570570

571+
test('reports required state for schema intersections', async () => {
572+
const metaSpy = vi.fn();
573+
mountWithHoc({
574+
setup() {
575+
const schema = toTypedSchema(
576+
v.intersect([
577+
v.object({
578+
req: v.string(),
579+
}),
580+
v.object({
581+
nreq: v.optional(v.string()),
582+
}),
583+
]),
584+
);
585+
586+
useForm({
587+
validationSchema: schema,
588+
});
589+
590+
const { meta: req } = useField('req');
591+
const { meta: nreq } = useField('nreq');
592+
593+
metaSpy({
594+
req: req.required,
595+
nreq: nreq.required,
596+
});
597+
598+
return {
599+
schema,
600+
};
601+
},
602+
template: `<div></div>`,
603+
});
604+
605+
await flushPromises();
606+
await expect(metaSpy).toHaveBeenLastCalledWith(
607+
expect.objectContaining({
608+
req: true,
609+
nreq: false,
610+
}),
611+
);
612+
});
613+
614+
test('reports required state for schema intersections with nested fields', async () => {
615+
const metaSpy = vi.fn();
616+
mountWithHoc({
617+
setup() {
618+
const schema = toTypedSchema(
619+
v.object({
620+
fields: v.intersect([
621+
v.object({
622+
req: v.string(),
623+
}),
624+
v.object({
625+
nreq: v.optional(v.string()),
626+
}),
627+
]),
628+
}),
629+
);
630+
631+
useForm({
632+
validationSchema: schema,
633+
});
634+
635+
const { meta: req } = useField('fields.req');
636+
const { meta: nreq } = useField('fields.nreq');
637+
638+
metaSpy({
639+
req: req.required,
640+
nreq: nreq.required,
641+
});
642+
643+
return {
644+
schema,
645+
};
646+
},
647+
template: `<div></div>`,
648+
});
649+
650+
await flushPromises();
651+
await expect(metaSpy).toHaveBeenLastCalledWith(
652+
expect.objectContaining({
653+
req: true,
654+
nreq: false,
655+
}),
656+
);
657+
});
658+
571659
test('allows passing valibot config', async () => {
572660
let errors!: Ref<string[]>;
573661
const wrapper = mountWithHoc({

0 commit comments

Comments
 (0)