♻️(react) extract duplicated utility functions and types
DateRangePicker and DatePicker would share quite a lot of common logics. Make sure the code elements are factorised easily reusable in each components.
This commit is contained in:
committed by
aleb_the_flash
parent
d16ada2825
commit
0dae71aa92
2
packages/react/src/components/Forms/DatePicker/types.ts
Normal file
2
packages/react/src/components/Forms/DatePicker/types.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export type StringOrDate = string | Date;
|
||||
export type StringsOrDateRange = [StringOrDate, StringOrDate];
|
||||
163
packages/react/src/components/Forms/DatePicker/utils.spec.ts
Normal file
163
packages/react/src/components/Forms/DatePicker/utils.spec.ts
Normal file
@@ -0,0 +1,163 @@
|
||||
import { CalendarDate, DateValue, parseDate } from "@internationalized/date";
|
||||
import {
|
||||
parseCalendarDate,
|
||||
parseRangeCalendarDate,
|
||||
} from ":/components/Forms/DatePicker/utils";
|
||||
import { StringOrDate } from ":/components/Forms/DatePicker/types";
|
||||
|
||||
const expectDateToBeEqual = (
|
||||
parsedDate: CalendarDate | DateValue | undefined,
|
||||
expectedYear: number,
|
||||
expectedMonth: number,
|
||||
expectedDay: number
|
||||
) => {
|
||||
expect(parsedDate).not.eq(undefined);
|
||||
expect(parsedDate?.year === expectedYear);
|
||||
expect(parsedDate?.month === expectedMonth);
|
||||
expect(parsedDate?.day === expectedDay);
|
||||
};
|
||||
|
||||
describe("parseCalendarDate", () => {
|
||||
it.each([
|
||||
[2023, 4, 12],
|
||||
[2022, 1, 1],
|
||||
[2022, 12, 31],
|
||||
[2022, 5, 2],
|
||||
])("parse an iso string date", (year: number, month: number, day: number) => {
|
||||
const d = new Date(year, month, day);
|
||||
const parsedDate = parseCalendarDate(d.toISOString());
|
||||
expectDateToBeEqual(parsedDate, year, month, day);
|
||||
});
|
||||
|
||||
it.each([
|
||||
[2023, 4, 12],
|
||||
[2022, 1, 1],
|
||||
[2022, 12, 31],
|
||||
[2022, 5, 2],
|
||||
])(
|
||||
"parse a 'YYYY-MM-DD' date",
|
||||
(year: number, month: number, day: number) => {
|
||||
const stringDate = `${year}-${month}-${day}`;
|
||||
const parsedDate = parseCalendarDate(stringDate);
|
||||
expectDateToBeEqual(parsedDate, year, month, day);
|
||||
}
|
||||
);
|
||||
|
||||
it.each([
|
||||
[2023, 4, 12],
|
||||
[2022, 1, 1],
|
||||
[2022, 12, 31],
|
||||
[2022, 5, 2],
|
||||
])("parse a datetime date", (year: number, month: number, day: number) => {
|
||||
const date = new Date(year, month, day);
|
||||
const parsedDate = parseCalendarDate(date);
|
||||
expectDateToBeEqual(parsedDate, year, month, day);
|
||||
});
|
||||
|
||||
it.each([undefined, ""])("parse an empty or null date", (date) => {
|
||||
const parsedDate = parseCalendarDate(date);
|
||||
expect(parsedDate).eq(undefined);
|
||||
});
|
||||
|
||||
it.each([
|
||||
"35/04/2024",
|
||||
"11 janvier 20O2",
|
||||
"22.04.2022",
|
||||
"22-4-2022",
|
||||
"2022-04-1T00:00:00-00:00",
|
||||
"2022-04-01 T00:00:00-00:00",
|
||||
])("parse a wrong date", (wrongFormattedDate) => {
|
||||
expect(() => parseCalendarDate(wrongFormattedDate)).toThrow(
|
||||
"Invalid date format when initializing props on DatePicker component"
|
||||
);
|
||||
});
|
||||
|
||||
it.each([
|
||||
[4, "2023-04-22"],
|
||||
[5, "2023-04-30"],
|
||||
[7, "2023-04-22"],
|
||||
])(
|
||||
"parse date to locale timezone, converted to day before",
|
||||
(offset: number, dateString: string) => {
|
||||
// Get the local offset
|
||||
const localOffset = new Date().getTimezoneOffset() / 60;
|
||||
const formattedOffset = offset.toLocaleString("en-US", {
|
||||
minimumIntegerDigits: 2,
|
||||
});
|
||||
|
||||
// Create an ISO string in a timezone that is the day after in local timezone
|
||||
const offsetISODate = `${dateString}T${
|
||||
24 - (offset - localOffset - 1)
|
||||
}:00:00-${formattedOffset}:00`;
|
||||
|
||||
// Parse this ISO string, that should be converted to local timezone
|
||||
const parsedDate = parseCalendarDate(offsetISODate);
|
||||
|
||||
// Make sure the ISO string have been converted to the local timezone
|
||||
const nextDay = parseDate(dateString).add({ days: 1 });
|
||||
expect(parsedDate?.compare(nextDay)).eq(0);
|
||||
}
|
||||
);
|
||||
|
||||
it.each([
|
||||
[4, "2023-04-22"],
|
||||
[5, "2023-04-30"],
|
||||
[7, "2023-04-22"],
|
||||
])(
|
||||
"parse date to locale timezone, converted to same day",
|
||||
(offset: number, dateString: string) => {
|
||||
// Get the local offset
|
||||
const localOffset = new Date().getTimezoneOffset() / 60;
|
||||
const formattedOffset = offset.toLocaleString("en-US", {
|
||||
minimumIntegerDigits: 2,
|
||||
});
|
||||
|
||||
// Create an ISO string in a timezone that is the day after in local timezone
|
||||
const offsetISODate = `${dateString}T${
|
||||
24 - (offset - localOffset + 2)
|
||||
}:00:00-${formattedOffset}:00`;
|
||||
|
||||
// Parse this ISO string, that should be converted to local timezone
|
||||
const parsedDate = parseCalendarDate(offsetISODate);
|
||||
const sameDay = parseDate(dateString);
|
||||
|
||||
// Make sure the ISO string have been converted to the local timezone
|
||||
expect(parsedDate?.compare(sameDay)).eq(0);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
describe("parseRangeCalendarDate", () => {
|
||||
it.each([
|
||||
["2023-03-22", "2023-04-22"],
|
||||
[new Date(2023, 3, 22), "2023-04-22"],
|
||||
["2023-03-22", new Date(2023, 4, 22)],
|
||||
["2022-03-22T00:00:00-00:00", "2023-04-22"],
|
||||
])("parse a date range", (start: string | Date, end: string | Date) => {
|
||||
const range = parseRangeCalendarDate([start, end]);
|
||||
expectDateToBeEqual(range?.start, 2023, 3, 22);
|
||||
expectDateToBeEqual(range?.end, 2023, 4, 22);
|
||||
});
|
||||
|
||||
it.each([
|
||||
["", "2023-03-22"],
|
||||
["2023-03-22", ""],
|
||||
])(
|
||||
"parse a partially null or empty date range",
|
||||
(start: StringOrDate, end: StringOrDate) => {
|
||||
expect(parseRangeCalendarDate([start, end])).eq(undefined);
|
||||
}
|
||||
);
|
||||
|
||||
it("parse an undefined date range", () => {
|
||||
expect(parseRangeCalendarDate(undefined)).eq(undefined);
|
||||
});
|
||||
|
||||
it("parse an inverted date range", () => {
|
||||
// Utils function accepts start date superior to the end date
|
||||
// However, DateRangePicker will trigger an error with the parsed range.
|
||||
const range = parseRangeCalendarDate(["2023-05-22", "2023-04-22"]);
|
||||
expectDateToBeEqual(range?.start, 2023, 5, 22);
|
||||
expectDateToBeEqual(range?.end, 2023, 4, 22);
|
||||
});
|
||||
});
|
||||
48
packages/react/src/components/Forms/DatePicker/utils.ts
Normal file
48
packages/react/src/components/Forms/DatePicker/utils.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import {
|
||||
CalendarDate,
|
||||
parseAbsoluteToLocal,
|
||||
toCalendarDate,
|
||||
} from "@internationalized/date";
|
||||
import { DateRange } from "react-aria";
|
||||
import {
|
||||
StringOrDate,
|
||||
StringsOrDateRange,
|
||||
} from ":/components/Forms/DatePicker/types";
|
||||
import { DatePickerAuxSubProps } from ":/components/Forms/DatePicker/DatePickerAux";
|
||||
|
||||
export const parseCalendarDate = (
|
||||
rawDate: StringOrDate | undefined
|
||||
): undefined | CalendarDate => {
|
||||
if (!rawDate) {
|
||||
return undefined;
|
||||
}
|
||||
try {
|
||||
const ISODateString = new Date(rawDate).toISOString();
|
||||
return toCalendarDate(parseAbsoluteToLocal(ISODateString));
|
||||
} catch (e) {
|
||||
throw new Error(
|
||||
"Invalid date format when initializing props on DatePicker component"
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export const parseRangeCalendarDate = (
|
||||
rawRange: StringsOrDateRange | undefined
|
||||
): DateRange | undefined => {
|
||||
if (!rawRange || !rawRange[0] || !rawRange[1]) {
|
||||
return undefined;
|
||||
}
|
||||
return {
|
||||
start: parseCalendarDate(rawRange[0])!,
|
||||
end: parseCalendarDate(rawRange[1])!,
|
||||
};
|
||||
};
|
||||
|
||||
export const getDefaultPickerOptions = (props: DatePickerAuxSubProps): any => ({
|
||||
minValue: parseCalendarDate(props.minValue),
|
||||
maxValue: parseCalendarDate(props.maxValue),
|
||||
shouldCloseOnSelect: true,
|
||||
granularity: "day",
|
||||
isDisabled: props.disabled,
|
||||
label: props.label,
|
||||
});
|
||||
Reference in New Issue
Block a user