Revision 4ba0fc0a
Added by Maria Agaphontzev about 5 years ago
webpack/assets/javascripts/react_app/components/common/DateTimePicker/DateComponents/DateConstants.js | ||
---|---|---|
export const YEAR = 'YEAR';
|
||
export const MONTH = 'MONTH';
|
||
export const DAY = 'DAY';
|
webpack/assets/javascripts/react_app/components/common/DateTimePicker/DateComponents/DateInput.js | ||
---|---|---|
import React from 'react';
|
||
import PropTypes from 'prop-types';
|
||
import classNames from 'classnames';
|
||
import { addMonths } from './helpers';
|
||
import MonthView from './MonthView';
|
||
import YearView from './YearView';
|
||
import DecadeView from './DecadeView';
|
||
import { YEAR, DAY, MONTH } from './DateConstants';
|
||
|
||
class DateInput extends React.Component {
|
||
state = {
|
||
date: new Date(this.props.date),
|
||
typeOfDateInput: this.props.typeOfDateInput,
|
||
};
|
||
componentDidUpdate = prevProps => {
|
||
const newDate = this.props.date;
|
||
if (prevProps.date !== newDate) {
|
||
this.setState({
|
||
date: newDate,
|
||
typeOfDateInput: this.props.typeOfDateInput,
|
||
});
|
||
}
|
||
};
|
||
getPrevMonth = () => {
|
||
const { date } = this.state;
|
||
this.setState({ date: addMonths(date, -1) });
|
||
};
|
||
getNextMonth = () => {
|
||
const { date } = this.state;
|
||
this.setState({ date: addMonths(date, 1) });
|
||
};
|
||
setSelected = day => {
|
||
this.setState({
|
||
date: day,
|
||
});
|
||
this.props.setSelected(day);
|
||
};
|
||
toggleDateView = (type = null) => {
|
||
this.setState({
|
||
typeOfDateInput: type,
|
||
});
|
||
};
|
||
getDateViewByType = type => {
|
||
const { date, locale, weekStartsOn, setSelected } = this.props;
|
||
switch (type) {
|
||
case DAY:
|
||
return (
|
||
<DecadeView
|
||
date={date}
|
||
setSelected={setSelected}
|
||
toggleDateView={this.toggleDateView}
|
||
/>
|
||
);
|
||
case YEAR:
|
||
return (
|
||
<YearView
|
||
date={date}
|
||
setSelected={setSelected}
|
||
locale={locale}
|
||
toggleDateView={this.toggleDateView}
|
||
/>
|
||
);
|
||
default:
|
||
return (
|
||
<MonthView
|
||
date={date}
|
||
setSelected={setSelected}
|
||
locale={locale}
|
||
weekStartsOn={weekStartsOn}
|
||
toggleDateView={this.toggleDateView}
|
||
/>
|
||
);
|
||
}
|
||
};
|
||
render() {
|
||
const { className } = this.props;
|
||
const { typeOfDateInput } = this.state;
|
||
return (
|
||
<div className={classNames('datepicker', className)}>
|
||
{this.getDateViewByType(typeOfDateInput)}
|
||
</div>
|
||
);
|
||
}
|
||
}
|
||
|
||
DateInput.propTypes = {
|
||
date: PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.string]),
|
||
setSelected: PropTypes.func,
|
||
locale: PropTypes.string,
|
||
weekStartsOn: PropTypes.number,
|
||
className: PropTypes.string,
|
||
typeOfDateInput: PropTypes.string,
|
||
};
|
||
|
||
DateInput.defaultProps = {
|
||
setSelected: null,
|
||
date: new Date(),
|
||
locale: 'en-US',
|
||
weekStartsOn: 1,
|
||
className: '',
|
||
typeOfDateInput: MONTH,
|
||
};
|
||
export default DateInput;
|
webpack/assets/javascripts/react_app/components/common/DateTimePicker/DateComponents/DateInput.test.js | ||
---|---|---|
import React from 'react';
|
||
import { shallow, mount } from 'enzyme';
|
||
import toJson from 'enzyme-to-json';
|
||
import DateInput from './DateInput';
|
||
|
||
test('DateInput is working properly', () => {
|
||
const component = shallow(<DateInput date="1/21/2019 , 2:22:31 PM" />);
|
||
|
||
expect(toJson(component.render())).toMatchSnapshot();
|
||
});
|
||
|
||
test('DateInput changes selected on click', () => {
|
||
const setSelected = jest.fn();
|
||
const component = mount(
|
||
<DateInput date="1/21/2019 , 2:22:31 PM" setSelected={setSelected} />
|
||
);
|
||
component
|
||
.find('.weekend')
|
||
.first()
|
||
.simulate('click');
|
||
expect(setSelected).toBeCalledWith(new Date('2019-01-04 14:22:31'));
|
||
});
|
||
|
||
test('DateInput toggles view to years', () => {
|
||
const component = mount(<DateInput date="1/21/2019 , 2:22:31 PM" />);
|
||
component
|
||
.find('.picker-switch')
|
||
.first()
|
||
.simulate('click');
|
||
expect(toJson(component.render())).toMatchSnapshot();
|
||
});
|
||
test('DateInput toggles view to decades', () => {
|
||
const component = mount(<DateInput date="1/21/2019 , 2:22:31 PM" />);
|
||
component
|
||
.find('.picker-switch')
|
||
.first()
|
||
.simulate('click');
|
||
component
|
||
.find('.picker-switch')
|
||
.first()
|
||
.simulate('click');
|
||
expect(toJson(component.render())).toMatchSnapshot();
|
||
});
|
webpack/assets/javascripts/react_app/components/common/DateTimePicker/DateComponents/Day.js | ||
---|---|---|
import React from 'react';
|
||
import PropTypes from 'prop-types';
|
||
import classNames from 'classnames';
|
||
|
||
const Day = ({ day, setSelected, classNamesArray }) => {
|
||
const date = day.getDate();
|
||
return (
|
||
<td
|
||
className={classNames('day', classNamesArray)}
|
||
data-day={date}
|
||
onClick={() => {
|
||
setSelected(day);
|
||
}}
|
||
>
|
||
{date}
|
||
</td>
|
||
);
|
||
};
|
||
|
||
Day.propTypes = {
|
||
day: PropTypes.instanceOf(Date).isRequired,
|
||
classNamesArray: PropTypes.object,
|
||
setSelected: PropTypes.func,
|
||
};
|
||
|
||
Day.defaultProps = {
|
||
setSelected: null,
|
||
classNamesArray: [],
|
||
};
|
||
export default Day;
|
webpack/assets/javascripts/react_app/components/common/DateTimePicker/DateComponents/Day.test.js | ||
---|---|---|
import React from 'react';
|
||
import { shallow } from 'enzyme';
|
||
import toJson from 'enzyme-to-json';
|
||
import Day from './Day';
|
||
|
||
test('Day is working properly', () => {
|
||
const day = new Date('2019-01-04 14:22:31');
|
||
const currDate = new Date('2019-01-02 12:22:31');
|
||
const selectedDate = new Date('2019-01-05 18:22:31');
|
||
const component = shallow(
|
||
<Day
|
||
day={day}
|
||
currDate={currDate}
|
||
selectedDate={selectedDate}
|
||
classNamesArray={{ weekend: true }}
|
||
/>
|
||
);
|
||
|
||
expect(toJson(component.render())).toMatchSnapshot();
|
||
});
|
webpack/assets/javascripts/react_app/components/common/DateTimePicker/DateComponents/DecadeView.js | ||
---|---|---|
import React from 'react';
|
||
import PropTypes from 'prop-types';
|
||
import times from 'lodash/times';
|
||
import { addYears } from './helpers';
|
||
import { noop } from '../../../../common/helpers';
|
||
import { DecadeViewHeader } from './DecadeViewHeader';
|
||
import { DecadeViewTable } from './DecadeViewTable';
|
||
import { YEAR } from './DateConstants';
|
||
|
||
class DecadeView extends React.Component {
|
||
state = {
|
||
date: new Date(this.props.date),
|
||
selectedDate: new Date(this.props.date),
|
||
};
|
||
getYearArray = () => {
|
||
const { date } = this.state;
|
||
date.setFullYear(Math.floor(date.getFullYear() / 10) * 10);
|
||
return times(12, i => addYears(date, i).getFullYear());
|
||
};
|
||
getPrevDecade = () => {
|
||
const { date } = this.state;
|
||
this.setState({ date: addYears(date, -10) });
|
||
};
|
||
getNextDecade = () => {
|
||
const { date } = this.state;
|
||
this.setState({ date: addYears(date, 10) });
|
||
};
|
||
setSelectedYear = year => {
|
||
const { setSelected, toggleDateView } = this.props;
|
||
const { date } = this.state;
|
||
date.setFullYear(year);
|
||
setSelected(date);
|
||
toggleDateView(YEAR);
|
||
};
|
||
|
||
render() {
|
||
const { date, selectedDate } = this.state;
|
||
const currDecade = Math.floor(date.getFullYear() / 10) * 10;
|
||
const selectedYear = selectedDate.getFullYear();
|
||
const yearArray = this.getYearArray();
|
||
return (
|
||
<div className="datepicker-years">
|
||
<table className="table-condensed">
|
||
<DecadeViewHeader
|
||
currDecade={currDecade}
|
||
getNextDecade={this.getNextDecade}
|
||
getPrevDecade={this.getPrevDecade}
|
||
/>
|
||
<DecadeViewTable
|
||
selectedYear={selectedYear}
|
||
yearArray={yearArray}
|
||
setSelectedYear={this.setSelectedYear}
|
||
/>
|
||
</table>
|
||
</div>
|
||
);
|
||
}
|
||
}
|
||
|
||
DecadeView.propTypes = {
|
||
date: PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.string]),
|
||
setSelected: PropTypes.func,
|
||
toggleDateView: PropTypes.func,
|
||
};
|
||
|
||
DecadeView.defaultProps = {
|
||
setSelected: noop,
|
||
toggleDateView: noop,
|
||
date: new Date(),
|
||
};
|
||
export default DecadeView;
|
webpack/assets/javascripts/react_app/components/common/DateTimePicker/DateComponents/DecadeView.test.js | ||
---|---|---|
import React from 'react';
|
||
import { shallow, mount } from 'enzyme';
|
||
import toJson from 'enzyme-to-json';
|
||
import DecadeView from './DecadeView';
|
||
|
||
test('DecadeView is working properly', () => {
|
||
const component = shallow(<DecadeView />);
|
||
|
||
expect(toJson(component.render())).toMatchSnapshot();
|
||
});
|
||
|
||
test('Edit year DecadeView', () => {
|
||
const date = new Date('2/21/2019 , 2:22:31 PM');
|
||
const setSelected = jest.fn();
|
||
const component = mount(<DecadeView date={date} setSelected={setSelected} />);
|
||
expect(toJson(component.render())).toMatchSnapshot();
|
||
component
|
||
.find('.year')
|
||
.first()
|
||
.simulate('click');
|
||
expect(setSelected).toBeCalledWith(new Date('2/21/2010, 2:22:31 PM'));
|
||
});
|
||
|
||
test('Edit decade DecadeView', () => {
|
||
const date = new Date('2/21/2019 , 2:22:31 PM');
|
||
const setSelected = jest.fn();
|
||
const component = mount(<DecadeView date={date} setSelected={setSelected} />);
|
||
expect(toJson(component.render())).toMatchSnapshot();
|
||
component
|
||
.find('.next')
|
||
.first()
|
||
.simulate('click');
|
||
component
|
||
.find('.year')
|
||
.first()
|
||
.simulate('click');
|
||
expect(setSelected).toBeCalledWith(new Date('2/21/2020 , 2:22:31 PM'));
|
||
component
|
||
.find('.prev')
|
||
.first()
|
||
.simulate('click');
|
||
component
|
||
.find('.year')
|
||
.first()
|
||
.simulate('click');
|
||
expect(setSelected).toBeCalledWith(new Date('2/21/2010 , 2:22:31 PM'));
|
||
});
|
webpack/assets/javascripts/react_app/components/common/DateTimePicker/DateComponents/DecadeViewHeader.js | ||
---|---|---|
import React from 'react';
|
||
import PropTypes from 'prop-types';
|
||
import { noop } from '../../../../common/helpers';
|
||
|
||
export const DecadeViewHeader = ({
|
||
currDecade,
|
||
getPrevDecade,
|
||
getNextDecade,
|
||
}) => (
|
||
<thead>
|
||
<tr>
|
||
<th className="prev" onClick={getPrevDecade}>
|
||
<span className="glyphicon glyphicon-chevron-left" />
|
||
</th>
|
||
<th className="picker-switch" data-action="pickerSwitch" colSpan="5">
|
||
{`${currDecade}-${currDecade + 11}`}
|
||
</th>
|
||
<th className="next" onClick={getNextDecade}>
|
||
<span className="glyphicon glyphicon-chevron-right" />
|
||
</th>
|
||
</tr>
|
||
</thead>
|
||
);
|
||
|
||
DecadeViewHeader.propTypes = {
|
||
currDecade: PropTypes.number,
|
||
getPrevDecade: PropTypes.func,
|
||
getNextDecade: PropTypes.func,
|
||
};
|
||
DecadeViewHeader.defaultProps = {
|
||
currDecade: 20,
|
||
getPrevDecade: noop,
|
||
getNextDecade: noop,
|
||
};
|
||
export default DecadeViewHeader;
|
webpack/assets/javascripts/react_app/components/common/DateTimePicker/DateComponents/DecadeViewHeader.test.js | ||
---|---|---|
import React from 'react';
|
||
import { shallow } from 'enzyme';
|
||
import toJson from 'enzyme-to-json';
|
||
import { DecadeViewHeader } from './DecadeViewHeader';
|
||
|
||
test('DecadeViewHeader is working properly', () => {
|
||
const component = shallow(<DecadeViewHeader currDecade={2010} />);
|
||
|
||
expect(toJson(component.render())).toMatchSnapshot();
|
||
});
|
webpack/assets/javascripts/react_app/components/common/DateTimePicker/DateComponents/DecadeViewTable.js | ||
---|---|---|
import React from 'react';
|
||
import PropTypes from 'prop-types';
|
||
import { noop } from '../../../../common/helpers';
|
||
|
||
export const DecadeViewTable = ({
|
||
yearArray,
|
||
selectedYear,
|
||
setSelectedYear,
|
||
}) => (
|
||
<tbody>
|
||
<tr>
|
||
<td colSpan="7">
|
||
{yearArray.map(year => (
|
||
<span
|
||
onClick={() => setSelectedYear(year)}
|
||
className={`year ${year === selectedYear ? 'active' : ''}`}
|
||
key={year}
|
||
>
|
||
{year}
|
||
</span>
|
||
))}
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
);
|
||
|
||
DecadeViewTable.propTypes = {
|
||
yearArray: PropTypes.array,
|
||
selectedYear: PropTypes.number,
|
||
setSelectedYear: PropTypes.func,
|
||
};
|
||
DecadeViewTable.defaultProps = {
|
||
yearArray: [],
|
||
selectedYear: new Date().getFullYear(),
|
||
setSelectedYear: noop,
|
||
};
|
||
|
||
export default DecadeViewTable;
|
webpack/assets/javascripts/react_app/components/common/DateTimePicker/DateComponents/DecadeViewTable.test.js | ||
---|---|---|
import React from 'react';
|
||
import { shallow } from 'enzyme';
|
||
import toJson from 'enzyme-to-json';
|
||
import { DecadeViewTable } from './DecadeViewTable';
|
||
|
||
test('DecadeViewTable is working properly', () => {
|
||
const component = shallow(
|
||
<DecadeViewTable
|
||
selectedYear={2019}
|
||
yearArray={[
|
||
2010,
|
||
2011,
|
||
2012,
|
||
2013,
|
||
2014,
|
||
2015,
|
||
2016,
|
||
2017,
|
||
2018,
|
||
2019,
|
||
2020,
|
||
2021,
|
||
]}
|
||
/>
|
||
);
|
||
|
||
expect(toJson(component.render())).toMatchSnapshot();
|
||
});
|
webpack/assets/javascripts/react_app/components/common/DateTimePicker/DateComponents/Header.js | ||
---|---|---|
import React from 'react';
|
||
import PropTypes from 'prop-types';
|
||
import { Icon } from 'patternfly-react';
|
||
import { YEAR } from './DateConstants';
|
||
import { getWeekArray } from './HeaderHelpers';
|
||
|
||
const Header = ({
|
||
getNextMonth,
|
||
getPrevMonth,
|
||
toggleDateView,
|
||
weekStartsOn,
|
||
date,
|
||
locale,
|
||
}) => {
|
||
date = new Date(date);
|
||
const month = Intl.DateTimeFormat(locale, {
|
||
month: 'long',
|
||
}).format(date);
|
||
const year = date.getFullYear();
|
||
const daysOfTheWeek = getWeekArray(weekStartsOn);
|
||
return (
|
||
<thead>
|
||
<tr>
|
||
<th className="prev" onClick={getPrevMonth}>
|
||
<Icon type="fa" name="angle-left" />
|
||
</th>
|
||
<th
|
||
className="picker-switch"
|
||
colSpan="5"
|
||
onClick={() => toggleDateView(YEAR)}
|
||
>
|
||
{month} {year}
|
||
</th>
|
||
<th className="next" onClick={getNextMonth}>
|
||
<Icon type="fa" name="angle-right" />
|
||
</th>
|
||
</tr>
|
||
<tr>
|
||
{daysOfTheWeek.map((day, idx) => (
|
||
<th key={idx} className="dow">
|
||
{day}
|
||
</th>
|
||
))}
|
||
</tr>
|
||
</thead>
|
||
);
|
||
};
|
||
|
||
Header.propTypes = {
|
||
date: PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.string]),
|
||
getPrevMonth: PropTypes.func,
|
||
getNextMonth: PropTypes.func,
|
||
toggleDateView: PropTypes.func,
|
||
locale: PropTypes.string,
|
||
weekStartsOn: PropTypes.number,
|
||
};
|
||
|
||
Header.defaultProps = {
|
||
date: new Date(),
|
||
getPrevMonth: null,
|
||
getNextMonth: null,
|
||
toggleDateView: null,
|
||
locale: 'en-US',
|
||
weekStartsOn: 1,
|
||
};
|
||
export default Header;
|
webpack/assets/javascripts/react_app/components/common/DateTimePicker/DateComponents/Header.test.js | ||
---|---|---|
import React from 'react';
|
||
import { shallow } from 'enzyme';
|
||
import toJson from 'enzyme-to-json';
|
||
import Header from './Header';
|
||
|
||
test('Header is working properly', () => {
|
||
const date = new Date('2019-01-04 14:22:31');
|
||
const component = shallow(<Header date={date} />);
|
||
|
||
expect(toJson(component.render())).toMatchSnapshot();
|
||
});
|
||
|
||
test('Header is working properly with different week start', () => {
|
||
const date = new Date('2019-01-04 14:22:31');
|
||
const component = shallow(<Header date={date} weekStartsOn={5} />);
|
||
|
||
expect(toJson(component.render())).toMatchSnapshot();
|
||
});
|
webpack/assets/javascripts/react_app/components/common/DateTimePicker/DateComponents/HeaderHelpers.js | ||
---|---|---|
import times from 'lodash/times';
|
||
import { addDays, getWeekStart } from './helpers';
|
||
|
||
export const getWeekArray = (weekStartsOn, locale) => {
|
||
const weekStart = getWeekStart(new Date());
|
||
const dayFormat =
|
||
Intl.DateTimeFormat(locale, { weekday: 'short' }).format(weekStart).length >
|
||
3
|
||
? 'narrow'
|
||
: 'short';
|
||
return times(7, i =>
|
||
Intl.DateTimeFormat(locale, { weekday: dayFormat })
|
||
.format(addDays(weekStart, (i + weekStartsOn) % 7))
|
||
.slice(0, 2)
|
||
);
|
||
};
|
webpack/assets/javascripts/react_app/components/common/DateTimePicker/DateComponents/MonthView.js | ||
---|---|---|
import React from 'react';
|
||
import PropTypes from 'prop-types';
|
||
import chunk from 'lodash/chunk';
|
||
import times from 'lodash/times';
|
||
import Day from './Day';
|
||
import {
|
||
addDays,
|
||
addMonths,
|
||
getMonthStart,
|
||
isEquelDate,
|
||
isWeekend,
|
||
} from './helpers';
|
||
import Header from './Header';
|
||
|
||
class MonthView extends React.Component {
|
||
state = {
|
||
selectedDate: new Date(this.props.date),
|
||
date: new Date(this.props.date),
|
||
};
|
||
|
||
componentDidUpdate = prevProps => {
|
||
const newDate = this.props.date;
|
||
if (prevProps.date !== newDate) {
|
||
this.setState({
|
||
selectedDate: newDate,
|
||
date: newDate,
|
||
});
|
||
}
|
||
};
|
||
|
||
calendarArray = date => {
|
||
const { weekStartsOn } = this.props;
|
||
const monthStart = getMonthStart(new Date(date));
|
||
const offset = monthStart.getDay() - weekStartsOn;
|
||
return chunk(times(35, i => addDays(monthStart, i - offset)), 7);
|
||
};
|
||
|
||
getPrevMonth = () => {
|
||
const { date } = this.state;
|
||
this.setState({ date: addMonths(date, -1) });
|
||
};
|
||
getNextMonth = () => {
|
||
const { date } = this.state;
|
||
this.setState({ date: addMonths(date, 1) });
|
||
};
|
||
setSelected = day => {
|
||
this.setState({
|
||
selectedDate: day,
|
||
date: day,
|
||
});
|
||
this.props.setSelected(day);
|
||
};
|
||
|
||
render() {
|
||
const { locale, weekStartsOn, toggleDateView } = this.props;
|
||
const { date, selectedDate } = this.state;
|
||
const calendar = this.calendarArray(date);
|
||
return (
|
||
<div className="datepicker-days">
|
||
<table className="table-condensed">
|
||
<Header
|
||
getPrevMonth={this.getPrevMonth}
|
||
getNextMonth={this.getNextMonth}
|
||
date={date}
|
||
locale={locale}
|
||
weekStartsOn={weekStartsOn}
|
||
toggleDateView={toggleDateView}
|
||
/>
|
||
<tbody>
|
||
{calendar.map((el, idx) => (
|
||
<tr key={idx}>
|
||
{el.map(day => (
|
||
<Day
|
||
key={day}
|
||
day={day}
|
||
setSelected={this.setSelected}
|
||
classNamesArray={{
|
||
weekend: isWeekend(day),
|
||
old: day.getMonth() !== date.getMonth(),
|
||
active: isEquelDate(day, selectedDate),
|
||
today: isEquelDate(day, new Date()),
|
||
}}
|
||
/>
|
||
))}
|
||
</tr>
|
||
))}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
);
|
||
}
|
||
}
|
||
|
||
MonthView.propTypes = {
|
||
date: PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.string]),
|
||
setSelected: PropTypes.func,
|
||
toggleDateView: PropTypes.func,
|
||
locale: PropTypes.string,
|
||
weekStartsOn: PropTypes.number,
|
||
};
|
||
|
||
MonthView.defaultProps = {
|
||
setSelected: null,
|
||
toggleDateView: null,
|
||
date: new Date(),
|
||
locale: 'en-US',
|
||
weekStartsOn: 1,
|
||
};
|
||
export default MonthView;
|
webpack/assets/javascripts/react_app/components/common/DateTimePicker/DateComponents/MonthView.test.js | ||
---|---|---|
import React from 'react';
|
||
import { shallow } from 'enzyme';
|
||
import toJson from 'enzyme-to-json';
|
||
import MonthView from './MonthView';
|
||
|
||
test('MonthView is working properly', () => {
|
||
const component = shallow(<MonthView date="1/21/2019 , 2:22:31 PM" />);
|
||
|
||
expect(toJson(component.render())).toMatchSnapshot();
|
||
});
|
webpack/assets/javascripts/react_app/components/common/DateTimePicker/DateComponents/TodayButton.js | ||
---|---|---|
import React from 'react';
|
||
import PropTypes from 'prop-types';
|
||
import { translate as __ } from '../../../../common/I18n';
|
||
|
||
const TodayButton = ({ setSelected }) => (
|
||
<table className="table-condensed">
|
||
<tbody>
|
||
<tr>
|
||
<td>
|
||
<button
|
||
className="today-button"
|
||
onClick={() => {
|
||
if (setSelected) setSelected(new Date());
|
||
}}
|
||
>
|
||
<span className="today-button-">{__('Today')}</span>
|
||
</button>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
);
|
||
|
||
TodayButton.propTypes = {
|
||
setSelected: PropTypes.func,
|
||
};
|
||
|
||
TodayButton.defaultProps = {
|
||
setSelected: null,
|
||
};
|
||
export default TodayButton;
|
webpack/assets/javascripts/react_app/components/common/DateTimePicker/DateComponents/TodayButton.test.js | ||
---|---|---|
import React from 'react';
|
||
import { shallow } from 'enzyme';
|
||
import toJson from 'enzyme-to-json';
|
||
import TodayButton from './TodayButton';
|
||
|
||
const mockedDate = new Date('2/21/2019 , 3:22:31 PM');
|
||
|
||
global.Date = jest.fn(() => mockedDate);
|
||
global.Date.now = jest.fn(() => mockedDate);
|
||
|
||
test('TodayButton is working properly', () => {
|
||
const component = shallow(<TodayButton />);
|
||
|
||
expect(toJson(component.render())).toMatchSnapshot();
|
||
});
|
||
test('TodayButton Click is setting the date', () => {
|
||
const setSelected = jest.fn();
|
||
const component = shallow(<TodayButton setSelected={setSelected} />);
|
||
const date = new Date();
|
||
component.find('.today-button').simulate('click');
|
||
expect(setSelected).toBeCalledWith(date);
|
||
});
|
webpack/assets/javascripts/react_app/components/common/DateTimePicker/DateComponents/YearView.js | ||
---|---|---|
import React from 'react';
|
||
import PropTypes from 'prop-types';
|
||
import times from 'lodash/times';
|
||
import classNames from 'classnames';
|
||
import { addMonths, addYears } from './helpers';
|
||
import { noop } from '../../../../common/helpers';
|
||
import { MONTH, DAY } from './DateConstants';
|
||
|
||
class YearView extends React.Component {
|
||
state = {
|
||
date: new Date(this.props.date),
|
||
selectedDate: new Date(this.props.date),
|
||
};
|
||
getMonthArray = () => {
|
||
const date = new Date('1/1/1');
|
||
return times(12, i =>
|
||
Intl.DateTimeFormat(this.props.locale, { month: 'short' }).format(
|
||
addMonths(date, i)
|
||
)
|
||
);
|
||
};
|
||
getPrevYear = () => {
|
||
const { date } = this.state;
|
||
this.setState({ date: addYears(date, -1) });
|
||
};
|
||
getNextYear = () => {
|
||
const { date } = this.state;
|
||
this.setState({ date: addYears(date, 1) });
|
||
};
|
||
setSelectedMonth = month => {
|
||
const { date } = this.state;
|
||
date.setMonth(month);
|
||
this.props.setSelected(date);
|
||
this.props.toggleDateView(MONTH);
|
||
};
|
||
|
||
render() {
|
||
const { date, selectedDate } = this.state;
|
||
const [currMonth, currYear] = [date.getMonth(), date.getFullYear()];
|
||
const selectedYear = selectedDate.getFullYear();
|
||
const monthArray = this.getMonthArray();
|
||
return (
|
||
<div className="datepicker-months">
|
||
<table className="table-condensed">
|
||
<thead>
|
||
<tr>
|
||
<th className="prev" onClick={this.getPrevYear}>
|
||
<span className="glyphicon glyphicon-chevron-left" />
|
||
</th>
|
||
<th
|
||
className="picker-switch"
|
||
onClick={() => this.props.toggleDateView(DAY)}
|
||
colSpan="5"
|
||
>
|
||
{currYear}
|
||
</th>
|
||
<th className="next" onClick={this.getNextYear}>
|
||
<span className="glyphicon glyphicon-chevron-right" />
|
||
</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td colSpan="7">
|
||
{monthArray.map((month, idx) => (
|
||
<span
|
||
onClick={() => this.setSelectedMonth(idx)}
|
||
className={classNames('month', {
|
||
active: idx === currMonth && selectedYear === currYear,
|
||
})}
|
||
key={idx}
|
||
>
|
||
{month}
|
||
</span>
|
||
))}
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
);
|
||
}
|
||
}
|
||
|
||
YearView.propTypes = {
|
||
date: PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.string]),
|
||
setSelected: PropTypes.func,
|
||
toggleDateView: PropTypes.func,
|
||
locale: PropTypes.string,
|
||
};
|
||
|
||
YearView.defaultProps = {
|
||
setSelected: noop,
|
||
toggleDateView: noop,
|
||
date: new Date(),
|
||
locale: 'en-US',
|
||
};
|
||
export default YearView;
|
webpack/assets/javascripts/react_app/components/common/DateTimePicker/DateComponents/YearView.test.js | ||
---|---|---|
import React from 'react';
|
||
import { shallow } from 'enzyme';
|
||
import toJson from 'enzyme-to-json';
|
||
import YearView from './YearView';
|
||
|
||
test('YearView is working properly', () => {
|
||
const date = new Date('2/21/2019 , 2:22:31 PM');
|
||
const component = shallow(<YearView date={date} />);
|
||
|
||
expect(toJson(component.render())).toMatchSnapshot();
|
||
});
|
||
|
||
test('Edit month YearView', () => {
|
||
const date = new Date('2/21/2019 , 2:22:31 PM');
|
||
const setSelected = jest.fn();
|
||
const component = shallow(<YearView date={date} setSelected={setSelected} />);
|
||
expect(toJson(component.render())).toMatchSnapshot();
|
||
component
|
||
.find('.month')
|
||
.first()
|
||
.simulate('click');
|
||
expect(setSelected).toBeCalledWith(new Date('1/21/2019 , 2:22:31 PM'));
|
||
});
|
||
|
||
test('Edit year and month YearView', () => {
|
||
const date = new Date('2/21/2019 , 2:22:31 PM');
|
||
const setSelected = jest.fn();
|
||
const component = shallow(<YearView date={date} setSelected={setSelected} />);
|
||
expect(toJson(component.render())).toMatchSnapshot();
|
||
component
|
||
.find('.next')
|
||
.first()
|
||
.simulate('click');
|
||
component
|
||
.find('.month')
|
||
.first()
|
||
.simulate('click');
|
||
expect(setSelected).toBeCalledWith(new Date('1/21/2020 , 2:22:31 PM'));
|
||
component
|
||
.find('.prev')
|
||
.first()
|
||
.simulate('click');
|
||
component
|
||
.find('.month')
|
||
.first()
|
||
.simulate('click');
|
||
expect(setSelected).toBeCalledWith(new Date('1/21/2019 , 2:22:31 PM'));
|
||
});
|
webpack/assets/javascripts/react_app/components/common/DateTimePicker/DateComponents/__snapshots__/DateInput.test.js.snap | ||
---|---|---|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||
|
||
exports[`DateInput is working properly 1`] = `
|
||
<div
|
||
class="datepicker"
|
||
>
|
||
<div
|
||
class="datepicker-days"
|
||
>
|
||
<table
|
||
class="table-condensed"
|
||
>
|
||
<thead>
|
||
<tr>
|
||
<th
|
||
class="prev"
|
||
>
|
||
<span
|
||
aria-hidden="true"
|
||
class="fa fa-angle-left"
|
||
/>
|
||
</th>
|
||
<th
|
||
class="picker-switch"
|
||
colspan="5"
|
||
>
|
||
January 2019
|
||
</th>
|
||
<th
|
||
class="next"
|
||
>
|
||
<span
|
||
aria-hidden="true"
|
||
class="fa fa-angle-right"
|
||
/>
|
||
</th>
|
||
</tr>
|
||
<tr>
|
||
<th
|
||
class="dow"
|
||
>
|
||
Mo
|
||
</th>
|
||
<th
|
||
class="dow"
|
||
>
|
||
Tu
|
||
</th>
|
||
<th
|
||
class="dow"
|
||
>
|
||
We
|
||
</th>
|
||
<th
|
||
class="dow"
|
||
>
|
||
Th
|
||
</th>
|
||
<th
|
||
class="dow"
|
||
>
|
||
Fr
|
||
</th>
|
||
<th
|
||
class="dow"
|
||
>
|
||
Sa
|
||
</th>
|
||
<th
|
||
class="dow"
|
||
>
|
||
Su
|
||
</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td
|
||
class="day old"
|
||
data-day="31"
|
||
>
|
||
31
|
||
</td>
|
||
<td
|
||
class="day"
|
||
data-day="1"
|
||
>
|
||
1
|
||
</td>
|
||
<td
|
||
class="day"
|
||
data-day="2"
|
||
>
|
||
2
|
||
</td>
|
||
<td
|
||
class="day"
|
||
data-day="3"
|
||
>
|
||
3
|
||
</td>
|
||
<td
|
||
class="day weekend"
|
||
data-day="4"
|
||
>
|
||
4
|
||
</td>
|
||
<td
|
||
class="day weekend"
|
||
data-day="5"
|
||
>
|
||
5
|
||
</td>
|
||
<td
|
||
class="day"
|
||
data-day="6"
|
||
>
|
||
6
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<td
|
||
class="day"
|
||
data-day="7"
|
||
>
|
||
7
|
||
</td>
|
||
<td
|
||
class="day"
|
||
data-day="8"
|
||
>
|
||
8
|
||
</td>
|
||
<td
|
||
class="day"
|
||
data-day="9"
|
||
>
|
||
9
|
||
</td>
|
||
<td
|
||
class="day"
|
||
data-day="10"
|
||
>
|
||
10
|
||
</td>
|
||
<td
|
||
class="day weekend"
|
||
data-day="11"
|
||
>
|
||
11
|
||
</td>
|
||
<td
|
||
class="day weekend"
|
||
data-day="12"
|
||
>
|
||
12
|
||
</td>
|
||
<td
|
||
class="day"
|
||
data-day="13"
|
||
>
|
||
13
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<td
|
||
class="day"
|
||
data-day="14"
|
||
>
|
||
14
|
||
</td>
|
||
<td
|
||
class="day"
|
||
data-day="15"
|
||
>
|
||
15
|
||
</td>
|
||
<td
|
||
class="day"
|
||
data-day="16"
|
||
>
|
||
16
|
||
</td>
|
||
<td
|
||
class="day"
|
||
data-day="17"
|
||
>
|
||
17
|
||
</td>
|
||
<td
|
||
class="day weekend"
|
||
data-day="18"
|
||
>
|
||
18
|
||
</td>
|
||
<td
|
||
class="day weekend"
|
||
data-day="19"
|
||
>
|
||
19
|
||
</td>
|
||
<td
|
||
class="day"
|
||
data-day="20"
|
||
>
|
||
20
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<td
|
||
class="day active"
|
||
data-day="21"
|
||
>
|
||
21
|
||
</td>
|
||
<td
|
||
class="day"
|
||
data-day="22"
|
||
>
|
||
22
|
||
</td>
|
||
<td
|
||
class="day"
|
||
data-day="23"
|
||
>
|
||
23
|
||
</td>
|
||
<td
|
||
class="day"
|
||
data-day="24"
|
||
>
|
||
24
|
||
</td>
|
||
<td
|
||
class="day weekend"
|
||
data-day="25"
|
||
>
|
||
25
|
||
</td>
|
||
<td
|
||
class="day weekend"
|
||
data-day="26"
|
||
>
|
||
26
|
||
</td>
|
||
<td
|
||
class="day"
|
||
data-day="27"
|
||
>
|
||
27
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<td
|
||
class="day"
|
||
data-day="28"
|
||
>
|
||
28
|
||
</td>
|
||
<td
|
||
class="day"
|
||
data-day="29"
|
||
>
|
||
29
|
||
</td>
|
||
<td
|
||
class="day"
|
||
data-day="30"
|
||
>
|
||
30
|
||
</td>
|
||
<td
|
||
class="day"
|
||
data-day="31"
|
||
>
|
||
31
|
||
</td>
|
||
<td
|
||
class="day weekend old"
|
||
data-day="1"
|
||
>
|
||
1
|
||
</td>
|
||
<td
|
||
class="day weekend old"
|
||
data-day="2"
|
||
>
|
||
2
|
||
</td>
|
||
<td
|
||
class="day old"
|
||
data-day="3"
|
||
>
|
||
3
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
`;
|
||
|
||
exports[`DateInput toggles view to decades 1`] = `
|
||
<div
|
||
class="datepicker"
|
||
>
|
||
<div
|
||
class="datepicker-years"
|
||
>
|
||
<table
|
||
class="table-condensed"
|
||
>
|
||
<thead>
|
||
<tr>
|
||
<th
|
||
class="prev"
|
||
>
|
||
<span
|
||
class="glyphicon glyphicon-chevron-left"
|
||
/>
|
||
</th>
|
||
<th
|
||
class="picker-switch"
|
||
colspan="5"
|
||
data-action="pickerSwitch"
|
||
>
|
||
2010-2021
|
||
</th>
|
||
<th
|
||
class="next"
|
||
>
|
||
<span
|
||
class="glyphicon glyphicon-chevron-right"
|
||
/>
|
||
</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td
|
||
colspan="7"
|
||
>
|
||
<span
|
||
class="year "
|
||
>
|
||
2010
|
||
</span>
|
||
<span
|
||
class="year "
|
||
>
|
||
2011
|
||
</span>
|
||
<span
|
||
class="year "
|
||
>
|
||
2012
|
||
</span>
|
||
<span
|
||
class="year "
|
||
>
|
||
2013
|
||
</span>
|
||
<span
|
||
class="year "
|
||
>
|
||
2014
|
||
</span>
|
||
<span
|
||
class="year "
|
||
>
|
||
2015
|
||
</span>
|
||
<span
|
||
class="year "
|
||
>
|
||
2016
|
||
</span>
|
||
<span
|
||
class="year "
|
||
>
|
||
2017
|
||
</span>
|
||
<span
|
||
class="year "
|
||
>
|
||
2018
|
||
</span>
|
||
<span
|
||
class="year active"
|
||
>
|
||
2019
|
||
</span>
|
||
<span
|
||
class="year "
|
||
>
|
||
2020
|
||
</span>
|
||
<span
|
||
class="year "
|
||
>
|
||
2021
|
||
</span>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
`;
|
||
|
||
exports[`DateInput toggles view to years 1`] = `
|
||
<div
|
||
class="datepicker"
|
||
>
|
||
<div
|
||
class="datepicker-months"
|
||
>
|
||
<table
|
||
class="table-condensed"
|
||
>
|
||
<thead>
|
||
<tr>
|
||
<th
|
||
class="prev"
|
||
>
|
||
<span
|
||
class="glyphicon glyphicon-chevron-left"
|
||
/>
|
||
</th>
|
||
<th
|
||
class="picker-switch"
|
||
colspan="5"
|
||
>
|
||
2019
|
||
</th>
|
||
<th
|
||
class="next"
|
||
>
|
||
<span
|
||
class="glyphicon glyphicon-chevron-right"
|
||
/>
|
||
</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td
|
||
colspan="7"
|
||
>
|
||
<span
|
||
class="month active"
|
||
>
|
||
Jan
|
||
</span>
|
||
<span
|
||
class="month"
|
||
>
|
||
Feb
|
||
</span>
|
||
<span
|
||
class="month"
|
||
>
|
||
Mar
|
||
</span>
|
||
<span
|
||
class="month"
|
||
>
|
||
Apr
|
||
</span>
|
||
<span
|
||
class="month"
|
||
>
|
||
May
|
||
</span>
|
||
<span
|
||
class="month"
|
||
>
|
||
Jun
|
||
</span>
|
||
<span
|
||
class="month"
|
||
>
|
||
Jul
|
||
</span>
|
||
<span
|
||
class="month"
|
||
>
|
||
Aug
|
||
</span>
|
||
<span
|
||
class="month"
|
||
>
|
||
Sep
|
||
</span>
|
||
<span
|
||
class="month"
|
||
>
|
||
Oct
|
||
</span>
|
||
<span
|
||
class="month"
|
||
>
|
||
Nov
|
||
</span>
|
||
<span
|
||
class="month"
|
||
>
|
||
Dec
|
||
</span>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
`;
|
webpack/assets/javascripts/react_app/components/common/DateTimePicker/DateComponents/__snapshots__/Day.test.js.snap | ||
---|---|---|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
Also available in: Unified diff
Fixes #26081 - Add DateTimePicker component (#6596)