Date picker > Explore replacing Pickaday
Purpose
In #1190 (closed) several items were brought up about the shortcomings of the current Pickaday date picker in regards to its usability for keyboarding and screen readers. As a result, we face the choice between trying to push upstream changes, forking and fixing, finding a new third-party option, or rolling our own. Each comes with a cost, but it's clear that a change has to be made.
We'd do ourselves and our users a disservice if we also didn't consider the need for a date picker to begin with. In his article, Maybe You Don’t Need a Date Picker, Adrian Roselli provides some considerations for other ways to enter dates as well as thinking through a progressively enhanced experience.
Requirements
- Min/Max dates: disable selection of dates outside of desired range.
- Focus state: links, buttons, and controls have a visible focus state.
- Keyboard: all actions and functionality can be done with a keyboard.
- Screen-reader: announces current value, days/dates in calendar.
-
Compatibility: current
<gl-datepicker>props and Vue 2.x/3.x - Performance: small dependancy footprint.
- i18n: supports internationalisation customisation
- Theming: supports dark/light mode
Usability
- Output value in
yyyy-mm-ddfull-date format as defined in RFC 3339 specification- Reads output in localised string on selection e.g. "First of January two-thousand and twenty-two"
- Localise input and value to device language e.g.
mm/dd/yyyyfor en-US anddd/mm/yyyyfor en-AU/en-UK etc.- B – big-endian (year, month, day), e.g.
2022-03-31or2022.03.31or2022/03/31or "2022 March 31" - L – little-endian (day, month, year), e.g.
31.03.2022or31-03-2022or31/03/2022or "31 March 2022" - M – middle-endian (month, day, year), e.g.
03/31/2022or "March 31, 2022"
- B – big-endian (year, month, day), e.g.
- Internationalisation of labels, days/months in calendar, and seperators in output e.g.
6.6.2022 - Validate
minandmaxattribute values - Have seperate inputs for day/month/year
- Keyboard up//down keys increment/decrement value for each day/month/year (start at current day/month/year for each)
- Keyboard left//right keys navigate betweemn day/month/year inputs
- Keyboard inputmode
numeralfor digits - Move focus to next input when input digit length entered e.g. entering
31moves focus to month field
- Button to clear selected value from day/month/year inputs
- Button to open calendar (conditonal to mouse/finger pointer event, keyboard-only bypasses calendar)
- Button to open/close calendar
- Year selector and/or increment/decrement controls
- Month selector and increment/decrement controls
- Day dates in table
- Day name in header e.g. "Mon" (read as full name "Monday")
- Day dates in cells e.g 8 (read as full date "Thursday 08 September 2022")
- Keyboard arrow keys (up/down/left/right) traverse dates between weeks and months
- Keybord enter key populates selected date into input
- Visually highlight today's date
- Soft-select today's date by default (has focus, selectable with enter key, and starting point for keyboard navigation)
- Current month includes previous/next month days for selection as part of days of the week
- Styled to match device theme light/dark mode with sufficient contrast (min 4.5:1 contrast ratio for WCAG AA)
- Remove or disable days/months/years before or after
minandmaxattribute values
Current props
| prop | type | default | notes |
|---|---|---|---|
target |
String |
'' | Selector of element that triggers the datepicker. Defaults to the calendar icon. Pass null to trigger on input focus. |
container |
String |
'' | DOM node to render calendar into. Defaults to the datepicker container. Pass null to use Pikaday default. In gitlab-org/gitlab: app/assets/javascripts/members/components/table/expiration_datepicker.vue :container="null" renders the datepicker in the body to prevent conflicting CSS table styles |
value |
Date |
null |
|
minDate |
Date |
null |
Date native constructor set to new Pikaday instance on this.calendar property e.g. this.calendar.setMinDate(minDate)
|
maxDate |
Date |
null |
Date native constructor set to new Pikaday instance on this.calendar property e.g. this.calendar.setMaxDate(maxDate)
|
startRange |
Date |
null |
Date native constructor set to new Pikaday instance on this.calendar property e.g. this.calendar.setStartRange(startRange)
|
endRange |
Date |
null |
Date native constructor set to new Pikaday instance on this.calendar property e.g. this.calendar.setEndRange(endRange)
|
disableDayFn |
Function |
null |
Callback function that gets passed a Date object for each day in view. Should return true to disable selection of that day. In gitlab-org/gitlab: couldn't find usage of :disable-day-fn="
|
firstDay |
Number |
0 |
Callback function that gets passed a Date object for each day in view. Should return true to disable selection of that day. In gitlab-org/gitlab: app/assets/javascripts/sidebar/components/date/sidebar_date_widget.vue
|
ariaLabel |
String |
'' | data-attribute on the input field with an aria assistance text (only applied when bound is set) |
placeholder |
String |
defaultDateFormat |
|
autocomplete |
String |
'' | Defaults to off when datepicker opens on focus, otherwise defaults to null. |
disabled |
Boolean |
false |
|
displayField |
Boolean |
true |
Not used at all? |
startOpened |
Boolean |
false |
Triggers this.calendar.show() on mounted when true. In gitlab-org/gitlab: couldn't find usage of :start-opened=" or start-opened
|
defaultDate |
Date |
null |
The initial date to view when first opened (this.defaultDate or this.value) |
i18n |
Object |
null |
Language defaults for month and weekday names (see internationalization) |
theme |
String |
'' | Define a classname that can be used as a hook for styling different themes, see theme example |
showClearButton |
Boolean |
false |
Custom implementation of clear button. In gitlab-org/gitlab: show-clear-button usage in |
inputId |
String |
null |
|
inputLabel |
String |
'Enter date' | |
inputName |
String |
null |
Resources
Edited by Scott de Jonge