ExploreToolbar.tsx 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. import React, { lazy, PureComponent, RefObject, Suspense } from 'react';
  2. import { connect, ConnectedProps } from 'react-redux';
  3. import { DataSourceInstanceSettings, RawTimeRange } from '@grafana/data';
  4. import { config, DataSourcePicker, reportInteraction } from '@grafana/runtime';
  5. import {
  6. defaultIntervals,
  7. PageToolbar,
  8. RefreshPicker,
  9. SetInterval,
  10. ToolbarButton,
  11. ToolbarButtonRow,
  12. } from '@grafana/ui';
  13. import { createAndCopyShortLink } from 'app/core/utils/shortLinks';
  14. import { ExploreId } from 'app/types/explore';
  15. import { StoreState } from 'app/types/store';
  16. import { DashNavButton } from '../dashboard/components/DashNav/DashNavButton';
  17. import { getTimeSrv } from '../dashboard/services/TimeSrv';
  18. import { updateFiscalYearStartMonthForSession, updateTimeZoneForSession } from '../profile/state/reducers';
  19. import { getFiscalYearStartMonth, getTimeZone } from '../profile/state/selectors';
  20. import { ExploreTimeControls } from './ExploreTimeControls';
  21. import { LiveTailButton } from './LiveTailButton';
  22. import { changeDatasource } from './state/datasource';
  23. import { splitClose, splitOpen } from './state/main';
  24. import { cancelQueries, runQueries } from './state/query';
  25. import { isSplit } from './state/selectors';
  26. import { syncTimes, changeRefreshInterval } from './state/time';
  27. import { LiveTailControls } from './useLiveTailControls';
  28. const AddToDashboard = lazy(() =>
  29. import('./AddToDashboard').then(({ AddToDashboard }) => ({ default: AddToDashboard }))
  30. );
  31. interface OwnProps {
  32. exploreId: ExploreId;
  33. onChangeTime: (range: RawTimeRange, changedByScanner?: boolean) => void;
  34. topOfViewRef: RefObject<HTMLDivElement>;
  35. }
  36. type Props = OwnProps & ConnectedProps<typeof connector>;
  37. class UnConnectedExploreToolbar extends PureComponent<Props> {
  38. onChangeDatasource = async (dsSettings: DataSourceInstanceSettings) => {
  39. this.props.changeDatasource(this.props.exploreId, dsSettings.uid, { importQueries: true });
  40. };
  41. onRunQuery = (loading = false) => {
  42. const { runQueries, cancelQueries, exploreId } = this.props;
  43. if (loading) {
  44. return cancelQueries(exploreId);
  45. } else {
  46. return runQueries(exploreId);
  47. }
  48. };
  49. onChangeRefreshInterval = (item: string) => {
  50. const { changeRefreshInterval, exploreId } = this.props;
  51. changeRefreshInterval(exploreId, item);
  52. };
  53. onChangeTimeSync = () => {
  54. const { syncTimes, exploreId } = this.props;
  55. syncTimes(exploreId);
  56. };
  57. renderRefreshPicker = (showSmallTimePicker: boolean) => {
  58. const { loading, refreshInterval, isLive } = this.props;
  59. let refreshPickerText: string | undefined = loading ? 'Cancel' : 'Run query';
  60. let refreshPickerTooltip = undefined;
  61. let refreshPickerWidth = '108px';
  62. if (showSmallTimePicker) {
  63. refreshPickerTooltip = refreshPickerText;
  64. refreshPickerText = undefined;
  65. refreshPickerWidth = '35px';
  66. }
  67. return (
  68. <RefreshPicker
  69. onIntervalChanged={this.onChangeRefreshInterval}
  70. value={refreshInterval}
  71. isLoading={loading}
  72. text={refreshPickerText}
  73. tooltip={refreshPickerTooltip}
  74. intervals={getTimeSrv().getValidIntervals(defaultIntervals)}
  75. isLive={isLive}
  76. onRefresh={() => this.onRunQuery(loading)}
  77. noIntervalPicker={isLive}
  78. primary={true}
  79. width={refreshPickerWidth}
  80. />
  81. );
  82. };
  83. render() {
  84. const {
  85. datasourceMissing,
  86. closeSplit,
  87. exploreId,
  88. loading,
  89. range,
  90. timeZone,
  91. fiscalYearStartMonth,
  92. splitted,
  93. syncedTimes,
  94. refreshInterval,
  95. onChangeTime,
  96. split,
  97. hasLiveOption,
  98. isLive,
  99. isPaused,
  100. containerWidth,
  101. onChangeTimeZone,
  102. onChangeFiscalYearStartMonth,
  103. topOfViewRef,
  104. } = this.props;
  105. const showSmallDataSourcePicker = (splitted ? containerWidth < 700 : containerWidth < 800) || false;
  106. const showSmallTimePicker = splitted || containerWidth < 1210;
  107. return (
  108. <div ref={topOfViewRef}>
  109. <PageToolbar
  110. aria-label="Explore toolbar"
  111. title={exploreId === ExploreId.left ? 'Explore' : undefined}
  112. pageIcon={exploreId === ExploreId.left ? 'compass' : undefined}
  113. leftItems={[
  114. exploreId === ExploreId.left && (
  115. <DashNavButton
  116. key="share"
  117. tooltip="Copy shortened link"
  118. icon="share-alt"
  119. onClick={() => createAndCopyShortLink(window.location.href)}
  120. aria-label="Copy shortened link"
  121. />
  122. ),
  123. !datasourceMissing && (
  124. <DataSourcePicker
  125. key={`${exploreId}-ds-picker`}
  126. onChange={this.onChangeDatasource}
  127. current={this.props.datasourceName}
  128. hideTextValue={showSmallDataSourcePicker}
  129. width={showSmallDataSourcePicker ? 8 : undefined}
  130. />
  131. ),
  132. ].filter(Boolean)}
  133. >
  134. <ToolbarButtonRow>
  135. {!splitted ? (
  136. <ToolbarButton title="Split" onClick={() => split()} icon="columns" disabled={isLive}>
  137. Split
  138. </ToolbarButton>
  139. ) : (
  140. <ToolbarButton title="Close split pane" onClick={() => closeSplit(exploreId)} icon="times">
  141. Close
  142. </ToolbarButton>
  143. )}
  144. {config.featureToggles.explore2Dashboard && (
  145. <Suspense fallback={null}>
  146. <AddToDashboard exploreId={exploreId} />
  147. </Suspense>
  148. )}
  149. {!isLive && (
  150. <ExploreTimeControls
  151. exploreId={exploreId}
  152. range={range}
  153. timeZone={timeZone}
  154. fiscalYearStartMonth={fiscalYearStartMonth}
  155. onChangeTime={onChangeTime}
  156. splitted={splitted}
  157. syncedTimes={syncedTimes}
  158. onChangeTimeSync={this.onChangeTimeSync}
  159. hideText={showSmallTimePicker}
  160. onChangeTimeZone={onChangeTimeZone}
  161. onChangeFiscalYearStartMonth={onChangeFiscalYearStartMonth}
  162. />
  163. )}
  164. {this.renderRefreshPicker(showSmallTimePicker)}
  165. {refreshInterval && <SetInterval func={this.onRunQuery} interval={refreshInterval} loading={loading} />}
  166. {hasLiveOption && (
  167. <LiveTailControls exploreId={exploreId}>
  168. {(c) => {
  169. const controls = {
  170. ...c,
  171. start: () => {
  172. reportInteraction('grafana_explore_logs_live_tailing_clicked', {
  173. datasourceType: this.props.datasourceType,
  174. });
  175. c.start();
  176. },
  177. };
  178. return (
  179. <LiveTailButton
  180. splitted={splitted}
  181. isLive={isLive}
  182. isPaused={isPaused}
  183. start={controls.start}
  184. pause={controls.pause}
  185. resume={controls.resume}
  186. stop={controls.stop}
  187. />
  188. );
  189. }}
  190. </LiveTailControls>
  191. )}
  192. </ToolbarButtonRow>
  193. </PageToolbar>
  194. </div>
  195. );
  196. }
  197. }
  198. const mapStateToProps = (state: StoreState, { exploreId }: OwnProps) => {
  199. const { syncedTimes } = state.explore;
  200. const exploreItem = state.explore[exploreId]!;
  201. const { datasourceInstance, datasourceMissing, range, refreshInterval, loading, isLive, isPaused, containerWidth } =
  202. exploreItem;
  203. const hasLiveOption = !!datasourceInstance?.meta?.streaming;
  204. return {
  205. datasourceMissing,
  206. datasourceName: datasourceInstance?.name,
  207. datasourceType: datasourceInstance?.type,
  208. loading,
  209. range,
  210. timeZone: getTimeZone(state.user),
  211. fiscalYearStartMonth: getFiscalYearStartMonth(state.user),
  212. splitted: isSplit(state),
  213. refreshInterval,
  214. hasLiveOption,
  215. isLive,
  216. isPaused,
  217. syncedTimes,
  218. containerWidth,
  219. };
  220. };
  221. const mapDispatchToProps = {
  222. changeDatasource,
  223. changeRefreshInterval,
  224. cancelQueries,
  225. runQueries,
  226. closeSplit: splitClose,
  227. split: splitOpen,
  228. syncTimes,
  229. onChangeTimeZone: updateTimeZoneForSession,
  230. onChangeFiscalYearStartMonth: updateFiscalYearStartMonthForSession,
  231. };
  232. const connector = connect(mapStateToProps, mapDispatchToProps);
  233. export const ExploreToolbar = connector(UnConnectedExploreToolbar);