diff --git a/public/app/features/explore/Explore.tsx b/public/app/features/explore/Explore.tsx index 6ec6c79ac5f..78764ab9876 100644 --- a/public/app/features/explore/Explore.tsx +++ b/public/app/features/explore/Explore.tsx @@ -898,6 +898,7 @@ export class Explore extends React.PureComponent { height={graphHeight} loading={graphLoading} id={`explore-graph-${position}`} + onChangeTime={this.onChangeTime} range={graphRange} split={split} /> @@ -908,7 +909,13 @@ export class Explore extends React.PureComponent { ) : null} {supportsLogs && showingLogs ? ( - + ) : null} )} diff --git a/public/app/features/explore/Graph.tsx b/public/app/features/explore/Graph.tsx index 6c22ca67509..2c1f08b871d 100644 --- a/public/app/features/explore/Graph.tsx +++ b/public/app/features/explore/Graph.tsx @@ -5,6 +5,7 @@ import { withSize } from 'react-sizeme'; import 'vendor/flot/jquery.flot'; import 'vendor/flot/jquery.flot.time'; +import 'vendor/flot/jquery.flot.selection'; import { RawTimeRange } from 'app/types/series'; import * as dateMath from 'app/core/utils/datemath'; @@ -62,10 +63,10 @@ const FLOT_OPTIONS = { margin: { left: 0, right: 0 }, labelMarginX: 0, }, - // selection: { - // mode: 'x', - // color: '#666', - // }, + selection: { + mode: 'x', + color: '#666', + }, // crosshair: { // mode: 'x', // }, @@ -80,6 +81,7 @@ interface GraphProps { split?: boolean; size?: { width: number; height: number }; userOptions?: any; + onChangeTime?: (range: RawTimeRange) => void; } interface GraphState { @@ -87,6 +89,8 @@ interface GraphState { } export class Graph extends PureComponent { + $el: any; + state = { showAllTimeSeries: false, }; @@ -99,6 +103,8 @@ export class Graph extends PureComponent { componentDidMount() { this.draw(); + this.$el = $(`#${this.props.id}`); + this.$el.bind('plotselected', this.onPlotSelected); } componentDidUpdate(prevProps: GraphProps) { @@ -113,6 +119,20 @@ export class Graph extends PureComponent { } } + componentWillUnmount() { + this.$el.unbind('plotselected', this.onPlotSelected); + } + + onPlotSelected = (event, ranges) => { + if (this.props.onChangeTime) { + const range = { + from: moment(ranges.xaxis.from), + to: moment(ranges.xaxis.to), + }; + this.props.onChangeTime(range); + } + }; + onShowAllTimeSeries = () => { this.setState( { diff --git a/public/app/features/explore/Logs.tsx b/public/app/features/explore/Logs.tsx index 5630d1de8f6..ccfa96bed0b 100644 --- a/public/app/features/explore/Logs.tsx +++ b/public/app/features/explore/Logs.tsx @@ -25,6 +25,7 @@ interface LogsProps { loading: boolean; position: string; range?: RawTimeRange; + onChangeTime?: (range: RawTimeRange) => void; } interface LogsState { @@ -88,6 +89,7 @@ export default class Logs extends PureComponent { height="100px" range={range} id={`explore-logs-graph-${position}`} + onChangeTime={this.props.onChangeTime} userOptions={graphOptions} /> diff --git a/public/app/features/explore/TimePicker.tsx b/public/app/features/explore/TimePicker.tsx index 8955fb4aa9b..ed2fd924c78 100644 --- a/public/app/features/explore/TimePicker.tsx +++ b/public/app/features/explore/TimePicker.tsx @@ -16,6 +16,9 @@ export const DEFAULT_RANGE = { * @param value Epoch or relative time */ export function parseTime(value: string, isUtc = false): string { + if (moment.isMoment(value)) { + return value; + } if (value.indexOf('now') !== -1) { return value; } @@ -39,7 +42,8 @@ interface TimePickerState { isOpen: boolean; isUtc: boolean; rangeString: string; - refreshInterval: string; + refreshInterval?: string; + initialRange: RawTimeRange; // Input-controlled text, keep these in a shape that is human-editable fromRaw: string; @@ -49,11 +53,24 @@ interface TimePickerState { export default class TimePicker extends PureComponent { dropdownEl: any; - constructor(props) { - super(props); + state = { + isOpen: false, + isUtc: false, + rangeString: '', + initialRange: DEFAULT_RANGE, + fromRaw: '', + toRaw: '', + refreshInterval: '', + }; + + static getDerivedStateFromProps(props, state) { + if (state.range && state.range === props.range) { + return null; + } const from = props.range ? props.range.from : DEFAULT_RANGE.from; const to = props.range ? props.range.to : DEFAULT_RANGE.to; + const initialRange = props.range || DEFAULT_RANGE; // Ensure internal format const fromRaw = parseTime(from, props.isUtc); @@ -63,13 +80,12 @@ export default class TimePicker extends PureComponent