Skip to content

Commit

Permalink
Merge pull request #3888 from shu1007/fix-month-year
Browse files Browse the repository at this point in the history
Fix month and year picker disabled
  • Loading branch information
martijnrusschen committed Jan 9, 2023
2 parents c8987a8 + 25575b5 commit 268d333
Show file tree
Hide file tree
Showing 8 changed files with 356 additions and 11 deletions.
28 changes: 24 additions & 4 deletions src/date_utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import startOfYear from "date-fns/startOfYear";
import endOfDay from "date-fns/endOfDay";
import endOfWeek from "date-fns/endOfWeek";
import endOfMonth from "date-fns/endOfMonth";
import endOfYear from "date-fns/endOfYear";
import dfIsEqual from "date-fns/isEqual";
import dfIsSameDay from "date-fns/isSameDay";
import dfIsSameMonth from "date-fns/isSameMonth";
Expand Down Expand Up @@ -377,7 +378,9 @@ export function getLocaleObject(localeSpec) {
}

export function getFormattedWeekdayInLocale(date, formatFunc, locale) {
return typeof formatFunc === "function" ? formatFunc(date, locale) : formatDate(date, "EEEE", locale);
return typeof formatFunc === "function"
? formatFunc(date, locale)
: formatDate(date, "EEEE", locale);
}

