diff --git a/packages/react/src/components/datepicker/datepicker.test.tsx b/packages/react/src/components/datepicker/datepicker.test.tsx index d9b3505280..a96ec934a9 100644 --- a/packages/react/src/components/datepicker/datepicker.test.tsx +++ b/packages/react/src/components/datepicker/datepicker.test.tsx @@ -1,10 +1,10 @@ import { getByTestId } from '@design-elements/test-utils/enzyme-selectors'; import { mount } from 'enzyme'; -import React from 'react'; +import React, { RefObject } from 'react'; -import { renderWithProviders } from '../../test-utils/renderer'; +import { actAndWaitForEffects, renderWithProviders } from '../../test-utils/renderer'; import { ThemeWrapped } from '../../test-utils/theme-wrapped'; -import { Datepicker } from './datepicker'; +import { Datepicker, DatepickerHandles } from './datepicker'; jest.mock('uuid/v4'); describe('Datepicker', () => { @@ -174,6 +174,34 @@ describe('Datepicker', () => { expect(getByTestId(wrapper, 'text-input').props().value).toBe(new Date().toLocaleDateString('en-CA')); }); + test('should reset date to startDate when reset is called on ref', async () => { + const ref: RefObject = React.createRef(); + const wrapper = mount( + ThemeWrapped(), + ); + + getByTestId(wrapper, 'text-input').simulate('change', { target: { value: '2010-07-07' } }); + + await actAndWaitForEffects(wrapper, () => { + ref.current?.reset(); + }); + + expect(getByTestId(wrapper, 'text-input').props().value).toBe('2002-02-02'); + }); + + test('should set date when setDate is called on ref', async () => { + const ref: RefObject = React.createRef(); + const wrapper = mount( + ThemeWrapped(), + ); + + await actAndWaitForEffects(wrapper, () => { + ref.current?.setDate(new Date('2002-02-02, 12:00')); + }); + + expect(getByTestId(wrapper, 'text-input').props().value).toBe('2002-02-02'); + }); + test('matches snapshot (desktop)', () => { const tree = renderWithProviders(, 'desktop'); diff --git a/packages/react/src/components/datepicker/datepicker.tsx b/packages/react/src/components/datepicker/datepicker.tsx index c58c61d21e..56223f0036 100644 --- a/packages/react/src/components/datepicker/datepicker.tsx +++ b/packages/react/src/components/datepicker/datepicker.tsx @@ -5,6 +5,7 @@ import React, { forwardRef, KeyboardEvent, MouseEvent, + ReactElement, Ref, useEffect, useImperativeHandle, @@ -234,6 +235,11 @@ const ReactDatePickerStyles = createGlobalStyle` export type SupportedLocale = 'fr-CA' | 'en-CA' | 'en-US'; export type DayOfWeek = 0 | 1 | 2 | 3 | 4 | 5 | 6; +export interface DatepickerHandles { + reset(): void; + setDate(date: Date): void; +} + interface StyledDatePickerProps extends ReactDatePickerProps { isMobile: boolean; theme: Theme; @@ -246,10 +252,6 @@ interface CalendarButtonProps { isMobile?: boolean; } -export interface DatepickerHandles { - reset(): void; -} - interface DatepickerProps { /** Sets date format (e.g.: dd/MM/yyyy). */ dateFormat?: string; @@ -330,7 +332,7 @@ export const Datepicker = forwardRef(({ validationErrorMessage, hint, ...props -}: DatepickerProps, ref: Ref) => { +}: DatepickerProps, ref: Ref): ReactElement => { const { t } = useTranslation('datepicker'); const localeArray = [ enUS, enCA, frCA ]; const { isMobile } = useDeviceContext(); @@ -348,6 +350,9 @@ export const Datepicker = forwardRef(({ reset: () => { setSelectedDate(startDate); }, + setDate: (date: Date) => { + setSelectedDate(date); + }, })); useEffect(() => { diff --git a/packages/react/src/index.ts b/packages/react/src/index.ts index 956313e438..aa8ab47a76 100644 --- a/packages/react/src/index.ts +++ b/packages/react/src/index.ts @@ -6,7 +6,7 @@ export { ToggleButtonGroup } from './components/toggle-button-group/toggle-butto // Form Elements export { CheckboxGroup } from './components/checkbox-group/checkbox-group'; -export { Datepicker } from './components/datepicker/datepicker'; +export { Datepicker, DatepickerHandles } from './components/datepicker/datepicker'; export { MoneyInput } from './components/money-input/money-input'; export { OptionButton } from './components/option-button/option-button'; export { RadioButtonGroup } from './components/radio-button-group/radio-button-group'; diff --git a/packages/react/src/test-utils/renderer.tsx b/packages/react/src/test-utils/renderer.tsx index 77ef911f25..13f8009e95 100644 --- a/packages/react/src/test-utils/renderer.tsx +++ b/packages/react/src/test-utils/renderer.tsx @@ -1,6 +1,7 @@ import { ThemeWrapped } from '@design-elements/test-utils/theme-wrapped'; -import { mount, ReactWrapper, render } from 'enzyme'; +import { CommonWrapper, mount, ReactWrapper, render } from 'enzyme'; import React, { Component, ReactElement } from 'react'; +import { act } from 'react-dom/test-utils'; import { MemoryRouter } from 'react-router-dom'; import { DeviceContextProvider, DeviceType } from '../components/device-context-provider/device-context-provider'; @@ -28,3 +29,20 @@ export function AllProviders({ children, device }: { children: ReactElement, dev ); } + +export async function actUpdate( + wrapper: CommonWrapper, +): Promise { + await act(async () => { + wrapper.update(); + }); +} + +export async function actAndWaitForEffects( + wrapper: ReactWrapper, + action: () => void, +): Promise> { + await act(async () => action()); + await actUpdate(wrapper); + return wrapper; +} diff --git a/packages/storybook/stories/datepicker.stories.tsx b/packages/storybook/stories/datepicker.stories.tsx index e7eefe493a..c5b97d5dfe 100644 --- a/packages/storybook/stories/datepicker.stories.tsx +++ b/packages/storybook/stories/datepicker.stories.tsx @@ -24,51 +24,66 @@ export const normal = () => ( export const withTodayButton = () => ( ); + export const disabled = () => ( ); + export const customDateFormat = () => ( ); + export const customPlaceholder = () => ( ); + export const invalid = () => ( ); + export const maxDate = () => ( ); + export const minDate = () => ( ); + export const readOnly = () => ( ); + export const required = () => (
event.preventDefault()}> ); + export const startOpen = () => ( ); + export const startDate = () => ( ); + export const customLocale = () => ( ); + export const withOnChangeCallback = () => ( console.log(`[onChange] Date: ${date}`)}/> ); + export const withOnBlurCallback = () => ( console.log(`[onBlur] Value: ${event.target.value}`)}/> ); + export const withOnFocusCallback = () => ( console.log(`[onFocus] Value: ${event.target.value}`)}/> ); + export const withDatepickerResetHandle = () => { const datepickerRef = useRef(null); @@ -83,3 +98,18 @@ export const withDatepickerResetHandle = () => { ); }; + +export const withDatepickerSetDateHandle = () => { + const datepickerRef = useRef(null); + + function handleClick(): void { + datepickerRef.current?.setDate(new Date()); + } + + return ( + <> + +