@ -303,6 +303,27 @@ import { CatalogDecorator } from '~/testing/decorators/CatalogDecorator';
|
||||
import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator';=
|
||||
```
|
||||
|
||||
## Schema Validation
|
||||
|
||||
[Zod](https://github.com/colinhacks/zod) is used as the schema validator for untyped objects:
|
||||
|
||||
```js
|
||||
const validationSchema = z
|
||||
.object({
|
||||
exist: z.boolean(),
|
||||
email: z
|
||||
.string()
|
||||
.email('Email must be a valid email'),
|
||||
password: z
|
||||
.string()
|
||||
.regex(PASSWORD_REGEX, 'Password must contain at least 8 characters'),
|
||||
})
|
||||
.required();
|
||||
|
||||
type Form = z.infer<typeof validationSchema>;
|
||||
```
|
||||
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
Prioritize thorough manual testing before proceeding to guarantee that modifications haven’t caused disruptions elsewhere, given that tests have not yet been extensively integrated.
|
||||
|
||||
@ -64,7 +64,6 @@
|
||||
"uuid": "^9.0.0",
|
||||
"web-vitals": "^2.1.4",
|
||||
"xlsx-ugnis": "^0.19.3",
|
||||
"yup": "^1.2.0",
|
||||
"zod": "^3.22.2"
|
||||
},
|
||||
"scripts": {
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { SubmitHandler, useForm } from 'react-hook-form';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
import { yupResolver } from '@hookform/resolvers/yup';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
import * as Yup from 'yup';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { authProvidersState } from '@/client-config/states/authProvidersState';
|
||||
import { isSignInPrefilledState } from '@/client-config/states/isSignInPrefilledState';
|
||||
@ -28,19 +28,18 @@ export enum SignInUpStep {
|
||||
Email = 'email',
|
||||
Password = 'password',
|
||||
}
|
||||
const validationSchema = Yup.object()
|
||||
.shape({
|
||||
exist: Yup.boolean().required(),
|
||||
email: Yup.string()
|
||||
.email('Email must be a valid email')
|
||||
.required('Email must be a valid email'),
|
||||
password: Yup.string()
|
||||
.matches(PASSWORD_REGEX, 'Password must contain at least 8 characters')
|
||||
.required(),
|
||||
|
||||
const validationSchema = z
|
||||
.object({
|
||||
exist: z.boolean(),
|
||||
email: z.string().email('Email must be a valid email'),
|
||||
password: z
|
||||
.string()
|
||||
.regex(PASSWORD_REGEX, 'Password must contain at least 8 characters'),
|
||||
})
|
||||
.required();
|
||||
|
||||
type Form = Yup.InferType<typeof validationSchema>;
|
||||
type Form = z.infer<typeof validationSchema>;
|
||||
|
||||
export const useSignInUp = () => {
|
||||
const navigate = useNavigate();
|
||||
@ -72,7 +71,7 @@ export const useSignInUp = () => {
|
||||
defaultValues: {
|
||||
exist: false,
|
||||
},
|
||||
resolver: yupResolver(validationSchema),
|
||||
resolver: zodResolver(validationSchema),
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@ -2,7 +2,7 @@ import { isBoolean } from '@sniptt/guards';
|
||||
|
||||
import { FieldBooleanValue } from '../FieldMetadata';
|
||||
|
||||
// TODO: add yup
|
||||
// TODO: add zod
|
||||
export const isFieldBooleanValue = (
|
||||
fieldValue: unknown,
|
||||
): fieldValue is FieldBooleanValue => isBoolean(fieldValue);
|
||||
|
||||
@ -2,7 +2,7 @@ import { isString } from '@sniptt/guards';
|
||||
|
||||
import { FieldChipValue } from '../FieldMetadata';
|
||||
|
||||
// TODO: add yup
|
||||
// TODO: add zod
|
||||
export const isFieldChipValue = (
|
||||
fieldValue: unknown,
|
||||
): fieldValue is FieldChipValue => isString(fieldValue);
|
||||
|
||||
@ -2,7 +2,7 @@ import { isNull, isString } from '@sniptt/guards';
|
||||
|
||||
import { FieldDateValue } from '../FieldMetadata';
|
||||
|
||||
// TODO: add yup
|
||||
// TODO: add zod
|
||||
export const isFieldDateValue = (
|
||||
fieldValue: unknown,
|
||||
): fieldValue is FieldDateValue => isNull(fieldValue) || isString(fieldValue);
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { FieldDoubleTextValue } from '../FieldMetadata';
|
||||
import { DoubleTextTypeResolver } from '../resolvers/DoubleTextTypeResolver';
|
||||
|
||||
// TODO: add yup
|
||||
// TODO: add zod
|
||||
export const isFieldDoubleTextValue = (
|
||||
fieldValue: unknown,
|
||||
): fieldValue is FieldDoubleTextValue =>
|
||||
|
||||
@ -2,7 +2,7 @@ import { isString } from '@sniptt/guards';
|
||||
|
||||
import { FieldEmailValue } from '../FieldMetadata';
|
||||
|
||||
// TODO: add yup
|
||||
// TODO: add zod
|
||||
export const isFieldEmailValue = (
|
||||
fieldValue: unknown,
|
||||
): fieldValue is FieldEmailValue => isString(fieldValue);
|
||||
|
||||
@ -2,7 +2,7 @@ import { isNull, isNumber } from '@sniptt/guards';
|
||||
|
||||
import { FieldMoneyValue } from '../FieldMetadata';
|
||||
|
||||
// TODO: add yup
|
||||
// TODO: add zod
|
||||
export const isFieldMoneyValue = (
|
||||
fieldValue: unknown,
|
||||
): fieldValue is FieldMoneyValue => isNull(fieldValue) || isNumber(fieldValue);
|
||||
|
||||
@ -2,7 +2,7 @@ import { isNull, isNumber } from '@sniptt/guards';
|
||||
|
||||
import { FieldNumberValue } from '../FieldMetadata';
|
||||
|
||||
// TODO: add yup
|
||||
// TODO: add zod
|
||||
export const isFieldNumberValue = (
|
||||
fieldValue: unknown,
|
||||
): fieldValue is FieldNumberValue => isNull(fieldValue) || isNumber(fieldValue);
|
||||
|
||||
@ -2,7 +2,7 @@ import { isString } from '@sniptt/guards';
|
||||
|
||||
import { FieldPhoneValue } from '../FieldMetadata';
|
||||
|
||||
// TODO: add yup
|
||||
// TODO: add zod
|
||||
export const isFieldPhoneValue = (
|
||||
fieldValue: unknown,
|
||||
): fieldValue is FieldPhoneValue => isString(fieldValue);
|
||||
|
||||
@ -2,7 +2,7 @@ import { isNumber } from '@sniptt/guards';
|
||||
|
||||
import { FieldProbabilityValue } from '../FieldMetadata';
|
||||
|
||||
// TODO: add yup
|
||||
// TODO: add zod
|
||||
export const isFieldProbabilityValue = (
|
||||
fieldValue: unknown,
|
||||
): fieldValue is FieldProbabilityValue => isNumber(fieldValue);
|
||||
|
||||
@ -2,7 +2,7 @@ import { isNull, isObject, isUndefined } from '@sniptt/guards';
|
||||
|
||||
import { FieldRelationValue } from '../FieldMetadata';
|
||||
|
||||
// TODO: add yup
|
||||
// TODO: add zod
|
||||
export const isFieldRelationValue = (
|
||||
fieldValue: unknown,
|
||||
): fieldValue is FieldRelationValue =>
|
||||
|
||||
@ -2,7 +2,7 @@ import { isString } from '@sniptt/guards';
|
||||
|
||||
import { FieldTextValue } from '../FieldMetadata';
|
||||
|
||||
// TODO: add yup
|
||||
// TODO: add zod
|
||||
export const isFieldTextValue = (
|
||||
fieldValue: unknown,
|
||||
): fieldValue is FieldTextValue => isString(fieldValue);
|
||||
|
||||
@ -7,7 +7,7 @@ const urlV2Schema = z.object({
|
||||
text: z.string(),
|
||||
});
|
||||
|
||||
// TODO: add yup
|
||||
// TODO: add zod
|
||||
export const isFieldURLV2Value = (
|
||||
fieldValue: unknown,
|
||||
): fieldValue is FieldURLV2Value => urlV2Schema.safeParse(fieldValue).success;
|
||||
|
||||
@ -2,7 +2,7 @@ import { isString } from '@sniptt/guards';
|
||||
|
||||
import { FieldURLValue } from '../FieldMetadata';
|
||||
|
||||
// TODO: add yup
|
||||
// TODO: add zod
|
||||
export const isFieldURLValue = (
|
||||
fieldValue: unknown,
|
||||
): fieldValue is FieldURLValue => isString(fieldValue);
|
||||
|
||||
@ -3,10 +3,10 @@ import { Controller, SubmitHandler, useForm } from 'react-hook-form';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { getOperationName } from '@apollo/client/utilities';
|
||||
import styled from '@emotion/styled';
|
||||
import { yupResolver } from '@hookform/resolvers/yup';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { Key } from 'ts-key-enum';
|
||||
import * as Yup from 'yup';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { SubTitle } from '@/auth/components/SubTitle';
|
||||
import { Title } from '@/auth/components/Title';
|
||||
@ -42,14 +42,14 @@ const StyledComboInputContainer = styled.div`
|
||||
}
|
||||
`;
|
||||
|
||||
const validationSchema = Yup.object()
|
||||
.shape({
|
||||
firstName: Yup.string().required('First name can not be empty'),
|
||||
lastName: Yup.string().required('Last name can not be empty'),
|
||||
const validationSchema = z
|
||||
.object({
|
||||
firstName: z.string().min(1, { message: 'First name can not be empty' }),
|
||||
lastName: z.string().min(1, { message: 'Last name can not be empty' }),
|
||||
})
|
||||
.required();
|
||||
|
||||
type Form = Yup.InferType<typeof validationSchema>;
|
||||
type Form = z.infer<typeof validationSchema>;
|
||||
|
||||
export const CreateProfile = () => {
|
||||
const navigate = useNavigate();
|
||||
@ -72,7 +72,7 @@ export const CreateProfile = () => {
|
||||
firstName: currentUser?.firstName ?? '',
|
||||
lastName: currentUser?.lastName ?? '',
|
||||
},
|
||||
resolver: yupResolver(validationSchema),
|
||||
resolver: zodResolver(validationSchema),
|
||||
});
|
||||
|
||||
const onSubmit: SubmitHandler<Form> = useCallback(
|
||||
|
||||
@ -3,8 +3,8 @@ import { Controller, SubmitHandler, useForm } from 'react-hook-form';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { getOperationName } from '@apollo/client/utilities';
|
||||
import styled from '@emotion/styled';
|
||||
import { yupResolver } from '@hookform/resolvers/yup';
|
||||
import * as Yup from 'yup';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { SubTitle } from '@/auth/components/SubTitle';
|
||||
import { Title } from '@/auth/components/Title';
|
||||
@ -31,13 +31,13 @@ const StyledButtonContainer = styled.div`
|
||||
width: 200px;
|
||||
`;
|
||||
|
||||
const validationSchema = Yup.object()
|
||||
.shape({
|
||||
name: Yup.string().required('Name can not be empty'),
|
||||
const validationSchema = z
|
||||
.object({
|
||||
name: z.string().min(1, { message: 'Name can not be empty' }),
|
||||
})
|
||||
.required();
|
||||
|
||||
type Form = Yup.InferType<typeof validationSchema>;
|
||||
type Form = z.infer<typeof validationSchema>;
|
||||
|
||||
export const CreateWorkspace = () => {
|
||||
const navigate = useNavigate();
|
||||
@ -57,7 +57,7 @@ export const CreateWorkspace = () => {
|
||||
defaultValues: {
|
||||
name: '',
|
||||
},
|
||||
resolver: yupResolver(validationSchema),
|
||||
resolver: zodResolver(validationSchema),
|
||||
});
|
||||
|
||||
const onSubmit: SubmitHandler<Form> = useCallback(
|
||||
|
||||
@ -15975,11 +15975,6 @@ prop-types@^15.6.1, prop-types@^15.7.2, prop-types@^15.8.1:
|
||||
object-assign "^4.1.1"
|
||||
react-is "^16.13.1"
|
||||
|
||||
property-expr@^2.0.5:
|
||||
version "2.0.5"
|
||||
resolved "https://registry.yarnpkg.com/property-expr/-/property-expr-2.0.5.tgz#278bdb15308ae16af3e3b9640024524f4dc02cb4"
|
||||
integrity sha512-IJUkICM5dP5znhCckHSv30Q4b5/JA5enCtkRHYaOVOAocnH/1BQEYTC5NMfT3AVl/iXKdr3aqQbQn9DxyWknwA==
|
||||
|
||||
property-information@^6.0.0:
|
||||
version "6.2.0"
|
||||
resolved "https://registry.yarnpkg.com/property-information/-/property-information-6.2.0.tgz#b74f522c31c097b5149e3c3cb8d7f3defd986a1d"
|
||||
@ -18399,11 +18394,6 @@ thunky@^1.0.2:
|
||||
resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d"
|
||||
integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==
|
||||
|
||||
tiny-case@^1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/tiny-case/-/tiny-case-1.0.3.tgz#d980d66bc72b5d5a9ca86fb7c9ffdb9c898ddd03"
|
||||
integrity sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q==
|
||||
|
||||
tiny-invariant@^1.0.6, tiny-invariant@^1.3.1:
|
||||
version "1.3.1"
|
||||
resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.3.1.tgz#8560808c916ef02ecfd55e66090df23a4b7aa642"
|
||||
@ -18462,11 +18452,6 @@ toidentifier@1.0.1:
|
||||
resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35"
|
||||
integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==
|
||||
|
||||
toposort@^2.0.2:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/toposort/-/toposort-2.0.2.tgz#ae21768175d1559d48bef35420b2f4962f09c330"
|
||||
integrity sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==
|
||||
|
||||
tough-cookie@^4.0.0:
|
||||
version "4.1.3"
|
||||
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.3.tgz#97b9adb0728b42280aa3d814b6b999b2ff0318bf"
|
||||
@ -19996,16 +19981,6 @@ yocto-queue@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-1.0.0.tgz#7f816433fb2cbc511ec8bf7d263c3b58a1a3c251"
|
||||
integrity sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==
|
||||
|
||||
yup@^1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/yup/-/yup-1.2.0.tgz#9e51af0c63bdfc9be0fdc6c10aa0710899d8aff6"
|
||||
integrity sha512-PPqYKSAXjpRCgLgLKVGPA33v5c/WgEx3wi6NFjIiegz90zSwyMpvTFp/uGcVnnbx6to28pgnzp/q8ih3QRjLMQ==
|
||||
dependencies:
|
||||
property-expr "^2.0.5"
|
||||
tiny-case "^1.0.3"
|
||||
toposort "^2.0.2"
|
||||
type-fest "^2.19.0"
|
||||
|
||||
zen-observable-ts@^1.2.5:
|
||||
version "1.2.5"
|
||||
resolved "https://registry.yarnpkg.com/zen-observable-ts/-/zen-observable-ts-1.2.5.tgz#6c6d9ea3d3a842812c6e9519209365a122ba8b58"
|
||||
|
||||
Reference in New Issue
Block a user