diff --git a/.changeset/little-planets-bake.md b/.changeset/little-planets-bake.md new file mode 100644 index 000000000..8ab5b6b64 --- /dev/null +++ b/.changeset/little-planets-bake.md @@ -0,0 +1,5 @@ +--- +"@effect-app/vue-components": minor +--- + +Restore OmegaForm input reactivity broken by the @tanstack/vue-form 1.32 bump — the Field slot's state prop became a stale snapshot, so bind inputs to the reactive field.state instead. diff --git a/packages/vue-components/__tests__/OmegaForm/InputReactivity.test.ts b/packages/vue-components/__tests__/OmegaForm/InputReactivity.test.ts new file mode 100644 index 000000000..37b2b4996 --- /dev/null +++ b/packages/vue-components/__tests__/OmegaForm/InputReactivity.test.ts @@ -0,0 +1,49 @@ +import { mount } from "@vue/test-utils" +import * as S from "effect-app/Schema" +import { describe, expect, it } from "vitest" +import { useOmegaForm } from "../../src/components/OmegaForm" +import OmegaIntlProvider from "../OmegaIntlProvider.vue" + +describe("OmegaForm input reactivity", () => { + it("reflects value changes from the slot `state` in the view", async () => { + const schema = S.Struct({ + name: S.String + }) + + const wrapper = mount({ + components: { OmegaIntlProvider }, + template: ` + + + + + + + + `, + setup() { + const form = useOmegaForm(schema, { + defaultValues: { name: "" } + }) + return { form } + } + }) + + await wrapper.vm.$nextTick() + + const input = wrapper.find("[data-testid='input']") + await input.setValue("hello") + await wrapper.vm.$nextTick() + + // The slot `state` must stay in sync with the form store + expect(wrapper.find("[data-testid='display']").text()).toBe("hello") + expect((input.element as HTMLInputElement).value).toBe("hello") + }) +}) diff --git a/packages/vue-components/src/components/OmegaForm/InputProps.ts b/packages/vue-components/src/components/OmegaForm/InputProps.ts index 090c257ca..eaec0cf14 100644 --- a/packages/vue-components/src/components/OmegaForm/InputProps.ts +++ b/packages/vue-components/src/components/OmegaForm/InputProps.ts @@ -43,7 +43,7 @@ export type InputProps, TName extends Deep inputClass: string | undefined | null } field: OmegaFieldInternalApi - /** be sure to use this state and not `field.state` as it is not reactive */ + /** reactive field state, sourced from `field.state` (the Field slot's own `state` prop is a stale snapshot since @tanstack/vue-form 1.32) */ state: OmegaFieldInternalApi["state"] } diff --git a/packages/vue-components/src/components/OmegaForm/OmegaArray.vue b/packages/vue-components/src/components/OmegaForm/OmegaArray.vue index f6a630427..ec1ca29eb 100644 --- a/packages/vue-components/src/components/OmegaForm/OmegaArray.vue +++ b/packages/vue-components/src/components/OmegaForm/OmegaArray.vue @@ -3,10 +3,11 @@ :is="form.Field" :name="name" > -