Просмотр исходного кода

feat(ddm): Show detail tabs in dashboard (#62311)

- closes https://github.com/getsentry/sentry/issues/62310
ArthurKnaus 1 год назад
Родитель
Сommit
39a549902a

+ 58 - 20
static/app/components/modals/widgetViewerModal.tsx

@@ -27,6 +27,7 @@ import Pagination from 'sentry/components/pagination';
 import QuestionTooltip from 'sentry/components/questionTooltip';
 import {parseSearch} from 'sentry/components/searchSyntax/parser';
 import HighlightQuery from 'sentry/components/searchSyntax/renderer';
+import {TabList, TabPanels, Tabs} from 'sentry/components/tabs';
 import {t, tct} from 'sentry/locale';
 import {space} from 'sentry/styles/space';
 import {Organization, PageFilters, SelectValue} from 'sentry/types';
@@ -42,6 +43,7 @@ import {
   isEquation,
   isEquationAlias,
 } from 'sentry/utils/discover/fields';
+import {parseField, parseMRI} from 'sentry/utils/metrics/mri';
 import {createOnDemandFilterWarning} from 'sentry/utils/onDemandMetrics';
 import {hasOnDemandMetricWidgetFeature} from 'sentry/utils/onDemandMetrics/features';
 import parseLinkHeader from 'sentry/utils/parseLinkHeader';
@@ -89,6 +91,8 @@ import MetricWidgetQueries from 'sentry/views/dashboards/widgetCard/metricWidget
 import ReleaseWidgetQueries from 'sentry/views/dashboards/widgetCard/releaseWidgetQueries';
 import {WidgetCardChartContainer} from 'sentry/views/dashboards/widgetCard/widgetCardChartContainer';
 import WidgetQueries from 'sentry/views/dashboards/widgetCard/widgetQueries';
+import {CodeLocations} from 'sentry/views/ddm/codeLocations';
+import {TraceTable} from 'sentry/views/ddm/traceTable';
 import {decodeColumnOrder} from 'sentry/views/discover/utils';
 import {OrganizationContext} from 'sentry/views/organizationContext';
 import {MetricsDataSwitcher} from 'sentry/views/performance/landing/metricsDataSwitcher';
@@ -707,28 +711,57 @@ function WidgetViewerModal(props: Props) {
       (a, b) => Number(b[mainField]) - Number(a[mainField])
     );
 
+    const parsedField = parseField(mainField);
+
+    if (!parsedField) {
+      return null;
+    }
+
+    const {useCase} = parseMRI(parsedField.mri)!;
+
     return (
       <Fragment>
-        <GridEditable
-          isLoading={loading}
-          data={sortedData}
-          columnOrder={columnOrder}
-          columnSortBy={columnSortBy}
-          grid={{
-            renderHeadCell: renderMetricGridHeaderCell() as (
-              column: GridColumnOrder,
-              columnIndex: number
-            ) => React.ReactNode,
-            renderBodyCell: renderGridBodyCell({
-              ...props,
-              location,
-              tableData: tableResults?.[0],
-              isFirstPage,
-            }),
-            onResizeColumn,
-          }}
-          location={location}
-        />
+        <Tabs>
+          <TabList>
+            <TabList.Item key="summary">{t('Summary')}</TabList.Item>
+            <TabList.Item hidden={useCase !== 'custom'} key="codeLocation">
+              {t('Code Location')}
+            </TabList.Item>
+            <TabList.Item key="samples">{t('Samples')}</TabList.Item>
+          </TabList>
+          <MetricWidgetTabContent>
+            <TabPanels>
+              <TabPanels.Item key="summary">
+                <GridEditable
+                  isLoading={loading}
+                  data={sortedData}
+                  columnOrder={columnOrder}
+                  columnSortBy={columnSortBy}
+                  grid={{
+                    renderHeadCell: renderMetricGridHeaderCell() as (
+                      column: GridColumnOrder,
+                      columnIndex: number
+                    ) => React.ReactNode,
+                    renderBodyCell: renderGridBodyCell({
+                      ...props,
+                      location,
+                      tableData: tableResults?.[0],
+                      isFirstPage,
+                    }),
+                    onResizeColumn,
+                  }}
+                  location={location}
+                />
+              </TabPanels.Item>
+              <TabPanels.Item key="codeLocation">
+                <CodeLocations mri={parsedField.mri} />
+              </TabPanels.Item>
+              <TabPanels.Item key="samples">
+                <TraceTable />
+              </TabPanels.Item>
+            </TabPanels>
+          </MetricWidgetTabContent>
+        </Tabs>
       </Fragment>
     );
   };
