Skip to content

Commit 6b2ff59

Browse files
Merge pull request #6091 from gnato/feature/render-custom-day-name
feat: Add renderCustomDayName prop for custom weekday header rendering
2 parents ccda414 + 33b0fa2 commit 6b2ff59

7 files changed

Lines changed: 314 additions & 8 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ es
9898
tmp
9999

100100
.vscode
101+
.history
101102
*.iml
102103
.idea
103104

docs-site/src/components/Examples/config.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import ConfigureFloatingUI from "../../examples/ts/configureFloatingUI?raw";
1717
import CustomInput from "../../examples/ts/customInput?raw";
1818
import RenderCustomHeader from "../../examples/ts/renderCustomHeader?raw";
1919
import RenderCustomHeaderTwoMonths from "../../examples/ts/renderCustomHeaderTwoMonths?raw";
20+
import RenderCustomDayName from "../../examples/ts/renderCustomDayName?raw";
2021
import RenderCustomDay from "../../examples/ts/renderCustomDay?raw";
2122
import RenderCustomMonth from "../../examples/ts/renderCustomMonth?raw";
2223
import RenderCustomQuarter from "../../examples/ts/renderCustomQuarter?raw";
@@ -184,6 +185,10 @@ export const EXAMPLE_CONFIG: IExampleConfig[] = [
184185
title: "Custom header with two months displayed",
185186
component: RenderCustomHeaderTwoMonths,
186187
},
188+
{
189+
title: "Custom Day Names",
190+
component: RenderCustomDayName,
191+
},
187192
{
188193
title: "Custom Day",
189194
component: RenderCustomDay,
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
const RenderCustomDayName = () => {
2+
const [selectedDate, setSelectedDate] = useState<Date | null>(new Date());
3+
4+
const renderDayName = ({
5+
day,
6+
shortName,
7+
fullName,
8+
customDayNameCount,
9+
}: ReactDatePickerCustomDayNameProps): React.ReactNode => {
10+
// Example: Add emoji or custom styling to day names
11+
const dayEmojis: { [key: string]: string } = {
12+
Monday: "🌙",
13+
Tuesday: "🔥",
14+
Wednesday: "🌊",
15+
Thursday: "⚡",
16+
Friday: "🎉",
17+
Saturday: "🌞",
18+
Sunday: "☀️",
19+
};
20+
21+
const emoji = dayEmojis[fullName] || "";
22+
23+
// Apply different styling based on customDayNameCount when showing multiple months
24+
const style = customDayNameCount > 0 ? { color: "red" } : {};
25+
26+
return (
27+
<>
28+
<span className="react-datepicker__sr-only">{fullName}</span>
29+
<span aria-hidden="true" title={fullName} style={style}>
30+
{emoji}
31+
<br />
32+
{shortName}
33+
</span>
34+
</>
35+
);
36+
};
37+
38+
return (
39+
<DatePicker
40+
selected={selectedDate}
41+
onChange={setSelectedDate}
42+
renderCustomDayName={renderDayName}
43+
monthsShown={2}
44+
/>
45+
);
46+
};
47+
48+
render(RenderCustomDayName);

src/calendar.tsx

Lines changed: 46 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,14 @@ export interface ReactDatePickerCustomHeaderProps {
114114
};
115115
}
116116

117+
export interface ReactDatePickerCustomDayNameProps {
118+
day: Date;
119+
shortName: string;
120+
fullName: string;
121+
locale?: Locale;
122+
customDayNameCount: number;
123+
}
124+
117125
type CalendarProps = React.PropsWithChildren<
118126
Omit<
119127
YearDropdownProps,
@@ -195,6 +203,9 @@ type CalendarProps = React.PropsWithChildren<
195203
renderCustomHeader?: (
196204
props: ReactDatePickerCustomHeaderProps,
197205
) => React.ReactElement;
206+
renderCustomDayName?: (
207+
props: ReactDatePickerCustomDayNameProps,
208+
) => React.ReactNode;
198209
onYearMouseEnter?: YearProps["onYearMouseEnter"];
199210
onYearMouseLeave?: YearProps["onYearMouseLeave"];
200211
monthAriaLabelPrefix?: MonthProps["ariaLabelPrefix"];
@@ -477,7 +488,10 @@ export default class Calendar extends Component<CalendarProps, CalendarState> {
477488
);
478489
};
479490

