Commit 23c9da61 by David Kaltschmidt

Fixed custom dates for react timepicker

* added jest tests for timepicker component
parent eadaff61
import React from 'react';
import { shallow } from 'enzyme';
import sinon from 'sinon';
import * as rangeUtil from 'app/core/utils/rangeutil';
import TimePicker, { DEFAULT_RANGE, parseTime } from './TimePicker';
describe('<TimePicker />', () => {
it('renders closed with default values', () => {
const rangeString = rangeUtil.describeTimeRange(DEFAULT_RANGE);
const wrapper = shallow(<TimePicker />);
expect(wrapper.find('.timepicker-rangestring').text()).toBe(rangeString);
expect(wrapper.find('.gf-timepicker-dropdown').exists()).toBe(false);
});
it('renders with relative range', () => {
const range = {
from: 'now-7h',
to: 'now',
};
const rangeString = rangeUtil.describeTimeRange(range);
const wrapper = shallow(<TimePicker range={range} isOpen />);
expect(wrapper.find('.timepicker-rangestring').text()).toBe(rangeString);
expect(wrapper.state('fromRaw')).toBe(range.from);
expect(wrapper.state('toRaw')).toBe(range.to);
expect(wrapper.find('.timepicker-from').props().value).toBe(range.from);
expect(wrapper.find('.timepicker-to').props().value).toBe(range.to);
});
it('renders with epoch (millies) range converted to ISO-ish', () => {
const range = {
from: '1',
to: '1000',
};
const rangeString = rangeUtil.describeTimeRange({
from: parseTime(range.from),
to: parseTime(range.to),
});
const wrapper = shallow(<TimePicker range={range} isUtc isOpen />);
expect(wrapper.state('fromRaw')).toBe('1970-01-01 00:00:00');
expect(wrapper.state('toRaw')).toBe('1970-01-01 00:00:01');
expect(wrapper.find('.timepicker-rangestring').text()).toBe(rangeString);
expect(wrapper.find('.timepicker-from').props().value).toBe('1970-01-01 00:00:00');
expect(wrapper.find('.timepicker-to').props().value).toBe('1970-01-01 00:00:01');
});
it('moves ranges forward and backward by half the range on arrow click', () => {
const range = {
from: '2000',
to: '4000',
};
const rangeString = rangeUtil.describeTimeRange({
from: parseTime(range.from),
to: parseTime(range.to),
});
const onChangeTime = sinon.spy();
const wrapper = shallow(<TimePicker range={range} isUtc isOpen onChangeTime={onChangeTime} />);
expect(wrapper.state('fromRaw')).toBe('1970-01-01 00:00:02');
expect(wrapper.state('toRaw')).toBe('1970-01-01 00:00:04');
expect(wrapper.find('.timepicker-rangestring').text()).toBe(rangeString);
expect(wrapper.find('.timepicker-from').props().value).toBe('1970-01-01 00:00:02');
expect(wrapper.find('.timepicker-to').props().value).toBe('1970-01-01 00:00:04');
wrapper.find('.timepicker-left').simulate('click');
expect(onChangeTime.calledOnce).toBe(true);
expect(wrapper.state('fromRaw')).toBe('1970-01-01 00:00:01');
expect(wrapper.state('toRaw')).toBe('1970-01-01 00:00:03');
wrapper.find('.timepicker-right').simulate('click');
expect(wrapper.state('fromRaw')).toBe('1970-01-01 00:00:02');
expect(wrapper.state('toRaw')).toBe('1970-01-01 00:00:04');
});
});
...@@ -4,22 +4,43 @@ import moment from 'moment'; ...@@ -4,22 +4,43 @@ import moment from 'moment';
import * as dateMath from 'app/core/utils/datemath'; import * as dateMath from 'app/core/utils/datemath';
import * as rangeUtil from 'app/core/utils/rangeutil'; import * as rangeUtil from 'app/core/utils/rangeutil';
const DATE_FORMAT = 'YYYY-MM-DD HH:mm:ss';
export const DEFAULT_RANGE = { export const DEFAULT_RANGE = {
from: 'now-6h', from: 'now-6h',
to: 'now', to: 'now',
}; };
export function parseTime(value, isUtc = false, asString = false) {
if (value.indexOf('now') !== -1) {
return value;
}
if (!isNaN(value)) {
const epoch = parseInt(value);
const m = isUtc ? moment.utc(epoch) : moment(epoch);
return asString ? m.format(DATE_FORMAT) : m;
}
return undefined;
}
export default class TimePicker extends PureComponent<any, any> { export default class TimePicker extends PureComponent<any, any> {
dropdownEl: any; dropdownEl: any;
constructor(props) { constructor(props) {
super(props); super(props);
const fromRaw = props.range ? props.range.from : DEFAULT_RANGE.from;
const toRaw = props.range ? props.range.to : DEFAULT_RANGE.to;
const range = {
from: parseTime(fromRaw),
to: parseTime(toRaw),
};
this.state = { this.state = {
fromRaw: props.range ? props.range.from : DEFAULT_RANGE.from, fromRaw: parseTime(fromRaw, props.isUtc, true),
isOpen: false, isOpen: props.isOpen,
isUtc: false, isUtc: props.isUtc,
rangeString: rangeUtil.describeTimeRange(props.range || DEFAULT_RANGE), rangeString: rangeUtil.describeTimeRange(range),
refreshInterval: '', refreshInterval: '',
toRaw: props.range ? props.range.to : DEFAULT_RANGE.to, toRaw: parseTime(toRaw, props.isUtc, true),
}; };
} }
...@@ -49,14 +70,15 @@ export default class TimePicker extends PureComponent<any, any> { ...@@ -49,14 +70,15 @@ export default class TimePicker extends PureComponent<any, any> {
} }
const rangeString = rangeUtil.describeTimeRange(range); const rangeString = rangeUtil.describeTimeRange(range);
to = moment.utc(to); // No need to convert to UTC again
from = moment.utc(from); to = moment(to);
from = moment(from);
this.setState( this.setState(
{ {
rangeString, rangeString,
fromRaw: from, fromRaw: from.format(DATE_FORMAT),
toRaw: to, toRaw: to.format(DATE_FORMAT),
}, },
() => { () => {
onChangeTime({ to, from }); onChangeTime({ to, from });
...@@ -76,6 +98,27 @@ export default class TimePicker extends PureComponent<any, any> { ...@@ -76,6 +98,27 @@ export default class TimePicker extends PureComponent<any, any> {
}); });
}; };
handleClickApply = () => {
const { onChangeTime } = this.props;
const { toRaw, fromRaw } = this.state;
const range = {
from: dateMath.parse(fromRaw, false),
to: dateMath.parse(toRaw, true),
};
const rangeString = rangeUtil.describeTimeRange(range);
this.setState(
{
isOpen: false,
rangeString,
},
() => {
if (onChangeTime) {
onChangeTime(range);
}
}
);
};
handleClickLeft = () => this.move(-1); handleClickLeft = () => this.move(-1);
handleClickPicker = () => { handleClickPicker = () => {
this.setState(state => ({ this.setState(state => ({
...@@ -118,7 +161,7 @@ export default class TimePicker extends PureComponent<any, any> { ...@@ -118,7 +161,7 @@ export default class TimePicker extends PureComponent<any, any> {
const timeOptions = this.getTimeOptions(); const timeOptions = this.getTimeOptions();
return ( return (
<div ref={this.dropdownRef} className="gf-timepicker-dropdown"> <div ref={this.dropdownRef} className="gf-timepicker-dropdown">
<form name="timeForm" className="gf-timepicker-absolute-section"> <div className="gf-timepicker-absolute-section">
<h3 className="section-heading">Custom range</h3> <h3 className="section-heading">Custom range</h3>
<label className="small">From:</label> <label className="small">From:</label>
...@@ -126,7 +169,7 @@ export default class TimePicker extends PureComponent<any, any> { ...@@ -126,7 +169,7 @@ export default class TimePicker extends PureComponent<any, any> {
<div className="gf-form max-width-28"> <div className="gf-form max-width-28">
<input <input
type="text" type="text"
className="gf-form-input input-large" className="gf-form-input input-large timepicker-from"
value={fromRaw} value={fromRaw}
onChange={this.handleChangeFrom} onChange={this.handleChangeFrom}
/> />
...@@ -136,7 +179,12 @@ export default class TimePicker extends PureComponent<any, any> { ...@@ -136,7 +179,12 @@ export default class TimePicker extends PureComponent<any, any> {
<label className="small">To:</label> <label className="small">To:</label>
<div className="gf-form-inline"> <div className="gf-form-inline">
<div className="gf-form max-width-28"> <div className="gf-form max-width-28">
<input type="text" className="gf-form-input input-large" value={toRaw} onChange={this.handleChangeTo} /> <input
type="text"
className="gf-form-input input-large timepicker-to"
value={toRaw}
onChange={this.handleChangeTo}
/>
</div> </div>
</div> </div>
...@@ -146,7 +194,12 @@ export default class TimePicker extends PureComponent<any, any> { ...@@ -146,7 +194,12 @@ export default class TimePicker extends PureComponent<any, any> {
<select className="gf-form-input input-medium" ng-options="f.value as f.text for f in ctrl.refresh.options"></select> <select className="gf-form-input input-medium" ng-options="f.value as f.text for f in ctrl.refresh.options"></select>
</div> </div>
</div> */} </div> */}
</form> <div className="gf-form">
<button className="btn gf-form-btn btn-secondary" onClick={this.handleClickApply}>
Apply
</button>
</div>
</div>
<div className="gf-timepicker-relative-section"> <div className="gf-timepicker-relative-section">
<h3 className="section-heading">Quick ranges</h3> <h3 className="section-heading">Quick ranges</h3>
...@@ -172,16 +225,16 @@ export default class TimePicker extends PureComponent<any, any> { ...@@ -172,16 +225,16 @@ export default class TimePicker extends PureComponent<any, any> {
return ( return (
<div className="timepicker"> <div className="timepicker">
<div className="navbar-buttons"> <div className="navbar-buttons">
<button className="btn navbar-button navbar-button--tight" onClick={this.handleClickLeft}> <button className="btn navbar-button navbar-button--tight timepicker-left" onClick={this.handleClickLeft}>
<i className="fa fa-chevron-left" /> <i className="fa fa-chevron-left" />
</button> </button>
<button className="btn navbar-button gf-timepicker-nav-btn" onClick={this.handleClickPicker}> <button className="btn navbar-button gf-timepicker-nav-btn" onClick={this.handleClickPicker}>
<i className="fa fa-clock-o" /> <i className="fa fa-clock-o" />
<span> {rangeString}</span> <span className="timepicker-rangestring">{rangeString}</span>
{isUtc ? <span className="gf-timepicker-utc">UTC</span> : null} {isUtc ? <span className="gf-timepicker-utc">UTC</span> : null}
{refreshInterval ? <span className="text-warning">&nbsp; Refresh every {refreshInterval}</span> : null} {refreshInterval ? <span className="text-warning">&nbsp; Refresh every {refreshInterval}</span> : null}
</button> </button>
<button className="btn navbar-button navbar-button--tight" onClick={this.handleClickRight}> <button className="btn navbar-button navbar-button--tight timepicker-right" onClick={this.handleClickRight}>
<i className="fa fa-chevron-right" /> <i className="fa fa-chevron-right" />
</button> </button>
</div> </div>
......
...@@ -36,6 +36,10 @@ ...@@ -36,6 +36,10 @@
.timepicker { .timepicker {
display: flex; display: flex;
&-rangestring {
margin-left: 0.5em;
}
} }
.run-icon { .run-icon {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment