Skip to main content

DatePicker

The DatePicker component is used to select a date value. At mobile viewports, the browsers native input is used.

Import

// with @dhl-official/react-library:
import { DhlDatePicker } from "@dhl-official/react-library"
// with @dhl-official/ui-libraries/react-library:
import { DhlDatePicker } from "@dhl-official/ui-libraries/react-library"
info

Please ensure all date values are passed in the international ISO-8601 format: YYYY-MM-DD.

Accessibility​

The DatePicker is built with consideration for accessibility standards and keyboard support.

Choose Date Button​

  • Space, Enter : Toggles DatePicker visibility and moves focus to month and year text.

DatePicker Dialog​

  • Esc : Close DatePicker dialog and move focus to "choose date" button.
  • Tab : Move focus to next focusable element.
  • Shift + Tab : Move focus to previous focusable element
note

The calendar used inside DatePicker uses role="grid" meaning only one 'day button' is in the tab sequence at any one time.

DatePicker Dialog: Calendar Grid​

  • Space, Enter : Selects a date, closes the dialog, and moves focus back to the “Choose Date” button.
    • Additionally updates the value of the Duet Date Picker input with the selected date, and adds selected date to “Choose Date” button label.
  • Arrow up : Moves focus to the same day of the previous week.
  • Arrow down : Moves focus to the same day of the next week.
  • Arrow right : Moves focus to the next day.
  • Arrow left : Moves focus to the previous day.
  • Home : Moves focus to the first day (e.g Monday) of the current week.
  • End : Moves focus to the last day (e.g. Sunday) of the current week.
  • Page Up : Changes the grid of dates to the previous month and sets focus on the same day of the same week.
  • Shift + Page Up: Changes the grid of dates to the previous year and sets focus on the same day of the same week.
  • Page Down : Changes the grid of dates to the next month and sets focus on the same day of the same week.
  • Shift + Page Down : Changes the grid of dates to the next year and sets focus on the same day of the same week.
caution

Please ensure the prop localeLabels is supplied and matches the locale language. This ensures screen-reader users can leverage voice-over technology in the context of DatePicker.

Using Events​

The DatePicker can be used with DOM events and listeners. Additionally, custom events are available (see Events table below) which include a custom detail object. This object contains fields designed to increase ease of use. The example below demonstrates how the detail object can be used to retrieve the value as a date through the valueAsDate field.

// Select the DatePicker component
const date = document.querySelector("duil311-dhl-date-picker");

// Listen for when date is selected
date.addEventListener("dhlChange", function (e) {
console.log("selected date", e.detail.valueAsDate);
});

With an output:

Tue Feb 14 2023 00:00:00 GMT+0000 (Greenwich Mean Time)

Using Custom Formats​

By default, the DatePicker uses the yyyy-mm-dd (DE locale) format.

To use a different format, the prop dateAdapter is available for use. The example below demonstrates how the prop dateAdapter can be used with a en-GB locale format DD-MM-YYYY.

const picker = document.querySelector("duil311-dhl-date-picker");
const DATE_FORMAT = /^(\d{1,2})\.(\d{1,2})\.(\d{4})$/;

picker.dateAdapter = {
parse(value = "", createDate) {
const matches = value.match(DATE_FORMAT);

if (matches) {
return createDate(matches[3], matches[2], matches[1]);
}
},
format(date) {
const day = date.getDate().toString().padStart(2, "0"); // Get the day of the month as a two-digit string
const month = (date.getMonth() + 1).toString().padStart(2, "0"); // Get the month as a two-digit string
const year = date.getFullYear().toString(); // Get the year as a string
return `${day}/${month}/${year}`;
},
};

Code​

<DhlDatePicker></DhlDatePicker>

Interactive Demo​

Migrating from DUIL 1.0​

  • Aria labels now passed through required localeLabels prop - please see documentation for all available aria fields found within this object.
  • Rename firstDayOfTheWeek to firstDayOfWeek
  • Remove date
  • Remove externalValidation
  • Rename minDate to min
  • Rename maxDate to max
  • Remove isBlock
  • Remove variant
  • Remove displayFormat see new prop dateAdapter for new implementation
  • Add dataAriaLabelledby
  • Add inputLabel
  • Add isDisabled
  • Add role
  • Add direction
  • Add dateAdapter
  • Add isDateDisabled
  • Add dhlChange
  • AdddhlBlur
  • Add new event dhlChange
  • Add new event dhlFocus
  • Add new event dhlOpen
  • Add new event dhlClose
  • Add public method setFocus
  • Add public method show
  • Add public method hide

