groupStatusChart.tsx 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. import {useMemo} from 'react';
  2. import styled from '@emotion/styled';
  3. import MarkLine from 'sentry/components/charts/components/markLine';
  4. import MiniBarChart from 'sentry/components/charts/miniBarChart';
  5. import {LazyRender} from 'sentry/components/lazyRender';
  6. import Placeholder from 'sentry/components/placeholder';
  7. import {t} from 'sentry/locale';
  8. import type {TimeseriesValue} from 'sentry/types';
  9. import type {Series} from 'sentry/types/echarts';
  10. import {formatAbbreviatedNumber} from 'sentry/utils/formatters';
  11. import theme from 'sentry/utils/theme';
  12. function asChartPoint(point: [number, number]): {name: number | string; value: number} {
  13. return {
  14. name: point[0] * 1000,
  15. value: point[1],
  16. };
  17. }
  18. const EMPTY_STATS: ReadonlyArray<TimeseriesValue> = [];
  19. type Props = {
  20. stats: ReadonlyArray<TimeseriesValue>;
  21. groupStatus?: string;
  22. height?: number;
  23. hideZeros?: boolean;
  24. loading?: boolean;
  25. secondaryStats?: ReadonlyArray<TimeseriesValue>;
  26. showMarkLine?: boolean;
  27. showSecondaryPoints?: boolean;
  28. };
  29. function GroupStatusChart({
  30. stats,
  31. groupStatus,
  32. height = 24,
  33. loading = false,
  34. hideZeros = false,
  35. secondaryStats = EMPTY_STATS,
  36. showMarkLine = false,
  37. showSecondaryPoints = false,
  38. }: Props) {
  39. const graphOptions = useMemo<{
  40. colors: [string] | undefined;
  41. emphasisColors: [string] | undefined;
  42. series: Series[];
  43. }>(() => {
  44. if (!stats || !stats.length) {
  45. return {colors: undefined, emphasisColors: undefined, series: []};
  46. }
  47. const max = Math.max(...stats.map(p => p[1]));
  48. const formattedMarkLine = formatAbbreviatedNumber(max);
  49. if (showSecondaryPoints && secondaryStats && secondaryStats.length) {
  50. const series: Series[] = [
  51. {
  52. seriesName: t('Total Events'),
  53. data: secondaryStats.map(asChartPoint),
  54. },
  55. {
  56. seriesName: t('Matching Events'),
  57. data: stats.map(asChartPoint),
  58. },
  59. ];
  60. return {colors: undefined, emphasisColors: undefined, series};
  61. }
  62. const series: Series[] = [
  63. {
  64. seriesName: t('Events'),
  65. data: stats.map(asChartPoint),
  66. markLine:
  67. showMarkLine && max > 0
  68. ? MarkLine({
  69. silent: true,
  70. lineStyle: {
  71. color: theme.gray200,
  72. type: [4, 3], // Sets line type to "dashed" with 4 length and 3 gap
  73. opacity: 0.6,
  74. cap: 'round', // Rounded edges for the dashes
  75. },
  76. data: [
  77. {
  78. type: 'max',
  79. },
  80. ],
  81. animation: false,
  82. label: {
  83. show: true,
  84. position: 'end',
  85. opacity: 1,
  86. color: `${theme.gray300}`,
  87. fontFamily: 'Rubik',
  88. fontSize: 10,
  89. formatter: `${formattedMarkLine}`,
  90. },
  91. })
  92. : undefined,
  93. },
  94. ];
  95. return {colors: [theme.gray300], emphasisColors: [theme.gray300], series};
  96. }, [showSecondaryPoints, secondaryStats, showMarkLine, stats]);
  97. return (
  98. <LazyRender containerHeight={showMarkLine ? 26 : height}>
  99. <ChartWrapper>
  100. {loading ? (
  101. <Placeholder height={'36px'} />
  102. ) : (
  103. <ChartAnimationWrapper>
  104. <MiniBarChart
  105. animateBars
  106. showXAxisLine
  107. hideZeros={hideZeros}
  108. markLineLabelSide="right"
  109. barOpacity={0.4}
  110. height={showMarkLine ? 36 : height}
  111. isGroupedByDate
  112. showTimeInTooltip
  113. series={graphOptions.series}
  114. colors={graphOptions.colors}
  115. emphasisColors={graphOptions.emphasisColors}
  116. hideDelay={50}
  117. showMarkLineLabel={showMarkLine}
  118. />
  119. </ChartAnimationWrapper>
  120. )}
  121. <GraphText>{groupStatus}</GraphText>
  122. </ChartWrapper>
  123. </LazyRender>
  124. );
  125. }
  126. export default GroupStatusChart;
  127. const ChartAnimationWrapper = styled('div')`
  128. animation: fade-in 0.5s;
  129. @keyframes fade-in {
  130. from {
  131. opacity: 0;
  132. }
  133. to {
  134. opacity: 100;
  135. }
  136. }
  137. `;
  138. const ChartWrapper = styled('div')`
  139. display: flex;
  140. flex-direction: column;
  141. `;
  142. const GraphText = styled('div')`
  143. font-size: ${p => p.theme.fontSizeSmall};
  144. color: ${p => p.theme.gray300};
  145. `;