widgetCardChartContainer.tsx 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. import {Fragment} from 'react';
  2. import {withRouter, WithRouterProps} from 'react-router';
  3. import styled from '@emotion/styled';
  4. import type {DataZoomComponentOption} from 'echarts';
  5. import {LegendComponentOption} from 'echarts';
  6. import {Client} from 'sentry/api';
  7. import TransparentLoadingMask from 'sentry/components/charts/transparentLoadingMask';
  8. import LoadingIndicator from 'sentry/components/loadingIndicator';
  9. import {Organization, PageFilters} from 'sentry/types';
  10. import {EChartEventHandler, Series} from 'sentry/types/echarts';
  11. import {TableDataWithTitle} from 'sentry/utils/discover/discoverQuery';
  12. import {Widget, WidgetType} from '../types';
  13. import WidgetCardChart, {AugmentedEChartDataZoomHandler} from './chart';
  14. import {IssueWidgetCard} from './issueWidgetCard';
  15. import IssueWidgetQueries from './issueWidgetQueries';
  16. import ReleaseWidgetQueries from './releaseWidgetQueries';
  17. import WidgetQueries from './widgetQueries';
  18. type Props = WithRouterProps & {
  19. api: Client;
  20. organization: Organization;
  21. selection: PageFilters;
  22. widget: Widget;
  23. chartZoomOptions?: DataZoomComponentOption;
  24. expandNumbers?: boolean;
  25. isMobile?: boolean;
  26. legendOptions?: LegendComponentOption;
  27. noPadding?: boolean;
  28. onDataFetched?: (results: {
  29. pageLinks?: string;
  30. tableResults?: TableDataWithTitle[];
  31. timeseriesResults?: Series[];
  32. totalIssuesCount?: string;
  33. }) => void;
  34. onLegendSelectChanged?: EChartEventHandler<{
  35. name: string;
  36. selected: Record<string, boolean>;
  37. type: 'legendselectchanged';
  38. }>;
  39. onZoom?: AugmentedEChartDataZoomHandler;
  40. renderErrorMessage?: (errorMessage?: string) => React.ReactNode;
  41. showSlider?: boolean;
  42. tableItemLimit?: number;
  43. windowWidth?: number;
  44. };
  45. export function WidgetCardChartContainer({
  46. location,
  47. router,
  48. api,
  49. organization,
  50. selection,
  51. widget,
  52. isMobile,
  53. renderErrorMessage,
  54. tableItemLimit,
  55. windowWidth,
  56. onZoom,
  57. onLegendSelectChanged,
  58. legendOptions,
  59. expandNumbers,
  60. onDataFetched,
  61. showSlider,
  62. noPadding,
  63. chartZoomOptions,
  64. }: Props) {
  65. if (widget.widgetType === WidgetType.ISSUE) {
  66. return (
  67. <IssueWidgetQueries
  68. api={api}
  69. organization={organization}
  70. widget={widget}
  71. selection={selection}
  72. limit={tableItemLimit}
  73. onDataFetched={onDataFetched}
  74. >
  75. {({tableResults, errorMessage, loading}) => {
  76. return (
  77. <Fragment>
  78. {typeof renderErrorMessage === 'function'
  79. ? renderErrorMessage(errorMessage)
  80. : null}
  81. <LoadingScreen loading={loading} />
  82. <IssueWidgetCard
  83. transformedResults={tableResults?.[0].data ?? []}
  84. loading={loading}
  85. errorMessage={errorMessage}
  86. widget={widget}
  87. organization={organization}
  88. location={location}
  89. selection={selection}
  90. />
  91. </Fragment>
  92. );
  93. }}
  94. </IssueWidgetQueries>
  95. );
  96. }
  97. if (widget.widgetType === WidgetType.RELEASE) {
  98. return (
  99. <ReleaseWidgetQueries
  100. api={api}
  101. organization={organization}
  102. widget={widget}
  103. selection={selection}
  104. limit={widget.limit ?? tableItemLimit}
  105. onDataFetched={onDataFetched}
  106. >
  107. {({tableResults, timeseriesResults, errorMessage, loading}) => {
  108. return (
  109. <Fragment>
  110. {typeof renderErrorMessage === 'function'
  111. ? renderErrorMessage(errorMessage)
  112. : null}
  113. <WidgetCardChart
  114. timeseriesResults={timeseriesResults}
  115. tableResults={tableResults}
  116. errorMessage={errorMessage}
  117. loading={loading}
  118. location={location}
  119. widget={widget}
  120. selection={selection}
  121. router={router}
  122. organization={organization}
  123. isMobile={isMobile}
  124. windowWidth={windowWidth}
  125. expandNumbers={expandNumbers}
  126. onZoom={onZoom}
  127. showSlider={showSlider}
  128. noPadding={noPadding}
  129. chartZoomOptions={chartZoomOptions}
  130. />
  131. </Fragment>
  132. );
  133. }}
  134. </ReleaseWidgetQueries>
  135. );
  136. }
  137. return (
  138. <WidgetQueries
  139. api={api}
  140. organization={organization}
  141. widget={widget}
  142. selection={selection}
  143. limit={tableItemLimit}
  144. onDataFetched={onDataFetched}
  145. >
  146. {({tableResults, timeseriesResults, errorMessage, loading}) => {
  147. return (
  148. <Fragment>
  149. {typeof renderErrorMessage === 'function'
  150. ? renderErrorMessage(errorMessage)
  151. : null}
  152. <WidgetCardChart
  153. timeseriesResults={timeseriesResults}
  154. tableResults={tableResults}
  155. errorMessage={errorMessage}
  156. loading={loading}
  157. location={location}
  158. widget={widget}
  159. selection={selection}
  160. router={router}
  161. organization={organization}
  162. isMobile={isMobile}
  163. windowWidth={windowWidth}
  164. onZoom={onZoom}
  165. onLegendSelectChanged={onLegendSelectChanged}
  166. legendOptions={legendOptions}
  167. expandNumbers={expandNumbers}
  168. showSlider={showSlider}
  169. noPadding={noPadding}
  170. chartZoomOptions={chartZoomOptions}
  171. />
  172. </Fragment>
  173. );
  174. }}
  175. </WidgetQueries>
  176. );
  177. }
  178. export default withRouter(WidgetCardChartContainer);
  179. const StyledTransparentLoadingMask = styled(props => (
  180. <TransparentLoadingMask {...props} maskBackgroundColor="transparent" />
  181. ))`
  182. display: flex;
  183. justify-content: center;
  184. align-items: center;
  185. `;
  186. const LoadingScreen = ({loading}: {loading: boolean}) => {
  187. if (!loading) {
  188. return null;
  189. }
  190. return (
  191. <StyledTransparentLoadingMask visible={loading}>
  192. <LoadingIndicator mini />
  193. </StyledTransparentLoadingMask>
  194. );
  195. };