checkInTimeline.tsx 3.9 KB

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