cloudscape-design / components

React components for Cloudscape Design System
https://cloudscape.design/
Apache License 2.0
2.36k stars 156 forks source link

[Feature Request]: Add Support for Customizable Date Formats in the Date Picker Component #2741

Open MwSpaceLLC opened 1 month ago

MwSpaceLLC commented 1 month ago

Description

Hello,😊

We have noticed that the current version of the Date Picker component does not allow for customizable date formats, which is a crucial feature for internationalization. Different regions around the world use various date formats, and for instance, in Italy, the standard format is DD/MM/YYYY. Unfortunately, this format is not currently supported by the component.

We are aware that there has been a response in this discussion indicating that there are no plans to implement this feature. However, we strongly encourage reconsideration of this decision.

The only current workaround involves creating a custom component from scratch using the base library, which we do not believe follows best practices. Additionally, from a business perspective, we feel it is not aligned with company policies to disregard such requests, especially since the solution could be as simple as allowing for the regular expression (regex) used for date validation to be customizable.

We believe this would enhance the flexibility of the component and improve its international applicability. Our company, MwSpace, relies heavily on Cloudscape components for our cloud systems and applications, and this feature would allow us to better serve our global clients.

Thank you for considering this request. We truly appreciate the continued development of this excellent library.

Best regards,

A. Ivanovitch - CEO of MwSpace LLC

Code of Conduct

MwSpaceLLC commented 1 month ago

For use it as italy value, for the moment, use patch-package

  1. npm install --save-dev patch-package
  2. create ./patches folder on root project
  3. indert code patch
  4. insert postinstall script

Patch Code for package: @cloudscape-design+components+3.0.762.patch

!! important !! Before commenting negatively on the temporary solution: The patch-package needs to be compiled according to your requirements. Below, you can see where we modified the original files to transform the dates. DO NOT COPY AND PASTE We also use this patch in production, so if it doesn’t work for you, you’ve done something wrong.

diff --git a/node_modules/@cloudscape-design/components/date-input/internal.js b/node_modules/@cloudscape-design/components/date-input/internal.js
index 0547990..ed5aebd 100644
--- a/node_modules/@cloudscape-design/components/date-input/internal.js
+++ b/node_modules/@cloudscape-design/components/date-input/internal.js
@@ -1,29 +1,42 @@
 // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
 // SPDX-License-Identifier: Apache-2.0
-import { __rest } from "tslib";
+import {__rest} from "tslib";
 import React from 'react';
 import clsx from 'clsx';
-import { getDaysInMonth } from 'date-fns';
+import {getDaysInMonth} from 'date-fns';
 import MaskedInput from '../internal/components/masked-input';
-import { fireNonCancelableEvent } from '../internal/events';
-import { displayToIso, isoToDisplay, parseDate } from '../internal/utils/date-time';
+import {fireNonCancelableEvent} from '../internal/events';
+import {displayToIso, isoToDisplay, parseDate} from '../internal/utils/date-time';
 import styles from './styles.css.js';
+
 function daysMax(value) {
     // force to first day in month, as new Date('2018-02-30') -> March 2nd 2018
     const baseDate = displayToIso(value).substring(0, 7);
     return getDaysInMonth(parseDate(baseDate));
 }
