Browse Source

feat(mobile-ui): Render screen charts (#70337)

Adds support for charts based off of top results in the screens table
Nar Saynorath 10 months ago
parent
commit
462a4e89e2

+ 4 - 12
static/app/views/performance/mobile/appStarts/screens/averageComparisonChart.tsx

@@ -11,15 +11,14 @@ import {decodeScalar} from 'sentry/utils/queryString';
 import {MutableSearch} from 'sentry/utils/tokenizeSearch';
 import {MutableSearch} from 'sentry/utils/tokenizeSearch';
 import {useLocation} from 'sentry/utils/useLocation';
 import {useLocation} from 'sentry/utils/useLocation';
 import usePageFilters from 'sentry/utils/usePageFilters';
 import usePageFilters from 'sentry/utils/usePageFilters';
-import {MAX_CHART_RELEASE_CHARS} from 'sentry/views/performance/mobile/appStarts/screens';
 import {COLD_START_TYPE} from 'sentry/views/performance/mobile/appStarts/screenSummary/startTypeSelector';
 import {COLD_START_TYPE} from 'sentry/views/performance/mobile/appStarts/screenSummary/startTypeSelector';
 import {YAxis, YAXIS_COLUMNS} from 'sentry/views/performance/mobile/screenload/screens';
 import {YAxis, YAXIS_COLUMNS} from 'sentry/views/performance/mobile/screenload/screens';
 import {ScreensBarChart} from 'sentry/views/performance/mobile/screenload/screens/screenBarChart';
 import {ScreensBarChart} from 'sentry/views/performance/mobile/screenload/screens/screenBarChart';
 import {useTableQuery} from 'sentry/views/performance/mobile/screenload/screens/screensTable';
 import {useTableQuery} from 'sentry/views/performance/mobile/screenload/screens/screensTable';
+import useTruncatedReleaseNames from 'sentry/views/performance/mobile/useTruncatedRelease';
 import {PRIMARY_RELEASE_COLOR} from 'sentry/views/starfish/colors';
 import {PRIMARY_RELEASE_COLOR} from 'sentry/views/starfish/colors';
 import {useReleaseSelection} from 'sentry/views/starfish/queries/useReleases';
 import {useReleaseSelection} from 'sentry/views/starfish/queries/useReleases';
 import {SpanMetricsField} from 'sentry/views/starfish/types';
 import {SpanMetricsField} from 'sentry/views/starfish/types';
-import {formatVersionAndCenterTruncate} from 'sentry/views/starfish/utils/centerTruncate';
 import {appendReleaseFilters} from 'sentry/views/starfish/utils/releaseComparison';
 import {appendReleaseFilters} from 'sentry/views/starfish/utils/releaseComparison';
 
 
 interface Props {
 interface Props {
@@ -90,14 +89,7 @@ export function AverageComparisonChart({chartHeight}: Props) {
     return transformData(data?.data, appStartType);
     return transformData(data?.data, appStartType);
   }, [data, appStartType]);
   }, [data, appStartType]);
 
 
