Skip to content

Commit

Permalink
feat(Datepicker): Add setDate() ref handle
Browse files Browse the repository at this point in the history
  • Loading branch information
maxime-gendron committed Nov 17, 2020
1 parent d83178f commit 6557b98
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 10 deletions.
34 changes: 31 additions & 3 deletions packages/react/src/components/datepicker/datepicker.test.tsx
Original file line number Diff line number Diff line change
@@ -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', () => {
Expand Down Expand Up @@ -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<DatepickerHandles> = React.createRef();
const wrapper = mount(
ThemeWrapped(<Datepicker ref={ref} startDate={new Date('2002-02-02, 12:00')} />),
);

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<DatepickerHandles> = React.createRef();
const wrapper = mount(
ThemeWrapped(<Datepicker ref={ref} />),
);

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(<Datepicker label="date"/>, 'desktop');

Expand Down
15 changes: 10 additions & 5 deletions packages/react/src/components/datepicker/datepicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import React, {
forwardRef,
KeyboardEvent,
MouseEvent,
ReactElement,
Ref,
useEffect,
useImperativeHandle,
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -330,7 +332,7 @@ export const Datepicker = forwardRef(({
validationErrorMessage,
hint,
...props
}: DatepickerProps, ref: Ref<DatepickerHandles>) => {
}: DatepickerProps, ref: Ref<DatepickerHandles>): ReactElement => {
const { t } = useTranslation('datepicker');
const localeArray = [ enUS, enCA, frCA ];
const { isMobile } = useDeviceContext();
Expand All @@ -348,6 +350,9 @@ export const Datepicker = forwardRef(({
reset: () => {
setSelectedDate(startDate);
},
setDate: (date: Date) => {
setSelectedDate(date);
},
}));

useEffect(() => {
Expand Down
2 changes: 1 addition & 1 deletion packages/react/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
20 changes: 19 additions & 1 deletion packages/react/src/test-utils/renderer.tsx
Original file line number Diff line number Diff line change
@@ -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';

Expand Down Expand Up @@ -28,3 +29,20 @@ export function AllProviders({ children, device }: { children: ReactElement, dev
</MemoryRouter>
);
}

export async function actUpdate<C extends Component, P = C['props'], S = C['state']>(
wrapper: CommonWrapper<P, S, C>,
): Promise<void> {
await act(async () => {
wrapper.update();
});
}

export async function actAndWaitForEffects<C extends Component, P = C['props'], S = C['state']>(
wrapper: ReactWrapper<P, S, C>,
action: () => void,
): Promise<ReactWrapper<P, S, C>> {
await act(async () => action());
await actUpdate(wrapper);
return wrapper;
}
30 changes: 30 additions & 0 deletions packages/storybook/stories/datepicker.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,51 +24,66 @@ export const normal = () => (
export const withTodayButton = () => (
<Datepicker label="Date" hasTodayButton/>
);

export const disabled = () => (
<Datepicker label="Date" disabled/>
);

export const customDateFormat = () => (
<Datepicker label="Date" dateFormat="dd/MM"/>
);

export const customPlaceholder = () => (
<Datepicker label="Date" placeholder="Hello World"/>
);

export const invalid = () => (
<Datepicker label="Date" valid={false}/>
);

export const maxDate = () => (
<Datepicker label="Date" maxDate={upcomingDate}/>
);

export const minDate = () => (
<Datepicker label="Date" minDate={currentDate}/>
);

export const readOnly = () => (
<Datepicker label="Date" value="2002-02-02" readOnly/>
);

export const required = () => (
<form onSubmit={(event: FormEvent) => event.preventDefault()}>
<Datepicker label="Date" required/>
<button type="submit">Submit</button>
</form>
);

export const startOpen = () => (
<Datepicker label="Date" startOpen/>
);

export const startDate = () => (
<Datepicker label="Date" startDate={new Date('1995-05-05')}/>
);

export const customLocale = () => (
<Datepicker label="Date" locale="fr-CA"/>
);

export const withOnChangeCallback = () => (
<Datepicker label="Date" onChange={(date) => console.log(`[onChange] Date: ${date}`)}/>
);

export const withOnBlurCallback = () => (
<Datepicker label="Date" onBlur={(event) => console.log(`[onBlur] Value: ${event.target.value}`)}/>
);

export const withOnFocusCallback = () => (
<Datepicker label="Date" onFocus={(event) => console.log(`[onFocus] Value: ${event.target.value}`)}/>
);

export const withDatepickerResetHandle = () => {
const datepickerRef = useRef<DatepickerHandles>(null);

Expand All @@ -83,3 +98,18 @@ export const withDatepickerResetHandle = () => {
</>
);
};

export const withDatepickerSetDateHandle = () => {
const datepickerRef = useRef<DatepickerHandles>(null);

function handleClick(): void {
datepickerRef.current?.setDate(new Date());
}

return (
<>
<Datepicker ref={datepickerRef}/>
<Button buttonType="primary" label="Set to today" onClick={handleClick}/>
</>
);
};

0 comments on commit 6557b98

Please sign in to comment.