ExploreToolbar.tsx 9.6 KB

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