Integration into forms#
Pickers are quite complex controls, where date can be submitted from different places, so we can't provide events as arguments in
an onChange
callback. So you need to set up form field manually, based on the onChange
param.
Also we are providing prop onError
that is ready-to-use for validation. This callback fires once
the selected value by user is becomes invalid based on the passed validation props.
It returns you a reason why error was occured so it is possible to render custom error message.
Validation props#
There are different validation props for each component. The reason
prop returned by onError
will basically return you a name of prop that match an error (e.g. minDate
or shouldDisableDate
)
For reference, here is the list of validation props for DatePicker in the order of applying:
shouldDisableDate
disableFuture
disablePast
minDate
maxDate
Formik#
Here is an example how to use onError
callback in integration with formik
/* eslint-disable no-console */
// @ts-nocheck
import * as React from "react";
import Grid from "@material-ui/core/Grid";
import TextField from "@material-ui/core/TextField";
import { Formik, Form, Field, FieldProps } from "formik";
import { format, isWeekend, isWednesday } from "date-fns";
import { DatePicker, DatePickerProps } from "@material-ui/pickers";
interface DatePickerFieldProps extends FieldProps, DatePickerProps {
getShouldDisableDateError: (date: Date) => string;
}
function DatePickerField({
form,
field: { value, name },
maxDate = new Date("2099-12-31"),
minDate = new Date("1900-01-01"),
getShouldDisableDateError,
...other
}: DatePickerFieldProps) {
const currentError = form.errors[name];
const toShowError = Boolean(currentError && form.touched[name]);
return (
<DatePicker
clearable
minDate={minDate}
maxDate={maxDate}
value={value}
onError={(reason, value) => {
switch (reason) {
case "invalidDate":
form.setFieldError(name, "Invalid date format");
break;
case "disablePast":
form.setFieldError(name, "Values in the past are not allowed");
break;
case "maxDate":
form.setFieldError(name, `Date should not be after ${format(maxDate, "P")}`);
break;
case "minDate":
form.setFieldError(name, `Date should not be before ${format(minDate, "P")}`);
break;
case "shouldDisableDate":
// shouldDisableDate returned true, render custom message according to the `shouldDisableDate` logic
form.setFieldError(name, getShouldDisableDateError(value));
break;
default:
form.setErrors({
...form.errors,
[name]: undefined,
});
}
}}
// Make sure that your 3d param is set to `false` on order to not clear errors
onChange={(date) => form.setFieldValue(name, date, false)}
renderInput={(props) => (
<TextField
{...props}
name={name}
error={toShowError}
helperText={toShowError ? currentError ?? props.helperText : undefined}
// Make sure that your 3d param is set to `false` on order to not clear errors
onBlur={() => form.setFieldTouched(name, true, false)}
/>
)}
{...other}
/>
);
}
function validateDatePickerValue(date: Date) {
if (isWeekend(date)) {
return "Weekends are not allowed";
}
if (isWednesday(date)) {
return "Wednesdays are not allowed";
}
return null;
}
export default function FormikExample() {
return (
<Formik onSubmit={console.log} initialValues={{ date: new Date() }}>
{({ values, errors }) => (
<Form>
<Grid container>
<Grid item container justifyContent="center" xs={12}>
<Field
name="date"
disablePast
component={DatePickerField}
shouldDisableDate={(date: Date) => validateDatePickerValue(date) !== null}
getShouldDisableDateError={validateDatePickerValue}
/>
</Grid>
<Grid item xs={12} sm={12} style={{ margin: "24px" }}>
<pre>
<code>{JSON.stringify({ errors, values }, null, 2)}</code>
</pre>
</Grid>
</Grid>
</Form>
)}
</Formik>
);
}
Formik with validation schema#
This example shows how to use formik and custom validation schema. When using this approach please make sure that
your validation schema logic covers all the validation props that are passed to the DatePicker
.
/* eslint-disable no-console */
// @ts-nocheck
import React from "react";
import Grid from "@material-ui/core/Grid";
import TextField from "@material-ui/core/TextField";
import { date, object } from "yup";
import { Formik, Form, Field, FieldProps } from "formik";
import { DatePicker, BaseDatePickerProps } from "@material-ui/pickers";
interface DatePickerFieldProps extends FieldProps, BaseDatePickerProps {
getShouldDisableDateError: (date: Date) => string;
}
function DatePickerField(props: DatePickerFieldProps) {
const {
field,
form,
getShouldDisableDateError,
maxDate = new Date("2099-12-31"),
minDate = new Date("1900-01-01"),
...other
} = props;
const currentError = form.errors[field.name];
return (
<DatePicker
clearable
minDate={minDate}
maxDate={maxDate}
value={field.value}
// Make sure that your 3d param is set to `true` in order to run validation
onChange={(newValue) => form.setFieldValue(field.name, newValue, true)}
renderInput={(inputProps) => (
<TextField
name={field.name}
{...inputProps}
error={Boolean(currentError)}
helperText={currentError ?? inputProps.helperText}
// Make sure that your 3d param is set to `true` in order to run validation
onBlur={() => form.setFieldTouched(field.name, true, true)}
/>
)}
{...other}
/>
);
}
const schema = object({
date: date().required().min(new Date()).max(new Date("2100-10-10")),
});
export default function FormikValidationSchemaExample() {
return (
<Formik validationSchema={schema} onSubmit={console.log} initialValues={{ date: new Date() }}>
{({ values, errors }) => (
<Form>
<Grid container>
<Grid item container justifyContent="center" xs={12}>
<Field name="date" disablePast component={DatePickerField} />
</Grid>
<Grid item xs={12} sm={12} style={{ margin: "24px" }}>
<pre>
<code>{JSON.stringify({ errors, values }, null, 2)}</code>
</pre>
</Grid>
</Grid>
</Form>
)}
</Formik>
);
}