ExploreToolbar.tsx 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. import React, { PureComponent } from 'react';
  2. import { connect } from 'react-redux';
  3. import { hot } from 'react-hot-loader';
  4. import { ExploreId } from 'app/types/explore';
  5. import { DataSourceSelectItem, RawTimeRange, TimeRange } from '@grafana/ui';
  6. import { DataSourcePicker } from 'app/core/components/Select/DataSourcePicker';
  7. import { StoreState } from 'app/types/store';
  8. import { changeDatasource, clearQueries, splitClose, runQueries, splitOpen } from './state/actions';
  9. import TimePicker from './TimePicker';
  10. enum IconSide {
  11. left = 'left',
  12. right = 'right',
  13. }
  14. const createResponsiveButton = (options: {
  15. splitted: boolean;
  16. title: string;
  17. onClick: () => void;
  18. buttonClassName?: string;
  19. iconClassName?: string;
  20. iconSide?: IconSide;
  21. }) => {
  22. const defaultOptions = {
  23. iconSide: IconSide.left,
  24. };
  25. const props = { ...options, defaultOptions };
  26. const { title, onClick, buttonClassName, iconClassName, splitted, iconSide } = props;
  27. return (
  28. <button className={`btn navbar-button ${buttonClassName ? buttonClassName : ''}`} onClick={onClick}>
  29. {iconClassName && iconSide === IconSide.left ? <i className={`${iconClassName} icon-margin-right`} /> : null}
  30. <span className="btn-title">{!splitted ? title : ''}</span>
  31. {iconClassName && iconSide === IconSide.right ? <i className={`${iconClassName} icon-margin-left`} /> : null}
  32. </button>
  33. );
  34. };
  35. interface OwnProps {
  36. exploreId: ExploreId;
  37. timepickerRef: React.RefObject<TimePicker>;
  38. onChangeTime: (range: TimeRange, changedByScanner?: boolean) => void;
  39. }
  40. interface StateProps {
  41. datasourceMissing: boolean;
  42. exploreDatasources: DataSourceSelectItem[];
  43. loading: boolean;
  44. range: RawTimeRange;
  45. selectedDatasource: DataSourceSelectItem;
  46. splitted: boolean;
  47. }
  48. interface DispatchProps {
  49. changeDatasource: typeof changeDatasource;
  50. clearAll: typeof clearQueries;
  51. runQuery: typeof runQueries;
  52. closeSplit: typeof splitClose;
  53. split: typeof splitOpen;
  54. }
  55. type Props = StateProps & DispatchProps & OwnProps;
  56. export class UnConnectedExploreToolbar extends PureComponent<Props, {}> {
  57. constructor(props) {
  58. super(props);
  59. }
  60. onChangeDatasource = async option => {
  61. this.props.changeDatasource(this.props.exploreId, option.value);
  62. };
  63. onClearAll = () => {
  64. this.props.clearAll(this.props.exploreId);
  65. };
  66. onRunQuery = () => {
  67. this.props.runQuery(this.props.exploreId);
  68. };
  69. render() {
  70. const {
  71. datasourceMissing,
  72. exploreDatasources,
  73. exploreId,
  74. loading,
  75. range,
  76. selectedDatasource,
  77. splitted,
  78. timepickerRef,
  79. } = this.props;
  80. return (
  81. <div className={splitted ? 'explore-toolbar splitted' : 'explore-toolbar'}>
  82. <div className="explore-toolbar-item">
  83. <div className="explore-toolbar-header">
  84. <div className="explore-toolbar-header-title">
  85. {exploreId === 'left' && (
  86. <a className="navbar-page-btn">
  87. <i className="fa fa-rocket fa-fw" />
  88. Explore
  89. </a>
  90. )}
  91. </div>
  92. <div className="explore-toolbar-header-close">
  93. {exploreId === 'right' && (
  94. <a onClick={this.props.closeSplit}>
  95. <i className="fa fa-times fa-fw" />
  96. </a>
  97. )}
  98. </div>
  99. </div>
  100. </div>
  101. <div className="explore-toolbar-item">
  102. <div className="explore-toolbar-content">
  103. {!datasourceMissing ? (
  104. <div className="explore-toolbar-content-item">
  105. <div className="datasource-picker">
  106. <DataSourcePicker
  107. onChange={this.onChangeDatasource}
  108. datasources={exploreDatasources}
  109. current={selectedDatasource}
  110. />
  111. </div>
  112. </div>
  113. ) : null}
  114. {exploreId === 'left' && !splitted ? (
  115. <div className="explore-toolbar-content-item">
  116. {createResponsiveButton({
  117. splitted,
  118. title: 'Split',
  119. onClick: this.props.split,
  120. iconClassName: 'fa fa-fw fa-columns icon-margin-right',
  121. iconSide: IconSide.left,
  122. })}
  123. </div>
  124. ) : null}
  125. <div className="explore-toolbar-content-item timepicker">
  126. <TimePicker ref={timepickerRef} range={range} onChangeTime={this.props.onChangeTime} />
  127. </div>
  128. <div className="explore-toolbar-content-item">
  129. <button className="btn navbar-button navbar-button--no-icon" onClick={this.onClearAll}>
  130. Clear All
  131. </button>
  132. </div>
  133. <div className="explore-toolbar-content-item">
  134. {createResponsiveButton({
  135. splitted,
  136. title: 'Run Query',
  137. onClick: this.onRunQuery,
  138. buttonClassName: 'navbar-button--primary',
  139. iconClassName: loading ? 'fa fa-spinner fa-fw fa-spin run-icon' : 'fa fa-level-down fa-fw run-icon',
  140. iconSide: IconSide.right,
  141. })}
  142. </div>
  143. </div>
  144. </div>
  145. </div>
  146. );
  147. }
  148. }
  149. const mapStateToProps = (state: StoreState, { exploreId }: OwnProps): StateProps => {
  150. const splitted = state.explore.split;
  151. const exploreItem = state.explore[exploreId];
  152. const { datasourceInstance, datasourceMissing, exploreDatasources, queryTransactions, range } = exploreItem;
  153. const selectedDatasource = datasourceInstance
  154. ? exploreDatasources.find(datasource => datasource.name === datasourceInstance.name)
  155. : undefined;
  156. const loading = queryTransactions.some(qt => !qt.done);
  157. return {
  158. datasourceMissing,
  159. exploreDatasources,
  160. loading,
  161. range,
  162. selectedDatasource,
  163. splitted,
  164. };
  165. };
  166. const mapDispatchToProps: DispatchProps = {
  167. changeDatasource,
  168. clearAll: clearQueries,
  169. runQuery: runQueries,
  170. closeSplit: splitClose,
  171. split: splitOpen,
  172. };
  173. export const ExploreToolbar = hot(module)(connect(mapStateToProps, mapDispatchToProps)(UnConnectedExploreToolbar));