From 8633b12362527148f909ae6421706c30787b87ed Mon Sep 17 00:00:00 2001 From: Andrey Medvedev Date: Mon, 4 Dec 2023 19:52:51 +0300 Subject: [PATCH 1/3] Treat array of nulls as empty value to set start date on click Fix Storybook examples to make them interactive --- .../CalendarRange/CalendarRange.stories.tsx | 21 +++++++++++++++++-- .../CalendarRange/CalendarRange.tsx | 3 ++- .../DateRangeInput/DateRangeInput.stories.tsx | 21 +++++++++++++++++-- 3 files changed, 40 insertions(+), 5 deletions(-) diff --git a/packages/vkui/src/components/CalendarRange/CalendarRange.stories.tsx b/packages/vkui/src/components/CalendarRange/CalendarRange.stories.tsx index 0ecd7b45a6..5eda9b1a36 100644 --- a/packages/vkui/src/components/CalendarRange/CalendarRange.stories.tsx +++ b/packages/vkui/src/components/CalendarRange/CalendarRange.stories.tsx @@ -1,4 +1,5 @@ import * as React from 'react'; +import { useArgs } from '@storybook/preview-api'; import { Meta, StoryObj } from '@storybook/react'; import { CanvasFullLayout, DisableCartesianParam } from '../../storybook/constants'; import { CalendarRange, CalendarRangeProps } from './CalendarRange'; @@ -40,10 +41,26 @@ export default story; type Story = StoryObj; export const Playground: Story = { - render: ({ value, startDate, endDate, ...args }) => { + render: function Render() { + const [{ value, startDate, endDate, ...args }, updateArgs] = useArgs(); + + const handleDateRangeUpdate: CalendarRangeProps['onChange'] = (updatedValue) => { + const [changedStartDate, changedEndDate] = updatedValue || [null, null]; + updateArgs({ + startDate: changedStartDate ? new Date(changedStartDate) : null, + endDate: changedEndDate ? new Date(changedEndDate) : null, + }); + }; + const parsedStartDate = startDate ? new Date(startDate) : null; const parsedEndDate = endDate ? new Date(endDate) : null; - return ; + return ( + + ); }, }; diff --git a/packages/vkui/src/components/CalendarRange/CalendarRange.tsx b/packages/vkui/src/components/CalendarRange/CalendarRange.tsx index 958b7855e5..13eda46781 100644 --- a/packages/vkui/src/components/CalendarRange/CalendarRange.tsx +++ b/packages/vkui/src/components/CalendarRange/CalendarRange.tsx @@ -106,7 +106,8 @@ export const CalendarRange = ({ const getNewValue = React.useCallback( (date: Date) => { - if (!value) { + const isValueEmpty = !value || (value[0] === null && value[1] === null); + if (isValueEmpty) { return [date, null]; } diff --git a/packages/vkui/src/components/DateRangeInput/DateRangeInput.stories.tsx b/packages/vkui/src/components/DateRangeInput/DateRangeInput.stories.tsx index a5d79efa9a..a08261dbe0 100644 --- a/packages/vkui/src/components/DateRangeInput/DateRangeInput.stories.tsx +++ b/packages/vkui/src/components/DateRangeInput/DateRangeInput.stories.tsx @@ -1,4 +1,5 @@ import * as React from 'react'; +import { useArgs } from '@storybook/preview-api'; import { Meta, StoryObj } from '@storybook/react'; import { CanvasFullLayout, DisableCartesianParam } from '../../storybook/constants'; import { DateRangeInput, DateRangeInputProps } from './DateRangeInput'; @@ -40,10 +41,26 @@ export default story; type Story = StoryObj; export const Playground: Story = { - render: ({ value, startDate, endDate, ...args }) => { + render: function Render() { + const [{ value, startDate, endDate, ...args }, updateArgs] = useArgs(); + + const handleDateRangeUpdate: DateRangeInputProps['onChange'] = (updatedValue) => { + const [changedStartDate, changedEndDate] = updatedValue || [null, null]; + updateArgs({ + startDate: changedStartDate ? new Date(changedStartDate) : null, + endDate: changedEndDate ? new Date(changedEndDate) : null, + }); + }; + const parsedStartDate = startDate ? new Date(startDate) : null; const parsedEndDate = endDate ? new Date(endDate) : null; - return ; + return ( + + ); }, }; From 2c2cb9ee5b0f438f48fe82a8799d4276ef9d58a7 Mon Sep 17 00:00:00 2001 From: Andrey Medvedev Date: Wed, 6 Dec 2023 17:14:45 +0300 Subject: [PATCH 2/3] Check that onChange is called with selected date --- .../CalendarRange/CalendarRange.test.tsx | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/packages/vkui/src/components/CalendarRange/CalendarRange.test.tsx b/packages/vkui/src/components/CalendarRange/CalendarRange.test.tsx index 4b757cfa2d..a07a60f3c0 100644 --- a/packages/vkui/src/components/CalendarRange/CalendarRange.test.tsx +++ b/packages/vkui/src/components/CalendarRange/CalendarRange.test.tsx @@ -1,6 +1,21 @@ +import * as React from 'react'; +import { fireEvent, render, screen } from '@testing-library/react'; import { baselineComponent } from '../../testing/utils'; import { CalendarRange } from './CalendarRange'; describe('CalendarRange', () => { baselineComponent(CalendarRange); + + it('calls onChange when initial value is [null, null]', () => { + const onChangeStub = jest.fn(); + render( + , + ); + + fireEvent.click(screen.getAllByText('6')[0]); + expect(onChangeStub).not.toHaveBeenLastCalledWith([null, null]); + + fireEvent.click(screen.getAllByText('6')[1]); + expect(onChangeStub).not.toHaveBeenLastCalledWith([null, null]); + }); }); From 6102b217902f0d3eb7ddffcc9ab14d438b120755 Mon Sep 17 00:00:00 2001 From: Andrey Medvedev Date: Thu, 7 Dec 2023 09:10:31 +0300 Subject: [PATCH 3/3] Convert DateRateInput value type from array to tuple --- .../src/components/CalendarRange/CalendarRange.tsx | 12 +++++++----- .../src/components/DateRangeInput/DateRangeInput.tsx | 4 ++-- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/packages/vkui/src/components/CalendarRange/CalendarRange.tsx b/packages/vkui/src/components/CalendarRange/CalendarRange.tsx index 13eda46781..650ea479e7 100644 --- a/packages/vkui/src/components/CalendarRange/CalendarRange.tsx +++ b/packages/vkui/src/components/CalendarRange/CalendarRange.tsx @@ -17,6 +17,8 @@ import { CalendarHeader, CalendarHeaderProps } from '../CalendarHeader/CalendarH import { RootComponent } from '../RootComponent/RootComponent'; import styles from './CalendarRange.module.css'; +export type DateRangeType = [Date | null, Date | null]; + export interface CalendarRangeProps extends Omit, 'onChange'>, Pick< @@ -29,18 +31,18 @@ export interface CalendarRangeProps | 'nextMonthIcon' >, Pick { - value?: Array; + value?: DateRangeType; disablePast?: boolean; disableFuture?: boolean; disablePickers?: boolean; changeDayLabel?: string; weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6; - onChange?(value?: Array): void; + onChange?(value?: DateRangeType): void; shouldDisableDate?(value: Date): boolean; onClose?(): void; } -const getIsDaySelected = (day: Date, value?: Array) => { +const getIsDaySelected = (day: Date, value?: DateRangeType) => { if (!value?.[0] || !value[1]) { return false; } @@ -81,7 +83,7 @@ export const CalendarRange = ({ isDayDisabled, resetSelectedDay, } = useCalendar({ value, disableFuture, disablePast, shouldDisableDate }); - const [hintedDate, setHintedDate] = React.useState>(); + const [hintedDate, setHintedDate] = React.useState(); const secondViewDate = addMonths(viewDate, 1); const handleKeyDown = React.useCallback( @@ -105,7 +107,7 @@ export const CalendarRange = ({ ); const getNewValue = React.useCallback( - (date: Date) => { + (date: Date): DateRangeType => { const isValueEmpty = !value || (value[0] === null && value[1] === null); if (isValueEmpty) { return [date, null]; diff --git a/packages/vkui/src/components/DateRangeInput/DateRangeInput.tsx b/packages/vkui/src/components/DateRangeInput/DateRangeInput.tsx index 301a288a5b..b21a619c01 100644 --- a/packages/vkui/src/components/DateRangeInput/DateRangeInput.tsx +++ b/packages/vkui/src/components/DateRangeInput/DateRangeInput.tsx @@ -8,7 +8,7 @@ import { callMultiple } from '../../lib/callMultiple'; import { format, isAfter, isMatch, parse } from '../../lib/date'; import type { PlacementWithAuto } from '../../lib/floating'; import { HasRootRef } from '../../types'; -import { CalendarRange, CalendarRangeProps } from '../CalendarRange/CalendarRange'; +import { CalendarRange, CalendarRangeProps, DateRangeType } from '../CalendarRange/CalendarRange'; import { FormField, FormFieldProps } from '../FormField/FormField'; import { IconButton } from '../IconButton/IconButton'; import { InputLike } from '../InputLike/InputLike'; @@ -221,7 +221,7 @@ export const DateRangeInput = ({ const handleRootRef = useExternRef(rootRef, getRootRef); const onCalendarChange = React.useCallback( - (newValue?: Array | undefined) => { + (newValue?: DateRangeType) => { onChange?.(newValue); if (closeOnChange && newValue?.[1] && newValue[1] !== value?.[1]) { removeFocusFromField();