React Yup schema - javascript

I'm using Yup to validate an email field:
const Schema = Yup.object().shape({
email: Yup.string()
.email("non valid email format")
.required("Email required"),
...
During form submit, I check if the email domain is included in a list of forbidden domains, if yes, I display an error message in a popup:
const forbidDomains = domains;
const domain = data.email.split("#");
if(forbidDomains.Domains.includes(domain[1])) {
this.setState({openPopup:true,statusMessage:"domain not allowed : " + domain[1]})
this.setState({isSubmitting:false})
return;
}
I would like to check the domain in the Yup schema, but I'm not sure if it's possible.

I think that what you are looking for is the .test() from Yup. Maybe something like this may work:
const schema = {
email: Yup.string()
.string()
.email()
.test('test-name', 'Validation failure message',
function(value) {
// your logic to check the domain
})
}

Related

Why is my yup string array validation schema not working?

Does anyone have any idea why this isnt working? I have a schema im trying to enforce with yup and ensure that the data inside the array are strings, here is the yup schema:
signUpSchema: async (req, res, next) => {
const signUpSchema = yup.object().shape({
username: yup
.string()
.required('name is required')
.typeError('name must be a string'),
email: yup
.string()
.email('email must be a valid email')
.required('email is required')
.typeError('email must be a string'),
password: yup
.string()
.required('password is required')
.typeError('password must be a string'),
password2: yup
.string()
.oneOf([yup.ref('password'), null], 'Passwords must match'),
isArtist: yup.boolean().required('isArtist is required').typeError('isArtist must be a boolean'),
areaCode: yup.string().required('areaCode is required').typeError('areaCode must be a string'),
// ! WHY IS THIS NOT WORKING, VALIDATION OF DATA INSIDE ARRAY ISNT WORKING?
locationTracking: yup
.array()
.of(yup.string())
.required('locationTracking is required')
.typeError('locationTracking must be an array')
.min(1, 'locationTracking must have at least one location'),
});
try {
await signUpSchema.validate(req.body);
next();
} catch (error) {
next(error);
}
},
locationTracking: yup
.array()
.of(yup.string())
.required('locationTracking is required')
.typeError('locationTracking must be an array')
.min(1, 'locationTracking must have at least one location'),
});
I also attempted adding the required and typeError methods
locationTracking: yup
.array()
.of(yup.string().require().typeError('data must be strings'))
.required('locationTracking is required')
.typeError('locationTracking must be an array')
.min(1, 'locationTracking must have at least one location'),
});
the schema enforces that locationTracking is an array but does not enforce that it needs to be an array of strings. Arrays of numbers, booleans etc all fly by this validation. Not sure what im doing wrong can't find anything online about this issue.
the data being validated is the req.body being sent as json by postman, i thought theres was some sort of type coercion occuring but when i check for the type of data it returns number, boolean etc, so completely flying by my validation
let schema = yup.array().of(yup.number().min(2));
await schema.isValid([2, 3]); // => true
await schema.isValid([1, -24]); // => false
schema.cast(['2', '3']); // => [2, 3]
Refer.
Your code
locationTracking: yup
.array()
.of(yup.string().min(1, 'locationTracking must have at least one location').required())
.required('locationTracking is required')
});

Validation to prevent brand names being in the email field?

So, I have this list of some brandnames:
const blackListedDomains = [
'facebook',
'google',
'amazon',
'netflix',
'hotmail',
'microsoft',
'gmail',
'yahoo',
]
which I need to prevent users to enter in the email field, what Schema could be built using Yup Package.
For example, I am trying this way:
const DomainSchema = Yup.object().shape({
subdomain: Yup.string()
.required('Required')
.lowercase()
.notOneOf(
blackListedDomains,
'Cannot use trademarked names in domain!',
),
})
But, I don't know how to get it from the email string, for example, if user inputs xyz#google.xyz, then how to extract google from the email string and then iterate it over the domain names list to find if it exists there and then show an error message ?
You can use test function of yup library to create a custom validation rule like this:
const DomainSchema = Yup.object().shape({
email: Yup.string()
.required("Required")
.lowercase()
.email("Not a valid mail")
.test(
"is-not-blacklist",
"Cannot use trademarked names in domain!",
(value) => {
if (value) {
const currentDomain = value.substring(
value.indexOf("#") + 1,
value.indexOf(".")
);
return !blackListedDomains.includes(currentDomain);
}
}
)
});
You can take a look at this sandbox for a live working example of this solution.

Can we add custom validations in formik YupValidationSchema?