Readme​

Usage​

Dhl-date-picker​

Snippets of code in HTML and JavaScript to show some of the use cases for the component. The code is not meant to be executed, but to be used as a reference for the usage of the component.
Angular, React and Vue usages are not included in this documentation, but can be easily derived from the html and javascript code.

default usage​

<form novalidate>
<duil311-dhl-grid-container columns="2">
<duil311-dhl-grid-cell span-columns="2">
<duil311-dhl-date-picker
name="date-picker-1"
input-label="Example text"
></duil311-dhl-date-picker>
</duil311-dhl-grid-cell>
<duil311-dhl-grid-cell>
<duil311-dhl-button
type="reset"
variant="text"
>RESET</duil311-dhl-button
>
<duil311-dhl-button
type="submit"
variant="primary"
>SUBMIT</duil311-dhl-button
>
</duil311-dhl-grid-cell>
</duil311-dhl-grid-container>
</form>

<script type="module">
const form = document.querySelector("form");
const datePicker = document.querySelector("duil311-dhl-date-picker");
form.addEventListener("submit", async (e) => {
e.preventDefault();
console.log("datePicker - isValid", await datePicker.checkValidity());
console.log(Object.fromEntries(new FormData(form)));
return await e.target.checkValidity();
});
</script>

usage with validation (required) and (browser) default validation message​

<form novalidate>
<duil311-dhl-grid-container columns="2">
<duil311-dhl-grid-cell span-columns="2">
<duil311-dhl-date-picker
required
name="date-picker-1"
input-label="Example text"
></duil311-dhl-date-picker>
</duil311-dhl-grid-cell>
<duil311-dhl-grid-cell>
<duil311-dhl-button
type="reset"
variant="text"
>RESET</duil311-dhl-button
>
<duil311-dhl-button
type="submit"
variant="primary"
>SUBMIT</duil311-dhl-button
>
</duil311-dhl-grid-cell>
</duil311-dhl-grid-container>
</form>

<script type="module">
const form = document.querySelector("form");
const datePicker = document.querySelector("duil311-dhl-date-picker");
form.addEventListener("submit", async (e) => {
e.preventDefault();
console.log("datePicker - isValid", await datePicker.checkValidity());
console.log(Object.fromEntries(new FormData(form)));
return await e.target.checkValidity();
});
</script>

usage with validation (required) and custom validation message​

<form novalidate>
<duil311-dhl-grid-container columns="2">
<duil311-dhl-grid-cell span-columns="2">
<duil311-dhl-date-picker
name="date-picker-1"
input-label="Example text"
required
>
</duil311-dhl-date-picker>
</duil311-dhl-grid-cell>

<duil311-dhl-grid-cell>
<duil311-dhl-button
type="reset"
variant="text"
>RESET</duil311-dhl-button
>
<duil311-dhl-button
type="submit"
variant="primary"
>SUBMIT</duil311-dhl-button
>
</duil311-dhl-grid-cell>
</duil311-dhl-grid-container>
</form>

<script type="module">
const form = document.querySelector("form");
const datePicker = document.querySelector("duil311-dhl-date-picker");
const validationMessageInvalid = "This field is required";
const validationMessageValid = "This field is valid";

form.addEventListener("submit", async (e) => {
e.preventDefault();
const isValid = await datePicker.checkValidity();
if (isValid) {
datePicker.validation = {
type: "valid",
message: validationMessageValid,
};
} else {
datePicker.validation = {
type: "invalid",
message: validationMessageInvalid,
};
}
return await form.checkValidity();
});
</script>

usage with custom events​

<form novalidate>
<duil311-dhl-grid-container columns="2">
<duil311-dhl-grid-cell span-columns="2">
<duil311-dhl-date-picker
required
name="date-picker-1"
input-label="Example text"
>
</duil311-dhl-date-picker>
</duil311-dhl-grid-cell>
<duil311-dhl-grid-cell>
<duil311-dhl-button
type="reset"
variant="text"
>RESET</duil311-dhl-button
>
<duil311-dhl-button
type="submit"
variant="primary"
>SUBMIT</duil311-dhl-button
>
</duil311-dhl-grid-cell>
</duil311-dhl-grid-container>
</form>

<script type="module">
const form = document.querySelector("form");
const datePicker = document.querySelector("duil311-dhl-date-picker");

