Agriculture Design System
Beta
Design System for the Export Service

Date range picker

The Date range picker component allows users to select a range of dates via a calendar or text input.

View in FigmaView in StorybookView in Github
import { ... } from '@ag.ds-next/react/date-range-picker';
Open in Playroom, opens in a new tab
() => {
	const [value, setValue] = React.useState({ from: undefined, to: undefined });
	return (
		<DateRangePicker
			value={value}
			onChange={setValue}
			onFromInputChange={(from) => setValue({ ...value, from })}
			onToInputChange={(to) => setValue({ ...value, to })}
		/>
	);
};

Date range picker is a controlled component which means consumers of this component need to manage the state of this component by using the value, onChange, onFromInputChange and onToInputChange props.

For an example of using this component in a form built with react-hook-form and yup, please see the Single-page form template.

Do

  • always display the date format above the text input
  • prefer Australian date format 'dd/mm/yyyy'
  • allow users to navigate dates via the calendar
  • ensure users can enter dates via the calendar or text input
  • make sure error messages are specific about what input is required.

Don’t

  • hide the date format
  • use international date formats such as USA mm/dd/yyyy
  • hijack built-in keyboard navigation behaviour.

Tracking the input value

The Date range picker component has two methods of input:

  1. Typing in a date string (dd/mm/yyyy) via the text inputs
  2. Selecting a date via the calendar widgets

Since we do not have any sort of input masking due to accessibility and user experience concerns, it is possible for the user to enter invalid values via the text inputs.

In this case, you can use the onFromInputChange and onToInputChange props to keep track of the user’s input. The value.from and value.to props can also be set to a string, which represents the value of the text inputs.

() => {
	// Set the value to a value that the user might think is valid
	const [value, setValue] = React.useState({
		from: '30/1o/2020',
		to: '31/1o/2020',
	});

	// This logic is for documentation purposes only. This should be done with `yup` or `zod`.
	// See Single-page form template
	const isInvalid = React.useCallback((value) => {
		if (typeof value === 'undefined' || value == '') return false;
		if (value instanceof Date && !isNaN(value.getTime())) return false;
		return true;
	}, []);

	const fromInvalid = isInvalid(value.from);
	const toInvalid = isInvalid(value.to);

	const onFromInputChange = (fromValue) => {
		console.log('onFromInputChange', fromValue);
		setValue({ ...value, from: fromValue });
	};

	const onToInputChange = (toValue) => {
		console.log('onToInputChange', toValue);
		setValue({ ...value, to: toValue });
	};

	const onChange = (dateRange) => {
		console.log('onChange', dateRange);
		setValue(dateRange);
	};

	return (
		<DateRangePicker
			value={value}
			onChange={onChange}
			onFromInputChange={onFromInputChange}
			onToInputChange={onToInputChange}
			fromInvalid={fromInvalid}
			toInvalid={toInvalid}
			message={
				fromInvalid && toInvalid
					? 'Enter valid start and end dates'
					: fromInvalid
					? 'Enter a valid start date'
					: toInvalid
					? 'Enter a valid end date'
					: undefined
			}
		/>
	);
};

Legend

Use the legend prop to describe the purpose of the group of fields.

() => {
	const [value, setValue] = React.useState({ from: undefined, to: undefined });
	return (
		<DateRangePicker
			value={value}
			onChange={setValue}
			onFromInputChange={(from) => setValue({ ...value, from })}
			onToInputChange={(to) => setValue({ ...value, to })}
			legend="Date period"
		/>
	);
};

Hint

Use the hint prop to provide help that’s relevant to the majority of users, like how their information will be used, or where to find it.

Don’t use long paragraphs and lists in hint text. Screen readers read out the entire text when users interact with the form element. This could frustrate users if the text is long.

Don’t include links within hint text. While screen readers will read out the link text when describing the field, they will not tell users that the text is a link.

() => {
	const [value, setValue] = React.useState({ from: undefined, to: undefined });
	return (
		<DateRangePicker
			value={value}
			onChange={setValue}
			onFromInputChange={(from) => setValue({ ...value, from })}
			onToInputChange={(to) => setValue({ ...value, to })}
			legend="Date period"
			hint="Example hint text"
		/>
	);
};

Invalid

Use the fromInvalid and toInvalid props to indicate if the user input is invalid.

() => {
	const [value, setValue] = React.useState({ from: undefined, to: undefined });
	return (
		<FormStack>
			<DateRangePicker
				value={value}
				onChange={setValue}
				onFromInputChange={(from) => setValue({ ...value, from })}
				onToInputChange={(to) => setValue({ ...value, to })}
				fromInvalid={true}
				toInvalid={true}
				legend="Date period"
				message="Enter valid start and end dates"
			/>
			<DateRangePicker
				value={value}
				onChange={setValue}
				onFromInputChange={(from) => setValue({ ...value, from })}
				onToInputChange={(to) => setValue({ ...value, to })}
				fromInvalid={true}
				toInvalid={false}
				legend="Date period"
				message="Enter a valid start date"
			/>
			<DateRangePicker
				value={value}
				onChange={setValue}
				onFromInputChange={(from) => setValue({ ...value, from })}
				onToInputChange={(to) => setValue({ ...value, to })}
				fromInvalid={false}
				toInvalid={true}
				legend="Date period"
				message="Enter a valid end date"
			/>
		</FormStack>
	);
};

Custom labels

Use the fromLabel and toLabel props to change the field labels.

