diff --git a/react/src/Calendar/Calendar.stories.tsx b/react/src/Calendar/Calendar.stories.tsx index 8d50eb87..5456e72e 100644 --- a/react/src/Calendar/Calendar.stories.tsx +++ b/react/src/Calendar/Calendar.stories.tsx @@ -1,5 +1,6 @@ import { useControllableState } from '@chakra-ui/react' import { Meta, StoryFn } from '@storybook/react' +import { userEvent, within } from '@storybook/testing-library' import { isWeekend } from 'date-fns' import { mockDateDecorator } from '~/utils/storybook' @@ -7,7 +8,7 @@ import { mockDateDecorator } from '~/utils/storybook' import { Calendar, CalendarProps } from './Calendar' export default { - title: 'Components/Calendar/Calendar', + title: 'Components/Calendar', component: Calendar, tags: ['autodocs'], decorators: [mockDateDecorator], @@ -39,6 +40,16 @@ CalendarWithValue.args = { value: new Date('2001-01-01'), } +export const SelectTodayWhenTodayButtonClicked = CalendarOnlyTemplate.bind({}) +SelectTodayWhenTodayButtonClicked.args = { + shouldSetDateOnTodayButtonClick: true, +} +SelectTodayWhenTodayButtonClicked.play = async ({ canvasElement }) => { + const canvas = within(canvasElement) + const todayButton = canvas.getByText('Today') + userEvent.click(todayButton) +} + export const HideOutsideDays = CalendarOnlyTemplate.bind({}) HideOutsideDays.args = { showOutsideDays: false, diff --git a/react/src/Calendar/CalendarBase/CalendarContext.tsx b/react/src/Calendar/CalendarBase/CalendarContext.tsx index 54a778cc..f85c7373 100644 --- a/react/src/Calendar/CalendarBase/CalendarContext.tsx +++ b/react/src/Calendar/CalendarBase/CalendarContext.tsx @@ -13,6 +13,7 @@ import { differenceInCalendarMonths, isFirstDayOfMonth, isSameDay, + startOfDay, } from 'date-fns' import { Props as DayzedProps, RenderProps, useDayzed } from 'dayzed' import { inRange } from 'lodash' @@ -85,6 +86,11 @@ type PassthroughProps = { * @default false */ isCalendarFixedHeight?: boolean + /** + * Whether clicking the Today button should set the date on the input to today. + * @default false + */ + shouldSetDateOnTodayButtonClick?: boolean } // Removed - and _ from alphabets for simpler classnames @@ -163,6 +169,7 @@ const useProvideCalendar = ({ defaultFocusedDate, showOutsideDays, isCalendarFixedHeight, + shouldSetDateOnTodayButtonClick, }: UseProvideCalendarProps) => { const isMobile = useIsMobile({ ssr }) // Ensure that calculations are always made based on date of initial render, @@ -216,7 +223,11 @@ const useProvideCalendar = ({ ) as HTMLButtonElement | null elementToFocus?.focus() }) - }, [classNameId]) + + if (shouldSetDateOnTodayButtonClick) { + onSelectDate?.(startOfDay(today)) + } + }, [classNameId, onSelectDate, shouldSetDateOnTodayButtonClick]) const updateMonthYear = useCallback( (newDate: Date) => { diff --git a/react/src/Calendar/CalendarBase/types.ts b/react/src/Calendar/CalendarBase/types.ts index d368919f..2e35666a 100644 --- a/react/src/Calendar/CalendarBase/types.ts +++ b/react/src/Calendar/CalendarBase/types.ts @@ -8,6 +8,7 @@ export type CalendarBaseProps = Pick< | 'size' | 'defaultFocusedDate' | 'isCalendarFixedHeight' + | 'shouldSetDateOnTodayButtonClick' > & { /** * Whether to show or hide the button to focus on Today. diff --git a/react/src/Calendar/RangeCalendar.stories.tsx b/react/src/Calendar/RangeCalendar.stories.tsx index cc01c14a..40d2ce5b 100644 --- a/react/src/Calendar/RangeCalendar.stories.tsx +++ b/react/src/Calendar/RangeCalendar.stories.tsx @@ -1,5 +1,6 @@ import { useControllableState } from '@chakra-ui/react' import { Meta, StoryFn } from '@storybook/react' +import { userEvent, within } from '@storybook/testing-library' import { isWeekend } from 'date-fns' import { mockDateDecorator } from '~/utils/storybook' @@ -7,7 +8,7 @@ import { mockDateDecorator } from '~/utils/storybook' import { RangeCalendar, RangeCalendarProps } from './RangeCalendar' export default { - title: 'Components/Calendar/RangeCalendar', + title: 'Components/RangeCalendar', component: RangeCalendar, tags: ['autodocs'], decorators: [mockDateDecorator], @@ -43,6 +44,18 @@ RangeCalendarWithValue.args = { value: [new Date('2001-01-01'), null], } +export const SelectTodayWhenTodayButtonClicked = RangeCalendarOnlyTemplate.bind( + {}, +) +SelectTodayWhenTodayButtonClicked.args = { + shouldSetDateOnTodayButtonClick: true, +} +SelectTodayWhenTodayButtonClicked.play = async ({ canvasElement }) => { + const canvas = within(canvasElement) + const todayButton = canvas.getByText('Today') + userEvent.click(todayButton) +} + export const RangeCalendarWithRange = RangeCalendarOnlyTemplate.bind({}) RangeCalendarWithRange.args = { value: [new Date('2001-01-01'), new Date('2001-02-02')], diff --git a/react/src/DatePicker/DatePicker.stories.tsx b/react/src/DatePicker/DatePicker.stories.tsx index 35a3a76c..f9c5992d 100644 --- a/react/src/DatePicker/DatePicker.stories.tsx +++ b/react/src/DatePicker/DatePicker.stories.tsx @@ -1,4 +1,5 @@ import { Meta, StoryFn } from '@storybook/react' +import { userEvent, within } from '@storybook/testing-library' import { getMobileViewParameters, mockDateDecorator } from '~/utils/storybook' @@ -22,6 +23,11 @@ DatePickerWithValue.args = { defaultValue: new Date('2001-01-01'), } +export const SelectTodayWhenTodayButtonClicked = Template.bind({}) +SelectTodayWhenTodayButtonClicked.args = { + shouldSetDateOnTodayButtonClick: true, +} + export const DatePickerInvalid = Template.bind({}) DatePickerInvalid.args = { isInvalid: true, diff --git a/react/src/DatePicker/DatePickerContext.tsx b/react/src/DatePicker/DatePickerContext.tsx index 44d510a2..640d5e29 100644 --- a/react/src/DatePicker/DatePickerContext.tsx +++ b/react/src/DatePicker/DatePickerContext.tsx @@ -55,6 +55,7 @@ interface DatePickerContextReturn { | 'defaultFocusedDate' | 'showOutsideDays' | 'showTodayButton' + | 'shouldSetDateOnTodayButtonClick' > inputPattern?: string } @@ -155,7 +156,9 @@ const useProvideDatePicker = ({ isDisabled: isDisabledProp, isReadOnly: isReadOnlyProp, isRequired: isRequiredProp, - ...props, + 'aria-describedby': props['aria-describedby'], + onFocus: props.onFocus, + id: props.id, }) const handleInputBlur: FocusEventHandler = useCallback( diff --git a/react/src/DatePicker/utils/pickCalendarProps.ts b/react/src/DatePicker/utils/pickCalendarProps.ts index e81ea434..327c6f29 100644 --- a/react/src/DatePicker/utils/pickCalendarProps.ts +++ b/react/src/DatePicker/utils/pickCalendarProps.ts @@ -11,5 +11,6 @@ export const pickCalendarProps = (props: DatePickerProps) => { 'defaultFocusedDate', 'showOutsideDays', 'showTodayButton', + 'shouldSetDateOnTodayButtonClick', ) } diff --git a/react/src/DateRangePicker/DateRangePicker.stories.tsx b/react/src/DateRangePicker/DateRangePicker.stories.tsx index 7ecfed43..1ac72630 100644 --- a/react/src/DateRangePicker/DateRangePicker.stories.tsx +++ b/react/src/DateRangePicker/DateRangePicker.stories.tsx @@ -23,6 +23,10 @@ export const DateRangePickerWithValue = Template.bind({}) DateRangePickerWithValue.args = { defaultValue: [new Date('2001-01-01'), null], } +export const SelectTodayWhenTodayButtonClicked = Template.bind({}) +SelectTodayWhenTodayButtonClicked.args = { + shouldSetDateOnTodayButtonClick: true, +} export const DateRangePickerDisallowManualInput = Template.bind({}) DateRangePickerDisallowManualInput.args = { diff --git a/react/src/DateRangePicker/DateRangePickerContext.tsx b/react/src/DateRangePicker/DateRangePickerContext.tsx index 44a61eb0..582d6f6a 100644 --- a/react/src/DateRangePicker/DateRangePickerContext.tsx +++ b/react/src/DateRangePicker/DateRangePickerContext.tsx @@ -60,6 +60,7 @@ interface DateRangePickerContextReturn { | 'isDateUnavailable' | 'defaultFocusedDate' | 'showTodayButton' + | 'shouldSetDateOnTodayButtonClick' > inputPattern?: string } @@ -195,7 +196,9 @@ const useProvideDateRangePicker = ({ isDisabled: isDisabledProp, isReadOnly: isReadOnlyProp, isRequired: isRequiredProp, - ...props, + 'aria-describedby': props['aria-describedby'], + onFocus: props.onFocus, + id: props.id, }) const handleInputBlur: FocusEventHandler = useCallback(