QueryOptions.tsx 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  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.
  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: 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. // @ts-ignore
  134. panel[panelKey] = this.state[panelKey];
  135. panel.refresh();
  136. };
  137. onDataSourceOptionChange = (panelKey: string) => (event: ChangeEvent<HTMLInputElement>) => {
  138. this.setState({ ...this.state, [panelKey]: event.target.value });
  139. };
  140. renderOptions = () => {
  141. const { datasource } = this.props;
  142. const { queryOptions } = datasource.meta;
  143. if (!queryOptions) {
  144. return null;
  145. }
  146. return Object.keys(queryOptions).map(key => {
  147. const options = this.allOptions[key];
  148. const panelKey = options.panelKey || key;
  149. return (
  150. <DataSourceOption
  151. key={key}
  152. {...options}
  153. onChange={this.onDataSourceOptionChange(panelKey)}
  154. onBlur={this.onDataSourceOptionBlur(panelKey)}
  155. // @ts-ignore
  156. value={this.state[panelKey]}
  157. />
  158. );
  159. });
  160. };
  161. render() {
  162. const { hideTimeOverride } = this.state;
  163. const { relativeTime, timeShift } = this.state;
  164. return (
  165. <div className="gf-form-inline">
  166. {this.renderOptions()}
  167. <div className="gf-form">
  168. <FormLabel>Relative time</FormLabel>
  169. <Input
  170. type="text"
  171. className="width-6"
  172. placeholder="1h"
  173. onChange={this.onRelativeTimeChange}
  174. onBlur={this.onOverrideTime}
  175. validationEvents={timeRangeValidationEvents}
  176. hideErrorMessage={true}
  177. value={relativeTime}
  178. />
  179. </div>
  180. <div className="gf-form">
  181. <span className="gf-form-label">Time shift</span>
  182. <Input
  183. type="text"
  184. className="width-6"
  185. placeholder="1h"
  186. onChange={this.onTimeShiftChange}
  187. onBlur={this.onTimeShift}
  188. validationEvents={timeRangeValidationEvents}
  189. hideErrorMessage={true}
  190. value={timeShift}
  191. />
  192. </div>
  193. {(timeShift || relativeTime) && (
  194. <div className="gf-form-inline">
  195. <Switch label="Hide time info" checked={hideTimeOverride} onChange={this.onToggleTimeOverride} />
  196. </div>
  197. )}
  198. </div>
  199. );
  200. }
  201. }