ExplorePaneContainer.tsx 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. import memoizeOne from 'memoize-one';
  2. import React from 'react';
  3. import { connect, ConnectedProps } from 'react-redux';
  4. import { DataQuery, ExploreUrlState, EventBusExtended, EventBusSrv } from '@grafana/data';
  5. import { selectors } from '@grafana/e2e-selectors';
  6. import store from 'app/core/store';
  7. import {
  8. DEFAULT_RANGE,
  9. ensureQueries,
  10. getTimeRange,
  11. getTimeRangeFromUrl,
  12. lastUsedDatasourceKeyForOrgId,
  13. parseUrlState,
  14. } from 'app/core/utils/explore';
  15. import { StoreState } from 'app/types';
  16. import { ExploreId } from 'app/types/explore';
  17. import { getFiscalYearStartMonth, getTimeZone } from '../profile/state/selectors';
  18. import Explore from './Explore';
  19. import { initializeExplore, refreshExplore } from './state/explorePane';
  20. import { lastSavedUrl, cleanupPaneAction } from './state/main';
  21. interface OwnProps {
  22. exploreId: ExploreId;
  23. urlQuery: string;
  24. split: boolean;
  25. }
  26. interface Props extends OwnProps, ConnectedProps<typeof connector> {}
  27. /**
  28. * This component is responsible for handling initialization of an Explore pane and triggering synchronization
  29. * of state based on URL changes and preventing any infinite loops.
  30. */
  31. class ExplorePaneContainerUnconnected extends React.PureComponent<Props> {
  32. el: any;
  33. exploreEvents: EventBusExtended;
  34. constructor(props: Props) {
  35. super(props);
  36. this.exploreEvents = new EventBusSrv();
  37. this.state = {
  38. openDrawer: undefined,
  39. };
  40. }
  41. componentDidMount() {
  42. const { initialized, exploreId, initialDatasource, initialQueries, initialRange, panelsState } = this.props;
  43. const width = this.el?.offsetWidth ?? 0;
  44. // initialize the whole explore first time we mount and if browser history contains a change in datasource
  45. if (!initialized) {
  46. this.props.initializeExplore(
  47. exploreId,
  48. initialDatasource,
  49. initialQueries,
  50. initialRange,
  51. width,
  52. this.exploreEvents,
  53. panelsState
  54. );
  55. }
  56. }
  57. componentWillUnmount() {
  58. this.exploreEvents.removeAllListeners();
  59. this.props.cleanupPaneAction({ exploreId: this.props.exploreId });
  60. }
  61. componentDidUpdate(prevProps: Props) {
  62. this.refreshExplore(prevProps.urlQuery);
  63. }
  64. refreshExplore = (prevUrlQuery: string) => {
  65. const { exploreId, urlQuery } = this.props;
  66. // Update state from url only if it changed and only if the change wasn't initialised by redux to prevent any loops
  67. if (urlQuery !== prevUrlQuery && urlQuery !== lastSavedUrl[exploreId]) {
  68. this.props.refreshExplore(exploreId, urlQuery);
  69. }
  70. };
  71. getRef = (el: any) => {
  72. this.el = el;
  73. };
  74. render() {
  75. const exploreClass = this.props.split ? 'explore explore-split' : 'explore';
  76. return (
  77. <div className={exploreClass} ref={this.getRef} data-testid={selectors.pages.Explore.General.container}>
  78. {this.props.initialized && <Explore exploreId={this.props.exploreId} />}
  79. </div>
  80. );
  81. }
  82. }
  83. const ensureQueriesMemoized = memoizeOne(ensureQueries);
  84. const getTimeRangeFromUrlMemoized = memoizeOne(getTimeRangeFromUrl);
  85. function mapStateToProps(state: StoreState, props: OwnProps) {
  86. const urlState = parseUrlState(props.urlQuery);
  87. const timeZone = getTimeZone(state.user);
  88. const fiscalYearStartMonth = getFiscalYearStartMonth(state.user);
  89. const { datasource, queries, range: urlRange, panelsState } = (urlState || {}) as ExploreUrlState;
  90. const initialDatasource = datasource || store.get(lastUsedDatasourceKeyForOrgId(state.user.orgId));
  91. const initialQueries: DataQuery[] = ensureQueriesMemoized(queries);
  92. const initialRange = urlRange
  93. ? getTimeRangeFromUrlMemoized(urlRange, timeZone, fiscalYearStartMonth)
  94. : getTimeRange(timeZone, DEFAULT_RANGE, fiscalYearStartMonth);
  95. return {
  96. initialized: state.explore[props.exploreId]?.initialized,
  97. initialDatasource,
  98. initialQueries,
  99. initialRange,
  100. panelsState,
  101. };
  102. }
  103. const mapDispatchToProps = {
  104. initializeExplore,
  105. refreshExplore,
  106. cleanupPaneAction,
  107. };
  108. const connector = connect(mapStateToProps, mapDispatchToProps);
  109. export const ExplorePaneContainer = connector(ExplorePaneContainerUnconnected);