Skip to content

Commit

Permalink
fix(Datepicker): expose focus() and clear() methods via ref.
Browse files Browse the repository at this point in the history
Closes #1299
  • Loading branch information
MiroslavPetrik committed Mar 15, 2024
1 parent 31c9d9d commit c8e46ba
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 22 deletions.
32 changes: 31 additions & 1 deletion src/components/Datepicker/Datepicker.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { render, screen } from '@testing-library/react';
import { act, render, renderHook, screen } from '@testing-library/react';
import { describe, expect, it, vi } from 'vitest';
import type { DatepickerRef } from './Datepicker';
import { Datepicker } from './Datepicker';
import { getFormattedDate } from './helpers';
import userEvent from '@testing-library/user-event';
import { useRef } from 'react';

describe('Components / Datepicker', () => {
it("should display today's date by default", () => {
Expand Down Expand Up @@ -74,4 +76,32 @@ describe('Components / Datepicker', () => {
await userEvent.click(screen.getByRole('textbox'));
await userEvent.click(document.body);
});

it('should focus the input when ref.current.focus is called', () => {
const {
result: { current: ref },
} = renderHook(() => useRef<DatepickerRef>(null));
render(<Datepicker ref={ref} />);

act(() => ref.current?.focus());

expect(screen.getByRole('textbox')).toHaveFocus();
});

it('should clear the value when ref.current.clear is called', async () => {
const todaysDateInDefaultLanguage = getFormattedDate('en', new Date());
const todaysDayOfMonth = new Date().getDate();
const anotherDay = todaysDayOfMonth === 1 ? 2 : 1;

const {
result: { current: ref },
} = renderHook(() => useRef<DatepickerRef>(null));
render(<Datepicker ref={ref} />);

await userEvent.click(screen.getByRole('textbox'));
await userEvent.click(screen.getAllByText(anotherDay)[0]);
act(() => ref.current?.clear());

expect(screen.getByDisplayValue(todaysDateInDefaultLanguage)).toBeInTheDocument();
});
});
74 changes: 53 additions & 21 deletions src/components/Datepicker/Datepicker.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use client';

import type { FC, ReactNode } from 'react';
import { useEffect, useRef, useState } from 'react';
import type { ForwardRefRenderFunction, ReactNode } from 'react';
import { forwardRef, useEffect, useImperativeHandle, useRef, useState } from 'react';
import { HiArrowLeft, HiArrowRight, HiCalendar } from 'react-icons/hi';
import { twMerge } from 'tailwind-merge';
import { mergeDeep } from '../../helpers/merge-deep';
Expand Down Expand Up @@ -71,6 +71,17 @@ export interface FlowbiteDatepickerPopupTheme {
};
}

export interface DatepickerRef {
/**
* Focus the datepicker input.
*/
focus: () => void;
/**
* Clears the datepicker value back to the defaultDate.
*/
clear: () => void;
}

export interface DatepickerProps extends Omit<TextInputProps, 'theme'> {
open?: boolean;
inline?: boolean;
Expand All @@ -88,25 +99,28 @@ export interface DatepickerProps extends Omit<TextInputProps, 'theme'> {
onSelectedDateChanged?: (date: Date) => void;
}

export const Datepicker: FC<DatepickerProps> = ({
title,
open,
inline = false,
autoHide = true, // Hide when selected the day
showClearButton = true,
labelClearButton = 'Clear',
showTodayButton = true,
labelTodayButton = 'Today',
defaultDate = new Date(),
minDate,
maxDate,
language = 'en',
weekStart = WeekStart.Sunday,
className,
theme: customTheme = {},
onSelectedDateChanged,
...props
}) => {
const DatepickerRender: ForwardRefRenderFunction<DatepickerRef, DatepickerProps> = (
{
title,
open,
inline = false,
autoHide = true, // Hide when selected the day
showClearButton = true,
labelClearButton = 'Clear',
showTodayButton = true,
labelTodayButton = 'Today',
defaultDate = new Date(),
minDate,
maxDate,
language = 'en',
weekStart = WeekStart.Sunday,
className,
theme: customTheme = {},
onSelectedDateChanged,
...props
},
ref,
) => {
const theme = mergeDeep(getTheme().datepicker, customTheme);

// Default date should respect the range
Expand Down Expand Up @@ -135,6 +149,22 @@ export const Datepicker: FC<DatepickerProps> = ({
}
};

const clearDate = () => {
changeSelectedDate(defaultDate, true);
if (defaultDate) {
setViewDate(defaultDate);
}
};

useImperativeHandle(ref, () => ({
focus() {
inputRef.current?.focus();
},
clear() {
clearDate();
},
}));

// Render the DatepickerView* node
const renderView = (type: Views): ReactNode => {
switch (type) {
Expand Down Expand Up @@ -325,4 +355,6 @@ export const Datepicker: FC<DatepickerProps> = ({
);
};

export const Datepicker = forwardRef(DatepickerRender);

Datepicker.displayName = 'Datepicker';

0 comments on commit c8e46ba

Please sign in to comment.