-  const truncatedPrimaryChart = formatVersionAndCenterTruncate(
-    primaryRelease ?? '',
-    MAX_CHART_RELEASE_CHARS
-  );
-  const truncatedSecondaryChart = formatVersionAndCenterTruncate(
-    secondaryRelease ?? '',
-    MAX_CHART_RELEASE_CHARS
-  );
+  const {truncatedPrimaryRelease, truncatedSecondaryRelease} = useTruncatedReleaseNames();
 
 
   return (
   return (
     <ScreensBarChart
     <ScreensBarChart
@@ -116,8 +108,8 @@ export function AverageComparisonChart({chartHeight}: Props) {
           subtitle: primaryRelease
           subtitle: primaryRelease
             ? t(
             ? t(
                 '%s v. %s',
                 '%s v. %s',
-                truncatedPrimaryChart,
-                secondaryRelease ? truncatedSecondaryChart : ''
+                truncatedPrimaryRelease,
+                secondaryRelease ? truncatedSecondaryRelease : ''
               )
               )
             : '',
             : '',
         },
         },

+ 4 - 12
static/app/views/performance/mobile/appStarts/screens/countChart.tsx

@@ -12,9 +12,9 @@ import {decodeScalar} from 'sentry/utils/queryString';
 import {MutableSearch} from 'sentry/utils/tokenizeSearch';
 import {MutableSearch} from 'sentry/utils/tokenizeSearch';
 import {useLocation} from 'sentry/utils/useLocation';
 import {useLocation} from 'sentry/utils/useLocation';
 import usePageFilters from 'sentry/utils/usePageFilters';
 import usePageFilters from 'sentry/utils/usePageFilters';
-import {MAX_CHART_RELEASE_CHARS} from 'sentry/views/performance/mobile/appStarts/screens';
 import {COLD_START_TYPE} from 'sentry/views/performance/mobile/appStarts/screenSummary/startTypeSelector';
 import {COLD_START_TYPE} from 'sentry/views/performance/mobile/appStarts/screenSummary/startTypeSelector';
 import {OUTPUT_TYPE, YAxis} from 'sentry/views/performance/mobile/screenload/screens';
 import {OUTPUT_TYPE, YAxis} from 'sentry/views/performance/mobile/screenload/screens';
+import useTruncatedReleaseNames from 'sentry/views/performance/mobile/useTruncatedRelease';
 import {
 import {
   PRIMARY_RELEASE_COLOR,
   PRIMARY_RELEASE_COLOR,
   SECONDARY_RELEASE_COLOR,
   SECONDARY_RELEASE_COLOR,
@@ -23,7 +23,6 @@ import Chart, {ChartType} from 'sentry/views/starfish/components/chart';
 import MiniChartPanel from 'sentry/views/starfish/components/miniChartPanel';
 import MiniChartPanel from 'sentry/views/starfish/components/miniChartPanel';
 import {useReleaseSelection} from 'sentry/views/starfish/queries/useReleases';
 import {useReleaseSelection} from 'sentry/views/starfish/queries/useReleases';
 import {SpanMetricsField} from 'sentry/views/starfish/types';
 import {SpanMetricsField} from 'sentry/views/starfish/types';
-import {formatVersionAndCenterTruncate} from 'sentry/views/starfish/utils/centerTruncate';
 import {STARFISH_CHART_INTERVAL_FIDELITY} from 'sentry/views/starfish/utils/constants';
 import {STARFISH_CHART_INTERVAL_FIDELITY} from 'sentry/views/starfish/utils/constants';
 import {appendReleaseFilters} from 'sentry/views/starfish/utils/releaseComparison';
 import {appendReleaseFilters} from 'sentry/views/starfish/utils/releaseComparison';
 import {useEventsStatsQuery} from 'sentry/views/starfish/utils/useEventsStatsQuery';
 import {useEventsStatsQuery} from 'sentry/views/starfish/utils/useEventsStatsQuery';
@@ -107,14 +106,7 @@ export function CountChart({chartHeight}: Props) {
     }
     }
   );
   );
 
 
-  const truncatedPrimaryChart = formatVersionAndCenterTruncate(
-    primaryRelease ?? '',
-    MAX_CHART_RELEASE_CHARS
-  );
-  const truncatedSecondaryChart = formatVersionAndCenterTruncate(
-    secondaryRelease ?? '',
-    MAX_CHART_RELEASE_CHARS
-  );
+  const {truncatedPrimaryRelease, truncatedSecondaryRelease} = useTruncatedReleaseNames();
 
 
   return (
   return (
     <MiniChartPanel
     <MiniChartPanel
@@ -123,8 +115,8 @@ export function CountChart({chartHeight}: Props) {
         primaryRelease
         primaryRelease
           ? t(
           ? t(
               '%s v. %s',
               '%s v. %s',
-              truncatedPrimaryChart,
-              secondaryRelease ? truncatedSecondaryChart : ''
+              truncatedPrimaryRelease,
+              secondaryRelease ? truncatedSecondaryRelease : ''
             )
             )
           : ''
           : ''
       }
       }

+ 5 - 14
static/app/views/performance/mobile/appStarts/screens/index.tsx

@@ -20,22 +20,21 @@ import {AverageComparisonChart} from 'sentry/views/performance/mobile/appStarts/
 import {CountChart} from 'sentry/views/performance/mobile/appStarts/screens/countChart';
 import {CountChart} from 'sentry/views/performance/mobile/appStarts/screens/countChart';
 import {AppStartScreens} from 'sentry/views/performance/mobile/appStarts/screens/screensTable';
 import {AppStartScreens} from 'sentry/views/performance/mobile/appStarts/screens/screensTable';
 import {COLD_START_TYPE} from 'sentry/views/performance/mobile/appStarts/screenSummary/startTypeSelector';
 import {COLD_START_TYPE} from 'sentry/views/performance/mobile/appStarts/screenSummary/startTypeSelector';
+import {TOP_SCREENS} from 'sentry/views/performance/mobile/constants';
 import {
 import {
   getFreeTextFromQuery,
   getFreeTextFromQuery,
-  TOP_SCREENS,
   YAxis,
   YAxis,
   YAXIS_COLUMNS,
   YAXIS_COLUMNS,
 } from 'sentry/views/performance/mobile/screenload/screens';
 } from 'sentry/views/performance/mobile/screenload/screens';
 import {ScreensBarChart} from 'sentry/views/performance/mobile/screenload/screens/screenBarChart';
 import {ScreensBarChart} from 'sentry/views/performance/mobile/screenload/screens/screenBarChart';
 import {useTableQuery} from 'sentry/views/performance/mobile/screenload/screens/screensTable';
 import {useTableQuery} from 'sentry/views/performance/mobile/screenload/screens/screensTable';
 import {transformReleaseEvents} from 'sentry/views/performance/mobile/screenload/screens/utils';
 import {transformReleaseEvents} from 'sentry/views/performance/mobile/screenload/screens/utils';
+import useTruncatedReleaseNames from 'sentry/views/performance/mobile/useTruncatedRelease';
 import {getTransactionSearchQuery} from 'sentry/views/performance/utils';
 import {getTransactionSearchQuery} from 'sentry/views/performance/utils';
 import {useReleaseSelection} from 'sentry/views/starfish/queries/useReleases';
 import {useReleaseSelection} from 'sentry/views/starfish/queries/useReleases';
 import {SpanMetricsField} from 'sentry/views/starfish/types';
 import {SpanMetricsField} from 'sentry/views/starfish/types';
-import {formatVersionAndCenterTruncate} from 'sentry/views/starfish/utils/centerTruncate';
 import {appendReleaseFilters} from 'sentry/views/starfish/utils/releaseComparison';
 import {appendReleaseFilters} from 'sentry/views/starfish/utils/releaseComparison';
 
 
-export const MAX_CHART_RELEASE_CHARS = 12;
 const Y_AXES = [YAxis.COLD_START, YAxis.WARM_START];
 const Y_AXES = [YAxis.COLD_START, YAxis.WARM_START];
 const Y_AXIS_COLS = [YAXIS_COLUMNS[YAxis.COLD_START], YAXIS_COLUMNS[YAxis.WARM_START]];
 const Y_AXIS_COLS = [YAXIS_COLUMNS[YAxis.COLD_START], YAXIS_COLUMNS[YAxis.WARM_START]];
 
 
@@ -57,6 +56,7 @@ function AppStartup({additionalFilters, chartHeight}: Props) {
     secondaryRelease,
     secondaryRelease,
     isLoading: isReleasesLoading,
     isLoading: isReleasesLoading,
   } = useReleaseSelection();
   } = useReleaseSelection();
