Skip to content

Commit cdd9396

Browse files
authored
feat(preprod): add cards to build details (#103470)
add top level metrics to build details https://github.com/user-attachments/assets/589b1907-32ea-4bd1-bad3-08b5d2edc10c ### Legal Boilerplate Look, I get it. The entity doing business as "Sentry" was incorporated in the State of Delaware in 2015 as Functional Software, Inc. and is gonna need some rights from me in order to utilize my contributions in this here PR. So here's the deal: I retain all rights, title and interest in and to my contributions, and by keeping this boilerplate intact I confirm that Sentry can use, modify, copy, and redistribute my contributions, under Sentry's choice of terms.
1 parent cdbda20 commit cdd9396

File tree

8 files changed

+303
-173
lines changed

8 files changed

+303
-173
lines changed

static/app/views/preprod/buildComparison/main/BuildComparisonMetricCards.tsx renamed to static/app/views/preprod/buildComparison/main/buildComparisonMetricCards.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ interface ComparisonMetric {
2525
head: number;
2626
icon: ReactNode;
2727
key: string;
28+
labelTooltip: string;
2829
percentageChange: number;
2930
title: string;
3031
}
@@ -47,6 +48,7 @@ export function BuildComparisonMetricCards(props: BuildComparisonMetricCardsProp
4748
key: 'install',
4849
title: labels.installSizeLabel,
4950
icon: <IconCode size="sm" />,
51+
labelTooltip: labels.installSizeDescription,
5052
head: size_metric_diff_item.head_install_size,
5153
base: size_metric_diff_item.base_install_size,
5254
diff:
@@ -63,6 +65,7 @@ export function BuildComparisonMetricCards(props: BuildComparisonMetricCardsProp
6365
key: 'download',
6466
title: labels.downloadSizeLabel,
6567
icon: <IconDownload size="sm" />,
68+
labelTooltip: labels.downloadSizeDescription,
6669
head: size_metric_diff_item.head_download_size,
6770
base: size_metric_diff_item.base_download_size,
6871
diff:
@@ -88,7 +91,12 @@ export function BuildComparisonMetricCards(props: BuildComparisonMetricCardsProp
8891
const {variant, icon} = getTrend(metric.diff);
8992

9093
return (
91-
<MetricCard key={metric.key} icon={metric.icon} label={metric.title}>
94+
<MetricCard
95+
key={metric.key}
96+
icon={metric.icon}
97+
label={metric.title}
98+
labelTooltip={metric.labelTooltip}
99+
>
92100
<Stack gap="xs">
93101
<Flex align="end" gap="sm" wrap="wrap">
94102
<Heading as="h3">{formatBytesBase10(metric.head)}</Heading>

static/app/views/preprod/buildComparison/main/sizeCompareMainContent.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import type RequestError from 'sentry/utils/requestError/requestError';
1717
import {useNavigate} from 'sentry/utils/useNavigate';
1818
import useOrganization from 'sentry/utils/useOrganization';
1919
import {useParams} from 'sentry/utils/useParams';
20-
import {BuildComparisonMetricCards} from 'sentry/views/preprod/buildComparison/main/BuildComparisonMetricCards';
20+
import {BuildComparisonMetricCards} from 'sentry/views/preprod/buildComparison/main/buildComparisonMetricCards';
2121
import {SizeCompareItemDiffTable} from 'sentry/views/preprod/buildComparison/main/sizeCompareItemDiffTable';
2222
import {SizeCompareSelectedBuilds} from 'sentry/views/preprod/buildComparison/main/sizeCompareSelectedBuilds';
2323
import {BuildError} from 'sentry/views/preprod/components/buildError';

static/app/views/preprod/buildDetails/main/buildDetailsMainContent.tsx

Lines changed: 71 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import {useCallback} from 'react';
2+
import {useSearchParams} from 'react-router-dom';
13
import styled from '@emotion/styled';
24

35
import {Alert} from '@sentry/scraps/alert';
@@ -13,6 +15,7 @@ import {t} from 'sentry/locale';
1315
import type {UseApiQueryResult} from 'sentry/utils/queryClient';
1416
import type RequestError from 'sentry/utils/requestError/requestError';
1517
import {useQueryParamState} from 'sentry/utils/url/useQueryParamState';
18+
import {BuildDetailsMetricCards} from 'sentry/views/preprod/buildDetails/main/buildDetailsMetricCards';
1619
import {AppSizeInsights} from 'sentry/views/preprod/buildDetails/main/insights/appSizeInsights';
1720
import {BuildError} from 'sentry/views/preprod/components/buildError';
1821
import {BuildProcessing} from 'sentry/views/preprod/components/buildProcessing';
@@ -52,6 +55,12 @@ export function BuildDetailsMainContent(props: BuildDetailsMainContentProps) {
5255
isError: isAppSizeError,
5356
error: appSizeError,
5457
} = appSizeQuery;
58+
const [searchParams, setSearchParams] = useSearchParams();
59+
const openInsightsSidebar = useCallback(() => {
60+
const next = new URLSearchParams(searchParams);
61+
next.set('insights', 'open');
62+
setSearchParams(next);
63+
}, [searchParams, setSearchParams]);
5564

5665
// If the main data fetch fails, this component will not be rendered
5766
// so we don't handle 'isBuildDetailsError'.
@@ -107,13 +116,21 @@ export function BuildDetailsMainContent(props: BuildDetailsMainContentProps) {
107116

108117
if (isLoadingRequests) {
109118
return (
110-
<Stack gap="lg" width="100%">
111-
<Flex width="100%" justify="between" align="center" gap="md">
112-
<Placeholder width="92px" height="40px" />
113-
<Placeholder style={{flex: 1}} height="40px" />
119+
<Stack gap="xl" minHeight="700px" width="100%">
120+
<Flex gap="lg" wrap="wrap">
121+
<Placeholder style={{flex: 1}} height="100px" />
122+
<Placeholder style={{flex: 1}} height="100px" />
123+
<Placeholder style={{flex: 1}} height="100px" />
114124
</Flex>
115-
<Placeholder width="100%" height="540px" />
116-
<Placeholder height="140px" />
125+
<Stack gap="sm">
126+
<Flex width="100%" justify="between" align="center" gap="md">
127+
<Placeholder width="92px" height="40px" />
128+
<Placeholder style={{flex: 1}} height="40px" />
129+
</Flex>
130+
<Placeholder width="100%" height="540px" />
131+
<Placeholder height="140px" />
132+
</Stack>
133+
<Placeholder height="200px" />
117134
</Stack>
118135
);
119136
}
@@ -204,7 +221,6 @@ export function BuildDetailsMainContent(props: BuildDetailsMainContentProps) {
204221
appSizeData.insights && totalSize > 0
205222
? processInsights(appSizeData.insights, totalSize)
206223
: [];
207-
208224
const categoriesEnabled =
209225
appSizeData.treemap.category_breakdown &&
210226
Object.keys(appSizeData.treemap.category_breakdown).length > 0;
@@ -283,55 +299,63 @@ export function BuildDetailsMainContent(props: BuildDetailsMainContentProps) {
283299
}
284300

285301
return (
286-
<Flex direction="column" gap="sm" minHeight="700px" width="100%">
287-
<Flex align="center" gap="md">
288-
{categoriesEnabled && (
289-
<SegmentedControl value={selectedContent} onChange={handleContentChange}>
290-
<SegmentedControl.Item key="treemap" icon={<IconGrid />} />
291-
<SegmentedControl.Item key="categories" icon={<IconGraphCircle />} />
292-
</SegmentedControl>
293-
)}
294-
{selectedContent === 'treemap' && (
295-
<InputGroup style={{flexGrow: 1}}>
296-
<InputGroup.LeadingItems>
297-
<IconSearch />
298-
</InputGroup.LeadingItems>
299-
<InputGroup.Input
300-
placeholder="Search files"
301-
value={searchQuery || ''}
302-
onChange={e => setSearchQuery(e.target.value || undefined)}
303-
/>
304-
{searchQuery && (
305-
<InputGroup.TrailingItems>
306-
<Button
307-
onClick={() => setSearchQuery(undefined)}
308-
aria-label="Clear search"
309-
borderless
310-
size="zero"
311-
>
312-
<IconClose size="sm" />
313-
</Button>
314-
</InputGroup.TrailingItems>
315-
)}
316-
</InputGroup>
317-
)}
318-
</Flex>
319-
<ChartContainer>{visualizationContent}</ChartContainer>
302+
<Stack gap="xl" minHeight="700px" width="100%">
303+
<BuildDetailsMetricCards
304+
sizeInfo={sizeInfo}
305+
processedInsights={processedInsights}
306+
totalSize={totalSize}
307+
platform={buildDetailsData?.app_info?.platform ?? null}
308+
onOpenInsightsSidebar={openInsightsSidebar}
309+
/>
320310

321-
<Stack gap="xl">
311+
<Stack gap="sm">
312+
<Flex align="center" gap="md">
313+
{categoriesEnabled && (
314+
<SegmentedControl value={selectedContent} onChange={handleContentChange}>
315+
<SegmentedControl.Item key="treemap" icon={<IconGrid />} />
316+
<SegmentedControl.Item key="categories" icon={<IconGraphCircle />} />
317+
</SegmentedControl>
318+
)}
319+
{selectedContent === 'treemap' && (
320+
<InputGroup style={{flexGrow: 1}}>
321+
<InputGroup.LeadingItems>
322+
<IconSearch />
323+
</InputGroup.LeadingItems>
324+
<InputGroup.Input
325+
placeholder="Search files"
326+
value={searchQuery || ''}
327+
onChange={e => setSearchQuery(e.target.value || undefined)}
328+
/>
329+
{searchQuery && (
330+
<InputGroup.TrailingItems>
331+
<Button
332+
onClick={() => setSearchQuery(undefined)}
333+
aria-label="Clear search"
334+
borderless
335+
size="zero"
336+
>
337+
<IconClose size="sm" />
338+
</Button>
339+
</InputGroup.TrailingItems>
340+
)}
341+
</InputGroup>
342+
)}
343+
</Flex>
344+
<ChartContainer>{visualizationContent}</ChartContainer>
322345
{selectedContent === 'treemap' && appSizeData && (
323346
<AppSizeLegend
324347
root={appSizeData.treemap.root}
325348
selectedCategories={selectedCategories}
326349
onToggleCategory={handleToggleCategory}
327350
/>
328351
)}
329-
<AppSizeInsights
330-
processedInsights={processedInsights}
331-
platform={validatedPlatform(buildDetailsData?.app_info?.platform)}
332-
/>
333352
</Stack>
334-
</Flex>
353+
354+
<AppSizeInsights
355+
processedInsights={processedInsights}
356+
platform={validatedPlatform(buildDetailsData?.app_info?.platform)}
357+
/>
358+
</Stack>
335359
);
336360
}
337361

0 commit comments

Comments
 (0)