Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Issues with scheduling timezone change without end time #19103

Merged
merged 8 commits into from
Feb 20, 2025
2 changes: 1 addition & 1 deletion apps/web/components/settings/TravelScheduleModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ const TravelScheduleModal = ({
<DialogFooter showDivider className="relative">
<DialogClose />
<Button
disabled={!endDate}
disabled={isNoEndDate ? !startDate : !startDate || !endDate}
onClick={() => {
createNewSchedule();
}}>
Expand Down
57 changes: 41 additions & 16 deletions packages/ui/components/form/datepicker/DatePicker.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import "react-calendar/dist/Calendar.css";
import "react-date-picker/dist/DatePicker.css";
import PrimitiveDatePicker from "react-date-picker/dist/entry.nostyle";
import * as Popover from "@radix-ui/react-popover";
import { format } from "date-fns";

import classNames from "@calcom/lib/classNames";
import { classNames as cn } from "@calcom/lib";
import { Button } from "@calcom/ui";

import { Icon } from "../../icon";
import { Calendar } from "../date-range-picker/Calendar";

type Props = {
date: Date;
Expand All @@ -15,20 +15,45 @@ type Props = {
};

const DatePicker = ({ minDate, disabled, date, onDatesChange, className }: Props) => {
return (
<PrimitiveDatePicker
className={classNames(
"focus:ring-primary-500 focus:border-primary-500 border-default rounded-md border p-1 pl-2 shadow-sm sm:text-sm",
className
)}
calendarClassName="rounded-md"
clearIcon={null}
calendarIcon={<Icon name="calendar" className="text-subtle h-5 w-5 rounded-md" />}
value={date}
function handleDayClick(newDate: Date) {
onDatesChange?.(newDate ?? new Date());
}
const fromDate = minDate ?? new Date();
const calender = (
<Calendar
initialFocus
fromDate={minDate === null ? undefined : fromDate}
// toDate={maxDate}
mode="single"
defaultMonth={date}
selected={date}
onDayClick={(day) => handleDayClick(day)}
numberOfMonths={1}
disabled={disabled}
onChange={onDatesChange}
/>
);

return (
<div className={cn("grid gap-2", className)}>
<Popover.Root>
<Popover.Trigger asChild>
<Button
data-testid="pick-date"
color="secondary"
EndIcon="calendar"
className={cn("justify-between text-left font-normal", !date && "text-subtle")}>
{date ? <>{format(date, "LLL dd, y")}</> : <span>Pick a date</span>}
</Button>
</Popover.Trigger>
<Popover.Content
className="bg-default text-emphasis z-50 w-auto rounded-md border p-0 outline-none"
align="start"
sideOffset={4}>
{calender}
</Popover.Content>
</Popover.Root>
</div>
);
};

export default DatePicker;
69 changes: 44 additions & 25 deletions packages/ui/components/form/datepicker/datepicker.test.tsx
Original file line number Diff line number Diff line change
@@ -1,44 +1,63 @@
/* eslint-disable playwright/missing-playwright-await */
import { render, fireEvent } from "@testing-library/react";
import { format } from "date-fns";
import { vi } from "vitest";

import DatePicker from "./DatePicker";

describe("Tests for DatePicker component", () => {
const date = new Date("2023-07-15");
const onChangeMock = vi.fn();

test("Should display the selected date correctly and call the onDatesChange callback when the selected date changes", () => {
const mockOnDatesChange = vi.fn((changedDate: Date) => format(new Date(changedDate), "yyyy-MM-dd"));
const { container } = render(<DatePicker date={date} onDatesChange={mockOnDatesChange} />);
describe("Tests for DatePicker Component", () => {
const testDate = new Date("2024-02-20");

const day = container.querySelector('input[name="day"]') as HTMLInputElement;
const dayEvent = { target: { value: "27" } };
test("Should render correctly with default date", () => {
const testDate = new Date("2024-02-20");
const { getByTestId } = render(<DatePicker date={testDate} />);

const month = container.querySelector('input[name="month"]') as HTMLInputElement;
const monthEvent = { target: { value: "06" } };
const dateButton = getByTestId("pick-date");
expect(dateButton).toHaveTextContent(format(testDate, "LLL dd, y"));
});

const year = container.querySelector('input[name="year"]') as HTMLInputElement;
const yearEvent = { target: { value: "2022" } };
test("Should show placeholder when no date is provided", () => {
const { getByTestId } = render(<DatePicker date={null as unknown as Date} />);

fireEvent.change(day, dayEvent);
expect(mockOnDatesChange).toHaveReturnedWith("2023-07-27");
const dateButton = getByTestId("pick-date");
expect(dateButton).toHaveTextContent("Pick a date");
});

fireEvent.change(month, monthEvent);
expect(mockOnDatesChange).toHaveReturnedWith("2023-06-27");
test("Should handle date selection correctly", async () => {
const testDate = new Date("2024-02-20");
const { getByTestId, getAllByRole } = render(<DatePicker date={testDate} onDatesChange={onChangeMock} />);

fireEvent.change(year, yearEvent);
expect(mockOnDatesChange).toHaveReturnedWith("2022-06-27");
const dateButton = getByTestId("pick-date");
fireEvent.click(dateButton);
const gridCells = getAllByRole("gridcell");
const selectedDate = gridCells.find((cell) => {
return cell.getAttribute("tabindex") === "0";
});

expect(mockOnDatesChange).toHaveBeenCalledTimes(3);
expect(selectedDate).toBeTruthy();
await expect(selectedDate).not.toHaveClass("opacity-50");
});
test("Should respect minDate prop", async () => {
const testDate = new Date("2024-02-20");
const minDate = new Date("2024-02-19");
const { getByTestId, getAllByRole } = render(
<DatePicker date={testDate} minDate={minDate} onDatesChange={onChangeMock} />
);

const dateButton = getByTestId("pick-date");
fireEvent.click(dateButton);

const disabledDates = getAllByRole("gridcell").filter((cell) => cell.classList.contains("opacity-50"));
expect(disabledDates.length).toBeGreaterThan(0);
await expect(disabledDates[0]).toHaveAttribute("disabled");
});

test("Should disable the DatePicker when disabled prop is true", () => {
const { getByDisplayValue } = render(<DatePicker date={date} disabled />);
test("Should respect disabled prop", () => {
const { getByTestId } = render(<DatePicker date={testDate} disabled={true} />);

const dateInput = getByDisplayValue(format(date, "yyyy-MM-dd")) as HTMLInputElement;
expect(dateInput).toBeDisabled();
const dateButton = getByTestId("pick-date");
expect(dateButton.classList.toString()).toContain("disabled:cursor-not-allowed");
expect(dateButton.classList.toString()).toContain("disabled:opacity-30");
});
});

HTMLCanvasElement.prototype.getContext = vi.fn() as never;
Loading