-const yearMask = { min: 0, max: 9999, default: 2000, length: 4 };
-const monthMask = { min: 1, max: 12, length: 2 };
-const dayMask = { min: 1, max: daysMax, length: 2 };
+
+const yearMask = {min: 0, max: 2050, default: 2000, length: 4};
+const monthMask = {min: 1, max: 12, length: 2};
+const dayMask = {min: 1, max: 31, length: 2};
 const InternalDateInput = React.forwardRef((_a, ref) => {
-    var { value, onChange, granularity, __internalRootRef = null } = _a, props = __rest(_a, ["value", "onChange", "granularity", "__internalRootRef"]);
+    var {value, onChange, granularity, __internalRootRef = null} = _a,
+        props = __rest(_a, ["value", "onChange", "granularity", "__internalRootRef"]);
     const maskArgs = {
         separator: '/',
         inputSeparators: ['-', '.', ' '],
-        segments: granularity === 'month' ? [yearMask, monthMask] : [yearMask, monthMask, dayMask],
+        segments: granularity === 'month' ? [monthMask, yearMask] : [dayMask, monthMask, yearMask],
     };
-    return (React.createElement(MaskedInput, Object.assign({ ref: ref }, props, { value: isoToDisplay(value || ''), onChange: event => fireNonCancelableEvent(onChange, { value: displayToIso(event.detail.value) }), className: clsx(styles.root, props.className), mask: maskArgs, autofix: true, autoComplete: false, disableAutocompleteOnBlur: false, disableBrowserAutocorrect: true, __internalRootRef: __internalRootRef })));
+    return (React.createElement(MaskedInput, Object.assign({ref: ref}, props, {
+        value: isoToDisplay(value || ''),
+        onChange: event => fireNonCancelableEvent(onChange, {value: displayToIso(event.detail.value)}),
+        className: clsx(styles.root, props.className),
+        mask: maskArgs,
+        autofix: true,
+        autoComplete: false,
+        disableAutocompleteOnBlur: false,
+        disableBrowserAutocorrect: true,
+        __internalRootRef: __internalRootRef
+    })));
 });
 export default InternalDateInput;
 //# sourceMappingURL=internal.js.map
