From cccf94a47b843e3db6956c15e4cd868171c9a762 Mon Sep 17 00:00:00 2001 From: Leo Singer Date: Sun, 26 May 2024 21:32:04 -0400 Subject: [PATCH] Handle validation for YYYY-MM-DD format --- .../forms/DatePicker/DatePicker.tsx | 12 ++- src/components/forms/DatePicker/utils.test.ts | 79 +++++++++++++------ src/components/forms/DatePicker/utils.tsx | 18 ++++- 3 files changed, 79 insertions(+), 30 deletions(-) diff --git a/src/components/forms/DatePicker/DatePicker.tsx b/src/components/forms/DatePicker/DatePicker.tsx index f2eeff1bbf..a5f563254d 100644 --- a/src/components/forms/DatePicker/DatePicker.tsx +++ b/src/components/forms/DatePicker/DatePicker.tsx @@ -97,7 +97,12 @@ export const DatePicker = ({ const parsedRangeDate = rangeDate ? parseDateString(rangeDate) : undefined const validateInput = (): void => { - const isInvalid = isDateInvalid(externalValue, parsedMinDate, parsedMaxDate) + const isInvalid = isDateInvalid( + externalValue, + dateFormat, + parsedMinDate, + parsedMaxDate + ) if (isInvalid && !externalInputEl?.current?.validationMessage) { externalInputEl?.current?.setCustomValidity(VALIDATION_MESSAGE) @@ -134,7 +139,10 @@ export const DatePicker = ({ const inputDate = parseDateString(value, dateFormat, true) let newValue = '' - if (inputDate && !isDateInvalid(value, parsedMinDate, parsedMaxDate)) { + if ( + inputDate && + !isDateInvalid(value, dateFormat, parsedMinDate, parsedMaxDate) + ) { newValue = formatDate(inputDate) } diff --git a/src/components/forms/DatePicker/utils.test.ts b/src/components/forms/DatePicker/utils.test.ts index 8b7ee8eefc..c2f05c3ba0 100644 --- a/src/components/forms/DatePicker/utils.test.ts +++ b/src/components/forms/DatePicker/utils.test.ts @@ -81,34 +81,63 @@ describe('formatDate', () => { }) describe('isDateInvalid', () => { - it('returns false if the date is within the min & max', () => { - const testMin = new Date('May 1, 1988') - const testMax = new Date('June 1, 1988') - expect(isDateInvalid('05/16/1988', testMin, testMax)).toEqual(false) - }) - - it('returns true if the date is not within the min & max', () => { - const testMin = new Date('May 1, 1988') - const testMax = new Date('June 1, 1988') - expect(isDateInvalid('08/16/1988', testMin, testMax)).toEqual(true) - }) - - it('returns true if the date is not valid', () => { - const testMin = new Date('May 1, 1988') - const testMax = new Date('June 1, 1988') - expect(isDateInvalid('not a date', testMin, testMax)).toEqual(true) - }) + it.each([ + ['05/16/1988', DEFAULT_EXTERNAL_DATE_FORMAT], + ['1988-05-16', INTERNAL_DATE_FORMAT], + ] as const)( + 'returns false if the date is within the min & max', + (date, format) => { + const testMin = new Date('May 1, 1988') + const testMax = new Date('June 1, 1988') + expect(isDateInvalid(date, format, testMin, testMax)).toEqual(false) + } + ) + + it.each([ + ['08/16/1988', DEFAULT_EXTERNAL_DATE_FORMAT], + ['1988-08-16', INTERNAL_DATE_FORMAT], + ] as const)( + 'returns true if the date is not within the min & max', + (date, format) => { + const testMin = new Date('May 1, 1988') + const testMax = new Date('June 1, 1988') + expect(isDateInvalid(date, format, testMin, testMax)).toEqual(true) + } + ) + + it.each([DEFAULT_EXTERNAL_DATE_FORMAT, INTERNAL_DATE_FORMAT] as const)( + 'returns true if the date is not valid', + (format) => { + const testMin = new Date('May 1, 1988') + const testMax = new Date('June 1, 1988') + expect(isDateInvalid('not a date', format, testMin, testMax)).toEqual( + true + ) + } + ) describe('with no max date', () => { - it('returns false if the date is after the min', () => { - const testMin = new Date('May 1, 1988') - expect(isDateInvalid('05/16/1988', testMin)).toEqual(false) - }) + it.each([ + ['05/16/1988', DEFAULT_EXTERNAL_DATE_FORMAT], + ['1988-05-16', INTERNAL_DATE_FORMAT], + ] as const)( + 'returns false if the date is after the min', + (date, format) => { + const testMin = new Date('May 1, 1988') + expect(isDateInvalid(date, format, testMin)).toEqual(false) + } + ) - it('returns true if the date is not after the min', () => { - const testMin = new Date('May 1, 1988') - expect(isDateInvalid('02/16/1988', testMin)).toEqual(true) - }) + it.each([ + ['02/16/1988', DEFAULT_EXTERNAL_DATE_FORMAT], + ['1988-02-16', INTERNAL_DATE_FORMAT], + ] as const)( + 'returns true if the date is not after the min', + (date, format) => { + const testMin = new Date('May 1, 1988') + expect(isDateInvalid(date, format, testMin)).toEqual(true) + } + ) }) }) diff --git a/src/components/forms/DatePicker/utils.tsx b/src/components/forms/DatePicker/utils.tsx index a4629ef310..a4e4ad328c 100644 --- a/src/components/forms/DatePicker/utils.tsx +++ b/src/components/forms/DatePicker/utils.tsx @@ -460,6 +460,7 @@ export const formatDate = ( export const isDateInvalid = ( dateString: string, + dateFormat: DateFormat, minDate: Date, maxDate?: Date ): boolean => { @@ -468,14 +469,25 @@ export const isDateInvalid = ( if (dateString) { isInvalid = true - const dateStringParts = dateString.split('/') - const [month, day, year] = dateStringParts.map((str) => { + const dateStringParts = dateString.split( + dateFormat === DEFAULT_EXTERNAL_DATE_FORMAT ? '/' : '-' + ) + const dateParts = dateStringParts.map((str) => { let value const parsed = parseInt(str, 10) if (!Number.isNaN(parsed)) value = parsed return value }) + let month, day, year, yearStringPart + if (dateFormat === DEFAULT_EXTERNAL_DATE_FORMAT) { + yearStringPart = dateStringParts[2] + ;[month, day, year] = dateParts + } else { + yearStringPart = dateStringParts[0] + ;[year, month, day] = dateParts + } + if (month && day && year != null) { const checkDate = setDate(year, month - 1, day) @@ -483,7 +495,7 @@ export const isDateInvalid = ( checkDate.getMonth() === month - 1 && checkDate.getDate() === day && checkDate.getFullYear() === year && - dateStringParts[2].length === 4 && + yearStringPart.length === 4 && isDateWithinMinAndMax(checkDate, minDate, maxDate) ) { isInvalid = false