diff --git a/.changeset/green-wolves-reply.md b/.changeset/green-wolves-reply.md new file mode 100644 index 000000000..4612022d5 --- /dev/null +++ b/.changeset/green-wolves-reply.md @@ -0,0 +1,5 @@ +--- +'vee-validate': patch +--- + +feat: allow null as a valid Form prop type closes #4483 diff --git a/packages/vee-validate/src/Form.ts b/packages/vee-validate/src/Form.ts index 1ad6b6ac4..6f49c7920 100644 --- a/packages/vee-validate/src/Form.ts +++ b/packages/vee-validate/src/Form.ts @@ -40,7 +40,7 @@ const FormImpl = /** #__PURE__ */ defineComponent({ inheritAttrs: false, props: { as: { - type: String, + type: null as unknown as PropType, default: 'form', }, validationSchema: { @@ -196,16 +196,16 @@ const FormImpl = /** #__PURE__ */ defineComponent({ return function renderForm() { // avoid resolving the form component as itself - const tag = props.as === 'form' ? props.as : (resolveDynamicComponent(props.as) as string); + const tag = props.as === 'form' ? props.as : !props.as ? null : (resolveDynamicComponent(props.as) as string); const children = normalizeChildren(tag, ctx, slotProps as any); - if (!props.as) { + if (!tag) { return children; } // Attributes to add on a native `form` tag const formAttrs = - props.as === 'form' + tag === 'form' ? { // Disables native validation as vee-validate will handle it. novalidate: true, diff --git a/packages/vee-validate/src/utils/vnode.ts b/packages/vee-validate/src/utils/vnode.ts index 86da764e8..1dffd6d8b 100644 --- a/packages/vee-validate/src/utils/vnode.ts +++ b/packages/vee-validate/src/utils/vnode.ts @@ -3,11 +3,11 @@ import { SetupContext } from 'vue'; type HTMLElementWithValueBinding = HTMLElement & { _value: unknown }; // eslint-disable-next-line @typescript-eslint/no-explicit-any -export const normalizeChildren = ( - tag: string | Record | undefined, +export function normalizeChildren( + tag: string | null, context: SetupContext, slotProps: () => Record, -) => { +) { if (!context.slots.default) { return context.slots.default; } @@ -19,7 +19,7 @@ export const normalizeChildren = ( return { default: () => context.slots.default?.(slotProps()), }; -}; +} /** * Vue adds a `_value` prop at the moment on the input elements to store the REAL value on them, real values are different than the `value` attribute diff --git a/packages/vee-validate/tests/Form.spec.ts b/packages/vee-validate/tests/Form.spec.ts index 5fb171189..590437543 100644 --- a/packages/vee-validate/tests/Form.spec.ts +++ b/packages/vee-validate/tests/Form.spec.ts @@ -351,6 +351,39 @@ describe('
', () => { expect(submitMock).toHaveBeenCalledTimes(1); }); + test('can be renderless with null', async () => { + const submitMock = vi.fn(); + const wrapper = mountWithHoc({ + template: ` +
+ + + + {{ errors.field }} + + + + +
+ `, + }); + + const form = wrapper.$el.querySelector('form'); + form.submit = submitMock; + const input = wrapper.$el.querySelector('input'); + await flushPromises(); + + wrapper.$el.querySelector('button').click(); + await flushPromises(); + expect(submitMock).toHaveBeenCalledTimes(0); + + setValue(input, '12'); + wrapper.$el.querySelector('button').click(); + await flushPromises(); + + expect(submitMock).toHaveBeenCalledTimes(1); + }); + test('validation schema with yup', async () => { const wrapper = mountWithHoc({ setup() {