+  const {truncatedPrimaryRelease, truncatedSecondaryRelease} = useTruncatedReleaseNames();
 
 
   const router = useRouter();
   const router = useRouter();
 
 
@@ -173,15 +173,6 @@ function AppStartup({additionalFilters, chartHeight}: Props) {
     topTransactions,
     topTransactions,
   });
   });
 
 
-  const truncatedPrimaryChart = formatVersionAndCenterTruncate(
-    primaryRelease ?? '',
-    MAX_CHART_RELEASE_CHARS
-  );
-  const truncatedSecondaryChart = formatVersionAndCenterTruncate(
-    secondaryRelease ?? '',
-    MAX_CHART_RELEASE_CHARS
-  );
-
   const countTopScreens = Math.min(TOP_SCREENS, topTransactions.length);
   const countTopScreens = Math.min(TOP_SCREENS, topTransactions.length);
   const [singularTopScreenTitle, pluralTopScreenTitle] =
   const [singularTopScreenTitle, pluralTopScreenTitle] =
     appStartType === COLD_START_TYPE
     appStartType === COLD_START_TYPE
@@ -204,8 +195,8 @@ function AppStartup({additionalFilters, chartHeight}: Props) {
               subtitle: primaryRelease
               subtitle: primaryRelease
                 ? t(
                 ? t(
                     '%s v. %s',
                     '%s v. %s',
-                    truncatedPrimaryChart,
-                    secondaryRelease ? truncatedSecondaryChart : ''
+                    truncatedPrimaryRelease,
+                    secondaryRelease ? truncatedSecondaryRelease : ''
                   )
                   )
                 : '',
                 : '',
             },
             },

+ 1 - 1
static/app/views/performance/mobile/appStarts/screens/screensTable.tsx

@@ -13,7 +13,7 @@ import TopResultsIndicator from 'sentry/views/discover/table/topResultsIndicator
 import Breakdown from 'sentry/views/performance/mobile/appStarts/screens/breakdown';
 import Breakdown from 'sentry/views/performance/mobile/appStarts/screens/breakdown';
 import {COLD_START_TYPE} from 'sentry/views/performance/mobile/appStarts/screenSummary/startTypeSelector';
 import {COLD_START_TYPE} from 'sentry/views/performance/mobile/appStarts/screenSummary/startTypeSelector';
 import {ScreensTable} from 'sentry/views/performance/mobile/components/screensTable';
 import {ScreensTable} from 'sentry/views/performance/mobile/components/screensTable';
