Skip to content

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

  1. Min/Max dates: disable selection of dates outside of desired range.
  2. Focus state: links, buttons, and controls have a visible focus state.
  3. Keyboard: all actions and functionality can be done with a keyboard.
  4. Screen-reader: announces current value, days/dates in calendar.
  5. Compatibility: current <gl-datepicker> props and Vue 2.x/3.x
  6. Performance: small dependancy footprint.
  7. i18n: supports internationalisation customisation
  8. Theming: supports dark/light mode

Usability

  1. Output value in yyyy-mm-dd full-date format as defined in RFC 3339 specification
    1. Reads output in localised string on selection e.g. "First of January two-thousand and twenty-two"
  2. Localise input and value to device language e.g. mm/dd/yyyy for en-US and dd/mm/yyyy for en-AU/en-UK etc.
    1. B – big-endian (year, month, day), e.g. 2022-03-31 or 2022.03.31 or 2022/03/31 or "2022 March 31"
    2. L – little-endian (day, month, year), e.g. 31.03.2022 or 31-03-2022 or 31/03/2022 or "31 March 2022"
    3. M – middle-endian (month, day, year), e.g. 03/31/2022 or "March 31, 2022"
  3. Internationalisation of labels, days/months in calendar, and seperators in output e.g. 6.6.2022
  4. Validate min and max attribute values
  5. Have seperate inputs for day/month/year
    1. Keyboard up//down keys increment/decrement value for each day/month/year (start at current day/month/year for each)
    2. Keyboard left//right keys navigate betweemn day/month/year inputs
    3. Keyboard inputmode numeral for digits
    4. Move focus to next input when input digit length entered e.g. entering 31 moves focus to month field
  6. Button to clear selected value from day/month/year inputs
  7. Button to open calendar (conditonal to mouse/finger pointer event, keyboard-only bypasses calendar)
    1. Button to open/close calendar
    2. Year selector and/or increment/decrement controls
    3. Month selector and increment/decrement controls
    4. Day dates in table
      1. Day name in header e.g. "Mon" (read as full name "Monday")
      2. Day dates in cells e.g 8 (read as full date "Thursday 08 September 2022")
      3. Keyboard arrow keys (up/down/left/right) traverse dates between weeks and months
      4. Keybord enter key populates selected date into input
      5. Visually highlight today's date
      6. Soft-select today's date by default (has focus, selectable with enter key, and starting point for keyboard navigation)
      7. Current month includes previous/next month days for selection as part of days of the week
    5. Styled to match device theme light/dark mode with sufficient contrast (min 4.5:1 contrast ratio for WCAG AA)
    6. Remove or disable days/months/years before or after min and max attribute 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