Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refresh date picker components #537

Open
connor-baer opened this issue Jan 30, 2020 · 19 comments
Open

Refresh date picker components #537

connor-baer opened this issue Jan 30, 2020 · 19 comments
Assignees
Labels
⚛️ component Changes to a React component
Milestone

Comments

@connor-baer
Copy link
Member

connor-baer commented Jan 30, 2020

Components to amend

Calendar, CalendarTag, CalendarTagTwoStep, RangePicker, SingleDayPicker

Context

Circuit UI contains a bunch of similar and confusingly named date components right now.

Tasks

  • bring input styles inline with Circuit UI's other input components
  • rename SingleDayPicker to DatePicker and RangePicker to DateRangePicker
  • look into deprecating CalendarTag and CalendarTagTwoStep (can be built with SingleDayPicker and RangePicker and doesn't seem to belong in Circuit UI)
  • replace react-dates with a library that supports TypeScript, tree-shaking, and doesn't depend on moment.js
@connor-baer connor-baer added feature A new feature or enhancement help wanted Looking for contributions labels Jan 30, 2020
@connor-baer connor-baer added this to the v2.0 milestone Jan 30, 2020
@fernandofleury
Copy link
Contributor

What about using something like https://github.com/gpbl/react-day-picker? This way we would get rid of external dependencies as well.

@connor-baer
Copy link
Member Author

connor-baer commented May 25, 2020

@marielakas You indicated that you're interested in tackling this issue. Is this still the case? Do you have an estimate when you'll be able to work on it?

@connor-baer
Copy link
Member Author

connor-baer commented Jan 19, 2021

I've done some research on third-party calendar libraries and the native date input.

Criteria

  • Localisation: SumUp operates in over 20 countries with over 30 languages. The calendar needs to be localized for all of them. That includes strings and date formats.
  • Accessibility: This is a strict requirement for all (Circuit UI) components. All users, no matter how they perceive and interact with content, need to be able to use our apps.
  • Types: Circuit UI is being migrated to TypeScript ([RFC] Typescript #516), so any dependencies need to export types or have a DefinitelyTyped package available.
  • Server-side rendering (SSR): Some of SumUp's apps are server-side rendered, so any calendar library should support this as well. There are hacks to only render on the client, but that could negatively affect cumulative layout shift (CLS), one of the core web vital metrics.
  • Treeshaking: For performance reasons, only the parts of the library that are used should end up in the client bundle. That means ES exports and no side-effects.
  • No moment.js: moment.js is heavy and doesn't support treeshaking (there are Babel and Webpack plugins to achieve something similar, but they require manual setup). It's also deprecated and no longer recommended for use. Modern date libraries such as date-fns are okay.
  • Maintained: No software is ever free of bugs, so it needs active maintainers to patch them. Recent commit history, number of downloads, and open issues & PRs are good indicators.

Ideally, the library should have an intuitive and flexible API though Circuit UI can add an abstraction on top.

Comparison

Update (April 16, 2024): I've added an updated version of this table below.

Ranked by popularity based on weekly NPM downloads

Name Localisation Accessibility Types SSR Treeshaking No moment.js Maintained
react-dates (current) 🚨 🚨 ⚠️
react-datepicker ⚠️ 🚨
react-day-picker ⚠️
react-datetime 🚨 🚨 🚨
react-calendar
input[type="date"]
wc-datepicker

Conclusion

Only two options meet all criteria: react-calendar and the input[type="date"].

The native date input is the simplest, most performant, most accessible solution since it shifts the responsibility of rendering the datepicker to the browser. Unfortunately, it's not yet supported by Safari on macOS where it gracefully degrades to a simple text input. Further, it only works for selecting single dates. Date ranges, disabled dates, etc. are not supported.

This advanced functionality requires a custom datepicker. Despite having the lowest number of downloads, react-calendar seems to be the best option here. With some polish and a better API abstraction (I quite like react-day-picker's API, we could use it as inspiration), this could be a viable option.

An important consideration here is the date library that's used by a calendar library. Aside from moment.js, date-fns and day-js are quite popular. There's also the very exciting Temporal proposal (currently in stage 2) that aims to improve JavaScript's native datetime handling.

Update (February 9, 2021): Turns out react-day-picker is maintained after all with a fresh beta release which puts it back in the running. It has the most intuitive and powerful API, but we need to carefully evaluate its accessibility and submit bugfix PRs if necessary.

@connor-baer connor-baer added ⚛️ component Changes to a React component and removed feature A new feature or enhancement labels Jan 29, 2021
@connor-baer connor-baer changed the title Improve date picker components Refresh date picker components Jan 29, 2021
@connor-baer connor-baer modified the milestones: v2.x, v3.0 Feb 3, 2021
@gpbl
Copy link

gpbl commented Feb 10, 2021

@connor-baerThanks for considering DayPicker in your list!

Just an head up – the next version of react-day-picker will depend on date-fns, but there's a discussion going on.

Also we will give an extensive look at a11y before the release: follow gpbl/react-day-picker#1036).

@connor-baer
Copy link
Member Author

Thanks for reaching out, @gpbl!

date-fns would work fine for us.

I'll do a more thorough review of the accessibility of react-day-picker before starting to work on this and will contribute fixes as necessary.

@connor-baer connor-baer modified the milestones: v3.0, v4.0 Apr 12, 2021
@robinmetral
Copy link
Contributor

Perhaps another option could be this date picker from Duet's web components design system. It has a strong focus on accessibility (there was even an audit), has a fantastic README and it could be easily wrapped to expose a React component in Circuit.

The main downside is that it doesn't support date ranges in a single visual element (there was a feature request for it: duetds/date-picker#61). A workaround is to use two separate inputs for selecting ranges: duetds/date-picker#4 (comment) but this may not be enough to cover all our use cases.

@long-lazuli
Copy link
Contributor

As I see you're mentionning the native input[type="date"],
we can imagine a progressive enhancement:

const DatePicker = () => {
  const [hasJS, setJS] = useState(false)
  useEffect(() => setJS(true))
  
  if(!hasJS) return (
    <input type="date" />
  )
  
  return (
    your beloved work
  )
}

of course, with all the attributes an accessible app need...

that can be a nice pattern everytime we do something relying too much on JS.

@connor-baer
Copy link
Member Author

@long-lazuli What problem are you trying to solve with this approach?

If you're trying to avoid JavaScript bloat, you'd need to code-split the custom date picker by dynamically importing it. This is difficult to do at the design-system-level since we can't make assumptions about the bundler that's gonna be used and whether it supports dynamic imports. Therefore we leave such optimizations to be implemented in userland.

@connor-baer connor-baer modified the milestones: v4.0, v6.0 Jan 7, 2022
@robinmetral
Copy link
Contributor

robinmetral commented Mar 12, 2022

I'm jotting down some thoughts here because I've done a bit of research related to date in forms yesterday:

What date picker components do we need?

Moving forward, we'll want to have (at least) three date input components:

  • a date input
  • a calendar date picker
  • a calendar range picker

Here's the rationale for that, along with extra thoughts:

  • We want both a date input and a calendar date picker because they serve different purposes
    • the date input is useful for entering a date that users already know and that may be far removed, e.g. a birthdate
    • the calendar date picker is useful for choosing a date close to the present, e.g. a desired delivery date. The calendar view showing days of the week is also useful to help users choose a date
  • 💭 I don't think we want the CalendarTag components any more, but we need to think of proper replacements for the filtering pattern
  • 💭 We haven't supported date pickers with support for time yet, but might want to explore it
  • 💭 We haven't supported week/month pickers yet, but might want to explore it

Date input

Our current DateInput component is a styled wrapper around the native <input type="date" />.

Issues

Although it can seem that using the platform™️ is a good choice here, the native date input has a few issues that make us reluctant to use it:

  • the native date picker in mobile Safari renders a calendar UI without an alternative way to type a date, making it long to find a date in the past:

    Screen.Recording.2022-03-11.at.18.26.32.mov
  • it's even worse in order Safari mobile versions (that we still support), where three spin buttons are rendered:
    Screenshot 2022-03-11 at 18 34 20

  • in older Safari desktop versions (that we still support), the input falls back to a text input. This is still usable, but might cause validation issues if pages rely on the native date input's formatting hints (although they shouldn't)
    Screenshot 2022-03-11 at 18 38 28

  • there are accessibility concerns with the native date input, primarily around dictation. I won't go into details, but will reference good sources:

Improvements

I would recommend creating a new input component wrapping a default text field, and simply formatting the date much like our CurrencyInput formats numbers.

A designer created a POC for it (mind the UX primarily): https://jsfiddle.net/vabe284o/3/

Further accessibility testing will be required, but we would ideally like:

  • mobile device users to be shown a number pad, instead of a calendar or spin buttons
  • focus switching automatically from day to month to year (similar to a lot of card payment widgets, but TBC in terms of accessibility)
  • the day, month and year values to be independently focusable (i.e. three inputs under the hood) while having the component expose a single date value (in the ISO YYYY-MM-DD format, a common date format for APIs)

If there are no concerns here, we will start working on the implementation for this next quarter (designs will be ready by then), which should eventually replace our current DateInput and start being used for use cases like date of birth fields.

Calendar input components

The calendar input components will likely reach for a third-party library, since we want to avoid writing the implementation ourselves. @connor-baer did excellent research on these.

I haven't looked much into them (yet), and we don't have the capacity to tackle it right now, since this is a larger change than the DateInput improvements.

Here are some extra resources for consideration when we look into it again:

@connor-baer
Copy link
Member Author

connor-baer commented Mar 27, 2022

react-day-picker v8 has just been released.

Notable changes

  • added date-fns library as peer dependency
  • native TypeScript support
  • selection modes: single, multiple, range
  • improved ARIA support
  • replaced DayPickerInput component with useInput hook
  • new and redesigned props

@connor-baer connor-baer added this to the v6.0 milestone Jun 1, 2022
@connor-baer
Copy link
Member Author

connor-baer commented Jun 22, 2022

Adobe just released a fully accessible and customizable range of date picker components: https://react-spectrum.adobe.com/blog/date-and-time-pickers-for-all.html

Edit: accessibility review

@long-lazuli
Copy link
Contributor

What would be great is to allow this component to be an abstraction of two uncontrolled inputs.

So we could use react-hook-form without having to wrap in a Controller.

@robinmetral robinmetral removed this from the v6.0 milestone Jul 21, 2022
@robinmetral robinmetral removed the help wanted Looking for contributions label Nov 10, 2022
@fernandofleury
Copy link
Contributor

@connor-baer I was thinking of tackling this as a hack day project... react-dates has to go. Any specs to follow or should I just try my best to copy what we have already?

@robinmetral
Copy link
Contributor

robinmetral commented Mar 28, 2023

That's awesome @fernandofleury! No specs really, there's a WIP new design for the calendar picker (Figma internal link) but it needs review (and doesn't cover all existing use cases). if you replicate the API one-to-one we can ship it with minimal migration effort. Otherwise, we can also look into usage (CodeSearch is really good) and see if we want to drop any component/variant/option. Did you have any rough idea of a replacement library? Did you want to tackle everything that uses react-dates or just one component?

@fernandofleury
Copy link
Contributor

@robinmetral I think the first step is replacing react-dates with something more feasible while keeping the API compatible, then we can talk about changing things.

I'll first try it out with react-date-picker. Even though the spectrum one doesn't look back, I'd rather rely on date-fns for lts rather than adobe's library

@connor-baer
Copy link
Member Author

connor-baer commented Mar 29, 2023

I explored using wc-datepicker last month (#2020, preview). It's web components-based, which is a direction we will likely take for Circuit UI as a whole.

Would you like to continue with my proof of concept, @fernandofleury?

@connor-baer connor-baer added this to the v7.x milestone May 22, 2023
@connor-baer connor-baer modified the milestones: v7.x, v8.x Feb 9, 2024
@connor-baer
Copy link
Member Author

connor-baer commented Apr 16, 2024

The design system team has started work on new date and date range input components. The original review of third-party calendar libraries is over 3 years old, so I've updated it below. I've added another criterium:

  • Customization: Is it possible to customize the default styles? Can the layout be modified? Can individual days be disabled, highlighted or otherwise marked up?
Name Localisation Accessibility Types SSR Treeshaking Date library Customization Maintained
react-datepicker ⚠️ 🚨 ⚠️ date-fns ⚠️
react-day-picker ⚠️ ⚠️ date-fns
react-calendar 🚨 ⚠️
react-dates (current) 🚨 🚨 moment.js ⚠️ ⚠️
react-aria ⚠️ @internationalized/date
react-datetime 🚨 🚨 🚨 moment.js ⚠️ ⚠️
@duetds/date-picker ⚠️ ⚠️ ⚠️
wc-datepicker ⚠️ ⚠️
cally ⚠️ ⚠️
input[type="date"] ⚠️ 🚨

react-calendar and react-datetime are disqualified due to their lack of accessibility. react-dates is out of the running because of its dependency on moment.js. The native input[type="date"] element lacks customizability and has accessibility shortcomings.

As much as I want to start moving towards web components, React's lack of compatibility would make the integration of @duetds/date-picker, wc-datepicker, and cally cumbersome at best.

react-aria ticks all the boxes, but would tie us into a complex ecosystem that might cause us similar issues as react-dates.

react-datepicker lacks customization and doesn't support SSR.

This leaves react-day-picker as the least bad option. I'm still concerned about its questionable accessibility (e.g. gpbl/react-day-picker#1688). The maintainer appears to be quite active, though, so we should be able to contribute fixes.

Edit: Added react-aria and @duetds/date-picker.

@gpbl
Copy link

gpbl commented Apr 17, 2024

Hi @connor-baer, we indeed have 2 known accessibility issues with react-day-picker. gpbl/react-day-picker#1688 is being addressed via gpbl/react-day-picker#2089 and will be fixed in the upcoming major release.

Keep in mind in your evaluation that DayPicker requires some performance optimization. The CSS need to be simplified. The TypeScript declarations need some refresh... I'm addressing these issues in my spare time. However, they are time-consuming as they necessitate rewriting most of the code.

I would greatly appreciate any help with DayPicker! While it's exciting to see my work help other developers, it also comes with external pressures that can be challenging to manage. Hopefully adding more docs about the architecture and the DayPicker roadmap will help get more contributors...

@connor-baer
Copy link
Member Author

Thanks for reaching out, @gpbl! Your responsiveness is exemplary and I appreciate all the effort you've put into react-day-picker 👏🏻

I started a proof-of-concept using react-day-picker and quickly ran into two issues:

  1. I wasn't able to customize the component to support our more advanced use cases such as adding descriptions for individual dates.
  2. Localizing date-fns requires importing and passing around a locale object (docs). This hurts the developer experience and could lead to performance issues if developers forget to tree-shake the locales.

In the end, I found that implementing the calendar from scratch using the Temporal API (inspired by cally) required the least amount of work to fulfill all of our requirements. The pull request description has more details on the approach: #2494

@connor-baer connor-baer self-assigned this Apr 25, 2024
@connor-baer connor-baer modified the milestones: v8.x, v9 Jun 27, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
⚛️ component Changes to a React component
Projects
None yet
Development

No branches or pull requests

6 participants