-import {TOP_SCREENS} from 'sentry/views/performance/mobile/screenload/screens';
+import {TOP_SCREENS} from 'sentry/views/performance/mobile/constants';
 import {COLD_START_COLOR, WARM_START_COLOR} from 'sentry/views/starfish/colors';
 import {COLD_START_COLOR, WARM_START_COLOR} from 'sentry/views/starfish/colors';
 import {
 import {
   PRIMARY_RELEASE_ALIAS,
   PRIMARY_RELEASE_ALIAS,

+ 2 - 0
static/app/views/performance/mobile/constants.tsx

@@ -0,0 +1,2 @@
+export const TOP_SCREENS = 5;
+export const MAX_CHART_RELEASE_CHARS = 12;

+ 24 - 16
static/app/views/performance/mobile/screenload/screens/index.tsx

@@ -23,6 +23,7 @@ import useOrganization from 'sentry/utils/useOrganization';
 import usePageFilters from 'sentry/utils/usePageFilters';
 import usePageFilters from 'sentry/utils/usePageFilters';
 import useRouter from 'sentry/utils/useRouter';
 import useRouter from 'sentry/utils/useRouter';
 import {prepareQueryForLandingPage} from 'sentry/views/performance/data';
 import {prepareQueryForLandingPage} from 'sentry/views/performance/data';
+import {TOP_SCREENS} from 'sentry/views/performance/mobile/constants';
 import {MobileCursors} from 'sentry/views/performance/mobile/screenload/screens/constants';
 import {MobileCursors} from 'sentry/views/performance/mobile/screenload/screens/constants';
 import {
 import {
   DEFAULT_PLATFORM,
   DEFAULT_PLATFORM,
@@ -40,12 +41,12 @@ import {
   isCrossPlatform,
   isCrossPlatform,
   transformReleaseEvents,
   transformReleaseEvents,
 } from 'sentry/views/performance/mobile/screenload/screens/utils';
 } from 'sentry/views/performance/mobile/screenload/screens/utils';
+import useTruncatedReleaseNames from 'sentry/views/performance/mobile/useTruncatedRelease';
 import {getTransactionSearchQuery} from 'sentry/views/performance/utils';
 import {getTransactionSearchQuery} from 'sentry/views/performance/utils';
 import ChartPanel from 'sentry/views/starfish/components/chartPanel';
 import ChartPanel from 'sentry/views/starfish/components/chartPanel';
 import {useTTFDConfigured} from 'sentry/views/starfish/queries/useHasTtfdConfigured';
 import {useTTFDConfigured} from 'sentry/views/starfish/queries/useHasTtfdConfigured';
 import {useReleaseSelection} from 'sentry/views/starfish/queries/useReleases';
 import {useReleaseSelection} from 'sentry/views/starfish/queries/useReleases';
 import {SpanMetricsField} from 'sentry/views/starfish/types';
 import {SpanMetricsField} from 'sentry/views/starfish/types';
-import {formatVersionAndCenterTruncate} from 'sentry/views/starfish/utils/centerTruncate';
 import {appendReleaseFilters} from 'sentry/views/starfish/utils/releaseComparison';
 import {appendReleaseFilters} from 'sentry/views/starfish/utils/releaseComparison';
 
 
 export enum YAxis {
 export enum YAxis {
@@ -57,11 +58,11 @@ export enum YAxis {
   FROZEN_FRAME_RATE = 5,
   FROZEN_FRAME_RATE = 5,
   THROUGHPUT = 6,
   THROUGHPUT = 6,
   COUNT = 7,
   COUNT = 7,
+  SLOW_FRAMES = 8,
+  FROZEN_FRAMES = 9,
+  FRAMES_DELAY = 10,
 }
 }
 
 
-export const TOP_SCREENS = 5;
-const MAX_CHART_RELEASE_CHARS = 12;
-
 export const YAXIS_COLUMNS: Readonly<Record<YAxis, string>> = {
 export const YAXIS_COLUMNS: Readonly<Record<YAxis, string>> = {
   [YAxis.WARM_START]: 'avg(measurements.app_start_warm)',
   [YAxis.WARM_START]: 'avg(measurements.app_start_warm)',
   [YAxis.COLD_START]: 'avg(measurements.app_start_cold)',
   [YAxis.COLD_START]: 'avg(measurements.app_start_cold)',
@@ -71,6 +72,11 @@ export const YAXIS_COLUMNS: Readonly<Record<YAxis, string>> = {
   [YAxis.FROZEN_FRAME_RATE]: 'avg(measurements.frames_frozen_rate)',
   [YAxis.FROZEN_FRAME_RATE]: 'avg(measurements.frames_frozen_rate)',
   [YAxis.THROUGHPUT]: 'tpm()',
   [YAxis.THROUGHPUT]: 'tpm()',
   [YAxis.COUNT]: 'count()',
   [YAxis.COUNT]: 'count()',
+
+  // Using span metrics
+  [YAxis.SLOW_FRAMES]: 'avg(mobile.slow_frames)',
+  [YAxis.FROZEN_FRAMES]: 'avg(mobile.frozen_frames)',
+  [YAxis.FRAMES_DELAY]: 'avg(mobile.frames_delay)',
 };
 };
 
 
 export const READABLE_YAXIS_LABELS: Readonly<Record<YAxis, string>> = {
 export const READABLE_YAXIS_LABELS: Readonly<Record<YAxis, string>> = {
@@ -82,6 +88,9 @@ export const READABLE_YAXIS_LABELS: Readonly<Record<YAxis, string>> = {
   [YAxis.FROZEN_FRAME_RATE]: 'avg(frames_frozen_rate)',
   [YAxis.FROZEN_FRAME_RATE]: 'avg(frames_frozen_rate)',
   [YAxis.THROUGHPUT]: 'tpm()',
   [YAxis.THROUGHPUT]: 'tpm()',
   [YAxis.COUNT]: 'count()',
   [YAxis.COUNT]: 'count()',
+  [YAxis.SLOW_FRAMES]: 'avg(mobile.slow_frames)',
+  [YAxis.FROZEN_FRAMES]: 'avg(mobile.frozen_frames)',
+  [YAxis.FRAMES_DELAY]: 'avg(mobile.frames_delay)',
 };
 };
 
 
 export const CHART_TITLES: Readonly<Record<YAxis, string>> = {
 export const CHART_TITLES: Readonly<Record<YAxis, string>> = {
@@ -93,6 +102,9 @@ export const CHART_TITLES: Readonly<Record<YAxis, string>> = {
   [YAxis.FROZEN_FRAME_RATE]: t('Frozen Frame Rate'),
   [YAxis.FROZEN_FRAME_RATE]: t('Frozen Frame Rate'),
   [YAxis.THROUGHPUT]: t('Throughput'),
   [YAxis.THROUGHPUT]: t('Throughput'),
   [YAxis.COUNT]: t('Count'),
   [YAxis.COUNT]: t('Count'),
+  [YAxis.SLOW_FRAMES]: t('Slow Frames'),
+  [YAxis.FROZEN_FRAMES]: t('Frozen Frames'),
+  [YAxis.FRAMES_DELAY]: t('Frames Delay'),
 };
 };
 
 
 export const OUTPUT_TYPE: Readonly<Record<YAxis, AggregationOutputType>> = {
 export const OUTPUT_TYPE: Readonly<Record<YAxis, AggregationOutputType>> = {
@@ -104,6 +116,9 @@ export const OUTPUT_TYPE: Readonly<Record<YAxis, AggregationOutputType>> = {
   [YAxis.FROZEN_FRAME_RATE]: 'percentage',
   [YAxis.FROZEN_FRAME_RATE]: 'percentage',
   [YAxis.THROUGHPUT]: 'number',
   [YAxis.THROUGHPUT]: 'number',
   [YAxis.COUNT]: 'number',
   [YAxis.COUNT]: 'number',
+  [YAxis.SLOW_FRAMES]: 'number',
+  [YAxis.FROZEN_FRAMES]: 'number',
+  [YAxis.FRAMES_DELAY]: 'duration',
 };
 };
 
 
 type Props = {
 type Props = {
@@ -135,6 +150,7 @@ export function ScreensView({yAxes, additionalFilters, chartHeight, project}: Pr
     secondaryRelease,
     secondaryRelease,
     isLoading: isReleasesLoading,
     isLoading: isReleasesLoading,
   } = useReleaseSelection();
   } = useReleaseSelection();
+  const {truncatedPrimaryRelease, truncatedSecondaryRelease} = useTruncatedReleaseNames();
 
 
   const router = useRouter();
   const router = useRouter();
 
 
@@ -277,14 +293,6 @@ export function ScreensView({yAxes, additionalFilters, chartHeight, project}: Pr
     topTransactions,
     topTransactions,
   });
   });
 
 
-  const truncatedPrimaryChart = formatVersionAndCenterTruncate(
-    primaryRelease ?? '',
-    MAX_CHART_RELEASE_CHARS
-  );
-  const truncatedSecondaryChart = formatVersionAndCenterTruncate(
-    secondaryRelease ?? '',
-    MAX_CHART_RELEASE_CHARS
-  );
   const derivedQuery = getTransactionSearchQuery(location, tableEventView.query);
   const derivedQuery = getTransactionSearchQuery(location, tableEventView.query);
 
 
   const tableSearchFilters = new MutableSearch(['transaction.op:ui.load']);
   const tableSearchFilters = new MutableSearch(['transaction.op:ui.load']);
@@ -313,8 +321,8 @@ export function ScreensView({yAxes, additionalFilters, chartHeight, project}: Pr
                   subtitle: primaryRelease
                   subtitle: primaryRelease
                     ? t(
                     ? t(
                         '%s v. %s',
                         '%s v. %s',
-                        truncatedPrimaryChart,
-                        secondaryRelease ? truncatedSecondaryChart : ''
+                        truncatedPrimaryRelease,
+                        secondaryRelease ? truncatedSecondaryRelease : ''
                       )
                       )
                     : '',
                     : '',
                 },
                 },
@@ -345,8 +353,8 @@ export function ScreensView({yAxes, additionalFilters, chartHeight, project}: Pr
                     subtitle: primaryRelease
                     subtitle: primaryRelease
                       ? t(
                       ? t(
                           '%s v. %s',
                           '%s v. %s',
-                          truncatedPrimaryChart,
-                          secondaryRelease ? truncatedSecondaryChart : ''
+                          truncatedPrimaryRelease,
+                          secondaryRelease ? truncatedSecondaryRelease : ''
                         )
                         )
                       : '',
                       : '',
                   },
                   },

+ 1 - 1
static/app/views/performance/mobile/screenload/screens/screensTable.tsx

@@ -24,7 +24,7 @@ import usePageFilters from 'sentry/utils/usePageFilters';
 import {normalizeUrl} from 'sentry/utils/withDomainRequired';
 import {normalizeUrl} from 'sentry/utils/withDomainRequired';
 import TopResultsIndicator from 'sentry/views/discover/table/topResultsIndicator';
 import TopResultsIndicator from 'sentry/views/discover/table/topResultsIndicator';
 import type {TableColumn} from 'sentry/views/discover/table/types';
 import type {TableColumn} from 'sentry/views/discover/table/types';
-import {TOP_SCREENS} from 'sentry/views/performance/mobile/screenload/screens';
+import {TOP_SCREENS} from 'sentry/views/performance/mobile/constants';
 import {
 import {
   PRIMARY_RELEASE_ALIAS,
   PRIMARY_RELEASE_ALIAS,
   SECONDARY_RELEASE_ALIAS,
   SECONDARY_RELEASE_ALIAS,

+ 1 - 0
static/app/views/performance/mobile/ui/referrers.tsx

@@ -1,3 +1,4 @@
 export enum Referrer {
 export enum Referrer {
   OVERVIEW_SCREENS_TABLE = 'api.performance.module.ui.screen-table',
   OVERVIEW_SCREENS_TABLE = 'api.performance.module.ui.screen-table',
+  MOBILE_UI_BAR_CHART = 'api.performance.mobile.ui.bar-chart',
 }
 }

+ 72 - 0
static/app/views/performance/mobile/ui/screens/index.spec.tsx

@@ -2,6 +2,7 @@ import {ProjectFixture} from 'sentry-fixture/project';
 
 
 import {render, screen} from 'sentry-test/reactTestingLibrary';
 import {render, screen} from 'sentry-test/reactTestingLibrary';
 
 
+import type {Project} from 'sentry/types';
 import usePageFilters from 'sentry/utils/usePageFilters';
 import usePageFilters from 'sentry/utils/usePageFilters';
 import {Referrer} from 'sentry/views/performance/mobile/ui/referrers';
 import {Referrer} from 'sentry/views/performance/mobile/ui/referrers';
 import {UIScreens} from 'sentry/views/performance/mobile/ui/screens';
 import {UIScreens} from 'sentry/views/performance/mobile/ui/screens';
@@ -16,6 +17,29 @@ jest.mocked(useReleaseSelection).mockReturnValue({
   secondaryRelease: 'com.example.vu.android@2.10.3+42',
   secondaryRelease: 'com.example.vu.android@2.10.3+42',
 });
 });
 
 
+const createMockTablePayload = ({
+  transaction,
+  project,
+}: {
+  project: Project;
+  transaction: string;
+}) => ({
+  'avg_compare(mobile.frames_delay,release,com.example.vu.android@2.10.5,com.example.vu.android@2.10.3+42)':
+    null,
+  'avg_compare(mobile.frozen_frames,release,com.example.vu.android@2.10.5,com.example.vu.android@2.10.3+42)':
+    null,
+  'avg_compare(mobile.slow_frames,release,com.example.vu.android@2.10.5,com.example.vu.android@2.10.3+42)':
+    null,
+  'avg_if(mobile.frames_delay,release,com.example.vu.android@2.10.5)': 0,
+  'avg_if(mobile.frames_delay,release,com.example.vu.android@2.10.3+42)': 0.259326119,
+  'avg_if(mobile.frozen_frames,release,com.example.vu.android@2.10.5)': 0,
+  'avg_if(mobile.frozen_frames,release,com.example.vu.android@2.10.3+42)': 0,
+  'avg_if(mobile.slow_frames,release,com.example.vu.android@2.10.5)': 0,
+  'avg_if(mobile.slow_frames,release,com.example.vu.android@2.10.3+42)': 2,
+  'project.id': project.id,
+  transaction,
+});
+
 describe('Performance Mobile UI Screens', () => {
 describe('Performance Mobile UI Screens', () => {
   const project = ProjectFixture({platform: 'apple-ios'});
   const project = ProjectFixture({platform: 'apple-ios'});
 
 
@@ -83,4 +107,52 @@ describe('Performance Mobile UI Screens', () => {
       })
       })
     );
     );
   });
   });
+
+  it('queries for the correct chart data using the top transactions', async () => {
+    MockApiClient.addMockResponse({
+      url: '/organizations/org-slug/events/',
+      body: {
+        data: [
+          createMockTablePayload({transaction: 'top 1', project}),
+          createMockTablePayload({transaction: 'top 2', project}),
+          createMockTablePayload({transaction: 'top 3', project}),
+          createMockTablePayload({transaction: 'top 4', project}),
+          createMockTablePayload({transaction: 'top 5', project}),
+          createMockTablePayload({transaction: 'top 6', project}), // excluded
+        ],
+      },
+      match: [MockApiClient.matchQuery({referrer: Referrer.OVERVIEW_SCREENS_TABLE})],
+    });
+
+    const chartDataRequest = MockApiClient.addMockResponse({
+      url: '/organizations/org-slug/events/',
+      body: [],
+      match: [MockApiClient.matchQuery({referrer: Referrer.MOBILE_UI_BAR_CHART})],
+    });
+
+    render(<UIScreens />);
+
+    await screen.findByText('top 1');
+
+    screen.getByText('Top 5 Screen Slow Frames');
+    screen.getByText('Top 5 Screen Frozen Frames');
+    screen.getByText('Top 5 Screen Frames Delay');
+
+    expect(chartDataRequest).toHaveBeenCalledWith(
+      '/organizations/org-slug/events/',
+      expect.objectContaining({
+        query: expect.objectContaining({
+          field: [
+            'transaction',
+            'release',
+            'avg(mobile.slow_frames)',
+            'avg(mobile.frozen_frames)',
+            'avg(mobile.frames_delay)',
+          ],
+          query:
+            'release:[com.example.vu.android@2.10.5,com.example.vu.android@2.10.3+42] transaction:["top 1","top 2","top 3","top 4","top 5"]',
+        }),
+      })
+    );
+  });
 });
 });

