From ec0858fa43614a367bf353944f1309c21c34b1cd Mon Sep 17 00:00:00 2001 From: Andrey Medvedev Date: Thu, 7 Dec 2023 11:58:29 +0300 Subject: [PATCH] fix(DateRangeInput): Treat array of nulls as empty value to set start date on click (#6215) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Правим пример в сторибуке, чтобы DateRangeInput реагировал на изменения дат. - исправляем поведение при выборе даты, если value={[null, null]}. В этом случае onChange возвращал всегда [null, null], что бы мы не выбрали в DateRangeInput. Временное решение без этого фикса это передавать `undefined`. Будет актуально для v5, если не выпустим там патч. - Меняем тип value c Array на Tuple, так как value может содержать только два элемента. --- .../CalendarRange/CalendarRange.stories.tsx | 21 +++++++++++++++++-- .../CalendarRange/CalendarRange.test.tsx | 15 +++++++++++++ .../CalendarRange/CalendarRange.tsx | 15 +++++++------ .../DateRangeInput/DateRangeInput.stories.tsx | 21 +++++++++++++++++-- .../DateRangeInput/DateRangeInput.tsx | 4 ++-- 5 files changed, 64 insertions(+), 12 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.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]); + }); }); diff --git a/packages/vkui/src/components/CalendarRange/CalendarRange.tsx b/packages/vkui/src/components/CalendarRange/CalendarRange.tsx index 958b7855e5..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,8 +107,9 @@ export const CalendarRange = ({ ); const getNewValue = React.useCallback( - (date: Date) => { - if (!value) { + (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.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 ( + + ); }, }; 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();