charts.tsx 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. import {browserHistory} from 'react-router';
  2. import {Location} from 'history';
  3. import OptionSelector from 'sentry/components/charts/optionSelector';
  4. import {
  5. ChartContainer,
  6. ChartControls,
  7. InlineContainer,
  8. SectionHeading,
  9. SectionValue,
  10. } from 'sentry/components/charts/styles';
  11. import {Panel} from 'sentry/components/panels';
  12. import Placeholder from 'sentry/components/placeholder';
  13. import {t} from 'sentry/locale';
  14. import {Organization, SelectValue} from 'sentry/types';
  15. import trackAdvancedAnalyticsEvent from 'sentry/utils/analytics/trackAdvancedAnalyticsEvent';
  16. import EventView from 'sentry/utils/discover/eventView';
  17. import {removeHistogramQueryStrings} from 'sentry/utils/performance/histogram';
  18. import {decodeScalar} from 'sentry/utils/queryString';
  19. import {TransactionsListOption} from 'sentry/views/releases/detail/overview';
  20. import {TrendColumnField, TrendFunctionField} from '../../trends/types';
  21. import {TRENDS_FUNCTIONS, TRENDS_PARAMETERS} from '../../trends/utils';
  22. import {SpanOperationBreakdownFilter} from '../filter';
  23. import LatencyChartControls from './latencyChart/chartControls';
  24. import {ZOOM_END, ZOOM_START} from './latencyChart/utils';
  25. import DurationChart from './durationChart';
  26. import DurationPercentileChart from './durationPercentileChart';
  27. import LatencyChart from './latencyChart';
  28. import TrendChart from './trendChart';
  29. import UserMiseryChart from './userMiseryChart';
  30. import VitalsChart from './vitalsChart';
  31. export enum DisplayModes {
  32. DURATION_PERCENTILE = 'durationpercentile',
  33. DURATION = 'duration',
  34. LATENCY = 'latency',
  35. TREND = 'trend',
  36. VITALS = 'vitals',
  37. USER_MISERY = 'usermisery',
  38. }
  39. function generateDisplayOptions(
  40. currentFilter: SpanOperationBreakdownFilter
  41. ): SelectValue<string>[] {
  42. if (currentFilter === SpanOperationBreakdownFilter.None) {
  43. return [
  44. {value: DisplayModes.DURATION, label: t('Duration Breakdown')},
  45. {value: DisplayModes.DURATION_PERCENTILE, label: t('Duration Percentiles')},
  46. {value: DisplayModes.LATENCY, label: t('Duration Distribution')},
  47. {value: DisplayModes.TREND, label: t('Trends')},
  48. {value: DisplayModes.VITALS, label: t('Web Vitals')},
  49. {value: DisplayModes.USER_MISERY, label: t('User Misery')},
  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. currentFilter: SpanOperationBreakdownFilter;
  69. eventView: EventView;
  70. location: Location;
  71. organization: Organization;
  72. totalValues: number | null;
  73. withoutZerofill: boolean;
  74. };
  75. function TransactionSummaryCharts({
  76. totalValues,
  77. eventView,
  78. organization,
  79. location,
  80. currentFilter,
  81. withoutZerofill,
  82. }: Props) {
  83. function handleDisplayChange(value: string) {
  84. const display = decodeScalar(location.query.display, DisplayModes.DURATION);
  85. trackAdvancedAnalyticsEvent(
  86. 'performance_views.transaction_summary.change_chart_display',
  87. {
  88. organization,
  89. from_chart: display,
  90. to_chart: value,
  91. }
  92. );
  93. browserHistory.push({
  94. pathname: location.pathname,
  95. query: {
  96. ...removeHistogramQueryStrings(location, [ZOOM_START, ZOOM_END]),
  97. display: value,
  98. },
  99. });
  100. }
  101. function handleTrendDisplayChange(value: string) {
  102. browserHistory.push({
  103. pathname: location.pathname,
  104. query: {...location.query, trendFunction: value},
  105. });
  106. }
  107. function handleTrendColumnChange(value: string) {
  108. browserHistory.push({
  109. pathname: location.pathname,
  110. query: {...location.query, trendColumn: value},
  111. });
  112. }
  113. const TREND_PARAMETERS_OPTIONS: SelectValue<string>[] = TRENDS_PARAMETERS.map(
  114. ({column, label}) => ({
  115. value: column,
  116. label,
  117. })
  118. );
  119. let display = decodeScalar(location.query.display, DisplayModes.DURATION);
  120. let trendFunction = decodeScalar(
  121. location.query.trendFunction,
  122. TREND_FUNCTIONS_OPTIONS[0].value
  123. ) as TrendFunctionField;
  124. let trendColumn = decodeScalar(
  125. location.query.trendColumn,
  126. TREND_PARAMETERS_OPTIONS[0].value
  127. );
  128. if (!Object.values(DisplayModes).includes(display as DisplayModes)) {
  129. display = DisplayModes.DURATION;
  130. }
  131. if (!Object.values(TrendFunctionField).includes(trendFunction)) {
  132. trendFunction = TrendFunctionField.P50;
  133. }
  134. if (!Object.values(TrendColumnField).includes(trendColumn as TrendColumnField)) {
  135. trendColumn = TrendColumnField.DURATION;
  136. }
  137. const releaseQueryExtra = {
  138. yAxis: display === DisplayModes.VITALS ? 'countVital' : 'countDuration',
  139. showTransactions:
  140. display === DisplayModes.VITALS
  141. ? TransactionsListOption.SLOW_LCP
  142. : display === DisplayModes.DURATION
  143. ? TransactionsListOption.SLOW
  144. : undefined,
  145. };
  146. return (
  147. <Panel>
  148. <ChartContainer data-test-id="transaction-summary-charts">
  149. {display === DisplayModes.LATENCY && (
  150. <LatencyChart
  151. organization={organization}
  152. location={location}
  153. query={eventView.query}
  154. project={eventView.project}
  155. environment={eventView.environment}
  156. start={eventView.start}
  157. end={eventView.end}
  158. statsPeriod={eventView.statsPeriod}
  159. currentFilter={currentFilter}
  160. />
  161. )}
  162. {display === DisplayModes.DURATION && (
  163. <DurationChart
  164. organization={organization}
  165. query={eventView.query}
  166. queryExtra={releaseQueryExtra}
  167. project={eventView.project}
  168. environment={eventView.environment}
  169. start={eventView.start}
  170. end={eventView.end}
  171. statsPeriod={eventView.statsPeriod}
  172. currentFilter={currentFilter}
  173. withoutZerofill={withoutZerofill}
  174. />
  175. )}
  176. {display === DisplayModes.DURATION_PERCENTILE && (
  177. <DurationPercentileChart
  178. organization={organization}
  179. location={location}
  180. query={eventView.query}
  181. project={eventView.project}
  182. environment={eventView.environment}
  183. start={eventView.start}
  184. end={eventView.end}
  185. statsPeriod={eventView.statsPeriod}
  186. currentFilter={currentFilter}
  187. />
  188. )}
  189. {display === DisplayModes.TREND && (
  190. <TrendChart
  191. trendFunction={trendFunction}
  192. trendParameter={trendColumn}
  193. organization={organization}
  194. query={eventView.query}
  195. queryExtra={releaseQueryExtra}
  196. project={eventView.project}
  197. environment={eventView.environment}
  198. start={eventView.start}
  199. end={eventView.end}
  200. statsPeriod={eventView.statsPeriod}
  201. withoutZerofill={withoutZerofill}
  202. />
  203. )}
  204. {display === DisplayModes.VITALS && (
  205. <VitalsChart
  206. organization={organization}
  207. query={eventView.query}
  208. queryExtra={releaseQueryExtra}
  209. project={eventView.project}
  210. environment={eventView.environment}
  211. start={eventView.start}
  212. end={eventView.end}
  213. statsPeriod={eventView.statsPeriod}
  214. withoutZerofill={withoutZerofill}
  215. />
  216. )}
  217. {display === DisplayModes.USER_MISERY && (
  218. <UserMiseryChart
  219. organization={organization}
  220. query={eventView.query}
  221. queryExtra={releaseQueryExtra}
  222. project={eventView.project}
  223. environment={eventView.environment}
  224. start={eventView.start}
  225. end={eventView.end}
  226. statsPeriod={eventView.statsPeriod}
  227. withoutZerofill={withoutZerofill}
  228. />
  229. )}
  230. </ChartContainer>
  231. <ChartControls>
  232. <InlineContainer>
  233. <SectionHeading key="total-heading">{t('Total Transactions')}</SectionHeading>
  234. <SectionValue key="total-value">
  235. {totalValues === null ? (
  236. <Placeholder height="24px" />
  237. ) : (
  238. totalValues.toLocaleString()
  239. )}
  240. </SectionValue>
  241. </InlineContainer>
  242. <InlineContainer>
  243. {display === DisplayModes.TREND && (
  244. <OptionSelector
  245. title={t('Percentile')}
  246. selected={trendFunction}
  247. options={TREND_FUNCTIONS_OPTIONS}
  248. onChange={handleTrendDisplayChange}
  249. />
  250. )}
  251. {display === DisplayModes.TREND && (
  252. <OptionSelector
  253. title={t('Parameter')}
  254. selected={trendColumn}
  255. options={TREND_PARAMETERS_OPTIONS}
  256. onChange={handleTrendColumnChange}
  257. />
  258. )}
  259. {display === DisplayModes.LATENCY && (
  260. <LatencyChartControls location={location} />
  261. )}
  262. <OptionSelector
  263. title={t('Display')}
  264. selected={display}
  265. options={generateDisplayOptions(currentFilter)}
  266. onChange={handleDisplayChange}
  267. />
  268. </InlineContainer>
  269. </ChartControls>
  270. </Panel>
  271. );
  272. }
  273. export default TransactionSummaryCharts;