+ 110 - 7
static/app/views/performance/mobile/ui/screens/index.tsx

@@ -1,28 +1,46 @@
+import {useTheme} from '@emotion/react';
 import styled from '@emotion/styled';
 import styled from '@emotion/styled';
 
 
+import Alert from 'sentry/components/alert';
 import SearchBar from 'sentry/components/performance/searchBar';
 import SearchBar from 'sentry/components/performance/searchBar';
 import {t} from 'sentry/locale';
 import {t} from 'sentry/locale';
 import {space} from 'sentry/styles/space';
 import {space} from 'sentry/styles/space';
 import type {NewQuery} from 'sentry/types';
 import type {NewQuery} from 'sentry/types';
+import {defined} from 'sentry/utils';
 import EventView from 'sentry/utils/discover/eventView';
 import EventView from 'sentry/utils/discover/eventView';
 import {DiscoverDatasets} from 'sentry/utils/discover/types';
 import {DiscoverDatasets} from 'sentry/utils/discover/types';
 import {decodeScalar} from 'sentry/utils/queryString';
 import {decodeScalar} from 'sentry/utils/queryString';
-import {MutableSearch} from 'sentry/utils/tokenizeSearch';
+import {escapeFilterValue, MutableSearch} from 'sentry/utils/tokenizeSearch';
 import {useLocation} from 'sentry/utils/useLocation';
 import {useLocation} from 'sentry/utils/useLocation';
 import useOrganization from 'sentry/utils/useOrganization';
 import useOrganization from 'sentry/utils/useOrganization';
 import usePageFilters from 'sentry/utils/usePageFilters';
 import usePageFilters from 'sentry/utils/usePageFilters';
 import useRouter from 'sentry/utils/useRouter';
 import useRouter from 'sentry/utils/useRouter';
 import {prepareQueryForLandingPage} from 'sentry/views/performance/data';
 import {prepareQueryForLandingPage} from 'sentry/views/performance/data';
