Skip to content

Commit

Permalink
Merge pull request #117 from vantezzen/feat/v2.1
Browse files Browse the repository at this point in the history
  • Loading branch information
vantezzen authored Oct 20, 2024
2 parents 6859fd6 + 065de8e commit 988773e
Show file tree
Hide file tree
Showing 109 changed files with 4,429 additions and 574 deletions.
30 changes: 30 additions & 0 deletions .github/workflows/cypress.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: Cypress Tests

on: push

jobs:
cypress-run:
runs-on: ubuntu-22.04
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: "20"

- name: Install dependencies
run: npm ci

- name: Build packages
run: npm run build

- name: Cypress run
uses: cypress-io/github-action@v6
with:
install-command: npm install
project: apps/web
start: npm run cypress
browser: chrome
component: true
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ For releases, AutoForm uses changesets. To create a new release, run:

```bash
npm run build
npm run cypress # Run the component tests
npx changeset
```

Expand Down
1 change: 1 addition & 0 deletions apps/docs/pages/docs/react/_meta.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ export default {
migration: "Migration from v1",
customization: "Customization",
"custom-integration": "Custom integration",
api: "API",
};
37 changes: 37 additions & 0 deletions apps/docs/pages/docs/react/api.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# API

## schema: SchemaProvider

The `schema` prop is used to pass the schema to the form. It can be a `ZodProvider` or a `YupProvider`.

## onSubmit?: (values: T, form: FormInstance) => void

The `onSubmit` prop is called when the form is submitted and the data is valid. It receives the form data and the form instance from react-hook-form.

## onFormInit?: (form: FormInstance) => void

The `onFormInit` prop is called when the form is initialized. It receives the form instance from react-hook-form.

## withSubmit?: boolean

The `withSubmit` prop is used to automatically add a submit button to the form. It defaults to `false`.

## defaultValues?: Partial\<T>

The `defaultValues` prop is used to set the initial values of the form.

## values: Partial\<T>

The `values` prop is used to set the values of the form. It is a controlled input.

## children: React.ReactNode

All children passed to the `AutoForm` component will be rendered below the form.

## formComponents: Partial\<AutoFormFieldComponents>

Additional, custom form components can be passed to the `formComponents` prop. This allows you to add custom field types to the form.

## uiComponents: Partial\<AutoFormUIComponents>

Override the default UI components with custom components. This allows you to customize the look and feel of the form.
63 changes: 6 additions & 57 deletions apps/docs/pages/docs/react/custom-integration.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -129,19 +129,10 @@ import { AutoFormFieldProps } from "@autoform/react";

export const StringField: React.FC<AutoFormFieldProps> = ({
field,
value,
onChange,
inputProps,
error,
id,
}) => (
<input
id={id}
type="text"
value={value || ""}
onChange={(e) => onChange(e.target.value)}
{...field.fieldConfig?.inputProps}
/>
);
}) => <input id={id} {...inputProps} />;
```

```tsx
Expand All @@ -151,19 +142,9 @@ import { AutoFormFieldProps } from "@autoform/react";

export const NumberField: React.FC<AutoFormFieldProps> = ({
field,
value,
onChange,
error,
inputProps,
id,
}) => (
<input
id={id}
type="number"
value={value || ""}
onChange={(e) => onChange(Number(e.target.value))}
{...field.fieldConfig?.inputProps}
/>
);
}) => <input id={id} type="number" {...inputProps} />;
```

```tsx
Expand All @@ -173,19 +154,9 @@ import { AutoFormFieldProps } from "@autoform/react";

export const DateField: React.FC<AutoFormFieldProps> = ({
field,
value,
onChange,
error,
inputProps,
id,
}) => (
<input
id={id}
type="date"
value={value ? new Date(value).toISOString().split("T")[0] : ""}
onChange={(e) => onChange(new Date(e.target.value))}
{...field.fieldConfig?.inputProps}
/>
);
}) => <input id={id} type="date" {...inputProps} />;
```

Additionally, you can create custom field components for other types like checkboxes, radio buttons, etc. These can later be used by setting a custom `fieldType` in the schema.
Expand Down Expand Up @@ -234,28 +205,6 @@ export function AutoForm<T extends Record<string, any>>(
}
```

## Implement utility functions

To provide type-safety when selecting field types, you should create a custom wrapper for the `fieldConfig` function.

Create a new file called `src/utils.ts`:

```tsx
// src/utils.ts
import { FieldConfig } from "@autoform/core";
import { SuperRefineFunction } from "@autoform/zod";
import { fieldConfig as baseFieldConfig } from "@autoform/react";
import { ReactNode } from "react";

type FieldTypes = "string" | "number" | "date" | string;

export function fieldConfig(
config: FieldConfig<ReactNode, FieldTypes>
): SuperRefineFunction {
return baseFieldConfig<FieldTypes>(config);
}
```

## Add custom field types (optional)

