diff --git a/packages/react-calendar/README.md b/packages/react-calendar/README.md
index 887d3c4d..47b1f5c6 100644
--- a/packages/react-calendar/README.md
+++ b/packages/react-calendar/README.md
@@ -144,6 +144,7 @@ Displays a complete, interactive calendar.
| showNeighboringMonth | Whether days from previous or next month shall be rendered if the month doesn't start on the first day of the week or doesn't end on the last day of the week, respectively. | `true` | `false` |
| selectRange | Whether the user shall select two dates forming a range instead of just one. **Note**: This feature will make react-calendar return array with two dates regardless of returnValue setting. | `false` | `true` |
| showWeekNumbers | Whether week numbers shall be shown at the left of MonthView or not. | `false` | `true` |
+| tileAs | Component used for rendering calendar tiles. | `"button"` |
String: `"div"` React element: `MyCustomTile` |
| tileClassName | Class name(s) that will be applied to a given calendar item (day on month view, month on year view and so on). | n/a | String: `"class1 class2"` Array of strings: `["class1", "class2 class3"]` Function: `({ activeStartDate, date, view }) => view === 'month' && date.getDay() === 3 ? 'wednesday' : null` |
| tileContent | Allows to render custom content within a given calendar item (day on month view, month on year view and so on). | n/a | String: `"Sample"` React element: ` ` Function: `({ activeStartDate, date, view }) => view === 'month' && date.getDay() === 0 ? It's Sunday!
: null` |
| tileDisabled | Pass a function to determine if a certain day should be displayed as disabled. | n/a | Function: `({ activeStartDate, date, view }) => date.getDay() === 0` |
@@ -163,8 +164,10 @@ Displays a given month, year, decade and a century, respectively.
| maxDate | Maximum date that the user can select. Periods partially overlapped by maxDate will also be selectable, although react-calendar will ensure that no later date is selected. | n/a | Date: `new Date()` |
| minDate | Minimum date that the user can select. Periods partially overlapped by minDate will also be selectable, although react-calendar will ensure that no earlier date is selected. | n/a | Date: `new Date()` |
| onClick | Function called when the user clicks an item (day on month view, month on year view and so on). | n/a | `(value) => alert('New date is: ', value)` |
+| tileAs | Component used for rendering calendar tiles. | `"button"` | String: `"div"` React element: `MyCustomTile` |
| tileClassName | Class name(s) that will be applied to a given calendar item (day on month view, month on year view and so on). | n/a | String: `"class1 class2"` Array of strings: `["class1", "class2 class3"]` Function: `({ date, view }) => view === 'month' && date.getDay() === 3 ? 'wednesday' : null` |
| tileContent | Allows to render custom content within a given item (day on month view, month on year view and so on). **Note**: For tiles with custom content you might want to set fixed height of `react-calendar__tile` to ensure consistent layout. | n/a | `({ date, view }) => view === 'month' && date.getDay() === 0 ? It's Sunday!
: null` |
+| tileDisabled | Pass a function to determine if a certain day should be displayed as disabled. | n/a | Function: `({ activeStartDate, date, view }) => date.getDay() === 0` |
| value | Calendar value. Can be either one value or an array of two values. | n/a | Date: `new Date()` An array of dates: `[new Date(2017, 0, 1), new Date(2017, 7, 1)]` String: `2017-01-01` An array of strings: `['2017-01-01', '2017-08-01']` |
## Useful links
diff --git a/packages/react-calendar/src/Calendar.tsx b/packages/react-calendar/src/Calendar.tsx
index ffdd6227..dee907d2 100644
--- a/packages/react-calendar/src/Calendar.tsx
+++ b/packages/react-calendar/src/Calendar.tsx
@@ -31,6 +31,7 @@ import type {
LooseValue,
NavigationLabelFunc,
OnArgs,
+ OnClickEventType,
OnClickFunc,
OnClickWeekNumberFunc,
Range,
@@ -60,7 +61,7 @@ defaultMinDate.setFullYear(1, 0, 1);
defaultMinDate.setHours(0, 0, 0, 0);
const defaultMaxDate = new Date(8.64e15);
-export type CalendarProps = {
+export type CalendarProps = {
activeStartDate?: Date;
allowPartialRange?: boolean;
calendarType?: CalendarType | DeprecatedCalendarType;
@@ -90,12 +91,12 @@ export type CalendarProps = {
nextAriaLabel?: string;
nextLabel?: React.ReactNode;
onActiveStartDateChange?: ({ action, activeStartDate, value, view }: OnArgs) => void;
- onChange?: (value: Value, event: React.MouseEvent) => void;
- onClickDay?: OnClickFunc;
- onClickDecade?: OnClickFunc;
- onClickMonth?: OnClickFunc;
+ onChange?: (value: Value, event: OnClickEventType) => void;
+ onClickDay?: OnClickFunc;
+ onClickDecade?: OnClickFunc;
+ onClickMonth?: OnClickFunc;
onClickWeekNumber?: OnClickWeekNumberFunc;
- onClickYear?: OnClickFunc;
+ onClickYear?: OnClickFunc;
onDrillDown?: ({ action, activeStartDate, value, view }: OnArgs) => void;
onDrillUp?: ({ action, activeStartDate, value, view }: OnArgs) => void;
onViewChange?: ({ action, activeStartDate, value, view }: OnArgs) => void;
@@ -110,6 +111,7 @@ export type CalendarProps = {
showNavigation?: boolean;
showNeighboringMonth?: boolean;
showWeekNumbers?: boolean;
+ tileAs?: T;
tileClassName?: TileClassNameFunc | ClassName;
tileContent?: TileContentFunc | React.ReactNode;
tileDisabled?: TileDisabledFunc;
@@ -293,7 +295,10 @@ function areDatesEqual(date1?: Date | null, date2?: Date | null) {
return date1 instanceof Date && date2 instanceof Date && date1.getTime() === date2.getTime();
}
-const Calendar = forwardRef(function Calendar(props: CalendarProps, ref) {
+const Calendar = forwardRef(function Calendar(
+ props: CalendarProps,
+ ref,
+) {
const {
activeStartDate: activeStartDateProps,
allowPartialRange,
@@ -344,6 +349,7 @@ const Calendar = forwardRef(function Calendar(props: CalendarProps, ref) {
showNavigation = true,
showNeighboringMonth = true,
showWeekNumbers,
+ tileAs,
tileClassName,
tileContent,
tileDisabled,
@@ -457,7 +463,7 @@ const Calendar = forwardRef(function Calendar(props: CalendarProps, ref) {
);
const onClickTile = useCallback(
- (value: Date, event: React.MouseEvent) => {
+ (value: Date, event: OnClickEventType) => {
const callback = (() => {
switch (view) {
case 'century':
@@ -479,7 +485,7 @@ const Calendar = forwardRef(function Calendar(props: CalendarProps, ref) {
);
const drillDown = useCallback(
- (nextActiveStartDate: Date, event: React.MouseEvent) => {
+ (nextActiveStartDate: Date, event: OnClickEventType) => {
if (!drillDownAvailable) {
return;
}
@@ -573,7 +579,7 @@ const Calendar = forwardRef(function Calendar(props: CalendarProps, ref) {
]);
const onChange = useCallback(
- (rawNextValue: Date, event: React.MouseEvent) => {
+ (rawNextValue: Date, event: OnClickEventType) => {
const previousValue = value;
onClickTile(rawNextValue, event);
@@ -712,6 +718,7 @@ const Calendar = forwardRef(function Calendar(props: CalendarProps, ref) {
minDate,
onClick,
onMouseOver: selectRange ? onMouseOver : undefined,
+ tileAs,
tileClassName,
tileContent,
tileDisabled,
@@ -871,6 +878,7 @@ Calendar.propTypes = {
showNavigation: PropTypes.bool,
showNeighboringMonth: PropTypes.bool,
showWeekNumbers: PropTypes.bool,
+ tileAs: PropTypes.oneOfType([PropTypes.func, PropTypes.oneOf(['button', 'div'] as const)]),
tileClassName: PropTypes.oneOfType([PropTypes.func, isClassName]),
tileContent: PropTypes.oneOfType([PropTypes.func, PropTypes.node]),
tileDisabled: PropTypes.func,
diff --git a/packages/react-calendar/src/Tile.spec.tsx b/packages/react-calendar/src/Tile.spec.tsx
index 1d44595a..f452e0c9 100644
--- a/packages/react-calendar/src/Tile.spec.tsx
+++ b/packages/react-calendar/src/Tile.spec.tsx
@@ -15,12 +15,24 @@ describe(' component', () => {
view: 'month',
} satisfies React.ComponentProps;
- it('renders button properly', () => {
+ it('renders button properly by default', () => {
const { container } = render( );
expect(container.querySelector('button')).toBeInTheDocument();
});
+ it('renders button given tileAs="button"', () => {
+ const { container } = render( );
+
+ expect(container.querySelector('button')).toBeInTheDocument();
+ });
+
+ it('renders div given tileAs="div"', () => {
+ const { container } = render( );
+
+ expect(container.querySelector('div')).toBeInTheDocument();
+ });
+
it('passes onClick to button', () => {
const onClick = vi.fn();
diff --git a/packages/react-calendar/src/Tile.tsx b/packages/react-calendar/src/Tile.tsx
index 3e51f728..a2099ae0 100644
--- a/packages/react-calendar/src/Tile.tsx
+++ b/packages/react-calendar/src/Tile.tsx
@@ -3,13 +3,14 @@ import clsx from 'clsx';
import type {
ClassName,
+ OnClickEventType,
TileClassNameFunc,
TileContentFunc,
TileDisabledFunc,
View,
} from './shared/types.js';
-type TileProps = {
+type TileProps = {
activeStartDate: Date;
children: React.ReactNode;
classes?: string[];
@@ -20,16 +21,17 @@ type TileProps = {
maxDateTransform: (date: Date) => Date;
minDate?: Date;
minDateTransform: (date: Date) => Date;
- onClick?: (date: Date, event: React.MouseEvent) => void;
+ onClick?: (date: Date, event: OnClickEventType) => void;
onMouseOver?: (date: Date) => void;
style?: React.CSSProperties;
+ tileAs?: T;
tileClassName?: TileClassNameFunc | ClassName;
tileContent?: TileContentFunc | React.ReactNode;
tileDisabled?: TileDisabledFunc;
view: View;
};
-export default function Tile(props: TileProps) {
+export default function Tile(props: TileProps) {
const {
activeStartDate,
children,
@@ -44,6 +46,7 @@ export default function Tile(props: TileProps) {
onClick,
onMouseOver,
style,
+ tileAs,
tileClassName: tileClassNameProps,
tileContent: tileContentProps,
tileDisabled,
@@ -62,8 +65,10 @@ export default function Tile(props: TileProps) {
return typeof tileContentProps === 'function' ? tileContentProps(args) : tileContentProps;
}, [activeStartDate, date, tileContentProps, view]);
+ const TileComponent = tileAs || 'button';
+
return (
- date) ||
@@ -78,6 +83,6 @@ export default function Tile(props: TileProps) {
>
{formatAbbr ? {children} : children}
{tileContent}
-
+
);
}
diff --git a/packages/react-calendar/src/shared/propTypes.ts b/packages/react-calendar/src/shared/propTypes.ts
index 87a15a85..3f8b65f5 100644
--- a/packages/react-calendar/src/shared/propTypes.ts
+++ b/packages/react-calendar/src/shared/propTypes.ts
@@ -155,6 +155,7 @@ export const tileProps = {
onClick: PropTypes.func,
onMouseOver: PropTypes.func,
style: PropTypes.objectOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number])),
+ tileAs: PropTypes.oneOfType([PropTypes.func, PropTypes.oneOf(['button', 'div'] as const)]),
tileClassName: PropTypes.oneOfType([PropTypes.func, isClassName]),
tileContent: PropTypes.oneOfType([PropTypes.func, PropTypes.node]),
tileDisabled: PropTypes.func,
diff --git a/packages/react-calendar/src/shared/types.ts b/packages/react-calendar/src/shared/types.ts
index 4d5fb24d..d36af8e0 100644
--- a/packages/react-calendar/src/shared/types.ts
+++ b/packages/react-calendar/src/shared/types.ts
@@ -45,8 +45,14 @@ export type OnArgs = {
value: Value;
view: View;
};
+export type OnClickType = React.ComponentPropsWithoutRef['onClick'];
-export type OnClickFunc = (value: Date, event: React.MouseEvent) => void;
+export type OnClickEventType = Parameters>[1];
+
+export type OnClickFunc = (
+ value: Date,
+ event: OnClickEventType,
+) => void;
export type OnClickWeekNumberFunc = (
weekNumber: number,