Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/date_utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1090,6 +1090,7 @@ export function isTimeInDisabledRange(
try {
valid = !isWithinInterval(baseTime, { start: min, end: max });
} catch (err) {
/* istanbul ignore next - date-fns historically threw on invalid intervals */
valid = false;
}
return valid;
Expand Down
11 changes: 11 additions & 0 deletions src/test/calendar_container.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { render } from "@testing-library/react";
import React from "react";

import CalendarContainer from "../calendar_container";
import { CalendarContainer as CalendarContainerFromIndex } from "../index";

describe("CalendarContainer", () => {
it("renders with default props", () => {
Expand All @@ -18,6 +19,16 @@ describe("CalendarContainer", () => {
expect(dialog?.textContent).toBe("Test Content");
});

it("exposes CalendarContainer via the package entry point", () => {
const { container } = render(
<CalendarContainerFromIndex>
<div>Entry Content</div>
</CalendarContainerFromIndex>,
);

expect(container.querySelector('[role="dialog"]')).toBeTruthy();
});

it("renders with showTimeSelectOnly prop", () => {
const { container } = render(
<CalendarContainer showTimeSelectOnly>
Expand Down
54 changes: 54 additions & 0 deletions src/test/click_outside_wrapper.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -225,4 +225,58 @@ describe("ClickOutsideWrapper", () => {
addEventListenerSpy.mockRestore();
removeEventListenerSpy.mockRestore();
});

it("falls back to event.target when composedPath does not return nodes", () => {
const addEventListenerSpy = jest.spyOn(document, "addEventListener");
render(
<ClickOutsideWrapper onClickOutside={onClickOutsideMock}>
<div>Inside</div>
</ClickOutsideWrapper>,
);

const handlerEntry = addEventListenerSpy.mock.calls.find(
([type]) => type === "mousedown",
);
const handler = handlerEntry?.[1] as EventListener;

const outsideNode = document.createElement("div");
const mockEvent = {
composed: true,
composedPath: () => [{}],
target: outsideNode,
} as unknown as MouseEvent;

handler(mockEvent);

expect(onClickOutsideMock).toHaveBeenCalledTimes(1);
addEventListenerSpy.mockRestore();
});

it("does not treat non-HTMLElement targets as ignored elements", () => {
const addEventListenerSpy = jest.spyOn(document, "addEventListener");
render(
<ClickOutsideWrapper
onClickOutside={onClickOutsideMock}
ignoreClass="ignore-me"
>
<div>Inside</div>
</ClickOutsideWrapper>,
);

const handlerEntry = addEventListenerSpy.mock.calls.find(
([type]) => type === "mousedown",
);
const handler = handlerEntry?.[1] as EventListener;

const textNode = document.createTextNode("outside");
const mockEvent = {
composed: false,
target: textNode,
} as unknown as MouseEvent;

handler(mockEvent);

expect(onClickOutsideMock).toHaveBeenCalledTimes(1);
addEventListenerSpy.mockRestore();
});
});
22 changes: 22 additions & 0 deletions src/test/date_utils_test.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1148,6 +1148,14 @@ describe("date_utils", () => {

expect(isMonthInRange(startDate, endDate, 1, day)).toBe(false);
});

it("should return false when the start year is after the end year", () => {
const day = newDate("2025-01-01");
const startDate = newDate("2026-01-01");
const endDate = newDate("2024-01-01");

expect(isMonthInRange(startDate, endDate, 1, day)).toBe(false);
});
});

describe("getStartOfYear", () => {
Expand Down Expand Up @@ -1190,6 +1198,14 @@ describe("date_utils", () => {

expect(isQuarterInRange(startDate, endDate, 1, day)).toBe(false);
});

it("should return false when the start year is after the end year", () => {
const day = newDate("2025-01-01");
const startDate = newDate("2026-01-01");
const endDate = newDate("2024-04-01");

expect(isQuarterInRange(startDate, endDate, 1, day)).toBe(false);
});
});

describe("isYearInRange", () => {
Expand All @@ -1214,6 +1230,12 @@ describe("date_utils", () => {
it("should return false if range isn't passed", () => {
expect(isYearInRange(2016)).toBe(false);
});

it("should return false if provided dates are invalid", () => {
expect(
isYearInRange(2016, new Date("invalid"), new Date("invalid")),
).toBe(false);
});
});

describe("getYearsPeriod", () => {
Expand Down
73 changes: 72 additions & 1 deletion src/test/datepicker_test.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,11 @@ import {
subWeeks,
subYears,
} from "../date_utils";
import DatePicker, { registerLocale } from "../index";
import DatePicker, {
registerLocale,
setDefaultLocale,
getDefaultLocale,
} from "../index";

import CustomInput from "./helper_components/custom_input";
import ShadowRoot from "./helper_components/shadow_root";
Expand Down Expand Up @@ -121,6 +125,73 @@ describe("DatePicker", () => {
jest.resetAllMocks();
});

it("exposes locale helpers via the main entry point", () => {
const originalLocale = getDefaultLocale();
try {
expect(getDefaultLocale()).toBe(originalLocale);
setDefaultLocale("en-GB");
expect(getDefaultLocale()).toBe("en-GB");
} finally {
setDefaultLocale(originalLocale);
}
});

it("does not trigger selection changes when readOnly", () => {
const onChange = jest.fn();
const { instance } = renderDatePickerWithRef({
readOnly: true,
onChange,
});

act(() => {
instance?.handleSelect(newDate("2024-05-01"));
});

expect(onChange).not.toHaveBeenCalled();
});

it("skips updating preSelection when readOnly", () => {
const selected = newDate("2024-01-01");
const { instance } = renderDatePickerWithRef({
readOnly: true,
selected,
});

const originalPreSelection = instance?.state.preSelection;

act(() => {
instance?.setPreSelection(newDate("2024-02-01"));
});

expect(instance?.state.preSelection).toBe(originalPreSelection);
});

it("short-circuits day key navigation when keyboard navigation is disabled", () => {
const onKeyDown = jest.fn();
const preSelection = newDate("2024-06-15");
const { instance } = renderDatePickerWithRef({
disabledKeyboardNavigation: true,
onKeyDown,
inline: true,
selected: preSelection,
});

act(() => {
instance?.setState({ preSelection });
});

act(() => {
instance?.onDayKeyDown({
key: "ArrowRight",
shiftKey: false,
preventDefault: jest.fn(),
} as unknown as React.KeyboardEvent<HTMLDivElement>);
});

expect(onKeyDown).toHaveBeenCalled();
expect(instance?.state.preSelection).toBe(preSelection);
});

it("should retain the calendar open status when the document visibility change", () => {
const { container } = render(<DatePicker />);
const input = safeQuerySelector(container, "input");
Expand Down
75 changes: 75 additions & 0 deletions src/test/month_logic.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import type React from "react";
import Month from "../month";
import { KeyType, newDate } from "../date_utils";

type MonthComponentProps = React.ComponentProps<typeof Month>;

const buildProps = (
override: Partial<MonthComponentProps> = {},
): MonthComponentProps =>
({
day: newDate("2024-01-01"),
onDayClick: jest.fn(),
onDayMouseEnter: jest.fn(),
onMouseLeave: jest.fn(),
setPreSelection: jest.fn(),
preSelection: newDate("2024-01-01"),
showFourColumnMonthYearPicker: false,
showTwoColumnMonthYearPicker: false,
disabledKeyboardNavigation: false,
...override,
}) as MonthComponentProps;

describe("Month logic helpers", () => {
it("short-circuits keyboard navigation when there is no preSelection", () => {
const props = buildProps({ preSelection: undefined });
const instance = new Month(props);
const getVerticalOffsetSpy = jest.spyOn(instance, "getVerticalOffset");

instance.handleKeyboardNavigation(
{
preventDefault: jest.fn(),
} as unknown as React.KeyboardEvent<HTMLDivElement>,
KeyType.ArrowRight,
1,
);

expect(getVerticalOffsetSpy).not.toHaveBeenCalled();
expect(props.setPreSelection).not.toHaveBeenCalled();
});

it("prevents quarter navigation when the destination date is disabled", () => {
const props = buildProps();
const instance = new Month(props);
jest.spyOn(instance, "isDisabled").mockReturnValue(true);
jest.spyOn(instance, "isExcluded").mockReturnValue(false);

instance.handleQuarterNavigation(2, newDate("2024-04-01"));

expect(props.setPreSelection).not.toHaveBeenCalled();
});

it("does not handle quarter arrow keys without a preSelection value", () => {
const props = buildProps({ preSelection: undefined });
const instance = new Month(props);
const navigationSpy = jest.spyOn(instance, "handleQuarterNavigation");

instance.onQuarterKeyDown(
{
key: KeyType.ArrowRight,
preventDefault: jest.fn(),
} as unknown as React.KeyboardEvent<HTMLDivElement>,
2,
);

instance.onQuarterKeyDown(
{
key: KeyType.ArrowLeft,
preventDefault: jest.fn(),
} as unknown as React.KeyboardEvent<HTMLDivElement>,
2,
);

expect(navigationSpy).not.toHaveBeenCalled();
});
});
12 changes: 12 additions & 0 deletions src/test/shadow_root.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,4 +75,16 @@ describe("ShadowRoot", () => {

expect(container.querySelector("div")).not.toBeNull();
});

it("should avoid re-initializing when effect runs multiple times", () => {
const { container } = render(
<React.StrictMode>
<ShadowRoot>
<div>Strict Content</div>
</ShadowRoot>
</React.StrictMode>,
);

expect(container.querySelector("div")).not.toBeNull();
});
});
8 changes: 8 additions & 0 deletions src/test/test_utils.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { KeyType } from "../date_utils";
import {
SafeElementWrapper,
safeQuerySelector,
safeQuerySelectorAll,
getKey,
} from "./test_utils";

describe("test_utils", () => {
Expand Down Expand Up @@ -37,6 +39,12 @@ describe("test_utils", () => {
});
});

describe("getKey", () => {
it("should throw when key is not supported", () => {
expect(() => getKey("?" as KeyType)).toThrow("Unknown key");
});
});

describe("safeQuerySelectorAll", () => {
let container: HTMLElement;

Expand Down
Loading
Loading