Skip to content

Commit

Permalink
Merge pull request #168 from gpbl/can-display-month
Browse files Browse the repository at this point in the history
Make sure month navigation is blocked using canChangeMonth
  • Loading branch information
gpbl committed May 24, 2016
2 parents 1d56af9 + 0a534a9 commit 91de54b
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 48 deletions.
22 changes: 14 additions & 8 deletions examples/src/Examples.js
Expand Up @@ -8,6 +8,7 @@ import './vendors/prism.css';

import Birthdays from './examples/Birthdays';
import DisabledDays from './examples/DisabledDays';
import BlockedNavigation from './examples/BlockedNavigation';
import InputField from './examples/InputField';
import Localized from './examples/Localized';
import LocalizedCustom from './examples/LocalizedCustom';
Expand All @@ -29,17 +30,17 @@ const EXAMPLES = {
},
selectable: {
title: 'Selectable Day',
description: "This example uses the <code>selectedDays</code> prop and <a href='http://www.gpbl.org/react-day-picker/docs/DateUtils.html'>DateUtils</a> to select a day. Note how the selected day is stored in the parent component's state.",
description: "Use the <code>selectedDays</code> prop and <a href='http://www.gpbl.org/react-day-picker/docs/DateUtils.html'>DateUtils</a> to select a day. Note how the selected day is stored in the parent components state.",
Component: SelectableDay,
},
disabled: {
title: 'Disabled Days',
description: 'Using the <code>disabledDays</code> prop, you can prevent the user to select a day in the past: the <code>handleDayClick</code> handler doesn\'t update the state if the day has been marked as <code>disabled</code>.',
description: 'Use the <code>disabledDays</code> prop to prevent the selection of days: the <code>handleDayClick</code> handler doesn\'t update the state if the day has been marked as <code>disabled</code>.',
Component: DisabledDays,
},
input: {
title: 'Input Field',
description: 'Connect the day picker with an input field.',
description: 'How to connect the day picker with an input field.',
Component: InputField,
},
range: {
Expand All @@ -49,17 +50,22 @@ const EXAMPLES = {
},
modifiers: {
title: 'Advanced Modifiers',
description: 'Using the <code>modifiers</code> prop you can customize the aspect and the behaviour for each day. Note how the <code>onDay*</code> props receive the modifiers as third argument.',
description: 'Use the <code>modifiers</code> prop to customize the aspect and the behaviour for each day. Note how the <code>onDay*</code> props receive the modifiers as third argument.',
Component: Modifiers,
},
blocked: {
title: 'Block Navigation',
description: 'Set <code>canChangeMonth</code> to <code>false</code> to block the navigation between months and years.',
Component: BlockedNavigation,
},
restricted: {
title: 'Restrict Months',
description: 'The <code>fromMonth</code> and <code>toMonth</code> props allow to restrict the navigation between months. The following day picker is enabled from April to November 2015. A <code>disabled</code> modifier displays the other days as grayed out.',
description: 'Use the <code>fromMonth</code> and <code>toMonth</code> props to restrict the navigation between months.',
Component: Restricted,
},
localized: {
title: 'Localization (moment.js)',
description: "This calendar is localized using moment.js. <a href='http://www.gpbl.org/react-day-picker/docs/Localization.html'>Read more about localization</a>.<br>Note the use of the <a href='https://www.w3.org/TR/html/dom.html#the-dir-attribute'>dir attribute</a> to support <abbr title='Right to left'>RTL</abbr> languages.",
description: "This day picker is localized using moment.js. Note the use of the <a href='https://www.w3.org/TR/html/dom.html#the-dir-attribute'>dir attribute</a> to support <abbr title='Right to left'>RTL</abbr> languages. <a href='http://www.gpbl.org/react-day-picker/docs/Localization.html'>Read more about localization</a>.",
Component: Localized,
},
localizedCustom: {
Expand All @@ -69,12 +75,12 @@ const EXAMPLES = {
},
yearNavigation: {
title: 'Year Navigation',
description: 'With the <code>captionElement</code> prop, you can use your own element as month caption. In this example, the caption element is a form to navigate up to the next 10 years.',
description: 'Use the <code>captionElement</code> prop to render your custom element as caption. This example is showing a form to navigate up to the next 10 years.',
Component: YearNavigation,
},
birthdays: {
title: 'Birthdays',
description: 'Add custom content to days cells using the <code>renderDay</code> prop.',
description: 'Use the <code>renderDay</code> prop to add custom content to day cells.',
Component: Birthdays,
},
year: {
Expand Down
8 changes: 8 additions & 0 deletions examples/src/examples/BlockedNavigation.js
@@ -0,0 +1,8 @@
import React from 'react';
import DayPicker from 'react-day-picker';

import 'react-day-picker/lib/style.css';

export default function BlockedNavigation() {
return <DayPicker canChangeMonth={false} />;
}
1 change: 1 addition & 0 deletions examples/webpack.config.js
Expand Up @@ -40,6 +40,7 @@ module.exports = {
except: [
'Birthdays',
'DisabledDays',
'BlockedNavigation',
'InputField',
'Localized',
'LocalizedCustom',
Expand Down
83 changes: 43 additions & 40 deletions src/DayPicker.js
Expand Up @@ -113,23 +113,24 @@ export default class DayPicker extends Component {
}

allowMonth(d) {
const { fromMonth, toMonth } = this.props;
if ((fromMonth && Helpers.getMonthsDiff(fromMonth, d) < 0) ||
const { fromMonth, toMonth, canChangeMonth } = this.props;
if (!canChangeMonth ||
(fromMonth && Helpers.getMonthsDiff(fromMonth, d) < 0) ||
(toMonth && Helpers.getMonthsDiff(toMonth, d) > 0)) {
return false;
}
return true;
}

allowYearChange() {
return this.props.canChangeMonth;
}

showMonth(d, callback) {
if (!this.allowMonth(d)) {
return;
}
this.setState({ currentMonth: Helpers.startOfMonth(d) }, callback);
}

showMonthAndCallHandler(d, callback) {
this.showMonth(d, () => {
this.setState({ currentMonth: Helpers.startOfMonth(d) }, () => {
if (callback) {
callback();
}
Expand All @@ -140,27 +141,35 @@ export default class DayPicker extends Component {
}

showNextMonth(callback) {
if (this.allowNextMonth()) {
const nextMonth = DateUtils.addMonths(this.state.currentMonth, 1);
this.showMonthAndCallHandler(nextMonth, callback);
if (!this.allowNextMonth()) {
return;
}
const nextMonth = DateUtils.addMonths(this.state.currentMonth, 1);
this.showMonth(nextMonth, callback);
}

showPreviousMonth(callback) {
if (this.allowPreviousMonth()) {
const previousMonth = DateUtils.addMonths(this.state.currentMonth, -1);
this.showMonthAndCallHandler(previousMonth, callback);
if (!this.allowPreviousMonth()) {
return;
}
const previousMonth = DateUtils.addMonths(this.state.currentMonth, -1);
this.showMonth(previousMonth, callback);
}

showNextYear(callback) {
showNextYear() {
if (!this.allowYearChange()) {
return;
}
const nextMonth = DateUtils.addMonths(this.state.currentMonth, 12);
this.showMonthAndCallHandler(nextMonth, callback);
this.showMonth(nextMonth);
}

showPreviousYear(callback) {
showPreviousYear() {
if (!this.allowYearChange()) {
return;
}
const nextMonth = DateUtils.addMonths(this.state.currentMonth, -12);
this.showMonthAndCallHandler(nextMonth, callback);
this.showMonth(nextMonth);
}

focusFirstDayOfMonth() {
Expand Down Expand Up @@ -231,32 +240,26 @@ export default class DayPicker extends Component {

handleKeyDown(e) {
e.persist();
const { canChangeMonth, onKeyDown } = this.props;

if (!canChangeMonth && onKeyDown) {
onKeyDown(e);
return;
switch (e.keyCode) {
case keys.LEFT:
this.showPreviousMonth();
break;
case keys.RIGHT:
this.showNextMonth();
break;
case keys.UP:
this.showPreviousYear();
break;
case keys.DOWN:
this.showNextYear();
break;
default:
break;
}

if (canChangeMonth) {
switch (e.keyCode) {
case keys.LEFT:
this.showPreviousMonth(onKeyDown);
break;
case keys.RIGHT:
this.showNextMonth(onKeyDown);
break;
case keys.UP:
this.showPreviousYear(onKeyDown);
break;
case keys.DOWN:
this.showNextYear(onKeyDown);
break;
default:
if (onKeyDown) {
onKeyDown(e);
}
}
if (this.props.onKeyDown) {
this.props.onKeyDown(e);
}
}

Expand Down
18 changes: 18 additions & 0 deletions test/DayPicker.js
Expand Up @@ -584,12 +584,24 @@ describe('<DayPicker />', () => {
);
expect(wrapper.instance().allowPreviousMonth()).to.be.false;
});
it('should not allow the previous month when cannot change months', () => {
const wrapper = shallow(
<DayPicker canChangeMonth={false} />
);
expect(wrapper.instance().allowPreviousMonth()).to.be.false;
});
it('should not allow the next month when the last month is the last allowed one', () => {
const wrapper = shallow(
<DayPicker initialMonth={new Date(2015, 7)} toMonth={new Date(2015, 9)} numberOfMonths={3} />
);
expect(wrapper.instance().allowNextMonth()).to.be.false;
});
it('should not allow the next month when cannot change months', () => {
const wrapper = shallow(
<DayPicker canChangeMonth={false} />
);
expect(wrapper.instance().allowNextMonth()).to.be.false;
});
it('should show the next month when clicking the next button', () => {
const wrapper = mount(<DayPicker initialMonth={new Date(2015, 7)} />);
wrapper.find('.DayPicker-NavButton--next').simulate('click');
Expand Down Expand Up @@ -619,6 +631,12 @@ describe('<DayPicker />', () => {
expect(instance.state.currentMonth.getFullYear()).to.equal(2015);
expect(instance.state.currentMonth.getMonth()).to.equal(6);
});
it('should not allow changing to the year when cannot change months', () => {
const wrapper = shallow(
<DayPicker canChangeMonth={false} />
);
expect(wrapper.instance().allowYearChange()).to.be.false;
});
it('should call `showNextMonth()` when the RIGHT key is pressed', () => {
const wrapper = mount(<DayPicker />);
const showNextMonth = spy(wrapper.instance(), 'showNextMonth');
Expand Down

0 comments on commit 91de54b

Please sign in to comment.