QueryOptions.tsx 6.2 KB

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