export function getWeekdayMinInLocale(date, locale) {
Expand Down Expand Up @@ -454,7 +457,10 @@ export function isMonthDisabled(
{ minDate, maxDate, excludeDates, includeDates, filterDate } = {}
) {
return (
isOutOfBounds(month, { minDate, maxDate }) ||
isOutOfBounds(month, {
minDate: startOfMonth(minDate),
maxDate: endOfMonth(maxDate),
}) ||
(excludeDates &&
excludeDates.some((excludeDate) => isSameMonth(month, excludeDate))) ||
(includeDates &&
Expand Down Expand Up @@ -500,9 +506,23 @@ export function isQuarterDisabled(
);
}

export function isYearDisabled(year, { minDate, maxDate } = {}) {
export function isYearDisabled(
year,
{ minDate, maxDate, excludeDates, includeDates, filterDate } = {}
) {
const date = new Date(year, 0, 1);
return isOutOfBounds(date, { minDate, maxDate }) || false;
return (
isOutOfBounds(date, {
minDate: startOfYear(minDate),
maxDate: endOfYear(maxDate),
}) ||
(excludeDates &&
excludeDates.some((excludeDate) => isSameYear(date, excludeDate))) ||
(includeDates &&
!includeDates.some((includeDate) => isSameYear(date, includeDate))) ||
(filterDate && !filterDate(newDate(date))) ||
false
);
}

export function isQuarterInRange(startDate, endDate, q, day) {
Expand Down
20 changes: 18 additions & 2 deletions src/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ import {
getDefaultLocale,
DEFAULT_YEAR_ITEM_NUMBER,
isSameDay,
isMonthDisabled,
isYearDisabled,
} from "./date_utils";
import TabLoop from "./tab_loop";
import onClickOutside from "react-onclickoutside";
Expand Down Expand Up @@ -539,9 +541,23 @@ export default class DatePicker extends React.Component {
setSelected = (date, event, keepInput, monthSelectedIn) => {
let changedDate = date;

if (changedDate !== null && isDayDisabled(changedDate, this.props)) {
return;
if (this.props.showYearPicker) {
if (
changedDate !== null &&
isYearDisabled(getYear(changedDate), this.props)
) {
return;
}
} else if (this.props.showMonthYearPicker) {
if (changedDate !== null && isMonthDisabled(changedDate, this.props)) {
return;
}
} else {
if (changedDate !== null && isDayDisabled(changedDate, this.props)) {
return;
}
}

const { onChange, selectsRange, startDate, endDate } = this.props;

if (
Expand Down
7 changes: 4 additions & 3 deletions src/month.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,8 @@ export default class Month extends React.Component {
maxDate,
preSelection,
monthClassName,
excludeDates,
includeDates,
} = this.props;
const _monthClassName = monthClassName ? monthClassName(day) : undefined;
const labelDate = utils.setMonth(day, m);
Expand All @@ -309,9 +311,8 @@ export default class Month extends React.Component {
_monthClassName,
{
"react-datepicker__month--disabled":
((minDate || maxDate) && utils.isMonthDisabled(labelDate, this.props)) ||
this.isDisabled(labelDate) ||
this.isExcluded(labelDate),
(minDate || maxDate || excludeDates || includeDates) &&
utils.isMonthDisabled(labelDate, this.props),
"react-datepicker__month--selected": this.isSelectedMonth(
day,
m,
Expand Down
15 changes: 13 additions & 2 deletions src/year.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ export default class Year extends React.Component {
inline: PropTypes.bool,
maxDate: PropTypes.instanceOf(Date),
minDate: PropTypes.instanceOf(Date),
excludeDates: PropTypes.array,
includeDates: PropTypes.array,
filterDate: PropTypes.func,
yearItemNumber: PropTypes.number,
};

Expand Down Expand Up @@ -102,11 +105,19 @@ export default class Year extends React.Component {
};

getYearClassNames = (y) => {
const { minDate, maxDate, selected } = this.props;
const {
minDate,
maxDate,
selected,
excludeDates,
includeDates,
filterDate,
} = this.props;
return classnames("react-datepicker__year-text", {
"react-datepicker__year-text--selected": y === getYear(selected),
"react-datepicker__year-text--disabled":
(minDate || maxDate) && utils.isYearDisabled(y, this.props),
(minDate || maxDate || excludeDates || includeDates || filterDate) &&
utils.isYearDisabled(y, this.props),
"react-datepicker__year-text--keyboard-selected":
this.isKeyboardSelected(y),
"react-datepicker__year-text--today": this.isCurrentYear(y),
Expand Down
65 changes: 65 additions & 0 deletions test/date_utils_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
isDayExcluded,
isMonthDisabled,
isQuarterDisabled,
isYearDisabled,
isValid,
monthDisabledBefore,
monthDisabledAfter,
Expand Down Expand Up @@ -39,6 +40,7 @@ import setHours from "date-fns/setHours";
import addQuarters from "date-fns/addQuarters";
import ptBR from "date-fns/locale/pt-BR";
import { registerLocale } from "../src/date_utils";
import { addYears } from "date-fns";

registerLocale("pt-BR", ptBR);

Expand Down Expand Up @@ -449,6 +451,18 @@ describe("date_utils", function () {
isMonthDisabled(day, { filterDate });
expect(isEqual(day, dayClone)).to.be.true;
});

it("should be enabled if before minDate but same month", () => {
const day = newDate("2023-01-01");
expect(isMonthDisabled(day, { minDate: newDate("2023-01-02") })).to.be
.false;
});

it("should be enabled if after maxDate but same month", () => {
const day = newDate("2023-01-02");
expect(isMonthDisabled(day, { maxDate: newDate("2023-01-01") })).to.be
.false;
});
});

describe("isQuarterDisabled", function () {
Expand Down Expand Up @@ -519,6 +533,57 @@ describe("date_utils", function () {
});
});

describe("isYearDisabled", function () {
const year = 2023;
const newYearsDay = newDate(`${year}-01-01`);

it("should be enabled by default", () => {
expect(isYearDisabled(year)).to.be.false;
});

it("should be enabled if on the min date", () => {
expect(isYearDisabled(year, { minDate: newYearsDay })).to.be.false;
});

it("should be disabled if before the min date", () => {
expect(isYearDisabled(year, { minDate: addYears(newYearsDay, 1) })).to.be
.true;
});

it("should be enabled if on the max date", () => {
expect(isYearDisabled(year, { maxDate: newYearsDay })).to.be.false;
});

it("should be disabled if after the max date", () => {
expect(isYearDisabled(year, { maxDate: addYears(newYearsDay, -1) })).to.be
.true;
});

it("should be disabled if in excluded dates", () => {
const day = newDate();
expect(isYearDisabled(year, { excludeDates: [day] })).to.be.true;
});

it("should be enabled if in included dates", () => {
expect(isYearDisabled(year, { includeDates: [newYearsDay] })).to.be.false;
});

it("should be disabled if not in included dates", () => {
const includeDates = [addYears(newYearsDay, 1)];
expect(isYearDisabled(year, { includeDates })).to.be.true;
});

it("should be enabled if date filter returns true", () => {
const filterDate = (d) => isSameYear(d, newYearsDay);
expect(isYearDisabled(year, { filterDate })).to.be.false;
});

it("should be disabled if date filter returns false", () => {
const filterDate = (d) => !isSameYear(d, newYearsDay);
expect(isYearDisabled(year, { filterDate })).to.be.true;
});
});

describe("isValid", () => {
it("should return true if date is valid and equal or after minDate", () => {
expect(isValid(newDate("2021-11-15"), newDate("2021-11-15"))).to.be.true;
Expand Down
56 changes: 56 additions & 0 deletions test/datepicker_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1984,4 +1984,60 @@ describe("DatePicker", () => {
expect(utils.getMinutes(date)).to.equal(22);
});
});

it("should selected month when specified minDate same month", () => {
const selected = utils.newDate("2023-01-09");
let date = null;
const datePicker = TestUtils.renderIntoDocument(
<DatePicker
selected={selected}
onChange={(d) => (date = d)}
dateFormat="MM/yyyy"
minDate={utils.newDate("2022-12-31")}
showMonthYearPicker
/>
);

TestUtils.Simulate.change(datePicker.input, {
target: {
value: "11/2022",
},
});
expect(date).to.equal(null);

TestUtils.Simulate.change(datePicker.input, {
target: {
value: "12/2022",
},
});
expect(date.toString()).to.equal(utils.newDate("2022-12-01").toString());
});

it("should selected year when specified minDate same year", () => {
const selected = utils.newDate("2023-01-09");
let date = null;
const datePicker = TestUtils.renderIntoDocument(
<DatePicker
selected={selected}
onChange={(d) => (date = d)}
dateFormat="yyyy"
minDate={utils.newDate("2022-12-31")}
showYearPicker
/>
);

TestUtils.Simulate.change(datePicker.input, {
target: {
value: "2021",
},
});
expect(date).to.equal(null);

TestUtils.Simulate.change(datePicker.input, {
target: {
value: "2022",
},
});
expect(date.toString()).to.equal(utils.newDate("2022-01-01").toString());
});
});
82 changes: 82 additions & 0 deletions test/month_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,88 @@ describe("Month", () => {
expect(month.hasClass("react-datepicker__month--disabled")).to.equal(true);
});

it("should not return disabled class if current date is before minDate but same month", () => {
const monthComponent = mount(
<Month
day={utils.newDate("2015-01-01")}
minDate={utils.newDate("2015-01-10")}
showMonthYearPicker
/>
);
const month = monthComponent.find(".react-datepicker__month-text").at(0);
expect(month.hasClass("react-datepicker__month--disabled")).to.not.equal(
true
);
});

it("should not return disabled class if current date is after maxDate but same month", () => {
const monthComponent = mount(
<Month
day={utils.newDate("2015-01-10")}
maxDate={utils.newDate("2015-01-01")}
showMonthYearPicker
/>
);
const month = monthComponent.find(".react-datepicker__month-text").at(0);
expect(month.hasClass("react-datepicker__month--disabled")).to.not.equal(
true
);
});

it("should return disabled class if specified excludeDate", () => {
const monthComponent = mount(
<Month
day={utils.newDate("2015-01-01")}
excludeDates={[
utils.newDate("2015-02-01"),
utils.newDate("2015-04-02"),
utils.newDate("2015-07-03"),
utils.newDate("2015-10-04"),
]}
showMonthYearPicker
/>
);
// exclude month index
const monthTexts = monthComponent.find(".react-datepicker__month-text");

[(1, 3, 6, 9)].forEach((i) => {
const month = monthTexts.at(i);
expect(month.hasClass("react-datepicker__month--disabled")).to.equal(
true
);
});
});

it("should return disabled class if specified includeDate", () => {
const monthComponent = mount(
<Month
day={utils.newDate("2015-01-01")}
includeDates={[
utils.newDate("2015-01-01"),
utils.newDate("2015-02-02"),
utils.newDate("2015-03-03"),
utils.newDate("2015-04-04"),
utils.newDate("2015-05-05"),
utils.newDate("2015-06-06"),
]}
showMonthYearPicker
/>
);
const monthTexts = monthComponent.find(".react-datepicker__month-text");
for (let i = 0; i < 6; i++) {
const month = monthTexts.at(i);
expect(month.hasClass("react-datepicker__month--disabled")).to.equal(
false
);
}
for (let i = 6; i < 12; i++) {
const month = monthTexts.at(i);
expect(month.hasClass("react-datepicker__month--disabled")).to.equal(
true
);
}
});

it("should have no axe violations", () => {
const monthComponent = mount(
<Month
Expand Down

0 comments on commit 268d333

Please sign in to comment.