QueryOptions.tsx 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. // Libraries
  2. import React, { PureComponent, ChangeEvent, FocusEvent, ReactText } from 'react';
  3. // Utils
  4. import { rangeUtil } from '@grafana/data';
  5. // Components
  6. import {
  7. DataSourceSelectItem,
  8. EventsWithValidation,
  9. Input,
  10. InputStatus,
  11. Switch,
  12. ValidationEvents,
  13. FormLabel,
  14. } from '@grafana/ui';
  15. import { DataSourceOption } from './DataSourceOption';
  16. // Types
  17. import { PanelModel } from '../state';
  18. const timeRangeValidationEvents: ValidationEvents = {
  19. [EventsWithValidation.onBlur]: [
  20. {
  21. rule: value => {
  22. if (!value) {
  23. return true;
  24. }
  25. return rangeUtil.isValidTimeSpan(value);
  26. },
  27. errorMessage: 'Not a valid timespan',
  28. },
  29. ],
  30. };
  31. const emptyToNull = (value: string) => {
  32. return value === '' ? null : value;
  33. };
  34. interface Props {
  35. panel: PanelModel;
  36. datasource: DataSourceSelectItem;
  37. }
  38. interface State {
  39. relativeTime: string;
  40. timeShift: string;
  41. cacheTimeout: string;
  42. maxDataPoints: string | ReactText;
  43. interval: string;
  44. hideTimeOverride: boolean;
  45. }
  46. export class QueryOptions extends PureComponent<Props, State> {
  47. allOptions: any = {
  48. cacheTimeout: {
  49. label: 'Cache timeout',
  50. placeholder: '60',
  51. name: 'cacheTimeout',
  52. tooltipInfo: (
  53. <>
  54. If your time series store has a query cache this option can override the default cache timeout. Specify a
  55. numeric value in seconds.
  56. </>
  57. ),
  58. },
  59. maxDataPoints: {
  60. label: 'Max data points',
  61. placeholder: 'auto',
  62. name: 'maxDataPoints',
  63. tooltipInfo: (
  64. <>
  65. The maximum data points the query should return. For graphs this is automatically set to one data point per
  66. pixel. For some data sources this can also be capped in the datasource settings page. With streaming data,
  67. this value is used for the rolling buffer.
  68. </>
  69. ),
  70. },
  71. minInterval: {
  72. label: 'Min time interval',
  73. placeholder: '0',
  74. name: 'minInterval',
  75. panelKey: 'interval',
  76. tooltipInfo: (
  77. <>
  78. A lower limit for the auto group by time interval. Recommended to be set to write frequency, for example{' '}
  79. <code>1m</code> if your data is written every minute. Access auto interval via variable{' '}
  80. <code>$__interval</code> for time range string and <code>$__interval_ms</code> for numeric variable that can
  81. be used in math expressions.
  82. </>
  83. ),
  84. },
  85. };
  86. constructor(props: Props) {
  87. super(props);
  88. this.state = {
  89. relativeTime: props.panel.timeFrom || '',
  90. timeShift: props.panel.timeShift || '',
  91. cacheTimeout: props.panel.cacheTimeout || '',
  92. maxDataPoints: props.panel.maxDataPoints || '',
  93. interval: props.panel.interval || '',
  94. hideTimeOverride: props.panel.hideTimeOverride || false,
  95. };
  96. }
  97. onRelativeTimeChange = (event: ChangeEvent<HTMLInputElement>) => {
  98. this.setState({
  99. relativeTime: event.target.value,
  100. });
  101. };
  102. onTimeShiftChange = (event: ChangeEvent<HTMLInputElement>) => {
  103. this.setState({
  104. timeShift: event.target.value,
  105. });
  106. };
  107. onOverrideTime = (event: FocusEvent<HTMLInputElement>, status: InputStatus) => {
  108. const { value } = event.target;
  109. const { panel } = this.props;
  110. const emptyToNullValue = emptyToNull(value);
  111. if (status === InputStatus.Valid && panel.timeFrom !== emptyToNullValue) {
  112. panel.timeFrom = emptyToNullValue;
  113. panel.refresh();
  114. }
  115. };
  116. onTimeShift = (event: FocusEvent<HTMLInputElement>, status: InputStatus) => {
  117. const { value } = event.target;
  118. const { panel } = this.props;
  119. const emptyToNullValue = emptyToNull(value);
  120. if (status === InputStatus.Valid && panel.timeShift !== emptyToNullValue) {
  121. panel.timeShift = emptyToNullValue;
  122. panel.refresh();
  123. }
  124. };
  125. onToggleTimeOverride = () => {
  126. const { panel } = this.props;
  127. this.setState({ hideTimeOverride: !this.state.hideTimeOverride }, () => {
  128. panel.hideTimeOverride = this.state.hideTimeOverride;
  129. panel.refresh();
  130. });
  131. };
  132. onDataSourceOptionBlur = (panelKey: string) => () => {
  133. const { panel } = this.props;
  134. // @ts-ignore
  135. panel[panelKey] = this.state[panelKey];
  136. panel.refresh();
  137. };
  138. onDataSourceOptionChange = (panelKey: string) => (event: ChangeEvent<HTMLInputElement>) => {
  139. this.setState({ ...this.state, [panelKey]: event.target.value });
  140. };
  141. /**
  142. * Show options for any value that is set, or values that the
  143. * current datasource says it will use
  144. */
  145. renderOptions = () => {
  146. const { datasource } = this.props;
  147. const queryOptions: any = datasource.meta.queryOptions || {};
  148. return Object.keys(this.allOptions).map(key => {
  149. const options = this.allOptions[key];
  150. const panelKey = options.panelKey || key;
  151. // @ts-ignore
  152. const value = this.state[panelKey];
  153. if (value || queryOptions[key]) {
  154. return (
  155. <DataSourceOption
  156. key={key}
  157. {...options}
  158. onChange={this.onDataSourceOptionChange(panelKey)}
  159. onBlur={this.onDataSourceOptionBlur(panelKey)}
  160. value={value}
  161. />
  162. );
  163. }
  164. return null; // nothing to render
  165. });
  166. };
  167. render() {
  168. const { hideTimeOverride } = this.state;
  169. const { relativeTime, timeShift } = this.state;
  170. return (
  171. <div className="gf-form-inline">
  172. {this.renderOptions()}
  173. <div className="gf-form">
  174. <FormLabel>Relative time</FormLabel>
  175. <Input
  176. type="text"
  177. className="width-6"
  178. placeholder="1h"
  179. onChange={this.onRelativeTimeChange}
  180. onBlur={this.onOverrideTime}
  181. validationEvents={timeRangeValidationEvents}
  182. hideErrorMessage={true}
  183. value={relativeTime}
  184. />
  185. </div>
  186. <div className="gf-form">
  187. <span className="gf-form-label">Time shift</span>
  188. <Input
  189. type="text"
  190. className="width-6"
  191. placeholder="1h"
  192. onChange={this.onTimeShiftChange}
  193. onBlur={this.onTimeShift}
  194. validationEvents={timeRangeValidationEvents}
  195. hideErrorMessage={true}
  196. value={timeShift}
  197. />
  198. </div>
  199. {(timeShift || relativeTime) && (
  200. <div className="gf-form-inline">
  201. <Switch label="Hide time info" checked={hideTimeOverride} onChange={this.onToggleTimeOverride} />
  202. </div>
  203. )}
  204. </div>
  205. );
  206. }
  207. }