Browse Source

feat(perf): Remove UX widgets for performance refresh (#52978)

### Summary
Some of these UX widgets aren't as clear as they should be and have lead
to confusion more often than not. This removes "related issues", "span
ops breakdown" and improves the empty state for db/http ops.

---------

Co-authored-by: getsantry[bot] <66042841+getsantry[bot]@users.noreply.github.com>
Kev 1 year ago
parent
commit
393a3f3d81

+ 10 - 0
static/app/utils/docs.tsx

@@ -70,3 +70,13 @@ export function getConfigureTracingDocsLink(
     ? null // this platform does not support performance
     ? null // this platform does not support performance
     : `https://docs.sentry.io/platforms/${docsPlatform}/performance/`;
     : `https://docs.sentry.io/platforms/${docsPlatform}/performance/`;
 }
 }
+
+export function getConfigureIntegrationsDocsLink(
+  project: AvatarProject | undefined
+): string | null {
+  const platform = project?.platform ?? null;
+  const docsPlatform = platform ? getDocsPlatform(platform, true) : null;
+  return docsPlatform === null
+    ? null // this platform does not support performance
+    : `https://docs.sentry.io/platforms/${docsPlatform}/configuration/integrations`;
+}

+ 3 - 3
static/app/views/performance/landing/index.spec.tsx

@@ -251,13 +251,13 @@ describe('Performance > Landing > Index', function () {
       })
       })
     );
     );
 
 
-    expect(eventsMock).toHaveBeenCalledTimes(3);
+    expect(eventsMock).toHaveBeenCalledTimes(2);
 
 
     const titles = await screen.findAllByTestId('performance-widget-title');
     const titles = await screen.findAllByTestId('performance-widget-title');
     expect(titles).toHaveLength(5);
     expect(titles).toHaveLength(5);
 
 
     expect(titles.at(0)).toHaveTextContent('Most Regressed');
     expect(titles.at(0)).toHaveTextContent('Most Regressed');
-    expect(titles.at(1)).toHaveTextContent('Most Related Issues');
+    expect(titles.at(1)).toHaveTextContent('Most Improved');
     expect(titles.at(2)).toHaveTextContent('User Misery');
     expect(titles.at(2)).toHaveTextContent('User Misery');
     expect(titles.at(3)).toHaveTextContent('Transactions Per Minute');
     expect(titles.at(3)).toHaveTextContent('Transactions Per Minute');
     expect(titles.at(4)).toHaveTextContent('Failure Rate');
     expect(titles.at(4)).toHaveTextContent('Failure Rate');
@@ -362,7 +362,7 @@ describe('Performance > Landing > Index', function () {
 
 
       wrapper = render(<WrappedComponent data={data} />, data.routerContext);
       wrapper = render(<WrappedComponent data={data} />, data.routerContext);
       const titles = await screen.findAllByTestId('performance-widget-title');
       const titles = await screen.findAllByTestId('performance-widget-title');
-      expect(titles.at(0)).toHaveTextContent('Span Operations');
+      expect(titles.at(0)).toHaveTextContent('Most Regressed');
     });
     });
   });
   });
 });
 });

+ 2 - 2
static/app/views/performance/landing/metricsDataSwitcherAlert.tsx

@@ -14,9 +14,9 @@ import EventView from 'sentry/utils/discover/eventView';
 import {MetricDataSwitcherOutcome} from 'sentry/utils/performance/contexts/metricsCardinality';
 import {MetricDataSwitcherOutcome} from 'sentry/utils/performance/contexts/metricsCardinality';
 
 
 import {
 import {
-  areMultipleProjectsSelected,
   createUnnamedTransactionsDiscoverTarget,
   createUnnamedTransactionsDiscoverTarget,
   DiscoverQueryPageSource,
   DiscoverQueryPageSource,
+  getIsMultiProject,
   getSelectedProjectPlatformsArray,
   getSelectedProjectPlatformsArray,
 } from '../utils';
 } from '../utils';
 
 
@@ -107,7 +107,7 @@ export function MetricsDataSwitcherAlert(
         {t('update your SDK version')}
         {t('update your SDK version')}
       </Link>
       </Link>
     );
     );
