Skip to content
Merged
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 .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ es
tmp

.vscode
.history
*.iml
.idea

Expand Down
5 changes: 5 additions & 0 deletions docs-site/src/components/Examples/config.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import ConfigureFloatingUI from "../../examples/ts/configureFloatingUI?raw";
import CustomInput from "../../examples/ts/customInput?raw";
import RenderCustomHeader from "../../examples/ts/renderCustomHeader?raw";
import RenderCustomHeaderTwoMonths from "../../examples/ts/renderCustomHeaderTwoMonths?raw";
import RenderCustomDayName from "../../examples/ts/renderCustomDayName?raw";
import RenderCustomDay from "../../examples/ts/renderCustomDay?raw";
import RenderCustomMonth from "../../examples/ts/renderCustomMonth?raw";
import RenderCustomQuarter from "../../examples/ts/renderCustomQuarter?raw";
Expand Down Expand Up @@ -184,6 +185,10 @@ export const EXAMPLE_CONFIG: IExampleConfig[] = [
title: "Custom header with two months displayed",
component: RenderCustomHeaderTwoMonths,
},
{
title: "Custom Day Names",
component: RenderCustomDayName,
},
{
title: "Custom Day",
component: RenderCustomDay,
Expand Down
48 changes: 48 additions & 0 deletions docs-site/src/examples/ts/renderCustomDayName.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
const RenderCustomDayName = () => {
const [selectedDate, setSelectedDate] = useState<Date | null>(new Date());

const renderDayName = ({
day,
shortName,
fullName,
customDayNameCount,
}: ReactDatePickerCustomDayNameProps): React.ReactNode => {
// Example: Add emoji or custom styling to day names
const dayEmojis: { [key: string]: string } = {
Monday: "🌙",
Tuesday: "🔥",
Wednesday: "🌊",
Thursday: "⚡",
Friday: "🎉",
Saturday: "🌞",
Sunday: "☀️",
};

const emoji = dayEmojis[fullName] || "";

// Apply different styling based on customDayNameCount when showing multiple months
const style = customDayNameCount > 0 ? { color: "red" } : {};

return (
<>
<span className="react-datepicker__sr-only">{fullName}</span>
<span aria-hidden="true" title={fullName} style={style}>
{emoji}
<br />
{shortName}
</span>
</>
);
};

return (
<DatePicker
selected={selectedDate}
onChange={setSelectedDate}
renderCustomDayName={renderDayName}
monthsShown={2}
/>
);
};

render(RenderCustomDayName);
53 changes: 46 additions & 7 deletions src/calendar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,14 @@ export interface ReactDatePickerCustomHeaderProps {
};
}

export interface ReactDatePickerCustomDayNameProps {
day: Date;
shortName: string;
fullName: string;
locale?: Locale;
customDayNameCount: number;
}

type CalendarProps = React.PropsWithChildren<
Omit<
YearDropdownProps,
Expand Down Expand Up @@ -195,6 +203,9 @@ type CalendarProps = React.PropsWithChildren<
renderCustomHeader?: (
props: ReactDatePickerCustomHeaderProps,
) => React.ReactElement;
renderCustomDayName?: (
props: ReactDatePickerCustomDayNameProps,
) => React.ReactNode;
onYearMouseEnter?: YearProps["onYearMouseEnter"];
onYearMouseLeave?: YearProps["onYearMouseLeave"];
monthAriaLabelPrefix?: MonthProps["ariaLabelPrefix"];
Expand Down Expand Up @@ -477,7 +488,10 @@ export default class Calendar extends Component<CalendarProps, CalendarState> {
);
};

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

const weekDayClassName = this.props.weekDayClassName
? this.props.weekDayClassName(day)
: undefined;

// Use custom render if provided
if (this.props.renderCustomDayName) {
const customContent = this.props.renderCustomDayName({
day,
shortName: weekDayName,
fullName: fullDayName,
locale: this.props.locale,
customDayNameCount,
});

return (
<div
key={offset}
role="columnheader"
className={clsx(
"react-datepicker__day-name",
weekDayClassName,
disabled ? "react-datepicker__day-name--disabled" : "",
)}
>
{customContent}
</div>
);
}

// Default render
return (
<div
key={offset}
Expand All @@ -522,9 +563,7 @@ export default class Calendar extends Component<CalendarProps, CalendarState> {
disabled ? "react-datepicker__day-name--disabled" : "",
)}
>
<span className="react-datepicker__sr-only">
{formatDate(day, "EEEE", this.props.locale)}
</span>
<span className="react-datepicker__sr-only">{fullDayName}</span>
<span aria-hidden="true">{weekDayName}</span>
</div>
);
Expand Down Expand Up @@ -871,9 +910,9 @@ export default class Calendar extends Component<CalendarProps, CalendarState> {
);
};

renderDayNamesHeader = (monthDate: Date) => (
renderDayNamesHeader = (monthDate: Date, customDayNameCount: number = 0) => (
<div className="react-datepicker__day-names" role="row">
{this.header(monthDate)}
{this.header(monthDate, customDayNameCount)}
</div>
);

Expand Down Expand Up @@ -1055,7 +1094,7 @@ export default class Calendar extends Component<CalendarProps, CalendarState> {
selectingDate={this.state.selectingDate}
monthShowsDuplicateDaysEnd={monthShowsDuplicateDaysEnd}
monthShowsDuplicateDaysStart={monthShowsDuplicateDaysStart}
dayNamesHeader={this.renderDayNamesHeader(monthDate)}
dayNamesHeader={this.renderDayNamesHeader(monthDate, i)}
/>
</div>,
);
Expand Down
5 changes: 4 additions & 1 deletion src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,10 @@ export { default as CalendarContainer } from "./calendar_container";

export { registerLocale, setDefaultLocale, getDefaultLocale };

export { ReactDatePickerCustomHeaderProps } from "./calendar";
export {
ReactDatePickerCustomHeaderProps,
ReactDatePickerCustomDayNameProps,
} from "./calendar";

// Compares dates year+month combinations
function hasPreSelectionChanged(
Expand Down
49 changes: 49 additions & 0 deletions src/test/calendar_test.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2864,4 +2864,53 @@ describe("Calendar", () => {
);
});
});

describe("handleMonthChange with adjustDateOnChange but without setOpen", () => {
it("should call onSelect when adjustDateOnChange is true but setOpen is not provided", () => {
const onSelect = jest.fn();
const setPreSelection = jest.fn();

const { instance } = getCalendar({
adjustDateOnChange: true,
onSelect,
setPreSelection,
// setOpen is intentionally NOT provided to cover line 442
selected: new Date("2024-01-15T00:00:00"),
});

const targetMonth = new Date("2024-02-01T00:00:00");
act(() => {
instance?.handleMonthChange(targetMonth);
});

expect(onSelect).toHaveBeenCalled();
expect(setPreSelection).toHaveBeenCalled();
});
});

describe("header method with invalid date", () => {
it("should return empty array when date is invalid", () => {
const { instance } = getCalendar({
selected: new Date("2024-01-15T00:00:00"),
});

// Call header method with invalid date
const result = instance?.header(new Date("invalid"));

expect(result).toEqual([]);
});

it("should use default date parameter when not provided", () => {
const { instance } = getCalendar({
selected: new Date("2024-01-15T00:00:00"),
});

// Call header method without arguments to cover default parameter
const result = instance?.header();

expect(result).toBeDefined();
expect(Array.isArray(result)).toBe(true);
expect(result?.length).toBeGreaterThan(0);
});
});
});
Loading
Loading