Can we add custom validations in formik YupValidationSchema which I have mentioned below ?
YupValidationSchema = () => {
return Yup.object({
Email: Yup.string()
.max(256, "Length exceed 256 chars")
.matches(EMAIL_REGEXP, "Enter a valid email address")
.required("Email is required")
})
}
I need to add one more validation for the email field like it should accept certain domains
let domainVal = companyName.some((val) => email.includes(val));
if(!domainVal) error('Invalid')
else success('Valid');
can someone help me how can we add this domain validation code in yupvalidationschema?
You can use the Yup test method to add a custom validation to your Email
Basic syntax: .test("test-name", "Error message", function)
return Yup.object({
Email: Yup.string()
.max(256, "Length exceed 256 chars")
.matches(EMAIL_REGEXP, "Enter a valid email address")
.required("Email is required")
.test("email-include-domain", "Email Must include domain", (value) => companyNames.some((company) => value.includes(company));
Related Stackoverflow Post: How to get Yup to perform more than one custom validation?

Access to required fields with Yup

I use react-hook-form with yup to validate my forms.
I want to know all required fields of a schema to display some information in form (like '*' for required fields).
We could achieve this with this line of code :
schema.describe().fields[field].tests.findIndex(({ name }) => name === 'required'
However, this code doesn't work for conditional validation.
Schema example :
const schema = yup.object().shape({
email: yup
.string()
.email()
.required(),
isProfileRequired: yup
.boolean(),
profile: yup
.object()
.when('isProfileRequired',{
is: (isProfileRequired) => isProfileRequired,
then:
yup
.object()
.nullable()
.required()
})
})
Is there a way to retrieve this informations within the form ?
There is actually no "nice" way to do it but this works:
function isRequired(field){
return schema.fields[field]._exclusive.required || false
}
Notice: schema.fields[field]._exclusive.required returns true if required or undefined.
Testing exclusiveTests instead of _exclusive worked for me.
const isRequired =
validationSchema?.fields[aField.name]?.exclusiveTests?.required || false;

Conditional Validation in Yup

I have an email field that only gets shown if a checkbox is selected (boolean value is true). When the form get submitted, I only what this field to be required if the checkbox is checked (boolean is true).
This is what I've tried so far:
const validationSchema = yup.object().shape({
email: yup
.string()
.email()
.label('Email')
.when('showEmail', {
is: true,
then: yup.string().required('Must enter email address'),
}),
})
I've tried several other variations, but I get errors from Formik and Yup:
Uncaught (in promise) TypeError: Cannot read property 'length' of undefined
at yupToFormErrors (formik.es6.js:6198)
at formik.es6.js:5933
at <anonymous>
yupToFormErrors # formik.es6.js:6198
And I get validation errors from Yup as well. What am I doing wrong?
You probably aren't defining a validation rule for the showEmail field.
I've done a CodeSandox to test it out and as soon as I added:
showEmail: yup.boolean()
The form started validation correctly and no error was thrown.
This is the url: https://codesandbox.io/s/74z4px0k8q
And for future this was the correct validation schema:
validationSchema={yup.object().shape({
showEmail: yup.boolean(),
email: yup
.string()
.email()
.when("showEmail", {
is: true,
then: yup.string().required("Must enter email address")
})
})
}
Formik author here...
To make Yup.when work properly, you would have to add showEmail to initialValues and to your Yup schema shape.
In general, when using validationSchema, it is best practices to ensure that all of you form's fields have initial values so that Yup can see them immediately.
The result would look like:
<Formik
initialValues={{ email: '', showEmail: false }}
validationSchema={Yup.object().shape({
showEmail: Yup.boolean(),
email: Yup
.string()
.email()
.when("showEmail", {
is: true,
then: Yup.string().required("Must enter email address")
})
})
}
/>
You can even use a function for complex cases . Function case helps for complex validations
validationSchema={yup.object().shape({
showEmail: yup.boolean(),
email: yup
.string()
.email()
.when("showEmail", (showEmail, schema) => {
if(showEmail)
return schema.required("Must enter email address")
return schema
})
})
}
Totally agree with #João Cunha's answer. Just a supplement for the use case of Radio button.
When we use radio button as condition, we can check value of string instead of boolean. e.g. is: 'Phone'
const ValidationSchema = Yup.object().shape({
// This is the radio button.
preferredContact: Yup.string()
.required('Preferred contact is required.'),
// This is the input field.
contactPhone: Yup.string()
.when('preferredContact', {
is: 'Phone',
then: Yup.string()
.required('Phone number is required.'),
}),
// This is another input field.
contactEmail: Yup.string()
.when('preferredContact', {
is: 'Email',
then: Yup.string()
.email('Please use a valid email address.')
.required('Email address is required.'),
}),
});
This the radio button written in ReactJS, onChange method is the key to trigger the condition checking.
<label>
<input
name="preferredContact" type="radio" value="Email"
checked={this.state.preferredContact == 'Email'}
onChange={() => this.handleRadioButtonChange('Email', setFieldValue)}
/>
Email
</label>
<label>
<input
name="preferredContact" type="radio" value="Phone"
checked={this.state.preferredContact == 'Phone'}
onChange={() => this.handleRadioButtonChange('Phone', setFieldValue)}
/>
Phone
</label>
And here's the callback function when radio button get changed. if we are using Formik, setFieldValue is the way to go.
handleRadioButtonChange(value, setFieldValue) {
this.setState({'preferredContact': value});
setFieldValue('preferredContact', value);
}
email: Yup.string()
.when(['showEmail', 'anotherField'], {
is: (showEmail, anotherField) => {
return (showEmail && anotherField);
},
then: Yup.string().required('Must enter email address')
}),
it works for me very well :
Yup.object().shape({
voyageStartDate:Yup.date(),
voyageEndDate:Yup.date()
.when(
'voyageStartDate',
(voyageStartDate, schema) => (moment(voyageStartDate).isValid() ? schema.min(voyageStartDate) : schema),
),
})
I use yup with vee-validate
vee-validate
here is the sample code from project
const schema = yup.object({
first_name: yup.string().required().max(45).label('Name'),
last_name: yup.string().required().max(45).label('Last name'),
email: yup.string().email().required().max(255).label('Email'),
self_user: yup.boolean(),
company_id: yup.number()
.when('self_user', {
is: false,
then: yup.number().required()
})
})
const { validate, resetForm } = useForm({
validationSchema: schema,
initialValues: {
self_user: true
}
})
const {
value: self_user
} = useField('self_user')
const handleSelfUserChange = () => {
self_user.value = !self_user.value
}

Categories