Skip to content

Commit

Permalink
[datetime2] fix(DRP3): accept custom month/year formatters (#6490)
Browse files Browse the repository at this point in the history
  • Loading branch information
adidahiya authored Oct 23, 2023
1 parent b4d986e commit b9adf0a
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 12 deletions.
1 change: 1 addition & 0 deletions packages/datetime2/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
"classnames": "^2.3.1",
"date-fns": "^2.28.0",
"react-day-picker": "^8.5.1",
"react-innertext": "^1.1.5",
"tslib": "~2.5.0"
},
"peerDependencies": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@
*/

import classNames from "classnames";
import { format } from "date-fns";
import * as React from "react";
import { CaptionLabel, type CaptionProps, useDayPicker, useNavigation } from "react-day-picker";
import innerText from "react-innertext";

import { Button, DISPLAYNAME_PREFIX, HTMLSelect, type OptionProps } from "@blueprintjs/core";
import { DateUtils, Months } from "@blueprintjs/datetime";
Expand All @@ -36,7 +36,7 @@ import { DatePicker3Context } from "../date-picker3/datePicker3Context";
* @see https://react-day-picker.js.org/guides/custom-components
*/
export const DatePicker3Caption: React.FC<CaptionProps> = props => {
const { classNames: rdpClassNames, fromDate, toDate, labels } = useDayPicker();
const { classNames: rdpClassNames, formatters, fromDate, toDate, labels } = useDayPicker();
const { locale, reverseMonthAndYearMenus } = React.useContext(DatePicker3Context);

// non-null assertion because we define these values in defaultProps
Expand Down Expand Up @@ -80,13 +80,25 @@ export const DatePicker3Caption: React.FC<CaptionProps> = props => {
/>
);

const years: Array<number | OptionProps> = [minYear];
for (let year = minYear + 1; year <= maxYear; ++year) {
years.push(year);
}
// allow out-of-bounds years but disable the option. this handles the Dec 2016 case in #391.
// build the list of available years, relying on react-day-picker's default date-fns formatter or a
// user-provided formatter to localize the year "names"
const { formatYearCaption } = formatters;
const allYearOptions = React.useMemo<OptionProps[]>(() => {
const years: OptionProps[] = [];
for (let year = minYear; year <= maxYear; year++) {
const yearDate = new Date(year, 0);
const yearCaption = formatYearCaption(yearDate, { locale });
years.push({ label: innerText(yearCaption), value: year });
}
return years;
}, [formatYearCaption, maxYear, minYear, locale]);

// allow out-of-bounds years but disable the option.
// this handles the Dec 2016 case in https://github.com/palantir/blueprint/issues/391
if (displayYear > maxYear) {
years.push({ value: displayYear, disabled: true });
const displayYearDate = new Date(displayYear, 0);
const displayYearCaption = formatYearCaption(displayYearDate, { locale });
allYearOptions.push({ label: innerText(displayYearCaption), value: displayYear, disabled: true });
}

const handleMonthSelectChange = React.useCallback(
Expand All @@ -106,14 +118,18 @@ export const DatePicker3Caption: React.FC<CaptionProps> = props => {
const startMonth = displayYear === minYear ? fromDate!.getMonth() : 0;
const endMonth = displayYear === maxYear ? toDate!.getMonth() + 1 : 12;

// build the list of available months and localize their full names
// build the list of available months, relying on react-day-picker's default date-fns formatter or a
// user-provided formatter to localize the month names
const { formatMonthCaption } = formatters;
const allMonths = React.useMemo<string[]>(() => {
const months: string[] = [];
for (let i = Months.JANUARY; i <= Months.DECEMBER; i++) {
months.push(format(new Date(displayYear, i), "LLLL", { locale }));
const monthDate = new Date(displayYear, i);
const formattedMonth = formatMonthCaption(monthDate, { locale });
months.push(innerText(formattedMonth));
}
return months;
}, [displayYear, locale]);
}, [displayYear, formatMonthCaption, locale]);
const allMonthOptions = allMonths.map<OptionProps>((month, i) => ({ label: month, value: i }));
const availableMonthOptions = allMonthOptions.slice(startMonth, endMonth);
const displayedMonthText = allMonths[displayMonth];
Expand Down Expand Up @@ -155,7 +171,7 @@ export const DatePicker3Caption: React.FC<CaptionProps> = props => {
minimal={true}
onChange={handleYearSelectChange}
value={displayYear}
options={years}
options={allYearOptions}
/>
);

Expand Down
39 changes: 39 additions & 0 deletions packages/datetime2/test/components/dateRangePicker3Tests.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,45 @@ describe("<DateRangePicker3>", () => {
assert.isTrue(onDayClick.called);
});
});

describe("for i18n", () => {
// regression test for https://github.com/palantir/blueprint/issues/6489
it("DatePicker3Caption accepts custom month name formatters (contiguousCalendarMonths={false})", () => {
const CUSTOM_MONTH_NAMES = [
"First",
"Second",
"Third",
"Fourth",
"Fifth",
"Sixth",
"Seventh",
"Eighth",
"Ninth",
"Tenth",
"Eleventh",
"Twelfth",
];
const formatters = {
formatMonthCaption: (d: Date) => CUSTOM_MONTH_NAMES[d.getMonth()],
};
// try a month which is not January to make sure we're actually setting a value in the <select>
// and not just displaying the default value which is the first option
const initialMonthIndex = Months.AUGUST;
const initialMonth = new Date(2023, initialMonthIndex, 1);
const { left } = wrap(
<DateRangePicker3
initialMonth={initialMonth}
contiguousCalendarMonths={false}
dayPickerProps={{ formatters }}
/>,
);
const leftMonth = left.monthSelect.getDOMNode<HTMLSelectElement>();
assert.strictEqual(leftMonth.selectedIndex, initialMonthIndex);
for (const option of Array.from(leftMonth.options)) {
assert.strictEqual(option.text, CUSTOM_MONTH_NAMES[option.index]);
}
});
});
});

describe("initially displayed month", () => {
Expand Down

1 comment on commit b9adf0a

@adidahiya
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[datetime2] fix(DRP3): accept custom month/year formatters (#6490)

Build artifact links for this commit: documentation | landing | table | demo

This is an automated comment from the deploy-preview CircleCI job.

Please sign in to comment.