QueryOptions.tsx 6.0 KB

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