checkInTimeline.tsx 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. import styled from '@emotion/styled';
  2. import {DateTime} from 'sentry/components/dateTime';
  3. import {Tooltip} from 'sentry/components/tooltip';
  4. import {CheckInStatus} from 'sentry/views/monitors/types';
  5. import {getTickStyle} from 'sentry/views/monitors/utils';
  6. import {getAggregateStatus} from './utils/getAggregateStatus';
  7. import {mergeBuckets} from './utils/mergeBuckets';
  8. import {CheckInTooltip} from './checkInTooltip';
  9. import type {MonitorBucketData, TimeWindowConfig} from './types';
  10. interface TimelineProps {
  11. timeWindowConfig: TimeWindowConfig;
  12. }
  13. export interface CheckInTimelineProps extends TimelineProps {
  14. bucketedData: MonitorBucketData;
  15. environment: string;
  16. }
  17. function getBucketedCheckInsPosition(
  18. timestamp: number,
  19. timelineStart: Date,
  20. msPerPixel: number
  21. ) {
  22. const elapsedSinceStart = new Date(timestamp).getTime() - timelineStart.getTime();
  23. return elapsedSinceStart / msPerPixel;
  24. }
  25. export function CheckInTimeline(props: CheckInTimelineProps) {
  26. const {bucketedData, timeWindowConfig, environment} = props;
  27. const {start, end, timelineWidth} = timeWindowConfig;
  28. const elapsedMs = end.getTime() - start.getTime();
  29. const msPerPixel = elapsedMs / timelineWidth;
  30. const jobTicks = mergeBuckets(bucketedData, environment);
  31. return (
  32. <TimelineContainer>
  33. {jobTicks.map(jobTick => {
  34. const {
  35. startTs,
  36. width: tickWidth,
  37. envMapping,
  38. roundedLeft,
  39. roundedRight,
  40. } = jobTick;
  41. const timestampMs = startTs * 1000;
  42. const left = getBucketedCheckInsPosition(timestampMs, start, msPerPixel);
  43. return (
  44. <CheckInTooltip
  45. jobTick={jobTick}
  46. timeWindowConfig={timeWindowConfig}
  47. skipWrapper
  48. key={startTs}
  49. >
  50. <JobTick
  51. style={{left, width: tickWidth}}
  52. status={getAggregateStatus(envMapping)}
  53. roundedLeft={roundedLeft}
  54. roundedRight={roundedRight}
  55. data-test-id="monitor-checkin-tick"
  56. />
  57. </CheckInTooltip>
  58. );
  59. })}
  60. </TimelineContainer>
  61. );
  62. }
  63. export interface MockCheckInTimelineProps extends TimelineProps {
  64. mockTimestamps: Date[];
  65. }
  66. export function MockCheckInTimeline({
  67. mockTimestamps,
  68. timeWindowConfig,
  69. }: MockCheckInTimelineProps) {
  70. const {start, end} = timeWindowConfig;
  71. const elapsedMs = end.getTime() - start.getTime();
  72. const msPerPixel = elapsedMs / timeWindowConfig.timelineWidth;
  73. return (
  74. <TimelineContainer>
  75. {mockTimestamps.map(ts => {
  76. const timestampMs = ts.getTime();
  77. const left = getBucketedCheckInsPosition(timestampMs, start, msPerPixel);
  78. return (
  79. <Tooltip
  80. key={left}
  81. title={
  82. <DateTime date={timestampMs} format={timeWindowConfig.dateLabelFormat} />
  83. }
  84. skipWrapper
  85. >
  86. <JobTick
  87. style={{left}}
  88. status={CheckInStatus.IN_PROGRESS}
  89. roundedLeft
  90. roundedRight
  91. data-test-id="monitor-checkin-tick"
  92. />
  93. </Tooltip>
  94. );
  95. })}
  96. </TimelineContainer>
  97. );
  98. }
  99. const TimelineContainer = styled('div')`
  100. position: relative;
  101. height: 100%;
  102. `;
  103. const JobTick = styled('div')<{
  104. roundedLeft: boolean;
  105. roundedRight: boolean;
  106. status: CheckInStatus;
  107. }>`
  108. position: absolute;
  109. top: calc(50% + 1px);
  110. width: 4px;
  111. height: 14px;
  112. transform: translateY(-50%);
  113. opacity: 0.7;
  114. ${p => getTickStyle(p.status, p.theme)};
  115. ${p =>
  116. p.roundedLeft &&
  117. `
  118. border-top-left-radius: 2px;
  119. border-bottom-left-radius: 2px;
  120. `};
  121. ${p =>
  122. p.roundedRight &&
  123. `
  124. border-top-right-radius: 2px;
  125. border-bottom-right-radius: 2px;
  126. `}
  127. ${p => !p.roundedLeft && 'border-left-width: 0'};
  128. ${p => !p.roundedRight && 'border-right-width: 0'};
  129. `;