-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #28 from stevent-team/docs/improve-examples
Improve examples
- Loading branch information
Showing
24 changed files
with
1,052 additions
and
615 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,5 @@ | ||
--- | ||
"@stevent-team/react-zoom-form": patch | ||
--- | ||
|
||
Update types to allow any zod type as a schema |
This file was deleted.
Oops, something went wrong.
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,94 @@ | ||
import { Route, Switch, Link, useLocation } from 'wouter' | ||
import { SubmitHandler } from '@stevent-team/react-zoom-form' | ||
import { repository } from '../package.json' | ||
|
||
import Basic from './basic' | ||
import Arrays from './arrays' | ||
import Nested from './nested' | ||
import Coerced from './coerced' | ||
import Conditional from './conditional' | ||
import Controlled from './controlled' | ||
import KitchenSink from './kitchen-sink' | ||
|
||
interface Example { | ||
name: string | ||
path: string | ||
component: ({ onSubmit }: { onSubmit: SubmitHandler }) => JSX.Element | ||
} | ||
|
||
const EXAMPLES: Example[] = [ | ||
{ | ||
name: 'Basic', | ||
path: '/basic', | ||
component: Basic, | ||
}, | ||
{ | ||
name: 'Array Field', | ||
path: '/arrays', | ||
component: Arrays, | ||
}, | ||
{ | ||
name: 'Nested Fields', | ||
path: '/nested', | ||
component: Nested, | ||
}, | ||
{ | ||
name: 'Coerced Fields', | ||
path: '/coerced', | ||
component: Coerced, | ||
}, | ||
{ | ||
name: 'Conditional Field', | ||
path: '/conditional', | ||
component: Conditional, | ||
}, | ||
{ | ||
name: '3rd Party & Controlled Fields', | ||
path: '/controlled', | ||
component: Controlled, | ||
}, | ||
{ | ||
name: 'Kitchen Sink', | ||
path: '/kitchen-sink', | ||
component: KitchenSink, | ||
}, | ||
] | ||
|
||
// Shared submit handler | ||
const onSubmit: SubmitHandler = values => { | ||
console.log(values) | ||
document.dispatchEvent(new CustomEvent('zoomSubmit', { detail: values })) | ||
} | ||
|
||
const App = () => { | ||
const [location] = useLocation() | ||
|
||
return <> | ||
<nav> | ||
{'🏎️ '}<a href={repository} target="_blank" rel="nofollow noreferrer">React Zoom Form</a> | ||
<h2>Examples</h2> | ||
<ul> | ||
{EXAMPLES.map((example, i) => { | ||
const active = location === (i === 0 ? '/' : example.path) | ||
|
||
return <li key={example.path} className={active ? 'active' : undefined}> | ||
{active ? <span>{example.name}</span> : <Link to={i === 0 ? '/' : example.path}>{example.name}</Link>} | ||
{' '}(<a href={`${repository}/blob/main/examples${example.path}`} target="_blank" rel="nofollow noreferrer">code</a>) | ||
</li> | ||
})} | ||
</ul> | ||
</nav> | ||
|
||
<main> | ||
<Switch> | ||
{EXAMPLES.map((example, i) => <Route key={example.path} path={i === 0 ? '/' : example.path}> | ||
<example.component onSubmit={onSubmit} /> | ||
</Route>)} | ||
|
||
<Route>Example not found</Route> | ||
</Switch> | ||
</main> | ||
</> | ||
} | ||
|
||
export default App |
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,32 @@ | ||
import { Field, fieldErrors, getValue } from '@stevent-team/react-zoom-form' | ||
import { useCallback, useEffect, useState } from 'react' | ||
|
||
interface OutputProps { | ||
isDirty: boolean | ||
fields: Field | ||
} | ||
|
||
const Output = ({ isDirty, fields }: OutputProps) => { | ||
const [submittedValue, setSubmittedValue] = useState() | ||
|
||
const handleSubmit = useCallback((e: Event) => { | ||
if (e instanceof CustomEvent) setSubmittedValue(e.detail) | ||
}, []) | ||
|
||
useEffect(() => { | ||
document.addEventListener('zoomSubmit', handleSubmit) | ||
return () => document.removeEventListener('zoomSubmit', handleSubmit) | ||
}) | ||
|
||
return <output> | ||
<div><strong>isDirty:</strong> {isDirty ? 'true' : 'false'}</div> | ||
<div><strong>value:</strong> {JSON.stringify(getValue(fields), null, 2)}</div> | ||
<div><strong>errors:</strong> {JSON.stringify(fieldErrors(fields), null, 2)}</div> | ||
{submittedValue && <> | ||
<div><strong>submitted value:</strong> {JSON.stringify(submittedValue, null, 2)}</div> | ||
<div><em>Also view the submitted value in the console</em></div> | ||
</>} | ||
</output> | ||
} | ||
|
||
export default Output |
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,41 @@ | ||
import { SubmitHandler, useForm, Errors, getValue, setValue } from '@stevent-team/react-zoom-form' | ||
import { z } from 'zod' | ||
import Output from '../Output' | ||
|
||
export const schema = z.object({ | ||
flowers: z.array(z.string().min(1)).min(1).max(10), | ||
}) | ||
|
||
const Arrays = ({ onSubmit }: { onSubmit: SubmitHandler<typeof schema> }) => { | ||
const { fields, handleSubmit, isDirty } = useForm({ schema, initialValues: { flowers: [''] } }) | ||
|
||
return <> | ||
<form onSubmit={handleSubmit(onSubmit)}> | ||
<label>Flower names</label> | ||
|
||
{getValue(fields.flowers)?.map((_value, i) => | ||
<div key={i} style={{ display: 'flex', alignItems: 'center', gap: 20 }}> | ||
<input {...fields.flowers[i].register()} placeholder={`Flower ${i + 1}`} type="text" /> | ||
<button | ||
type="button" | ||
onClick={() => setValue(fields.flowers, value => value?.filter((_, j) => j !== i))} | ||
style={{ margin: 0, width: 100 }} | ||
>Delete</button> | ||
</div> | ||
)} | ||
|
||
<button | ||
type="button" | ||
onClick={() => setValue(fields.flowers, value => [...value ?? [], ''])} | ||
>Add flower</button> | ||
|
||
<Errors field={fields.flowers} className="error" /> | ||
|
||
<button>Save changes</button> | ||
</form> | ||
|
||
<Output isDirty={isDirty} fields={fields} /> | ||
</> | ||
} | ||
|
||
export default Arrays |
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,69 @@ | ||
import { describe, it, expect, vi } from 'vitest' | ||
import { render, screen } from '@testing-library/react' | ||
import { z } from 'zod' | ||
import { SubmitHandler } from '@stevent-team/react-zoom-form' | ||
import userEvent from '@testing-library/user-event' | ||
import Basic, { schema } from '.' | ||
|
||
|
||
describe('Simple flat form with text values', () => { | ||
const setup = (onSubmit?: SubmitHandler<typeof schema>) => ({ | ||
user: userEvent.setup(), | ||
...render(<Basic onSubmit={onSubmit ?? (() => void {})} />) | ||
}) | ||
|
||
it('renders inputs with the given names', async () => { | ||
// Render the form | ||
const { container } = setup() | ||
|
||
// Look for the inputs | ||
for (const inputName of Object.keys(schema.shape)) { | ||
const input = container.querySelector(`#${inputName}`) | ||
expect(input?.getAttribute('name')).toBe(inputName) | ||
} | ||
}) | ||
|
||
it('submit recieves registered values', async () => { | ||
// Render form + track values | ||
let formValues = null | ||
const { user, container } = setup((values: z.infer<typeof schema>) => { | ||
formValues = values | ||
}) | ||
|
||
const submitButton = screen.getByRole('button') | ||
const nameInput = container.querySelector('#name') | ||
const ageInput = container.querySelector('#age') | ||
|
||
expect(nameInput).not.toBeNull() | ||
expect(ageInput).not.toBeNull() | ||
|
||
await user.type(nameInput as Element, 'john') | ||
await user.type(ageInput as Element, '18') | ||
await user.click(submitButton) | ||
|
||
expect(formValues).toBeTruthy() | ||
expect(formValues).toEqual({ | ||
name: 'john', | ||
age: 18, | ||
}) | ||
}) | ||
|
||
it('validates missing fields', async () => { | ||
// Render form + spy on submissions | ||
const onSubmit = vi.fn() | ||
const { user, container } = setup(onSubmit) | ||
|
||
// Enter a name but not an age | ||
const submitButton = screen.getByRole('button') | ||
const nameInput = container.querySelector('#name') | ||
await user.type(nameInput as Element, 'john') | ||
await user.click(submitButton) | ||
|
||
// Validation should fail because missing age | ||
// thus shouldn't call on submit | ||
expect(onSubmit).not.toHaveBeenCalled() | ||
|
||
// Should show errors | ||
expect(container.querySelectorAll('.error')).toHaveLength(1) | ||
}) | ||
}) |
Oops, something went wrong.