I'm having a hard time making a custom header for my react datepicker component.
Anytime I click on any month or the popover itself, it closes the whole datepicker component and does not change the date.
Here is my code:
datepicker.tsx
"use client";
import React from "react";
import DatePicker, { DatePickerProps } from "react-datepicker";
import { Ionicons } from "../../../lib";
import { enUS, pt, ptBR } from "date-fns/locale";
import { useTranslation } from "react-i18next";
import "react-datepicker/dist/react-datepicker.css";
import "./datepicker.css";
import { CustomHeader } from "./custom-header";
function Calendar({ className, ...props }: DatePickerProps) {
const { i18n } = useTranslation();
function getLocale() {
switch (i18n.language) {
case "en":
return enUS;
case "pt-br":
return ptBR;
case "pt-pt":
return pt;
default:
return enUS;
}
}
const locale = getLocale();
type CustomDateInputProps = React.ButtonHTMLAttributes<HTMLButtonElement>;
const CustomDateInput = React.forwardRef<
HTMLButtonElement,
CustomDateInputProps
>(({ value, onClick }, ref) => (
<button
id="date-picker"
onClick={onClick}
className="w-full h-10 text-start text-foreground/80 bg-background rounded-lg border border-input px-10"
ref={ref}
>
<Ionicons
name="calendar-outline"
className="absolute left-2 top-1 text-input"
size={25}
/>
{value}
</button>
));
return (
<DatePicker
locale={locale}
calendarClassName="bg-background border-input"
popperClassName="border-input"
dateFormat="dd/MM/YYYY"
renderCustomHeader={({
date,
changeMonth,
changeYear,
decreaseMonth,
increaseMonth,
}) => (
<CustomHeader
date={date}
changeMonth={changeMonth}
changeYear={changeYear}
decreaseMonth={decreaseMonth}
increaseMonth={increaseMonth}
/>
)}
showPopperArrow={false}
customInput={<CustomDateInput />}
fixedHeight
shouldCloseOnSelect={false}
{...props}
/>
);
}
Calendar.displayName = "Calendar";
export { Calendar };
custom-header.tsx
import React from "react";
import { Ionicons } from "../../../lib";
import { Button } from "../button";
import { format } from "date-fns";
import { useTranslation } from "react-i18next";
import { enUS, pt, ptBR } from "date-fns/locale";
import { MonthDropdown } from "./month-dropdown";
import { YearDropdown } from "./year-dropdown";
type CustomHeaderProps = {
date: Date;
changeYear: (year: number) => void;
changeMonth: (month: number) => void;
decreaseMonth: () => void;
increaseMonth: () => void;
};
export const CustomHeader = ({
date,
changeYear,
changeMonth,
decreaseMonth,
increaseMonth,
}: CustomHeaderProps) => {
const { i18n } = useTranslation();
function getLocale() {
switch (i18n.language) {
case "en":
return enUS;
case "pt-br":
return ptBR;
case "pt-pt":
return pt;
default:
return enUS;
}
}
const locale = getLocale();
return (
<div className="flex flex-col gap-2">
<div className="flex px-2 items-center bg-background">
<Button size="sm-icon" variant="ghost" onPress={() => decreaseMonth()}>
<Ionicons
name="chevron-back-outline"
className="text-foreground/60"
size={15}
/>
</Button>
<span className="w-full text-foreground font-semibold">
{format(date, "MMMM yyyy", { locale })}
</span>
<Button size="sm-icon" variant="ghost" onPress={() => increaseMonth()}>
<Ionicons
name="chevron-forward-outline"
className="text-foreground/60"
size={15}
/>
</Button>
</div>
<div className="flex w-full justify-evenly">
<MonthDropdown date={date} changeMonth={changeMonth} />
<YearDropdown date={date} changeYear={changeYear} />
</div>
</div>
);
};
month-dropdown.tsx
import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { Button } from "../button";
import { Popover } from "../web-popover";
const months = {
en: [
"JAN",
"FEB",
"MAR",
"APR",
"MAY",
"JUN",
"JUL",
"AUG",
"SEP",
"OCT",
"NOV",
"DEC",
],
"pt-pt": [
"jan",
"fev",
"mar",
"abr",
"mai",
"jun",
"jul",
"ago",
"set",
"out",
"nov",
"dez",
],
"pt-br": [
"jan",
"fev",
"mar",
"abr",
"mai",
"jun",
"jul",
"ago",
"set",
"out",
"nov",
"dez",
],
};
export const MonthDropdown = ({
date,
changeMonth,
}: {
date: Date;
changeMonth: (mon: number) => void;
}) => {
const [month, setMonth] = useState<number>(date.getMonth());
const { i18n } = useTranslation();
useEffect(() => {
setMonth(date.getMonth());
}, [date]);
return (
<Popover>
<Popover.Trigger className="transition-colors duration-300 text-foreground px-2 py-1 border border-input rounded-md hover:bg-accent/30">
{months[i18n.language][month]}
</Popover.Trigger>
<Popover.Content className="bg-background p-2 gap-4">
<div className="grid grid-cols-3 gap-3">
{months[i18n.language].map((mon: string, idx: number) => (
<Button
key={idx}
size="sm"
variant={month === idx ? "default" : "outline"}
className="w-12"
onPress={() => {
changeMonth(idx);
setMonth(idx);
}}
>
{mon}
</Button>
))}
</div>
</Popover.Content>
</Popover>
);
};
The Popover component comes from shadcn/ui, react-datepicker is a few of the components that does not belong in the shadcn / radix environment. I've already tried many solutions but nothing worked.
