ExploreToolbar.tsx 9.9 KB

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