Reputation: 1708
I have a form that creates an event using Formik library. I need to check to see if the start date overlaps the end date and vice-versa. I have two date pickers that choose the date and time. How can I use Yup to validate this and show an error message if they do overlap?
Thanks for the help in advance
const validationSchema = Yup.object().shape({
eventName: Yup.string()
.min(1, "Must have a character")
.max(10, "Must be shorter than 255")
.required("Must enter an event name"),
email: Yup.string()
.email("Must be a valid email address")
.max(255, "Must be shorter than 255")
.required("Must enter an email"),
eventStartDate: Yup.date()
.required("Must enter start date"),
eventEndDate: Yup.date()
.required("Must enter end date")
})
var defaultValue = new Date().toDateString
export default function EventForm(){
return (
<Formik
initialValues={{eventName: "", email: "", }}
validationSchema={validationSchema}
onSubmit={(values, {setSubmitting, resetForm}) => {
setTimeout(() => {
}}
>
{ ({
values,
errors,
touched,
handleChange,
handleBlur,
handleSubmit,
isSubmitting
}) => (
<form onSubmit={handleSubmit}>
<div className="input-row">
<TextField
id="eventName"
label="Event Name"
margin="normal"
variant="filled"
onChange={handleChange}
onBlur={handleBlur}
value={values.eventName}
className={touched.eventName && errors.eventName ? "has-error" : null}
/>
<Error touched={touched.eventName} message={errors.eventName}/>
</div>
<div className="dateHolder">
<div className="startDate">
<TextField
id="eventStartDate"
label="Event Start Date"
type="datetime-local"
InputLabelProps={{
shrink: true
}}
format="yyy-dd-mm HH:MM:ss"
onChange={handleChange}
onBlur={handleBlur}
value={values.eventStartDate}
/>
<Error touched={touched.eventStartDate} message={errors.eventStartDate}/>
</div>
<div className="endDate">
<TextField
id="eventEndDate"
label="Event End Date"
type="datetime-local"
InputLabelProps={{
shrink: true
}}
format="yyy-dd-mm HH:MM:ss"
onChange={handleChange}
onBlur={handleBlur}
value={values.eventEndDate}
/>
<Error touched={touched.eventEndDate} message={errors.eventEndDate}/>
</div>
</div>
<div className="input-row">
<button type="submit" disabled={isSubmitting} >
Submit
</button>
</div>
</form>
)}
</Formik>
)
}
Upvotes: 39
Views: 51465
Reputation: 3
@seem7teen, thank you for your code, it helped me build what I needed. Just to note as @Roman pointed out it wont work on html type="time". However, I modified it a bit and shortened it for my needs with html time input fields.
const timeValidationSchema = Yup.object({
startTime: Yup.mixed().test({
name: 'validator-time',
test: function (startTime) {
const { from } = this
const formValue = from[0].value
const endTime = formValue.endTime
if (endTime && startTime >= endTime) {
return this.createError({
message: 'Start time cannot occur before end time.',
path: 'startTime',
})
} else {
return true;
}
}
}).required('A start time is required.'),
endTime: Yup.mixed().test({
name: 'validator-time',
test: function (endTime) {
if (endTime) {
return true
}
}
}).required('An end time is required.'),
})
Upvotes: 0
Reputation: 273
This is what I use to validate time/date assuming I have two input fields start and end time. It's a lengthy solution but it takes care of all the scenarios. I use date-fns for this example but it can be replaced with other libraries
import { isAfter, isBefore, isEqual, isValid } from 'date-fns';
const validateTime: Schema = yup
.object({
startTime: yup.mixed().test({
name: 'validator-time',
test: function (startTime: Date | null) {
if (startTime) {
if (isValid(startTime)) {
const { from } = this;
const formValue = from[0].value;
const endTime: Date | null = formValue.endTime;
if (isValid(endTime) && endTime) {
if (isEqual(endTime, startTime) || isAfter(startTime, endTime) || isBefore(endTime, startTime)) {
return this.createError({
message: "start time should be before end time" ,
path: 'startTime',
});
}
return true;
}
return true;
}
return this.createError({
message: 'Invalid Time',
path: 'startTime',
});
}
return this.createError({
message:"Required field",
path: 'startTime',
});
},
}),
endTime: yup.mixed().test({
name: 'validator-time',
test: function (endTime: Date | null) {
if (endTime) {
if (isValid(endTime)) {
const { from } = this;
const formValue = from[0].value;
const startTime: Date | null = formValue.startTime;
if (isValid(startTime) && startTime) {
if (isEqual(endTime, startTime) || isAfter(startTime, endTime) || isBefore(endTime, startTime)) {
return this.createError({
message: 'end time cannot be before start time',
path: 'endTime',
});
}
return true;
}
return true;
}
return this.createError({
message: 'invalid time',
path: 'endTime',
});
}
return this.createError({
message:'required field',
path: 'endTime',
});
},
}),
})
.required();
Upvotes: 1
Reputation: 250
For date it's maybe ok, but not for time during one day.
Like that: from = 12:00, to = 15:00 - it cant pass validation with yup. Always error.
Upvotes: 0
Reputation: 1
eventStartDate: Yup.date().default(() => new Date()),
startDate: Yup.date()
.when(
'eventStartDate',
(eventStartDate, Yup) => eventStartDate && Yup.min(eventStartDate, 'Date can select from Current Date')
)
.required('Start-date is Required'),
Upvotes: 0
Reputation: 871
You can use when
condition:
eventStartDate: yup.date().default(() => new Date()),
eventEndDate: yup
.date()
.when(
"eventStartDate",
(eventStartDate, schema) => eventStartDate && schema.min(eventStartDate))
Upvotes: 30
Reputation: 655
Nader's answer worked for me. But I had some additional validation condition that if a checkbox is checked then validate the date start date before the end date. So, I came up with this code. leaving in case someone needs this in future
Yup.object.shape({
ServiceCheck: Yup.boolean().default(false),
StartDateTime: Yup.date().when('ServiceCheck', {
is: (ServiceCheck=> {
return (!!ServiceCheck) ? true : false;
}),
then: Yup.date().required('Start Date/Time is required')
}).nullable(),
EndDateTime: Yup.date().when('ServiceCheck', {
is: (ServiceCheck=> {
return (!!ServiceCheck) ? true : false;
}),
then: Yup.date().min(Yup.ref('StartDateTime'),
"End date can't be before Start date").required('End Date/Time is required')
}).nullable(),
})
without condition
Yup.object().shape({
StartDate: Yup.date(),
EndDate: Yup.date().min(
Yup.ref('StartDate'),
"End date can't be before Start date"
)
});
Upvotes: 9
Reputation: 736
use ref
it works just fine
yup.object().shape({
startDate: date(),
endDate: date().min(
yup.ref('startDate'),
"end date can't be before start date"
)
});
Upvotes: 69