-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* start form docs * submit example * add server errror section * add focus state management example * change message to an error * full server example * demo * done * udpate * update pnpm * update form recipe * add comment about react query * Create moody-humans-yawn.md --------- Co-authored-by: Marcel Köhler <[email protected]>
- Loading branch information
Showing
16 changed files
with
623 additions
and
125 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
--- | ||
"@marigold/docs": minor | ||
"@marigold/components": patch | ||
--- | ||
|
||
docs: Add docs for `<Form>` |
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,39 @@ | ||
import { z } from 'zod'; | ||
|
||
import { NextResponse } from 'next/server'; | ||
|
||
// Helpers | ||
// --------------- | ||
const ALREADY_REGISTERED_EMAILS = [ | ||
'[email protected]', | ||
'[email protected]', | ||
]; | ||
|
||
const schema = z.object({ | ||
email: z | ||
.string() | ||
.email() | ||
.refine( | ||
val => { | ||
return !ALREADY_REGISTERED_EMAILS.includes(val); | ||
}, | ||
{ | ||
message: 'This email is already subscribed.', | ||
} | ||
), | ||
}); | ||
|
||
// POST | ||
// --------------- | ||
export const POST = async (req: Request) => { | ||
const body = await req.json(); | ||
const result = schema.safeParse(body); | ||
|
||
if (!result.success) { | ||
return NextResponse.json(result.error.flatten().fieldErrors, { | ||
status: 400, | ||
}); | ||
} | ||
|
||
return NextResponse.json(result.data); | ||
}; |
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,22 @@ | ||
import { Button, Form, Inset, Stack, TextField } from '@marigold/components'; | ||
|
||
export default () => { | ||
return ( | ||
<Form> | ||
<Inset space={8}> | ||
<Stack space={2} alignX="left"> | ||
<TextField label="User Name" name="user" type="name" width="1/2" /> | ||
<TextField | ||
label="Password" | ||
name="password" | ||
type="password" | ||
width="1/2" | ||
/> | ||
<Button variant="primary" type="submit"> | ||
Login | ||
</Button> | ||
</Stack> | ||
</Inset> | ||
</Form> | ||
); | ||
}; |
68 changes: 68 additions & 0 deletions
68
docs/content/components/form/form/form-focus-management.demo.tsx
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,68 @@ | ||
import { useState } from 'react'; | ||
|
||
import { | ||
Button, | ||
Form, | ||
Inline, | ||
Inset, | ||
Message, | ||
Stack, | ||
TextField, | ||
} from '@marigold/components'; | ||
|
||
export default () => { | ||
let [invalid, setInvalid] = useState(false); | ||
return ( | ||
<Form | ||
onInvalid={e => { | ||
e.preventDefault(); | ||
setInvalid(true); | ||
}} | ||
onSubmit={e => { | ||
e.preventDefault(); | ||
setInvalid(false); | ||
}} | ||
onReset={() => setInvalid(false)} | ||
> | ||
<Inset space={8}> | ||
<Stack space={4}> | ||
{invalid ? ( | ||
<Message | ||
variant="error" | ||
messageTitle="Whoopsies!" | ||
role="alert" | ||
tabIndex={-1} | ||
ref={e => e?.focus()} | ||
> | ||
Please enter both your email address and password to proceed. | ||
Ensure that all required fields are filled correctly before | ||
attempting to log in. | ||
</Message> | ||
) : null} | ||
<Stack space={2} alignX="left"> | ||
<TextField | ||
label="User Name" | ||
name="user" | ||
type="name" | ||
width="1/2" | ||
required | ||
/> | ||
<TextField | ||
label="Password" | ||
name="password" | ||
type="password" | ||
width="1/2" | ||
required | ||
/> | ||
<Inline space={2}> | ||
<Button variant="primary" type="submit"> | ||
Login | ||
</Button> | ||
<Button type="reset">Reset</Button> | ||
</Inline> | ||
</Stack> | ||
</Stack> | ||
</Inset> | ||
</Form> | ||
); | ||
}; |
63 changes: 63 additions & 0 deletions
63
docs/content/components/form/form/form-submission.demo.tsx
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,63 @@ | ||
import { useState } from 'react'; | ||
|
||
import { | ||
Button, | ||
Form, | ||
Inline, | ||
Inset, | ||
Stack, | ||
Text, | ||
TextField, | ||
} from '@marigold/components'; | ||
|
||
export default () => { | ||
let [action, setAction] = useState<string | null>(null); | ||
return ( | ||
<Form | ||
onSubmit={e => { | ||
// This will prevent the native form submission | ||
e.preventDefault(); | ||
|
||
// Read the form values and convert it to a regular object | ||
const data = Object.fromEntries(new FormData(e.currentTarget)); | ||
setAction(`data: ${JSON.stringify(data, null, 2)}`); | ||
}} | ||
onReset={() => setAction('reset')} | ||
> | ||
<Inset space={8}> | ||
<Stack space={4}> | ||
<Stack space={2} alignX="left"> | ||
<TextField | ||
label="User Name" | ||
name="user" | ||
type="name" | ||
width="1/2" | ||
required | ||
/> | ||
<TextField | ||
label="Password" | ||
name="password" | ||
type="password" | ||
width="1/2" | ||
required | ||
/> | ||
<Inline space={2}> | ||
<Button variant="primary" type="submit"> | ||
Login | ||
</Button> | ||
<Button type="reset">Reset</Button> | ||
</Inline> | ||
</Stack> | ||
{action && ( | ||
<div className="bg-secondary-200 rounded-lg p-4"> | ||
<Text weight="bold">Action:</Text> | ||
<pre> | ||
<code>{action}</code> | ||
</pre> | ||
</div> | ||
)} | ||
</Stack> | ||
</Inset> | ||
</Form> | ||
); | ||
}; |
22 changes: 22 additions & 0 deletions
22
docs/content/components/form/form/form-validation-errors.demo.tsx
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,22 @@ | ||
import { Button, Form, Inset, Stack, TextField } from '@marigold/components'; | ||
|
||
export default () => { | ||
return ( | ||
<Form validationErrors={{ password: 'Incorrect password.' }}> | ||
<Inset space={8}> | ||
<Stack space={2} alignX="left"> | ||
<TextField label="User Name" name="user" type="name" width="1/2" /> | ||
<TextField | ||
label="Password" | ||
name="password" | ||
type="password" | ||
width="1/2" | ||
/> | ||
<Button variant="primary" type="submit"> | ||
Login | ||
</Button> | ||
</Stack> | ||
</Inset> | ||
</Form> | ||
); | ||
}; |
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,109 @@ | ||
--- | ||
title: Form | ||
caption: Wrap your fields to submit user data and enable input validation. | ||
badge: new | ||
--- | ||
|
||
The `<Form>` component acts as a container for a set of fields, enabling data transmission to a server. It operates like a standard HTML form, initiating either a request based on the specified `method` attribute. | ||
|
||
Additionally, the `<Form>` allows to validate user input and offers feedback for incorrect data entries, enhancing the overall resilience and user-friendliness of the form submission process. See the [Validation](/concepts/validation) guide to learn more about form validation. | ||
|
||
## Usage | ||
|
||
### Import | ||
|
||
To import the component you just have to use this code below. | ||
|
||
```tsx | ||
import { Form } from '@marigold/components'; | ||
``` | ||
|
||
### Appearance | ||
|
||
<AppearanceTable component={title} /> | ||
|
||
### Props | ||
|
||
<PropsTable | ||
props={[ | ||
{ | ||
property: 'validationErrors', | ||
type: 'Record<string, string | string[]>', | ||
description: | ||
'Validation errors for the form, typically returned by a server. This should be set to an object mapping from input names to errors.', | ||
default: '-', | ||
}, | ||
{ | ||
property: 'action', | ||
type: `string | FormHTMLAttributes<HTMLFormElement>['action']`, | ||
description: | ||
'Endpoint where to send the form-data when the form is submitted.', | ||
default: '-', | ||
}, | ||
{ | ||
property: 'method', | ||
type: `'get' | 'post' | 'dialog'`, | ||
description: 'The HTTP method to submit the form with.', | ||
default: '-', | ||
}, | ||
{ | ||
property: 'onSubmit', | ||
type: `(event: FormEvent<HTMLFormElement>) => void`, | ||
description: 'Triggered when a user submits the form.', | ||
default: '-', | ||
}, | ||
{ | ||
property: 'onReset', | ||
type: `(event: FormEvent<HTMLFormElement>) => void`, | ||
description: 'Triggered when a user resets the form.', | ||
default: '-', | ||
}, | ||
{ | ||
property: 'onInvalid', | ||
type: `(event: FormEvent<HTMLFormElement>) => void`, | ||
description: | ||
'Triggered for each invalid field when a user submits the form.', | ||
default: '-', | ||
}, | ||
{ | ||
property: '...', | ||
type: '', | ||
description: 'Yes you can use all regular attributes of `form`!', | ||
default: '', | ||
}, | ||
]} | ||
/> | ||
|
||
## Examples | ||
|
||
### Setup | ||
|
||
This is a simple setup how to use a `<Form>`. | ||
|
||
<ComponentDemo file="./form-base.demo.tsx" /> | ||
|
||
### Handling submission | ||
|
||
The `onSubmit` event is useful for custom form actions, such as calling a REST API, instead of relying on the native form submission. It triggeres when a user submits the form using the Enter key or clicks the submit button. The `onReset` event is triggered when a user presses a reset button (`[type=reset]`). | ||
|
||
<ComponentDemo file="./form-submission.demo.tsx" /> | ||
|
||
### Server Errors | ||
|
||
The `<Form>` component handles passed errors, typically received from a server after form submission. To display validation errors, set the `validationErrors` prop as an object mapping each field's name prop to a string or array of strings representing errors. These errors appear to the user as soon as the `validationErrors` prop is set and are cleared when the user modifies the corresponding field's value. | ||
|
||
<ComponentDemo file="./form-validation-errors.demo.tsx" /> | ||
|
||
For more information about form validation, see the [Validation](/concepts/validation) guide. | ||
|
||
### Focus Management | ||
|
||
As you can see in the previous server errors example, when a user submits a form with validation errors, the first invalid field is automatically focused. This behavior can be customized using `e.preventDefault` during the `onInvalid` event and manage the focus manually. | ||
|
||
<ComponentDemo file="./form-focus-management.demo.tsx" /> | ||
|
||
<p /> | ||
|
||
<Message messageTitle="Want more?!"> | ||
You can find more examples and usages of the `<Form>` component on the [Validation](/concepts/validation) page and in the [Forms](/recipes/form-recipes) recipes. | ||
</Message> |
Oops, something went wrong.