Skip to content

Commit

Permalink
fix: Issues with scheduling timezone change without end time (calcom#…
Browse files Browse the repository at this point in the history
…19103)

* fix: Issues with scheduling timezone change without end time

* test: remove DatePicker component tests

* feat: implement DatePicker component with tests and update button data-testid
  • Loading branch information
nayan-bagale authored and MuhammadAimanSulaiman committed Feb 25, 2025
1 parent 92811b6 commit 3b2a3a5
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 42 deletions.
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;

0 comments on commit 3b2a3a5

Please sign in to comment.