breakdown.tsx 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. import styled from '@emotion/styled';
  2. import {RowRectangle} from 'sentry/components/performance/waterfall/rowBar';
  3. import {Tooltip} from 'sentry/components/tooltip';
  4. import {space} from 'sentry/styles/space';
  5. import toPercent from 'sentry/utils/number/toPercent';
  6. import toRoundedPercent from 'sentry/utils/number/toRoundedPercent';
  7. interface Row {
  8. [index: string]: number | undefined;
  9. }
  10. interface BreakdownGroup {
  11. color: string;
  12. key: string;
  13. name: string;
  14. }
  15. export function TooltipContents({
  16. row,
  17. total,
  18. breakdownGroups,
  19. }: {
  20. breakdownGroups: BreakdownGroup[];
  21. row: Row;
  22. total: number;
  23. }) {
  24. return (
  25. <TooltipContentWrapper data-test-id="breakdown-tooltip-content">
  26. {breakdownGroups.map(({key, color, name}) => (
  27. <StartupType key={key}>
  28. <StartupNameContainer>
  29. <StartupDot style={{backgroundColor: color}} />
  30. <StartupName>{name}</StartupName>
  31. </StartupNameContainer>
  32. <StartupCount>{row[key] ?? 0}</StartupCount>
  33. {toRoundedPercent((row[key] ?? 0) / total)}
  34. </StartupType>
  35. ))}
  36. </TooltipContentWrapper>
  37. );
  38. }
  39. function Breakdown({
  40. row,
  41. breakdownGroups,
  42. ['data-test-id']: dataTestId,
  43. }: {
  44. breakdownGroups: BreakdownGroup[];
  45. row: Row;
  46. ['data-test-id']?: string;
  47. }) {
  48. const total = breakdownGroups.reduce((acc, {key}) => acc + (row?.[key] ?? 0), 0);
  49. if (total === 0) {
  50. return null;
  51. }
  52. return (
  53. <Tooltip
  54. title={
  55. <TooltipContents row={row} total={total} breakdownGroups={breakdownGroups} />
  56. }
  57. >
  58. <RelativeOpsBreakdown data-test-id={dataTestId}>
  59. {breakdownGroups.map(({key, color}) => (
  60. <div
  61. key={key}
  62. style={{
  63. width: toPercent((row[key] ?? 0) / total),
  64. }}
  65. >
  66. <RectangleRelativeOpsBreakdown
  67. style={{
  68. backgroundColor: color,
  69. }}
  70. />
  71. </div>
  72. ))}
  73. </RelativeOpsBreakdown>
  74. </Tooltip>
  75. );
  76. }
  77. export default Breakdown;
  78. const RelativeOpsBreakdown = styled('div')`
  79. position: relative;
  80. display: flex;
  81. `;
  82. const RectangleRelativeOpsBreakdown = styled(RowRectangle)`
  83. position: relative;
  84. width: 100%;
  85. `;
  86. const StartupDot = styled('div')`
  87. content: '';
  88. display: block;
  89. width: 8px;
  90. min-width: 8px;
  91. height: 8px;
  92. margin-right: ${space(1)};
  93. border-radius: 100%;
  94. `;
  95. const OpsContent = styled('div')`
  96. display: flex;
  97. align-items: center;
  98. `;
  99. const StartupNameContainer = styled(OpsContent)`
  100. overflow: hidden;
  101. `;
  102. const StartupType = styled('div')`
  103. display: flex;
  104. justify-content: space-between;
  105. gap: ${space(2)};
  106. `;
  107. const StartupCount = styled('div')`
  108. color: ${p => p.theme.gray300};
  109. `;
  110. const StartupName = styled('div')`
  111. white-space: nowrap;
  112. overflow: hidden;
  113. text-overflow: ellipsis;
  114. `;
  115. const TooltipContentWrapper = styled('div')`
  116. display: flex;
  117. flex-direction: column;
  118. gap: ${space(1)};
  119. `;