deviceClassBreakdownBarChart.tsx 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. import {BarChart} from 'sentry/components/charts/barChart';
  2. import ErrorPanel from 'sentry/components/charts/errorPanel';
  3. import TransitionChart from 'sentry/components/charts/transitionChart';
  4. import {getInterval} from 'sentry/components/charts/utils';
  5. import {IconWarning} from 'sentry/icons';
  6. import {t} from 'sentry/locale';
  7. import {space} from 'sentry/styles/space';
  8. import {
  9. axisLabelFormatter,
  10. getDurationUnit,
  11. tooltipFormatter,
  12. } from 'sentry/utils/discover/charts';
  13. import EventView from 'sentry/utils/discover/eventView';
  14. import {aggregateOutputType} from 'sentry/utils/discover/fields';
  15. import {DiscoverDatasets} from 'sentry/utils/discover/types';
  16. import {formatVersion} from 'sentry/utils/formatters';
  17. import {decodeScalar} from 'sentry/utils/queryString';
  18. import {MutableSearch} from 'sentry/utils/tokenizeSearch';
  19. import {useLocation} from 'sentry/utils/useLocation';
  20. import usePageFilters from 'sentry/utils/usePageFilters';
  21. import {prepareQueryForLandingPage} from 'sentry/views/performance/data';
  22. import {COLD_START_TYPE} from 'sentry/views/performance/mobile/appStarts/screenSummary/startTypeSelector';
  23. import {YAxis, YAXIS_COLUMNS} from 'sentry/views/performance/mobile/screenload/screens';
  24. import {useTableQuery} from 'sentry/views/performance/mobile/screenload/screens/screensTable';
  25. import {transformDeviceClassEvents} from 'sentry/views/performance/mobile/screenload/screens/utils';
  26. import {
  27. PRIMARY_RELEASE_COLOR,
  28. SECONDARY_RELEASE_COLOR,
  29. } from 'sentry/views/starfish/colors';
  30. import {LoadingScreen} from 'sentry/views/starfish/components/chart';
  31. import MiniChartPanel from 'sentry/views/starfish/components/miniChartPanel';
  32. import {useReleaseSelection} from 'sentry/views/starfish/queries/useReleases';
  33. import {SpanMetricsField} from 'sentry/views/starfish/types';
  34. import {formatVersionAndCenterTruncate} from 'sentry/views/starfish/utils/centerTruncate';
  35. import {STARFISH_CHART_INTERVAL_FIDELITY} from 'sentry/views/starfish/utils/constants';
  36. import {appendReleaseFilters} from 'sentry/views/starfish/utils/releaseComparison';
  37. const YAXES = [YAxis.COLD_START, YAxis.WARM_START];
  38. const XAXIS_CATEGORIES = ['high', 'medium', 'low', 'Unknown'];
  39. interface DeviceClassBreakdownBarChartProps {
  40. additionalFilters?: string[];
  41. chartHeight?: number;
  42. }
  43. function DeviceClassBreakdownBarChart({
  44. chartHeight,
  45. additionalFilters,
  46. }: DeviceClassBreakdownBarChartProps) {
  47. const pageFilter = usePageFilters();
  48. const location = useLocation();
  49. const {query: locationQuery} = location;
  50. const {
  51. primaryRelease,
  52. secondaryRelease,
  53. isLoading: isReleasesLoading,
  54. } = useReleaseSelection();
  55. const startType =
  56. decodeScalar(location.query[SpanMetricsField.APP_START_TYPE]) ?? COLD_START_TYPE;
  57. const yAxis =
  58. YAXIS_COLUMNS[startType === COLD_START_TYPE ? YAxis.COLD_START : YAxis.WARM_START];
  59. const query = new MutableSearch([...(additionalFilters ?? [])]);
  60. const searchQuery = decodeScalar(locationQuery.query, '');
  61. if (searchQuery) {
  62. query.addStringFilter(prepareQueryForLandingPage(searchQuery, false));
  63. }
  64. const queryString = appendReleaseFilters(query, primaryRelease, secondaryRelease);
  65. const {
  66. data: startupDataByDeviceClass,
  67. isLoading,
  68. isError,
  69. } = useTableQuery({
  70. eventView: EventView.fromNewQueryWithPageFilters(
  71. {
  72. name: '',
  73. fields: [
  74. startType === COLD_START_TYPE
  75. ? 'avg(measurements.app_start_cold)'
  76. : 'avg(measurements.app_start_warm)',
  77. 'device.class',
  78. 'release',
  79. ],
  80. yAxis: YAXES.map(val => YAXIS_COLUMNS[val]),
  81. query: queryString,
  82. dataset: DiscoverDatasets.METRICS,
  83. version: 2,
  84. interval: getInterval(
  85. pageFilter.selection.datetime,
  86. STARFISH_CHART_INTERVAL_FIDELITY
  87. ),
  88. },
  89. pageFilter.selection
  90. ),
  91. enabled: !isReleasesLoading,
  92. referrer: 'api.starfish.mobile-startup-bar-chart',
  93. initialData: {data: []},
  94. });
  95. const transformedData = transformDeviceClassEvents({
  96. data: startupDataByDeviceClass,
  97. yAxes: YAXES,
  98. primaryRelease,
  99. secondaryRelease,
  100. });
  101. const data = Object.values(
  102. transformedData[
  103. YAXIS_COLUMNS[startType === COLD_START_TYPE ? YAxis.COLD_START : YAxis.WARM_START]
  104. ]
  105. );
  106. return (
  107. <MiniChartPanel
  108. title={
  109. startType === COLD_START_TYPE
  110. ? t('Cold Start Device Distribution')
  111. : t('Warm Start Device Distribution')
  112. }
  113. subtitle={
  114. primaryRelease
  115. ? t(
  116. '%s v. %s',
  117. formatVersionAndCenterTruncate(primaryRelease, 12),
  118. secondaryRelease ? formatVersionAndCenterTruncate(secondaryRelease, 12) : ''
  119. )
  120. : ''
  121. }
  122. >
  123. <TransitionChart
  124. loading={isLoading || isReleasesLoading}
  125. reloading={isLoading || isReleasesLoading}
  126. height={`${chartHeight}px`}
  127. >
  128. <LoadingScreen loading={Boolean(isLoading)} />
  129. {isError ? (
  130. <ErrorPanel height={`${chartHeight}px`}>
  131. <IconWarning color="gray300" size="lg" />
  132. </ErrorPanel>
  133. ) : (
  134. <BarChart
  135. legend={{
  136. show: true,
  137. right: 12,
  138. }}
  139. height={chartHeight}
  140. colors={[PRIMARY_RELEASE_COLOR, SECONDARY_RELEASE_COLOR]}
  141. series={
  142. data.map(series => ({
  143. ...series,
  144. data: series.data.map(datum =>
  145. datum.value !== 0
  146. ? {
  147. ...datum,
  148. itemStyle: {
  149. color:
  150. series.seriesName === primaryRelease
  151. ? PRIMARY_RELEASE_COLOR
  152. : SECONDARY_RELEASE_COLOR,
  153. },
  154. }
  155. : datum
  156. ),
  157. name: formatVersion(series.seriesName),
  158. })) ?? []
  159. }
  160. grid={{
  161. left: '0',
  162. right: '0',
  163. top: space(2),
  164. bottom: '0',
  165. containLabel: true,
  166. }}
  167. xAxis={{
  168. type: 'category',
  169. axisTick: {show: true},
  170. data: XAXIS_CATEGORIES,
  171. truncate: 14,
  172. axisLabel: {
  173. interval: 0,
  174. },
  175. }}
  176. yAxis={{
  177. axisLabel: {
  178. formatter(value: number) {
  179. return axisLabelFormatter(
  180. value,
  181. aggregateOutputType(yAxis),
  182. undefined,
  183. getDurationUnit(data ?? [])
  184. );
  185. },
  186. },
  187. }}
  188. tooltip={{
  189. valueFormatter: (value, _seriesName) => {
  190. return tooltipFormatter(value, aggregateOutputType(yAxis));
  191. },
  192. }}
  193. />
  194. )}
  195. </TransitionChart>
  196. </MiniChartPanel>
  197. );
  198. }
  199. export default DeviceClassBreakdownBarChart;