Skip to content

Commit

Permalink
fix: basic intent support
Browse files Browse the repository at this point in the history
  • Loading branch information
edmundhung committed Dec 14, 2023
1 parent f6001db commit ab9cc5c
Show file tree
Hide file tree
Showing 6 changed files with 123 additions and 94 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"@babel/preset-env": "^7.20.2",
"@babel/preset-react": "^7.18.6",
"@babel/preset-typescript": "^7.20.2",
"@playwright/test": "^1.36.2",
"@playwright/test": "^1.40.1",
"@remix-run/eslint-config": "^1.19.3",
"@remix-run/node": "^1.19.3",
"@rollup/plugin-babel": "^5.3.1",
Expand Down
1 change: 1 addition & 0 deletions packages/conform-dom/submission.ts
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,7 @@ export function replySubmission<Error>(

return {
status: context.intent ? undefined : error ? 'error' : 'success',
intent: context.intent ? context.intent : undefined,
initialValue: simplify(context.payload) ?? {},
error,
state: context.state,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
getInputProps,
getTextareaProps,
getControlButtonProps,
FormStateInput,
} from '@conform-to/react';
import { parseWithZod } from '@conform-to/zod';
import type { ActionArgs, LoaderArgs } from '@remix-run/node';
Expand Down Expand Up @@ -33,7 +34,7 @@ export async function action({ request }: ActionArgs) {
return json(submission.reply());
}

export default function Validate() {
export default function Intent() {
const { noClientValidate } = useLoaderData<typeof loader>();
const lastResult = useActionData();
const { meta, fields } = useForm({
Expand All @@ -45,14 +46,15 @@ export default function Validate() {

return (
<Form method="post" {...getFormProps(meta)}>
<Playground title="Validate" lastSubmission={lastResult}>
<FormStateInput context={meta.context} />
<Playground title="Intent" lastSubmission={lastResult}>
<Field label="Name" config={fields.name}>
<input {...getInputProps(fields.name, { type: 'text' })} />
</Field>
<Field label="Message" config={fields.message}>
<textarea {...getTextareaProps(fields.message)} />
</Field>
<div className="flex flex-row gap-2">
<div className="flex flex-col gap-2">
<button
className="rounded-md border p-2 hover:border-black"
{...getControlButtonProps(
Expand All @@ -71,6 +73,46 @@ export default function Validate() {
>
Validate Message
</button>
<button
className="rounded-md border p-2 hover:border-black"
{...getControlButtonProps(
meta.id,
intent.replace({
name: fields.message.name,
value: 'Hello World',
}),
)}
>
Update message
</button>
<button
className="rounded-md border p-2 hover:border-black"
{...getControlButtonProps(
meta.id,
intent.replace({
name: fields.message.name,
value: '',
validated: true,
}),
)}
>
Clear message
</button>
<button
className="rounded-md border p-2 hover:border-black"
{...getControlButtonProps(
meta.id,
intent.reset({ name: fields.message.name }),
)}
>
Reset message
</button>
<button
className="rounded-md border p-2 hover:border-black"
{...getControlButtonProps(meta.id, intent.reset())}
>
Reset form
</button>
</div>
</Playground>
</Form>
Expand Down
2 changes: 1 addition & 1 deletion pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

75 changes: 75 additions & 0 deletions tests/integrations/intent.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { type Page, type Locator, test, expect } from '@playwright/test';
import { getPlayground } from '../helpers';

function getFieldset(form: Locator) {
return {
name: form.locator('[name="name"]'),
message: form.locator('[name="message"]'),
validateName: form.locator('button:text("Validate Name")'),
validateMessage: form.locator('button:text("Validate Message")'),
updateMessage: form.locator('button:text("Update message")'),
clearMessage: form.locator('button:text("Clear message")'),
resetMessage: form.locator('button:text("Reset message")'),
resetForm: form.locator('button:text("Reset form")'),
};
}

async function runValidationScenario(page: Page) {
const playground = getPlayground(page);
const fieldset = getFieldset(playground.container);

await expect(playground.error).toHaveText(['', '']);

await fieldset.validateName.click();
await expect(playground.error).toHaveText(['Name is required', '']);

await fieldset.validateMessage.click();
await expect(playground.error).toHaveText([
'Name is required',
'Message is required',
]);

await fieldset.updateMessage.click();
await expect(fieldset.name).toHaveValue('');
await expect(fieldset.message).toHaveValue('Hello World');
await expect(playground.error).toHaveText(['Name is required', '']);

await fieldset.clearMessage.click();
await expect(fieldset.name).toHaveValue('');
await expect(fieldset.message).toHaveValue('');
await expect(playground.error).toHaveText([
'Name is required',
'Message is required',
]);

await fieldset.resetMessage.click();
await expect(fieldset.name).toHaveValue('');
await expect(fieldset.message).toHaveValue('');
await expect(playground.error).toHaveText(['Name is required', '']);

await fieldset.resetForm.click();
await expect(fieldset.name).toHaveValue('');
await expect(fieldset.message).toHaveValue('');
await expect(playground.error).toHaveText(['', '']);
}

test.describe('With JS', () => {
test('Client Validation', async ({ page }) => {
await page.goto('/intent');
await runValidationScenario(page);
});

test('Server Validation', async ({ page }) => {
await page.goto('/intent?noClientValidate=yes');
await runValidationScenario(page);
});
});

test.describe('No JS', () => {
test.use({ javaScriptEnabled: false });

test('Validation', async ({ page }) => {
await page.goto('/intent');
await runValidationScenario(page);
});
});
89 changes: 0 additions & 89 deletions tests/integrations/validate.spec.ts

This file was deleted.

0 comments on commit ab9cc5c

Please sign in to comment.