480-
header = (date: Date = this.state.date): React.ReactElement[] => {
491+
header = (
492+
date: Date = this.state.date,
493+
customDayNameCount: number = 0,
494+
): React.ReactElement[] => {
481495
// Return empty array if date is invalid
482496
if (!isValid(date)) {
483497
return [];
@@ -507,11 +521,38 @@ export default class Calendar extends Component<CalendarProps, CalendarState> {
507521
[0, 1, 2, 3, 4, 5, 6].map((offset) => {
508522
const day = addDays(startOfWeek, offset);
509523
const weekDayName = this.formatWeekday(day, this.props.locale);
524+
const fullDayName = formatDate(day, "EEEE", this.props.locale);
510525

511526
const weekDayClassName = this.props.weekDayClassName
512527
? this.props.weekDayClassName(day)
513528
: undefined;
514529

530+
// Use custom render if provided
531+
if (this.props.renderCustomDayName) {
532+
const customContent = this.props.renderCustomDayName({
533+
day,
534+
shortName: weekDayName,
535+
fullName: fullDayName,
536+
locale: this.props.locale,
537+
customDayNameCount,
538+
});
539+
540+
return (
541+
<div
542+
key={offset}
543+
role="columnheader"
544+
className={clsx(
545+
"react-datepicker__day-name",
546+
weekDayClassName,
547+
disabled ? "react-datepicker__day-name--disabled" : "",
548+
)}
549+
>
550+
{customContent}
551+
</div>
552+
);
553+
}
554+
555+
// Default render
515556
return (
516557
<div
517558
key={offset}
@@ -522,9 +563,7 @@ export default class Calendar extends Component<CalendarProps, CalendarState> {
522563
disabled ? "react-datepicker__day-name--disabled" : "",
523564
)}
524565
>
525-
<span className="react-datepicker__sr-only">
526-
{formatDate(day, "EEEE", this.props.locale)}
527-
</span>
566+
<span className="react-datepicker__sr-only">{fullDayName}</span>
528567
<span aria-hidden="true">{weekDayName}</span>
529568
</div>
530569
);
@@ -871,9 +910,9 @@ export default class Calendar extends Component<CalendarProps, CalendarState> {
871910
);
872911
};
873912

874-
renderDayNamesHeader = (monthDate: Date) => (
913+
renderDayNamesHeader = (monthDate: Date, customDayNameCount: number = 0) => (
875914
<div className="react-datepicker__day-names" role="row">
876-
{this.header(monthDate)}
915+
{this.header(monthDate, customDayNameCount)}
877916
</div>
878917
);
879918

@@ -1055,7 +1094,7 @@ export default class Calendar extends Component<CalendarProps, CalendarState> {
10551094
selectingDate={this.state.selectingDate}
10561095
monthShowsDuplicateDaysEnd={monthShowsDuplicateDaysEnd}
10571096
monthShowsDuplicateDaysStart={monthShowsDuplicateDaysStart}
1058-
dayNamesHeader={this.renderDayNamesHeader(monthDate)}
1097+
dayNamesHeader={this.renderDayNamesHeader(monthDate, i)}
10591098
/>
10601099
</div>,
10611100
);

src/index.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,10 @@ export { default as CalendarContainer } from "./calendar_container";
6464

6565
export { registerLocale, setDefaultLocale, getDefaultLocale };
6666

67-
export { ReactDatePickerCustomHeaderProps } from "./calendar";
67+
export {
68+
ReactDatePickerCustomHeaderProps,
69+
ReactDatePickerCustomDayNameProps,
70+
} from "./calendar";
6871

6972
// Compares dates year+month combinations
7073
function hasPreSelectionChanged(

src/test/calendar_test.test.tsx

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2864,4 +2864,53 @@ describe("Calendar", () => {
28642864
);
28652865
});
28662866
});
2867+
2868+
describe("handleMonthChange with adjustDateOnChange but without setOpen", () => {
2869+
it("should call onSelect when adjustDateOnChange is true but setOpen is not provided", () => {
2870+
const onSelect = jest.fn();
2871+
const setPreSelection = jest.fn();
2872+
2873+
const { instance } = getCalendar({
2874+
adjustDateOnChange: true,
2875+
onSelect,
2876+
setPreSelection,
2877+
// setOpen is intentionally NOT provided to cover line 442
2878+
selected: new Date("2024-01-15T00:00:00"),
2879+
});
2880+
2881+
const targetMonth = new Date("2024-02-01T00:00:00");
2882+
act(() => {
2883+
instance?.handleMonthChange(targetMonth);
2884+
});
2885+
2886+
expect(onSelect).toHaveBeenCalled();
2887+
expect(setPreSelection).toHaveBeenCalled();
2888+
});
2889+
});
2890+
2891+
describe("header method with invalid date", () => {
2892+
it("should return empty array when date is invalid", () => {
2893+
const { instance } = getCalendar({
2894+
selected: new Date("2024-01-15T00:00:00"),
2895+
});
2896+
2897+
// Call header method with invalid date
2898+
const result = instance?.header(new Date("invalid"));
2899+
2900+
expect(result).toEqual([]);
2901+
});
2902+
2903+
it("should use default date parameter when not provided", () => {
2904+
const { instance } = getCalendar({
2905+
selected: new Date("2024-01-15T00:00:00"),
2906+
});
2907+
2908+
// Call header method without arguments to cover default parameter
2909+
const result = instance?.header();
2910+
2911+
expect(result).toBeDefined();
2912+
expect(Array.isArray(result)).toBe(true);
2913+
expect(result?.length).toBeGreaterThan(0);
2914+
});
2915+
});
28672916
});

0 commit comments

Comments
 (0)