const validationMessageInvalid = "This field is required";
const validationMessageValid = "This field valid";
const text = document.querySelector("duil311-dhl-text");

const isElementValid = async () => {
const isValid = await datePicker.checkValidity();
if (isValid) {
datePicker.validation = {
type: "valid",
message: validationMessageValid,
};
} else {
datePicker.validation = {
type: "invalid",
message: validationMessageInvalid,
};
}
return isValid;
};

form.addEventListener("submit", async (e) => {
e.preventDefault();
await isElementValid();
});

datePicker.addEventListener("dhlChange", async (e) => await isElementValid());
datePicker.addEventListener("dhlBlur", async (e) => isElementValid());
</script>

usage with a custom date format​

The optional format prop changes how the date is shown in and typed into the input. It is display only: the value prop and the emitted dhlChange value (and valueAsDate) always stay in ISO YYYY-MM-DD, so min/max, form submission, and any code reading the value are unaffected.

Supported formats: YYYY-MM-DD, DD/MM/YYYY, MM/DD/YYYY, DD.MM.YYYY, DD-MM-YYYY, YYYY/MM/DD, DD MMM YYYY.

<duil311-dhl-date-picker
name="date-picker-1"
input-label="Example text"
value="2024-12-31"
format="DD/MM/YYYY"
></duil311-dhl-date-picker>
<!-- the input shows 31/12/2024; datePicker.value is still "2024-12-31" -->

DD MMM YYYY shows the month as a three letter abbreviation instead of digits. The month names come from the English locale (Jan, Feb, ... Dec) and are accepted in any casing when typed.

<duil311-dhl-date-picker
name="date-picker-1"
input-label="Example text"
value="2024-12-31"
format="DD MMM YYYY"
></duil311-dhl-date-picker>
<!-- the input shows 31 Dec 2024; datePicker.value is still "2024-12-31" -->

usage with the format derived from the locale​

Without a format, the format follows the locale prop: the order and separator of the locale's numeric date are mapped to the closest supported format. An explicit format always wins, and without a locale the format stays YYYY-MM-DD.

localeformat
en-USMM/DD/YYYY
en-GB, fr-FRDD/MM/YYYY
he-IL, de-DE, pl-PLDD.MM.YYYY
nl-NLDD-MM-YYYY
ja-JP, zh-CNYYYY/MM/DD
sv-SEYYYY-MM-DD
<duil311-dhl-date-picker
name="date-picker-1"
input-label="Example text"
value="2024-12-31"
locale="he-IL"
></duil311-dhl-date-picker>
<!-- the input shows 31.12.2024; datePicker.value is still "2024-12-31" -->

usage with calendar only (no manual input)​

disable-manual-input blocks typing and pasting, so a date can only be picked from the calendar. Backspace or Delete clears the date, and required keeps working.

<duil311-dhl-date-picker
name="date-picker-1"
input-label="Example text"
disable-manual-input
></duil311-dhl-date-picker>

usage with always open​

<duil311-dhl-date-picker
name="date-picker-1"
input-label="Example text"
always-open
></duil311-dhl-date-picker>

usage with disabling specific dates​

<form novalidate>
<duil311-dhl-grid-container columns="2">
<duil311-dhl-grid-cell span-columns="2">
<duil311-dhl-date-picker
name="date-picker-123"
input-label="Example text"
></duil311-dhl-date-picker>
</duil311-dhl-grid-cell>
</duil311-dhl-grid-container>
</form>

<script type="module">
const datePicker = document.querySelector("duil311-dhl-date-picker");

const disabledDates = [
1, 2, 10, 11, 12, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
];

datePicker.isDateDisabled = (date) => {
const day = date.getDate();
return disabledDates.includes(day);
};
</script>

full screen calendar on mobile​

The optional is-fullscreen-on-mobile prop swaps the popover calendar for a full screen one below the 768px breakpoint: the calendar fills the viewport, its header and its Cancel/Select footer stay put, and the months scroll vertically without paging. Above the breakpoint nothing changes, so the same markup serves a phone and a desktop.

Tapping a date marks it and enables Select, which confirms it. Cancel, the close button and Escape all dismiss the calendar and leave the value as it was before it was opened, so nothing is kept unless it is confirmed.

While the full screen calendar is in use the field is a text input rather than type="date", so the platform's own picker is not summoned on top of it, and the calendar button is shown on mobile as the calendar's trigger.

