charts.tsx 9.4 KB

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