diff --git a/README.md b/README.md
index 7b663e34..2a79d65c 100644
--- a/README.md
+++ b/README.md
@@ -49,12 +49,12 @@ import React from 'react';
import SemanticDatepicker from 'react-semantic-ui-datepickers';
import 'react-semantic-ui-datepickers/dist/react-semantic-ui-datepickers.css';
-const AppWithBasic = ({ onDateChange }) => (
-
+const AppWithBasic = ({ onChange }) => (
+
);
-const AppWithRangeAndInPortuguese = ({ onDateChange }) => (
-
+const AppWithRangeAndInPortuguese = ({ onChange }) => (
+
);
```
@@ -75,7 +75,7 @@ More examples [here](https://react-semantic-ui-datepickers.now.sh).
| keepOpenOnSelect | boolean | no | false | Keeps the datepicker open when a date is selected |
| locale | string | no | 'en-US' | Filename of the locale to be used. PS: Feel free to submit PR's with more locales! |
| onBlur | function | no | () => {} | Callback fired when the input loses focus |
-| onDateChange | function | yes | | Callback fired when the value changes |
+| onChange | function | no | () => {} | Callback fired when the value changes |
| pointing | string | no | 'left' | Location to render the component around input. Available options: 'left', 'right', 'top left', 'top right' |
| type | string | no | basic | Type of input to render. Available options: 'basic' and 'range' |
diff --git a/src/__tests__/datepicker.test.tsx b/src/__tests__/datepicker.test.tsx
index 7c72e6a7..4f758b90 100644
--- a/src/__tests__/datepicker.test.tsx
+++ b/src/__tests__/datepicker.test.tsx
@@ -3,10 +3,11 @@ import { fireEvent, render } from '@testing-library/react';
import { SemanticDatepickerProps } from '../types';
import localeEn from '../locales/en-US.json';
import localePt from '../locales/pt-BR.json';
+import { getShortDate } from '../utils';
import DatePicker from '../';
const setup = (props?: Partial) => {
- const options = render();
+ const options = render();
return {
...options,
@@ -17,7 +18,7 @@ const setup = (props?: Partial) => {
},
rerender: (newProps?: Partial) =>
options.rerender(
-
+
),
};
};
@@ -55,6 +56,26 @@ describe('Basic datepicker', () => {
expect(todayButton.textContent).toBe(localeEn.todayButton);
});
+
+ it('fires onChange with event and selected date as arguments', async () => {
+ const onChange = jest.fn();
+ const today = getShortDate(new Date()) as string;
+ const { getByTestId, openDatePicker } = setup({ onChange });
+
+ openDatePicker();
+
+ const todayCell = getByTestId(RegExp(today));
+
+ fireEvent.click(todayCell);
+
+ expect(onChange).toHaveBeenNthCalledWith(
+ 1,
+ expect.any(Object),
+ expect.objectContaining({
+ value: expect.any(Date),
+ })
+ );
+ });
});
describe('Range datepicker', () => {
@@ -86,8 +107,46 @@ describe('Range datepicker', () => {
expect(todayButton.textContent).toBe(localeEn.todayButton);
// @ts-ignore
- rerender({ locale: 'invalid language' });
+ rerender({ locale: 'invalid' });
expect(todayButton.textContent).toBe(localeEn.todayButton);
});
+
+ it('fires onChange with event and selected dates as arguments', async () => {
+ const onChange = jest.fn();
+ const now = new Date();
+ const today = getShortDate(now) as string;
+ const tomorrow = getShortDate(
+ new Date(now.setDate(now.getDate() + 1))
+ ) as string;
+ const { getByTestId, openDatePicker } = setup({
+ onChange,
+ type: 'range',
+ });
+
+ openDatePicker();
+
+ const todayCell = getByTestId(RegExp(today));
+ const tomorrowCell = getByTestId(RegExp(tomorrow));
+
+ fireEvent.click(todayCell);
+
+ expect(onChange).toHaveBeenNthCalledWith(
+ 1,
+ expect.any(Object),
+ expect.objectContaining({
+ value: [expect.any(Date)],
+ })
+ );
+
+ fireEvent.click(tomorrowCell);
+
+ expect(onChange).toHaveBeenNthCalledWith(
+ 2,
+ expect.any(Object),
+ expect.objectContaining({
+ value: [expect.any(Date), expect.any(Date)],
+ })
+ );
+ });
});
diff --git a/src/__tests__/utils.test.ts b/src/__tests__/utils.test.ts
index 2e8c9f41..42d6be4a 100644
--- a/src/__tests__/utils.test.ts
+++ b/src/__tests__/utils.test.ts
@@ -4,6 +4,7 @@ import localeEn from '../locales/en-US.json';
import {
formatDate,
formatSelectedDate,
+ getShortDate,
getToday,
isSelectable,
moveElementsByN,
@@ -14,7 +15,8 @@ import {
} from '../utils';
const objectTest = { a: 'a', b: 'b', c: 'c' };
-const dateTest = parse('2018-06-21');
+const dateTestString = '2018-06-21';
+const dateTest = parse(dateTestString);
const june14 = parse('2018-06-14');
const june20 = parse('2018-06-20');
const june25 = parse('2018-06-25');
@@ -159,3 +161,13 @@ describe('onlyNumbers', () => {
expect(onlyNumbers('ABC-1025.4.8')).toBe('102548');
});
});
+
+describe('getShortDate', () => {
+ it('should return undefined if date is not provided', () => {
+ expect(getShortDate(undefined)).toBe(undefined);
+ });
+
+ it('should return the date provided in the right format', () => {
+ expect(getShortDate(new Date(dateTestString))).toBe(dateTestString);
+ });
+});
diff --git a/src/components/calendar/calendar.tsx b/src/components/calendar/calendar.tsx
index 404ed08f..51a12921 100644
--- a/src/components/calendar/calendar.tsx
+++ b/src/components/calendar/calendar.tsx
@@ -2,7 +2,7 @@ import cn from 'classnames';
import React, { Fragment } from 'react';
import { Segment } from 'semantic-ui-react';
import { DateFns, Locale, SemanticDatepickerProps } from 'types';
-import { getToday } from '../../utils';
+import { getShortDate, getToday } from '../../utils';
import Button from '../button';
import CalendarCell from '../cell';
import TodayButton from '../today-button';
@@ -124,12 +124,14 @@ const Calendar: React.FC = ({
const selectable =
dateObj.selectable && filterDate(dateObj.date);
+ const shortDate = getShortDate(dateObj.date);
return (
{dateObj.date.getDate()}
diff --git a/src/index.tsx b/src/index.tsx
index 60d02718..2a421e9d 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -10,8 +10,7 @@ import {
parseOnBlur,
pick,
} from './utils';
-import BasicDatePicker from './pickers/basic';
-import RangeDatePicker from './pickers/range';
+import { BasicDatePicker, RangeDatePicker } from './pickers';
import { Locale, SemanticDatepickerProps } from './types';
import Calendar from './components/calendar';
import Input from './components/input';
@@ -80,22 +79,23 @@ class SemanticDatepicker extends React.Component<
locale: 'en-US',
name: undefined,
onBlur: () => {},
+ onChange: () => {},
placeholder: null,
pointing: 'left',
+ readOnly: false,
required: false,
- selected: null,
showOutsideDays: false,
type: 'basic',
- readOnly: false,
+ value: null,
};
el = React.createRef();
componentDidUpdate(prevProps: SemanticDatepickerProps) {
- const { locale, selected } = this.props;
+ const { locale, value } = this.props;
- if (!isEqual(selected, prevProps.selected)) {
- this.onDateSelected(selected);
+ if (!isEqual(value, prevProps.value)) {
+ this.onDateSelected(undefined, value);
}
if (locale !== prevProps.locale) {
@@ -108,14 +108,14 @@ class SemanticDatepicker extends React.Component<
}
get initialState() {
- const { format, selected } = this.props;
+ const { format, value } = this.props;
const initialSelectedDate = this.isRangeInput ? [] : null;
return {
isVisible: false,
locale: this.locale,
- selectedDate: selected || initialSelectedDate,
- selectedDateFormatted: formatSelectedDate(selected, format),
+ selectedDate: value || initialSelectedDate,
+ selectedDateFormatted: formatSelectedDate(value, format),
typedValue: null,
};
}
@@ -138,6 +138,10 @@ class SemanticDatepicker extends React.Component<
const { selectedDate } = this.state;
const { date } = this.props;
+ if (!selectedDate) {
+ return date;
+ }
+
return this.isRangeInput ? selectedDate[0] : selectedDate || date;
}
@@ -169,8 +173,8 @@ class SemanticDatepicker extends React.Component<
? RangeDatePicker
: BasicDatePicker;
- resetState = () => {
- const { keepOpenOnClear, onDateChange } = this.props;
+ resetState = event => {
+ const { keepOpenOnClear, onChange } = this.props;
const newState = {
isVisible: keepOpenOnClear,
selectedDate: this.isRangeInput ? [] : null,
@@ -178,7 +182,7 @@ class SemanticDatepicker extends React.Component<
};
this.setState(newState, () => {
- onDateChange(null);
+ onChange(event, { ...this.props, value: null });
});
};
@@ -219,11 +223,11 @@ class SemanticDatepicker extends React.Component<
});
};
- handleRangeInput = newDates => {
- const { format, keepOpenOnSelect, onDateChange } = this.props;
+ handleRangeInput = (newDates, event) => {
+ const { format, keepOpenOnSelect, onChange } = this.props;
if (!newDates || !newDates.length) {
- this.resetState();
+ this.resetState(event);
return;
}
@@ -235,7 +239,7 @@ class SemanticDatepicker extends React.Component<
};
this.setState(newState, () => {
- onDateChange(newDates);
+ onChange(event, { ...this.props, value: newDates });
if (newDates.length === 2) {
this.setState({ isVisible: keepOpenOnSelect });
@@ -243,11 +247,11 @@ class SemanticDatepicker extends React.Component<
});
};
- handleBasicInput = newDate => {
+ handleBasicInput = (newDate, event) => {
const {
format,
keepOpenOnSelect,
- onDateChange,
+ onChange,
clearOnSameDateClick,
} = this.props;
@@ -256,7 +260,7 @@ class SemanticDatepicker extends React.Component<
// then reset the state. This is what was previously the default
// behavior, without a specific prop.
if (clearOnSameDateClick) {
- this.resetState();
+ this.resetState(event);
} else {
// Don't reset the state. Instead, close or keep open the
// datepicker according to the value of keepOpenOnSelect.
@@ -277,7 +281,7 @@ class SemanticDatepicker extends React.Component<
};
this.setState(newState, () => {
- onDateChange(newDate);
+ onChange(event, { ...this.props, value: newDate });
});
};
@@ -301,14 +305,14 @@ class SemanticDatepicker extends React.Component<
const areDatesValid = parsedValue.every(isValid);
if (areDatesValid) {
- this.handleRangeInput(parsedValue);
+ this.handleRangeInput(parsedValue, event);
return;
}
} else {
const isDateValid = isValid(parsedValue);
if (isDateValid) {
- this.handleBasicInput(parsedValue);
+ this.handleBasicInput(parsedValue, event);
return;
}
}
@@ -316,8 +320,8 @@ class SemanticDatepicker extends React.Component<
this.setState({ typedValue: null });
};
- handleChange = (_evt, { value }) => {
- const { allowOnlyNumbers, format, onDateChange } = this.props;
+ handleChange = (event: React.SyntheticEvent, { value }) => {
+ const { allowOnlyNumbers, format, onChange } = this.props;
const formatString = this.isRangeInput ? `${format} - ${format}` : format;
const typedValue = allowOnlyNumbers ? onlyNumbers(value) : value;
@@ -329,7 +333,7 @@ class SemanticDatepicker extends React.Component<
};
this.setState(newState, () => {
- onDateChange(null);
+ onChange(event, { ...this.props, value: null });
});
return;
@@ -349,11 +353,11 @@ class SemanticDatepicker extends React.Component<
}
};
- onDateSelected = dateOrDates => {
+ onDateSelected = (event: React.SyntheticEvent | undefined, dateOrDates) => {
if (this.isRangeInput) {
- this.handleRangeInput(dateOrDates);
+ this.handleRangeInput(dateOrDates, event);
} else {
- this.handleBasicInput(dateOrDates);
+ this.handleBasicInput(dateOrDates, event);
}
};
diff --git a/src/pickers/basic.tsx b/src/pickers/basic.tsx
index b7a3f7df..f5a793ff 100644
--- a/src/pickers/basic.tsx
+++ b/src/pickers/basic.tsx
@@ -3,12 +3,11 @@ import { BasicDatePickerProps } from '../types';
import BaseDatePicker from './base';
class DatePicker extends React.Component {
- /* eslint-disable-next-line */
- _handleOnDateSelected = ({ selected, selectable, date }) => {
- const { selected: selectedDate, onChange, onDateSelected } = this.props;
- if (onDateSelected) {
- onDateSelected({ selected, selectable, date });
- }
+ _handleOnDateSelected = (
+ { selectable, date },
+ event: React.SyntheticEvent
+ ) => {
+ const { selected: selectedDate, onChange } = this.props;
if (!selectable) {
return;
@@ -20,7 +19,7 @@ class DatePicker extends React.Component {
}
if (onChange) {
- onChange(newDate);
+ onChange(event, newDate);
}
};
diff --git a/src/pickers/index.tsx b/src/pickers/index.tsx
index 1cdfc4d9..9373b0f5 100644
--- a/src/pickers/index.tsx
+++ b/src/pickers/index.tsx
@@ -2,8 +2,7 @@
https://github.com/deseretdigital/dayzed/pull/25
He didn't publish the components to npm, so I copied his work */
-import BaseDatePicker from './base';
-import DatePicker from './basic';
+import BasicDatePicker from './basic';
import RangeDatePicker from './range';
-export { BaseDatePicker, DatePicker, RangeDatePicker };
+export { BasicDatePicker, RangeDatePicker };
diff --git a/src/pickers/range.tsx b/src/pickers/range.tsx
index ec10b903..d18ffe78 100644
--- a/src/pickers/range.tsx
+++ b/src/pickers/range.tsx
@@ -38,12 +38,11 @@ class RangeDatePicker extends React.Component<
this.setHoveredDate(date);
}
- /* eslint-disable-next-line */
- _handleOnDateSelected = ({ selected, selectable, date }) => {
- const { selected: selectedDates, onDateSelected, onChange } = this.props;
- if (onDateSelected) {
- onDateSelected({ selected, selectable, date });
- }
+ _handleOnDateSelected = (
+ { selectable, date },
+ event: React.SyntheticEvent
+ ) => {
+ const { selected: selectedDates, onChange } = this.props;
if (!selectable) {
return;
@@ -67,7 +66,7 @@ class RangeDatePicker extends React.Component<
}
if (onChange) {
- onChange(newDates);
+ onChange(event, newDates);
}
if (newDates.length === 2) {
diff --git a/src/types/index.ts b/src/types/index.ts
index 1c52d407..b370b4ed 100644
--- a/src/types/index.ts
+++ b/src/types/index.ts
@@ -28,12 +28,7 @@ export type LocaleOptions =
export type PickedDayzedProps = Pick<
DayzedProps,
- | 'date'
- | 'maxDate'
- | 'minDate'
- | 'firstDayOfWeek'
- | 'selected'
- | 'showOutsideDays'
+ 'date' | 'maxDate' | 'minDate' | 'firstDayOfWeek' | 'showOutsideDays'
>;
export type PickedFormInputProps = Pick<
@@ -63,9 +58,13 @@ export type SemanticDatepickerProps = PickedDayzedProps &
keepOpenOnSelect: boolean;
locale: LocaleOptions;
onBlur: (event?: React.SyntheticEvent) => void;
- onDateChange: (date: Date | Date[] | null) => void;
+ onChange: (
+ event: React.SyntheticEvent | undefined,
+ data: SemanticDatepickerProps
+ ) => void;
pointing: 'left' | 'right' | 'top left' | 'top right';
type: 'basic' | 'range';
+ value: DayzedProps['selected'];
};
export type DayzedProps = {
@@ -76,18 +75,18 @@ export type DayzedProps = {
minDate?: Date;
monthsToDisplay: number;
offset: number;
- onDateSelected: (props: any) => void;
+ onDateSelected: (dateObj: any, event: React.SyntheticEvent) => void;
onOffsetChanged: () => void;
- selected: Date | Date[];
+ selected: Date | Date[] | null;
showOutsideDays: boolean;
};
export type BasicDatePickerProps = DayzedProps & {
- onChange: (date: Date | null) => void;
+ onChange: (event: React.SyntheticEvent, date: Date | null) => void;
selected: Date;
};
export type RangeDatePickerProps = DayzedProps & {
- onChange: (dates: Date[] | null) => void;
+ onChange: (event: React.SyntheticEvent, dates: Date[] | null) => void;
selected: Date[];
};
diff --git a/src/utils.ts b/src/utils.ts
index 18dcbd33..d1d3e391 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -93,3 +93,11 @@ export const parseOnBlur = (
};
export const onlyNumbers = (value = '') => value.replace(/[^\d]/g, '');
+
+export function getShortDate(date?: Date) {
+ if (!date) {
+ return undefined;
+ }
+
+ return date.toISOString().slice(0, 10);
+}
diff --git a/stories/basic.stories.tsx b/stories/basic.stories.tsx
index e835df23..54e7b7fa 100644
--- a/stories/basic.stories.tsx
+++ b/stories/basic.stories.tsx
@@ -18,20 +18,20 @@ export default {
export const simple = () => (
-
+
);
export const withReadOnly = () => (
-
+
);
export const withoutClearOnSameDateClick = () => (
@@ -39,28 +39,19 @@ export const withoutClearOnSameDateClick = () => (
export const withAllowOnlyNumbers = () => (
-
+
);
export const withFirstDayOfWeek = () => (
-
+
);
export const withOutsideDays = () => (
-
+
);
@@ -68,7 +59,7 @@ export const withFormatProp = () => (
);
@@ -76,20 +67,17 @@ export const withFormatProp = () => (
export const withBrazilianPortugueseLocale = () => (
);
export const withKeepOpenOnSelect = () => (
-
+
);
@@ -100,13 +88,13 @@ export const asFormComponent = () => (
@@ -115,19 +103,13 @@ export const asFormComponent = () => (
export const withLeftPointing = () => (
-
+
);
export const withRightPointing = () => (
-
+
);
@@ -144,7 +126,7 @@ export const withTopLeftPointing = () => (
>
);
@@ -162,7 +144,7 @@ export const withTopRightPointing = () => (
>
);
@@ -171,7 +153,7 @@ export const withFilterDate = () => (
@@ -182,7 +164,7 @@ export const withFilterDateSettingMaxDate = () => (
diff --git a/stories/range.stories.tsx b/stories/range.stories.tsx
index 0ba17c25..00855290 100644
--- a/stories/range.stories.tsx
+++ b/stories/range.stories.tsx
@@ -10,7 +10,7 @@ export default {
export const simple = () => (
-
+
);
@@ -19,7 +19,7 @@ export const withRightPointing = () => (
);
@@ -29,7 +29,7 @@ export const withFirstDayOfWeek = () => (
);
@@ -39,7 +39,7 @@ export const withOutsideDays = () => (
);
@@ -49,7 +49,7 @@ export const withFormatProp = () => (
);
@@ -58,7 +58,7 @@ export const withPolishLocale = () => (