diff --git a/docs-site/src/components/Examples/config.tsx b/docs-site/src/components/Examples/config.tsx index b1ab79704..634d0179e 100644 --- a/docs-site/src/components/Examples/config.tsx +++ b/docs-site/src/components/Examples/config.tsx @@ -56,6 +56,7 @@ import IncludeDatesMonthPicker from "../../examples/ts/includeDatesMonthPicker?r import IncludeTimes from "../../examples/ts/includeTimes?raw"; import InjectTimes from "../../examples/ts/injectTimes?raw"; import Inline from "../../examples/ts/inline?raw"; +import InlineDisabled from "../../examples/ts/disabledInline?raw"; import InlineVisible from "../../examples/ts/inlineVisible?raw"; import TimeInput from "../../examples/ts/timeInput?raw"; import Locale from "../../examples/ts/locale?raw"; @@ -338,6 +339,10 @@ export const EXAMPLE_CONFIG: IExampleConfig[] = [ title: "Inline version", component: Inline, }, + { + title: "Inline version disabled", + component: InlineDisabled, + }, { title: "Button to show Inline version", component: InlineVisible, diff --git a/docs-site/src/examples/ts/disabledInline.tsx b/docs-site/src/examples/ts/disabledInline.tsx new file mode 100644 index 000000000..97f92a907 --- /dev/null +++ b/docs-site/src/examples/ts/disabledInline.tsx @@ -0,0 +1,14 @@ +const DisabledInline = () => { + const [selectedDate, setSelectedDate] = useState(null); + + return ( + setSelectedDate(date)} + disabled + inline + /> + ); +}; + +render(DisabledInline); diff --git a/src/calendar.tsx b/src/calendar.tsx index 9b00b22ef..e04158010 100644 --- a/src/calendar.tsx +++ b/src/calendar.tsx @@ -468,6 +468,7 @@ export default class Calendar extends Component { }; header = (date: Date = this.state.date): React.ReactElement[] => { + const disabled = this.props.disabled; const startOfWeek = getStartOfWeek( date, this.props.locale, @@ -477,7 +478,11 @@ export default class Calendar extends Component { const dayNames: React.ReactElement[] = []; if (this.props.showWeekNumbers) { dayNames.push( -
+
Week number
, @@ -496,7 +501,11 @@ export default class Calendar extends Component {
{formatDate(day, "EEEE", this.props.locale)} @@ -551,6 +560,9 @@ export default class Calendar extends Component { let allPrevDaysDisabled; switch (true) { + case this.props.disabled: + allPrevDaysDisabled = true; + break; case this.props.showMonthYearPicker: allPrevDaysDisabled = yearDisabledBefore(this.state.date, this.props); break; @@ -662,6 +674,9 @@ export default class Calendar extends Component { let allNextDaysDisabled: boolean; switch (true) { + case this.props.disabled: + allNextDaysDisabled = true; + break; case this.props.showMonthYearPicker: allNextDaysDisabled = yearDisabledAfter(this.state.date, this.props); break; diff --git a/src/date_utils.ts b/src/date_utils.ts index 12600b89b..eee05b9b0 100644 --- a/src/date_utils.ts +++ b/src/date_utils.ts @@ -728,6 +728,10 @@ export interface DateFilterOptions { yearItemNumber?: number; } +export type DateFilterOptionsWithDisabled = DateFilterOptions & { + disabled?: boolean; +}; + /** * Checks if a day is disabled. * @@ -745,8 +749,13 @@ export function isDayDisabled( includeDates, includeDateIntervals, filterDate, - }: DateFilterOptions = {}, + disabled, + }: DateFilterOptionsWithDisabled = {}, ): boolean { + if (disabled) { + return true; + } + return ( isOutOfBounds(day, { minDate, maxDate }) || (excludeDates && @@ -898,11 +907,21 @@ export function isQuarterDisabled( excludeDates, includeDates, filterDate, + disabled, }: Pick< - DateFilterOptions, - "minDate" | "maxDate" | "excludeDates" | "includeDates" | "filterDate" + DateFilterOptionsWithDisabled, + | "minDate" + | "maxDate" + | "excludeDates" + | "includeDates" + | "filterDate" + | "disabled" > = {}, ): boolean { + if (disabled) { + return true; + } + return ( isOutOfBounds(quarter, { minDate, maxDate }) || excludeDates?.some((excludeDate) => @@ -941,11 +960,21 @@ export function isYearDisabled( excludeDates, includeDates, filterDate, + disabled, }: Pick< - DateFilterOptions, - "minDate" | "maxDate" | "excludeDates" | "includeDates" | "filterDate" + DateFilterOptionsWithDisabled, + | "minDate" + | "maxDate" + | "excludeDates" + | "includeDates" + | "filterDate" + | "disabled" > = {}, ): boolean { + if (disabled) { + return true; + } + const date = new Date(year, 0, 1); return ( isOutOfBounds(date, { diff --git a/src/day.tsx b/src/day.tsx index 4413c8873..42291bda3 100644 --- a/src/day.tsx +++ b/src/day.tsx @@ -16,7 +16,7 @@ import { getDayOfWeekCode, getStartOfWeek, formatDate, - type DateFilterOptions, + type DateFilterOptionsWithDisabled, type DateNumberType, type Locale, type HolidaysMap, @@ -25,7 +25,7 @@ import { interface DayProps extends Pick< - DateFilterOptions, + DateFilterOptionsWithDisabled, | "minDate" | "maxDate" | "excludeDates" @@ -33,6 +33,7 @@ interface DayProps | "includeDateIntervals" | "includeDates" | "filterDate" + | "disabled" > { ariaLabelPrefixWhenEnabled?: string; ariaLabelPrefixWhenDisabled?: string; @@ -210,6 +211,7 @@ export default class Day extends Component { includeDateIntervals: this.props.includeDateIntervals, includeDates: this.props.includeDates, filterDate: this.props.filterDate, + disabled: this.props.disabled, }); isExcluded = () => diff --git a/src/month.tsx b/src/month.tsx index c39054d23..e8681d04d 100644 --- a/src/month.tsx +++ b/src/month.tsx @@ -250,6 +250,7 @@ export default class Month extends Component { includeDateIntervals: this.props.includeDateIntervals, includeDates: this.props.includeDates, filterDate: this.props.filterDate, + disabled: this.props.disabled, }); isExcluded = (day: Date) => @@ -783,8 +784,17 @@ export default class Month extends Component { isDisabled: boolean; labelDate: Date; } => { - const { day, minDate, maxDate, excludeDates, includeDates } = this.props; + const { day, disabled, minDate, maxDate, excludeDates, includeDates } = + this.props; const labelDate = setMonth(day, month); + + if (disabled) { + return { + isDisabled: true, + labelDate: setMonth(day, month), + }; + } + return { isDisabled: ((minDate || maxDate || excludeDates || includeDates) && @@ -919,10 +929,16 @@ export default class Month extends Component { filterDate, preSelection, disabledKeyboardNavigation, + disabled, } = this.props; const isDisabled = - (minDate || maxDate || excludeDates || includeDates || filterDate) && + (minDate || + maxDate || + excludeDates || + includeDates || + filterDate || + disabled) && isQuarterDisabled(setQuarter(day, q), this.props); return clsx( diff --git a/src/stylesheets/datepicker.scss b/src/stylesheets/datepicker.scss index 8423ca549..434580e07 100644 --- a/src/stylesheets/datepicker.scss +++ b/src/stylesheets/datepicker.scss @@ -399,6 +399,11 @@ h2.react-datepicker__current-month { line-height: $datepicker__item-size; text-align: center; margin: $datepicker__day-margin; + + &--disabled { + cursor: default; + color: $datepicker__muted-color; + } } .react-datepicker__day, diff --git a/src/test/datepicker_test.test.tsx b/src/test/datepicker_test.test.tsx index c856bf109..658d57a35 100644 --- a/src/test/datepicker_test.test.tsx +++ b/src/test/datepicker_test.test.tsx @@ -4883,4 +4883,76 @@ describe("DatePicker", () => { expect(eventObject.target?.value).toBe(inputValue); }); }); + + describe("disabled", () => { + const validateAllDisabled = (container: HTMLElement, className: string) => { + const allDays = Array.from(container.querySelectorAll(`.${className}`)); + expect(allDays.length).toBeGreaterThan(0); + expect( + allDays.every((day) => + day.classList.contains(`${className}--disabled`), + ), + ).toBe(true); + }; + + const validateNonExistence = ( + container: HTMLElement, + querySelector: string, + ) => { + const element = container.querySelector(querySelector); + expect(element).toBeFalsy(); + }; + + it("should disable all days and headers in DatePicker when disabled prop is true", () => { + const { container } = render(); + + validateAllDisabled(container, "react-datepicker__day"); + validateAllDisabled(container, "react-datepicker__day-name"); + validateNonExistence(container, "react-datepicker__navigation"); + }); + + it("should disable all days and headers in DatePicker Range Selector when disabled prop is true", () => { + const { container } = render(); + + validateAllDisabled(container, "react-datepicker__day"); + validateAllDisabled(container, "react-datepicker__day-name"); + validateNonExistence(container, "react-datepicker__navigation"); + }); + + it("should disable all days and headers in MonthPicker when disabled prop is true", () => { + const { container } = render( + , + ); + + validateAllDisabled(container, "react-datepicker__month-text"); + validateNonExistence(container, "react-datepicker__navigation"); + }); + + it("should disable all days and headers in YearPicker when disabled prop is true", () => { + const { container } = render( + , + ); + + validateAllDisabled(container, "react-datepicker__year-text"); + validateNonExistence(container, "react-datepicker__navigation"); + }); + + it("should disable all days and headers in WeekPicker when disabled prop is true", () => { + const { container } = render( + , + ); + + validateAllDisabled(container, "react-datepicker__day"); + validateNonExistence(container, "react-datepicker__navigation"); + }); + + it("should disable all days and headers in QuarterPicker when disabled prop is true", () => { + const { container } = render( + , + ); + + validateAllDisabled(container, "react-datepicker__quarter-text"); + validateNonExistence(container, "react-datepicker__navigation"); + }); + }); }); diff --git a/src/year.tsx b/src/year.tsx index 271c3f18b..eb7fe419c 100644 --- a/src/year.tsx +++ b/src/year.tsx @@ -2,7 +2,7 @@ import { clsx } from "clsx"; import React, { Component, createRef } from "react"; import { - type DateFilterOptions, + type DateFilterOptionsWithDisabled, addYears, getStartOfYear, getYear, @@ -24,8 +24,13 @@ const VERTICAL_NAVIGATION_OFFSET = 3; interface YearProps extends Pick< - DateFilterOptions, - "minDate" | "maxDate" | "excludeDates" | "includeDates" | "filterDate" + DateFilterOptionsWithDisabled, + | "minDate" + | "maxDate" + | "excludeDates" + | "includeDates" + | "filterDate" + | "disabled" > { clearSelectingDate?: VoidFunction; date?: Date; @@ -381,6 +386,7 @@ export default class Year extends Component { getYearClassNames = (y: number) => { const { date, + disabled, minDate, maxDate, excludeDates, @@ -396,7 +402,12 @@ export default class Year extends Component { { "react-datepicker__year-text--selected": this.isSelectedYear(y), "react-datepicker__year-text--disabled": - (minDate || maxDate || excludeDates || includeDates || filterDate) && + (minDate || + maxDate || + excludeDates || + includeDates || + filterDate || + disabled) && isYearDisabled(y, this.props), "react-datepicker__year-text--keyboard-selected": this.isKeyboardSelected(y),