mui / mui-x

MUI X: Build complex and data-rich applications using a growing list of advanced React components, like the Data Grid, Date and Time Pickers, Charts, and more!
https://mui.com/x/
4.07k stars 1.26k forks source link

[pickers] New component: `DateField` #5531

Closed flaviendelangle closed 1 year ago

flaviendelangle commented 2 years ago

I am calling this component DateField like React Sectrum, some are calling it DateInput like Telerik. The base idea is to have a basic component to enter dates without any view.

Here is a POC to see what's doable: #5504

Benchmark

Related components

Other field components

Most of the logic of this component can be used for future components like DateTimeField or DateRangeField or TimeField if relevant.

Telerik did a single DateInput component that can act like a TimeInput or a DateTimeInput. The only use case where another component would be usefull is probably DateRangeField where the input format is inherently different.

Usage with views

When used with views (for instance in DatePicker) one of the goal can be to make the view and the input parts as independent as possible to allows users to deeply override one of the two. For instance, replacing the DateField in DatePicker with a custom one that has one input per section.

Goals

Principle

The basic idea is to split a date into sections that will be editable independently. For instance in the format dd/MM/yyyy' (date-fns format), we want to split into :

  1. dd => the day
  2. MM => the month
  3. yyyy => the year

We will then be able to navigate across those sections with the left and right arrow keys and to edit the currently selected sections in several ways:

  1. With the ArrowUp / ArrowDown to remove / add one unit to the section
  2. With the numerical keys to set the numerical sections
  3. With the alphabetical keys to set the full letters sections (the month in most scenarios)

Challenges

Analyze a format token (i.e MMMM) and know if it targets a day, a month or a year)

Maybe some date libraries expose this information. Otherwise, it could probably be hard-coded in the adapter.

Release process

The idea is to work on this new component in parallel with the current pickers. The process could look something like

  1. Release an Unstable_DateField component
  2. Release an Unstable_DatePicker component using the Unstable_DateField as its input component instead of our current PireDateInput
  3. Replace DateField with the new Unstable_DateField in some future major
alexfauquette commented 2 years ago

I think spectrum approach is more promising for this component. Instead of controlling the selection, they use dedicated <span> of each element of the date.

It sounds easier to customize and to have screen readers

About spliting dd/MM/yyyy to know the formats, this way of managing customization is a pain for two reasons:

We've already got an issue to put date elements in the correct order according to localization #4756

A way to be reliable with the different libraries could be to define them as a combination of different properties:

flaviendelangle commented 2 years ago

It sounds easier to customize and to have screen readers

Why is that ?

For customization, Telerik approach allows users to very easily use there own input whereas in Spectrum, users need to customize a div to look like there input.

For screen readers, I'm not very good on that topic. But I would expect a single input to be able to have some aria attribute so that the screen reader understands it's a date.

About spliting dd/MM/yyyy to know the formats, this way of managing customization is a pain for two reasons:

Do you have an approach that does not require to split the date ? It's clearly one of the biggest challenge (for both Telerik and Spectrum approach). And I would be in favor of strongly limiting the valid formats in the inputs if it helps.

it kind of breaks internationalization, because countries do not write dates in the same order

My POC supports order change, I parse the format string to determine where is the month / the day and the year.

format standards vary across libraries

Yes, I only support date-fns on the POC but it would clearly not work out of the box for other libraries. The adapter would need to be able to say that YYYY is a year for instance, and MM a month in numerical form.

A way to be reliable with the different libraries could be to define them as a combination of different properties:

Not sure to understand how the lines below differs from my current implementation. For me at one point we need to be able to parse a format to understand where are the various parts of the date. Because even the default of the library depends on the locale, so unless we create MUI specific date locals, we need to parse those formats.

  1. The date library locale gives us the format to render (or the user if he wants to pass a custom one)
  2. The date library tells us the meaning of each token in this format
  3. Our component parses the format according to the meaning of each token and create and object representing the format but in a way we can increment / decrement / concatenate values
alexfauquette commented 2 years ago

About customization, I agree. I was thinking, about specific style for month/day but it 's not really an helpful customization

About a11y my concern is to be able to say that the focus is on the month section of the date input, and when updating it only says the month updated, and not the full date.


My POC supports order change, I parse the format string to determine where is the month / the day and the year.

Yes, you support orders provided by users, but not the order provided by the date library. From what I understand, libraries offer ways to display the month in multiple ways (1 digit, 2 digits, short name, full name, ...) with M, MM, ... (same for all the other atoms of a date/time)

But the date localization includes also the way all those elements are mixed. Then they have special tokens for full-date, full-date-time, ...

For example in the date-io adapter for date-fns, we have the following encoding.

fullDate: "PP"
flaviendelangle commented 2 years ago

For example in the date-io adapter for date-fns, we have the following encoding.

We should be able to do the same thing as the getFormatHelperText methods in the adapters to get the detailed format from those short ones.

But yes if I'm missing something and we can't do it, we could be totally stuck.

flaviendelangle commented 2 years ago

I confirm that it does seem to work for date-fns at least.

alexfauquette commented 2 years ago

And I see it's now possible to add this getFormatHelperText to luxon, so I od not see any blocker on this side. Maybe the specific calendars, but not even sure it's a problem

flaviendelangle commented 2 years ago

For Luxon it's a work in progress (I just tried to use there new method but I still have some issues), but yes it should work in the near future. The main issue being that it's a luxon v3 feature so we will need to drop support for v1 and v2. But that's a discussion for another issue I think.

