jquense / react-widgets

Polished, feature rich, accessible form inputs built with React
http://jquense.github.io/react-widgets/
MIT License
2.34k stars 395 forks source link

Manual input date ignores edit format and parse format #1111

Closed ponytojas closed 4 months ago

ponytojas commented 2 years ago

I'm trying to migrate from "4.6.1" version to "5.5.1". In the previous implementation I was using setDateLocalizer and no longer is available.

The app had a behavior that I can't reproduce again using Intl API (I can't move to moment.js or date-fns)

In the previous version, when the use click on the input (not changing the popup visibility) the date format changes from the users one (based on the language code, for example "DD/MM/YYYY HH:mm") to a standard ISO "YYYY/MM/DD HH:ss", the component received the input converts to Date and parse back to user format.

In the new version I can change the display format with valueEditFormat and valueDisplayFormat to set the ISO one (more or less, I can get exactly the same) but if the input changes it ignore the format and set a new date with "MM/DD/YYYY HH:mm" format, what generates errors of first data been bigger than 12.

I tried changing parse, formats and Localization but I can't figure how to make it works.

Also, I don't know if that "string to Date" conversion could be dynamically by the user language using the Intl Api (if the user has "DD/MM/YYYY" use that instead predefined ISO or US format"

jquense commented 2 years ago

can post a reproduction of the issue I can poke at? Having a hard time understanding exactly where the issue is from the description

ponytojas commented 2 years ago

We need a Date Time control that uses the locale set in the browser, except when the user manually tries to edit the date in the control (not using the calendar). In that case we need to show an ISO formatted date so the user can see what format we expect him to enter ('YYYY-MM-DD') no matter what the locale is being used, and the date can be correctly parsed by the component (probably using new Date() under the hood).

The problem is that we can not find a way, using Intl, to pass a format to the valueEditFormat property (http://jquense.github.io/react-widgets/docs/DatePicker#valueEditFormat) to display the date in ISO format.

Letting the user input the date in the locale format will make some dates break (in Spanish for example when the day is bigger than 12) or even worse parsing an incorrect date (in Spanish for example when the day is lower than 12 and the date gets days as months and months as days).

That was possible before, but we can not find a way to port the previous implementation.

DavidCasillasRivero commented 2 years ago

¿Is this possible? To pass a locale dependant format to the valueDisplayFormat and a fixed format to the valueEditFormat property that does not depend on the browser locale, so the control can hint the user on the exact format expected when he wants to input a data manually.

For example in spanish locale and December, 31 date:

jquense commented 2 years ago

The Intl API doesn't provide any tools for parsing dates from a format, so formats are only used to display values, e.g. for turning dates into strings but not strings into dates. The parse prop accepts a function tho which you can use to provide your own logic. Alternatively you can use a localizer that supports date format parsing for this

DavidCasillasRivero commented 2 years ago

Thanks for the help and sorry for not being really clear in our description of the issue.

We know that Intl is not used for parsing dates.

We think that if the displayed format does not equal the accepted format it is not really user friendly to let the user manually input a Date that will break depending on the language configured in the browser. For us the valueDisplayFormat is useles since there us no way to set a non-locale dependant format that guaranties a correct parsing with new Date().

How is the new version of the control prepared to handle this, without depending on moment or date-fns? In the basic HTML5 date control it seems that this is handled out of the box. You can click on a date control an manually input a date in the displayed locale and the date is correctly parsed.

Providing a custom parse function as is suggested does not help either, we don't know what the input format is in that function so we can not apply a parser.

So the problem here is:

The way to go seems to implement a custom Localizer that can dynamically adjust what is displayed depending on the control having focus or not, by using Intl in one case and outputing a fixed format in the other. Is it required that the custom localizer is implemented in TypeScript? Our project does not use TypeScript.

jquense commented 2 years ago

There is no way to hint the user that he must input the date in a different format (YYYY-MM-DD) for example.

I'm not sure what you'd want the input to do to hint, it's up to you to tell users what valid date formats are, via field helper text or validation errors, etc.

Providing a custom parse function as is suggested does not help either, we don't know what the input format is in that function so we can not apply a parser.

i'm not sure I understand this, y'all valid input formats, so should have access to them in your parse function, which you also provide. The parse function is also passed the configured localizer, so you can check what locale it is, or in the case of native Intl api's you can just use the Intl global object. If you are trying to get a list of valid input formats for a locale, that info is just not available, natively. You'd need to use moment, or luxon, etc for that, which ship lists of valid formats

DavidCasillasRivero commented 2 years ago

Thanks. The way things work are clear now. With the new implementation we should depend on moment or date-fns to get the previous functionality and that is something we do not want. Our app is using Intl and do not want to add any other dependency. So we probably will forget a DateTime control and implement something based on native HTML5 date input and time input.

Just to clarify our issues, if this can help you to understand this use case:

There is no way to hint the user that he must input the date in a different format (YYYY-MM-DD) for example.

If a user click on a date control to manually edit it , and the displayed date is 31/12/2012 (DD/MM/YYYY), even if we add a help text somewhere, the natural thing is to edit the date trying to set it to DD/MM/YYYY and not what new Date() needs (YYYY-MM-DD). For us it is a very bad user experience. With the old version we just switched the displayed format to 2021-12-31, which we think is not optimal but at least it is clear what the date format must be.

Providing a custom parse function as is suggested does not help either, we don't know what the input format is in that function so we can not apply a parser.

Even if I know the formating of the date inside the parse method, there is no way to parse it back. For example in the format there is no indication on the separator used or even the order of the date parts. So again we depend on an external library, which we do not want.

The old version provided us with a workaround (not perfect but valid enough) without depending on external libraries. Honestly I don't see how the control using the native Intl API can be used, if we are forced tell the user "Please, if you want to input a date manually do not write it in the format you are seeing in the control but use this other format documented here."

jquense commented 2 years ago

With the new implementation we should depend on moment or date-fns to get the previous functionality and that is something we do not want.

I'm not sure I understand this concern. the previous behavior also depending on moment or date-fns, v5 makes it possible to not use these, but the tradeoff is that you are limited by what the Intl API supports. if you want previous behavior from v4, you should use a different localizer, this is not a new requirement it's the same as v4

With the old version we just switched the displayed format to 2021-12-31, which we think is not optimal but at least it is clear what the date format must be.

again because v4 required you use moment or another localizer, to get the same behavior, you need to use the same localizer you used in v4

The old version provided us with a workaround (not perfect but valid enough) without depending on external libraries.

not quite right, it required you depend on external libraries

DavidCasillasRivero commented 2 years ago

You are right. I have reviewed the old implementation and we had to use a custom Localizer to overcome the limitation using the old version. We have managed to implement a simple Localizer that can use a specific value of the valueDisplayFormat to know if it must render the date using Intl, or by generating an ISO date string. We just pass a predefined string valueDisplayFormat='ISO-format' so the formater function (date(), datetime() and time()) can check for this special format and switch to the ISO formating instead of Intl. Seems that we had a hard time getting all pieces together so thanks for your patience.