summaryTable.tsx 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. import {Fragment} from 'react';
  2. import styled from '@emotion/styled';
  3. import {t} from 'sentry/locale';
  4. import {space} from 'sentry/styles/space';
  5. import {formatMetricsUsingUnitAndOp, getNameFromMRI} from 'sentry/utils/metrics';
  6. import {Series} from 'sentry/views/ddm/metricsExplorer';
  7. export function SummaryTable({
  8. series,
  9. operation,
  10. onClick,
  11. }: {
  12. onClick: (seriesName: string) => void;
  13. series: Series[];
  14. operation?: string;
  15. }) {
  16. return (
  17. <SummaryTableWrapper>
  18. <HeaderCell />
  19. <HeaderCell>{t('Name')}</HeaderCell>
  20. <HeaderCell>{t('Avg')}</HeaderCell>
  21. <HeaderCell>{t('Min')}</HeaderCell>
  22. <HeaderCell>{t('Max')}</HeaderCell>
  23. <HeaderCell>{t('Sum')}</HeaderCell>
  24. {series.map(({seriesName, color, hidden, unit, data}) => {
  25. const {avg, min, max, sum} = getValues(data);
  26. return (
  27. <Fragment key={seriesName}>
  28. <FlexCell onClick={() => onClick(seriesName)} hidden={hidden}>
  29. <ColorDot color={color} />
  30. </FlexCell>
  31. <Cell onClick={() => onClick(seriesName)}>{getNameFromMRI(seriesName)}</Cell>
  32. {/* TODO(ddm): Add a tooltip with the full value, don't add on click in case users want to copy the value */}
  33. <Cell>{formatMetricsUsingUnitAndOp(avg, unit, operation)}</Cell>
  34. <Cell>{formatMetricsUsingUnitAndOp(min, unit, operation)}</Cell>
  35. <Cell>{formatMetricsUsingUnitAndOp(max, unit, operation)}</Cell>
  36. <Cell>{formatMetricsUsingUnitAndOp(sum, unit, operation)}</Cell>
  37. </Fragment>
  38. );
  39. })}
  40. </SummaryTableWrapper>
  41. );
  42. }
  43. function getValues(seriesData: Series['data']) {
  44. if (!seriesData) {
  45. return {min: null, max: null, avg: null, sum: null};
  46. }
  47. const res = seriesData.reduce(
  48. (acc, {value}) => {
  49. if (value === null) {
  50. return acc;
  51. }
  52. acc.min = Math.min(acc.min, value);
  53. acc.max = Math.max(acc.max, value);
  54. acc.sum += value;
  55. return acc;
  56. },
  57. {min: Infinity, max: -Infinity, sum: 0}
  58. );
  59. return {...res, avg: res.sum / seriesData.length};
  60. }
  61. // TODO(ddm): PanelTable component proved to be a bit too opinionated for this use case,
  62. // so we're using a custom styled component instead. Figure out what we want to do here
  63. const SummaryTableWrapper = styled(`div`)`
  64. display: grid;
  65. grid-template-columns: 0.5fr 8fr 1fr 1fr 1fr 1fr;
  66. `;
  67. // TODO(ddm): This is a copy of PanelTableHeader, try to figure out how to reuse it
  68. const HeaderCell = styled('div')`
  69. color: ${p => p.theme.subText};
  70. font-size: ${p => p.theme.fontSizeSmall};
  71. font-weight: 600;
  72. text-transform: uppercase;
  73. border-radius: ${p => p.theme.borderRadius} ${p => p.theme.borderRadius} 0 0;
  74. line-height: 1;
  75. display: flex;
  76. flex-direction: column;
  77. justify-content: center;
  78. padding: ${space(0.5)};
  79. `;
  80. const Cell = styled('div')`
  81. padding: ${space(0.25)};
  82. :hover {
  83. cursor: ${p => (p.onClick ? 'pointer' : 'default')};
  84. }
  85. `;
  86. const FlexCell = styled(Cell)`
  87. display: flex;
  88. justify-content: center;
  89. align-items: center;
  90. opacity: ${p => (p.hidden ? 0.5 : 1)};
  91. `;
  92. const ColorDot = styled(`div`)`
  93. background-color: ${p => p.color};
  94. border-radius: 50%;
  95. width: ${space(1)};
  96. height: ${space(1)};
  97. `;