Browse Source

feat(insights): Use `GlobalDrawer` instead of detail panels (#82534)

Closes https://github.com/getsentry/sentry/issues/81273

Replaces all Insights side panels with new `GlobalDrawer`
implementations that match the drawers in Issues. The visual changes are
minimal

1. Slightly different animations
2. A "Close" button on the top left of the drawer
3. Scroll bars on the left of the drawer
4. Tighter padding
5. Panel doesn't re-animate as often on clicking another transaction

There are small feature changes!

- no more docking bottom or right. Docking is to the right only
- panels now have closing animations

## Code Changes

`GlobalDrawer` is invoked with a hook, rather than rendered inline. So,
I had to make some changes.

**Before:**

- rendering panels mostly unconditionally like `<CacheSamplesPanel />`
- each sample panel checks the URL to see if it's supposed to be open
- if open, renders itself, otherwise stays invisible
- on close, updates the URL, which re-renders and hides

This is problematic for a bunch of reasons:

1. When components can decide to not render themselves, the code flow is
confusing. Parents should generally decide when to render children
2. When a component is rendered but hidden, it runs its side effects.
This means that in our case, network requests were sometimes going out,
even though the panel isn't visible

**After:**

- the panel is rendered using a `useEffect`, the effect detects a
necessary URL parameter or state, and open the panel if necessary. All
conditions that the panel used to check for itself are pulled up into
the parent
- the panel is rendered straight, it doesn't have any conditionals to
check if it's supposed to be open
- on close, the panel hides itself, and tells the parent to update the
URL or state as necessary

This is tidier, avoids component side-effects, and preserves closing
animations! Plus, this means I can re-use a header component, make the
analytics tracking consistent, etc.

---

This only leaves a small handful of users of `DetailPanel`, mostly in
the trace view.
George Gritsouk 2 months ago
parent
commit
81e09187e9

+ 23 - 12
static/app/views/insights/browser/resources/views/resourceSummaryPage.tsx

@@ -1,4 +1,4 @@
-import React from 'react';
+import React, {useEffect} from 'react';
 
 import * as Layout from 'sentry/components/layouts/thirds';
 import {t, tct} from 'sentry/locale';
@@ -23,6 +23,7 @@ import {ModuleBodyUpsellHook} from 'sentry/views/insights/common/components/modu
 import {ToolRibbon} from 'sentry/views/insights/common/components/ribbon';
 import {useSpanMetrics} from 'sentry/views/insights/common/queries/useDiscover';
 import {useModuleURL} from 'sentry/views/insights/common/utils/useModuleURL';
+import {useSamplesDrawer} from 'sentry/views/insights/common/utils/useSamplesDrawer';
 import SubregionSelector from 'sentry/views/insights/common/views/spans/selectors/subregionSelector';
 import {SampleList} from 'sentry/views/insights/common/views/spanSummaryPage/sampleList';
 import {FrontendHeader} from 'sentry/views/insights/pages/frontend/frontendPageHeader';
@@ -47,6 +48,27 @@ function ResourceSummary() {
   const {
     query: {transaction},
   } = useLocation();
+
+  const {openSamplesDrawer} = useSamplesDrawer({
+    Component: (
+      <SampleList
+        transactionRoute={webVitalsModuleURL}
+        subregions={filters[SpanMetricsField.USER_GEO_SUBREGION]}
+        groupId={groupId!}
+        moduleName={ModuleName.RESOURCE}
+        transactionName={transaction as string}
+        referrer={TraceViewSources.ASSETS_MODULE}
+      />
+    ),
+    moduleName: ModuleName.RESOURCE,
+  });
+
+  useEffect(() => {
+    if (transaction) {
+      openSamplesDrawer();
+    }
+  });
+
   const {data, isPending} = useSpanMetrics(
     {
       search: MutableSearch.fromQueryObject({
@@ -139,17 +161,6 @@ function ResourceSummary() {
               <ModuleLayout.Full>
                 <ResourceSummaryTable />
               </ModuleLayout.Full>
-
-              <ModuleLayout.Full>
-                <SampleList
-                  transactionRoute={webVitalsModuleURL}
-                  subregions={filters[SpanMetricsField.USER_GEO_SUBREGION]}
-                  groupId={groupId!}
-                  moduleName={ModuleName.RESOURCE}
-                  transactionName={transaction as string}
-                  referrer={TraceViewSources.ASSETS_MODULE}
-                />
-              </ModuleLayout.Full>
             </ModuleLayout.Layout>
           </Layout.Main>
         </Layout.Body>

+ 6 - 5
static/app/views/insights/browser/webVitals/components/pageOverviewWebVitalsDetailPanel.tsx

@@ -2,6 +2,7 @@ import {useMemo} from 'react';
 import styled from '@emotion/styled';
 
 import type {LineChartSeries} from 'sentry/components/charts/lineChart';
+import {DrawerHeader} from 'sentry/components/globalDrawer/components';
 import type {
   GridColumnHeader,
   GridColumnOrder,
@@ -38,7 +39,7 @@ import type {
 } from 'sentry/views/insights/browser/webVitals/types';
 import decodeBrowserTypes from 'sentry/views/insights/browser/webVitals/utils/queryParameterDecoders/browserType';
 import useProfileExists from 'sentry/views/insights/browser/webVitals/utils/useProfileExists';
-import DetailPanel from 'sentry/views/insights/common/components/detailPanel';
+import {SampleDrawerBody} from 'sentry/views/insights/common/components/sampleDrawerBody';
 import {SpanIndexedField, type SubregionCode} from 'sentry/views/insights/types';
 import {TraceViewSources} from 'sentry/views/performance/newTraceDetails/traceHeader/breadcrumbs';
 import {generateReplayLink} from 'sentry/views/performance/transactionSummary/utils';
@@ -77,9 +78,7 @@ const inpSort: GridColumnSortBy<keyof InteractionSpanSampleRowWithScore> = {
 
 export function PageOverviewWebVitalsDetailPanel({
   webVital,
-  onClose,
 }: {
-  onClose: () => void;
   webVital: WebVitals | null;
 }) {
   const location = useLocation();
@@ -367,7 +366,9 @@ export function PageOverviewWebVitalsDetailPanel({
 
   return (
     <PageAlertProvider>
-      <DetailPanel detailKey={webVital ?? undefined} onClose={onClose}>
+      <DrawerHeader />
+
+      <SampleDrawerBody>
         {webVital && (
           <WebVitalDetailHeader
             value={
@@ -410,7 +411,7 @@ export function PageOverviewWebVitalsDetailPanel({
           )}
         </TableContainer>
         <PageAlert />
-      </DetailPanel>
+      </SampleDrawerBody>
     </PageAlertProvider>
   );
 }

+ 0 - 1
static/app/views/insights/browser/webVitals/components/webVitalDescription.tsx

@@ -273,7 +273,6 @@ const Value = styled('h2')`
 
 const WebVitalName = styled('h4')`
   margin-bottom: ${space(1)};
-  margin-top: 40px;
   max-width: 400px;
   ${p => p.theme.overflowEllipsis}
 `;

+ 1 - 1
static/app/views/insights/browser/webVitals/components/webVitalsDetailPanel.spec.tsx

@@ -58,7 +58,7 @@ describe('WebVitalsDetailPanel', function () {
   });
 
   it('renders correctly with empty results', async () => {
-    render(<WebVitalsDetailPanel onClose={() => undefined} webVital="lcp" />, {
+    render(<WebVitalsDetailPanel webVital="lcp" />, {
       organization,
     });
     await waitForElementToBeRemoved(() => screen.queryByTestId('loading-indicator'));

+ 7 - 12
static/app/views/insights/browser/webVitals/components/webVitalsDetailPanel.tsx

@@ -2,6 +2,7 @@ import {useEffect, useMemo} from 'react';
 import styled from '@emotion/styled';
 
 import type {LineChartSeries} from 'sentry/components/charts/lineChart';
+import {DrawerHeader} from 'sentry/components/globalDrawer/components';
 import type {
   GridColumnHeader,
   GridColumnOrder,
@@ -34,7 +35,7 @@ import type {
   WebVitals,
 } from 'sentry/views/insights/browser/webVitals/types';
 import decodeBrowserTypes from 'sentry/views/insights/browser/webVitals/utils/queryParameterDecoders/browserType';
-import DetailPanel from 'sentry/views/insights/common/components/detailPanel';
+import {SampleDrawerBody} from 'sentry/views/insights/common/components/sampleDrawerBody';
 import {SpanIndexedField, type SubregionCode} from 'sentry/views/insights/types';
 
 type Column = GridColumnHeader;
@@ -51,13 +52,7 @@ const sort: GridColumnSortBy<keyof Row> = {key: 'count()', order: 'desc'};
 
 const MAX_ROWS = 10;
 
-export function WebVitalsDetailPanel({
-  webVital,
-  onClose,
-}: {
-  onClose: () => void;
-  webVital: WebVitals | null;
-}) {
+export function WebVitalsDetailPanel({webVital}: {webVital: WebVitals | null}) {
   const location = useLocation();
   const organization = useOrganization();
   const browserTypes = decodeBrowserTypes(location.query[SpanIndexedField.BROWSER_NAME]);
@@ -133,8 +128,6 @@ export function WebVitalsDetailPanel({
     seriesName: webVital ?? '',
   };
 
-  const detailKey = webVital;
-
   useEffect(() => {
     if (webVital !== null) {
       trackAnalytics('insight.vital.vital_sidebar_opened', {
@@ -243,7 +236,9 @@ export function WebVitalsDetailPanel({
 
   return (
     <PageAlertProvider>
-      <DetailPanel detailKey={detailKey ?? undefined} onClose={onClose}>
+      <DrawerHeader />
+
+      <SampleDrawerBody>
         {webVital && (
           <WebVitalDescription
             value={
@@ -274,7 +269,7 @@ export function WebVitalsDetailPanel({
           />
         </TableContainer>
         <PageAlert />
-      </DetailPanel>
+      </SampleDrawerBody>
     </PageAlertProvider>
   );
 }

+ 21 - 11
static/app/views/insights/browser/webVitals/views/pageOverview.tsx

@@ -1,4 +1,4 @@
-import React, {Fragment, useMemo, useState} from 'react';
+import React, {Fragment, useEffect, useMemo, useState} from 'react';
 import styled from '@emotion/styled';
 import omit from 'lodash/omit';
 
@@ -32,6 +32,7 @@ import {ModulePageFilterBar} from 'sentry/views/insights/common/components/modul
 import {ModulePageProviders} from 'sentry/views/insights/common/components/modulePageProviders';
 import {ModuleBodyUpsellHook} from 'sentry/views/insights/common/components/moduleUpsellHookWrapper';
 import {useModuleURL} from 'sentry/views/insights/common/utils/useModuleURL';
+import {useWebVitalsDrawer} from 'sentry/views/insights/common/utils/useWebVitalsDrawer';
 import {FrontendHeader} from 'sentry/views/insights/pages/frontend/frontendPageHeader';
 import {useDomainViewFilters} from 'sentry/views/insights/pages/useFilters';
 import {
@@ -106,6 +107,25 @@ export function PageOverview() {
   const {data: projectScores, isPending: isProjectScoresLoading} =
     useProjectWebVitalsScoresQuery({transaction, browserTypes, subregions});
 
+  const {openVitalsDrawer} = useWebVitalsDrawer({
+    Component: <PageOverviewWebVitalsDetailPanel webVital={state.webVital} />,
+    webVital: state.webVital,
+    onClose: () => {
+      router.replace({
+        pathname: router.location.pathname,
+        query: omit(router.location.query, 'webVital'),
+      });
+
+      setState({...state, webVital: null});
+    },
+  });
+
+  useEffect(() => {
+    if (state.webVital) {
+      openVitalsDrawer();
+    }
+  });
+
   if (transaction === undefined) {
     // redirect user to webvitals landing page
     window.location.href = moduleURL;
@@ -239,16 +259,6 @@ export function PageOverview() {
             </Layout.Body>
           )}
         </ModuleBodyUpsellHook>
-        <PageOverviewWebVitalsDetailPanel
-          webVital={state.webVital}
-          onClose={() => {
-            router.replace({
-              pathname: router.location.pathname,
-              query: omit(router.location.query, 'webVital'),
-            });
-            setState({...state, webVital: null});
-          }}
-        />
       </Tabs>
     </React.Fragment>
   );

+ 16 - 15
static/app/views/insights/browser/webVitals/views/webVitalsLandingPage.tsx

@@ -1,6 +1,5 @@
-import React, {Fragment, useState} from 'react';
+import React, {Fragment, useEffect, useState} from 'react';
 import styled from '@emotion/styled';
-import omit from 'lodash/omit';
 
 import Alert from 'sentry/components/alert';
 import {Button} from 'sentry/components/button';
@@ -12,7 +11,6 @@ import {t, tct} from 'sentry/locale';
 import {space} from 'sentry/styles/space';
 import {decodeList} from 'sentry/utils/queryString';
 import {useLocation} from 'sentry/utils/useLocation';
-import useRouter from 'sentry/utils/useRouter';
 import BrowserTypeSelector from 'sentry/views/insights/browser/webVitals/components/browserTypeSelector';
 import {PerformanceScoreChart} from 'sentry/views/insights/browser/webVitals/components/charts/performanceScoreChart';
 import {PagePerformanceTable} from 'sentry/views/insights/browser/webVitals/components/tables/pagePerformanceTable';
@@ -32,6 +30,7 @@ import {ModulePageFilterBar} from 'sentry/views/insights/common/components/modul
 import {ModulePageProviders} from 'sentry/views/insights/common/components/modulePageProviders';
 import {ModulesOnboarding} from 'sentry/views/insights/common/components/modulesOnboarding';
 import {ModuleBodyUpsellHook} from 'sentry/views/insights/common/components/moduleUpsellHookWrapper';
+import {useWebVitalsDrawer} from 'sentry/views/insights/common/utils/useWebVitalsDrawer';
 import {FrontendHeader} from 'sentry/views/insights/pages/frontend/frontendPageHeader';
 import {
   ModuleName,
@@ -44,8 +43,6 @@ const WEB_VITALS_COUNT = 5;
 export function WebVitalsLandingPage() {
   const location = useLocation();
 
-  const router = useRouter();
-
   const [state, setState] = useState<{webVital: WebVitals | null}>({
     webVital: (location.query.webVital as WebVitals) ?? null,
   });
@@ -67,6 +64,20 @@ export function WebVitalsLandingPage() {
       ? undefined
       : getWebVitalScoresFromTableDataRow(projectScores?.data?.[0]);
 
+  const {openVitalsDrawer} = useWebVitalsDrawer({
+    Component: <WebVitalsDetailPanel webVital={state.webVital} />,
+    webVital: state.webVital,
+    onClose: () => {
+      setState({webVital: null});
+    },
+  });
+
+  useEffect(() => {
+    if (state.webVital) {
+      openVitalsDrawer();
+    }
+  });
+
   return (
     <React.Fragment>
       <FrontendHeader
@@ -147,16 +158,6 @@ export function WebVitalsLandingPage() {
           </Layout.Main>
         </Layout.Body>
       </ModuleBodyUpsellHook>
-      <WebVitalsDetailPanel
-        webVital={state.webVital}
-        onClose={() => {
-          router.replace({
-            pathname: router.location.pathname,
-            query: omit(router.location.query, 'webVital'),
-          });
-          setState({...state, webVital: null});
-        }}
-      />
     </React.Fragment>
   );
 }

+ 14 - 94
static/app/views/insights/cache/components/samplePanel.tsx

@@ -1,21 +1,16 @@
-import {Fragment, useCallback} from 'react';
-import styled from '@emotion/styled';
+import {Fragment} from 'react';
 import keyBy from 'lodash/keyBy';
-import * as qs from 'query-string';
 
-import ProjectAvatar from 'sentry/components/avatar/projectAvatar';
 import {Button} from 'sentry/components/button';
 import {CompactSelect} from 'sentry/components/compactSelect';
-import Link from 'sentry/components/links/link';
+import {DrawerHeader} from 'sentry/components/globalDrawer/components';
 import {SpanSearchQueryBuilder} from 'sentry/components/performance/spanSearchQueryBuilder';
 import {t} from 'sentry/locale';
-import {space} from 'sentry/styles/space';
 import {trackAnalytics} from 'sentry/utils/analytics';
 import {DurationUnit, RateUnit, SizeUnit} from 'sentry/utils/discover/fields';
 import {PageAlertProvider} from 'sentry/utils/performance/contexts/pageAlert';
 import {decodeScalar} from 'sentry/utils/queryString';
 import {MutableSearch} from 'sentry/utils/tokenizeSearch';
-import normalizeUrl from 'sentry/utils/url/normalizeUrl';
 import useLocationQuery from 'sentry/utils/url/useLocationQuery';
 import {useLocation} from 'sentry/utils/useLocation';
 import {useNavigate} from 'sentry/utils/useNavigate';
@@ -27,7 +22,6 @@ import {TransactionDurationChart} from 'sentry/views/insights/cache/components/c
 import {SpanSamplesTable} from 'sentry/views/insights/cache/components/tables/spanSamplesTable';
 import {Referrer} from 'sentry/views/insights/cache/referrers';
 import {BASE_FILTERS} from 'sentry/views/insights/cache/settings';
-import DetailPanel from 'sentry/views/insights/common/components/detailPanel';
 import {MetricReadout} from 'sentry/views/insights/common/components/metricReadout';
 import * as ModuleLayout from 'sentry/views/insights/common/components/moduleLayout';
 import {ReadoutRibbon} from 'sentry/views/insights/common/components/ribbon';
@@ -45,7 +39,6 @@ import {
   getThroughputTitle,
 } from 'sentry/views/insights/common/views/spans/types';
 import {useDebouncedState} from 'sentry/views/insights/http/utils/useDebouncedState';
-import {useDomainViewFilters} from 'sentry/views/insights/pages/useFilters';
 import {
   MetricsFields,
   type MetricsQueryFilters,
@@ -57,7 +50,9 @@ import {
   SpanMetricsField,
   type SpanMetricsQueryFilters,
 } from 'sentry/views/insights/types';
-import {getTransactionSummaryBaseUrl} from 'sentry/views/performance/transactionSummary/utils';
+
+import {SampleDrawerBody} from '../../common/components/sampleDrawerBody';
+import {SampleDrawerHeaderTransaction} from '../../common/components/sampleDrawerHeaderTransaction';
 
 // This is similar to http sample table, its difficult to use the generic span samples sidebar as we require a bunch of custom things.
 export function CacheSamplePanel() {
@@ -65,7 +60,6 @@ export function CacheSamplePanel() {
   const location = useLocation();
   const organization = useOrganization();
   const {selection} = usePageFilters();
-  const {view} = useDomainViewFilters();
 
   const query = useLocationQuery({
     fields: {
@@ -98,13 +92,6 @@ export function CacheSamplePanel() {
     });
   };
 
-  // `detailKey` controls whether the panel is open. If all required properties are ailable, concat them to make a key, otherwise set to `undefined` and hide the panel
-  const detailKey = query.transaction
-    ? [query.transaction].filter(Boolean).join(':')
-    : undefined;
-
-  const isPanelOpen = Boolean(detailKey);
-
   const filters: SpanMetricsQueryFilters = {
     ...BASE_FILTERS,
     transaction: query.transaction,
@@ -130,7 +117,6 @@ export function CacheSamplePanel() {
           `sum(${SpanMetricsField.SPAN_SELF_TIME})`,
           `avg(${SpanMetricsField.CACHE_ITEM_SIZE})`,
         ],
-        enabled: isPanelOpen,
       },
       Referrer.SAMPLES_CACHE_METRICS_RIBBON
     );
@@ -142,7 +128,6 @@ export function CacheSamplePanel() {
           transaction: query.transaction,
         } satisfies MetricsQueryFilters),
         fields: [`avg(${MetricsFields.TRANSACTION_DURATION})`],
-        enabled: isPanelOpen && Boolean(query.transaction),
       },
       Referrer.SAMPLES_CACHE_TRANSACTION_DURATION
     );
@@ -177,7 +162,6 @@ export function CacheSamplePanel() {
         ],
         sorts: [SPAN_SAMPLES_SORT],
         limit,
-        enabled: isPanelOpen,
       },
       Referrer.SAMPLES_CACHE_SPAN_SAMPLES
     );
@@ -239,26 +223,6 @@ export function CacheSamplePanel() {
     });
   };
 
-  const handleClose = () => {
-    navigate({
-      pathname: location.pathname,
-      query: {
-        ...location.query,
-        transaction: undefined,
-        transactionMethod: undefined,
-      },
-    });
-  };
-
-  const handleOpen = useCallback(() => {
-    if (query.transaction) {
-      trackAnalytics('performance_views.sample_spans.opened', {
-        organization,
-        source: ModuleName.CACHE,
-      });
-    }
-  }, [organization, query.transaction]);
-
   const handleRefetch = () => {
     refetchCacheHits();
     refetchCacheMisses();
@@ -266,36 +230,15 @@ export function CacheSamplePanel() {
 
   return (
     <PageAlertProvider>
-      <DetailPanel detailKey={detailKey} onClose={handleClose} onOpen={handleOpen}>
+      <DrawerHeader>
+        <SampleDrawerHeaderTransaction
+          project={project}
+          transaction={query.transaction}
+        />
+      </DrawerHeader>
+
+      <SampleDrawerBody>
         <ModuleLayout.Layout>
-          <ModuleLayout.Full>
-            <HeaderContainer>
-              {project && (
-                <SpanSummaryProjectAvatar
-                  project={project}
-                  direction="left"
-                  size={40}
-                  hasTooltip
-                  tooltip={project.slug}
-                />
-              )}
-              <Title>
-                <Link
-                  to={normalizeUrl(
-                    `${getTransactionSummaryBaseUrl(organization.slug, view)}?${qs.stringify(
-                      {
-                        project: query.project,
-                        transaction: query.transaction,
-                      }
-                    )}`
-                  )}
-                >
-                  {query.transaction}
-                </Link>
-              </Title>
-            </HeaderContainer>
-          </ModuleLayout.Full>
-
           <ModuleLayout.Full>
             <ReadoutRibbon>
               <MetricReadout
@@ -437,7 +380,7 @@ export function CacheSamplePanel() {
             </ModuleLayout.Full>
           </Fragment>
         </ModuleLayout.Layout>
-      </DetailPanel>
+      </SampleDrawerBody>
     </PageAlertProvider>
   );
 }
@@ -463,26 +406,3 @@ const CACHE_STATUS_OPTIONS = [
     label: t('Miss'),
   },
 ];
-
-const SpanSummaryProjectAvatar = styled(ProjectAvatar)`
-  padding-right: ${space(1)};
-`;
-
-// TODO - copy of static/app/views/starfish/views/spanSummaryPage/sampleList/index.tsx
-const HeaderContainer = styled('div')`
-  display: grid;
-  grid-template-rows: auto auto auto;
-  align-items: center;
-
-  @media (min-width: ${p => p.theme.breakpoints.small}) {
-    grid-template-rows: auto;
-    grid-template-columns: auto 1fr;
-  }
-`;
-
-const Title = styled('h4')`
-  overflow: hidden;
-  text-overflow: ellipsis;
-  white-space: nowrap;
-  margin: 0;
-`;

+ 0 - 23
static/app/views/insights/cache/views/cacheLandingPage.spec.tsx

@@ -93,29 +93,6 @@ describe('CacheLandingPage', function () {
 
     await waitForElementToBeRemoved(() => screen.queryAllByTestId('loading-indicator'));
 
-    expect(requestMocks.missRateChart).toHaveBeenCalledWith(
-      `/organizations/${organization.slug}/events-stats/`,
-      expect.objectContaining({
-        method: 'GET',
-        query: {
-          cursor: undefined,
-          dataset: 'spansMetrics',
-          environment: [],
-          excludeOther: 0,
-          field: [],
-          interval: '30m',
-          orderby: undefined,
-          partial: 1,
-          per_page: 50,
-          project: [],
-          query: 'span.op:[cache.get_item,cache.get] project.id:1',
-          referrer: 'api.performance.cache.samples-cache-hit-miss-chart',
-          statsPeriod: '10d',
-          topEvents: undefined,
-          yAxis: 'cache_miss_rate()',
-        },
-      })
-    );
     expect(requestMocks.throughputChart).toHaveBeenCalledWith(
       `/organizations/${organization.slug}/events-stats/`,
       expect.objectContaining({

+ 20 - 1
static/app/views/insights/cache/views/cacheLandingPage.tsx

@@ -14,6 +14,7 @@ import {
 } from 'sentry/utils/performance/contexts/pageAlert';
 import {decodeScalar, decodeSorts} from 'sentry/utils/queryString';
 import {MutableSearch} from 'sentry/utils/tokenizeSearch';
+import useLocationQuery from 'sentry/utils/url/useLocationQuery';
 import {useLocation} from 'sentry/utils/useLocation';
 import {CacheHitMissChart} from 'sentry/views/insights/cache/components/charts/hitMissChart';
 import {ThroughputChart} from 'sentry/views/insights/cache/components/charts/throughputChart';
@@ -46,6 +47,8 @@ import {DataTitles} from 'sentry/views/insights/common/views/spans/types';
 import {BackendHeader} from 'sentry/views/insights/pages/backend/backendPageHeader';
 import {ModuleName, SpanFunction, SpanMetricsField} from 'sentry/views/insights/types';
 
+import {useSamplesDrawer} from '../../common/utils/useSamplesDrawer';
+
 const {CACHE_MISS_RATE} = SpanFunction;
 const {CACHE_ITEM_SIZE} = SpanMetricsField;
 
@@ -71,6 +74,23 @@ export function CacheLandingPage() {
   const sort = decodeSorts(sortField).filter(isAValidSort).at(0) ?? DEFAULT_SORT;
   const cursor = decodeScalar(location.query?.[QueryParameterNames.TRANSACTIONS_CURSOR]);
 
+  const query = useLocationQuery({
+    fields: {
+      transaction: decodeScalar,
+    },
+  });
+
+  const {openSamplesDrawer} = useSamplesDrawer({
+    Component: <CacheSamplePanel />,
+    moduleName: ModuleName.CACHE,
+  });
+
+  useEffect(() => {
+    if (query.transaction) {
+      openSamplesDrawer();
+    }
+  });
+
   const {
     isPending: isCacheMissRateLoading,
     data: cacheMissRateData,
@@ -233,7 +253,6 @@ export function CacheLandingPage() {
           </Layout.Main>
         </Layout.Body>
       </ModuleBodyUpsellHook>
-      <CacheSamplePanel />
     </React.Fragment>
   );
 }

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