durationChart.tsx 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. import styled from '@emotion/styled';
  2. import ErrorPanel from 'sentry/components/charts/errorPanel';
  3. import EventsRequest from 'sentry/components/charts/eventsRequest';
  4. import {HeaderTitleLegend} from 'sentry/components/charts/styles';
  5. import TransparentLoadingMask from 'sentry/components/charts/transparentLoadingMask';
  6. import {getInterval} from 'sentry/components/charts/utils';
  7. import {normalizeDateTimeParams} from 'sentry/components/organizations/pageFilters/parse';
  8. import Placeholder from 'sentry/components/placeholder';
  9. import QuestionTooltip from 'sentry/components/questionTooltip';
  10. import {IconWarning} from 'sentry/icons';
  11. import {space} from 'sentry/styles/space';
  12. import type {Organization} from 'sentry/types/organization';
  13. import {getUtcToLocalDateObject} from 'sentry/utils/dates';
  14. import type EventView from 'sentry/utils/discover/eventView';
  15. import getDynamicText from 'sentry/utils/getDynamicText';
  16. import useApi from 'sentry/utils/useApi';
  17. import {useLocation} from 'sentry/utils/useLocation';
  18. import Chart from '../../charts/chart';
  19. import {DoubleHeaderContainer} from '../../styles';
  20. import {getFieldOrBackup} from '../display/utils';
  21. type Props = {
  22. eventView: EventView;
  23. field: string;
  24. organization: Organization;
  25. title: string;
  26. titleTooltip: string;
  27. usingBackupAxis: boolean;
  28. backupField?: string;
  29. };
  30. function DurationChart({
  31. organization,
  32. eventView,
  33. field,
  34. title,
  35. titleTooltip,
  36. backupField,
  37. usingBackupAxis,
  38. }: Props) {
  39. const location = useLocation();
  40. const api = useApi();
  41. // construct request parameters for fetching chart data
  42. const globalSelection = eventView.getPageFilters();
  43. const start = globalSelection.datetime.start
  44. ? getUtcToLocalDateObject(globalSelection.datetime.start)
  45. : null;
  46. const end = globalSelection.datetime.end
  47. ? getUtcToLocalDateObject(globalSelection.datetime.end)
  48. : null;
  49. const {utc} = normalizeDateTimeParams(location.query);
  50. const _backupField = backupField ? [backupField] : [];
  51. const apiPayload = eventView.getEventsAPIPayload(location);
  52. return (
  53. <EventsRequest
  54. organization={organization}
  55. api={api}
  56. period={globalSelection.datetime.period}
  57. project={globalSelection.projects}
  58. environment={globalSelection.environments}
  59. team={apiPayload.team}
  60. start={start}
  61. end={end}
  62. interval={getInterval(
  63. {
  64. start,
  65. end,
  66. period: globalSelection.datetime.period,
  67. },
  68. 'high'
  69. )}
  70. showLoading={false}
  71. query={apiPayload.query}
  72. includePrevious={false}
  73. yAxis={[field, ..._backupField]}
  74. partial
  75. hideError
  76. referrer="api.performance.homepage.duration-chart"
  77. >
  78. {({
  79. loading,
  80. reloading,
  81. errored,
  82. timeseriesData: singleAxisResults,
  83. results: multiAxisResults,
  84. }) => {
  85. const _field = usingBackupAxis ? getFieldOrBackup(field, backupField) : field;
  86. const results = singleAxisResults
  87. ? singleAxisResults
  88. : [multiAxisResults?.find(r => r.seriesName === _field)].filter(Boolean);
  89. const series = results
  90. ? results.map(({...rest}) => {
  91. return {
  92. ...rest,
  93. seriesName: _field,
  94. };
  95. })
  96. : [];
  97. if (errored) {
  98. return (
  99. <ErrorPanel>
  100. <IconWarning color="gray300" size="lg" />
  101. </ErrorPanel>
  102. );
  103. }
  104. return (
  105. <div>
  106. <DoubleHeaderContainer>
  107. <HeaderTitleLegend>
  108. {title}
  109. <QuestionTooltip position="top" size="sm" title={titleTooltip} />
  110. </HeaderTitleLegend>
  111. </DoubleHeaderContainer>
  112. {results && (
  113. <ChartContainer>
  114. <MaskContainer>
  115. <TransparentLoadingMask visible={loading} />
  116. {getDynamicText({
  117. value: (
  118. <Chart
  119. height={250}
  120. data={series}
  121. loading={loading || reloading}
  122. statsPeriod={globalSelection.datetime.period}
  123. start={start}
  124. end={end}
  125. utc={utc === 'true'}
  126. grid={{
  127. left: space(3),
  128. right: space(3),
  129. top: space(3),
  130. bottom: loading || reloading ? space(4) : space(1.5),
  131. }}
  132. disableMultiAxis
  133. />
  134. ),
  135. fixed: <Placeholder height="250px" testId="skeleton-ui" />,
  136. })}
  137. </MaskContainer>
  138. </ChartContainer>
  139. )}
  140. </div>
  141. );
  142. }}
  143. </EventsRequest>
  144. );
  145. }
  146. const ChartContainer = styled('div')`
  147. padding-top: ${space(1)};
  148. `;
  149. const MaskContainer = styled('div')`
  150. position: relative;
  151. `;
  152. export default DurationChart;