<duil311-dhl-date-picker
name="delivery-date"
input-label="Delivery date"
is-fullscreen-on-mobile="true"
></duil311-dhl-date-picker>

The wording of the header and the two buttons comes from localeLabels:

<duil311-dhl-date-picker
name="delivery-date"
is-fullscreen-on-mobile="true"
></duil311-dhl-date-picker>

<script type="module">
document.querySelector("duil311-dhl-date-picker").localeLabels = {
...
calendarTitle: "Lieferdatum",
closeLabel: "Kalender schließen",
cancelLabel: "Abbrechen",
selectLabel: "Auswählen",
};
</script>

Properties​

PropertyAttributeDescriptionTypeDefault
alwaysOpenalways-openAn optional prop to keep the date picker always open.booleanfalse
dataAriaLabelledbydata-aria-labelledbyAn optional ariaLabelledby prop that establishes the relationships between objects and their label(s), and its value should be one or more element IDs, which refer to elements that have the text needed for labelling. List multiple element IDs in a space delimited fashion.stringundefined
dataIddata-idAn optional prop. Gives a valid HTML ID attribute value for the component.string`duil311-dhl-date-picker-${getRandomString()}`
dataMaskPiidata-mask-piiAn optional prop to mask sensitive data in session-replay tools. When true, sets data-di-mask on the inner element.booleanundefined
dataRoledata-roleAn optional prop defining the role attribute of the component.stringundefined
dataTestiddata-testidAn optional prop. The test id attached to the component as a data-testid attribute.stringundefined
directiondirectionAn optional prop which forces the opening direction of the calendar modal to be always left or right. This setting can be useful when the input is smaller than the opening date picker would be as by default the picker always opens towards right."left" | "right"DHL_DATE_PICKER.DIRECTION.RIGHT
disableManualInputdisable-manual-inputAn optional prop to block typing into the input, so a date can only be picked from the calendar.booleanfalse
firstDayOfWeekfirst-day-of-weekAn optional prop used to set the first day of the week. Sunday is treated as index 0. The default is 1; Monday.DaysOfWeek.Friday | DaysOfWeek.Monday | DaysOfWeek.Saturday | DaysOfWeek.Sunday | DaysOfWeek.Thursday | DaysOfWeek.Tuesday | DaysOfWeek.WednesdayDaysOfWeek.Monday
formatformatAn optional prop to set the date format shown in and typed into the input. This is display only: the value prop and every emitted value (dhlChange, valueAsDate) always stay ISO YYYY-MM-DD. When not set, the format is derived from locale (e.g. de-DE shows DD.MM.YYYY), and without a locale it is YYYY-MM-DD."DD MMM YYYY" | "DD-MM-YYYY" | "DD.MM.YYYY" | "DD/MM/YYYY" | "MM/DD/YYYY" | "YYYY-MM-DD" | "YYYY/MM/DD"undefined
inputLabelinput-labelAn optional prop used to set the label inside the DatePicker inputfield. This is also used as an aria label for the input. Please use the dataAriaLabelledby prop with an associated label should you choose to not supply this prop.stringundefined
isDateDisabledis-date-disabledAn optional prop used to set which days are disabled and therefore disallowed. For example, this can be used to disallow selection of weekends.(date: Date) => boolean() => false
isDisabledis-disabledAn optional flag to define if the component is disabled.booleanfalse
isFullscreenOnMobileis-fullscreen-on-mobileAn optional prop that turns the calendar into a full screen, vertically scrollable one on narrow screens. Above the 768px breakpoint the picker is unaffected and keeps rendering the popover calendar, so the same markup serves a phone and a desktop. Has no effect together with alwaysOpen, which is an inline calendar with nothing to dismiss.booleanfalse
localelocaleAn optional prop used to change the calendar locale - following the the BCP 47 standard Note that localeLabels is provided by default in English en-GB. If you want to use a different locale, you will need to provide your own localeLabels prop.string"default"
localeLabelslocale-labelsAn optional prop used to set the DatePicker localization labels and aria labels. Defaults to English en-GB.{ buttonLabel: string; selectedDateMessage: string; selectedDateRangeMessage: string; prevMonthLabel: string; nextMonthLabel: string; prevYearLabel: string; nextYearLabel: string; monthSelectLabel: string; yearSelectLabel: string; invalidDateErrorMessage: string; invalidDateRangeErrorMessage: string; closeLabel?: string; cancelLabel?: string; selectLabel?: string; calendarTitle?: string; calendarRangeTitle?: string; }defaultLocaleLabels
maxmaxAn optional prop used to set the maximum date allowed to be picked. Must be in IS0-8601 format: YYYY-MM-DD. This setting can be used alone or together with the min property.Date | stringundefined
minminAn optional prop used to set the minimum date allowed to be picked. Must be in IS0-8601 format: YYYY-MM-DD. This setting can be used alone or together with the max property.Date | stringundefined
namenameAn optional value to be set to the element HTML name attribute. It takes any valid value that can be used for the name attribute of an HTMLInputElement.string`${getRandomString()}-date`
openOnFocusopen-on-focusAn optional prop when set to true, the date picker option list will be shown on input focus.booleanfalse
requiredrequiredAn optional prop to flag the component as required within a form context.booleanfalse
showCalendarHeadershow-calendar-headerAn optional prop to hide the full screen calendar's header, which carries the close button and the year.booleantrue
showCtashow-ctaAn optional prop that puts Cancel and Select buttons under the calendar. A tapped date is then only applied once Select is pressed, and Cancel discards it, leaving the value as it was. Has no effect together with alwaysOpen, which is an inline calendar the buttons have nothing to close.booleanfalse
showNextMonthshow-next-monthAn optional prop to hide the step-forward-a-month control.booleantrue
showNextYearshow-next-yearAn optional prop to hide the step-forward-a-year control.booleantrue
showPreviousMonthshow-previous-monthAn optional prop to hide the step-back-a-month control.booleantrue
showPreviousYearshow-previous-yearAn optional prop to hide the step-back-a-year control.booleantrue
sizesizeAn optional size prop for the component."md" | "sm"Sizes.MD
validationvalidationAn optional DatePicker validation prop Required Fields type{ type: "warning" | "valid" | "invalid" | "note"; message?: string; }undefined
valuevalueAn optional DatePicker value. Must be in IS0-8601 format: YYYY-MM-DD.stringundefined

