charts.tsx 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. import {Component} from 'react';
  2. import {browserHistory} from 'react-router';
  3. import {Location} from 'history';
  4. import OptionSelector from 'app/components/charts/optionSelector';
  5. import {
  6. ChartContainer,
  7. ChartControls,
  8. InlineContainer,
  9. SectionHeading,
  10. SectionValue,
  11. } from 'app/components/charts/styles';
  12. import {Panel} from 'app/components/panels';
  13. import Placeholder from 'app/components/placeholder';
  14. import {t} from 'app/locale';
  15. import {OrganizationSummary, SelectValue} from 'app/types';
  16. import EventView from 'app/utils/discover/eventView';
  17. import {removeHistogramQueryStrings} from 'app/utils/performance/histogram';
  18. import {decodeScalar} from 'app/utils/queryString';
  19. import {TransactionsListOption} from 'app/views/releases/detail/overview';
  20. import {YAxis} from 'app/views/releases/detail/overview/chart/releaseChartControls';
  21. import {TrendColumnField, TrendFunctionField} from '../trends/types';
  22. import {
  23. generateTrendFunctionAsString,
  24. getTrendsParameters,
  25. TRENDS_FUNCTIONS,
  26. } from '../trends/utils';
  27. import DurationChart from './durationChart';
  28. import DurationPercentileChart from './durationPercentileChart';
  29. import {SpanOperationBreakdownFilter} from './filter';
  30. import LatencyChart, {LatencyChartControls, ZOOM_END, ZOOM_START} from './latencyChart';
  31. import TrendChart from './trendChart';
  32. import VitalsChart from './vitalsChart';
  33. export enum DisplayModes {
  34. DURATION_PERCENTILE = 'durationpercentile',
  35. DURATION = 'duration',
  36. LATENCY = 'latency',
  37. TREND = 'trend',
  38. VITALS = 'vitals',
  39. }
  40. function generateDisplayOptions(
  41. currentFilter: SpanOperationBreakdownFilter
  42. ): SelectValue<string>[] {
  43. if (currentFilter === SpanOperationBreakdownFilter.None) {
  44. return [
  45. {value: DisplayModes.DURATION, label: t('Duration Breakdown')},
  46. {value: DisplayModes.DURATION_PERCENTILE, label: t('Duration Percentiles')},
  47. {value: DisplayModes.LATENCY, label: t('Duration Distribution')},
  48. {value: DisplayModes.TREND, label: t('Trends')},
  49. {value: DisplayModes.VITALS, label: t('Web Vitals')},
  50. ];
  51. }
  52. // A span operation name breakdown has been chosen.
  53. return [
  54. {value: DisplayModes.DURATION, label: t('Span Operation Breakdown')},
  55. {value: DisplayModes.DURATION_PERCENTILE, label: t('Span Operation Percentiles')},
  56. {value: DisplayModes.LATENCY, label: t('Span Operation Distribution')},
  57. {value: DisplayModes.TREND, label: t('Trends')},
  58. {value: DisplayModes.VITALS, label: t('Web Vitals')},
  59. ];
  60. }
  61. const TREND_FUNCTIONS_OPTIONS: SelectValue<string>[] = TRENDS_FUNCTIONS.map(
  62. ({field, label}) => ({
  63. value: field,
  64. label,
  65. })
  66. );
  67. type Props = {
  68. organization: OrganizationSummary;
  69. location: Location;
  70. eventView: EventView;
  71. totalValues: number | null;
  72. currentFilter: SpanOperationBreakdownFilter;
  73. };
  74. class TransactionSummaryCharts extends Component<Props> {
  75. handleDisplayChange = (value: string) => {
  76. const {location} = this.props;
  77. browserHistory.push({
  78. pathname: location.pathname,
  79. query: {
  80. ...removeHistogramQueryStrings(location, [ZOOM_START, ZOOM_END]),
  81. display: value,
  82. },
  83. });
  84. };
  85. handleTrendDisplayChange = (value: string) => {
  86. const {location} = this.props;
  87. browserHistory.push({
  88. pathname: location.pathname,
  89. query: {...location.query, trendFunction: value},
  90. });
  91. };
  92. handleTrendColumnChange = (value: string) => {
  93. const {location} = this.props;
  94. browserHistory.push({
  95. pathname: location.pathname,
  96. query: {...location.query, trendColumn: value},
  97. });
  98. };
  99. render() {
  100. const {totalValues, eventView, organization, location, currentFilter} = this.props;
  101. const TREND_PARAMETERS_OPTIONS: SelectValue<string>[] = getTrendsParameters({
  102. canSeeSpanOpTrends: organization.features.includes('performance-ops-breakdown'),
  103. }).map(({column, label}) => ({
  104. value: column,
  105. label,
  106. }));
  107. let display = decodeScalar(location.query.display, DisplayModes.DURATION);
  108. let trendFunction = decodeScalar(
  109. location.query.trendFunction,
  110. TREND_FUNCTIONS_OPTIONS[0].value
  111. ) as TrendFunctionField;
  112. let trendColumn = decodeScalar(
  113. location.query.trendColumn,
  114. TREND_PARAMETERS_OPTIONS[0].value
  115. );
  116. if (!Object.values(DisplayModes).includes(display as DisplayModes)) {
  117. display = DisplayModes.DURATION;
  118. }
  119. if (!Object.values(TrendFunctionField).includes(trendFunction)) {
  120. trendFunction = TrendFunctionField.P50;
  121. }
  122. if (!Object.values(TrendColumnField).includes(trendColumn as TrendColumnField)) {
  123. trendColumn = TrendColumnField.DURATION;
  124. }
  125. const releaseQueryExtra = {
  126. yAxis: display === DisplayModes.VITALS ? YAxis.COUNT_VITAL : YAxis.COUNT_DURATION,
  127. showTransactions:
  128. display === DisplayModes.VITALS
  129. ? TransactionsListOption.SLOW_LCP
  130. : display === DisplayModes.DURATION
  131. ? TransactionsListOption.SLOW
  132. : undefined,
  133. };
  134. return (
  135. <Panel>
  136. <ChartContainer>
  137. {display === DisplayModes.LATENCY && (
  138. <LatencyChart
  139. organization={organization}
  140. location={location}
  141. query={eventView.query}
  142. project={eventView.project}
  143. environment={eventView.environment}
  144. start={eventView.start}
  145. end={eventView.end}
  146. statsPeriod={eventView.statsPeriod}
  147. currentFilter={currentFilter}
  148. />
  149. )}
  150. {display === DisplayModes.DURATION && (
  151. <DurationChart
  152. organization={organization}
  153. query={eventView.query}
  154. queryExtra={releaseQueryExtra}
  155. project={eventView.project}
  156. environment={eventView.environment}
  157. start={eventView.start}
  158. end={eventView.end}
  159. statsPeriod={eventView.statsPeriod}
  160. currentFilter={currentFilter}
  161. />
  162. )}
  163. {display === DisplayModes.DURATION_PERCENTILE && (
  164. <DurationPercentileChart
  165. organization={organization}
  166. location={location}
  167. query={eventView.query}
  168. project={eventView.project}
  169. environment={eventView.environment}
  170. start={eventView.start}
  171. end={eventView.end}
  172. statsPeriod={eventView.statsPeriod}
  173. currentFilter={currentFilter}
  174. />
  175. )}
  176. {display === DisplayModes.TREND && (
  177. <TrendChart
  178. trendDisplay={generateTrendFunctionAsString(trendFunction, trendColumn)}
  179. organization={organization}
  180. query={eventView.query}
  181. queryExtra={releaseQueryExtra}
  182. project={eventView.project}
  183. environment={eventView.environment}
  184. start={eventView.start}
  185. end={eventView.end}
  186. statsPeriod={eventView.statsPeriod}
  187. />
  188. )}
  189. {display === DisplayModes.VITALS && (
  190. <VitalsChart
  191. organization={organization}
  192. query={eventView.query}
  193. queryExtra={releaseQueryExtra}
  194. project={eventView.project}
  195. environment={eventView.environment}
  196. start={eventView.start}
  197. end={eventView.end}
  198. statsPeriod={eventView.statsPeriod}
  199. />
  200. )}
  201. </ChartContainer>
  202. <ChartControls>
  203. <InlineContainer>
  204. <SectionHeading key="total-heading">{t('Total Transactions')}</SectionHeading>
  205. <SectionValue key="total-value">
  206. {totalValues === null ? (
  207. <Placeholder height="24px" />
  208. ) : (
  209. totalValues.toLocaleString()
  210. )}
  211. </SectionValue>
  212. </InlineContainer>
  213. <InlineContainer>
  214. {display === DisplayModes.TREND && (
  215. <OptionSelector
  216. title={t('Percentile')}
  217. selected={trendFunction}
  218. options={TREND_FUNCTIONS_OPTIONS}
  219. onChange={this.handleTrendDisplayChange}
  220. />
  221. )}
  222. {display === DisplayModes.TREND && (
  223. <OptionSelector
  224. title={t('Parameter')}
  225. selected={trendColumn}
  226. options={TREND_PARAMETERS_OPTIONS}
  227. onChange={this.handleTrendColumnChange}
  228. />
  229. )}
  230. {display === DisplayModes.LATENCY && (
  231. <LatencyChartControls location={location} />
  232. )}
  233. <OptionSelector
  234. title={t('Display')}
  235. selected={display}
  236. options={generateDisplayOptions(currentFilter)}
  237. onChange={this.handleDisplayChange}
  238. />
  239. </InlineContainer>
  240. </ChartControls>
  241. </Panel>
  242. );
  243. }
  244. }
  245. export default TransactionSummaryCharts;