chart.tsx 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. import {Fragment} from 'react';
  2. import {useTheme} from '@emotion/react';
  3. import BaseChart from 'sentry/components/charts/baseChart';
  4. import {t} from 'sentry/locale';
  5. import type {Project} from 'sentry/types/project';
  6. import {axisLabelFormatter} from 'sentry/utils/discover/charts';
  7. import NoEvents from './noEvents';
  8. type BaseChartProps = React.ComponentProps<typeof BaseChart>;
  9. type Props = {
  10. firstEvent: boolean;
  11. stats: Project['stats'];
  12. transactionStats?: Project['transactionStats'];
  13. };
  14. function Chart({firstEvent, stats, transactionStats}: Props) {
  15. const series: BaseChartProps['series'] = [];
  16. const hasTransactions = transactionStats !== undefined;
  17. const theme = useTheme();
  18. if (transactionStats) {
  19. const transactionSeries = transactionStats.map(([timestamp, value]) => [
  20. timestamp * 1000,
  21. value,
  22. ]);
  23. series.push({
  24. cursor: 'normal' as const,
  25. name: t('Transactions'),
  26. type: 'bar',
  27. data: transactionSeries,
  28. barMinHeight: 1,
  29. xAxisIndex: 1,
  30. yAxisIndex: 1,
  31. itemStyle: {
  32. color: theme.gray200,
  33. opacity: 0.8,
  34. },
  35. emphasis: {
  36. itemStyle: {
  37. color: theme.gray200,
  38. opacity: 1.0,
  39. },
  40. },
  41. });
  42. }
  43. if (stats) {
  44. series.push({
  45. cursor: 'normal' as const,
  46. name: t('Errors'),
  47. type: 'bar',
  48. data: stats.map(([timestamp, value]) => [timestamp * 1000, value]),
  49. barMinHeight: 1,
  50. xAxisIndex: 0,
  51. yAxisIndex: 0,
  52. itemStyle: {
  53. color: theme.purple300,
  54. opacity: 0.6,
  55. },
  56. emphasis: {
  57. itemStyle: {
  58. color: theme.purple300,
  59. opacity: 0.8,
  60. },
  61. },
  62. });
  63. }
  64. const grid = hasTransactions
  65. ? [
  66. {
  67. top: 10,
  68. bottom: 60,
  69. left: 2,
  70. right: 2,
  71. },
  72. {
  73. top: 105,
  74. bottom: 0,
  75. left: 2,
  76. right: 2,
  77. },
  78. ]
  79. : [
  80. {
  81. top: 10,
  82. bottom: 0,
  83. left: 2,
  84. right: 2,
  85. },
  86. ];
  87. const chartOptions = {
  88. series,
  89. colors: [],
  90. height: 150,
  91. isGroupedByDate: true,
  92. showTimeInTooltip: true,
  93. grid,
  94. tooltip: {
  95. trigger: 'axis' as const,
  96. },
  97. xAxes: Array.from(new Array(series.length)).map((_i, index) => ({
  98. gridIndex: index,
  99. axisLine: {
  100. show: false,
  101. },
  102. axisTick: {
  103. show: false,
  104. },
  105. axisLabel: {
  106. show: false,
  107. },
  108. axisPointer: {
  109. type: 'line' as const,
  110. label: {
  111. show: false,
  112. },
  113. lineStyle: {
  114. width: 0,
  115. },
  116. },
  117. })),
  118. yAxes: Array.from(new Array(series.length)).map((_i, index) => ({
  119. gridIndex: index,
  120. interval: Infinity,
  121. max(value: {max: number}) {
  122. // This keeps small datasets from looking 'scary'
  123. // by having full bars for < 10 values.
  124. return Math.max(10, value.max);
  125. },
  126. axisLabel: {
  127. margin: 2,
  128. showMaxLabel: true,
  129. showMinLabel: false,
  130. color: theme.chartLabel,
  131. fontFamily: theme.text.family,
  132. inside: true,
  133. lineHeight: 12,
  134. formatter: (value: number) => axisLabelFormatter(value, 'number', true),
  135. textBorderColor: theme.backgroundSecondary,
  136. textBorderWidth: 1,
  137. },
  138. splitLine: {
  139. show: false,
  140. },
  141. zlevel: theme.zIndex.header,
  142. })),
  143. axisPointer: {
  144. // Link each x-axis together.
  145. link: [{xAxisIndex: [0, 1]}],
  146. },
  147. options: {
  148. animation: false,
  149. },
  150. };
  151. return (
  152. <Fragment>
  153. <BaseChart {...chartOptions} />
  154. {!firstEvent && <NoEvents seriesCount={series.length} />}
  155. </Fragment>
  156. );
  157. }
  158. export default Chart;