\ No newline at end of file
diff --git a/node_modules/@cloudscape-design/components/date-picker/utils.js b/node_modules/@cloudscape-design/components/date-picker/utils.js
index a0afddc..dd6706b 100644
--- a/node_modules/@cloudscape-design/components/date-picker/utils.js
+++ b/node_modules/@cloudscape-design/components/date-picker/utils.js
@@ -2,7 +2,7 @@
 // SPDX-License-Identifier: Apache-2.0
 import { getDateLabel, renderMonthAndYear, renderYear } from '../calendar/utils/intl';
 export function isValidFullDate({ date, granularity }) {
-    const regex = granularity === 'month' ? /^\d{4}-\d{2}(-\d{2})?$/ : /^\d{4}-\d{2}-\d{2}$/;
+    const regex = granularity === 'month' ? /^\d{2}\/\d{4}$/ : /^\d{2}\/\d{2}\/\d{4}$/;
     return !!date.match(regex);
 }
 export function getSelectedDateLabel({ date, granularity, locale, }) {
diff --git a/node_modules/@cloudscape-design/components/internal/utils/date-time/format-date.js b/node_modules/@cloudscape-design/components/internal/utils/date-time/format-date.js
index c8f5520..fb2c0ae 100644
--- a/node_modules/@cloudscape-design/components/internal/utils/date-time/format-date.js
+++ b/node_modules/@cloudscape-design/components/internal/utils/date-time/format-date.js
@@ -10,9 +10,9 @@ export function formatDate(value, granularity = 'day') {
     const year = value.getFullYear();
     const month = padLeftZeros(`${value.getMonth() + 1}`, 2);
     if (granularity === 'month') {
-        return `${year}-${month}`;
+        return `${month}-${year}`;
     }
     const date = padLeftZeros(`${value.getDate()}`, 2);
-    return `${year}-${month}-${date}`;
+    return `${date}-${month}-${year}`;
 }
 //# sourceMappingURL=format-date.js.map
\ No newline at end of file
diff --git a/node_modules/@cloudscape-design/components/internal/utils/date-time/parse-date.js b/node_modules/@cloudscape-design/components/internal/utils/date-time/parse-date.js
index 4fe4640..a79368f 100644
--- a/node_modules/@cloudscape-design/components/internal/utils/date-time/parse-date.js
+++ b/node_modules/@cloudscape-design/components/internal/utils/date-time/parse-date.js
@@ -1,7 +1,7 @@
 // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
 // SPDX-License-Identifier: Apache-2.0
 export function parseDate(value, strict = false) {
-    const [yearString, monthString, dayString] = value.split('-');
+    const [dayString, monthString, yearString] = value.split('-');
     const year = Number(yearString);
     const month = Number(monthString);
     const day = Number(dayString);
johannes-weber commented 1 month ago

Hi @MwSpaceLLC, thanks for reaching out! We did choose YYYY/MM/DD for the date picker to have a single internationally understood format and we do not have any plans to support customizable data formats for the date picker. If anything changes we'll post updates on this thread.

We strongly discourage monkey patching to change the date format as the patch relies on component internals which are not part of the public API and could change in the future. It introduces the risk of unintended side effects which leads to errors or unexpected behavior.

MwSpaceLLC commented 1 month ago

@johannes-weber, @pan-kot, @just-boris, @avinashbot, @jperals , @timogasda , @gethinwebster , @connorlanigan , @gmarchand, @olamad

Thank you for your response. I want to emphasize that when a structure or company releases a library, the final decisions naturally rest with the producer of the library. However, I would like to inform you that, for us in Italy, this library is useful, but in your opinion, should the software we are developing for our clients using this library force our clients to use the American standard format just because there are no plans for any changes? I believe this is not entirely logical, but the choice is yours.

I think that changing a date format isn’t particularly difficult, and I don’t think it should be a matter of policy. It’s not a feature that serves no purpose—it’s a global functionality.

It would be like publishing your own website and assuming it should only be displayed in English, end of story.

The question is: why did you release a library for everyone, but we are forced to use dates in the American format?

I believe this doesn’t make sense, but clearly, we in Italy are not in a position to judge how you decide to proceed with this project.

I would like to reiterate that your team manager should reflect on such a trivial matter and consider how important a date field is in a UI library.

The answer we would have to give to our clients, if we hadn’t used a patch, would have been:

“Excuse me, dear customer, the date is in the American format, and there’s nothing we can do about it because the library creator, being a corporate entity, has no intention of standardizing dates. Therefore, you’re stuck with a format that is difficult for you to understand and causes inconvenience.”

We will try to reach out internally through some representatives because, from our point of view, this situation is simply ridiculous.

Despite everything, it’s undoubtedly a great library, but stumbling over such a trivial issue like “dates cannot be modified” doesn’t reflect the true essence of your corporate identity.

That being said, I’d also like to add that on the AWS portal, the dates appear in Italian, and we don’t read them in other languages or formats.

I wish you all the best; I can’t say much more.

A. Ivanovitch | CEO of MwSpace llc

gethinwebster commented 1 month ago

Hello, and thanks for your response.

First, the good news: in a recent internal review of feature requests this same topic came up, and we decided that it is something that we want to prioritize working on. We can’t promise an exact date, but we plan to work on it soon.

I’d also like to give a bit more background to the current situation, and why we’ve historically been quite opinionated about the format supported by the date picker component. We originally chose this format (YYYY/MM/DD) based on research to identify the most internationally understood formats. It was very deliberately not the standard American format of MM/DD/YYYY, nor the European format of DD/MM/YYYY because the two can very easily be confused, leading to mis-reading (or mis-entering) of dates. Especially when building for a diverse international audience, we wanted to minimize the possibility for confusion by using (and enforcing usage of) a format that could be clearly and unambiguously understood by anyone.

Since releasing the component we have received a lot of positive feedback about it, but also a small but significant amount of feedback similar to yours: that in certain contexts, a different format might be preferable. We are listening to this, and starting to consider how best to support this.

I would finally add a small note of caution: in my experience any date in the format XX/XX/XXXX is at a relatively high risk of being misinterpreted unless you have a very well-defined customer base, but that’s of course your decision to make in the context of your application.

I’ll leave this request open, and update when we make any progress on this.

awskaran commented 1 month ago

+1 on the feature request