() => {
	const [value, setValue] = React.useState({ from: undefined, to: undefined });
	return (
		<DateRangePicker
			value={value}
			onChange={setValue}
			onFromInputChange={(from) => setValue({ ...value, from })}
			onToInputChange={(to) => setValue({ ...value, to })}
			fromLabel="From"
			toLabel="To"
		/>
	);
};

Disabled

Disabled input elements are unusable and can not be clicked. This prevents a user from interacting with the input element until another action is complete.

() => {
	const [value, setValue] = React.useState({ from: undefined, to: undefined });
	return (
		<DateRangePicker
			value={value}
			onChange={setValue}
			onFromInputChange={(from) => setValue({ ...value, from })}
			onToInputChange={(to) => setValue({ ...value, to })}
			disabled
		/>
	);
};

Minimum and maximum dates

The minDate property can be used to disable any days before a specific date.

The maxDate property can be used to disable any days after a specific date.

If a valid date is entered using the text input but it falls outside the constrained range, the closest valid date will be used.

() => {
	const [value, setValue] = React.useState({ from: undefined, to: undefined });

	const today = new Date();
	const lastWeek = new Date(today.getTime() - 7 * 24 * 60 * 60 * 1000);
	const nextWeek = new Date(today.getTime() + 7 * 24 * 60 * 60 * 1000);

	return (
		<DateRangePicker
			value={value}
			onChange={setValue}
			onFromInputChange={(from) => setValue({ ...value, from })}
			onToInputChange={(to) => setValue({ ...value, to })}
			minDate={lastWeek}
			maxDate={nextWeek}
		/>
	);
};

Changing the date format

The Date range picker component allows users to enter dates in various formats. To select the 18th February 2023, a user could input '18/02/2023', '18 Feb 2023', '18th February 2023', or any other supported date format.

When a valid date is typed, the date will be formatted using the dateFormat prop, which is set to 'dd/MM/yyyy' by default.

To modify the date format, you can change the dateFormat prop to one of these supported date formats:

  • 'dd/MM/yyyy' (e.g. 18/02/2023)
  • 'dd-MM-yyyy' (e.g. 18-02-2023)
  • 'dd MM yyyy' (e.g. 18 02 2023)
  • 'MM/dd/yyyy' (e.g. 02/18/2023)
  • 'MM-dd-yyyy' (e.g. 02-18-2023)
  • 'MM dd yyyy' (e.g. 02 18 2023)
  • 'do MMMM yyyy' (e.g. 8th February 2023)
  • 'do MMM yyyy' (e.g. 8th Feb 2023)
  • 'MMMM do yyyy' (e.g. February 8th 2023)
  • 'MMM do yyyy' (e.g. Feb 8th 2023)
  • 'd MMMM yyyy' (e.g. 8 February 2023)
  • 'd MMM yyyy' (e.g. 8 Feb 2023)
  • 'MMMM d yyyy' (e.g. February 8 2023)
  • 'MMM d yyyy' (e.g. Feb 8 2023)
  • 'dd MMMM yyyy' (e.g. 08 February 2023)
  • 'dd MMM yyyy' (e.g. 08 Feb 2023)
  • 'MMMM dd yyyy' (e.g. February 08 2023)
  • 'MMM dd yyyy' (e.g. Feb 08 2023)
() => {
	const [value, setValue] = React.useState({ from: undefined, to: undefined });
	return (
		<DateRangePicker
			value={value}
			onChange={setValue}
			onFromInputChange={(from) => setValue({ ...value, from })}
			onToInputChange={(to) => setValue({ ...value, to })}
			dateFormat="d MMM yyyy"
		/>
	);
};

Changing the allowed date formats

By default, all of the supported date formats are allowed, but should you need to restrict the formats that can be parsed and validated, you can pass in an array of date format strings to the allowedDateFormats prop.

Some applications may find it useful to remove US formats, since an invalid AU date could become a valid US date and cause confusion for users.

Date formats are parsed in order from first to last and stops when a valid date is found. The preferred dateFormat always becomes the first to check, even if omitted in allowedDateFormats.

() => {
	// Set the value to a value that the user might think is valid
	const [value, setValue] = React.useState({ from: '', to: '' });

	// This logic is for documentation purposes only. This should be done with `yup` or `zod`.
	// See Single-page form template
	const isInvalid = React.useCallback((value) => {
		if (typeof value === 'undefined' || value == '') return false;
		if (value instanceof Date && !isNaN(value.getTime())) return false;
		return true;
	}, []);

	const fromInvalid = isInvalid(value.from);
	const toInvalid = isInvalid(value.to);

	const onFromInputChange = (fromValue) => {
		console.log('onFromInputChange', fromValue);
		setValue({ ...value, from: fromValue });
	};

	const onToInputChange = (toValue) => {
		console.log('onToInputChange', toValue);
		setValue({ ...value, to: toValue });
	};

	const onChange = (dateRange) => {
		console.log('onChange', dateRange);
		setValue(dateRange);
	};

	return (
		<DateRangePicker
			value={value}
			onChange={onChange}
			onFromInputChange={onFromInputChange}
			onToInputChange={onToInputChange}
			fromInvalid={fromInvalid}
			toInvalid={toInvalid}
			message={
				fromInvalid && toInvalid
					? 'Enter valid start and end dates'
					: fromInvalid
					? 'Enter a valid start date'
					: toInvalid
					? 'Enter a valid end date'
					: undefined
			}
			hint="Only short AU formats allowed"
			allowedDateFormats={['dd MM yyyy', 'dd-MM-yyyy']}
		/>
	);
};
  • Date picker The Date picker component allows users to select a single date via a calendar or text input.