ExploreToolbar.tsx 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. import React, { PureComponent } from 'react';
  2. import { connect } from 'react-redux';
  3. import { hot } from 'react-hot-loader';
  4. import { ExploreId, ExploreMode } from 'app/types/explore';
  5. import {
  6. DataSourceSelectItem,
  7. RawTimeRange,
  8. ClickOutsideWrapper,
  9. TimeZone,
  10. TimeRange,
  11. SelectOptionItem,
  12. } from '@grafana/ui';
  13. import { DataSourcePicker } from 'app/core/components/Select/DataSourcePicker';
  14. import { StoreState } from 'app/types/store';
  15. import {
  16. changeDatasource,
  17. clearQueries,
  18. splitClose,
  19. runQueries,
  20. splitOpen,
  21. changeRefreshInterval,
  22. changeMode,
  23. } from './state/actions';
  24. import TimePicker from './TimePicker';
  25. import { getTimeZone } from '../profile/state/selectors';
  26. import { RefreshPicker, SetInterval } from '@grafana/ui';
  27. import ToggleButtonGroup, { ToggleButton } from 'app/core/components/ToggleButtonGroup/ToggleButtonGroup';
  28. enum IconSide {
  29. left = 'left',
  30. right = 'right',
  31. }
  32. const createResponsiveButton = (options: {
  33. splitted: boolean;
  34. title: string;
  35. onClick: () => void;
  36. buttonClassName?: string;
  37. iconClassName?: string;
  38. iconSide?: IconSide;
  39. disabled?: boolean;
  40. }) => {
  41. const defaultOptions = {
  42. iconSide: IconSide.left,
  43. };
  44. const props = { ...options, defaultOptions };
  45. const { title, onClick, buttonClassName, iconClassName, splitted, iconSide, disabled } = props;
  46. return (
  47. <button
  48. className={`btn navbar-button ${buttonClassName ? buttonClassName : ''}`}
  49. onClick={onClick}
  50. disabled={disabled || false}
  51. >
  52. {iconClassName && iconSide === IconSide.left ? <i className={`${iconClassName}`} /> : null}
  53. <span className="btn-title">{!splitted ? title : ''}</span>
  54. {iconClassName && iconSide === IconSide.right ? <i className={`${iconClassName}`} /> : null}
  55. </button>
  56. );
  57. };
  58. interface OwnProps {
  59. exploreId: ExploreId;
  60. timepickerRef: React.RefObject<TimePicker>;
  61. onChangeTime: (range: RawTimeRange, changedByScanner?: boolean) => void;
  62. }
  63. interface StateProps {
  64. datasourceMissing: boolean;
  65. exploreDatasources: DataSourceSelectItem[];
  66. loading: boolean;
  67. range: TimeRange;
  68. timeZone: TimeZone;
  69. selectedDatasource: DataSourceSelectItem;
  70. splitted: boolean;
  71. refreshInterval: string;
  72. supportedModeOptions: Array<SelectOptionItem<ExploreMode>>;
  73. selectedModeOption: SelectOptionItem<ExploreMode>;
  74. hasLiveOption: boolean;
  75. isLive: boolean;
  76. }
  77. interface DispatchProps {
  78. changeDatasource: typeof changeDatasource;
  79. clearAll: typeof clearQueries;
  80. runQueries: typeof runQueries;
  81. closeSplit: typeof splitClose;
  82. split: typeof splitOpen;
  83. changeRefreshInterval: typeof changeRefreshInterval;
  84. changeMode: typeof changeMode;
  85. }
  86. type Props = StateProps & DispatchProps & OwnProps;
  87. export class UnConnectedExploreToolbar extends PureComponent<Props, {}> {
  88. constructor(props: Props) {
  89. super(props);
  90. }
  91. onChangeDatasource = async option => {
  92. this.props.changeDatasource(this.props.exploreId, option.value);
  93. };
  94. onClearAll = () => {
  95. this.props.clearAll(this.props.exploreId);
  96. };
  97. onRunQuery = () => {
  98. return this.props.runQueries(this.props.exploreId);
  99. };
  100. onCloseTimePicker = () => {
  101. this.props.timepickerRef.current.setState({ isOpen: false });
  102. };
  103. onChangeRefreshInterval = (item: string) => {
  104. const { changeRefreshInterval, exploreId } = this.props;
  105. changeRefreshInterval(exploreId, item);
  106. };
  107. onModeChange = (mode: ExploreMode) => {
  108. const { changeMode, exploreId } = this.props;
  109. changeMode(exploreId, mode);
  110. };
  111. render() {
  112. const {
  113. datasourceMissing,
  114. exploreDatasources,
  115. closeSplit,
  116. exploreId,
  117. loading,
  118. range,
  119. timeZone,
  120. selectedDatasource,
  121. splitted,
  122. timepickerRef,
  123. refreshInterval,
  124. onChangeTime,
  125. split,
  126. supportedModeOptions,
  127. selectedModeOption,
  128. hasLiveOption,
  129. isLive,
  130. } = this.props;
  131. return (
  132. <div className={splitted ? 'explore-toolbar splitted' : 'explore-toolbar'}>
  133. <div className="explore-toolbar-item">
  134. <div className="explore-toolbar-header">
  135. <div className="explore-toolbar-header-title">
  136. {exploreId === 'left' && (
  137. <span className="navbar-page-btn">
  138. <i className="gicon gicon-explore" />
  139. Explore
  140. </span>
  141. )}
  142. </div>
  143. {splitted && (
  144. <a className="explore-toolbar-header-close" onClick={() => closeSplit(exploreId)}>
  145. <i className="fa fa-times fa-fw" />
  146. </a>
  147. )}
  148. </div>
  149. </div>
  150. <div className="explore-toolbar-item">
  151. <div className="explore-toolbar-content">
  152. {!datasourceMissing ? (
  153. <div className="explore-toolbar-content-item">
  154. <div className="datasource-picker">
  155. <DataSourcePicker
  156. onChange={this.onChangeDatasource}
  157. datasources={exploreDatasources}
  158. current={selectedDatasource}
  159. />
  160. </div>
  161. {supportedModeOptions.length > 1 ? (
  162. <div className="query-type-toggle">
  163. <ToggleButtonGroup label="" transparent={true}>
  164. <ToggleButton
  165. key={ExploreMode.Metrics}
  166. value={ExploreMode.Metrics}
  167. onChange={this.onModeChange}
  168. selected={selectedModeOption.value === ExploreMode.Metrics}
  169. >
  170. {'Metrics'}
  171. </ToggleButton>
  172. <ToggleButton
  173. key={ExploreMode.Logs}
  174. value={ExploreMode.Logs}
  175. onChange={this.onModeChange}
  176. selected={selectedModeOption.value === ExploreMode.Logs}
  177. >
  178. {'Logs'}
  179. </ToggleButton>
  180. </ToggleButtonGroup>
  181. </div>
  182. ) : null}
  183. </div>
  184. ) : null}
  185. {exploreId === 'left' && !splitted ? (
  186. <div className="explore-toolbar-content-item">
  187. {createResponsiveButton({
  188. splitted,
  189. title: 'Split',
  190. onClick: split,
  191. iconClassName: 'fa fa-fw fa-columns icon-margin-right',
  192. iconSide: IconSide.left,
  193. disabled: isLive,
  194. })}
  195. </div>
  196. ) : null}
  197. <div className="explore-toolbar-content-item timepicker">
  198. {!isLive && (
  199. <ClickOutsideWrapper onClick={this.onCloseTimePicker}>
  200. <TimePicker ref={timepickerRef} range={range} isUtc={timeZone.isUtc} onChangeTime={onChangeTime} />
  201. </ClickOutsideWrapper>
  202. )}
  203. <RefreshPicker
  204. onIntervalChanged={this.onChangeRefreshInterval}
  205. onRefresh={this.onRunQuery}
  206. value={refreshInterval}
  207. tooltip="Refresh"
  208. hasLiveOption={hasLiveOption}
  209. />
  210. {refreshInterval && <SetInterval func={this.onRunQuery} interval={refreshInterval} loading={loading} />}
  211. </div>
  212. <div className="explore-toolbar-content-item">
  213. <button className="btn navbar-button" onClick={this.onClearAll}>
  214. Clear All
  215. </button>
  216. </div>
  217. <div className="explore-toolbar-content-item">
  218. {createResponsiveButton({
  219. splitted,
  220. title: 'Run Query',
  221. onClick: this.onRunQuery,
  222. buttonClassName: 'navbar-button--secondary',
  223. iconClassName:
  224. loading && !isLive ? 'fa fa-spinner fa-fw fa-spin run-icon' : 'fa fa-level-down fa-fw run-icon',
  225. iconSide: IconSide.right,
  226. })}
  227. </div>
  228. </div>
  229. </div>
  230. </div>
  231. );
  232. }
  233. }
  234. const mapStateToProps = (state: StoreState, { exploreId }: OwnProps): StateProps => {
  235. const splitted = state.explore.split;
  236. const exploreItem = state.explore[exploreId];
  237. const {
  238. datasourceInstance,
  239. datasourceMissing,
  240. exploreDatasources,
  241. range,
  242. refreshInterval,
  243. graphIsLoading,
  244. logIsLoading,
  245. tableIsLoading,
  246. supportedModes,
  247. mode,
  248. isLive,
  249. } = exploreItem;
  250. const selectedDatasource = datasourceInstance
  251. ? exploreDatasources.find(datasource => datasource.name === datasourceInstance.name)
  252. : undefined;
  253. const loading = graphIsLoading || logIsLoading || tableIsLoading;
  254. const hasLiveOption = datasourceInstance && datasourceInstance.convertToStreamTargets ? true : false;
  255. const supportedModeOptions: Array<SelectOptionItem<ExploreMode>> = [];
  256. let selectedModeOption = null;
  257. for (const supportedMode of supportedModes) {
  258. switch (supportedMode) {
  259. case ExploreMode.Metrics:
  260. const option1 = {
  261. value: ExploreMode.Metrics,
  262. label: ExploreMode.Metrics,
  263. };
  264. supportedModeOptions.push(option1);
  265. if (mode === ExploreMode.Metrics) {
  266. selectedModeOption = option1;
  267. }
  268. break;
  269. case ExploreMode.Logs:
  270. const option2 = {
  271. value: ExploreMode.Logs,
  272. label: ExploreMode.Logs,
  273. };
  274. supportedModeOptions.push(option2);
  275. if (mode === ExploreMode.Logs) {
  276. selectedModeOption = option2;
  277. }
  278. break;
  279. }
  280. }
  281. return {
  282. datasourceMissing,
  283. exploreDatasources,
  284. loading,
  285. range,
  286. timeZone: getTimeZone(state.user),
  287. selectedDatasource,
  288. splitted,
  289. refreshInterval,
  290. supportedModeOptions,
  291. selectedModeOption,
  292. hasLiveOption,
  293. isLive,
  294. };
  295. };
  296. const mapDispatchToProps: DispatchProps = {
  297. changeDatasource,
  298. changeRefreshInterval,
  299. clearAll: clearQueries,
  300. runQueries,
  301. closeSplit: splitClose,
  302. split: splitOpen,
  303. changeMode: changeMode,
  304. };
  305. export const ExploreToolbar = hot(module)(
  306. connect(
  307. mapStateToProps,
  308. mapDispatchToProps
  309. )(UnConnectedExploreToolbar)
  310. );