diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 987cb212f9..0bcc5479d4 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -30,6 +30,7 @@ jobs: "examples/inspector", "examples/music-player", "examples/organization", + "examples/tanstack-form", "starters/react-passkey-auth", "starters/svelte-passkey-auth", "tests/jazz-svelte" diff --git a/examples/tanstack-form/.gitignore b/examples/tanstack-form/.gitignore new file mode 100644 index 0000000000..576212b70b --- /dev/null +++ b/examples/tanstack-form/.gitignore @@ -0,0 +1,33 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +/test-results/ +/playwright-report/ + +# env files +.env +.env.* +!.env.example +!.env.test diff --git a/examples/tanstack-form/CHANGELOG.md b/examples/tanstack-form/CHANGELOG.md new file mode 100644 index 0000000000..b540e82eed --- /dev/null +++ b/examples/tanstack-form/CHANGELOG.md @@ -0,0 +1,858 @@ +# form + +## 0.1.60 + +### Patch Changes + +- Updated dependencies [048ac1d] + - jazz-tools@0.14.22 + - jazz-react@0.14.22 + +## 0.1.59 + +### Patch Changes + +- Updated dependencies [e7e505e] +- Updated dependencies [13b57aa] +- Updated dependencies [5662faa] +- Updated dependencies [2116a59] + - jazz-tools@0.14.21 + - jazz-react@0.14.21 + +## 0.1.58 + +### Patch Changes + +- Updated dependencies [6f72419] +- Updated dependencies [04b20c2] + - jazz-tools@0.14.20 + - jazz-react@0.14.20 + +## 0.1.57 + +### Patch Changes + +- jazz-react@0.14.19 +- jazz-tools@0.14.19 + +## 0.1.56 + +### Patch Changes + +- Updated dependencies [4b950bc] +- Updated dependencies [d6d9c0a] +- Updated dependencies [c559054] + - jazz-tools@0.14.18 + - jazz-react@0.14.18 + +## 0.1.55 + +### Patch Changes + +- Updated dependencies [e512df4] + - jazz-tools@0.14.17 + - jazz-react@0.14.17 + +## 0.1.54 + +### Patch Changes + +- jazz-react@0.14.16 +- jazz-tools@0.14.16 + +## 0.1.53 + +### Patch Changes + +- Updated dependencies [f9590f9] + - jazz-react@0.14.15 + - jazz-tools@0.14.15 + +## 0.1.52 + +### Patch Changes + +- Updated dependencies [e32a1f7] + - jazz-tools@0.14.14 + - jazz-react@0.14.14 + +## 0.1.51 + +### Patch Changes + +- jazz-react@0.14.13 + +## 0.1.50 + +### Patch Changes + +- jazz-react@0.14.12 + +## 0.1.49 + +### Patch Changes + +- Updated dependencies [dc746a2] +- Updated dependencies [f869d9a] +- Updated dependencies [3fe6832] + - hash-slash@0.2.3 + - jazz-react@0.14.10 + - jazz-tools@0.14.10 + +## 0.1.48 + +### Patch Changes + +- Updated dependencies [22c2600] + - jazz-tools@0.14.9 + - jazz-react@0.14.9 + +## 0.1.47 + +### Patch Changes + +- Updated dependencies [637ae13] + - jazz-tools@0.14.8 + - jazz-react@0.14.8 + +## 0.1.46 + +### Patch Changes + +- Updated dependencies [365b0ea] + - jazz-tools@0.14.7 + - jazz-react@0.14.7 + +## 0.1.45 + +### Patch Changes + +- Updated dependencies [9d6d9fe] +- Updated dependencies [9d6d9fe] + - jazz-tools@0.14.6 + - jazz-react@0.14.6 + +## 0.1.44 + +### Patch Changes + +- Updated dependencies [91cbb2f] +- Updated dependencies [20b3d88] + - jazz-tools@0.14.5 + - jazz-react@0.14.5 + +## 0.1.43 + +### Patch Changes + +- Updated dependencies [011af55] + - jazz-tools@0.14.4 + - jazz-react@0.14.4 + +## 0.1.42 + +### Patch Changes + +- Updated dependencies [3d1027f] +- Updated dependencies [c240eed] + - jazz-tools@0.14.2 + - jazz-react@0.14.2 + +## 0.1.41 + +### Patch Changes + +- Updated dependencies [cdfc105] + - jazz-tools@0.14.1 + - jazz-react@0.14.1 + +## 0.1.40 + +### Patch Changes + +- Updated dependencies [5835ed1] + - jazz-tools@0.14.0 + - jazz-react@0.14.0 + +## 0.1.39 + +### Patch Changes + +- jazz-react@0.13.32 + +## 0.1.38 + +### Patch Changes + +- Updated dependencies [e5b170f] + - jazz-tools@0.13.31 + - jazz-react@0.13.31 + +## 0.1.37 + +### Patch Changes + +- jazz-react@0.13.30 +- jazz-tools@0.13.30 + +## 0.1.36 + +### Patch Changes + +- jazz-react@0.13.29 +- jazz-tools@0.13.29 + +## 0.1.35 + +### Patch Changes + +- jazz-react@0.13.28 +- jazz-tools@0.13.28 + +## 0.1.34 + +### Patch Changes + +- jazz-react@0.13.27 +- jazz-tools@0.13.27 + +## 0.1.33 + +### Patch Changes + +- Updated dependencies [ff846d9] + - jazz-tools@0.13.26 + - jazz-react@0.13.26 + +## 0.1.32 + +### Patch Changes + +- jazz-react@0.13.25 +- jazz-tools@0.13.25 + +## 0.1.31 + +### Patch Changes + +- Updated dependencies [02a240c] + - jazz-tools@0.13.23 + - jazz-react@0.13.23 + +## 0.1.30 + +### Patch Changes + +- jazz-react@0.13.21 +- jazz-tools@0.13.21 + +## 0.1.29 + +### Patch Changes + +- Updated dependencies [439f0fe] + - jazz-tools@0.13.20 + - jazz-react@0.13.20 + +## 0.1.28 + +### Patch Changes + +- Updated dependencies [80530a4] + - jazz-tools@0.13.19 + - jazz-react@0.13.19 + +## 0.1.27 + +### Patch Changes + +- Updated dependencies [761759c] + - jazz-tools@0.13.18 + - jazz-react@0.13.18 + +## 0.1.26 + +### Patch Changes + +- jazz-react@0.13.17 +- jazz-tools@0.13.17 + +## 0.1.25 + +### Patch Changes + +- jazz-react@0.13.16 +- jazz-tools@0.13.16 + +## 0.1.24 + +### Patch Changes + +- jazz-react@0.13.15 +- jazz-tools@0.13.15 + +## 0.1.23 + +### Patch Changes + +- jazz-react@0.13.14 +- jazz-tools@0.13.14 + +## 0.1.22 + +### Patch Changes + +- jazz-react@0.13.13 +- jazz-tools@0.13.13 + +## 0.1.21 + +### Patch Changes + +- Updated dependencies [4547525] + - jazz-tools@0.13.12 + - jazz-react@0.13.12 + +## 0.1.20 + +### Patch Changes + +- Updated dependencies [17273a6] + - jazz-tools@0.13.11 + - jazz-react@0.13.11 + +## 0.1.19 + +### Patch Changes + +- jazz-react@0.13.10 +- jazz-tools@0.13.10 + +## 0.1.18 + +### Patch Changes + +- Updated dependencies [a6cf01f] + - jazz-tools@0.13.9 + - jazz-react@0.13.9 + +## 0.1.17 + +### Patch Changes + +- Updated dependencies [bc3d7bb] + - jazz-tools@0.13.7 + - jazz-react@0.13.7 + +## 0.1.16 + +### Patch Changes + +- Updated dependencies [fe6f561] + - jazz-tools@0.13.5 + - jazz-react@0.13.5 + +## 0.1.15 + +### Patch Changes + +- Updated dependencies [3129982] + - jazz-react@0.13.4 + - jazz-tools@0.13.4 + +## 0.1.14 + +### Patch Changes + +- Updated dependencies [12f8bfa] +- Updated dependencies [bd57177] + - jazz-tools@0.13.3 + - jazz-react@0.13.3 + +## 0.1.13 + +### Patch Changes + +- jazz-react@0.13.2 +- jazz-tools@0.13.2 + +## 0.1.12 + +### Patch Changes + +- Updated dependencies [afd1374] + - jazz-tools@0.13.0 + - jazz-react@0.13.0 + +## 0.1.11 + +### Patch Changes + +- jazz-react@0.12.2 +- jazz-tools@0.12.2 + +## 0.1.10 + +### Patch Changes + +- jazz-react@0.12.1 +- jazz-tools@0.12.1 + +## 0.1.9 + +### Patch Changes + +- Updated dependencies [01523dc] +- Updated dependencies [4ea87dc] +- Updated dependencies [1e6da19] +- Updated dependencies [b6c6a0a] + - jazz-tools@0.12.0 + - jazz-react@0.12.0 + +## 0.1.8 + +### Patch Changes + +- jazz-react@0.11.8 +- jazz-tools@0.11.8 + +## 0.1.7 + +### Patch Changes + +- Updated dependencies [a140f55] +- Updated dependencies [4019918] +- Updated dependencies [2b0d1b0] + - jazz-tools@0.11.7 + - jazz-react@0.11.7 + +## 0.1.6 + +### Patch Changes + +- Updated dependencies [e7c85b7] +- Updated dependencies [8ed144e] + - jazz-react@0.11.6 + - jazz-tools@0.11.6 + - jazz-browser-media-images@0.11.6 + +## 0.1.5 + +### Patch Changes + +- jazz-react@0.11.5 +- jazz-tools@0.11.5 +- jazz-browser-media-images@0.11.5 + +## 0.1.4 + +### Patch Changes + +- Updated dependencies [57a3dbe] +- Updated dependencies [a717754] +- Updated dependencies [a91f343] + - jazz-tools@0.11.4 + - jazz-browser-media-images@0.11.4 + - jazz-react@0.11.4 + +## 0.1.3 + +### Patch Changes + +- jazz-react@0.11.3 +- jazz-tools@0.11.3 +- jazz-browser-media-images@0.11.3 + +## 0.1.2 + +### Patch Changes + +- Updated dependencies [6892dc6] + - jazz-tools@0.11.2 + - jazz-react@0.11.2 + - jazz-browser-media-images@0.11.2 + +## 0.1.1 + +### Patch Changes + +- jazz-react@0.11.1 + +## 0.1.0 + +### Minor Changes + +- 18428ea: PasskeyAuth: Sets `profile.name` only if a non-empty username is passed to `signUp` + +### Patch Changes + +- Updated dependencies [6a96d8b] +- Updated dependencies [a35249a] +- Updated dependencies [b9d194a] +- Updated dependencies [a4713df] +- Updated dependencies [34cbdc3] +- Updated dependencies [f039e8f] +- Updated dependencies [e22de9f] + - jazz-tools@0.11.0 + - jazz-browser-media-images@0.11.0 + - jazz-react@0.11.0 + +## 0.0.53 + +### Patch Changes + +- Updated dependencies [2f99de0] + - jazz-tools@0.10.15 + - jazz-browser-media-images@0.10.15 + - jazz-react@0.10.15 + +## 0.0.52 + +### Patch Changes + +- Updated dependencies [75211e3] + - jazz-tools@0.10.14 + - jazz-react@0.10.14 + - jazz-browser-media-images@0.10.14 + +## 0.0.51 + +### Patch Changes + +- Updated dependencies [07feedd] + - jazz-tools@0.10.13 + - jazz-browser-media-images@0.10.13 + - jazz-react@0.10.13 + +## 0.0.50 + +### Patch Changes + +- Updated dependencies [4612e05] + - jazz-tools@0.10.12 + - jazz-react@0.10.12 + - jazz-browser-media-images@0.10.12 + +## 0.0.49 + +### Patch Changes + +- jazz-browser-media-images@0.10.9 +- jazz-react@0.10.9 + +## 0.0.48 + +### Patch Changes + +- Updated dependencies [2fb6428] + - jazz-tools@0.10.8 + - jazz-react@0.10.8 + - jazz-browser-media-images@0.10.8 + +## 0.0.47 + +### Patch Changes + +- Updated dependencies [1136d9b] +- Updated dependencies [0eed228] + - jazz-react@0.10.7 + - jazz-tools@0.10.7 + - jazz-browser-media-images@0.10.7 + +## 0.0.46 + +### Patch Changes + +- Updated dependencies [1d71ca1] +- Updated dependencies [ada802b] + - hash-slash@0.2.2 + - jazz-react@0.10.6 + - jazz-tools@0.10.6 + - jazz-browser-media-images@0.10.6 + +## 0.0.45 + +### Patch Changes + +- Updated dependencies [59ff77e] + - jazz-tools@0.10.5 + - jazz-browser-media-images@0.10.5 + - jazz-react@0.10.5 + +## 0.0.44 + +### Patch Changes + +- jazz-react@0.10.4 +- jazz-tools@0.10.4 +- jazz-browser-media-images@0.10.4 + +## 0.0.43 + +### Patch Changes + +- Updated dependencies [d8582fc] + - jazz-tools@0.10.3 + - jazz-browser-media-images@0.10.3 + - jazz-react@0.10.3 + +## 0.0.42 + +### Patch Changes + +- jazz-react@0.10.2 +- jazz-tools@0.10.2 +- jazz-browser-media-images@0.10.2 + +## 0.0.41 + +### Patch Changes + +- Updated dependencies [5a63cba] + - jazz-tools@0.10.1 + - jazz-browser-media-images@0.10.1 + - jazz-react@0.10.1 + +## 0.0.40 + +### Patch Changes + +- Updated dependencies [498954f] +- Updated dependencies [d42c2aa] +- Updated dependencies [dd03464] +- Updated dependencies [b426342] + - jazz-react@0.10.0 + - jazz-tools@0.10.0 + - jazz-browser-media-images@0.10.0 + +## 0.0.39 + +### Patch Changes + +- jazz-react@0.9.23 +- jazz-tools@0.9.23 +- jazz-browser-media-images@0.9.23 + +## 0.0.38 + +### Patch Changes + +- jazz-browser-media-images@0.9.22 +- jazz-react@0.9.22 + +## 0.0.37 + +### Patch Changes + +- Updated dependencies [1be017d] + - jazz-tools@0.9.21 + - jazz-browser-media-images@0.9.21 + - jazz-react@0.9.21 + +## 0.0.36 + +### Patch Changes + +- Updated dependencies [b01cc1f] + - jazz-tools@0.9.20 + - jazz-browser-media-images@0.9.20 + - jazz-react@0.9.20 + +## 0.0.35 + +### Patch Changes + +- jazz-react@0.9.19 +- jazz-tools@0.9.19 +- jazz-browser-media-images@0.9.19 + +## 0.0.34 + +### Patch Changes + +- jazz-react@0.9.18 +- jazz-tools@0.9.18 +- jazz-browser-media-images@0.9.18 + +## 0.0.33 + +### Patch Changes + +- Updated dependencies [c2ca1fe] +- Updated dependencies [1227047] + - jazz-tools@0.9.17 + - jazz-browser-media-images@0.9.17 + - jazz-react@0.9.17 + +## 0.0.32 + +### Patch Changes + +- Updated dependencies [24b3b6a] + - jazz-tools@0.9.16 + - jazz-browser-media-images@0.9.16 + - jazz-react@0.9.16 + +## 0.0.31 + +### Patch Changes + +- Updated dependencies [7491711] + - jazz-tools@0.9.15 + - jazz-browser-media-images@0.9.15 + - jazz-react@0.9.15 + +## 0.0.30 + +### Patch Changes + +- Updated dependencies [3df93cc] + - jazz-tools@0.9.14 + - jazz-browser-media-images@0.9.14 + - jazz-react@0.9.14 + +## 0.0.29 + +### Patch Changes + +- jazz-react@0.9.13 +- jazz-tools@0.9.13 +- jazz-browser-media-images@0.9.13 + +## 0.0.28 + +### Patch Changes + +- jazz-react@0.9.12 +- jazz-tools@0.9.12 +- jazz-browser-media-images@0.9.12 + +## 0.0.27 + +### Patch Changes + +- jazz-react@0.9.11 +- jazz-tools@0.9.11 +- jazz-browser-media-images@0.9.11 + +## 0.0.26 + +### Patch Changes + +- Updated dependencies [5e83864] + - jazz-react@0.9.10 + - jazz-tools@0.9.10 + - jazz-browser-media-images@0.9.10 + +## 0.0.25 + +### Patch Changes + +- Updated dependencies [8eb9247] + - jazz-tools@0.9.9 + - jazz-browser-media-images@0.9.9 + - jazz-react@0.9.9 + +## 0.0.24 + +### Patch Changes + +- Updated dependencies [d1d773b] + - jazz-tools@0.9.8 + - jazz-react@0.9.8 + - jazz-browser-media-images@0.9.8 + +## 0.0.23 + +### Patch Changes + +- jazz-react@0.9.4 + +## 0.0.22 + +### Patch Changes + +- Updated dependencies [1b71969] + - jazz-react@0.9.1 + - jazz-tools@0.9.1 + - jazz-browser-media-images@0.9.1 + +## 0.0.21 + +### Patch Changes + +- Updated dependencies [956a4d1] +- Updated dependencies [8eda792] + - jazz-react@0.9.0 + - jazz-tools@0.9.0 + - jazz-browser-media-images@0.9.0 + +## 0.0.20 + +### Patch Changes + +- Updated dependencies [dc62b95] +- Updated dependencies [1de26f8] + - jazz-tools@0.8.51 + - jazz-browser-media-images@0.8.51 + - jazz-react@0.8.51 + +## 0.0.19 + +### Patch Changes + +- jazz-react@0.8.50 +- jazz-tools@0.8.50 +- jazz-browser-media-images@0.8.50 + +## 0.0.18 + +### Patch Changes + +- jazz-react@0.8.49 +- jazz-tools@0.8.49 +- jazz-browser-media-images@0.8.49 + +## 0.0.17 + +### Patch Changes + +- Updated dependencies [635e824] +- Updated dependencies [0a85982] + - jazz-tools@0.8.48 + - jazz-browser-media-images@0.8.48 + - jazz-react@0.8.48 + +## 0.0.16 + +### Patch Changes + +- Updated dependencies [fa41f8e] +- Updated dependencies [88d7d9a] +- Updated dependencies [60e35ea] + - jazz-tools@0.8.45 + - jazz-react@0.8.45 + - jazz-browser-media-images@0.8.45 + +## 0.0.15 + +### Patch Changes + +- jazz-react@0.8.44 +- jazz-tools@0.8.44 +- jazz-browser-media-images@0.8.44 + +## 0.0.14 + +### Patch Changes + +- jazz-react@0.8.41 +- jazz-tools@0.8.41 +- jazz-browser-media-images@0.8.41 + +## 0.0.13 + +### Patch Changes + +- jazz-browser-media-images@0.8.40 +- jazz-react@0.8.40 diff --git a/examples/tanstack-form/README.md b/examples/tanstack-form/README.md new file mode 100644 index 0000000000..46b1edafb2 --- /dev/null +++ b/examples/tanstack-form/README.md @@ -0,0 +1,69 @@ +# Form example with Jazz and TanStack Form + +This is a simple form example that shows you how to make a form for creating and editing a `CoValue`, +called `BubbleTeaOrder`, with fields of different types such +as single-select, multi-select, date, text, and boolean. + +The form is built using [TanStack Form](https://tanstack.com/form/). We leverage TanStack Form's +support for [schema validation libraries](https://tanstack.com/form/latest/docs/framework/react/guides/validation#standard-schema-libraries) +and validate the form using Jazz's Zod-based schemas. + +## Getting started + +You can either +1. Clone the jazz repository, and run the app within the monorepo. +2. Or create a new Jazz project using this example as a template. + + +### Using the example as a template + +Create a new Jazz project, and use this example as a template. +```bash +npx create-jazz-app@latest form-app --example tanstack-form +``` + +Go to the new project directory. +```bash +cd form-app +``` + +Run the dev server. +```bash +npm run dev +``` + +### Using the monorepo + +This requires `pnpm` to be installed, see [https://pnpm.io/installation](https://pnpm.io/installation). + +Clone the jazz repository. +```bash +git clone https://github.com/garden-co/jazz.git +``` + +Install and build dependencies. +```bash +pnpm i && npx turbo build +``` + +Go to the example directory. +```bash +cd jazz/examples/tanstack-form/ +``` + +Start the dev server. +```bash +pnpm dev +``` + +Open [http://localhost:5173](http://localhost:5173) with your browser to see the result. + +## Questions / problems / feedback + +If you have feedback, let us know on [Discord](https://discord.gg/utDMjHYg42) or open an issue or PR to fix something that seems wrong. + +## Configuration: sync server + +By default, the example app uses [Jazz Cloud](https://jazz.tools/cloud) (`wss://cloud.jazz.tools`) - so cross-device use, invites and collaboration should just work. + +You can also run a local sync server by running `npx jazz-run sync`, and setting the `sync` parameter of `JazzReactProvider` in [./src/main.tsx](./src/main.tsx) to `{ peer: "ws://localhost:4200" }`. diff --git a/examples/tanstack-form/index.html b/examples/tanstack-form/index.html new file mode 100644 index 0000000000..547077c4c4 --- /dev/null +++ b/examples/tanstack-form/index.html @@ -0,0 +1,13 @@ + + + + + + + Jazz | Form example + + +
+ + + diff --git a/examples/tanstack-form/package.json b/examples/tanstack-form/package.json new file mode 100644 index 0000000000..c8cda1b49f --- /dev/null +++ b/examples/tanstack-form/package.json @@ -0,0 +1,35 @@ +{ + "name": "tanstack-form", + "private": true, + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "check": "tsc --noEmit", + "preview": "vite preview", + "format-and-lint": "biome check .", + "format-and-lint:fix": "biome check . --write" + }, + "dependencies": { + "@tanstack/react-form": "^1.0.0", + "hash-slash": "workspace:*", + "jazz-tools": "workspace:*", + "react": "19.0.0", + "react-dom": "19.0.0" + }, + "devDependencies": { + "@biomejs/biome": "1.9.4", + "@playwright/test": "^1.50.1", + "@tailwindcss/forms": "^0.5.10", + "@tailwindcss/postcss": "^4.1.10", + "@types/react": "19.0.0", + "@types/react-dom": "19.0.0", + "@vitejs/plugin-react": "^4.5.1", + "globals": "^15.11.0", + "is-ci": "^3.0.1", + "postcss": "^8.4.40", + "tailwindcss": "^4.1.10", + "typescript": "5.6.2", + "vite": "^6.3.5" + } +} diff --git a/examples/tanstack-form/playwright.config.ts b/examples/tanstack-form/playwright.config.ts new file mode 100644 index 0000000000..ecb4f4f255 --- /dev/null +++ b/examples/tanstack-form/playwright.config.ts @@ -0,0 +1,46 @@ +import { defineConfig, devices } from "@playwright/test"; +import isCI from "is-ci"; + +/** + * See https://playwright.dev/docs/test-configuration. + */ +export default defineConfig({ + testDir: "./tests", + /* Run tests in files in parallel */ + fullyParallel: true, + /* Fail the build on CI if you accidentally left test.only in the source code. */ + forbidOnly: isCI, + /* Retry on CI only */ + retries: isCI ? 2 : 0, + /* Opt out of parallel tests on CI. */ + workers: isCI ? 1 : undefined, + /* Reporter to use. See https://playwright.dev/docs/test-reporters */ + reporter: "html", + + /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ + use: { + /* Base URL to use in actions like `await page.goto('/')`. */ + baseURL: "http://localhost:5173/", + + /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ + trace: "on-first-retry", + permissions: ["clipboard-read", "clipboard-write"], + }, + + /* Configure projects for major browsers */ + projects: [ + { + name: "chromium", + use: { ...devices["Desktop Chrome"] }, + }, + ], + + /* Run your local dev server before starting the tests */ + webServer: [ + { + command: "pnpm preview --port 5173", + url: "http://localhost:5173/", + reuseExistingServer: !isCI, + }, + ], +}); diff --git a/examples/tanstack-form/postcss.config.js b/examples/tanstack-form/postcss.config.js new file mode 100644 index 0000000000..c2ddf74822 --- /dev/null +++ b/examples/tanstack-form/postcss.config.js @@ -0,0 +1,5 @@ +export default { + plugins: { + "@tailwindcss/postcss": {}, + }, +}; diff --git a/examples/tanstack-form/public/favicon.ico b/examples/tanstack-form/public/favicon.ico new file mode 100644 index 0000000000..799e937cd6 Binary files /dev/null and b/examples/tanstack-form/public/favicon.ico differ diff --git a/examples/tanstack-form/src/App.tsx b/examples/tanstack-form/src/App.tsx new file mode 100644 index 0000000000..fda85ad03b --- /dev/null +++ b/examples/tanstack-form/src/App.tsx @@ -0,0 +1,22 @@ +import { useIframeHashRouter } from "hash-slash"; +import { CreateOrder } from "./CreateOrder.tsx"; +import { EditOrder } from "./EditOrder.tsx"; +import { Orders } from "./Orders.tsx"; + +function App() { + const router = useIframeHashRouter(); + + return ( + <> +
+ {router.route({ + "/": () => , + "/order": () => , + "/order/:id": (id) => , + })} +
+ + ); +} + +export default App; diff --git a/examples/tanstack-form/src/CreateOrder.tsx b/examples/tanstack-form/src/CreateOrder.tsx new file mode 100644 index 0000000000..bde7c725a0 --- /dev/null +++ b/examples/tanstack-form/src/CreateOrder.tsx @@ -0,0 +1,53 @@ +import { useIframeHashRouter } from "hash-slash"; +import { CoPlainText } from "jazz-tools"; +import { useAccount } from "jazz-tools/react"; +import { LinkToHome } from "./LinkToHome.tsx"; +import { OrderForm, OrderFormData } from "./OrderForm.tsx"; +import { + BubbleTeaOrder, + JazzAccount, + ListOfBubbleTeaAddOns, +} from "./schema.ts"; + +export function CreateOrder() { + const { me } = useAccount(JazzAccount, { + resolve: { root: { orders: true } }, + }); + const router = useIframeHashRouter(); + + if (!me?.root) return; + + const addOrder = (draft: OrderFormData) => { + const newOrder = BubbleTeaOrder.create({ + baseTea: draft.baseTea, + deliveryDate: draft.deliveryDate, + withMilk: draft.withMilk, + addOns: ListOfBubbleTeaAddOns.create(draft.addOns), + instructions: draft.instructions + ? CoPlainText.create(draft.instructions) + : undefined, + }); + + me.root.orders.push(newOrder); + + router.navigate("/"); + }; + + const draftOrder: Partial = { + baseTea: "Black", + addOns: [], + withMilk: false, + }; + + return ( + <> + + +

+ Make a new bubble tea order 🧋 +

+ + + + ); +} diff --git a/examples/tanstack-form/src/EditOrder.tsx b/examples/tanstack-form/src/EditOrder.tsx new file mode 100644 index 0000000000..edf4b4d45e --- /dev/null +++ b/examples/tanstack-form/src/EditOrder.tsx @@ -0,0 +1,59 @@ +import { CoPlainText, Loaded } from "jazz-tools"; +import { useCoState } from "jazz-tools/react"; +import { LinkToHome } from "./LinkToHome.tsx"; +import { OrderForm, OrderFormData } from "./OrderForm.tsx"; +import { OrderThumbnail } from "./OrderThumbnail.tsx"; +import { BubbleTeaOrder } from "./schema.ts"; + +export type LoadedBubbleTeaOrder = Loaded< + typeof BubbleTeaOrder, + { addOns: { $each: true }; instructions: true } +>; + +export function EditOrder(props: { id: string }) { + const order = useCoState(BubbleTeaOrder, props.id, { + resolve: { addOns: true, instructions: true }, + }); + + if (!order) return; + + const onSubmit = (updatedOrder: OrderFormData) => { + // Apply changes to the original Jazz order + order.baseTea = updatedOrder.baseTea; + order.deliveryDate = updatedOrder.deliveryDate; + order.withMilk = updatedOrder.withMilk; + order.addOns.applyDiff(updatedOrder.addOns); + + // `applyDiff` requires nested objects to be CoValues as well + order.instructions ??= CoPlainText.create(""); + if (updatedOrder.instructions) { + order.instructions.applyDiff(updatedOrder.instructions); + } + }; + + const originalOrder: OrderFormData = order.toJSON(); + // Convert timestamp to Date + originalOrder.deliveryDate = new Date(originalOrder.deliveryDate); + + return ( + <> + + +
+

Saved order:

+ + +
+ +

+ Edit your bubble tea order 🧋 +

+ + + + ); +} diff --git a/examples/tanstack-form/src/LinkToHome.tsx b/examples/tanstack-form/src/LinkToHome.tsx new file mode 100644 index 0000000000..fc6c69a4b7 --- /dev/null +++ b/examples/tanstack-form/src/LinkToHome.tsx @@ -0,0 +1,7 @@ +export function LinkToHome() { + return ( + + < Back to all orders + + ); +} diff --git a/examples/tanstack-form/src/OrderForm.tsx b/examples/tanstack-form/src/OrderForm.tsx new file mode 100644 index 0000000000..2722bac195 --- /dev/null +++ b/examples/tanstack-form/src/OrderForm.tsx @@ -0,0 +1,210 @@ +import { useForm } from "@tanstack/react-form"; +import { z } from "jazz-tools"; +import { OrderThumbnail } from "./OrderThumbnail.tsx"; +import { + BubbleTeaAddOnTypes, + BubbleTeaBaseTeaTypes, + BubbleTeaOrder, +} from "./schema.ts"; + +// TanStack Form can leverage Jazz's Zod schema to validate the form +const orderZodSchemaShape = BubbleTeaOrder.getZodSchema().shape; +const orderFormSchema = z.object({ + ...orderZodSchemaShape, + baseTea: orderZodSchemaShape.baseTea.refine((value) => value, { + error: "Please select your preferred base tea.", + }), + deliveryDate: z.date("Plese select a delivery date."), + // TanStack Form doesn't support CoList fields, so we need to convert them to arrays + addOns: z + .array(z.enum(BubbleTeaAddOnTypes)) + .min(1, "Please select at least one add-on."), + // TanStack Form doesn't support CoPlainText fields, so we need to convert them to strings + instructions: z.string().optional(), +}); + +export type OrderFormData = z.infer; + +export function OrderForm({ + order: originalOrder, + onSubmit, + validateOn, +}: { + order: Partial; + onSubmit: (order: OrderFormData) => void; + validateOn: "submit" | "change"; +}) { + const form = useForm({ + defaultValues: originalOrder, + validators: { + onSubmit: validateOn === "submit" ? orderFormSchema : undefined, + onChange: validateOn === "change" ? orderFormSchema : undefined, + }, + onSubmit: ({ value }) => { + // If the form is not valid according to orderFormSchema, the value will not be submitted + onSubmit(value as OrderFormData); + }, + }); + + return ( +
{ + e.preventDefault(); + e.stopPropagation(); + form.handleSubmit(); + }} + className="grid gap-5" + > +
+

Unsaved order preview:

+ [state.values]} + children={([values]) => } + /> +
+ +
+ + ( + <> + + {field.state.meta.errors.length > 0 && ( + + {field.state.meta.errors[0]?.message} + + )} + + )} + /> +
+ +
+ Add-ons + ( + <> + {BubbleTeaAddOnTypes.map((addOn) => ( +
+ { + const currentValue = field.state.value ?? []; + const updatedValue = e.target.checked + ? [...currentValue, addOn] + : currentValue.filter((item) => item !== addOn); + field.handleChange(updatedValue); + }} + /> + +
+ ))} + {field.state.meta.errors.length > 0 && ( + + {field.state.meta.errors[0]?.message} + + )} + + )} + /> +
+ +
+ + { + // Check if the date is valid + const dateString = + field.state.value && !isNaN(field.state.value.getTime()) + ? field.state.value.toISOString().split("T")[0] + : ""; + return ( + <> + field.handleChange(new Date(e.target.value))} + onBlur={field.handleBlur} + /> + {field.state.meta.errors.length > 0 && ( + + {field.state.meta.errors[0]?.message} + + )} + + ); + }} + /> +
+ +
+ ( + field.handleChange(e.target.checked)} + /> + )} + /> + +
+ +
+ + ( +