Skip to content

Commit

Permalink
feat: input type docs and failing tests
Browse files Browse the repository at this point in the history
  • Loading branch information
airjp73 committed Jun 17, 2024
1 parent 19af435 commit 85f9128
Show file tree
Hide file tree
Showing 3 changed files with 145 additions and 4 deletions.
4 changes: 4 additions & 0 deletions apps/docs-v2/app/examples/basic/react.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export const ReactExample = () => {
defaultValues: {
projectName: "",
tasks: [] as Array<{ title: string; daysToComplete: number }>,
file: "" as File | "",
},
handleSubmit: async ({ projectName, tasks }) => {
await createProject({ name: projectName, tasks });
Expand All @@ -52,6 +53,9 @@ export const ReactExample = () => {
<form {...form.getFormProps()}>
<MyInput label="Project name" scope={form.scope("projectName")} />

<input {...form.getInputProps("file", { type: "file" })} />
<pre>{JSON.stringify(form.value("file"))}</pre>

<div>
<h3>Tasks</h3>
{form.error("tasks") && (
Expand Down
58 changes: 56 additions & 2 deletions apps/docs-v2/app/routes/_docs.input-types.mdx
Original file line number Diff line number Diff line change
@@ -1,6 +1,35 @@
# Working with different input types

This guide covers the basis of how to use RVF with different native input types.
Since RVF validates and submits data directly from the html `form` element by default,
nearly anything you can do with plain html forms can be done with RVF.
All native input types are supported out of the box.

When you're using RVF to observe or set the value of a field,
it should hopefully "just work" the way you expect.
But for completeness, this page is going to cover all the different input types
and how they interact with RVF.

## Common traits

### Setting default values

All input types (except for `file`) can have their default value set using a `string`.
For input types that represent some other type (like `number`),
you can usually set the default value using that type.

### Observing / setting values

The type of the value you get when you call `form.value(fieldName)` is always
the same as the type you pass into `defaultValues`.
You should use the same type when calling `form.setValue(fieldName, value)`.

### Validating

Unless you're using [state mode](/state-mode), the data received by your validator
will _always_ be a `string` (except for `file` inputs) **OR** a `string[]`.

- If only one input is in the form for a given field, the value will be a `string`.
- If multiple inputs in the form have the same name, the value will be a `string[]`.

## Number inputs

Expand Down Expand Up @@ -41,4 +70,29 @@ Unless you're using [state mode](/state-mode), your validator should be able to
- If you're using `yup`, then `yup.number()` already handles this.

If you're using state mode, then the value passed to your validator will be the same value
that you would get out of `form.value("myField")`.
that you would get out of `form.value("myField")`.

## File inputs

<Row>
<Col>
File inputs actually pretty inflexible. Try running this code to see what happens.
If you do that, you might see an error like this:

> This input element accepts a filename, which may only be programmatically set to the empty string.
</Col>
<Col>

```tsx
const FileInputTest = () => {
const inputRef = useRef<HTMLInputElement>(null);
return <input type="file" defaultValue="myFile.txt" />;
}
```
</Col>
</Row>

Unfortunately, this means we can't set the default value of a file input
or modify it with `setValue`, unless that value is an empty string (`""`).

TODO: note about observing the value
87 changes: 85 additions & 2 deletions packages/react/src/test/files.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,12 @@ it("should not blow up when a file has a default value", async () => {
handleSubmit: submit,
});

const props = form.field("file").getInputProps({ type: "file" });
return (
<form {...form.getFormProps()} encType="multipart/form-data">
<input data-testid="file" {...props} />
<input
data-testid="file"
{...form.getInputProps("file", { type: "file" })}
/>
<button data-testid="submit" type="submit" />
</form>
);
Expand All @@ -99,3 +101,84 @@ it("should not blow up when a file has a default value", async () => {
expect(submit).toHaveBeenCalledTimes(1);
expect(submit).toHaveBeenCalledWith({ file }, expect.any(FormData), {});
});

it("should be possible to observe and clear the value of a file input", async () => {
const submit = vi.fn();
const TestComp = () => {
const form = useForm({
defaultValues: {
file: "",
},
validator: successValidator,
handleSubmit: submit,
});

return (
<form {...form.getFormProps()} encType="multipart/form-data">
<input
data-testid="file"
{...form.getInputProps("file", { type: "file" })}
/>
<pre data-testid="file-value">{JSON.stringify(form.value("file"))}</pre>
<button
data-testid="clear"
type="button"
onClick={() => form.setValue("file", "")}
/>
<button data-testid="submit" type="submit" />
</form>
);
};

render(<TestComp />);

const file = new File(["test"], "test.txt", { type: "text/plain" });
await userEvent.upload(screen.getByTestId("file"), file);
expect(screen.getByTestId("file-value")).toHaveTextContent(

Check failure on line 137 in packages/react/src/test/files.test.tsx

View workflow job for this annotation

GitHub Actions / build

packages/react/src/test/files.test.tsx > should be possible to observe and clear the value of a file input

Error: expect(element).toHaveTextContent() Expected element to have text content: {} Received: "C:\\fakepath\\test.txt" ❯ packages/react/src/test/files.test.tsx:137:44
JSON.stringify(file),
);

await userEvent.click(screen.getByTestId("clear"));
expect(screen.getByTestId("file-value")).toHaveTextContent('""');
});

it("should be possible to observe and clear the value of a multi-file input", async () => {
const submit = vi.fn();
const TestComp = () => {
const form = useForm({
defaultValues: {
file: "",
},
validator: successValidator,
handleSubmit: submit,
});

return (
<form {...form.getFormProps()} encType="multipart/form-data">
<input
data-testid="file"
{...form.getInputProps("file", { type: "file" })}
multiple
/>
<pre data-testid="file-value">{JSON.stringify(form.value("file"))}</pre>
<button
data-testid="clear"
type="button"
onClick={() => form.setValue("file", "")}
/>
<button data-testid="submit" type="submit" />
</form>
);
};

render(<TestComp />);

const file = new File(["test"], "test.txt", { type: "text/plain" });
await userEvent.upload(screen.getByTestId("file"), file);
expect(screen.getByTestId("file-value")).toHaveTextContent(

Check failure on line 178 in packages/react/src/test/files.test.tsx

View workflow job for this annotation

GitHub Actions / build

packages/react/src/test/files.test.tsx > should be possible to observe and clear the value of a multi-file input

Error: expect(element).toHaveTextContent() Expected element to have text content: {} Received: "C:\\fakepath\\test.txt" ❯ packages/react/src/test/files.test.tsx:178:44
JSON.stringify(file),
);

await userEvent.click(screen.getByTestId("clear"));
expect(screen.getByTestId("file-value")).toHaveTextContent('""');
});

0 comments on commit 85f9128

Please sign in to comment.