-import {getFreeTextFromQuery} from 'sentry/views/performance/mobile/screenload/screens';
+import {TOP_SCREENS} from 'sentry/views/performance/mobile/constants';
+import {
+  getFreeTextFromQuery,
+  YAxis,
+  YAXIS_COLUMNS,
+} from 'sentry/views/performance/mobile/screenload/screens';
 import {useTableQuery} from 'sentry/views/performance/mobile/screenload/screens/screensTable';
 import {useTableQuery} from 'sentry/views/performance/mobile/screenload/screens/screensTable';
+import {transformReleaseEvents} from 'sentry/views/performance/mobile/screenload/screens/utils';
 import {Referrer} from 'sentry/views/performance/mobile/ui/referrers';
 import {Referrer} from 'sentry/views/performance/mobile/ui/referrers';
 import {UIScreensTable} from 'sentry/views/performance/mobile/ui/screens/table';
 import {UIScreensTable} from 'sentry/views/performance/mobile/ui/screens/table';
+import {TopScreensChart} from 'sentry/views/performance/mobile/ui/screens/topScreensChart';
 import {getTransactionSearchQuery} from 'sentry/views/performance/utils';
 import {getTransactionSearchQuery} from 'sentry/views/performance/utils';
 import {useReleaseSelection} from 'sentry/views/starfish/queries/useReleases';
 import {useReleaseSelection} from 'sentry/views/starfish/queries/useReleases';
 import {SpanMetricsField} from 'sentry/views/starfish/types';
 import {SpanMetricsField} from 'sentry/views/starfish/types';
 import {appendReleaseFilters} from 'sentry/views/starfish/utils/releaseComparison';
 import {appendReleaseFilters} from 'sentry/views/starfish/utils/releaseComparison';
 
 
