Repository: gpbl/react-day-picker
Pull request #2572 (2024-11-02 21:44:51)

feat: return `dayPickerProps` from `useDayPicker`
Idea from: https://github.com/gpbl/react-day-picker/discussions/2562 by @ImXico




Patch:
diff --git a/src/DayPicker.tsx b/src/DayPicker.tsx
index cbc278166..7d2f35a5a 100644
--- a/src/DayPicker.tsx
+++ b/src/DayPicker.tsx
@@ -239,6 +239,7 @@ export function DayPicker(props: DayPickerProps) {
   const dataAttributes = getDataAttributes(props);
 
   const contextValue: DayPickerContext<DayPickerProps> = {
+    dayPickerProps: props,
     selected: selectedValue as SelectedValue<DayPickerProps>,
     select: select as SelectHandler<DayPickerProps>,
     isSelected,
diff --git a/src/useDayPicker.ts b/src/useDayPicker.ts
index a662ee863..701c45dac 100644
--- a/src/useDayPicker.ts
+++ b/src/useDayPicker.ts
@@ -2,6 +2,7 @@ import { createContext, useContext } from "react";
 
 import { CalendarDay } from "./classes/CalendarDay.js";
 import { CalendarMonth } from "./classes/CalendarMonth.js";
+import { DayPickerProps } from "./types/props.js";
 import type { SelectedValue, SelectHandler } from "./types/selection.js";
 import {
   ClassNames,
@@ -13,7 +14,7 @@ import {
   Styles
 } from "./types/shared.js";
 
-/** @private */
+/** @ignore */
 export const dayPickerContext = createContext<
   | DayPickerContext<{
       mode?: Mode | undefined;
@@ -59,6 +60,8 @@ export type DayPickerContext<
   labels: Labels;
   /** The formatters used to format the UI elements. */
   formatters: Formatters;
+  /** The props as passed to the DayPicker component. */
+  dayPickerProps: DayPickerProps;
 };
 
 /**
diff --git a/website/docs/guides/custom-components.mdx b/website/docs/guides/custom-components.mdx
index a4d2d9f19..c0eed25d2 100644
--- a/website/docs/guides/custom-components.mdx
+++ b/website/docs/guides/custom-components.mdx
@@ -130,18 +130,19 @@ import { useDayPicker } from "react-day-picker";
 
 The DayPicker context provides the current state of the calendar, including the selected days, modifiers, and navigation state.
 
-| Name            | Type                                                                          | Description                                    |
-| --------------- | ----------------------------------------------------------------------------- | ---------------------------------------------- |
-| `classNames`    | [`ClassNames`](../api/type-aliases/ClassNames.md)                             | The class names for the UI elements.           |
-| `components`    | [`CustomComponents`](../api/type-aliases/CustomComponents.md)                 | The components used internally by DayPicker.   |
-| `formatters`    | [`Formatters`](../api/type-aliases/Formatters.md)                             | The formatters used to format the UI elements. |
-| `getModifiers`  | (`day`) => [`Modifiers`](../api/type-aliases/Modifiers.md)                    | Returns the modifiers for the given day.       |
-| `goToMonth`     | (`month`) => `void`                                                           | Navigate to the specified month.               |
-| `isSelected`    | (`date`) => `boolean` \| `undefined`                                          | Whether the given date is selected.            |
-| `labels`        | [`Labels`](../api/type-aliases/Labels.md)                                     | The labels used in the user interface.         |
-| `months`        | [`CalendarMonth`](../api/classes/CalendarMonth.md)[]                          | The months displayed in the calendar.          |
-| `nextMonth`     | `Date` \| `undefined`                                                         | The next month to display.                     |
-| `previousMonth` | `Date` \| `undefined`                                                         | The previous month to display.                 |
-| `select`        | [`SelectHandler`](../api/type-aliases/SelectHandler.md)\<`T`\> \| `undefined` | Set a selection.                               |
-| `selected`      | [`SelectedValue`](../api/type-aliases/SelectedValue.md)\<`T`\> \| `undefined` | The selected date(s).                          |
-| `styles`        | `Partial`\<[`Styles`](../api/type-aliases/Styles.md)\> \| `undefined`         | The styles for the UI elements.                |
+| Name             | Type                                                                          | Description                                    |
+| ---------------- | ----------------------------------------------------------------------------- | ---------------------------------------------- |
+| `classNames`     | [`ClassNames`](../api/type-aliases/ClassNames.md)                             | The class names for the UI elements.           |
+| `components`     | [`CustomComponents`](../api/type-aliases/CustomComponents.md)                 | The components used internally by DayPicker.   |
+| `formatters`     | [`Formatters`](../api/type-aliases/Formatters.md)                             | The formatters used to format the UI elements. |
+| `getModifiers`   | (`day`) => [`Modifiers`](../api/type-aliases/Modifiers.md)                    | Returns the modifiers for the given day.       |
+| `goToMonth`      | (`month`) => `void`                                                           | Navigate to the specified month.               |
+| `isSelected`     | (`date`) => `boolean` \| `undefined`                                          | Whether the given date is selected.            |
+| `labels`         | [`Labels`](../api/type-aliases/Labels.md)                                     | The labels used in the user interface.         |
+| `months`         | [`CalendarMonth`](../api/classes/CalendarMonth.md)[]                          | The months displayed in the calendar.          |
+| `nextMonth`      | `Date` \| `undefined`                                                         | The next month to display.                     |
+| `previousMonth`  | `Date` \| `undefined`                                                         | The previous month to display.                 |
+| `select`         | [`SelectHandler`](../api/type-aliases/SelectHandler.md)\<`T`\> \| `undefined` | Set a selection.                               |
+| `selected`       | [`SelectedValue`](../api/type-aliases/SelectedValue.md)\<`T`\> \| `undefined` | The selected date(s).                          |
+| `styles`         | `Partial`\<[`Styles`](../api/type-aliases/Styles.md)\> \| `undefined`         | The styles for the UI elements.                |
+| `dayPickerProps` | [`DayPickerProps`](../api/type-aliases/DayPickerProps.md)                     | The props passed to the DayPicker component.   |


Test patch:
diff --git a/src/useDayPicker.test.tsx b/src/useDayPicker.test.tsx
new file mode 100644
index 000000000..2a7a07766
--- /dev/null
+++ b/src/useDayPicker.test.tsx
@@ -0,0 +1,134 @@
+import React from "react";
+
+import { renderHook } from "@testing-library/react";
+
+import { DayFlag, SelectionState, UI } from "./UI";
+import { CalendarDay } from "./classes/CalendarDay";
+import { CalendarMonth } from "./classes/CalendarMonth";
+import { DayPickerProps } from "./types/props";
+import { Modifiers } from "./types/shared";
+import {
+  DayPickerContext,
+  dayPickerContext,
+  useDayPicker
+} from "./useDayPicker";
+
+describe("useDayPicker", () => {
+  const mockContextValue: DayPickerContext<{
+    required: false;
+    mode: "single";
+  }> = {
+    months: [new CalendarMonth(new Date(), [])],
+    nextMonth: new Date(),
+    previousMonth: new Date(),
+    goToMonth: jest.fn(),
+    getModifiers: jest.fn((day: CalendarDay) => ({}) as Modifiers),
+    selected: undefined,
+    select: jest.fn(),
+    isSelected: jest.fn((date: Date) => false),
+    components: {
+      Button: jest.fn(),
+      Chevron: jest.fn(),
+      CaptionLabel: jest.fn(),
+      Day: jest.fn(),
+      DayButton: jest.fn(),
+      Dropdown: jest.fn(),
+      DropdownNav: jest.fn(),
+      Footer: jest.fn(),
+      Month: jest.fn(),
+      MonthCaption: jest.fn(),
+      MonthGrid: jest.fn(),
+      Months: jest.fn(),
+      Nav: jest.fn(),
+      Option: jest.fn(),
+      PreviousMonthButton: jest.fn(),
+      NextMonthButton: jest.fn(),
+      Root: jest.fn(),
+      Select: jest.fn(),
+      Weeks: jest.fn(),
+      Week: jest.fn(),
+      Weekday: jest.fn(),
+      Weekdays: jest.fn(),
+      WeekNumber: jest.fn(),
+      WeekNumberHeader: jest.fn(),
+      MonthsDropdown: jest.fn(),
+      YearsDropdown: jest.fn()
+    },
+    classNames: {
+      [UI.Root]: "",
+      [UI.Chevron]: "",
+      [UI.Day]: "",
+      [UI.DayButton]: "",
+      [UI.CaptionLabel]: "",
+      [UI.Dropdowns]: "",
+      [UI.Dropdown]: "",
+      [UI.DropdownRoot]: "",
+      [UI.Footer]: "",
+      [UI.MonthGrid]: "",
+      [UI.MonthCaption]: "",
+      [UI.MonthsDropdown]: "",
+      [UI.Month]: "",
+      [UI.Months]: "",
+      [UI.Nav]: "",
+      [UI.NextMonthButton]: "",
+      [UI.PreviousMonthButton]: "",
+      [UI.Week]: "",
+      [UI.Weeks]: "",
+      [UI.Weekday]: "",
+      [UI.Weekdays]: "",
+      [UI.WeekNumber]: "",
+      [UI.WeekNumberHeader]: "",
+      [UI.YearsDropdown]: "",
+      [SelectionState.range_end]: "",
+      [SelectionState.range_middle]: "",
+      [SelectionState.range_start]: "",
+      [SelectionState.selected]: "",
+      [DayFlag.disabled]: "",
+      [DayFlag.hidden]: "",
+      [DayFlag.outside]: "",
+      [DayFlag.focused]: "",
+      [DayFlag.today]: ""
+    },
+    styles: {},
+    labels: {
+      labelNav: jest.fn(),
+      labelGrid: jest.fn(),
+      labelGridcell: jest.fn(),
+      labelMonthDropdown: jest.fn(),
+      labelYearDropdown: jest.fn(),
+      labelNext: jest.fn(),
+      labelPrevious: jest.fn(),
+      labelDayButton: jest.fn(),
+      labelDay: jest.fn(),
+      labelWeekday: jest.fn(),
+      labelWeekNumber: jest.fn(),
+      labelWeekNumberHeader: jest.fn()
+    },
+    formatters: {
+      formatCaption: jest.fn(),
+      formatDay: jest.fn(),
+      formatMonthDropdown: jest.fn(),
+      formatMonthCaption: jest.fn(),
+      formatWeekNumber: jest.fn(),
+      formatWeekNumberHeader: jest.fn(),
+      formatWeekdayName: jest.fn(),
+      formatYearDropdown: jest.fn(),
+      formatYearCaption: jest.fn()
+    },
+    dayPickerProps: {
+      mode: "single",
+      required: false
+    } as DayPickerProps
+  };
+
+  it("should return the context value when used within a DayPicker provider", () => {
+    const wrapper = ({ children }: { children: React.ReactNode }) => (
+      <dayPickerContext.Provider value={mockContextValue}>
+        {children}
+      </dayPickerContext.Provider>
+    );
+
+    const { result } = renderHook(() => useDayPicker(), { wrapper });
+    expect(result.current).toEqual(mockContextValue);
+  });
+});