Graph.tsx 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. import $ from 'jquery';
  2. import React, { Component } from 'react';
  3. import moment from 'moment';
  4. import 'vendor/flot/jquery.flot';
  5. import 'vendor/flot/jquery.flot.time';
  6. import * as dateMath from 'app/core/utils/datemath';
  7. import TimeSeries from 'app/core/time_series2';
  8. import Legend from './Legend';
  9. const MAX_NUMBER_OF_TIME_SERIES = 20;
  10. // Copied from graph.ts
  11. function time_format(ticks, min, max) {
  12. if (min && max && ticks) {
  13. const range = max - min;
  14. const secPerTick = range / ticks / 1000;
  15. const oneDay = 86400000;
  16. const oneYear = 31536000000;
  17. if (secPerTick <= 45) {
  18. return '%H:%M:%S';
  19. }
  20. if (secPerTick <= 7200 || range <= oneDay) {
  21. return '%H:%M';
  22. }
  23. if (secPerTick <= 80000) {
  24. return '%m/%d %H:%M';
  25. }
  26. if (secPerTick <= 2419200 || range <= oneYear) {
  27. return '%m/%d';
  28. }
  29. return '%Y-%m';
  30. }
  31. return '%H:%M';
  32. }
  33. const FLOT_OPTIONS = {
  34. legend: {
  35. show: false,
  36. },
  37. series: {
  38. lines: {
  39. linewidth: 1,
  40. zero: false,
  41. },
  42. shadowSize: 0,
  43. },
  44. grid: {
  45. minBorderMargin: 0,
  46. markings: [],
  47. backgroundColor: null,
  48. borderWidth: 0,
  49. // hoverable: true,
  50. clickable: true,
  51. color: '#a1a1a1',
  52. margin: { left: 0, right: 0 },
  53. labelMarginX: 0,
  54. },
  55. // selection: {
  56. // mode: 'x',
  57. // color: '#666',
  58. // },
  59. // crosshair: {
  60. // mode: 'x',
  61. // },
  62. };
  63. class Graph extends Component<any, any> {
  64. state = {
  65. showAllTimeSeries: false,
  66. };
  67. getGraphData() {
  68. const { data } = this.props;
  69. return this.state.showAllTimeSeries ? data : data.slice(0, MAX_NUMBER_OF_TIME_SERIES);
  70. }
  71. componentDidMount() {
  72. this.draw();
  73. }
  74. componentDidUpdate(prevProps) {
  75. if (
  76. prevProps.data !== this.props.data ||
  77. prevProps.options !== this.props.options ||
  78. prevProps.split !== this.props.split ||
  79. prevProps.height !== this.props.height
  80. ) {
  81. this.draw();
  82. }
  83. }
  84. onShowAllTimeSeries = () => {
  85. this.setState(
  86. {
  87. showAllTimeSeries: true,
  88. },
  89. this.draw
  90. );
  91. };
  92. draw() {
  93. const { options: userOptions } = this.props;
  94. const data = this.getGraphData();
  95. const $el = $(`#${this.props.id}`);
  96. if (!data) {
  97. $el.empty();
  98. return;
  99. }
  100. const series = data.map((ts: TimeSeries) => ({
  101. color: ts.color,
  102. label: ts.label,
  103. data: ts.getFlotPairs('null'),
  104. }));
  105. const ticks = $el.width() / 100;
  106. let { from, to } = userOptions.range;
  107. if (!moment.isMoment(from)) {
  108. from = dateMath.parse(from, false);
  109. }
  110. if (!moment.isMoment(to)) {
  111. to = dateMath.parse(to, true);
  112. }
  113. const min = from.valueOf();
  114. const max = to.valueOf();
  115. const dynamicOptions = {
  116. xaxis: {
  117. mode: 'time',
  118. min: min,
  119. max: max,
  120. label: 'Datetime',
  121. ticks: ticks,
  122. timeformat: time_format(ticks, min, max),
  123. },
  124. };
  125. const options = {
  126. ...FLOT_OPTIONS,
  127. ...dynamicOptions,
  128. ...userOptions,
  129. };
  130. $.plot($el, series, options);
  131. }
  132. render() {
  133. const { height, loading } = this.props;
  134. const data = this.getGraphData();
  135. if (!loading && data.length === 0) {
  136. return (
  137. <div className="panel-container">
  138. <div className="muted m-a-1">The queries returned no time series to graph.</div>
  139. </div>
  140. );
  141. }
  142. return (
  143. <div>
  144. {this.props.data.length > MAX_NUMBER_OF_TIME_SERIES &&
  145. !this.state.showAllTimeSeries && (
  146. <div className="time-series-disclaimer">
  147. <i className="fa fa-fw fa-warning disclaimer-icon" />
  148. {`Showing only ${MAX_NUMBER_OF_TIME_SERIES} time series. `}
  149. <span className="show-all-time-series" onClick={this.onShowAllTimeSeries}>{`Show all ${
  150. this.props.data.length
  151. }`}</span>
  152. </div>
  153. )}
  154. <div className="panel-container">
  155. <div id={this.props.id} className="explore-graph" style={{ height }} />
  156. <Legend data={data} />
  157. </div>
  158. </div>
  159. );
  160. }
  161. }
  162. export default Graph;