jobTickTooltip.tsx 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. import {Fragment} from 'react';
  2. import styled from '@emotion/styled';
  3. import DateTime from 'sentry/components/dateTime';
  4. import Text from 'sentry/components/text';
  5. import {Tooltip, TooltipProps} from 'sentry/components/tooltip';
  6. import {t} from 'sentry/locale';
  7. import {space} from 'sentry/styles/space';
  8. import {
  9. JobTickData,
  10. TimeWindowOptions,
  11. } from 'sentry/views/monitors/components/overviewTimeline/types';
  12. import {CheckInStatus} from 'sentry/views/monitors/types';
  13. import {getColorsFromStatus, statusToText} from 'sentry/views/monitors/utils';
  14. interface Props extends Omit<TooltipProps, 'title'> {
  15. jobTick: JobTickData;
  16. timeWindowConfig: TimeWindowOptions;
  17. }
  18. export function JobTickTooltip({jobTick, timeWindowConfig, children, ...props}: Props) {
  19. const {startTs, endTs, envMapping} = jobTick;
  20. const {dateLabelFormat} = timeWindowConfig;
  21. const capturedEnvs = Object.keys(envMapping);
  22. const representsSingleJob =
  23. capturedEnvs.length === 1 &&
  24. Object.values(envMapping[capturedEnvs[0]]).reduce((sum, count) => sum + count, 0) ===
  25. 1;
  26. const tooltipTitle = (
  27. <Fragment>
  28. <TooltipTimeLabel>
  29. <DateTime date={startTs * 1000} format={dateLabelFormat} />
  30. {!representsSingleJob && (
  31. <Fragment>
  32. <Text>{'\u2014'}</Text>
  33. <DateTime date={endTs * 1000} format={dateLabelFormat} />
  34. </Fragment>
  35. )}
  36. </TooltipTimeLabel>
  37. <StatusCountContainer>
  38. {/* Visually hidden but kept here for accessibility */}
  39. <HiddenHeader>
  40. <tr>
  41. <td>{t('Status')}</td>
  42. <td>{t('Environment')}</td>
  43. <td>{t('Count')}</td>
  44. </tr>
  45. </HiddenHeader>
  46. <tbody>
  47. {Object.entries(envMapping).map(([envName, statusCounts]) =>
  48. Object.entries(statusCounts).map(
  49. ([status, count]) =>
  50. count > 0 && (
  51. <tr key={`${envName}:${status}`}>
  52. {/* TODO(davidenwang): fix types to remove "as" here */}
  53. <StatusLabel status={status as CheckInStatus}>
  54. {statusToText[status]}
  55. </StatusLabel>
  56. {/* TODO(davidenwang): handle long env names */}
  57. <EnvLabel>{envName}</EnvLabel>
  58. <StatusCount>{count}</StatusCount>
  59. </tr>
  60. )
  61. )
  62. )}
  63. </tbody>
  64. </StatusCountContainer>
  65. </Fragment>
  66. );
  67. return (
  68. <Tooltip title={tooltipTitle} skipWrapper {...props}>
  69. {children}
  70. </Tooltip>
  71. );
  72. }
  73. const StatusCountContainer = styled('table')`
  74. width: 100%;
  75. margin: 0;
  76. `;
  77. const TooltipTimeLabel = styled('div')`
  78. display: flex;
  79. margin-bottom: ${space(0.5)};
  80. justify-content: center;
  81. `;
  82. const HiddenHeader = styled('thead')`
  83. display: block;
  84. overflow: hidden;
  85. height: 0;
  86. width: 0;
  87. `;
  88. const StatusLabel = styled('td')<{status: CheckInStatus}>`
  89. color: ${p => getColorsFromStatus(p.status, p.theme).labelColor};
  90. text-align: left;
  91. `;
  92. const StatusCount = styled('td')`
  93. font-variant-numeric: tabular-nums;
  94. `;
  95. const EnvLabel = styled('td')`
  96. padding: ${space(0.25)} ${space(0.5)};
  97. `;