@@ -24,15 +24,26 @@ import { InputHelperText } from './InputHelperText';
2424 * );
2525 *
2626 * @example
27+ * // If the initial value string contains more than a date (e.g. an hour, a timezone),
28+ * // these details are ignored.
29+ * <DateInput source="published_at" defaultValue="2021-09-11T20:46:20.000-04:00" />
30+ * // The input will display '2021-09-11' whatever the browser timezone.
31+ *
32+ * @example
2733 * // If the initial value is a Date object, DateInput converts it to a string
28- * // but you must pass a custom parse method to convert the form value
29- * // (which is always a date string) back to a Date object.
34+ * // and ignores the timezone.
35+ * <DateInput source="published_at" defaultValue={new Date("2021-09-11T20:46:20.000-04:00")} />
36+ * // The input will display '2021-09-11' whatever the browser timezone.
37+ *
38+ * @example
39+ * // If you want the returned value to be a Date, you must pass a custom parse method
40+ * to convert the form value (which is always a date string) back to a Date object.
3041 * <DateInput source="published_at" parse={val => new Date(val)} />
3142 */
3243export const DateInput = ( {
3344 className,
3445 defaultValue,
35- format = getStringFromDate ,
46+ format = defaultFormat ,
3647 label,
3748 source,
3849 resource,
@@ -61,6 +72,7 @@ export const DateInput = ({
6172 const localInputRef = React . useRef < HTMLInputElement > ( ) ;
6273 const initialDefaultValueRef = React . useRef ( field . value ) ;
6374
75+ // update the react-hook-form value if the field value changes
6476 React . useEffect ( ( ) => {
6577 const initialDateValue =
6678 new Date ( initialDefaultValueRef . current ) . getTime ( ) || null ;
@@ -82,8 +94,7 @@ export const DateInput = ({
8294 const hasFocus = React . useRef ( false ) ;
8395
8496 // Update the input text when the user types in the input.
85- // This does not directly update the react-hook-form value,
86- // which is updated on blur and by the useEffect above.
97+ // Also, update the react-hook-form value if the input value is a valid date string.
8798 const handleChange = useEvent (
8899 ( event : React . ChangeEvent < HTMLInputElement > ) => {
89100 if ( onChange ) {
@@ -102,7 +113,8 @@ export const DateInput = ({
102113 ( target . valueAsDate != null &&
103114 ! isNaN ( new Date ( target . valueAsDate ) . getTime ( ) ) ) ;
104115
105- // Some browsers will return null for an invalid date so we only change react-hook-form value if it's not null
116+ // Some browsers will return null for an invalid date
117+ // so we only change react-hook-form value if it's not null.
106118 // The input reset is handled in the onBlur event handler
107119 if ( newValue !== '' && newValue != null && isNewValueValid ) {
108120 field . onChange ( newValue ) ;
@@ -193,7 +205,7 @@ export type DateInputProps = CommonInputProps &
193205 Omit < TextFieldProps , 'helperText' | 'label' > ;
194206
195207/**
196- * Convert Date object to String
208+ * Convert Date object to String, ignoring the timezone.
197209 *
198210 * @param {Date } value value to convert
199211 * @returns {String } A standardized date (yyyy-MM-dd), to be passed to an <input type="date" />
@@ -211,23 +223,45 @@ const convertDateToString = (value: Date) => {
211223const dateRegex = / ^ ( \d { 4 } - \d { 2 } - \d { 2 } ) .* $ / ;
212224const defaultInputLabelProps = { shrink : true } ;
213225
214- const getStringFromDate = ( value : string | Date ) => {
226+ /**
227+ * Convert a form state value to a date string for the `<input type="date">` value.
228+ *
229+ * Form state values can be anything from:
230+ * - a string in the "YYYY-MM-DD" format
231+ * - A valid date string
232+ * - an ISO date string
233+ * - a Date object
234+ * - a Linux timestamp
235+ * - an empty string
236+ *
237+ * The output is always a string in the "YYYY-MM-DD" format.
238+ *
239+ * @example
240+ * defaultFormat('2021-09-11'); // '2021-09-11'
241+ * defaultFormat('09/11/2021'); // '2021-09-11'
242+ * defaultFormat('2021-09-11T20:46:20.000Z'); // '2021-09-11'
243+ * defaultFormat(new Date('2021-09-11T20:46:20.000Z')); // '2021-09-11'
244+ * defaultFormat(1631385980000); // '2021-09-11'
245+ * defaultFormat(''); // null
246+ */
247+ const defaultFormat = ( value : string | Date | number ) => {
215248 // null, undefined and empty string values should not go through dateFormatter
216249 // otherwise, it returns undefined and will make the input an uncontrolled one.
217250 if ( value == null || value === '' ) {
218251 return null ;
219252 }
220253
254+ // Date objects should be converted to strings
221255 if ( value instanceof Date ) {
222256 return convertDateToString ( value ) ;
223257 }
224258
225- // valid dates should not be converted
226- // This regex only returns the date part of the provided string
259+ // Valid date strings should be stripped of their time and timezone parts.
227260 const matches = dateRegex . exec ( value ) ;
228261 if ( matches ) {
229262 return matches [ 1 ] ;
230263 }
231264
265+ // other values (e.g., localized date strings, timestamps) need to be converted to Dates first
232266 return convertDateToString ( new Date ( value ) ) ;
233267} ;
0 commit comments