-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add packages, set up zod with translations * Update translation files * Create form components (from shadcn/ui) * Add styling for FormLabel * Create ExampleForm and schema for username/pass * Add width: 100% to input styling * Add dropdown to example, render validated data * Wrap Dropdown in forwardRef for proper control by react-hook-form * Automatically convert to Number for number inputs on onChange * Add number field to example form * Add missing number_input css class * Fix Checkbox component * Add Checkbox to example form * Simplify Checkbox component * Fix SamfFormFieldTypes after Checkbox changes * Begin docs * Biome * Move schema definition outside ExampleForm * Update docs * Update index title
- Loading branch information
Showing
23 changed files
with
913 additions
and
159 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,119 +1,104 @@ | ||
[👈 back](/docs/technical/README.md) | ||
|
||
# SamfForm | ||
# Forms | ||
|
||
SamfForm is a generic react component that makes it very easy to create forms for all kinds of data. The form automatically handles validation and UI for you, so you only have to specify things like field names, input type and labels: | ||
We use [React Hook Form](https://react-hook-form.com/) and [Zod](https://zod.dev/) for building and validating frontend | ||
forms. We also use wrapper components (lifted from [shadcn/ui](https://ui.shadcn.com/docs/components/form)), which wrap | ||
around the React Hook Form library. These projects have excellent documentation, so we won't try to replace them here. | ||
This document will simply act as a "Getting started" guide, along with some quick pointers. | ||
|
||
```html | ||
<SamfForm onSubmit={yourSubmitFunction} submitButton="Save Name"> | ||
<SamfFormField field="name" type="text" label="Enter name"/> | ||
<SamfFormField field="age" type="number" label="Enter age"/> | ||
</SamfForm> | ||
``` | ||
|
||
All fields are required by default (so an empty string will not be allowed). When you have filled in the name and click the submit button, `yourSubmitFunction` will be called with the entered data, (eg. `{'name': 'Sanctus', 'age': 69}`) as a parameter. | ||
|
||
## Usage | ||
We use [zod-i18n](https://github.com/aiji42/zod-i18n) for translating error messages. | ||
|
||
- Use the `<SamfForm>` component to create a new form. The component accepts any children, so you can place things in columns or add whatever wrappers you want. | ||
- Add inputs by placing `<SamfFormField>` components inside the form. You can set labels, value types and validation settings for these inputs. | ||
## Schema | ||
|
||
- **IMPORTANT:** | ||
- SamfForm does not care about elements other than `<SamfFormField>`. An `<input>` tag will, for instance, not get validation nor will its data be included in `onSubmit`. | ||
We define all schema fields in `./frontend/schema/`. This means we only have to define fields once, and lets us easily | ||
create new schemas using existing fields. The form schemas themselves are defined where they are used, meaning alongside | ||
the forms. | ||
|
||
### Form Properties | ||
Avoid adding options to the fields such as `nullish()`/`optional()`, as those should be decided by the schema using the | ||
fields. The fields should only contain the minimum required for validation. For instance, the `USERNAME` field is | ||
defined like so: | ||
|
||
Forms should use either `onSubmit` or `onChange`. Submit is useful for typical forms where you post/put data. By defining a form with a type (inside the `< >` after `SamfForm`) you can easily model any datatype. | ||
|
||
#### Posting/putting data | ||
```tsx | ||
function postEvent(event: EventDto) { | ||
// your posting logic | ||
} | ||
```ts | ||
export const USERNAME = z.string().min(USERNAME_LENGTH_MIN).max(USERNAME_LENGTH_MAX); | ||
``` | ||
|
||
```html | ||
<SamfForm<EventDto> onSubmit={postEvent}> | ||
<!-- Your input fields --> | ||
</SamfForm> | ||
``` | ||
This lets us easily use it in a Zod schema, and make it optional like so: | ||
|
||
#### Storing data in a state | ||
```ts | ||
const schema = z.object({ | ||
username: USERNAME.optional(), | ||
}); | ||
``` | ||
|
||
If the component needs to display some information about the form while you are editing, you can use the `onChange` property to get notified when data changes. | ||
## Defining forms | ||
|
||
```tsx | ||
const [event, setEvent] = useState<EventDto>(undefined); | ||
``` | ||
```html | ||
<SamfForm<EventDto> onChange={setEvent}> | ||
<!-- Your input fields --> | ||
</SamfForm> | ||
``` | ||
Always define forms in their own files, to keep code clean. | ||
|
||
You can also use `onValidityChanged` to get a simple boolean indicating if the form is valid or not (eg. if some fields are missing). | ||
To get started, create a new file, for example `YourForm.tsx`. This file will contain the form schema and the form | ||
itself. Define a schema using zod. Remember to reuse fields when possible as mentioned in the section above (we won't do | ||
this here for example's sake). | ||
|
||
#### Setting initial data | ||
```typescript jsx | ||
import { z } from 'zod'; | ||
|
||
If you are changing existing data (for instance when doing a http PATCH), set the `initialData` property of the form. The form expects a partial object which allows you to only include some of the fields in the Dto. | ||
const schema = z.object({ | ||
username: z.string().min(3).max(24), | ||
}); | ||
``` | ||
|
||
```tsx | ||
const event: Partial<EventDto> = { | ||
title_nb: 'some title', | ||
title_en: 'some title', | ||
Create your form component, and use the `useForm` hook to create the form. | ||
|
||
Create the form component, and use the `useForm` hook with your schema,. | ||
|
||
```typescript jsx | ||
export function YourForm() { | ||
// 1. Define the form | ||
const form = useForm<z.infer<typeof schema>>({ | ||
resolver: zodResolver(schema), | ||
defaultValues: { | ||
username: '', | ||
}, | ||
}); | ||
|
||
// 2. Define the submit handler | ||
function onSubmit(values: z.infer<typeof schema>) { | ||
// These values are type-safe and validated | ||
console.log(values); | ||
} | ||
} | ||
``` | ||
```html | ||
<SamfForm<EventDto> initialData={event}> | ||
<!-- Your input fields --> | ||
</SamfForm> | ||
``` | ||
|
||
#### Advanced usage | ||
|
||
- Set `validateOnInit` to check validity instantly. Missing fields will be marked with red. | ||
- Set `devMode` to get a live preview of all the form values, errors and fields | ||
|
||
### Input Fields | ||
|
||
All inputs area created using `<SamfFormField>`. Required properties are: | ||
- `type` Which type of input is used, eg. 'text', 'number', 'image', 'date' etc. See `SamfFormFieldTypes`. | ||
- `field` The name of the property to set. In an `EventDto` you may want to use a field like `title_nb` or `duration`. | ||
|
||
Optional properties include: | ||
- `required` whether the field is invalid when empty/undefined. Default `true`. | ||
- `label` a text string label that is shown above the input | ||
- `options` a list of `DropDownOption` used for dropdown inputs | ||
- `defaultOption` a `DropDownOption` set as the default for dropdown inputs | ||
- `validator` a custom validation function which checks special requirements | ||
|
||
Example: | ||
|
||
```html | ||
<SamfFormField type="text" field="title_en" label="English title" /> | ||
<SamfFormField type="text-long" field="description_en' label="English description" /> | ||
<SamfFormField type="text" field="social_media_url" label="Social media" required={false}/> | ||
<SamfFormField type="image" field="image" label="Event Image"/> | ||
Now use the `Form` wrapper components to build our form. | ||
|
||
```typescript jsx | ||
export function YourForm() { | ||
// ... | ||
|
||
return ( | ||
<Form {...form}> | ||
<form onSubmit={form.handleSubmit(onSubmit)}> | ||
<FormField | ||
control={form.control} | ||
name="username" | ||
render={({ field }) => ( | ||
<FormItem> | ||
<FormLabel>Username</FormLabel> | ||
<FormControl> | ||
<Input placeholder="Username" {...field} /> | ||
</FormControl> | ||
<FormMessage /> | ||
</FormItem> | ||
)} | ||
/> | ||
</form> | ||
</Form> | ||
); | ||
} | ||
``` | ||
|
||
Option Example (with value type inside the `< >`): | ||
```tsx | ||
const myOptions: DropDownOption<number>[] = [ | ||
{value: 1, label: "One"}, | ||
{value: 2, label: "Two"}, | ||
] | ||
``` | ||
```html | ||
<SamfFormField<number> | ||
type="options" | ||
field="some_number_field" | ||
label="Pick a number" | ||
options={myOptions} | ||
defaultOption={myOptions[0]} | ||
/> | ||
``` | ||
## Example | ||
|
||
## Implementation details | ||
To see an example form in action, check out the form on the [components page](http://localhost:3000/components), | ||
and [its code](../../../frontend/src/Pages/ComponentPage/ExampleForm.tsx). | ||
|
||
TODO |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
[👈 back](/docs/technical/README.md) | ||
|
||
# SamfForm | ||
|
||
SamfForm is a generic react component that makes it very easy to create forms for all kinds of data. The form automatically handles validation and UI for you, so you only have to specify things like field names, input type and labels: | ||
|
||
```html | ||
<SamfForm onSubmit={yourSubmitFunction} submitButton="Save Name"> | ||
<SamfFormField field="name" type="text" label="Enter name"/> | ||
<SamfFormField field="age" type="number" label="Enter age"/> | ||
</SamfForm> | ||
``` | ||
|
||
All fields are required by default (so an empty string will not be allowed). When you have filled in the name and click the submit button, `yourSubmitFunction` will be called with the entered data, (eg. `{'name': 'Sanctus', 'age': 69}`) as a parameter. | ||
|
||
## Usage | ||
|
||
- Use the `<SamfForm>` component to create a new form. The component accepts any children, so you can place things in columns or add whatever wrappers you want. | ||
- Add inputs by placing `<SamfFormField>` components inside the form. You can set labels, value types and validation settings for these inputs. | ||
|
||
- **IMPORTANT:** | ||
- SamfForm does not care about elements other than `<SamfFormField>`. An `<input>` tag will, for instance, not get validation nor will its data be included in `onSubmit`. | ||
|
||
### Form Properties | ||
|
||
Forms should use either `onSubmit` or `onChange`. Submit is useful for typical forms where you post/put data. By defining a form with a type (inside the `< >` after `SamfForm`) you can easily model any datatype. | ||
|
||
#### Posting/putting data | ||
```tsx | ||
function postEvent(event: EventDto) { | ||
// your posting logic | ||
} | ||
``` | ||
|
||
```html | ||
<SamfForm<EventDto> onSubmit={postEvent}> | ||
<!-- Your input fields --> | ||
</SamfForm> | ||
``` | ||
|
||
#### Storing data in a state | ||
|
||
If the component needs to display some information about the form while you are editing, you can use the `onChange` property to get notified when data changes. | ||
|
||
```tsx | ||
const [event, setEvent] = useState<EventDto>(undefined); | ||
``` | ||
```html | ||
<SamfForm<EventDto> onChange={setEvent}> | ||
<!-- Your input fields --> | ||
</SamfForm> | ||
``` | ||
|
||
You can also use `onValidityChanged` to get a simple boolean indicating if the form is valid or not (eg. if some fields are missing). | ||
|
||
#### Setting initial data | ||
|
||
If you are changing existing data (for instance when doing a http PATCH), set the `initialData` property of the form. The form expects a partial object which allows you to only include some of the fields in the Dto. | ||
|
||
```tsx | ||
const event: Partial<EventDto> = { | ||
title_nb: 'some title', | ||
title_en: 'some title', | ||
} | ||
``` | ||
```html | ||
<SamfForm<EventDto> initialData={event}> | ||
<!-- Your input fields --> | ||
</SamfForm> | ||
``` | ||
|
||
#### Advanced usage | ||
|
||
- Set `validateOnInit` to check validity instantly. Missing fields will be marked with red. | ||
- Set `devMode` to get a live preview of all the form values, errors and fields | ||
|
||
### Input Fields | ||
|
||
All inputs area created using `<SamfFormField>`. Required properties are: | ||
- `type` Which type of input is used, eg. 'text', 'number', 'image', 'date' etc. See `SamfFormFieldTypes`. | ||
- `field` The name of the property to set. In an `EventDto` you may want to use a field like `title_nb` or `duration`. | ||
|
||
Optional properties include: | ||
- `required` whether the field is invalid when empty/undefined. Default `true`. | ||
- `label` a text string label that is shown above the input | ||
- `options` a list of `DropDownOption` used for dropdown inputs | ||
- `defaultOption` a `DropDownOption` set as the default for dropdown inputs | ||
- `validator` a custom validation function which checks special requirements | ||
|
||
Example: | ||
|
||
```html | ||
<SamfFormField type="text" field="title_en" label="English title" /> | ||
<SamfFormField type="text-long" field="description_en' label="English description" /> | ||
<SamfFormField type="text" field="social_media_url" label="Social media" required={false}/> | ||
<SamfFormField type="image" field="image" label="Event Image"/> | ||
``` | ||
Option Example (with value type inside the `< >`): | ||
```tsx | ||
const myOptions: DropDownOption<number>[] = [ | ||
{value: 1, label: "One"}, | ||
{value: 2, label: "Two"}, | ||
] | ||
``` | ||
```html | ||
<SamfFormField<number> | ||
type="options" | ||
field="some_number_field" | ||
label="Pick a number" | ||
options={myOptions} | ||
defaultOption={myOptions[0]} | ||
/> | ||
``` | ||
## Implementation details | ||
TODO |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.