Skip to content

Commit

Permalink
Range month year picker (Hacker0x01#1692)
Browse files Browse the repository at this point in the history
* add range month picker

* fix issue Hacker0x01#1685

* fix for failing time input on backspace

* Add tests
  • Loading branch information
gautam-pahuja authored and martijnrusschen committed Apr 3, 2019
1 parent 07845e4 commit 373ae02
Show file tree
Hide file tree
Showing 11 changed files with 19,697 additions and 746 deletions.
20,166 changes: 19,426 additions & 740 deletions docs-site/bundle.js

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions docs-site/src/example_components.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ import RenderCustomDay from "./examples/render_custom_day";
import TimeInput from "./examples/timeInput";
import StrictParsing from "./examples/strict_parsing";
import MonthPicker from "./examples/month_picker";
import RangeMonthPicker from "./examples/range_month_picker";
import "react-datepicker/dist/react-datepicker.css";
import "./style.scss";

Expand Down Expand Up @@ -280,6 +281,10 @@ export default class exampleComponents extends React.Component {
{
title: "Month Picker",
component: <MonthPicker />
},
{
title: "Range Month Picker",
component: <RangeMonthPicker />
}
];

Expand Down
80 changes: 80 additions & 0 deletions docs-site/src/examples/range_month_picker.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import React from "react";
import DatePicker from "react-datepicker";
import isAfter from "date-fns/isAfter";

export default class RangeMonthPicker extends React.Component {
constructor(props) {
super(props);
this.state = {
startDate: new Date("2014/02/08"),
endDate: new Date("2014/04/08")
};
}

handleChange = ({ startDate, endDate }) => {
startDate = startDate || this.state.startDate;
endDate = endDate || this.state.endDate;

if (isAfter(startDate, endDate)) {
endDate = startDate;
}

this.setState({ startDate, endDate });
};

handleChangeStart = startDate => this.handleChange({ startDate });

handleChangeEnd = endDate => this.handleChange({ endDate });

render() {
return (
<div className="row">
<pre className="column example__code">
<code className="jsx">
{`
<DatePicker
selected={this.state.startDate}
selectsStart
startDate={this.state.startDate}
endDate={this.state.endDate}
dateFormat="MM/yyyy"
showMonthYearPicker
onChange={this.handleChangeStart}
/>
<DatePicker
selected={this.state.endDate}
selectsEnd
startDate={this.state.startDate}
endDate={this.state.endDate}
dateFormat="MM/yyyy"
showMonthYearPicker
onChange={this.handleChangeEnd}
/>
`}
</code>
</pre>
<div className="column">
<DatePicker
selected={this.state.startDate}
selectsStart
startDate={this.state.startDate}
endDate={this.state.endDate}
dateFormat="MM/yyyy"
showMonthYearPicker
onChange={this.handleChangeStart}
/>
<DatePicker
selected={this.state.endDate}
selectsEnd
startDate={this.state.startDate}
endDate={this.state.endDate}
dateFormat="MM/yyyy"
showMonthYearPicker
onChange={this.handleChangeEnd}
/>
</div>
</div>
);
}
}
32 changes: 32 additions & 0 deletions docs-site/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@
.react-datepicker__month .react-datepicker__month-text {
display: inline-block;
width: 4rem;
margin: 2px;
}

.react-datepicker__input-time-container {
Expand Down Expand Up @@ -428,6 +429,28 @@
margin: 0.166rem;
}

.react-datepicker__month--selected,
.react-datepicker__month--in-selecting-range,
.react-datepicker__month--in-range {
border-radius: 0.3rem;
background-color: #216ba5;
color: #fff;
}
.react-datepicker__month--selected:hover,
.react-datepicker__month--in-selecting-range:hover,
.react-datepicker__month--in-range:hover {
background-color: #1d5d90;
}

.react-datepicker__month--disabled {
color: #ccc;
pointer-events: none;
}
.react-datepicker__month--disabled:hover {
cursor: default;
background-color: transparent;
}

.react-datepicker__day,
.react-datepicker__month-text {
cursor: pointer;
Expand Down Expand Up @@ -507,6 +530,15 @@
background-color: transparent;
}

.react-datepicker__month-text.react-datepicker__month--selected:hover,
.react-datepicker__month-text.react-datepicker__month--in-range:hover {
background-color: #216ba5;
}

.react-datepicker__month-text:hover {
background-color: #f0f0f0;
}

.react-datepicker__input-container {
position: relative;
display: inline-block;
Expand Down
18 changes: 18 additions & 0 deletions src/date_utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,24 @@ export function isDayDisabled(
);
}

export function isMonthinRange(startDate, endDate, m, day) {
const startDateYear = getYear(startDate);
const startDateMonth = getMonth(startDate);
const endDateYear = getYear(endDate);
const endDateMonth = getMonth(endDate);
const dayYear = getYear(day);
if (startDateYear === endDateYear && startDateYear === dayYear) {
return startDateMonth <= m && m <= endDateMonth;
} else if (startDateYear < endDateYear) {
return (
(dayYear === startDateYear &&
(startDateMonth <= m || endDateMonth < m)) ||
(dayYear === endDateYear && (startDateMonth > m || endDateMonth >= m)) ||
(dayYear < endDateYear && dayYear > startDateYear)
);
}
}

export function isOutOfBounds(day, { minDate, maxDate } = {}) {
return (
(minDate && differenceInCalendarDays(day, minDate) < 0) ||
Expand Down
6 changes: 5 additions & 1 deletion src/inputTime.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from "react";
import PropTypes from "prop-types";
import _ from "lodash";

export default class inputTime extends React.Component {
static propTypes = {
Expand All @@ -26,6 +27,7 @@ export default class inputTime extends React.Component {

render() {
const { time } = this.state;
const { timeString } = this.props;
return (
<div className="react-datepicker__input-time-container">
<div className="react-datepicker-time__caption">
Expand All @@ -41,7 +43,9 @@ export default class inputTime extends React.Component {
required
value={time}
onChange={ev => {
this.onTimeChange(ev.target.value);
this.onTimeChange(
_.isEmpty(ev.target.value) ? timeString : ev.target.value
);
}}
/>
</div>
Expand Down
27 changes: 23 additions & 4 deletions src/month.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,28 @@ export default class Month extends React.Component {
);
};

getMonthClassNames = m => {
const { day, startDate, endDate, selected, minDate, maxDate } = this.props;

return classnames(
"react-datepicker__month-text",
`react-datepicker__month-${m}`,
{
"react-datepicker__month--disabled":
minDate && maxDate && !utils.isMonthinRange(minDate, maxDate, m, day),
"react-datepicker__month--selected":
utils.getMonth(day) === m &&
utils.getYear(day) === utils.getYear(selected),
"react-datepicker__month--in-range": utils.isMonthinRange(
startDate,
endDate,
m,
day
)
}
);
};

renderMonths = () => {
const months = [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10, 11]];
return months.map((month, i) => (
Expand All @@ -154,10 +176,7 @@ export default class Month extends React.Component {
onClick={ev => {
this.onMonthClick(ev.target, m);
}}
className={classnames(
"react-datepicker__month-text",
`react-datepicker__month-${m}`
)}
className={this.getMonthClassNames(m)}
>
{utils.getMonthShortInLocale(m, this.props.locale)}
</div>
Expand Down
35 changes: 35 additions & 0 deletions src/stylesheets/datepicker.scss
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@
.react-datepicker__month-text {
display: inline-block;
width: 4rem;
margin: 2px;
}
}

Expand Down Expand Up @@ -334,6 +335,28 @@
margin: $datepicker__day-margin;
}

.react-datepicker__month {
&--selected,
&--in-selecting-range,
&--in-range {
border-radius: $datepicker__border-radius;
background-color: $datepicker__selected-color;
color: #fff;

&:hover {
background-color: darken($datepicker__selected-color, 5%);
}
}
&--disabled {
color: $datepicker__muted-color;
pointer-events: none;
&:hover {
cursor: default;
background-color: transparent;
}
}
}

.react-datepicker__day,
.react-datepicker__month-text {
cursor: pointer;
Expand Down Expand Up @@ -408,6 +431,18 @@
}
}

.react-datepicker__month-text {
&.react-datepicker__month--selected,
&.react-datepicker__month--in-range {
&:hover {
background-color: $datepicker__selected-color;
}
}
&:hover {
background-color: $datepicker__background-color;
}
}

.react-datepicker__input-container {
position: relative;
display: inline-block;
Expand Down
27 changes: 26 additions & 1 deletion test/date_utils_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,19 @@ import {
addZero,
isTimeInDisabledRange,
isDayInRange,
parseDate
parseDate,
isMonthinRange
} from "../src/date_utils";
import setMinutes from "date-fns/setMinutes";
import setHours from "date-fns/setHours";

describe("date_utils", function() {
describe("newDate", function() {
it("should return null for invalid value passed", function() {
expect(newDate("21123asd")).to.be.null;
});
});

describe("isSameDay", function() {
it("should return true for null dates", function() {
expect(isSameDay(null, null)).to.be.true;
Expand Down Expand Up @@ -349,4 +356,22 @@ describe("date_utils", function() {
expect(parseDate(value, dateFormat, null, false)).to.not.be.null;
});
});

describe("isMonthinRange", () => {
it("should return true if the month passed is in range", () => {
const day = newDate("2015-02-01");
const startDate = newDate("2015-01-01");
const endDate = newDate("2015-08-01");

expect(isMonthinRange(startDate, endDate, 4, day)).to.be.true;
});

it("should return false if the month passed is not in range", () => {
const day = newDate("2015-02-01");
const startDate = newDate("2015-01-01");
const endDate = newDate("2015-08-01");

expect(isMonthinRange(startDate, endDate, 9, day)).to.be.false;
});
});
});
38 changes: 38 additions & 0 deletions test/month_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -167,4 +167,42 @@ describe("Month", () => {
month.simulate("click");
expect(utils.getMonth(monthClicked)).to.be.equal(6);
});

it("should return disabled class if current date is out of bound of minDate and maxdate", () => {
const monthComponent = mount(
<Month
day={utils.newDate("2015-12-01")}
minDate={utils.newDate("2016-02-01")}
maxDate={utils.newDate()}
showMonthYearPicker
/>
);
const month = monthComponent.find(".react-datepicker__month-text").at(0);
expect(month.hasClass("react-datepicker__month--disabled")).to.equal(true);
});

it("should return selected class if month is selected", () => {
const monthComponent = mount(
<Month
day={utils.newDate("2015-02-01")}
selected={utils.newDate("2015-02-01")}
showMonthYearPicker
/>
);
const month = monthComponent.find(".react-datepicker__month-text").at(1);
expect(month.hasClass("react-datepicker__month--selected")).to.equal(true);
});

it("should return month-in-range class if month is between the start date and end date", () => {
const monthComponent = mount(
<Month
day={utils.newDate("2015-02-01")}
startDate={utils.newDate("2015-01-01")}
endDate={utils.newDate("2015-08-01")}
showMonthYearPicker
/>
);
const month = monthComponent.find(".react-datepicker__month-text").at(4);
expect(month.hasClass("react-datepicker__month--in-range")).to.equal(true);
});
});
Loading

0 comments on commit 373ae02

Please sign in to comment.