Repository: laboratoriobridge/bold
Pull request #529 (2020-01-29 14:10:18)

Change DateField to gracefully treat nulish values


Patch:
diff --git a/src/components/Calendar/util.ts b/src/components/Calendar/util.ts
index 7dd69a414..bf9b3e242 100644
--- a/src/components/Calendar/util.ts
+++ b/src/components/Calendar/util.ts
@@ -74,3 +74,11 @@ export const createMonthMatrix = (target: Date): Date[][] => {
 export const isSameDay = (d1: Date, d2: Date): boolean => {
   return d1.getDate() === d2.getDate() && d1.getMonth() === d2.getMonth() && d1.getFullYear() === d2.getFullYear()
 }
+
+/**
+ * Check if the parameter is a valid date.
+ */
+export const isValidDate = (possibleDate: any): boolean => {
+  // If possibleDate is not a valid date, getTime() will return NaN, and NaN is never equal to itself.
+  return possibleDate && possibleDate.getTime() === possibleDate.getTime()
+}
diff --git a/src/components/DateField/DateField.tsx b/src/components/DateField/DateField.tsx
index e7caac1c6..cd4111e61 100644
--- a/src/components/DateField/DateField.tsx
+++ b/src/components/DateField/DateField.tsx
@@ -6,7 +6,7 @@ import { Theme } from '../../styles'
 import { useStyles } from '../../styles/hooks/useStyles'
 import { composeHandlers } from '../../util/react'
 import { Calendar, CalendarProps } from '../Calendar'
-import { isSameDay } from '../Calendar/util'
+import { isSameDay, isValidDate } from '../Calendar/util'
 import { FocusManagerContainer } from '../FocusManagerContainer'
 
 import { DateInput, DateInputProps } from './DateInput'
@@ -44,7 +44,11 @@ export function DateField(props: DateFieldProps) {
   const [open, setOpen] = useState(false)
 
   const [visibleDate, setVisibleDate] = useState(new Date())
-  useEffect(() => setVisibleDate(value ?? new Date()), [value])
+  useEffect(() => {
+    if (visibleDate !== value) {
+      setVisibleDate(isValidDate(value) ? value : new Date())
+    }
+  }, [value])
 
   const { style: popperStyle, placement } = usePopper(
     {


Test patch:
diff --git a/src/components/DateField/DateField.test.tsx b/src/components/DateField/DateField.test.tsx
index cc7e183a5..49fed8ea0 100644
--- a/src/components/DateField/DateField.test.tsx
+++ b/src/components/DateField/DateField.test.tsx
@@ -24,6 +24,24 @@ describe('DateField', () => {
     )
     expect(container).toMatchSnapshot()
   })
+  ;['', null, false, undefined].map((value: any) => {
+    it(`should gracefully treat "${value}" as value`, () => {
+      const { container } = render(<DateField value={value} />)
+      fireEvent.focus(container.querySelector('input'))
+    })
+  })
+  it('should open the Calendar visibleDate as the current value', () => {
+    const { container, queryByText, rerender } = render(<DateField value={new Date('2015-10-10')} />)
+    fireEvent.focus(container.querySelector('input'))
+    expect(queryByText('Oct')).not.toBeNull()
+    expect(queryByText('2015')).not.toBeNull()
+
+    rerender(<DateField value={new Date('2020-01-01')} />)
+    expect(queryByText('Oct')).toBeNull()
+    expect(queryByText('2015')).toBeNull()
+    expect(queryByText('Jan')).not.toBeNull()
+    expect(queryByText('2020')).not.toBeNull()
+  })
   it('should set the disabled modifier when using minDate and maxDate props', () => {
     const spy = jest.spyOn(DateFieldModule, 'disableByRange')
     const { container } = render(