@uxf/datepicker 11.111.0-canary.1 → 11.111.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/hooks/use-date-picker-navigation.d.ts +3 -4
  2. package/hooks/use-date-picker-navigation.js +34 -30
  3. package/hooks/use-date-picker-navigation.test.js +4 -40
  4. package/hooks/use-date-picker.js +26 -40
  5. package/hooks/use-date-picker.test.js +2 -2
  6. package/hooks/use-date-range-picker.js +76 -103
  7. package/hooks/use-day.js +11 -12
  8. package/hooks/use-decade.js +8 -9
  9. package/package.json +4 -9
  10. package/utils/can-select-range.d.ts +5 -6
  11. package/utils/can-select-range.js +19 -7
  12. package/utils/can-select-range.test.js +27 -30
  13. package/utils/dayjs-utils.d.ts +1 -0
  14. package/utils/dayjs-utils.js +5 -1
  15. package/utils/get-current-year-month-and-date.js +2 -1
  16. package/utils/get-date-invervals.js +5 -2
  17. package/utils/get-date-month-and-year.js +5 -6
  18. package/utils/get-days.js +9 -12
  19. package/utils/get-each.d.ts +1 -2
  20. package/utils/get-each.js +9 -37
  21. package/utils/get-each.test.js +19 -34
  22. package/utils/get-initial-months.js +6 -3
  23. package/utils/get-months.js +7 -7
  24. package/utils/get-next-active-month.js +10 -6
  25. package/utils/get-weekday-labels.js +2 -4
  26. package/utils/get-years.js +4 -5
  27. package/utils/is-date-blocked.d.ts +6 -7
  28. package/utils/is-date-blocked.js +7 -6
  29. package/utils/is-date-blocked.test.js +11 -108
  30. package/utils/is-date-hovered.d.ts +5 -6
  31. package/utils/is-date-hovered.js +24 -17
  32. package/utils/is-date-hovered.test.js +25 -42
  33. package/utils/is-date-inside-range.d.ts +1 -2
  34. package/utils/is-date-inside-range.js +2 -1
  35. package/utils/is-date-selected.test.js +8 -16
  36. package/utils/is-end-date.d.ts +1 -2
  37. package/utils/is-end-date.js +2 -1
  38. package/utils/is-end-date.test.js +6 -11
  39. package/utils/is-first-or-last-selected-date.d.ts +1 -2
  40. package/utils/is-first-or-last-selected-date.js +3 -1
  41. package/utils/is-first-or-last-selected-date.test.js +6 -9
  42. package/utils/is-in-unavailable-dates.d.ts +1 -2
  43. package/utils/is-in-unavailable-dates.js +2 -1
  44. package/utils/is-in-unavailable-dates.test.js +6 -10
  45. package/utils/is-start-date.d.ts +1 -2
  46. package/utils/is-start-date.js +2 -1
  47. package/utils/is-start-date.test.js +6 -12
  48. package/jest.dst.config.js +0 -22
  49. package/jest.dst.global-setup.js +0 -14
  50. package/utils/date-string.d.ts +0 -34
  51. package/utils/date-string.js +0 -64
  52. package/utils/date-string.test.d.ts +0 -1
  53. package/utils/date-string.test.js +0 -63
  54. package/utils/dst-robustness.test.d.ts +0 -11
  55. package/utils/dst-robustness.test.js +0 -328
@@ -2,14 +2,10 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const is_in_unavailable_dates_1 = require("./is-in-unavailable-dates");
4
4
  test("isInUnavailableDates", () => {
5
- expect((0, is_in_unavailable_dates_1.isInUnavailableDates)(["2020-08-08", "2021-08-08", "2020-12-08"], "2020-08-08")).toBe(true);
6
- expect((0, is_in_unavailable_dates_1.isInUnavailableDates)(["2021-12-15"], "2021-12-15")).toBe(true);
7
- expect((0, is_in_unavailable_dates_1.isInUnavailableDates)(["2020-08-08", "1999-01-05"], "1999-01-05")).toBe(true);
8
- expect((0, is_in_unavailable_dates_1.isInUnavailableDates)(undefined, "2020-08-08")).toBe(false);
9
- expect((0, is_in_unavailable_dates_1.isInUnavailableDates)([], "2020-08-08")).toBe(false);
10
- expect((0, is_in_unavailable_dates_1.isInUnavailableDates)(["2021-08-12"], "2020-08-08")).toBe(false);
11
- });
12
- test("isInUnavailableDates DST boundary", () => {
13
- expect((0, is_in_unavailable_dates_1.isInUnavailableDates)(["2026-10-25"], "2026-10-25")).toBe(true);
14
- expect((0, is_in_unavailable_dates_1.isInUnavailableDates)(["2026-10-24"], "2026-10-25")).toBe(false);
5
+ expect((0, is_in_unavailable_dates_1.isInUnavailableDates)([new Date("2020-08-08"), new Date("2021-08-08"), new Date("2020-12-08")], new Date("2020-08-08"))).toBe(true);
6
+ expect((0, is_in_unavailable_dates_1.isInUnavailableDates)([new Date("2021-12-15")], new Date("2021-12-15"))).toBe(true);
7
+ expect((0, is_in_unavailable_dates_1.isInUnavailableDates)([new Date("2020-08-08"), new Date("1999-01-05")], new Date("1999-01-05"))).toBe(true);
8
+ expect((0, is_in_unavailable_dates_1.isInUnavailableDates)(undefined, new Date("2020-08-08"))).toBe(false);
9
+ expect((0, is_in_unavailable_dates_1.isInUnavailableDates)([], new Date("2020-08-08"))).toBe(false);
10
+ expect((0, is_in_unavailable_dates_1.isInUnavailableDates)([new Date("2021-08-12")], new Date("2020-08-08"))).toBe(false);
15
11
  });