+const Y_AXES = [YAxis.SLOW_FRAMES, YAxis.FROZEN_FRAMES, YAxis.FRAMES_DELAY];
+const Y_AXIS_COLUMNS = [
+  'avg(mobile.slow_frames)',
+  'avg(mobile.frozen_frames)',
+  'avg(mobile.frames_delay)',
+];
+
 export function UIScreens() {
 export function UIScreens() {
+  const theme = useTheme();
   const router = useRouter();
   const router = useRouter();
   const {selection} = usePageFilters();
   const {selection} = usePageFilters();
   const location = useLocation();
   const location = useLocation();
@@ -80,14 +98,91 @@ export function UIScreens() {
     referrer: Referrer.OVERVIEW_SCREENS_TABLE,
     referrer: Referrer.OVERVIEW_SCREENS_TABLE,
   });
   });
 
 
+  const topTransactions =
+    topTransactionsData?.data?.slice(0, 5).map(datum => datum.transaction as string) ??
+    [];
+
+  // TODO: Fill with transaction.op filter
+  const topEventsQuery = new MutableSearch([]);
+
+  const topEventsQueryString = `${appendReleaseFilters(
+    topEventsQuery,
+    primaryRelease,
+    secondaryRelease
+  )} ${
+    topTransactions.length > 0
+      ? escapeFilterValue(
+          `transaction:[${topTransactions.map(name => `"${name}"`).join()}]`
+        )
+      : ''
+  }`.trim();
+
+  const {data: releaseEvents, isLoading: isReleaseEventsLoading} = useTableQuery({
+    eventView: EventView.fromNewQueryWithPageFilters(
+      {
+        name: '',
+        fields: ['transaction', 'release', ...Y_AXIS_COLUMNS],
+        yAxis: Y_AXIS_COLUMNS,
+        query: topEventsQueryString,
+        dataset: DiscoverDatasets.SPANS_METRICS,
+        version: 2,
+      },
+      selection
+    ),
+    enabled: !topTransactionsLoading,
+    referrer: Referrer.MOBILE_UI_BAR_CHART,
+  });
+
+  if (!defined(primaryRelease) && !isReleasesLoading) {
+    return (
+      <Alert type="warning" showIcon>
+        {t(
+          'No screens found on recent releases. Please try a single iOS or Android project, a single environment or a smaller date range.'
+        )}
+      </Alert>
+    );
+  }
+
   // TODO: Add transaction.op:ui.load when collecting begins
   // TODO: Add transaction.op:ui.load when collecting begins
   const tableSearchFilters = new MutableSearch([]);
   const tableSearchFilters = new MutableSearch([]);
 
 
   const derivedQuery = getTransactionSearchQuery(location, tableEventView.query);
   const derivedQuery = getTransactionSearchQuery(location, tableEventView.query);
 
 
+  const transformedReleaseEvents = transformReleaseEvents({
+    yAxes: Y_AXES,
+    primaryRelease,
+    secondaryRelease,
+    colorPalette: theme.charts.getColorPalette(TOP_SCREENS - 2),
+    releaseEvents,
+    topTransactions,
+  });
+
   return (
   return (
-    <div>
-      <StyledSearchBar
+    <Layout>
+      <ChartContainer>
+        <TopScreensChart
+          yAxis={YAXIS_COLUMNS[YAxis.SLOW_FRAMES]}
+          isLoading={isReleaseEventsLoading}
+          chartHeight={200}
+          topTransactions={topTransactions}
+          transformedReleaseEvents={transformedReleaseEvents}
+        />
+        <TopScreensChart
+          yAxis={YAXIS_COLUMNS[YAxis.FROZEN_FRAMES]}
+          isLoading={isReleaseEventsLoading}
+          chartHeight={200}
+          topTransactions={topTransactions}
+          transformedReleaseEvents={transformedReleaseEvents}
+        />
+        <TopScreensChart
+          yAxis={YAXIS_COLUMNS[YAxis.FRAMES_DELAY]}
+          isLoading={isReleaseEventsLoading}
+          chartHeight={200}
+          topTransactions={topTransactions}
+          transformedReleaseEvents={transformedReleaseEvents}
+        />
+      </ChartContainer>
+      <SearchBar
         eventView={tableEventView}
         eventView={tableEventView}
         onSearch={search => {
         onSearch={search => {
           router.push({
           router.push({
@@ -114,10 +209,18 @@ export function UIScreens() {
         isLoading={topTransactionsLoading}
         isLoading={topTransactionsLoading}
         pageLinks={pageLinks}
         pageLinks={pageLinks}
       />
       />
-    </div>
+    </Layout>
   );
   );
 }
 }
 
 
-const StyledSearchBar = styled(SearchBar)`
-  margin-bottom: ${space(1)};
+const Layout = styled('div')`
+  display: flex;
+  flex-direction: column;
+  gap: ${space(1)};
+`;
+
+const ChartContainer = styled('div')`
+  display: grid;
+  grid-template-columns: 33% 33% 33%;
+  gap: ${space(1)};
 `;
 `;

Some files were not shown because too many files changed in this diff