-    if (areMultipleProjectsSelected(props.eventView)) {
+    if (getIsMultiProject(props.eventView.project)) {
       if ((props.compatibleProjects ?? []).length === 0) {
       if ((props.compatibleProjects ?? []).length === 0) {
         return (
         return (
           <Alert
           <Alert

+ 1 - 9
static/app/views/performance/landing/views/allTransactionsView.tsx

@@ -10,11 +10,7 @@ import {PerformanceWidgetSetting} from '../widgets/widgetDefinitions';
 import {BasePerformanceViewProps} from './types';
 import {BasePerformanceViewProps} from './types';
 
 
 export function AllTransactionsView(props: BasePerformanceViewProps) {
 export function AllTransactionsView(props: BasePerformanceViewProps) {
-  const showSpanOperationsWidget =
-    props.organization.features.includes('performance-new-widget-designs') &&
-    canUseMetricsData(props.organization);
-
-  const doubleChartRowCharts = [PerformanceWidgetSetting.MOST_RELATED_ISSUES];
+  const doubleChartRowCharts: PerformanceWidgetSetting[] = [];
 
 
   if (
   if (
     props.organization.features.includes('performance-new-trends') &&
     props.organization.features.includes('performance-new-trends') &&
@@ -26,10 +22,6 @@ export function AllTransactionsView(props: BasePerformanceViewProps) {
     doubleChartRowCharts.push(PerformanceWidgetSetting.MOST_IMPROVED);
     doubleChartRowCharts.push(PerformanceWidgetSetting.MOST_IMPROVED);
   }
   }
 
 
-  if (showSpanOperationsWidget) {
-    doubleChartRowCharts.unshift(PerformanceWidgetSetting.SPAN_OPERATIONS);
-  }
-
   return (
   return (
     <PerformanceDisplayProvider value={{performanceType: ProjectPerformanceType.ANY}}>
     <PerformanceDisplayProvider value={{performanceType: ProjectPerformanceType.ANY}}>
       <div data-test-id="all-transactions-view">
       <div data-test-id="all-transactions-view">

+ 0 - 7
static/app/views/performance/landing/views/backendView.tsx

@@ -36,9 +36,6 @@ function getAllowedChartsSmall(
 
 
 export function BackendView(props: BasePerformanceViewProps) {
 export function BackendView(props: BasePerformanceViewProps) {
   const mepSetting = useMEPSettingContext();
   const mepSetting = useMEPSettingContext();
-  const showSpanOperationsWidget =
-    props.organization.features.includes('performance-new-widget-designs') &&
-    canUseMetricsData(props.organization);
 
 
   const doubleChartRowCharts = [
   const doubleChartRowCharts = [
     PerformanceWidgetSetting.SLOW_HTTP_OPS,
     PerformanceWidgetSetting.SLOW_HTTP_OPS,
@@ -55,10 +52,6 @@ export function BackendView(props: BasePerformanceViewProps) {
       ...[PerformanceWidgetSetting.MOST_REGRESSED, PerformanceWidgetSetting.MOST_IMPROVED]
       ...[PerformanceWidgetSetting.MOST_REGRESSED, PerformanceWidgetSetting.MOST_IMPROVED]
     );
     );
   }
   }
-
-  if (showSpanOperationsWidget) {
-    doubleChartRowCharts.unshift(PerformanceWidgetSetting.SPAN_OPERATIONS);
-  }
   return (
   return (
     <PerformanceDisplayProvider value={{performanceType: ProjectPerformanceType.ANY}}>
     <PerformanceDisplayProvider value={{performanceType: ProjectPerformanceType.ANY}}>
       <div>
       <div>

+ 0 - 9
static/app/views/performance/landing/views/frontendOtherView.tsx

@@ -1,5 +1,4 @@
 import {
 import {
-  canUseMetricsData,
   MetricsEnhancedSettingContext,
   MetricsEnhancedSettingContext,
   useMEPSettingContext,
   useMEPSettingContext,
 } from 'sentry/utils/performance/contexts/metricsEnhancedSetting';
 } from 'sentry/utils/performance/contexts/metricsEnhancedSetting';
@@ -33,20 +32,12 @@ function getAllowedChartsSmall(
 
 
 export function FrontendOtherView(props: BasePerformanceViewProps) {
 export function FrontendOtherView(props: BasePerformanceViewProps) {
   const mepSetting = useMEPSettingContext();
   const mepSetting = useMEPSettingContext();
-  const showSpanOperationsWidget =
-    props.organization.features.includes('performance-new-widget-designs') &&
-    canUseMetricsData(props.organization);
 
 
   const doubleChartRowCharts = [
   const doubleChartRowCharts = [
-    PerformanceWidgetSetting.MOST_RELATED_ISSUES,
     PerformanceWidgetSetting.SLOW_HTTP_OPS,
     PerformanceWidgetSetting.SLOW_HTTP_OPS,
     PerformanceWidgetSetting.SLOW_RESOURCE_OPS,
     PerformanceWidgetSetting.SLOW_RESOURCE_OPS,
   ];
   ];
 
 
-  if (showSpanOperationsWidget) {
-    doubleChartRowCharts.unshift(PerformanceWidgetSetting.SPAN_OPERATIONS);
-  }
-
   return (
   return (
     <PerformanceDisplayProvider
     <PerformanceDisplayProvider
       value={{performanceType: ProjectPerformanceType.FRONTEND_OTHER}}
       value={{performanceType: ProjectPerformanceType.FRONTEND_OTHER}}

+ 0 - 9
static/app/views/performance/landing/views/frontendPageloadView.tsx

@@ -1,5 +1,4 @@
 import {
 import {
-  canUseMetricsData,
   MetricsEnhancedSettingContext,
   MetricsEnhancedSettingContext,
   useMEPSettingContext,
   useMEPSettingContext,
 } from 'sentry/utils/performance/contexts/metricsEnhancedSetting';
 } from 'sentry/utils/performance/contexts/metricsEnhancedSetting';
@@ -32,23 +31,15 @@ function getAllowedChartsSmall(
 
 
 export function FrontendPageloadView(props: BasePerformanceViewProps) {
 export function FrontendPageloadView(props: BasePerformanceViewProps) {
   const mepSetting = useMEPSettingContext();
   const mepSetting = useMEPSettingContext();
-  const showSpanOperationsWidget =
-    props.organization.features.includes('performance-new-widget-designs') &&
-    canUseMetricsData(props.organization);
 
 
   const doubleChartRowCharts = [
   const doubleChartRowCharts = [
     PerformanceWidgetSetting.WORST_LCP_VITALS,
     PerformanceWidgetSetting.WORST_LCP_VITALS,
     PerformanceWidgetSetting.WORST_FCP_VITALS,
     PerformanceWidgetSetting.WORST_FCP_VITALS,
     PerformanceWidgetSetting.WORST_FID_VITALS,
     PerformanceWidgetSetting.WORST_FID_VITALS,
-    PerformanceWidgetSetting.MOST_RELATED_ISSUES,
     PerformanceWidgetSetting.SLOW_HTTP_OPS,
     PerformanceWidgetSetting.SLOW_HTTP_OPS,
     PerformanceWidgetSetting.SLOW_BROWSER_OPS,
     PerformanceWidgetSetting.SLOW_BROWSER_OPS,
     PerformanceWidgetSetting.SLOW_RESOURCE_OPS,
     PerformanceWidgetSetting.SLOW_RESOURCE_OPS,
   ];
   ];
-
-  if (showSpanOperationsWidget) {
-    doubleChartRowCharts.unshift(PerformanceWidgetSetting.SPAN_OPERATIONS);
-  }
   return (
   return (
     <PerformanceDisplayProvider
     <PerformanceDisplayProvider
       value={{performanceType: ProjectPerformanceType.FRONTEND}}
       value={{performanceType: ProjectPerformanceType.FRONTEND}}

+ 40 - 1
static/app/views/performance/landing/widgets/components/selectableList.tsx

@@ -2,12 +2,17 @@ import styled from '@emotion/styled';
 
 
 import EmptyStateWarning from 'sentry/components/emptyStateWarning';
 import EmptyStateWarning from 'sentry/components/emptyStateWarning';
 import {RadioLineItem} from 'sentry/components/forms/controls/radioGroup';
 import {RadioLineItem} from 'sentry/components/forms/controls/radioGroup';
+import ExternalLink from 'sentry/components/links/externalLink';
 import Link from 'sentry/components/links/link';
 import Link from 'sentry/components/links/link';
 import Radio from 'sentry/components/radio';
 import Radio from 'sentry/components/radio';
 import {Tooltip} from 'sentry/components/tooltip';
 import {Tooltip} from 'sentry/components/tooltip';
 import {IconClose} from 'sentry/icons';
 import {IconClose} from 'sentry/icons';
-import {t} from 'sentry/locale';
+import {t, tct} from 'sentry/locale';
 import {space} from 'sentry/styles/space';
 import {space} from 'sentry/styles/space';
+import {getConfigureIntegrationsDocsLink} from 'sentry/utils/docs';
+import usePageFilters from 'sentry/utils/usePageFilters';
+import useProjects from 'sentry/utils/useProjects';
+import {getIsMultiProject} from 'sentry/views/performance/utils';
 
 
 type Props = {
 type Props = {
   items: (() => React.ReactNode)[];
   items: (() => React.ReactNode)[];
@@ -84,6 +89,40 @@ export function WidgetEmptyStateWarning() {
   );
   );
 }
 }
 
 
+export function WidgetAddInstrumentationWarning() {
+  const pageFilters = usePageFilters();
+  const fullProjects = useProjects();
+
+  const projects = pageFilters.selection.projects;
+
+  const isMultiProject = getIsMultiProject(projects);
+
+  if (isMultiProject) {
+    return <WidgetEmptyStateWarning />;
+  }
+
+  const project = fullProjects.projects.find(p => p.id === '' + projects[0]);
+  const url = getConfigureIntegrationsDocsLink(project);
+
+  if (!url) {
+    return <WidgetEmptyStateWarning />;
+  }
+
+  return (
+    <StyledEmptyStateWarning>
+      <PrimaryMessage>{t('No results found')}</PrimaryMessage>
+      <SecondaryMessage>
+        {tct(
+          'No transactions with Database or HTTP spans found, you may need to [added].',
+          {
+            added: <ExternalLink href={url}>{t('add integrations')}</ExternalLink>,
+          }
+        )}
+      </SecondaryMessage>
+    </StyledEmptyStateWarning>
+  );
+}
+
 export function ListClose(props: {
 export function ListClose(props: {
   onClick: () => void;
   onClick: () => void;
   setSelectListIndex: (n: number) => void;
   setSelectListIndex: (n: number) => void;

+ 8 - 5
static/app/views/performance/landing/widgets/widgetDefinitions.tsx

@@ -10,12 +10,15 @@ import {GenericPerformanceWidgetDataType} from './types';
 export interface ChartDefinition {
 export interface ChartDefinition {
   dataType: GenericPerformanceWidgetDataType;
   dataType: GenericPerformanceWidgetDataType;
   fields: string[];
   fields: string[];
-
+  // Additional fields to get requested but are not directly used in visualization.
   title: string;
   title: string;
-  titleTooltip: string; // The first field in the list will be treated as the primary field in most widgets (except for special casing).
 
 
+  titleTooltip: string;
+  // The first field in the list will be treated as the primary field in most widgets (except for special casing).
   allowsOpenInDiscover?: boolean;
   allowsOpenInDiscover?: boolean;
-  chartColor?: string; // Optional. Will default to colors depending on placement in list or colors from the chart itself.
+
+  chartColor?: string;
+  secondaryFields?: string[]; // Optional. Will default to colors depending on placement in list or colors from the chart itself.
 
 
   vitalStops?: {
   vitalStops?: {
     meh: number;
     meh: number;
@@ -260,7 +263,7 @@ export const WIDGET_DEFINITIONS: ({
   [PerformanceWidgetSetting.SLOW_HTTP_OPS]: {
   [PerformanceWidgetSetting.SLOW_HTTP_OPS]: {
     title: t('Slow HTTP Ops'),
     title: t('Slow HTTP Ops'),
     titleTooltip: getTermHelp(organization, PerformanceTerm.SLOW_HTTP_SPANS),
     titleTooltip: getTermHelp(organization, PerformanceTerm.SLOW_HTTP_SPANS),
-    fields: [`p75(spans.http)`],
+    fields: [`p75(spans.http)`, 'p75(spans.db)'],
     dataType: GenericPerformanceWidgetDataType.LINE_LIST,
     dataType: GenericPerformanceWidgetDataType.LINE_LIST,
     chartColor: WIDGET_PALETTE[0],
     chartColor: WIDGET_PALETTE[0],
   },
   },
@@ -281,7 +284,7 @@ export const WIDGET_DEFINITIONS: ({
   [PerformanceWidgetSetting.SLOW_DB_OPS]: {
   [PerformanceWidgetSetting.SLOW_DB_OPS]: {
     title: t('Slow DB Ops'),
     title: t('Slow DB Ops'),
     titleTooltip: getTermHelp(organization, PerformanceTerm.SLOW_HTTP_SPANS),
     titleTooltip: getTermHelp(organization, PerformanceTerm.SLOW_HTTP_SPANS),
-    fields: [`p75(spans.db)`],
+    fields: [`p75(spans.db)`, 'p75(spans.http)'],
     dataType: GenericPerformanceWidgetDataType.LINE_LIST,
     dataType: GenericPerformanceWidgetDataType.LINE_LIST,
     chartColor: WIDGET_PALETTE[0],
     chartColor: WIDGET_PALETTE[0],
   },
   },

+ 14 - 2
static/app/views/performance/landing/widgets/widgets/lineChartListWidget.tsx

@@ -33,6 +33,7 @@ import SelectableList, {
   ListClose,
   ListClose,
   RightAlignedCell,
   RightAlignedCell,
   Subtitle,
   Subtitle,
+  WidgetAddInstrumentationWarning,
   WidgetEmptyStateWarning,
   WidgetEmptyStateWarning,
 } from '../components/selectableList';
 } from '../components/selectableList';
 import {transformDiscoverToList} from '../transforms/transformDiscoverToList';
 import {transformDiscoverToList} from '../transforms/transformDiscoverToList';
@@ -59,16 +60,27 @@ const framesList = [
   PerformanceWidgetSetting.MOST_FROZEN_FRAMES,
   PerformanceWidgetSetting.MOST_FROZEN_FRAMES,
 ];
 ];
 
 
+const integrationEmptyStateWidgets = [
+  PerformanceWidgetSetting.SLOW_DB_OPS,
+  PerformanceWidgetSetting.SLOW_HTTP_OPS,
+];
+
 export function LineChartListWidget(props: PerformanceWidgetProps) {
 export function LineChartListWidget(props: PerformanceWidgetProps) {
   const location = useLocation();
   const location = useLocation();
   const mepSetting = useMEPSettingContext();
   const mepSetting = useMEPSettingContext();
   const [selectedListIndex, setSelectListIndex] = useState<number>(0);
   const [selectedListIndex, setSelectListIndex] = useState<number>(0);
   const {ContainerActions, organization, InteractiveTitle} = props;
   const {ContainerActions, organization, InteractiveTitle} = props;
   const pageError = usePageError();
   const pageError = usePageError();
+  const canHaveIntegrationEmptyState = integrationEmptyStateWidgets.includes(
+    props.chartSetting
+  );
+  const emptyComponent = canHaveIntegrationEmptyState
+    ? WidgetAddInstrumentationWarning
+    : WidgetEmptyStateWarning;
 
 
   const field = props.fields[0];
   const field = props.fields[0];
 
 
-  if (props.fields.length !== 1) {
+  if (props.fields.length !== 1 && !canHaveIntegrationEmptyState) {
     throw new Error(
     throw new Error(
       `Line chart list widget can only accept a single field (${props.fields})`
       `Line chart list widget can only accept a single field (${props.fields})`
     );
     );
@@ -436,7 +448,7 @@ export function LineChartListWidget(props: PerformanceWidgetProps) {
           ? provided => <InteractiveTitle {...provided.widgetData.chart} />
           ? provided => <InteractiveTitle {...provided.widgetData.chart} />
           : null
           : null
       }
       }
-      EmptyComponent={WidgetEmptyStateWarning}
+      EmptyComponent={emptyComponent}
       Queries={Queries}
       Queries={Queries}
       Visualizations={Visualizations}
       Visualizations={Visualizations}
     />
     />

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