Another concern I have about this format parsing (for both Telerik and Spectrum) is that I need to know that MMMM is a month in full letter for instance. So the adapter would need to contain some sort of map describing each token so that I can decide what to do when the user interacts with it. That is not very elegant, maybe some libraries already have this option. In date-fns it's hidden inside a switch in there codebase (https://github.com/date-fns/date-fns/blob/main/src/_lib/format/formatters/index.ts#L87)

alexfauquette commented 2 years ago

Not sure to understad. You need to know that MMMM is a month, but do you need to know how it's represented?

You could get an array of options by formatting 12 dates with MMMM. Then the month selector is a kind of autoselect

flaviendelangle commented 2 years ago

Because if it's a full letter month, I need to store the "query". When you type "J", I store "J" in state and display "January", that way if you then press "U", I have 'JU" in state and display "June", etc... Maybe I can be fully agnostic of the rendering and have this full letter editing working for both full letter rendering and numeric rendering.


We might have similar topics for years. For instance, if the year has the format 22, I have probably have to be aware of that.

gerdadesign commented 2 years ago

Continuing the conversation from #5504 for more general customization options:

For now the placeholder of an empty section is the name of the section (month for a month section for instance) Which is really basic and do not support any kind of localization. We should probably improve this, I'm open to suggestions for the default behavior and the customization options.

Seeing "month" written out gives me the expectation that I will be choosing a month written out (January, February, March, etc.), even though the input is looking for a number. Since dates and time can have so many different formats, it would be beneficial to be as clear as possible for the end user about the expected input.

I was also thinking about the customization options and put together a basic prototype to explore customization. There are 2 flows: the standard Material components + a customized version with many style changes that I could imagine someone may want.

This also brought up the options for how to show information that is expected, but hasn't been keyed in yet. I see we currently use zeroes, but maybe this is something that people would want to customize as well? Options

flaviendelangle commented 2 years ago

I took a look at the mobile behavior of the field.

The issues comes from the fact that on android event.key and event.keyCode have fixed values (129 and Unifentified) so my logic does not work at all.

I try to change the approach and use the onChange handler to figure out which sections have been modified and update them. It could work, but since we select the section, if you select the year, then type 2, it writes 2. But if after that you type 1, it writes 1 not 21 because the behavior of the input is to replace the selected section, not to append.

I took a look at Telerik's DateInput behavior on Android, and it is exactly what I described above (it is impossible to enter a multi character value in a date section).


So right now we do have a major problem when it comes to mobile usage of the DateField (not the DatePicker since right now they have read only inputs).

I still have a few ideas to experiment:

LukasTy commented 2 years ago

@flaviendelangle Nitpick, but consider ignoring keyCode field as it's deprecated. code and key should be the ones we care about. In any case, I double checked and can confirm that this is not a fun problem having the same code (229) being spit whenever user enters any key on mobile keyboard.

flaviendelangle commented 2 years ago

keyCode field as it's deprecated.

Yes, I was not using it but I tried just in case to see if it was working for Android

flaviendelangle commented 2 years ago

@gerdadesign concerning the placeholder.

I see two topics here

  1. How do we display partially filled section (I press 1 for the year, do I see 1 ? 0001 ? 1YYY ? YYY1 ?

Spectrum and Telerik have the same behavior as we do today: only show the placeholder when the value is empty for this section and show the value of the section otherwise.

I do agree that some of your proposals feels cleaner. We have to make sure it works with variable length (for instance full letter month). And I am unclear about the amount of complexity it brings.

  1. What is the content of the placeholder for each section

On that one, I went to the easiest content (the name of the section: month / minute / ...) But it clearly is temporary.

Telerik uses the name of the section (month/

For Spectrum, they have different placeholder based on the section (code here). In french I have aaaa for year, jj for day and mm for month. It does not seem to respect the year length (I would probably write aaaa for a 2 letter year format).

For this one, I think the spectrum behavior is a good baseline. And if the localization is in the regular localeText then people could customize it.

gerdadesign commented 2 years ago

Thank you for this resource! It looks great.

(I would probably write aaaa for a 2 letter year format).

Just to clarify — was this intentional? 4 placeholder letters for a 2-numeral input? If so, why not 2 letters for 2 numerals?

flaviendelangle commented 2 years ago

(It would probably write aaaa for a 2 letter year format).

It has a small typo, I just meant to as far as I understand, Spectrum either does not support custom format for the year (that's very possible) or chose to keep its placeholder system as simple as possible with just one placeholder per type of section.

flaviendelangle commented 1 year ago

Another thought. The example you did was with the year section which 99.88% of the time has always the same amount of digits (4 or 2). But for the month section, users can types values that have 1 to 2 digits (without trailing zeroes). And in some approaches described above, I think that users would feel the need to type 0 then 1 to select the 1st day of a month. Pressing just 1 would give them the visual feedback that they are doing something wrong to get to their goal.

Here would be the visual output I think would appear with all solutions after pressing 1 on an empty month section:

Approaches that don't work well in this scenario:

Approaches that work but not perfectly:

Approaches that work perfectly:

gerdadesign commented 1 year ago

zeroes right to left*: 01 clear field then type: 1

I do see zeroes a lot and it seems to be a common default! I would expect it to accept both 1 and 01 to display 01. With clearing the field, I worry about losing the number of expected values: YYYY → 2 (wondering if it was 2 digits or 4?)

flaviendelangle commented 1 year ago

Closing this one, the DateField component is in good shape @gerdadesign if you feel like the current placeholders should be improved to follow some of the design displayed above, I can create a follow up issue and put in on the board