@@ -1,2 +1 @@
1
- import { DateString } from "@uxf/core/date";
2
- export declare function isStartDate(date: DateString, startDate: DateString | null): boolean;
1
+ export declare function isStartDate(date: Date, startDate: Date | null): boolean;
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.isStartDate = isStartDate;
4
+ const dayjs_utils_1 = require("./dayjs-utils");
4
5
  function isStartDate(date, startDate) {
5
- return date === startDate;
6
+ return Boolean(startDate && (0, dayjs_utils_1.dayjsEnRaw)(date).isSame(startDate, "day"));
6
7
  }
@@ -2,16 +2,10 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const is_start_date_1 = require("./is-start-date");
4
4
  test("isStartDate", () => {
5
- expect((0, is_start_date_1.isStartDate)("2020-08-08", "2020-08-08")).toBe(true);
6
- expect((0, is_start_date_1.isStartDate)("2021-12-15", "2021-12-15")).toBe(true);
7
- expect((0, is_start_date_1.isStartDate)("1999-01-05", "1999-01-05")).toBe(true);
8
- expect((0, is_start_date_1.isStartDate)("2020-08-09", "2020-08-08")).toBe(false);
9
- expect((0, is_start_date_1.isStartDate)("2020-08-10", "2020-08-08")).toBe(false);
10
- expect((0, is_start_date_1.isStartDate)("2021-08-08", "2020-08-08")).toBe(false);
11
- // null startDate
12
- expect((0, is_start_date_1.isStartDate)("2026-10-25", null)).toBe(false);
13
- });
14
- test("isStartDate DST fall-back (2026-10-25)", () => {
15
- expect((0, is_start_date_1.isStartDate)("2026-10-25", "2026-10-25")).toBe(true);
16
- expect((0, is_start_date_1.isStartDate)("2026-10-24", "2026-10-25")).toBe(false);
5
+ expect((0, is_start_date_1.isStartDate)(new Date("2020-08-08"), new Date("2020-08-08"))).toBe(true);
6
+ expect((0, is_start_date_1.isStartDate)(new Date("2021-12-15"), new Date("2021-12-15"))).toBe(true);
7
+ expect((0, is_start_date_1.isStartDate)(new Date("1999-01-05"), new Date("1999-01-05"))).toBe(true);
8
+ expect((0, is_start_date_1.isStartDate)(new Date("2020-08-09"), new Date("2020-08-08"))).toBe(false);
9
+ expect((0, is_start_date_1.isStartDate)(new Date("2020-08-10"), new Date("2020-08-08"))).toBe(false);
10
+ expect((0, is_start_date_1.isStartDate)(new Date("2021-08-08"), new Date("2020-08-08"))).toBe(false);
17
11
  });