Events​

EventDescriptionType
dhlBlurEvent emitted when the component loses focus.CustomEvent<{ component: string; componentId: string; }>
dhlChangeEvent emitted when the component changes value.CustomEvent<{ component: string; componentId: string; value: string; valueAsDate: Date; short: string; long: string; }>
dhlCloseEvent emitted when the component is closed.CustomEvent<{ component: string; componentId: string; }>
dhlFocusEvent emitted when the component is focused.CustomEvent<{ component: string; componentId: string; }>
dhlOpenEvent emitted when the component is opened.CustomEvent<{ component: string; componentId: string; }>

Methods​

checkValidity() => Promise<boolean>​

Checks the validity of the component.

Returns​

Type: Promise<boolean>

A promise that resolves to true if the component is valid, otherwise false.

getInputElement() => Promise<HTMLInputElement>​

Retrieves the input element asynchronously.

Returns​

Type: Promise<HTMLInputElement>

A promise that resolves to the input element.

getValidationMessage() => Promise<string>​

Retrieves the validation message for the component.

Returns​

Type: Promise<string>

A promise that resolves to a string representing the validation message.

hide(moveFocusToButton?: boolean, focusInput?: boolean) => Promise<void>​

Hide the calendar modal. Set moveFocusToButton to false to prevent focus returning to the date picker's button. Default is true.

Parameters​

NameTypeDescription
moveFocusToButtonboolean
focusInputboolean

Returns​

Type: Promise<void>

reportValidity() => Promise<boolean>​

Reports the validity of the component.

Returns​

Type: Promise<boolean>

A promise that resolves to a boolean indicating whether the component is valid.

setFocus() => Promise<void>​

Sets focus on the date picker's input. Use this method instead of the global focus().

Returns​

Type: Promise<void>

setValidity(validity: ValidityState, validationMessage?: string) => Promise<void>​

Sets the validity state of the component.

Parameters​

NameTypeDescription
validityValidityState- The validity state to set.
validationMessagestring- An optional validation message to set.

Returns​

Type: Promise<void>

A Promise that resolves when the validity state is set.

show() => Promise<void>​

Show the calendar modal, moving focus to the calendar inside.

Returns​

Type: Promise<void>

willValidate() => Promise<boolean>​

Returns a promise that resolves to true if the element will successfully validate, or false otherwise.

Returns​

Type: Promise<boolean>

A promise that resolves to a boolean value indicating whether the element will validate.

Dependencies​

Depends on​

Graph​


Built by DHL User Interface Library Team!