diff --git a/apps/web/components/eventtype/EventLimitsTab.tsx b/apps/web/components/eventtype/EventLimitsTab.tsx index f3daf7db89..d6b9e2ee86 100644 --- a/apps/web/components/eventtype/EventLimitsTab.tsx +++ b/apps/web/components/eventtype/EventLimitsTab.tsx @@ -474,8 +474,10 @@ export const EventLimitsTab = ({ eventType }: Pick ( { onChange({ diff --git a/apps/web/components/settings/TravelScheduleModal.tsx b/apps/web/components/settings/TravelScheduleModal.tsx index 40e8909f08..2c72d73cfe 100644 --- a/apps/web/components/settings/TravelScheduleModal.tsx +++ b/apps/web/components/settings/TravelScheduleModal.tsx @@ -99,10 +99,13 @@ const TravelScheduleModal = ({ <> { - setStartDate(newStartDate); + // If newStartDate does become undefined - we resort back to to-todays date + setStartDate(newStartDate ?? new Date()); setEndDate(newEndDate); setErrorMessage(""); }} diff --git a/apps/web/pages/settings/my-account/out-of-office/index.tsx b/apps/web/pages/settings/my-account/out-of-office/index.tsx index fbb68df081..85ccbf78a3 100644 --- a/apps/web/pages/settings/my-account/out-of-office/index.tsx +++ b/apps/web/pages/settings/my-account/out-of-office/index.tsx @@ -74,7 +74,7 @@ const CreateOutOfOfficeEntryModal = ({ label: member.name || "", })) || []; - const { handleSubmit, setValue, getValues, control, register } = useForm({ + const { handleSubmit, setValue, control, register } = useForm({ defaultValues: { dateRange: { startDate: dateRange.startDate, @@ -132,15 +132,11 @@ const CreateOutOfOfficeEntryModal = ({ name="dateRange" control={control} defaultValue={dateRange} - render={() => ( + render={({ field: { onChange, value } }) => ( { - setValue("dateRange", { - startDate, - endDate, - }); + dates={{ startDate: value.startDate, endDate: value.endDate }} + onDatesChange={(values) => { + onChange(values); }} /> )} diff --git a/packages/ui/components/form/date-range-picker/Calendar.tsx b/packages/ui/components/form/date-range-picker/Calendar.tsx new file mode 100644 index 0000000000..efc30f62fc --- /dev/null +++ b/packages/ui/components/form/date-range-picker/Calendar.tsx @@ -0,0 +1,66 @@ +"use client"; + +import * as React from "react"; +import { DayPicker } from "react-day-picker"; + +import dayjs from "@calcom/dayjs"; +import { classNames as cn } from "@calcom/lib"; + +import { Icon } from "../../../index"; +import { buttonClasses } from "../../button/Button"; + +export type CalendarProps = React.ComponentProps; + +function Calendar({ className, classNames, showOutsideDays = true, ...props }: CalendarProps) { + return ( + ( +
+ + {dayjs(capLabelProps.displayMonth).format("MMMM")}{" "} + + + {dayjs(capLabelProps.displayMonth).format("YYYY")} + +
+ ), + IconLeft: () => , + IconRight: () => , + }} + {...props} + /> + ); +} +Calendar.displayName = "Calendar"; + +export { Calendar }; diff --git a/packages/ui/components/form/date-range-picker/DateRangePicker.tsx b/packages/ui/components/form/date-range-picker/DateRangePicker.tsx index 4481123bff..5da010d8af 100644 --- a/packages/ui/components/form/date-range-picker/DateRangePicker.tsx +++ b/packages/ui/components/form/date-range-picker/DateRangePicker.tsx @@ -1,35 +1,70 @@ -// @see: https://github.com/wojtekmaj/react-daterange-picker/issues/91 -import "@wojtekmaj/react-daterange-picker/dist/DateRangePicker.css"; -import PrimitiveDateRangePicker from "@wojtekmaj/react-daterange-picker/dist/entry.nostyle"; +"use client"; -import { Icon } from "../../.."; -import "./styles.css"; +import { format } from "date-fns"; +import * as React from "react"; +import type { DateRange } from "react-day-picker"; -type Props = { - disabled?: boolean | undefined; - startDate: Date; - endDate: Date; - onDatesChange?: ((arg: { startDate: Date; endDate: Date }) => void) | undefined; +import { classNames as cn } from "@calcom/lib"; + +import { Button } from "../../button"; +import { Popover, PopoverContent, PopoverTrigger } from "../../popover"; +import { Calendar } from "./Calendar"; + +type DatePickerWithRangeProps = { + dates: { startDate: Date; endDate?: Date }; + onDatesChange: ({ startDate, endDate }: { startDate?: Date; endDate?: Date }) => void; + disabled?: boolean; }; -const DateRangePicker = ({ disabled, startDate, endDate, onDatesChange }: Props) => { +export function DatePickerWithRange({ + className, + dates, + onDatesChange, + disabled, +}: React.HTMLAttributes & DatePickerWithRangeProps) { + // Even though this is uncontrolled we need to do a bit of logic to improve the UX when selecting dates + function _onDatesChange(onChangeValues: DateRange | undefined) { + if (onChangeValues?.from && !onChangeValues?.to) { + onDatesChange({ startDate: onChangeValues.from, endDate: onChangeValues.from }); + } else { + onDatesChange({ startDate: onChangeValues?.from, endDate: onChangeValues?.to }); + } + } + return ( - <> - } - rangeDivider={} - value={[startDate, endDate]} - onChange={([startDate, endDate]: [Date, Date]) => { - if (typeof onDatesChange === "function") onDatesChange({ startDate, endDate }); - }} - nextLabel={} - prevLabel={} - /> - +
+ + + + + + _onDatesChange(values)} + numberOfMonths={1} + disabled={disabled} + /> + + +
); -}; - -export default DateRangePicker; +} diff --git a/packages/ui/components/form/date-range-picker/daterangepicker.test.tsx b/packages/ui/components/form/date-range-picker/daterangepicker.test.tsx deleted file mode 100644 index 2d2d3b1758..0000000000 --- a/packages/ui/components/form/date-range-picker/daterangepicker.test.tsx +++ /dev/null @@ -1,77 +0,0 @@ -/* eslint-disable playwright/missing-playwright-await */ -import { render, fireEvent } from "@testing-library/react"; -import { format } from "date-fns"; -import { vi } from "vitest"; - -import DateRangePicker from "./DateRangePicker"; - -describe("Tests for DateRangePicker component", () => { - const startDate = new Date("2023-07-15"); - const endDate = new Date("2023-07-20"); - - test("Should display the selected start and end dates", () => { - const { getByDisplayValue } = render(); - - const expectedStartDate = format(new Date(startDate), "yyyy-MM-dd"); - const expectedEndDate = format(new Date(endDate), "yyyy-MM-dd"); - - expect(getByDisplayValue(expectedStartDate)).toBeInTheDocument(); - expect(getByDisplayValue(expectedEndDate)).toBeInTheDocument(); - }); - - test("Should disable the DateRangePicker when disabled prop is true", () => { - const { getByDisplayValue } = render( - - ); - - const startDateInput = getByDisplayValue(format(startDate, "yyyy-MM-dd")) as HTMLInputElement; - const endDateInput = getByDisplayValue(format(endDate, "yyyy-MM-dd")) as HTMLInputElement; - expect(startDateInput).toBeDisabled(); - expect(endDateInput).toBeDisabled(); - }); - - test("Should call the onDatesChange callback when the selected dates change", () => { - const startDate = new Date("2023-07-14"); - const endDate = new Date("2023-07-19"); - - const mockOnDatesChange = vi.fn(); - - const { getByDisplayValue } = render( - - ); - - const startDateInput = getByDisplayValue(format(startDate, "yyyy-MM-dd")) as HTMLInputElement; - const endDateInput = getByDisplayValue(format(endDate, "yyyy-MM-dd")) as HTMLInputElement; - - const newStartDate = new Date(startDate); - newStartDate.setDate(newStartDate.getDate() + 1); - - const newEndDate = new Date(endDate); - newEndDate.setDate(newEndDate.getDate() + 1); - - const eventStartDate = { target: { value: format(newStartDate, "yyyy-MM-dd") } }; - const eventEndDate = { target: { value: format(newEndDate, "yyyy-MM-dd") } }; - fireEvent.change(startDateInput, eventStartDate); - fireEvent.change(endDateInput, eventEndDate); - - const formattedStartDate = mockOnDatesChange.mock.calls[0][0].startDate; - const formattedEndDate = mockOnDatesChange.mock.calls[1][0].endDate; - - expect(mockOnDatesChange).toHaveBeenCalled(); - - expect(mockOnDatesChange).toHaveBeenCalledWith({ - startDate: expect.any(Date), - endDate: expect.any(Date), - }); - - expect(formattedStartDate.getDate()).toEqual(newStartDate.getDate()); - expect(formattedStartDate.getMonth()).toEqual(newStartDate.getMonth()); - expect(formattedStartDate.getFullYear()).toEqual(newStartDate.getFullYear()); - - expect(formattedEndDate.getDate()).toEqual(newEndDate.getDate()); - expect(formattedEndDate.getMonth()).toEqual(newEndDate.getMonth()); - expect(formattedEndDate.getFullYear()).toEqual(newEndDate.getFullYear()); - }); -}); - -HTMLCanvasElement.prototype.getContext = vi.fn() as never; diff --git a/packages/ui/components/form/date-range-picker/index.ts b/packages/ui/components/form/date-range-picker/index.ts index 3099d9937a..06026a1b6c 100644 --- a/packages/ui/components/form/date-range-picker/index.ts +++ b/packages/ui/components/form/date-range-picker/index.ts @@ -1,5 +1,5 @@ import dynamic from "next/dynamic"; -export const DateRangePickerLazy = dynamic( - () => import("./DateRangePicker") -) as unknown as typeof import("./DateRangePicker").default; +export const DateRangePickerLazy = dynamic(() => + import("./DateRangePicker").then((mod) => mod.DatePickerWithRange) +); diff --git a/packages/ui/package.json b/packages/ui/package.json index 7d4089f4ae..791b7f1229 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -40,12 +40,14 @@ "class-variance-authority": "^0.4.0", "cmdk": "^0.2.0", "cmk": "^0.1.1", + "date-fns": "^3.6.0", "downshift": "^6.1.9", "lucide-react": "^0.364.0", "next": "^13.5.4", "next-seo": "^6.0.0", "react": "^18.2.0", "react-colorful": "^5.6.0", + "react-day-picker": "^8.10.1", "react-feather": "^2.0.10", "react-hook-form": "^7.43.3", "react-select": "^5.7.0",