@@ -1308,4 +1341,9 @@ const WidgetTitleRow = styled('div')`
   gap: ${space(0.75)};
 `;
 
+const MetricWidgetTabContent = styled('div')`
+  position: relative;
+  padding-top: ${space(2)};
+`;
+
 export default withPageFilters(WidgetViewerModal);

+ 1 - 1
static/app/views/dashboards/widgetCard/genericWidgetQueries.tsx

@@ -57,7 +57,7 @@ export type GenericWidgetQueriesChildrenProps = {
 
 export type GenericWidgetQueriesProps<SeriesResponse, TableResponse> = {
   api: Client;
-  children: (props: GenericWidgetQueriesChildrenProps) => JSX.Element;
+  children: (props: GenericWidgetQueriesChildrenProps) => React.ReactNode;
   config: DatasetConfig<SeriesResponse, TableResponse>;
   organization: Organization;
   selection: PageFilters;

+ 1 - 1
static/app/views/dashboards/widgetCard/metricWidgetQueries.tsx

@@ -20,7 +20,7 @@ import GenericWidgetQueries, {
 
 type Props = {
   api: Client;
-  children: (props: GenericWidgetQueriesChildrenProps) => JSX.Element;
+  children: (props: GenericWidgetQueriesChildrenProps) => React.ReactNode;
   organization: Organization;
   selection: PageFilters;
   widget: Widget;

+ 3 - 27
static/app/views/ddm/traceTable.tsx

@@ -1,5 +1,6 @@
-import {useEffect, useMemo, useState} from 'react';
+import {useState} from 'react';
 import styled from '@emotion/styled';
+import shuffle from 'lodash/shuffle';
 
 import Badge from 'sentry/components/badge';
 import {PanelTableHeader} from 'sentry/components/panels/panelTable';
@@ -7,7 +8,6 @@ import {space} from 'sentry/styles/space';
 import EventView from 'sentry/utils/discover/eventView';
 import {useLocation} from 'sentry/utils/useLocation';
 import useOrganization from 'sentry/utils/useOrganization';
-import useRouter from 'sentry/utils/useRouter';
 import {
   generateProfileLink,
   generateReplayLink,
@@ -21,9 +21,6 @@ import TransactionsTable from '../../components/discover/transactionsTable';
 export function TraceTable() {
   const location = useLocation();
   const organization = useOrganization();
-  const router = useRouter();
-  const routerQuery = useMemo(() => router.location.query ?? {}, [router.location.query]);
-
   const eventView = EventView.fromLocation(location);
 
   const tableData: any = {
@@ -150,21 +147,7 @@ export function TraceTable() {
     },
   };
 
-  const [rows, setRows] = useState(tableData.data);
-
-  useEffect(() => {
-    function shuffleArray(data) {
-      const array = [...data];
-      for (let i = array.length - 1; i > 0; i--) {
-        const j = Math.floor(Math.random() * (i + 1));
-        [array[i], array[j]] = [array[j], array[i]];
-      }
-      return array;
-    }
-
-    setRows(shuffleArray(tableData.data));
-    // eslint-disable-next-line react-hooks/exhaustive-deps
-  }, [routerQuery]);
+  const [rows] = useState(() => shuffle(tableData.data));
 
   const columnOrder: any = [
     {
@@ -312,13 +295,6 @@ export function TraceTable() {
     },
   ];
 
-  const widgetsQuery = JSON.parse((location?.query?.widgets as string) ?? '[]');
-  const hasSelectedMris = widgetsQuery.filter(w => w.mri);
-
-  if (!hasSelectedMris.length) {
-    return null;
-  }
-
   return (
     <TraceTableWrapper>
       <TitleOverlay>