QueryOptions.tsx 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. // Libraries
  2. import React, { PureComponent, ChangeEvent, FocusEvent } 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;
  43. interval: string;
  44. hideTimeOverride: boolean;
  45. }
  46. export class QueryOptions extends PureComponent<Props, State> {
  47. allOptions = {
  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.
  67. </>
  68. ),
  69. },
  70. minInterval: {
  71. label: 'Min time interval',
  72. placeholder: '0',
  73. name: 'minInterval',
  74. panelKey: 'interval',
  75. tooltipInfo: (
  76. <>
  77. A lower limit for the auto group by time interval. Recommended to be set to write frequency, for example{' '}
  78. <code>1m</code> if your data is written every minute. Access auto interval via variable{' '}
  79. <code>$__interval</code> for time range string and <code>$__interval_ms</code> for numeric variable that can
  80. be used in math expressions.
  81. </>
  82. ),
  83. },
  84. };
  85. constructor(props) {
  86. super(props);
  87. this.state = {
  88. relativeTime: props.panel.timeFrom || '',
  89. timeShift: props.panel.timeShift || '',
  90. cacheTimeout: props.panel.cacheTimeout || '',
  91. maxDataPoints: props.panel.maxDataPoints || '',
  92. interval: props.panel.interval || '',
  93. hideTimeOverride: props.panel.hideTimeOverride || false,
  94. };
  95. }
  96. onRelativeTimeChange = (event: ChangeEvent<HTMLInputElement>) => {
  97. this.setState({
  98. relativeTime: event.target.value,
  99. });
  100. };
  101. onTimeShiftChange = (event: ChangeEvent<HTMLInputElement>) => {
  102. this.setState({
  103. timeShift: event.target.value,
  104. });
  105. };
  106. onOverrideTime = (event: FocusEvent<HTMLInputElement>, status: InputStatus) => {
  107. const { value } = event.target;
  108. const { panel } = this.props;
  109. const emptyToNullValue = emptyToNull(value);
  110. if (status === InputStatus.Valid && panel.timeFrom !== emptyToNullValue) {
  111. panel.timeFrom = emptyToNullValue;
  112. panel.refresh();
  113. }
  114. };
  115. onTimeShift = (event: FocusEvent<HTMLInputElement>, status: InputStatus) => {
  116. const { value } = event.target;
  117. const { panel } = this.props;
  118. const emptyToNullValue = emptyToNull(value);
  119. if (status === InputStatus.Valid && panel.timeShift !== emptyToNullValue) {
  120. panel.timeShift = emptyToNullValue;
  121. panel.refresh();
  122. }
  123. };
  124. onToggleTimeOverride = () => {
  125. const { panel } = this.props;
  126. this.setState({ hideTimeOverride: !this.state.hideTimeOverride }, () => {
  127. panel.hideTimeOverride = this.state.hideTimeOverride;
  128. panel.refresh();
  129. });
  130. };
  131. onDataSourceOptionBlur = (panelKey: string) => () => {
  132. const { panel } = this.props;
  133. panel[panelKey] = this.state[panelKey];
  134. panel.refresh();
  135. };
  136. onDataSourceOptionChange = (panelKey: string) => (event: ChangeEvent<HTMLInputElement>) => {
  137. this.setState({ ...this.state, [panelKey]: event.target.value });
  138. };
  139. renderOptions = () => {
  140. const { datasource } = this.props;
  141. const { queryOptions } = datasource.meta;
  142. if (!queryOptions) {
  143. return null;
  144. }
  145. return Object.keys(queryOptions).map(key => {
  146. const options = this.allOptions[key];
  147. const panelKey = options.panelKey || key;
  148. return (
  149. <DataSourceOption
  150. key={key}
  151. {...options}
  152. onChange={this.onDataSourceOptionChange(panelKey)}
  153. onBlur={this.onDataSourceOptionBlur(panelKey)}
  154. value={this.state[panelKey]}
  155. />
  156. );
  157. });
  158. };
  159. render() {
  160. const { hideTimeOverride } = this.state;
  161. const { relativeTime, timeShift } = this.state;
  162. return (
  163. <div className="gf-form-inline">
  164. {this.renderOptions()}
  165. <div className="gf-form">
  166. <FormLabel>Relative time</FormLabel>
  167. <Input
  168. type="text"
  169. className="width-6"
  170. placeholder="1h"
  171. onChange={this.onRelativeTimeChange}
  172. onBlur={this.onOverrideTime}
  173. validationEvents={timeRangeValidationEvents}
  174. hideErrorMessage={true}
  175. value={relativeTime}
  176. />
  177. </div>
  178. <div className="gf-form">
  179. <span className="gf-form-label">Time shift</span>
  180. <Input
  181. type="text"
  182. className="width-6"
  183. placeholder="1h"
  184. onChange={this.onTimeShiftChange}
  185. onBlur={this.onTimeShift}
  186. validationEvents={timeRangeValidationEvents}
  187. hideErrorMessage={true}
  188. value={timeShift}
  189. />
  190. </div>
  191. {(timeShift || relativeTime) && (
  192. <div className="gf-form-inline">
  193. <Switch label="Hide time info" checked={hideTimeOverride} onChange={this.onToggleTimeOverride} />
  194. </div>
  195. )}
  196. </div>
  197. );
  198. }
  199. }