diff --git a/packages/vkui/src/components/DateInput/DateInput.test.tsx b/packages/vkui/src/components/DateInput/DateInput.test.tsx index 01b4650de5..a77be4e24e 100644 --- a/packages/vkui/src/components/DateInput/DateInput.test.tsx +++ b/packages/vkui/src/components/DateInput/DateInput.test.tsx @@ -123,4 +123,27 @@ describe('DateInput', () => { expect(container.contains(document.activeElement)).toBeFalsy(); }); + + it('should call onCloseCalendar calendar was closed', async () => { + jest.useFakeTimers(); + const onCalendarOpenChanged = jest.fn(); + const { container } = render( + , + ); + const inputLikes = getInputsLike(container); + + const [dates] = inputLikes; + + await userEvent.click(dates); + expect(onCalendarOpenChanged).toHaveBeenCalledTimes(1); + expect(onCalendarOpenChanged.mock.calls[0][0]).toBeTruthy(); + + expect(container.contains(document.activeElement)).toBeTruthy(); + await userEvent.click(screen.getByText(`${date.getDate() - 1}`)); + + expect(onCalendarOpenChanged).toHaveBeenCalledTimes(2); + expect(onCalendarOpenChanged.mock.calls[1][0]).toBeFalsy(); + + expect(container.contains(document.activeElement)).toBeFalsy(); + }); }); diff --git a/packages/vkui/src/components/DateInput/DateInput.tsx b/packages/vkui/src/components/DateInput/DateInput.tsx index 9c2b1674c2..85b23db213 100644 --- a/packages/vkui/src/components/DateInput/DateInput.tsx +++ b/packages/vkui/src/components/DateInput/DateInput.tsx @@ -62,6 +62,7 @@ export interface DateInputProps clearFieldLabel?: string; showCalendarLabel?: string; disableCalendar?: boolean; + onCalendarOpenChanged?: (opened: boolean) => void; } const elementsConfig = (index: number) => { @@ -147,6 +148,7 @@ export const DateInput = ({ nextMonthIcon, disableCalendar = false, renderDayContent, + onCalendarOpenChanged, ...props }: DateInputProps): React.ReactNode => { const daysRef = React.useRef(null); @@ -205,6 +207,7 @@ export const DateInput = ({ onInternalValueChange, getInternalValue, value, + onCalendarOpenChanged, }); const { sizeY = 'none' } = useAdaptivity(); diff --git a/packages/vkui/src/components/DateRangeInput/DateRangeInput.test.tsx b/packages/vkui/src/components/DateRangeInput/DateRangeInput.test.tsx index 61b16432d6..b30571b360 100644 --- a/packages/vkui/src/components/DateRangeInput/DateRangeInput.test.tsx +++ b/packages/vkui/src/components/DateRangeInput/DateRangeInput.test.tsx @@ -138,4 +138,27 @@ describe('DateRangeInput', () => { expect(onChange).toBeCalledTimes(0); }); + + it('should call onCalendarClose callback when calendar was closed', async () => { + jest.useFakeTimers(); + const onCalendarOpenChanged = jest.fn(); + const { container } = render( + , + ); + const inputLikes = getInputsLike(container); + const [dates] = inputLikes; + + await userEvent.click(dates); + + expect(onCalendarOpenChanged).toHaveBeenCalledTimes(1); + expect(onCalendarOpenChanged.mock.calls[0][0]).toBeTruthy(); + + expect(container.contains(document.activeElement)).toBeTruthy(); + await userEvent.click(screen.getAllByText('15')[1]); + + expect(onCalendarOpenChanged).toHaveBeenCalledTimes(2); + expect(onCalendarOpenChanged.mock.calls[1][0]).toBeFalsy(); + + expect(container.contains(document.activeElement)).toBeFalsy(); + }); }); diff --git a/packages/vkui/src/components/DateRangeInput/DateRangeInput.tsx b/packages/vkui/src/components/DateRangeInput/DateRangeInput.tsx index 537a269b14..f1be9e347a 100644 --- a/packages/vkui/src/components/DateRangeInput/DateRangeInput.tsx +++ b/packages/vkui/src/components/DateRangeInput/DateRangeInput.tsx @@ -53,6 +53,7 @@ export interface DateRangeInputProps Omit { calendarPlacement?: PlacementWithAuto; closeOnChange?: boolean; + onCalendarOpenChanged?: (opened: boolean) => void; clearFieldLabel?: string; showCalendarLabel?: string; changeStartDayLabel?: string; @@ -140,6 +141,7 @@ export const DateRangeInput = ({ prevMonthIcon, nextMonthIcon, disableCalendar = false, + onCalendarOpenChanged, renderDayContent, ...props }: DateRangeInputProps): React.ReactNode => { @@ -221,6 +223,7 @@ export const DateRangeInput = ({ onInternalValueChange, getInternalValue, value, + onCalendarOpenChanged, }); const { sizeY = 'none' } = useAdaptivity(); diff --git a/packages/vkui/src/hooks/useDateInput.ts b/packages/vkui/src/hooks/useDateInput.ts index d6ff9befe9..33565cc36d 100644 --- a/packages/vkui/src/hooks/useDateInput.ts +++ b/packages/vkui/src/hooks/useDateInput.ts @@ -1,3 +1,4 @@ +import { useCallback } from 'react'; import * as React from 'react'; import { useDOM } from '../lib/dom'; import { useBooleanState } from './useBooleanState'; @@ -17,6 +18,7 @@ export interface UseDateInputDependencies { onInternalValueChange: (value: string[]) => void; getInternalValue: (value?: D | undefined) => string[]; onChange?: (value?: D | undefined) => void; + onCalendarOpenChanged?: (opened: boolean) => void; } export function useDateInput({ @@ -29,6 +31,7 @@ export function useDateInput({ onInternalValueChange, getInternalValue, value, + onCalendarOpenChanged, }: UseDateInputDependencies): { rootRef: React.RefObject; calendarRef: React.RefObject; @@ -51,14 +54,28 @@ export function useDateInput({ const [focusedElement, setFocusedElement] = React.useState(null); const { window } = useDOM(); + const _onCalendarClose = useCallback(() => { + if (open) { + closeCalendar(); + onCalendarOpenChanged?.(false); + } + }, [closeCalendar, onCalendarOpenChanged, open]); + + const _onCalendarOpen = useCallback(() => { + if (!open) { + openCalendar(); + onCalendarOpenChanged?.(true); + } + }, [onCalendarOpenChanged, open, openCalendar]); + const removeFocusFromField = React.useCallback(() => { if (focusedElement !== null) { setFocusedElement(null); - closeCalendar(); + _onCalendarClose(); window!.getSelection()?.removeAllRanges(); setInternalValue(getInternalValue(value)); } - }, [focusedElement, closeCalendar, getInternalValue, value, window]); + }, [focusedElement, _onCalendarClose, window, getInternalValue, value]); const handleClickOutside = React.useCallback( (e: MouseEvent) => { @@ -101,14 +118,14 @@ export function useDateInput({ if (element) { element.focus(); - openCalendar(); + _onCalendarOpen(); range.selectNodeContents(element as Node); const selection = window!.getSelection(); selection?.removeAllRanges(); selection?.addRange(range); } - }, [disabled, focusedElement, openCalendar, refs, window]); + }, [disabled, focusedElement, _onCalendarOpen, refs, window]); const clear = React.useCallback(() => { onChange?.(undefined); @@ -190,8 +207,8 @@ export function useDateInput({ rootRef, calendarRef, open, - openCalendar, - closeCalendar, + openCalendar: _onCalendarOpen, + closeCalendar: _onCalendarClose, internalValue, focusedElement, setFocusedElement,