Skip to content

Commit

Permalink
Conform example with useLocationForm (#286)
Browse files Browse the repository at this point in the history
* example: confrom wrapper module

* example: restore location state form

* example: refactor useLocationForm type

* example: useLocationForm return with form props

* example: refactor&comment useLocationForm

* example: fix state name

* example: useForm behavoir test

* example: useLocationForm refactor rm typeof window

* example: useLocationForm fix return value

* example: fix useSyncExternalStore 1st arg same ref

* example: dynamic form item restore

* example: useLocationForm with dynamic form

* example: fix effect callback to useEffect

* example: getLocationFormProps memo

* feat: useLocationGetState impl

* example: useLocationForm with getLocationState

* example: fix onChange save location state

* example: @types/lodash.set add

* example: rm (dynamic)

* example: useLocationFrom option add id

* example: fix useLocationFrom checkbox restore

* example: useLocationForm add comment

* example: useLocationForm refactor noop name

* example: rm unused import

* example: (not use location state) conform search form

* example: useLocationForm sync url exmaple

* example: url useLocationForm example

* example: fix useLocationForm onChange default value

* example: refactor package, key

* example: useLocationForm refactor

* example: useLocationForm rendering optimize

* rebase on conform branch, update lock file

* example: useLocationForm callback deps fix

* refactor: useLocationForm option idPrefix rename

* feat: useLocationForm API fix

* fix: useLocationForm type

* refactor: useLocationForm re-render hooks useReducer

* refactor: useLocationForm add ref guard check

* fix: useLocationForm formOption fix

* feat: `filterFormValueWihZodShape` add

* fix: useLocationForm id setting timing

* fix: useLocationForm initial id set

* refactor: filterFormValueWihZodShape no use zod

* fix: useLocationForm inital id set

* fix: useLocationForm schema option add

* refactor: rename useLocationForm inner vars

* test: conform example e2e add

* example: useLocationForm option rm shape

* example: rename static form

* example: simple form with conform

* refactor: vars order fix

* refactor: fix defenition order

* feat: `useLocationKey` impl

* example: refactor rm lodash.set

* refactor: useLocationForm

* refactor: updateWithObjectPath

* example: type spec simple form

* example: simple conform submit redirect

* refactor: declarative order

* refactor: rename inner vars

* refactor: getPaths

* refactor: useLocationKey subscribe

* feat: useLocationKey option

* todo comment

* fix: useLocationKey default args

* fix: useLocationForm support intent

* refactor: useLocationForm console rm

* fix: simple form unnecessary if rm

* example: dynamic form add first button

* example: number's error field

* example: refactor schema

* fix: conform example test

* example: simple-form refactor

* example: static-from refactor

* refactor: useLocationForm

* example: simple/static form content

* example: test comment fix

* example: fix number error message

* example: dynamic form reset button impl

* example: submit/reset button layout

* example: rename schema

* refactor: conform page

---------

Co-authored-by: akfm.sato <01047245@CF0286.local>
  • Loading branch information
2 people authored and akfm.sato committed May 13, 2024
1 parent 1bdd0bf commit 1aa376d
Show file tree
Hide file tree
Showing 34 changed files with 1,206 additions and 160 deletions.
4 changes: 4 additions & 0 deletions apps/example-next-conform/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,7 @@ yarn-error.log*
# typescript
*.tsbuildinfo
next-env.d.ts
/test-results/
/playwright-report/
/blob-report/
/playwright/.cache/
11 changes: 10 additions & 1 deletion apps/example-next-conform/package.json
Original file line number Diff line number Diff line change
@@ -1,19 +1,28 @@
{
"name": "example-next-conform",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev --turbo",
"build": "next build",
"start": "next start"
"start": "next start",
"clean-start": "rm -rf .next && next build && next start",
"test": "vitest run",
"integration-test": "playwright test",
"integration-test:ui": "playwright test --ui"
},
"dependencies": {
"@conform-to/react": "1.0.6",
"@conform-to/zod": "1.0.6",
"@location-state/core": "workspace:*",
"next": "14.1.4",
"react": "18.2.0",
"react-dom": "18.2.0",
"zod": "3.22.4"
},
"devDependencies": {
"@playwright/test": "1.42.1",
"@testing-library/jest-dom": "6.4.2",
"@types/node": "20.11.30",
"@types/react": "18.2.67",
"@types/react-dom": "18.2.22",
Expand Down
30 changes: 30 additions & 0 deletions apps/example-next-conform/playwright.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { defineConfig, devices } from "@playwright/test";

export default defineConfig({
// Look for test files in the "tests" directory, relative to this configuration file.
testDir: "playwright",
// Run all tests in parallel.
fullyParallel: true, // Fail the build on CI if you accidentally left test.only in the source code.
forbidOnly: !!process.env.CI, // Retry on CI only.
retries: process.env.CI ? 2 : 0, // Opt out of parallel tests on CI.
workers: process.env.CI ? 1 : undefined, // Reporter to use
reporter: "html",
use: {
// Base URL to use in actions like `await page.goto('/')`.
baseURL: "http://127.0.0.1:3000", // Collect trace when retrying the failed test.
trace: "on-first-retry",
},
// Configure projects for major browsers.
projects: [
{
name: "chromium",
use: { ...devices["Desktop Chrome"] },
},
],
// Run your local dev server before starting the tests.
webServer: {
command: "pnpm build && pnpm start",
url: "http://127.0.0.1:3000",
reuseExistingServer: !process.env.CI,
},
});
52 changes: 52 additions & 0 deletions apps/example-next-conform/playwright/session-form.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { describe } from "node:test";
import { expect, test } from "@playwright/test";

// Safari/Firefox: different from actual behavior
test.skip(({ browserName }) => browserName !== "chromium", "Chromium only!");

describe('"simple form" is restored on browser back.', () => {
test("browser back on simple form", async ({ page }) => {
// Navigate to the target page.
await page.goto("http://localhost:3000/session/simple-form");
const firstName = page.getByRole("textbox", { name: "First name" });
const lastName = page.getByRole("textbox", { name: "Last name" });
// Default value is "".
await expect(firstName).toHaveValue("");
await expect(lastName).toHaveValue("");
// Change `firstName` & `lastName`.
await firstName.fill("Tanaka");
await lastName.fill("Taro");
// Navigate to the top page.
await page.goto("http://localhost:3000");
await expect(page.getByRole("heading")).toHaveText("`conform` example");
// Navigate back to the target page.
await page.goBack();
await expect(firstName).toHaveValue("Tanaka");
await expect(lastName).toHaveValue("Taro");
});
});

describe('"dynamic form" is restored on browser back.', () => {
test("browser back on dynamic form", async ({ page }) => {
// Navigate to the target page.
await page.goto("http://localhost:3000/session/dynamic-form");
// Add 2 member fields.
const addMember = page.getByRole("button", { name: "Add member to last" });
await addMember.click();
await addMember.click();
// Default value is "".
const member1Name = page.getByRole("textbox", { name: "name: " }).first();
const member2Name = page.getByRole("textbox", { name: "name: " }).nth(1);
await expect(member1Name).toHaveValue("");
await expect(member2Name).toHaveValue("");
// Change `firstName` & `lastName`.
await member1Name.fill("Tanaka");
// Navigate to the top page.
await page.goto("http://localhost:3000");
await expect(page.getByRole("heading")).toHaveText("`conform` example");
// Navigate back to the target page.
await page.goBack();
await expect(member1Name).toHaveValue("Tanaka");
await expect(member2Name).toHaveValue("");
});
});
52 changes: 52 additions & 0 deletions apps/example-next-conform/playwright/url-form.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { describe } from "node:test";
import { expect, test } from "@playwright/test";

// Safari/Firefox: different from actual behavior
test.skip(({ browserName }) => browserName !== "chromium", "Chromium only!");

describe('"simple form" is restored on browser back.', () => {
test("browser back on simple form", async ({ page }) => {
// Navigate to the target page.
await page.goto("http://localhost:3000/url/simple-form");
const firstName = page.getByRole("textbox", { name: "First name" });
const lastName = page.getByRole("textbox", { name: "Last name" });
// Default value is "".
await expect(firstName).toHaveValue("");
await expect(lastName).toHaveValue("");
// Change `firstName` & `lastName`.
await firstName.fill("Tanaka");
await lastName.fill("Taro");
// Navigate to the top page.
await page.goto("http://localhost:3000");
await expect(page.getByRole("heading")).toHaveText("`conform` example");
// Navigate back to the target page.
await page.goBack();
await expect(firstName).toHaveValue("Tanaka");
await expect(lastName).toHaveValue("Taro");
});
});

describe('"dynamic form" is restored on browser back.', () => {
test("browser back on dynamic form", async ({ page }) => {
// Navigate to the target page.
await page.goto("http://localhost:3000/url/dynamic-form");
// Add 2 member fields.
const addMember = page.getByRole("button", { name: "Add member to last" });
await addMember.click();
await addMember.click();
// Default value is "".
const member1Name = page.getByRole("textbox", { name: "name: " }).first();
const member2Name = page.getByRole("textbox", { name: "name: " }).nth(1);
await expect(member1Name).toHaveValue("");
await expect(member2Name).toHaveValue("");
// Change `firstName` & `lastName`.
await member1Name.fill("Tanaka");
// Navigate to the top page.
await page.goto("http://localhost:3000");
await expect(page.getByRole("heading")).toHaveText("`conform` example");
// Navigate back to the target page.
await page.goBack();
await expect(member1Name).toHaveValue("Tanaka");
await expect(member2Name).toHaveValue("");
});
});
71 changes: 0 additions & 71 deletions apps/example-next-conform/src/app/dynamic-form/form.tsx

This file was deleted.

10 changes: 0 additions & 10 deletions apps/example-next-conform/src/app/dynamic-form/page.tsx

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

import { parseWithZod } from "@conform-to/zod";
import { redirect } from "next/navigation";
import { teamSchema } from "./schema";
import { Team } from "./schema";

export async function saveTeam(prevState: unknown, formData: FormData) {
const submission = parseWithZod(formData, {
schema: teamSchema,
schema: Team,
});

if (submission.status !== "success") {
Expand Down
Loading

0 comments on commit 1aa376d

Please sign in to comment.