@@ -1,22 +0,0 @@
1
- /**
2
- * Jest config for DST robustness tests.
3
- * Uses America/New_York timezone so that new Date("2026-10-25") (UTC midnight)
4
- * resolves to Oct 24 20:00 local time — a different calendar day than new Date(2026, 9, 25).
5
- * This exposes timezone bugs where local and UTC calendar days diverge.
6
- *
7
- * Tests are expected to FAIL until the DateString refactor (Tasks 2–10) is complete.
8
- */
9
-
10
- /** @type {import('ts-jest').JestConfigWithTsJest} */
11
- module.exports = {
12
- preset: "ts-jest",
13
- // "node" overrides the project-wide jsdom default (set in root jest.config.js via ts-jest preset).
14
- // DST tests must run in node environment — jsdom swallows timezone behaviour.
15
- testEnvironment: "node",
16
- globalSetup: "<rootDir>/jest.dst.global-setup.js",
17
- rootDir: ".",
18
- testMatch: ["<rootDir>/utils/dst-robustness.test.ts"],
19
- transform: {
20
- "\\.[t]sx?$": ["ts-jest", { diagnostics: false }],
21
- },
22
- };
@@ -1,14 +0,0 @@
1
- /**
2
- * Custom globalSetup for DST robustness tests.
3
- *
4
- * Belt-and-suspenders reinforcement: the npm scripts (test:dst, test:tz:ny, test:tz:prague)
5
- * already set TZ=... as an env prefix before Node starts, which is the reliable way to
6
- * set the timezone. This file re-asserts the TZ for America/New_York as a fallback in case
7
- * the config is ever invoked without the env prefix (e.g. direct `jest --config=...` call).
8
- *
9
- * Note: the monorepo root global-setup.js sets TZ=UTC, but it only runs when Jest is
10
- * invoked via the root jest.config.js. This custom globalSetup overrides that for DST tests.
11
- */
12
- module.exports = async () => {
13
- process.env.TZ = process.env.TZ || "America/New_York";
14
- };
@@ -1,34 +0,0 @@
1
- import { DateString } from "@uxf/core/date";
2
- /**
3
- * Convert a Date to a zero-padded DateString.
4
- *
5
- * Handles both creation styles correctly:
6
- * - new Date("2026-10-25") → ISO string → UTC midnight → use UTC methods
7
- * - new Date(2026, 9, 25) → local constructor → use local methods
8
- *
9
- * The heuristic: if all UTC time components are zero the date was parsed from
10
- * an ISO date string (UTC midnight convention); otherwise it came from a local
11
- * constructor and local methods give the correct calendar day.
12
- */
13
- export declare function toDateString(date: Date): DateString;
14
- /**
15
- * Convert a DateString back to a Date at UTC midnight.
16
- */
17
- export declare function fromDateString(s: DateString): Date;
18
- /**
19
- * Today as a DateString in UTC — matches the `new Date("Y-M-D")` convention
20
- * used throughout the package. Use this instead of `toDateString(new Date())`
21
- * whenever you need a stable "today" string.
22
- */
23
- export declare function todayDateString(): DateString;
24
- /**
25
- * Add n calendar months to a DateString, snapping to the 1st of the resulting month.
26
- * Month arithmetic is purely numeric — no DST exposure.
27
- */
28
- export declare function addMonths(date: DateString, n: number): DateString;
29
- /**
30
- * Add n calendar days to a DateString.
31
- * Uses UTC midnight arithmetic — UTC has no DST, so adding DAY_MS is always correct.
32
- * Pass negative n to subtract days.
33
- */
34
- export declare function addDays(date: DateString, n: number): DateString;
@@ -1,64 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.toDateString = toDateString;
4
- exports.fromDateString = fromDateString;
5
- exports.todayDateString = todayDateString;
6
- exports.addMonths = addMonths;
7
- exports.addDays = addDays;
8
- const create_date_string_1 = require("./create-date-string");
9
- const DAY_MS = 86400000;
10
- /**
11
- * Convert a Date to a zero-padded DateString.
12
- *
13
- * Handles both creation styles correctly:
14
- * - new Date("2026-10-25") → ISO string → UTC midnight → use UTC methods
15
- * - new Date(2026, 9, 25) → local constructor → use local methods
16
- *
17
- * The heuristic: if all UTC time components are zero the date was parsed from
18
- * an ISO date string (UTC midnight convention); otherwise it came from a local
19
- * constructor and local methods give the correct calendar day.
20
- */
21
- function toDateString(date) {
22
- if (date.getUTCHours() === 0 &&
23
- date.getUTCMinutes() === 0 &&
24
- date.getUTCSeconds() === 0 &&
25
- date.getUTCMilliseconds() === 0) {
26
- return (0, create_date_string_1.createDateString)(date.getUTCFullYear(), date.getUTCMonth(), // 0-indexed; createDateString adds 1
27
- date.getUTCDate());
28
- }
29
- return (0, create_date_string_1.createDateString)(date.getFullYear(), date.getMonth(), // 0-indexed; createDateString adds 1
30
- date.getDate());
31
- }
32
- /**
33
- * Convert a DateString back to a Date at UTC midnight.
34
- */
35
- function fromDateString(s) {
36
- return new Date(s);
37
- }
38
- /**
39
- * Today as a DateString in UTC — matches the `new Date("Y-M-D")` convention
40
- * used throughout the package. Use this instead of `toDateString(new Date())`
41
- * whenever you need a stable "today" string.
42
- */
43
- function todayDateString() {
44
- const now = new Date();
45
- return (0, create_date_string_1.createDateString)(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate());
46
- }
47
- /**
48
- * Add n calendar months to a DateString, snapping to the 1st of the resulting month.
49
- * Month arithmetic is purely numeric — no DST exposure.
50
- */
51
- function addMonths(date, n) {
52
- const y = parseInt(date.slice(0, 4), 10);
53
- const m = parseInt(date.slice(5, 7), 10) - 1; // 0-indexed
54
- const total = y * 12 + m + n;
55
- return (0, create_date_string_1.createDateString)(Math.floor(total / 12), total % 12);
56
- }
57
- /**
58
- * Add n calendar days to a DateString.
59
- * Uses UTC midnight arithmetic — UTC has no DST, so adding DAY_MS is always correct.
60
- * Pass negative n to subtract days.
61
- */
62
- function addDays(date, n) {
63
- return toDateString(new Date(new Date(date).getTime() + n * DAY_MS));
64
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1,63 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const date_string_1 = require("./date-string");
4
- test("toDateString reads UTC date components from ISO-string Date", () => {
5
- expect((0, date_string_1.toDateString)(new Date("2026-10-25T00:00:00Z"))).toBe("2026-10-25");
6
- expect((0, date_string_1.toDateString)(new Date("2026-03-29T00:00:00Z"))).toBe("2026-03-29");
7
- expect((0, date_string_1.toDateString)(new Date("2021-01-01T00:00:00Z"))).toBe("2021-01-01");
8
- });
9
- test("toDateString reads local date components from local-constructor Date", () => {
10
- // new Date(y, m, d) creates local midnight — must give same DateString as ISO string
11
- expect((0, date_string_1.toDateString)(new Date(2026, 9, 25))).toBe("2026-10-25");
12
- expect((0, date_string_1.toDateString)(new Date(2026, 2, 29))).toBe("2026-03-29");
13
- expect((0, date_string_1.toDateString)(new Date(2025, 11, 31))).toBe("2025-12-31");
14
- expect((0, date_string_1.toDateString)(new Date(2026, 0, 1))).toBe("2026-01-01");
15
- });
16
- test("toDateString: ISO string and local constructor give the same DateString for the same calendar day", () => {
17
- expect((0, date_string_1.toDateString)(new Date(2026, 9, 25))).toBe((0, date_string_1.toDateString)(new Date("2026-10-25")));
18
- expect((0, date_string_1.toDateString)(new Date(2025, 11, 31))).toBe((0, date_string_1.toDateString)(new Date("2025-12-31")));
19
- expect((0, date_string_1.toDateString)(new Date(2026, 0, 1))).toBe((0, date_string_1.toDateString)(new Date("2026-01-01")));
20
- });
21
- test("toDateString zero-pads month and day", () => {
22
- expect((0, date_string_1.toDateString)(new Date(Date.UTC(2021, 0, 5)))).toBe("2021-01-05");
23
- expect((0, date_string_1.toDateString)(new Date(Date.UTC(2021, 8, 3)))).toBe("2021-09-03");
24
- });
25
- test("fromDateString returns UTC midnight Date", () => {
26
- const d = (0, date_string_1.fromDateString)("2026-10-25");
27
- expect(d.getUTCFullYear()).toBe(2026);
28
- expect(d.getUTCMonth()).toBe(9);
29
- expect(d.getUTCDate()).toBe(25);
30
- expect(d.getUTCHours()).toBe(0);
31
- expect(d.getUTCMinutes()).toBe(0);
32
- });
33
- test("toDateString and fromDateString are inverses", () => {
34
- const s = "2026-10-25";
35
- expect((0, date_string_1.toDateString)((0, date_string_1.fromDateString)(s))).toBe(s);
36
- });
37
- test("addDays adds calendar days across DST fall-back (Oct 25 2026)", () => {
38
- expect((0, date_string_1.addDays)("2026-10-24", 1)).toBe("2026-10-25");
39
- expect((0, date_string_1.addDays)("2026-10-25", 1)).toBe("2026-10-26");
40
- });
41
- test("addDays adds calendar days across DST spring-forward (Mar 29 2026)", () => {
42
- expect((0, date_string_1.addDays)("2026-03-28", 1)).toBe("2026-03-29");
43
- expect((0, date_string_1.addDays)("2026-03-29", 1)).toBe("2026-03-30");
44
- });
45
- test("addDays with negative n subtracts days", () => {
46
- expect((0, date_string_1.addDays)("2026-10-25", -1)).toBe("2026-10-24");
47
- expect((0, date_string_1.addDays)("2026-10-01", -1)).toBe("2026-09-30");
48
- });
49
- test("addDays with n=0 returns same date", () => {
50
- expect((0, date_string_1.addDays)("2026-10-25", 0)).toBe("2026-10-25");
51
- });
52
- test("addMonths advances by positive months", () => {
53
- expect((0, date_string_1.addMonths)("2026-04-01", 1)).toBe("2026-05-01");
54
- expect((0, date_string_1.addMonths)("2026-11-01", 2)).toBe("2027-01-01");
55
- expect((0, date_string_1.addMonths)("2026-04-15", 1)).toBe("2026-05-01");
56
- });
57
- test("addMonths goes back with negative months", () => {
58
- expect((0, date_string_1.addMonths)("2026-03-01", -1)).toBe("2026-02-01");
59
- expect((0, date_string_1.addMonths)("2026-01-01", -1)).toBe("2025-12-01");
60
- });
61
- test("addMonths with n=0 returns first of same month", () => {
62
- expect((0, date_string_1.addMonths)("2026-04-15", 0)).toBe("2026-04-01");
63
- });
@@ -1,11 +0,0 @@
1
- /**
2
- * DST robustness tests. Utility functions now operate on DateString, so they are
3
- * inherently timezone-proof. These tests verify string-based comparison correctness
4
- * across DST boundaries and large UTC offsets.
5
- *
6
- * Run with:
7
- * npm run test:tz:prague -w @uxf/datepicker
8
- * npm run test:tz:ny -w @uxf/datepicker
9
- * npm run test:tz:tokyo -w @uxf/datepicker
10
- */
11
- export {};
@@ -1,328 +0,0 @@
1
- "use strict";
2
- /**
3
- * DST robustness tests. Utility functions now operate on DateString, so they are
4
- * inherently timezone-proof. These tests verify string-based comparison correctness
5
- * across DST boundaries and large UTC offsets.
6
- *
7
- * Run with:
8
- * npm run test:tz:prague -w @uxf/datepicker
9
- * npm run test:tz:ny -w @uxf/datepicker
10
- * npm run test:tz:tokyo -w @uxf/datepicker
11
- */
12
- Object.defineProperty(exports, "__esModule", { value: true });
13
- const can_select_range_1 = require("./can-select-range");
14
- const date_string_1 = require("./date-string");
15
- const get_current_year_month_and_date_1 = require("./get-current-year-month-and-date");
16
- const get_days_1 = require("./get-days");
17
- const get_weekday_labels_1 = require("./get-weekday-labels");
18
- const get_years_1 = require("./get-years");
19
- const is_date_blocked_1 = require("./is-date-blocked");
20
- const is_date_hovered_1 = require("./is-date-hovered");
21
- const is_date_inside_range_1 = require("./is-date-inside-range");
22
- const is_end_date_1 = require("./is-end-date");
23
- const is_first_or_last_selected_date_1 = require("./is-first-or-last-selected-date");
24
- const is_in_unavailable_dates_1 = require("./is-in-unavailable-dates");
25
- const is_start_date_1 = require("./is-start-date");
26
- // Oct 25 2026 = DST fall-back in Europe/Prague (25h day, clocks go back)
27
- // Mar 29 2026 = DST spring-forward in Europe/Prague (23h day, clocks go forward)
28
- // Asia/Tokyo = UTC+9, no DST — local midnight is UTC previous-day 15:00 (extreme offset)
29
- describe("toDateString: local constructor vs ISO string are equivalent (any timezone)", () => {
30
- test("DST fall-back day: new Date(2026, 9, 25) === new Date('2026-10-25')", () => {
31
- // new Date(2026, 9, 25) = local midnight — different UTC timestamp than ISO string in UTC+ zones
32
- expect((0, date_string_1.toDateString)(new Date(2026, 9, 25))).toBe("2026-10-25");
33
- expect((0, date_string_1.toDateString)(new Date("2026-10-25"))).toBe("2026-10-25");
34
- expect((0, date_string_1.toDateString)(new Date(2026, 9, 25))).toBe((0, date_string_1.toDateString)(new Date("2026-10-25")));
35
- });
36
- test("DST spring-forward day: new Date(2026, 2, 29) === new Date('2026-03-29')", () => {
37
- expect((0, date_string_1.toDateString)(new Date(2026, 2, 29))).toBe("2026-03-29");
38
- expect((0, date_string_1.toDateString)(new Date(2026, 2, 29))).toBe((0, date_string_1.toDateString)(new Date("2026-03-29")));
39
- });
40
- test("year-end: new Date(2025, 11, 31) === new Date('2025-12-31')", () => {
41
- expect((0, date_string_1.toDateString)(new Date(2025, 11, 31))).toBe("2025-12-31");
42
- expect((0, date_string_1.toDateString)(new Date(2025, 11, 31))).toBe((0, date_string_1.toDateString)(new Date("2025-12-31")));
43
- });
44
- test("year-start: new Date(2026, 0, 1) === new Date('2026-01-01')", () => {
45
- expect((0, date_string_1.toDateString)(new Date(2026, 0, 1))).toBe("2026-01-01");
46
- expect((0, date_string_1.toDateString)(new Date(2026, 0, 1))).toBe((0, date_string_1.toDateString)(new Date("2026-01-01")));
47
- });
48
- });
49
- describe("DST fall-back Oct 25 2026 (Europe/Prague)", () => {
50
- test("isStartDate: date on DST boundary matches", () => {
51
- expect((0, is_start_date_1.isStartDate)("2026-10-25", "2026-10-25")).toBe(true);
52
- expect((0, is_start_date_1.isStartDate)("2026-10-24", "2026-10-25")).toBe(false);
53
- expect((0, is_start_date_1.isStartDate)("2026-10-26", "2026-10-25")).toBe(false);
54
- });
55
- test("isDateInsideRange: date on DST fall-back boundary is inside range", () => {
56
- expect((0, is_date_inside_range_1.isDateInsideRange)("2026-10-25", "2026-10-24", "2026-10-26")).toBe(true);
57
- });
58
- test("isInUnavailableDates: date on DST boundary found in list", () => {
59
- expect((0, is_in_unavailable_dates_1.isInUnavailableDates)(["2026-10-25"], "2026-10-25")).toBe(true);
60
- expect((0, is_in_unavailable_dates_1.isInUnavailableDates)(["2026-10-24"], "2026-10-25")).toBe(false);
61
- });
62
- test("canSelectRange: 2-day range across DST fall-back is valid", () => {
63
- expect((0, can_select_range_1.canSelectRange)({
64
- startDate: "2026-10-25",
65
- endDate: "2026-10-26",
66
- minBookingDays: 2,
67
- minBookingDate: undefined,
68
- maxBookingDate: undefined,
69
- exactMinBookingDays: false,
70
- isDateBlocked: () => false,
71
- })).toBe(true);
72
- });
73
- test("isDateBlocked: date on DST boundary not blocked when minBookingDate is same day", () => {
74
- expect((0, is_date_blocked_1.isDateBlocked)({
75
- date: "2026-10-25",
76
- startDate: null,
77
- endDate: null,
78
- minBookingDate: "2026-10-25",
79
- })).toBe(false);
80
- });
81
- test("isEndDate: date on DST boundary matches endDate", () => {
82
- expect((0, is_end_date_1.isEndDate)("2026-10-25", "2026-10-25")).toBe(true);
83
- expect((0, is_end_date_1.isEndDate)("2026-10-24", "2026-10-25")).toBe(false);
84
- });
85
- test("isFirstOrLastSelectedDate: DST boundary date matches start or end", () => {
86
- expect((0, is_first_or_last_selected_date_1.isFirstOrLastSelectedDate)("2026-10-25", "2026-10-25", "2026-10-28")).toBe(true);
87
- expect((0, is_first_or_last_selected_date_1.isFirstOrLastSelectedDate)("2026-10-28", "2026-10-25", "2026-10-28")).toBe(true);
88
- });
89
- test("isDateBlocked: date on DST boundary not blocked when maxBookingDate is same day", () => {
90
- expect((0, is_date_blocked_1.isDateBlocked)({
91
- date: "2026-10-25",
92
- startDate: null,
93
- endDate: null,
94
- minBookingDate: undefined,
95
- maxBookingDate: "2026-10-25",
96
- })).toBe(false);
97
- });
98
- });
99
- describe("DST spring-forward Mar 29 2026 (Europe/Prague)", () => {
100
- test("canSelectRange: 2-day range across DST spring-forward is valid", () => {
101
- expect((0, can_select_range_1.canSelectRange)({
102
- startDate: "2026-03-29",
103
- endDate: "2026-03-30",
104
- minBookingDays: 2,
105
- minBookingDate: undefined,
106
- maxBookingDate: undefined,
107
- exactMinBookingDays: false,
108
- isDateBlocked: () => false,
109
- })).toBe(true);
110
- });
111
- test("isDateInsideRange: date on spring-forward boundary is inside range", () => {
112
- expect((0, is_date_inside_range_1.isDateInsideRange)("2026-03-29", "2026-03-28", "2026-03-30")).toBe(true);
113
- });
114
- });
115
- describe("UTC+9 (Asia/Tokyo, no DST) — year-end boundary Dec 31 2025 / Jan 1 2026", () => {
116
- test("isStartDate: year-end date matches", () => {
117
- expect((0, is_start_date_1.isStartDate)("2025-12-31", "2025-12-31")).toBe(true);
118
- expect((0, is_start_date_1.isStartDate)("2026-01-01", "2025-12-31")).toBe(false);
119
- });
120
- test("isEndDate: New Year's Day matches", () => {
121
- expect((0, is_end_date_1.isEndDate)("2026-01-01", "2026-01-01")).toBe(true);
122
- expect((0, is_end_date_1.isEndDate)("2025-12-31", "2026-01-01")).toBe(false);
123
- });
124
- test("isDateInsideRange: date on year-end boundary is inside range", () => {
125
- expect((0, is_date_inside_range_1.isDateInsideRange)("2025-12-31", "2025-12-30", "2026-01-01")).toBe(true);
126
- expect((0, is_date_inside_range_1.isDateInsideRange)("2026-01-01", "2025-12-31", "2026-01-02")).toBe(true);
127
- });
128
- test("isInUnavailableDates: year-end date found in list", () => {
129
- expect((0, is_in_unavailable_dates_1.isInUnavailableDates)(["2025-12-31"], "2025-12-31")).toBe(true);
130
- expect((0, is_in_unavailable_dates_1.isInUnavailableDates)(["2025-12-31"], "2026-01-01")).toBe(false);
131
- });
132
- test("canSelectRange: 2-day range spanning year boundary is valid", () => {
133
- expect((0, can_select_range_1.canSelectRange)({
134
- startDate: "2025-12-31",
135
- endDate: "2026-01-01",
136
- minBookingDays: 2,
137
- minBookingDate: undefined,
138
- maxBookingDate: undefined,
139
- exactMinBookingDays: false,
140
- isDateBlocked: () => false,
141
- })).toBe(true);
142
- });
143
- test("canSelectRange: startDate on minBookingDate at year boundary is valid", () => {
144
- expect((0, can_select_range_1.canSelectRange)({
145
- startDate: "2026-01-01",
146
- endDate: null,
147
- minBookingDays: 1,
148
- minBookingDate: "2025-12-31",
149
- maxBookingDate: undefined,
150
- exactMinBookingDays: false,
151
- isDateBlocked: () => false,
152
- })).toBe(true);
153
- });
154
- test("canSelectRange: endDate within maxBookingDate at year boundary is valid", () => {
155
- expect((0, can_select_range_1.canSelectRange)({
156
- startDate: "2025-12-30",
157
- endDate: "2025-12-31",
158
- minBookingDays: 2,
159
- minBookingDate: undefined,
160
- maxBookingDate: "2026-01-02",
161
- exactMinBookingDays: false,
162
- isDateBlocked: () => false,
163
- })).toBe(true);
164
- });
165
- test("isDateBlocked: Dec 31 not blocked when minBookingDate is Dec 31", () => {
166
- expect((0, is_date_blocked_1.isDateBlocked)({
167
- date: "2025-12-31",
168
- startDate: null,
169
- endDate: null,
170
- minBookingDate: "2025-12-31",
171
- })).toBe(false);
172
- });
173
- test("isDateBlocked: Jan 1 not blocked when maxBookingDate is Jan 1", () => {
174
- expect((0, is_date_blocked_1.isDateBlocked)({
175
- date: "2026-01-01",
176
- startDate: null,
177
- endDate: null,
178
- maxBookingDate: "2026-01-01",
179
- })).toBe(false);
180
- });
181
- test("isDateBlocked: Dec 30 blocked when minBookingDate is Dec 31", () => {
182
- expect((0, is_date_blocked_1.isDateBlocked)({
183
- date: "2025-12-30",
184
- startDate: null,
185
- endDate: null,
186
- minBookingDate: "2025-12-31",
187
- })).toBe(true);
188
- });
189
- test("isDateBlocked: Jan 2 blocked when maxBookingDate is Jan 1", () => {
190
- expect((0, is_date_blocked_1.isDateBlocked)({
191
- date: "2026-01-02",
192
- startDate: null,
193
- endDate: null,
194
- maxBookingDate: "2026-01-01",
195
- })).toBe(true);
196
- });
197
- test("isFirstOrLastSelectedDate: year-end and new year match start/end", () => {
198
- expect((0, is_first_or_last_selected_date_1.isFirstOrLastSelectedDate)("2025-12-31", "2025-12-31", "2026-01-03")).toBe(true);
199
- expect((0, is_first_or_last_selected_date_1.isFirstOrLastSelectedDate)("2026-01-03", "2025-12-31", "2026-01-03")).toBe(true);
200
- expect((0, is_first_or_last_selected_date_1.isFirstOrLastSelectedDate)("2026-01-01", "2025-12-31", "2026-01-03")).toBe(false);
201
- });
202
- test("isDateHovered: date between startDate and hoveredDate highlighted across year boundary", () => {
203
- expect((0, is_date_hovered_1.isDateHovered)({
204
- date: "2025-12-31",
205
- startDate: "2025-12-29",
206
- endDate: null,
207
- isDateBlocked: () => false,
208
- hoveredDate: "2026-01-03",
209
- minBookingDays: 1,
210
- exactMinBookingDays: false,
211
- })).toBe(true);
212
- });
213
- });
214
- describe("getDays: month boundaries correct in UTC+ timezone", () => {
215
- // April 2026 with firstDayOfWeek=1 (Monday):
216
- // April 1 = Wednesday → 2 prev-month days (Mar 30, 31)
217
- // April 30 = Thursday → 3 next-month overflow days (May 1, 2, 3)
218
- // Total = 2 + 30 + 3 = 35 days
219
- //
220
- // Bug: in UTC+2 (Prague), dayjsEnWithTz().endOf("month").toDate() for April is
221
- // 2026-04-30T23:59:59.999Z which local-methods read as 2026-05-01 →
222
- // monthEndStr becomes "2026-05-01", so May 1 ends up in the current-month slice.
223
- test("April 2026 calendar has exactly 35 days (5 weeks, firstDayOfWeek=1)", () => {
224
- const days = (0, get_days_1.getDays)({ year: 2026, month: 3, firstDayOfWeek: 1 });
225
- expect(days).toHaveLength(35);
226
- });
227
- test("last current-month day in April 2026 is April 30", () => {
228
- const days = (0, get_days_1.getDays)({ year: 2026, month: 3, firstDayOfWeek: 1 });
229
- const currentMonthDays = days.filter((d) => d.currentMonth);
230
- expect(currentMonthDays).toHaveLength(30);
231
- const last = currentMonthDays[currentMonthDays.length - 1];
232
- expect((0, date_string_1.toDateString)(last.date)).toBe("2026-04-30");
233
- });
234
- test("May 1 appears as overflow (currentMonth: false) in April 2026 calendar", () => {
235
- const days = (0, get_days_1.getDays)({ year: 2026, month: 3, firstDayOfWeek: 1 });
236
- const may1 = days.find((d) => (0, date_string_1.toDateString)(d.date) === "2026-05-01");
237
- expect(may1).toBeDefined();
238
- expect(may1 === null || may1 === void 0 ? void 0 : may1.currentMonth).toBe(false);
239
- });
240
- test("overflow days after April 2026 are May 1, 2, 3", () => {
241
- const days = (0, get_days_1.getDays)({ year: 2026, month: 3, firstDayOfWeek: 1 });
242
- const overflow = days.filter((d) => !d.currentMonth && (0, date_string_1.toDateString)(d.date) >= "2026-05-01");
243
- expect(overflow.map((d) => (0, date_string_1.toDateString)(d.date))).toEqual(["2026-05-01", "2026-05-02", "2026-05-03"]);
244
- });
245
- });
246
- describe("getWeekdayLabels: exactly 7 labels in UTC+ timezone", () => {
247
- // Bug: endOf("week").add(n, "day").toDate() has non-zero UTC ms (23:59:59.999Z).
248
- // toDateString() falls back to local methods and shifts the date +1 day in UTC+
249
- // timezones, so getEach() returns 8 dates → 8 labels (last one is a duplicate Mon).
250
- test("returns exactly 7 labels with firstDayOfWeek=0", () => {
251
- expect((0, get_weekday_labels_1.getWeekdayLabels)({ firstDayOfWeek: 0 })).toHaveLength(7);
252
- });
253
- test("returns exactly 7 labels with firstDayOfWeek=1 (Monday-start)", () => {
254
- expect((0, get_weekday_labels_1.getWeekdayLabels)({ firstDayOfWeek: 1 })).toHaveLength(7);
255
- });
256
- test("Mo-start week is Mo…Su with no duplicates", () => {
257
- expect((0, get_weekday_labels_1.getWeekdayLabels)({ firstDayOfWeek: 1 })).toEqual(["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"]);
258
- });
259
- });
260
- describe("getYears: exactly 9 years for ±4 decade view in UTC+ timezone", () => {
261
- // Bug: useDecade passed endOf("year").toDate() to getYears, which called toDateString()
262
- // on 2030-12-31T23:59:59.999Z — local methods in CEST give 2031-01-01, adding a 10th year.
263
- // Fix: useDecade now passes new Date(createDateString(year+4, 0)) (UTC midnight Jan 1).
264
- test("year=2026: 9 years returned (2022–2030)", () => {
265
- const startYear = new Date("2022-01-01");
266
- const endYear = new Date("2030-01-01");
267
- expect((0, get_years_1.getYears)({ startYear, endYear })).toHaveLength(9);
268
- });
269
- test("year=2026: first year is 2022, last is 2030", () => {
270
- const startYear = new Date("2022-01-01");
271
- const endYear = new Date("2030-01-01");
272
- const years = (0, get_years_1.getYears)({ startYear, endYear });
273
- expect(years[0].yearLabel).toBe("2022");
274
- expect(years[years.length - 1].yearLabel).toBe("2030");
275
- });
276
- });
277
- describe("getCurrentYearMonthAndDate: returns local calendar month in UTC+ timezone", () => {
278
- // Bug: dayjsEnWithTz().startOf("day") anchors to UTC day, so at 00:30 local Prague
279
- // time (= 22:30 UTC the previous day) it returns the previous UTC day's month.
280
- // Fix: getDateMonthAndYear(new Date()) — toDateString(new Date()) uses local methods
281
- // for any non-UTC-midnight time, giving the correct local calendar day.
282
- beforeAll(() => jest.useFakeTimers());
283
- afterAll(() => jest.useRealTimers());
284
- test("returns local calendar month, not UTC month", () => {
285
- // Set clock to 22:30 UTC = 00:30 local in UTC+2 (Prague CEST)
286
- // In UTC the local month is still August (7); in Prague it is September (8).
287
- // The function must always agree with the local calendar, whatever the offset.
288
- jest.setSystemTime(new Date("2021-08-31T22:30:00Z"));
289
- const expectedMonth = new Date("2021-08-31T22:30:00Z").getMonth(); // local month
290
- const result = (0, get_current_year_month_and_date_1.getCurrentYearMonthAndDate)();
291
- expect(result.month).toBe(expectedMonth);
292
- });
293
- });
294
- describe("Range boundary checks", () => {
295
- test("canSelectRange: startDate on minBookingDate boundary is valid", () => {
296
- expect((0, can_select_range_1.canSelectRange)({
297
- startDate: "2026-10-26",
298
- endDate: null,
299
- minBookingDays: 1,
300
- minBookingDate: "2026-10-25",
301
- maxBookingDate: undefined,
302
- exactMinBookingDays: false,
303
- isDateBlocked: () => false,
304
- })).toBe(true);
305
- });
306
- test("canSelectRange: endDate within maxBookingDate boundary is valid", () => {
307
- expect((0, can_select_range_1.canSelectRange)({
308
- startDate: "2026-10-24",
309
- endDate: "2026-10-25",
310
- minBookingDays: 2,
311
- minBookingDate: undefined,
312
- maxBookingDate: "2026-10-26",
313
- exactMinBookingDays: false,
314
- isDateBlocked: () => false,
315
- })).toBe(true);
316
- });
317
- test("isDateHovered: date between startDate and hoveredDate is highlighted", () => {
318
- expect((0, is_date_hovered_1.isDateHovered)({
319
- date: "2026-10-26",
320
- startDate: "2026-10-24",
321
- endDate: null,
322
- isDateBlocked: () => false,
323
- hoveredDate: "2026-10-28",
324
- minBookingDays: 1,
325
- exactMinBookingDays: false,
326
- })).toBe(true);
327
- });
328
- });