resultsChart.tsx 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. import {Component, Fragment} from 'react';
  2. import {InjectedRouter} from 'react-router';
  3. import styled from '@emotion/styled';
  4. import {Location} from 'history';
  5. import isEqual from 'lodash/isEqual';
  6. import {Client} from 'app/api';
  7. import EventsChart from 'app/components/charts/eventsChart';
  8. import {getParams} from 'app/components/organizations/globalSelectionHeader/getParams';
  9. import {Panel} from 'app/components/panels';
  10. import Placeholder from 'app/components/placeholder';
  11. import {Organization} from 'app/types';
  12. import {getUtcToLocalDateObject} from 'app/utils/dates';
  13. import EventView from 'app/utils/discover/eventView';
  14. import {DisplayModes, TOP_N} from 'app/utils/discover/types';
  15. import getDynamicText from 'app/utils/getDynamicText';
  16. import {decodeScalar} from 'app/utils/queryString';
  17. import withApi from 'app/utils/withApi';
  18. import ChartFooter from './chartFooter';
  19. type ResultsChartProps = {
  20. api: Client;
  21. router: InjectedRouter;
  22. organization: Organization;
  23. eventView: EventView;
  24. location: Location;
  25. confirmedQuery: boolean;
  26. };
  27. class ResultsChart extends Component<ResultsChartProps> {
  28. shouldComponentUpdate(nextProps: ResultsChartProps) {
  29. const {eventView, ...restProps} = this.props;
  30. const {eventView: nextEventView, ...restNextProps} = nextProps;
  31. if (!eventView.isEqualTo(nextEventView)) {
  32. return true;
  33. }
  34. return !isEqual(restProps, restNextProps);
  35. }
  36. render() {
  37. const {api, eventView, location, organization, router, confirmedQuery} = this.props;
  38. const hasPerformanceChartInterpolation = organization.features.includes(
  39. 'performance-chart-interpolation'
  40. );
  41. const yAxisValue = eventView.getYAxis();
  42. const globalSelection = eventView.getGlobalSelection();
  43. const start = globalSelection.datetime.start
  44. ? getUtcToLocalDateObject(globalSelection.datetime.start)
  45. : null;
  46. const end = globalSelection.datetime.end
  47. ? getUtcToLocalDateObject(globalSelection.datetime.end)
  48. : null;
  49. const {utc} = getParams(location.query);
  50. const apiPayload = eventView.getEventsAPIPayload(location);
  51. const display = eventView.getDisplayMode();
  52. const isTopEvents =
  53. display === DisplayModes.TOP5 || display === DisplayModes.DAILYTOP5;
  54. const isPeriod = display === DisplayModes.DEFAULT || display === DisplayModes.TOP5;
  55. const isDaily = display === DisplayModes.DAILYTOP5 || display === DisplayModes.DAILY;
  56. const isPrevious = display === DisplayModes.PREVIOUS;
  57. return (
  58. <Fragment>
  59. {getDynamicText({
  60. value: (
  61. <EventsChart
  62. api={api}
  63. router={router}
  64. query={apiPayload.query}
  65. organization={organization}
  66. showLegend
  67. yAxis={yAxisValue}
  68. projects={globalSelection.projects}
  69. environments={globalSelection.environments}
  70. start={start}
  71. end={end}
  72. period={globalSelection.datetime.period}
  73. disablePrevious={!isPrevious}
  74. disableReleases={!isPeriod}
  75. field={isTopEvents ? apiPayload.field : undefined}
  76. interval={eventView.interval}
  77. showDaily={isDaily}
  78. topEvents={isTopEvents ? TOP_N : undefined}
  79. orderby={isTopEvents ? decodeScalar(apiPayload.sort) : undefined}
  80. utc={utc === 'true'}
  81. confirmedQuery={confirmedQuery}
  82. withoutZerofill={hasPerformanceChartInterpolation}
  83. />
  84. ),
  85. fixed: <Placeholder height="200px" testId="skeleton-ui" />,
  86. })}
  87. </Fragment>
  88. );
  89. }
  90. }
  91. type ContainerProps = {
  92. api: Client;
  93. router: InjectedRouter;
  94. eventView: EventView;
  95. location: Location;
  96. organization: Organization;
  97. confirmedQuery: boolean;
  98. // chart footer props
  99. total: number | null;
  100. onAxisChange: (value: string) => void;
  101. onDisplayChange: (value: string) => void;
  102. };
  103. class ResultsChartContainer extends Component<ContainerProps> {
  104. shouldComponentUpdate(nextProps: ContainerProps) {
  105. const {eventView, ...restProps} = this.props;
  106. const {eventView: nextEventView, ...restNextProps} = nextProps;
  107. if (
  108. !eventView.isEqualTo(nextEventView) ||
  109. this.props.confirmedQuery !== nextProps.confirmedQuery
  110. ) {
  111. return true;
  112. }
  113. return !isEqual(restProps, restNextProps);
  114. }
  115. render() {
  116. const {
  117. api,
  118. eventView,
  119. location,
  120. router,
  121. total,
  122. onAxisChange,
  123. onDisplayChange,
  124. organization,
  125. confirmedQuery,
  126. } = this.props;
  127. const yAxisValue = eventView.getYAxis();
  128. const hasQueryFeature = organization.features.includes('discover-query');
  129. const displayOptions = eventView.getDisplayOptions().filter(opt => {
  130. // top5 modes are only available with larger packages in saas.
  131. // We remove instead of disable here as showing tooltips in dropdown
  132. // menus is clunky.
  133. if (
  134. [DisplayModes.TOP5, DisplayModes.DAILYTOP5].includes(opt.value as DisplayModes) &&
  135. !hasQueryFeature
  136. ) {
  137. return false;
  138. }
  139. return true;
  140. });
  141. return (
  142. <StyledPanel>
  143. <ResultsChart
  144. api={api}
  145. eventView={eventView}
  146. location={location}
  147. organization={organization}
  148. router={router}
  149. confirmedQuery={confirmedQuery}
  150. />
  151. <ChartFooter
  152. total={total}
  153. yAxisValue={yAxisValue}
  154. yAxisOptions={eventView.getYAxisOptions()}
  155. onAxisChange={onAxisChange}
  156. displayOptions={displayOptions}
  157. displayMode={eventView.getDisplayMode()}
  158. onDisplayChange={onDisplayChange}
  159. />
  160. </StyledPanel>
  161. );
  162. }
  163. }
  164. export default withApi(ResultsChartContainer);
  165. const StyledPanel = styled(Panel)`
  166. @media (min-width: ${p => p.theme.breakpoints[1]}) {
  167. margin: 0;
  168. }
  169. `;