If you want to add custom field types, you can create new components and add them to the `formComponents` object in `src/AutoForm.tsx`. For example:
Expand Down
139 changes: 132 additions & 7 deletions apps/docs/pages/docs/react/customization.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,23 @@

The customization of the components is done by providing a `fieldConfig` to your schema fields. This allows you to customize the rendering of the field, add additional props, and more.

With zod, you can use the `superRefine` method to add a `fieldConfig` to your schema field. This method is used to add additional validation and configuration to your field.
With zod, you can use the `superRefine` method to add a `fieldConfig` to your schema field. For more information, see the [Zod documentation](/docs/schema/zod).

You should import `fieldConfig` from your AutoForm UI-specific package (e.g. `@autoform/mui`) so it will be type-safe for your specific UI. If you use a custom UI, you can import `fieldConfig` from `@autoform/react` or `@autoform/core`.
With yup, you can use the `transform` method to add a `fieldConfig` to your schema field. For more information, see the [Yup documentation](/docs/schema/yup). In these examples, we will use Zod.

You should import `buildZodFieldConfig` or `buildYupFieldConfig` from `@autoform/react` and customize it.

```tsx
import * as z from "zod";
import { fieldConfig } from "@autoform/mui"; // or your UI library
import { buildZodFieldConfig } from "@autoform/react";
import { FieldTypes } from "@autoform/mui";

const fieldConfig = buildZodFieldConfig<
FieldTypes, // You should provide the "FieldTypes" type from the UI library you use
{
isImportant?: boolean; // You can add custom props here
}
>();

const schema = z.object({
username: z.string().superRefine(
Expand All @@ -17,7 +27,10 @@ const schema = z.object({
inputProps: {
placeholder: "Enter your username",
},
}),
customData: {
isImportant: true, // You can add custom data here
},
})
),
// ...
});
Expand All @@ -35,7 +48,7 @@ const schema = z.object({
type: "text",
placeholder: "Username",
},
}),
})
),
});
// This will be rendered as:
Expand All @@ -51,13 +64,54 @@ const schema = z.object({
username: z.string().superRefine(
fieldConfig({
fieldType: "textarea",
}),
})
),
});
```

The list of available fields depends on the UI library you use - use the autocomplete in your IDE to see the available options.

### Custom field types

You can also add your own custom field types. To do this, you need to extend the `formComponents` prop of your AutoForm component and add your custom field type.

```tsx
<AutoForm
formComponents={{
custom: ({ field, label, inputProps }: AutoFormFieldProps) => {
return (
<div>
<input
type="text"
className="bg-red-400 rounded-lg p-4"
// You should always pass the "inputProps" to the input component
// This includes the handlers for "onChange", "onBlur", etc.
{...inputProps}
/>
</div>
);
},
}}
/>;

const fieldConfig = buildZodFieldConfig<
FieldTypes | "custom",
{
isImportant?: boolean;
}
>();

const schema = z.object({
username: z.string().superRefine(
fieldConfig({
fieldType: "custom",
})
),
});
```

Please note that this will still render the default `FieldWrapper` around your input field, which contains the label and error message. If you want to customize this, you can use the `fieldWrapper` property ([see below](#custom-field-wrapper)).

## Description

You can use the `description` property to add a description below the field.
Expand All @@ -68,9 +122,80 @@ const schema = z.object({
fieldConfig({
description:
"Enter a unique username. This will be shown to other users.",
}),
})
),
});
```

You can use JSX in the description.

## Order

If you want to change the order of fields, use the order config. You can pass an arbitrary number where smaller numbers will be displayed first. All fields without a defined order use "0" so they appear in the same order they are defined in

```tsx
const schema = z.object({
termsOfService: z.boolean().superRefine(
fieldConfig({
order: 1, // This will be displayed after other fields with order 0
})
),

username: z.string().superRefine(
fieldConfig({
order: -1, // This will be displayed first
})
),

email: z.string().superRefine(
fieldConfig({
// Without specifying an order, this will have order 0
})
),
});
```

## Custom field wrapper

You can use the `fieldWrapper` property to wrap the field in a custom component. This is useful if you want to add additional elements to the field.

The `fieldWrapper` is responsible for rendering the field label and error, so when you use a custom `fieldWrapper`, you need to handle these yourself. You can take a look at the `FieldWrapperProps` type to see what props are passed to the `fieldWrapper`.

```tsx
const schema = z.object({
email: z.string().superRefine(
fieldConfig({
fieldWrapper: (props: FieldWrapperProps) => {
return (
<>
{props.children}
<p className="text-muted-foreground text-sm">
Don't worry, we won't share your email with anyone!
</p>
</>
);
},
})
),
});
```

## Override UI components

You can also override the default UI components with custom components. This allows you to customize the look and feel of the form.

```tsx
<AutoForm
uiComponents={{
FieldWrapper: ({ children, label, error }) => {
return (
<div>
<label>{label}</label>
{children}
{error}
</div>
);
},
}}
/>
```
Loading

0 comments on commit 988773e

Please sign in to comment.