diff --git a/public/app/features/explore/Explore.tsx b/public/app/features/explore/Explore.tsx index 50d894de43f..22c48a65080 100644 --- a/public/app/features/explore/Explore.tsx +++ b/public/app/features/explore/Explore.tsx @@ -604,16 +604,18 @@ export class Explore extends React.Component {
- {supportsGraph && showingGraph ? ( - - ) : null} + {supportsGraph && + showingGraph && + graphResult && ( + + )} {supportsTable && showingTable ? ( ) : null} diff --git a/public/app/features/explore/Graph.test.tsx b/public/app/features/explore/Graph.test.tsx new file mode 100644 index 00000000000..eb9cb8a7f0d --- /dev/null +++ b/public/app/features/explore/Graph.test.tsx @@ -0,0 +1,60 @@ +import React from 'react'; +import { shallow } from 'enzyme'; +import Graph from './Graph'; +import { mockData } from './__mocks__/mockData'; + +const setup = (propOverrides?: object) => { + const props = Object.assign( + { + data: mockData().slice(0, 19), + options: { + interval: '20s', + range: { from: 'now-6h', to: 'now' }, + targets: [ + { + format: 'time_series', + instant: false, + hinting: true, + expr: 'prometheus_http_request_duration_seconds_bucket', + }, + ], + }, + }, + propOverrides + ); + + // Enzyme.shallow did not work well with jquery.flop. Mocking the draw function. + Graph.prototype.draw = jest.fn(); + + const wrapper = shallow(); + const instance = wrapper.instance() as Graph; + + return { + wrapper, + instance, + }; +}; + +describe('Render', () => { + it('should render component', () => { + const { wrapper } = setup(); + + expect(wrapper).toMatchSnapshot(); + }); + + it('should render component with disclaimer', () => { + const { wrapper } = setup({ + data: mockData(), + }); + + expect(wrapper).toMatchSnapshot(); + }); + + it('should show query return no time series', () => { + const { wrapper } = setup({ + data: [], + }); + + expect(wrapper).toMatchSnapshot(); + }); +}); diff --git a/public/app/features/explore/Graph.tsx b/public/app/features/explore/Graph.tsx index 9243f612466..8a9390a0b6b 100644 --- a/public/app/features/explore/Graph.tsx +++ b/public/app/features/explore/Graph.tsx @@ -9,6 +9,8 @@ import TimeSeries from 'app/core/time_series2'; import Legend from './Legend'; +const MAX_NUMBER_OF_TIME_SERIES = 20; + // Copied from graph.ts function time_format(ticks, min, max) { if (min && max && ticks) { @@ -67,6 +69,16 @@ const FLOT_OPTIONS = { }; class Graph extends Component { + state = { + showAllTimeSeries: false, + }; + + getGraphData() { + const { data } = this.props; + + return this.state.showAllTimeSeries ? data : data.slice(0, MAX_NUMBER_OF_TIME_SERIES); + } + componentDidMount() { this.draw(); } @@ -82,8 +94,19 @@ class Graph extends Component { } } + onShowAllTimeSeries = () => { + this.setState( + { + showAllTimeSeries: true, + }, + this.draw + ); + }; + draw() { - const { data, options: userOptions } = this.props; + const { options: userOptions } = this.props; + const data = this.getGraphData(); + const $el = $(`#${this.props.id}`); if (!data) { $el.empty(); @@ -124,8 +147,10 @@ class Graph extends Component { } render() { - const { data, height, loading } = this.props; - if (!loading && data && data.length === 0) { + const { height, loading } = this.props; + const data = this.getGraphData(); + + if (!loading && data.length === 0) { return (
The queries returned no time series to graph.
@@ -133,9 +158,21 @@ class Graph extends Component { ); } return ( -
-
- +
+ {this.props.data.length > MAX_NUMBER_OF_TIME_SERIES && + !this.state.showAllTimeSeries && ( +
+ + {`Showing only ${MAX_NUMBER_OF_TIME_SERIES} time series. `} + {`Show all ${ + this.props.data.length + }`} +
+ )} +
+
+ +
); } diff --git a/public/app/features/explore/__mocks__/mockData.ts b/public/app/features/explore/__mocks__/mockData.ts new file mode 100644 index 00000000000..2e89ded29cf --- /dev/null +++ b/public/app/features/explore/__mocks__/mockData.ts @@ -0,0 +1,274 @@ +export const mockData = () => { + return [ + { + metric: { + __name__: 'prometheus_http_request_duration_seconds_bucket', + handler: '/label/:name/values', + instance: 'localhost:9090', + job: 'prometheus', + le: '+Inf', + }, + values: [[1537858100, '16'], [1537861960, '1'], [1537861980, '1']], + }, + { + metric: { + __name__: 'prometheus_http_request_duration_seconds_bucket', + handler: '/label/:name/values', + instance: 'localhost:9090', + job: 'prometheus', + le: '0.1', + }, + values: [[1537858100, '16'], [1537861960, '1'], [1537861980, '1']], + }, + { + metric: { + __name__: 'prometheus_http_request_duration_seconds_bucket', + handler: '/label/:name/values', + instance: 'localhost:9090', + job: 'prometheus', + le: '0.2', + }, + values: [[1537858100, '16'], [1537861960, '1'], [1537861980, '1']], + }, + { + metric: { + __name__: 'prometheus_http_request_duration_seconds_bucket', + handler: '/label/:name/values', + instance: 'localhost:9090', + job: 'prometheus', + le: '0.4', + }, + values: [[1537858100, '16'], [1537861960, '1'], [1537861980, '1']], + }, + { + metric: { + __name__: 'prometheus_http_request_duration_seconds_bucket', + handler: '/label/:name/values', + instance: 'localhost:9090', + job: 'prometheus', + le: '1', + }, + values: [[1537858100, '16'], [1537861960, '1'], [1537861980, '1']], + }, + { + metric: { + __name__: 'prometheus_http_request_duration_seconds_bucket', + handler: '/label/:name/values', + instance: 'localhost:9090', + job: 'prometheus', + le: '120', + }, + values: [[1537858100, '16'], [1537861960, '1'], [1537861980, '1']], + }, + { + metric: { + __name__: 'prometheus_http_request_duration_seconds_bucket', + handler: '/label/:name/values', + instance: 'localhost:9090', + job: 'prometheus', + le: '20', + }, + values: [[1537858100, '16'], [1537861960, '1'], [1537861980, '1']], + }, + { + metric: { + __name__: 'prometheus_http_request_duration_seconds_bucket', + handler: '/label/:name/values', + instance: 'localhost:9090', + job: 'prometheus', + le: '3', + }, + values: [[1537858100, '16'], [1537861960, '1'], [1537861980, '1']], + }, + { + metric: { + __name__: 'prometheus_http_request_duration_seconds_bucket', + handler: '/label/:name/values', + instance: 'localhost:9090', + job: 'prometheus', + le: '60', + }, + values: [[1537858100, '16'], [1537861960, '1'], [1537861980, '1']], + }, + { + metric: { + __name__: 'prometheus_http_request_duration_seconds_bucket', + handler: '/label/:name/values', + instance: 'localhost:9090', + job: 'prometheus', + le: '8', + }, + values: [[1537858100, '16'], [1537861960, '1'], [1537861980, '1']], + }, + { + metric: { + __name__: 'prometheus_http_request_duration_seconds_bucket', + handler: '/metrics', + instance: 'localhost:9090', + job: 'prometheus', + le: '+Inf', + }, + values: [[1537858060, '1195'], [1537858080, '1195'], [1537858100, '1195']], + }, + { + metric: { + __name__: 'prometheus_http_request_duration_seconds_bucket', + handler: '/metrics', + instance: 'localhost:9090', + job: 'prometheus', + le: '0.1', + }, + values: [[1537858060, '1195'], [1537858080, '1195'], [1537858100, '1195']], + }, + { + metric: { + __name__: 'prometheus_http_request_duration_seconds_bucket', + handler: '/metrics', + instance: 'localhost:9090', + job: 'prometheus', + le: '0.4', + }, + values: [[1537858060, '1195'], [1537858080, '1195'], [1537858100, '1195']], + }, + { + metric: { + __name__: 'prometheus_http_request_duration_seconds_bucket', + handler: '/metrics', + instance: 'localhost:9090', + job: 'prometheus', + le: '1', + }, + values: [[1537847900, '953'], [1537858080, '1195'], [1537858100, '1195']], + }, + { + metric: { + __name__: 'prometheus_http_request_duration_seconds_bucket', + handler: '/metrics', + instance: 'localhost:9090', + job: 'prometheus', + le: '120', + }, + values: [[1537858060, '1195'], [1537858080, '1195'], [1537858100, '1195']], + }, + { + metric: { + __name__: 'prometheus_http_request_duration_seconds_bucket', + handler: '/metrics', + instance: 'localhost:9090', + job: 'prometheus', + le: '20', + }, + values: [[1537858060, '1195'], [1537858080, '1195'], [1537858100, '1195']], + }, + { + metric: { + __name__: 'prometheus_http_request_duration_seconds_bucket', + handler: '/metrics', + instance: 'localhost:9090', + job: 'prometheus', + le: '3', + }, + values: [[1537858060, '1195'], [1537858080, '1195'], [1537858100, '1195']], + }, + { + metric: { + __name__: 'prometheus_http_request_duration_seconds_bucket', + handler: '/metrics', + instance: 'localhost:9090', + job: 'prometheus', + le: '60', + }, + values: [[1537858060, '1195'], [1537858080, '1195'], [1537858100, '1195']], + }, + { + metric: { + __name__: 'prometheus_http_request_duration_seconds_bucket', + handler: '/metrics', + instance: 'localhost:9090', + job: 'prometheus', + le: '8', + }, + values: [[1537858060, '1195'], [1537858080, '1195'], [1537858100, '1195']], + }, + { + metric: { + __name__: 'prometheus_http_request_duration_seconds_bucket', + handler: '/query', + instance: 'localhost:9090', + job: 'prometheus', + le: '+Inf', + }, + values: [[1537858100, '55'], [1537861960, '1'], [1537861980, '1']], + }, + { + metric: { + __name__: 'prometheus_http_request_duration_seconds_bucket', + handler: '/query', + instance: 'localhost:9090', + job: 'prometheus', + le: '0.1', + }, + values: [[1537858100, '55'], [1537861960, '1'], [1537861980, '1']], + }, + { + metric: { + __name__: 'prometheus_http_request_duration_seconds_bucket', + handler: '/query', + instance: 'localhost:9090', + job: 'prometheus', + le: '0.2', + }, + values: [[1537858100, '55'], [1537861960, '1'], [1537861980, '1']], + }, + { + metric: { + __name__: 'prometheus_http_request_duration_seconds_bucket', + handler: '/query', + instance: 'localhost:9090', + job: 'prometheus', + le: '0.4', + }, + values: [[1537858100, '55'], [1537861960, '1'], [1537861980, '1']], + }, + { + metric: { + __name__: 'prometheus_http_request_duration_seconds_bucket', + handler: '/query', + instance: 'localhost:9090', + job: 'prometheus', + le: '1', + }, + values: [[1537858100, '55'], [1537861960, '1'], [1537861980, '1']], + }, + { + metric: { + __name__: 'prometheus_http_request_duration_seconds_bucket', + handler: '/query', + instance: 'localhost:9090', + job: 'prometheus', + le: '120', + }, + values: [[1537858100, '55'], [1537861960, '1'], [1537861980, '1']], + }, + { + metric: { + __name__: 'prometheus_http_request_duration_seconds_bucket', + handler: '/query', + instance: 'localhost:9090', + job: 'prometheus', + le: '20', + }, + values: [[1537858100, '55'], [1537861960, '1'], [1537861980, '1']], + }, + { + metric: { + __name__: 'prometheus_http_request_duration_seconds_bucket', + handler: '/query', + instance: 'localhost:9090', + job: 'prometheus', + le: '3', + }, + values: [[1537857260, '55'], [1537861960, '1'], [1537861980, '1']], + }, + ]; +}; diff --git a/public/app/features/explore/__snapshots__/Graph.test.tsx.snap b/public/app/features/explore/__snapshots__/Graph.test.tsx.snap new file mode 100644 index 00000000000..a1e80defe92 --- /dev/null +++ b/public/app/features/explore/__snapshots__/Graph.test.tsx.snap @@ -0,0 +1,970 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Render should render component 1`] = ` +
+
+
+ +
+
+`; + +exports[`Render should render component with disclaimer 1`] = ` +
+
+ + Showing only 20 time series. + + Show all 27 + +
+
+
+ +
+
+`; + +exports[`Render should show query return no time series 1`] = ` +
+
+ The queries returned no time series to graph. +
+
+`; diff --git a/public/sass/pages/_explore.scss b/public/sass/pages/_explore.scss index d427ee203b8..f8bd1108fb1 100644 --- a/public/sass/pages/_explore.scss +++ b/public/sass/pages/_explore.scss @@ -55,6 +55,25 @@ margin-top: 2 * $panel-margin; } + .time-series-disclaimer { + width: 300px; + margin: $panel-margin auto; + padding: 10px 0; + border-radius: $border-radius; + text-align: center; + background-color: $panel-bg; + + .disclaimer-icon { + color: $yellow; + margin-right: $panel-margin/2; + } + + .show-all-time-series { + cursor: pointer; + color: $external-link-color; + } + } + .elapsed-time { position: absolute; left: 0;