Skip to content

Commit

Permalink
Added onViewDateChange callback for when the viewed date changes
Browse files Browse the repository at this point in the history
  • Loading branch information
InvisibleBacon authored and JakeThurman committed May 11, 2021
1 parent 7e30d6c commit 3c3ed45
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 14 deletions.
1 change: 1 addition & 0 deletions README.md
Expand Up @@ -67,6 +67,7 @@ Below we have all the props that we can use with the `<DateTime>` component. The
| **onBeforeNavigate** | `function` | ( nextView, currentView, viewDate ) => nextView | Allows to intercept a change of the calendar view. The accepted function receives the view that it's supposed to navigate to, the view that is showing currently and the date currently shown in the view. Return a viewMode ( default ones are `years`, `months`, `days` or `time`) to navigate to it. If the function returns a "falsy" value, the navigation is stopped and we will remain in the current view. |
| **onNavigateBack** | `function` | empty function | Callback trigger when the user navigates to the previous month, year or decade. The callback receives the amount and type ('month', 'year') as parameters. |
| **onNavigateForward** | `function` | empty function | Callback trigger when the user navigates to the next month, year or decade. The callback receives the amount and type ('month', 'year') as parameters. |
| **onViewDateChange** | `function` | empty function | Callback trigger for when the user changes the currently viewed date. The callback receives the current view date `moment` object as only parameter. |
| **className** | `string` or `string array` | `''` | Extra class name for the outermost markup element. |
| **inputProps** | `object` | `undefined` | Defines additional attributes for the input element of the component. For example: `onClick`, `placeholder`, `disabled`, `required`, `name` and `className` (`className` *sets* the class attribute for the input element). See [Customize the Input Appearance](#customize-the-input-appearance). |
| **isValidDate** | `function` | `() => true` | Define the dates that can be selected. The function receives `(currentDate, selectedDate)` and shall return a `true` or `false` whether the `currentDate` is valid or not. See [selectable dates](#selectable-dates).|
Expand Down
34 changes: 20 additions & 14 deletions src/DateTime.js
Expand Up @@ -31,6 +31,7 @@ export default class Datetime extends React.Component {
onBeforeNavigate: TYPES.func,
onNavigateBack: TYPES.func,
onNavigateForward: TYPES.func,
onViewDateChange: TYPES.func,
updateOnView: TYPES.string,
locale: TYPES.string,
utc: TYPES.bool,
Expand Down Expand Up @@ -62,6 +63,7 @@ export default class Datetime extends React.Component {
onBeforeNavigate: function(next) { return next; },
onNavigateBack: nofn,
onNavigateForward: nofn,
onViewDateChange: nofn,
dateFormat: true,
timeFormat: true,
utc: false,
Expand Down Expand Up @@ -445,25 +447,29 @@ export default class Datetime extends React.Component {
}
}

componentDidUpdate( prevProps ) {
if ( prevProps === this.props ) return;
componentDidUpdate( prevProps, prevState ) {
if ( prevState.viewDate !== this.state.viewDate && !prevState.viewDate.isSame( this.state.viewDate ) ) {
this.props.onViewDateChange( this.state.viewDate );
}

let needsUpdate = false;
let thisProps = this.props;
if ( prevProps !== this.props ) {
let needsUpdate = false;
let thisProps = this.props;

['locale', 'utc', 'displayZone', 'dateFormat', 'timeFormat'].forEach( function(p) {
prevProps[p] !== thisProps[p] && (needsUpdate = true);
});
['locale', 'utc', 'displayZone', 'dateFormat', 'timeFormat'].forEach( function(p) {
prevProps[p] !== thisProps[p] && (needsUpdate = true);
});

if ( needsUpdate ) {
this.regenerateDates();
}
if ( needsUpdate ) {
this.regenerateDates();
}

if ( thisProps.value && thisProps.value !== prevProps.value ) {
this.setViewDate( thisProps.value );
}
if ( thisProps.value && thisProps.value !== prevProps.value ) {
this.setViewDate( thisProps.value );
}

this.checkTZ();
this.checkTZ();
}
}

regenerateDates() {
Expand Down
66 changes: 66 additions & 0 deletions test/tests.spec.js
Expand Up @@ -1259,6 +1259,72 @@ describe('Datetime', () => {
});
});

describe('onViewDateChange', () => {
it('when increase month', () => {
const date = new Date(2000, 0, 15, 2, 2, 2, 2),
mDate = moment(date),
onViewDateChangeFn = jest.fn(),
component = utils.createDatetime({ initialViewDate: date, onViewDateChange: onViewDateChangeFn });

utils.clickOnElement(component.find('.rdtNext').at(0));
expect(onViewDateChangeFn).toHaveBeenCalledTimes(1);
expect(onViewDateChangeFn.mock.calls[0][0].isSame(mDate.add(1, 'month'), 'month')).toBeTruthy();
});

it('when decrease month', () => {
const date = new Date(2000, 0, 15, 2, 2, 2, 2),
mDate = moment(date),
onViewDateChangeFn = jest.fn(),
component = utils.createDatetime({ initialViewDate: date, onViewDateChange: onViewDateChangeFn });

utils.clickOnElement(component.find('.rdtPrev').at(0));
expect(onViewDateChangeFn).toHaveBeenCalledTimes(1);
expect(onViewDateChangeFn.mock.calls[0][0].isSame(mDate.subtract(1, 'month'), 'month')).toBeTruthy();
});

it('when pick month in picker', () => {
const date = new Date(2000, 0, 15, 2, 2, 2, 2),
mDate = moment(date),
onViewDateChangeFn = jest.fn(),
component = utils.createDatetime({ initialViewDate: date, initialViewMode: 'months', onViewDateChange: onViewDateChangeFn });

utils.clickNthMonth(component, 5);
expect(onViewDateChangeFn).toHaveBeenCalledTimes(1);
expect(onViewDateChangeFn.mock.calls[0][0].isSame(mDate.add(5, 'month'), 'month')).toBeTruthy();
});

it('when pick same month in picker', () => {
const date = new Date(2000, 0, 15, 2, 2, 2, 2),
onViewDateChangeFn = jest.fn(),
component = utils.createDatetime({ initialViewDate: date, initialViewMode: 'months', onViewDateChange: onViewDateChangeFn });

utils.clickNthMonth(component, 0);
expect(onViewDateChangeFn).not.toHaveBeenCalled();
});

it('when next is clicked in month view', () => {
const date = new Date(2000, 0, 15, 2, 2, 2, 2),
mDate = moment(date),
onViewDateChangeFn = jest.fn(),
component = utils.createDatetime({ initialViewDate: date, initialViewMode: 'months', onViewDateChange: onViewDateChangeFn });

utils.clickOnElement(component.find('.rdtNext').at(0));
expect(onViewDateChangeFn).toHaveBeenCalledTimes(1);
expect(onViewDateChangeFn.mock.calls[0][0].isSame(mDate.add(1, 'year'), 'year')).toBeTruthy();
});

it('when prev is clicked in month view', () => {
const date = new Date(2000, 0, 15, 2, 2, 2, 2),
mDate = moment(date),
onViewDateChangeFn = jest.fn(),
component = utils.createDatetime({ initialViewDate: date, initialViewMode: 'months', onViewDateChange: onViewDateChangeFn });

utils.clickOnElement(component.find('.rdtPrev').at(0));
expect(onViewDateChangeFn).toHaveBeenCalledTimes(1);
expect(onViewDateChangeFn.mock.calls[0][0].isSame(mDate.subtract(1, 'year'), 'year')).toBeTruthy();
});
});

describe('onNavigateBack', () => {
it('when moving to previous month', () => {
const component = utils.createDatetime({ onNavigateBack: (amount, type) => {
Expand Down
5 changes: 5 additions & 0 deletions typings/DateTime.d.ts
Expand Up @@ -96,6 +96,11 @@ declare namespace ReactDatetimeClass {
to the user's local timezone (unless `utc` specified).
*/
displayTimeZone?: string;
/*
/*
Callback trigger for when the user navigates the calendar
*/
onViewDateChange?: (value: Moment) => void;
/*
Callback trigger when the date changes. The callback receives the selected `moment` object as
only parameter, if the date in the input is valid. If the date in the input is not valid, the
Expand Down

0 comments on commit 3c3ed45

Please sign in to comment.