From 64114f02087c2342ba25f839c1643b43d7bb4fed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Wed, 27 Aug 2025 05:40:39 +0200 Subject: [PATCH] Form tests --- packages/example/src/routeTree.gen.ts | 28 ++++++++++++- packages/example/src/routes/form.tsx | 57 +++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 packages/example/src/routes/form.tsx diff --git a/packages/example/src/routeTree.gen.ts b/packages/example/src/routeTree.gen.ts index 7f917e3..d29c115 100644 --- a/packages/example/src/routeTree.gen.ts +++ b/packages/example/src/routeTree.gen.ts @@ -9,12 +9,18 @@ // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. import { Route as rootRouteImport } from './routes/__root' +import { Route as FormRouteImport } from './routes/form' import { Route as BlankRouteImport } from './routes/blank' import { Route as IndexRouteImport } from './routes/index' import { Route as DevMemoRouteImport } from './routes/dev/memo' import { Route as DevInputRouteImport } from './routes/dev/input' import { Route as DevAsyncRenderingRouteImport } from './routes/dev/async-rendering' +const FormRoute = FormRouteImport.update({ + id: '/form', + path: '/form', + getParentRoute: () => rootRouteImport, +} as any) const BlankRoute = BlankRouteImport.update({ id: '/blank', path: '/blank', @@ -44,6 +50,7 @@ const DevAsyncRenderingRoute = DevAsyncRenderingRouteImport.update({ export interface FileRoutesByFullPath { '/': typeof IndexRoute '/blank': typeof BlankRoute + '/form': typeof FormRoute '/dev/async-rendering': typeof DevAsyncRenderingRoute '/dev/input': typeof DevInputRoute '/dev/memo': typeof DevMemoRoute @@ -51,6 +58,7 @@ export interface FileRoutesByFullPath { export interface FileRoutesByTo { '/': typeof IndexRoute '/blank': typeof BlankRoute + '/form': typeof FormRoute '/dev/async-rendering': typeof DevAsyncRenderingRoute '/dev/input': typeof DevInputRoute '/dev/memo': typeof DevMemoRoute @@ -59,6 +67,7 @@ export interface FileRoutesById { __root__: typeof rootRouteImport '/': typeof IndexRoute '/blank': typeof BlankRoute + '/form': typeof FormRoute '/dev/async-rendering': typeof DevAsyncRenderingRoute '/dev/input': typeof DevInputRoute '/dev/memo': typeof DevMemoRoute @@ -68,15 +77,23 @@ export interface FileRouteTypes { fullPaths: | '/' | '/blank' + | '/form' | '/dev/async-rendering' | '/dev/input' | '/dev/memo' fileRoutesByTo: FileRoutesByTo - to: '/' | '/blank' | '/dev/async-rendering' | '/dev/input' | '/dev/memo' + to: + | '/' + | '/blank' + | '/form' + | '/dev/async-rendering' + | '/dev/input' + | '/dev/memo' id: | '__root__' | '/' | '/blank' + | '/form' | '/dev/async-rendering' | '/dev/input' | '/dev/memo' @@ -85,6 +102,7 @@ export interface FileRouteTypes { export interface RootRouteChildren { IndexRoute: typeof IndexRoute BlankRoute: typeof BlankRoute + FormRoute: typeof FormRoute DevAsyncRenderingRoute: typeof DevAsyncRenderingRoute DevInputRoute: typeof DevInputRoute DevMemoRoute: typeof DevMemoRoute @@ -92,6 +110,13 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { + '/form': { + id: '/form' + path: '/form' + fullPath: '/form' + preLoaderRoute: typeof FormRouteImport + parentRoute: typeof rootRouteImport + } '/blank': { id: '/blank' path: '/blank' @@ -133,6 +158,7 @@ declare module '@tanstack/react-router' { const rootRouteChildren: RootRouteChildren = { IndexRoute: IndexRoute, BlankRoute: BlankRoute, + FormRoute: FormRoute, DevAsyncRenderingRoute: DevAsyncRenderingRoute, DevInputRoute: DevInputRoute, DevMemoRoute: DevMemoRoute, diff --git a/packages/example/src/routes/form.tsx b/packages/example/src/routes/form.tsx new file mode 100644 index 0000000..e1cd935 --- /dev/null +++ b/packages/example/src/routes/form.tsx @@ -0,0 +1,57 @@ +import { runtime } from "@/runtime" +import { Container, Flex, TextField } from "@radix-ui/themes" +import { createFileRoute } from "@tanstack/react-router" +import { Console, Effect, Schema, Stream } from "effect" +import { Component, Form } from "effect-fc" +import { useContext, useFork } from "effect-fc/hooks" + + +const LoginFormSchema = Schema.Struct({ + email: Schema.String, + password: Schema.String, +}) + +class LoginForm extends Effect.Service()("LoginForm", { + scoped: Form.make({ + schema: LoginFormSchema, + initialValue: { email: "", password: "" }, + }) +}) {} + +class LoginFormComponent extends Component.makeUntraced(function* LoginFormComponent() { + const form = yield* LoginForm + const emailInput = yield* form.useInput(["email"]) + const passwordInput = yield* form.useInput(["password"]) + + yield* useFork(() => Stream.runForEach(form.latestValueSubscribable.changes, Console.log)) + + return ( + + + emailInput.setValue(e.target.value)} + /> + + passwordInput.setValue(e.target.value)} + /> + + + ) +}) {} + + +const FormRoute = Component.makeUntraced(function* FormRoute() { + const context = yield* useContext(LoginForm.Default) + const LoginFormComponentFC = yield* Effect.provide(LoginFormComponent, context) + + return +}).pipe( + Component.withRuntime(runtime.context) +) + +export const Route = createFileRoute("/form")({ + component: FormRoute +})