durationChart.tsx 5.1 KB

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