chart.tsx 3.7 KB

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