-
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.
feat: add rudimentrary impl of zod to atom group
rudimentrary implementation, probably doesn't handle types probably
- Loading branch information
1 parent
ce67123
commit 5088546
Showing
5 changed files
with
172 additions
and
1 deletion.
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,61 @@ | ||
import 'regenerator-runtime/runtime'; | ||
|
||
import { | ||
act, | ||
cleanup, | ||
fireEvent, | ||
render, | ||
waitFor, | ||
} from '@testing-library/react'; | ||
import React from 'react'; | ||
import { z } from 'zod'; | ||
import { atomFromZodSchema } from '../src/utils/zod'; | ||
import { FormControlPrimitiveValues } from './components/FormControl'; | ||
|
||
afterEach(() => { | ||
cleanup(); | ||
}); | ||
|
||
describe('atomFromZodSchema', () => { | ||
it('will create a form atom', async () => { | ||
const schema = z.object({ | ||
email: z.string().email().default('[email protected]'), | ||
age: z.number(), | ||
agreed: z.boolean(), | ||
}); | ||
|
||
const formAtom = atomFromZodSchema(schema); | ||
|
||
const { getByText, getByLabelText } = render( | ||
<div> | ||
<FormControlPrimitiveValues atomDef={formAtom} /> | ||
</div>, | ||
); | ||
|
||
await act(async () => { | ||
await waitFor(() => { | ||
const emailInput = getByLabelText('email-input'); | ||
getByText('email: [email protected]'); | ||
|
||
const ageInput = getByLabelText('age-input'); | ||
getByText('age: 0'); | ||
|
||
const agreedInput = getByLabelText('agree-input'); | ||
getByText('agreed: No'); | ||
|
||
fireEvent.change(emailInput, { | ||
target: { value: '[email protected]' }, | ||
}); | ||
getByText('email: [email protected]'); | ||
|
||
fireEvent.change(ageInput, { | ||
target: { value: '2' }, | ||
}); | ||
getByText('age: 2'); | ||
|
||
fireEvent.click(agreedInput); | ||
getByText('agreed: Yes'); | ||
}); | ||
}); | ||
}); | ||
}); |
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
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 |
---|---|---|
@@ -0,0 +1,66 @@ | ||
import { ZodDefault, ZodObject, ZodType } from 'zod'; | ||
import { Options, atomWithFormControls } from '../atomWithFormControls'; | ||
import { atomWithValidate } from '../atomWithValidate'; | ||
import { AtomWithValidation } from '../validateAtoms'; | ||
|
||
const INSTANCE_DEFAULT_MAP = { | ||
string: 'defaultemail', | ||
number: 0, | ||
boolean: false, | ||
}; | ||
|
||
const INTERNAL_INSTANCE_PRIMITIVE_MAP = { | ||
ZodString: 'string', | ||
ZodNumber: 'number', | ||
ZodBoolean: 'boolean', | ||
}; | ||
|
||
type INTERNAL_PRIMITIVE_MAP_KEY = keyof typeof INTERNAL_INSTANCE_PRIMITIVE_MAP; | ||
type INTERNAL_DEFAULT_MAP_KEY = keyof typeof INSTANCE_DEFAULT_MAP; | ||
|
||
export function atomFromZodSchema< | ||
T, | ||
AtomGroup extends Record<string, AtomWithValidation<any>>, | ||
Keys extends Extract<keyof AtomGroup, string>, | ||
Vals extends AtomGroup[Keys], | ||
>(schema: ZodType<T>, options?: Options<Keys, Vals>) { | ||
const result = {} as Record<string, AtomWithValidation<any>>; | ||
|
||
// eslint-disable-next-line no-underscore-dangle | ||
if (schema instanceof ZodObject) { | ||
// eslint-disable-next-line no-underscore-dangle | ||
Object.entries(schema._def.shape()).forEach(([key, value]) => { | ||
if (value instanceof ZodDefault) { | ||
// eslint-disable-next-line no-underscore-dangle | ||
const validationAtom = atomWithValidate(value._def.defaultValue(), { | ||
validate: (d) => value.parse(d), | ||
}); | ||
result[key] = validationAtom; | ||
} else if (value instanceof ZodType) { | ||
const typeName = value.constructor.name; | ||
const toPrimitiveKey = Object.keys( | ||
INTERNAL_INSTANCE_PRIMITIVE_MAP, | ||
).find((d) => { | ||
return typeName === d; | ||
}) as INTERNAL_PRIMITIVE_MAP_KEY | undefined; | ||
if (toPrimitiveKey) { | ||
const primitiveValue = INTERNAL_INSTANCE_PRIMITIVE_MAP[ | ||
toPrimitiveKey | ||
] as INTERNAL_DEFAULT_MAP_KEY; | ||
const defaultValue = INSTANCE_DEFAULT_MAP[primitiveValue]; | ||
// eslint-disable-next-line no-underscore-dangle | ||
const validationAtom = atomWithValidate(defaultValue, { | ||
validate: (d) => value.parse(d), | ||
}); | ||
result[key] = validationAtom; | ||
} | ||
} | ||
}); | ||
} | ||
return atomWithFormControls<AtomGroup, Keys, Vals>(result as AtomGroup, { | ||
validate: (values) => { | ||
schema.parse(values); | ||
}, | ||
...options, | ||
}); | ||
} |