Browse Source

chore(change-explore): Remove unused change explorer (#81406)

The change explore uses some unsupported profiling features that are to
be removed. It's also unused so we can safely get rid of it.
Tony Xiao 3 months ago
parent
commit
91dc38c691

+ 0 - 682
static/app/views/performance/trends/changeExplorer.spec.tsx

@@ -1,682 +0,0 @@
-import {Fragment} from 'react';
-import moment from 'moment-timezone';
-
-import {initializeData} from 'sentry-test/performance/initializePerformanceData';
-import {act, render, screen, waitFor} from 'sentry-test/reactTestingLibrary';
-
-import ProjectsStore from 'sentry/stores/projectsStore';
-import type {SuspectSpans} from 'sentry/utils/performance/suspectSpans/types';
-import type {EventsResultsDataRow} from 'sentry/utils/profiling/hooks/types';
-import {PerformanceChangeExplorer} from 'sentry/views/performance/trends/changeExplorer';
-import type {FunctionsField} from 'sentry/views/performance/trends/changeExplorerUtils/functionsList';
-import {FunctionsList} from 'sentry/views/performance/trends/changeExplorerUtils/functionsList';
-import {
-  COLUMNS,
-  MetricsTable,
-  renderBodyCell,
-} from 'sentry/views/performance/trends/changeExplorerUtils/metricsTable';
-import {SpansList} from 'sentry/views/performance/trends/changeExplorerUtils/spansList';
-import type {NormalizedTrendsTransaction} from 'sentry/views/performance/trends/types';
-import {TrendChangeType, TrendFunctionField} from 'sentry/views/performance/trends/types';
-import {TRENDS_PARAMETERS} from 'sentry/views/performance/trends/utils';
-
-async function waitForMockCall(mock: jest.Mock) {
-  await waitFor(() => {
-    expect(mock).toHaveBeenCalled();
-  });
-}
-
-const transaction: NormalizedTrendsTransaction = {
-  aggregate_range_1: 78.2757131147541,
-  aggregate_range_2: 110.50465131578949,
-  breakpoint: 1687262400,
-  project: 'sentry',
-  transaction: 'sentry.tasks.store.save_event',
-  trend_difference: 32.22893820103539,
-  trend_percentage: 1.411736117354651,
-  count: 3459,
-  received_at: moment(1601251200000),
-};
-
-const spanResults: SuspectSpans = [
-  {
-    op: 'db',
-    group: '1',
-    description: 'span1',
-    frequency: 4,
-    count: 4,
-    avgOccurrences: undefined,
-    sumExclusiveTime: 345345,
-    p50ExclusiveTime: undefined,
-    p75ExclusiveTime: 25,
-    p95ExclusiveTime: undefined,
-    p99ExclusiveTime: undefined,
-    examples: [],
-  },
-  {
-    op: 'db',
-    group: '2',
-    description: 'span2',
-    frequency: 4,
-    count: 4,
-    avgOccurrences: undefined,
-    sumExclusiveTime: 345345,
-    p50ExclusiveTime: undefined,
-    p75ExclusiveTime: 25,
-    p95ExclusiveTime: undefined,
-    p99ExclusiveTime: undefined,
-    examples: [],
-  },
-  {
-    op: 'db',
-    group: '3',
-    description: 'span3',
-    frequency: 4,
-    count: 4,
-    avgOccurrences: undefined,
-    sumExclusiveTime: 345345,
-    p50ExclusiveTime: undefined,
-    p75ExclusiveTime: 25,
-    p95ExclusiveTime: undefined,
-    p99ExclusiveTime: undefined,
-    examples: [],
-  },
-  {
-    op: 'db',
-    group: '4',
-    description: 'span4',
-    frequency: 4,
-    count: 4,
-    avgOccurrences: undefined,
-    sumExclusiveTime: 345345,
-    p50ExclusiveTime: undefined,
-    p75ExclusiveTime: 25,
-    p95ExclusiveTime: undefined,
-    p99ExclusiveTime: undefined,
-    examples: [],
-  },
-  {
-    op: 'db',
-    group: '5',
-    description: 'span5',
-    frequency: 4,
-    count: 4,
-    avgOccurrences: undefined,
-    sumExclusiveTime: 345345,
-    p50ExclusiveTime: undefined,
-    p75ExclusiveTime: 25,
-    p95ExclusiveTime: undefined,
-    p99ExclusiveTime: undefined,
-    examples: [],
-  },
-  {
-    op: 'db',
-    group: '6',
-    description: 'span6',
-    frequency: 4,
-    count: 4,
-    avgOccurrences: undefined,
-    sumExclusiveTime: 345345,
-    p50ExclusiveTime: undefined,
-    p75ExclusiveTime: 25,
-    p95ExclusiveTime: undefined,
-    p99ExclusiveTime: undefined,
-    examples: [],
-  },
-  {
-    op: 'db',
-    group: '7',
-    description: 'span7',
-    frequency: 4,
-    count: 4,
-    avgOccurrences: undefined,
-    sumExclusiveTime: 345345,
-    p50ExclusiveTime: undefined,
-    p75ExclusiveTime: 25,
-    p95ExclusiveTime: undefined,
-    p99ExclusiveTime: undefined,
-    examples: [],
-  },
-  {
-    op: 'db',
-    group: '8',
-    description: 'span8',
-    frequency: 4,
-    count: 4,
-    avgOccurrences: undefined,
-    sumExclusiveTime: 345345,
-    p50ExclusiveTime: undefined,
-    p75ExclusiveTime: 25,
-    p95ExclusiveTime: undefined,
-    p99ExclusiveTime: undefined,
-    examples: [],
-  },
-  {
-    op: 'db',
-    group: '9',
-    description: 'span9',
-    frequency: 4,
-    count: 4,
-    avgOccurrences: undefined,
-    sumExclusiveTime: 345345,
-    p50ExclusiveTime: undefined,
-    p75ExclusiveTime: 25,
-    p95ExclusiveTime: undefined,
-    p99ExclusiveTime: undefined,
-    examples: [],
-  },
-  {
-    op: 'db',
-    group: '10',
-    description: 'span10',
-    frequency: 4,
-    count: 4,
-    avgOccurrences: undefined,
-    sumExclusiveTime: 345345,
-    p50ExclusiveTime: undefined,
-    p75ExclusiveTime: 25,
-    p95ExclusiveTime: undefined,
-    p99ExclusiveTime: undefined,
-    examples: [],
-  },
-  {
-    op: 'db',
-    group: '11',
-    description: 'span11',
-    frequency: 4,
-    count: 4,
-    avgOccurrences: undefined,
-    sumExclusiveTime: 345345,
-    p50ExclusiveTime: undefined,
-    p75ExclusiveTime: 25,
-    p95ExclusiveTime: undefined,
-    p99ExclusiveTime: undefined,
-    examples: [],
-  },
-];
-
-const functionResults: EventsResultsDataRow<FunctionsField>[] = [
-  {
-    'count()': 234,
-    'examples()': ['serw8r9s', 'aeo4i2u38'],
-    function: 'f1',
-    'p75()': 4239847,
-    package: 'p1',
-    'sum()': 2304823908,
-  },
-  {
-    'count()': 234,
-    'examples()': ['serw8r9s', 'aeo4i2u38'],
-    function: 'f2',
-    'p75()': 4239847,
-    package: 'p2',
-    'sum()': 2304823908,
-  },
-  {
-    'count()': 234,
-    'examples()': ['serw8r9s', 'aeo4i2u38'],
-    function: 'f3',
-    'p75()': 4239847,
-    package: 'p3',
-    'sum()': 2304823908,
-  },
-  {
-    'count()': 234,
-    'examples()': ['serw8r9s', 'aeo4i2u38'],
-    function: 'f4',
-    'p75()': 4239847,
-    package: 'p4',
-    'sum()': 2304823908,
-  },
-  {
-    'count()': 234,
-    'examples()': ['serw8r9s', 'aeo4i2u38'],
-    function: 'f5',
-    'p75()': 4239847,
-    package: 'p5',
-    'sum()': 2304823908,
-  },
-  {
-    'count()': 234,
-    'examples()': ['serw8r9s', 'aeo4i2u38'],
-    function: 'f5',
-    'p75()': 4239847,
-    package: 'p5',
-    'sum()': 2304823908,
-  },
-  {
-    'count()': 234,
-    'examples()': ['serw8r9s', 'aeo4i2u38'],
-    function: 'f7',
-    'p75()': 4239847,
-    package: 'p7',
-    'sum()': 2304823908,
-  },
-  {
-    'count()': 234,
-    'examples()': ['serw8r9s', 'aeo4i2u38'],
-    function: 'f8',
-    'p75()': 4239847,
-    package: 'p8',
-    'sum()': 2304823908,
-  },
-  {
-    'count()': 234,
-    'examples()': ['serw8r9s', 'aeo4i2u38'],
-    function: 'f9',
-    'p75()': 4239847,
-    package: 'p9',
-    'sum()': 2304823908,
-  },
-  {
-    'count()': 234,
-    'examples()': ['serw8r9s', 'aeo4i2u38'],
-    function: 'f10',
-    'p75()': 4239847,
-    package: 'p10',
-    'sum()': 2304823908,
-  },
-  {
-    'count()': 234,
-    'examples()': ['serw8r9s', 'aeo4i2u38'],
-    function: 'f11',
-    'p75()': 4239847,
-    package: 'p11',
-    'sum()': 2304823908,
-  },
-];
-
-describe('Performance > Trends > Performance Change Explorer', function () {
-  let eventsMockBefore: jest.Mock;
-  let spansMock: jest.Mock;
-  beforeEach(function () {
-    eventsMockBefore = MockApiClient.addMockResponse({
-      url: '/organizations/org-slug/events/',
-      body: {
-        data: [
-          {
-            'p95()': 1010.9232499999998,
-            'p50()': 47.34580982348902,
-            'tps()': 3.7226926286168966,
-            'count()': 345,
-            'failure_rate()': 0.23498234,
-            'examples()': ['dkwj4w8sdjk', 'asdi389a8'],
-          },
-        ],
-        meta: {
-          fields: {
-            'p95()': 'duration',
-            '950()': 'duration',
-            'tps()': 'number',
-            'count()': 'number',
-            'failure_rate()': 'number',
-            'examples()': 'Array',
-          },
-          units: {
-            'p95()': 'millisecond',
-            'p50()': 'millisecond',
-            'tps()': null,
-            'count()': null,
-            'failure_rate()': null,
-            'examples()': null,
-          },
-          isMetricsData: true,
-          tips: {},
-          dataset: 'metrics',
-        },
-      },
-    });
-
-    spansMock = MockApiClient.addMockResponse({
-      url: '/organizations/org-slug/events-spans-performance/',
-      body: [],
-    });
-  });
-
-  afterEach(function () {
-    MockApiClient.clearMockResponses();
-    act(() => ProjectsStore.reset());
-  });
-
-  it('renders basic UI elements', async function () {
-    const data = initializeData();
-    const statsData = {
-      ['/organizations/:orgId/performance/']: {
-        data: [],
-        order: 0,
-      },
-    };
-
-    render(
-      <PerformanceChangeExplorer
-        collapsed={false}
-        transaction={transaction}
-        onClose={() => {}}
-        trendChangeType={TrendChangeType.REGRESSION}
-        trendFunction={TrendFunctionField.P50}
-        trendParameter={TRENDS_PARAMETERS[0]}
-        trendView={data.eventView}
-        statsData={statsData}
-        isLoading={false}
-        organization={data.organization}
-        projects={data.projects}
-        location={data.location}
-      />,
-      {
-        router: data.router,
-        organization: data.organization,
-      }
-    );
-
-    await waitForMockCall(eventsMockBefore);
-    await waitForMockCall(spansMock);
-
-    await waitFor(() => {
-      expect(screen.getByTestId('pce-header')).toBeInTheDocument();
-      expect(screen.getByTestId('pce-graph')).toBeInTheDocument();
-      expect(screen.getByTestId('grid-editable')).toBeInTheDocument();
-      expect(screen.getAllByTestId('pce-metrics-chart-row-metric')).toHaveLength(4);
-      expect(screen.getAllByTestId('pce-metrics-chart-row-before')).toHaveLength(4);
-      expect(screen.getAllByTestId('pce-metrics-chart-row-after')).toHaveLength(4);
-      expect(screen.getAllByTestId('pce-metrics-chart-row-change')).toHaveLength(4);
-      expect(screen.getByTestId('list-item')).toBeInTheDocument();
-    });
-  });
-
-  it('shows correct change notation for no change', async () => {
-    const data = initializeData();
-
-    render(
-      <MetricsTable
-        isLoading={false}
-        location={data.location}
-        trendFunction={TrendFunctionField.P50}
-        transaction={transaction}
-        trendView={data.eventView}
-        organization={data.organization}
-      />
-    );
-
-    await waitForMockCall(eventsMockBefore);
-
-    await waitFor(() => {
-      expect(screen.getAllByText('3.7 ps')).toHaveLength(2);
-      expect(screen.getAllByTestId('pce-metrics-text-change')[0]).toHaveTextContent('-');
-    });
-  });
-
-  it('shows correct change notation for positive change', async () => {
-    const data = initializeData();
-
-    render(
-      <MetricsTable
-        isLoading={false}
-        location={data.location}
-        trendFunction={TrendFunctionField.P50}
-        transaction={transaction}
-        trendView={data.eventView}
-        organization={data.organization}
-      />
-    );
-
-    await waitForMockCall(eventsMockBefore);
-
-    await waitFor(() => {
-      expect(screen.getAllByTestId('pce-metrics-text-before')[1]).toHaveTextContent(
-        '78.3 ms'
-      );
-      expect(screen.getAllByTestId('pce-metrics-text-after')[1]).toHaveTextContent(
-        '110.5 ms'
-      );
-      expect(screen.getAllByTestId('pce-metrics-text-change')[1]).toHaveTextContent(
-        '+41.2%'
-      );
-    });
-  });
-
-  it('shows correct change notation for negative change', async () => {
-    const data = initializeData();
-    const negativeTransaction = {
-      ...transaction,
-      aggregate_range_1: 110.50465131578949,
-      aggregate_range_2: 78.2757131147541,
-      trend_percentage: 0.588263882645349,
-    };
-
-    render(
-      <MetricsTable
-        isLoading={false}
-        location={data.location}
-        trendFunction={TrendFunctionField.P50}
-        transaction={negativeTransaction}
-        trendView={data.eventView}
-        organization={data.organization}
-      />
-    );
-
-    await waitForMockCall(eventsMockBefore);
-
-    await waitFor(() => {
-      expect(screen.getAllByTestId('pce-metrics-text-after')[1]).toHaveTextContent(
-        '78.3 ms'
-      );
-      expect(screen.getAllByTestId('pce-metrics-text-before')[1]).toHaveTextContent(
-        '110.5 ms'
-      );
-      expect(screen.getAllByTestId('pce-metrics-text-change')[1]).toHaveTextContent(
-        '-41.2%'
-      );
-    });
-  });
-
-  it('shows correct change notation for no results', async () => {
-    const data = initializeData();
-
-    const nullEventsMock = MockApiClient.addMockResponse({
-      url: '/organizations/org-slug/events/',
-      body: {
-        data: [
-          {
-            'p95()': 1010.9232499999998,
-            'p50()': 47.34580982348902,
-            'count()': 345,
-          },
-        ],
-        meta: {
-          fields: {
-            'p95()': 'duration',
-            '950()': 'duration',
-            'count()': 'number',
-          },
-          units: {
-            'p95()': 'millisecond',
-            'p50()': 'millisecond',
-            'count()': null,
-          },
-          isMetricsData: true,
-          tips: {},
-          dataset: 'metrics',
-        },
-      },
-    });
-
-    render(
-      <MetricsTable
-        isLoading={false}
-        location={data.location}
-        trendFunction={TrendFunctionField.P50}
-        transaction={transaction}
-        trendView={data.eventView}
-        organization={data.organization}
-      />
-    );
-
-    await waitForMockCall(nullEventsMock);
-
-    await waitFor(() => {
-      expect(screen.getAllByTestId('pce-metrics-text-after')[0]).toHaveTextContent('-');
-      expect(screen.getAllByTestId('pce-metrics-text-before')[0]).toHaveTextContent('-');
-      expect(screen.getAllByTestId('pce-metrics-text-change')[0]).toHaveTextContent('-');
-    });
-  });
-
-  it('returns correct null formatting for change column', () => {
-    render(
-      <Fragment>
-        {renderBodyCell(COLUMNS.change, {
-          metric: null,
-          before: null,
-          after: null,
-          change: '0%',
-        })}
-        {renderBodyCell(COLUMNS.change, {
-          metric: null,
-          before: null,
-          after: null,
-          change: '+NaN%',
-        })}
-        {renderBodyCell(COLUMNS.change, {
-          metric: null,
-          before: null,
-          after: null,
-          change: '-',
-        })}
-      </Fragment>
-    );
-
-    expect(screen.getAllByTestId('pce-metrics-text-change')[0]).toHaveTextContent('-');
-    expect(screen.getAllByTestId('pce-metrics-text-change')[1]).toHaveTextContent('-');
-    expect(screen.getAllByTestId('pce-metrics-text-change')[2]).toHaveTextContent('-');
-  });
-
-  it('returns correct positive formatting for change column', () => {
-    render(
-      renderBodyCell(COLUMNS.change, {
-        metric: null,
-        before: null,
-        after: null,
-        change: '40.3%',
-      })
-    );
-
-    expect(screen.getByText('+40.3%')).toBeInTheDocument();
-  });
-
-  it('renders spans list with no results', async () => {
-    const data = initializeData();
-    const emptyEventsMock = MockApiClient.addMockResponse({
-      url: '/organizations/org-slug/events/',
-      body: {},
-    });
-
-    render(
-      <div>
-        <SpansList
-          location={data.location}
-          organization={data.organization}
-          trendView={data.eventView}
-          breakpoint={transaction.breakpoint!}
-          transaction={transaction}
-          trendChangeType={TrendChangeType.REGRESSION}
-        />
-        <FunctionsList
-          location={data.location}
-          organization={data.organization}
-          trendView={data.eventView}
-          breakpoint={transaction.breakpoint!}
-          transaction={transaction}
-          trendChangeType={TrendChangeType.REGRESSION}
-        />
-      </div>
-    );
-
-    await waitForMockCall(spansMock);
-    await waitForMockCall(emptyEventsMock);
-
-    await waitFor(() => {
-      expect(screen.getAllByTestId('empty-state')).toHaveLength(2);
-      expect(screen.getByTestId('spans-no-results')).toBeInTheDocument();
-      expect(screen.getByTestId('functions-no-results')).toBeInTheDocument();
-    });
-  });
-
-  it('renders spans list with error message', async () => {
-    MockApiClient.addMockResponse({
-      url: '/organizations/org-slug/events-spans-performance/',
-      statusCode: 504,
-    });
-    MockApiClient.addMockResponse({
-      url: '/organizations/org-slug/events/',
-      statusCode: 504,
-    });
-    const data = initializeData();
-
-    render(
-      <div>
-        <SpansList
-          location={data.location}
-          organization={data.organization}
-          trendView={data.eventView}
-          breakpoint={transaction.breakpoint!}
-          transaction={transaction}
-          trendChangeType={TrendChangeType.REGRESSION}
-        />
-        <FunctionsList
-          location={data.location}
-          organization={data.organization}
-          trendView={data.eventView}
-          breakpoint={transaction.breakpoint!}
-          transaction={transaction}
-          trendChangeType={TrendChangeType.REGRESSION}
-        />
-      </div>
-    );
-
-    await waitFor(() => {
-      expect(screen.getByTestId('error-indicator-spans')).toBeInTheDocument();
-      expect(screen.getByTestId('error-indicator-functions')).toBeInTheDocument();
-    });
-  });
-
-  it('renders spans list with no changes message', async () => {
-    MockApiClient.addMockResponse({
-      url: '/organizations/org-slug/events-spans-performance/',
-      body: spanResults,
-    });
-    MockApiClient.addMockResponse({
-      url: '/organizations/org-slug/events/',
-      body: {
-        data: functionResults,
-        meta: {},
-      },
-    });
-    const data = initializeData();
-
-    render(
-      <div>
-        <SpansList
-          location={data.location}
-          organization={data.organization}
-          trendView={data.eventView}
-          breakpoint={transaction.breakpoint!}
-          transaction={transaction}
-          trendChangeType={TrendChangeType.REGRESSION}
-        />
-        <FunctionsList
-          location={data.location}
-          organization={data.organization}
-          trendView={data.eventView}
-          breakpoint={transaction.breakpoint!}
-          transaction={transaction}
-          trendChangeType={TrendChangeType.REGRESSION}
-        />
-      </div>
-    );
-
-    await waitFor(() => {
-      expect(screen.getAllByTestId('empty-state')).toHaveLength(2);
-      expect(screen.getByTestId('spans-no-changes')).toBeInTheDocument();
-      expect(screen.getByTestId('functions-no-changes')).toBeInTheDocument();
-    });
-  });
-});

+ 0 - 353
static/app/views/performance/trends/changeExplorer.tsx

@@ -1,353 +0,0 @@
-import {Fragment} from 'react';
-import styled from '@emotion/styled';
-import type {Location} from 'history';
-import moment from 'moment-timezone';
-
-import {LinkButton} from 'sentry/components/button';
-import {getArbitraryRelativePeriod} from 'sentry/components/timeRangeSelector/utils';
-import {DEFAULT_RELATIVE_PERIODS} from 'sentry/constants';
-import {IconFire, IconOpen} from 'sentry/icons';
-import {t} from 'sentry/locale';
-import {space} from 'sentry/styles/space';
-import type {Organization} from 'sentry/types/organization';
-import type {Project} from 'sentry/types/project';
-import {trackAnalytics} from 'sentry/utils/analytics';
-import theme from 'sentry/utils/theme';
-import normalizeUrl from 'sentry/utils/url/normalizeUrl';
-import DetailPanel from 'sentry/views/insights/common/components/detailPanel';
-import {
-  DisplayModes,
-  transactionSummaryRouteWithQuery,
-} from 'sentry/views/performance/transactionSummary/utils';
-import {FunctionsList} from 'sentry/views/performance/trends/changeExplorerUtils/functionsList';
-import {MetricsTable} from 'sentry/views/performance/trends/changeExplorerUtils/metricsTable';
-import {SpansList} from 'sentry/views/performance/trends/changeExplorerUtils/spansList';
-import {Chart} from 'sentry/views/performance/trends/chart';
-import type {
-  NormalizedTrendsTransaction,
-  TrendParameter,
-  TrendsStats,
-  TrendView,
-} from 'sentry/views/performance/trends/types';
-import {TrendChangeType} from 'sentry/views/performance/trends/types';
-import {getTrendProjectId} from 'sentry/views/performance/trends/utils';
-
-type PerformanceChangeExplorerProps = {
-  collapsed: boolean;
-  isLoading: boolean;
-  location: Location;
-  onClose: () => void;
-  organization: Organization;
-  projects: Project[];
-  statsData: TrendsStats;
-  transaction: NormalizedTrendsTransaction;
-  trendChangeType: TrendChangeType;
-  trendFunction: string;
-  trendParameter: TrendParameter;
-  trendView: TrendView;
-};
-
-type ExplorerBodyProps = {
-  isLoading: boolean;
-  location: Location;
-  organization: Organization;
-  projects: Project[];
-  statsData: TrendsStats;
-  transaction: NormalizedTrendsTransaction;
-  trendChangeType: TrendChangeType;
-  trendFunction: string;
-  trendParameter: TrendParameter;
-  trendView: TrendView;
-};
-
-type HeaderProps = {
-  organization: Organization;
-  projects: Project[];
-  transaction: NormalizedTrendsTransaction;
-  trendChangeType: TrendChangeType;
-  trendFunction: string;
-  trendParameter: TrendParameter;
-  trendView: TrendView;
-};
-
-export function PerformanceChangeExplorer({
-  collapsed,
-  transaction,
-  onClose,
-  trendChangeType,
-  trendFunction,
-  trendView,
-  statsData,
-  isLoading,
-  organization,
-  projects,
-  trendParameter,
-  location,
-}: PerformanceChangeExplorerProps) {
-  return (
-    <DetailPanel detailKey={!collapsed ? transaction.transaction : ''} onClose={onClose}>
-      {!collapsed && (
-        <PanelBodyWrapper>
-          <ExplorerBody
-            transaction={transaction}
-            trendChangeType={trendChangeType}
-            trendFunction={trendFunction}
-            trendView={trendView}
-            statsData={statsData}
-            isLoading={isLoading}
-            organization={organization}
-            projects={projects}
-            trendParameter={trendParameter}
-            location={location}
-          />
-        </PanelBodyWrapper>
-      )}
-    </DetailPanel>
-  );
-}
-
-function ExplorerBody(props: ExplorerBodyProps) {
-  const {
-    transaction,
-    trendChangeType,
-    trendFunction,
-    trendView,
-    trendParameter,
-    isLoading,
-    location,
-    organization,
-    projects,
-  } = props;
-  const breakpointDate = transaction.breakpoint
-    ? moment(transaction.breakpoint * 1000).format('ddd, DD MMM YYYY HH:mm:ss z')
-    : '';
-
-  const start = moment(trendView.start).format('DD MMM YYYY HH:mm:ss z');
-  const end = moment(trendView.end).format('DD MMM YYYY HH:mm:ss z');
-  return (
-    <Fragment>
-      <Header
-        transaction={transaction}
-        trendChangeType={trendChangeType}
-        trendView={trendView}
-        projects={projects}
-        organization={organization}
-        trendFunction={trendFunction}
-        trendParameter={trendParameter}
-      />
-      <div style={{display: 'flex', gap: space(4)}}>
-        <InfoItem
-          label={
-            trendChangeType === TrendChangeType.REGRESSION
-              ? t('Regression Metric')
-              : t('Improvement Metric')
-          }
-          value={trendFunction}
-        />
-        <InfoItem label={t('Start Time')} value={breakpointDate} />
-      </div>
-      <GraphPanel data-test-id="pce-graph">
-        <strong>{`${trendParameter.label} (${trendFunction})`}</strong>
-        <ExplorerText color={theme.gray300} margin={`-${space(3)}`}>
-          {trendView.statsPeriod
-            ? DEFAULT_RELATIVE_PERIODS[trendView.statsPeriod] ||
-              getArbitraryRelativePeriod(trendView.statsPeriod)[trendView.statsPeriod]
-            : `${start} - ${end}`}
-        </ExplorerText>
-        <Chart
-          query={trendView.query}
-          project={trendView.project}
-          environment={trendView.environment}
-          start={trendView.start}
-          end={trendView.end}
-          statsPeriod={trendView.statsPeriod}
-          disableXAxis
-          disableLegend
-          neutralColor
-          {...props}
-        />
-      </GraphPanel>
-      <MetricsTable
-        isLoading={isLoading}
-        location={location}
-        transaction={transaction}
-        trendFunction={trendFunction}
-        trendView={trendView}
-        organization={organization}
-      />
-      <SpansList
-        location={location}
-        organization={organization}
-        trendView={trendView}
-        transaction={transaction}
-        breakpoint={transaction.breakpoint!}
-        trendChangeType={trendChangeType}
-      />
-      <FunctionsList
-        location={location}
-        organization={organization}
-        trendView={trendView}
-        transaction={transaction}
-        breakpoint={transaction.breakpoint!}
-        trendChangeType={trendChangeType}
-      />
-    </Fragment>
-  );
-}
-
-function InfoItem({label, value}: {label: string; value: string}) {
-  return (
-    <div>
-      <InfoLabel>{label}</InfoLabel>
-      <InfoText>{value}</InfoText>
-    </div>
-  );
-}
-
-function Header(props: HeaderProps) {
-  const {
-    transaction,
-    trendChangeType,
-    trendView,
-    projects,
-    organization,
-    trendFunction,
-    trendParameter,
-  } = props;
-
-  const regression = trendChangeType === TrendChangeType.REGRESSION;
-  const transactionSummaryLink = getTransactionSummaryLink(
-    trendView,
-    transaction,
-    projects,
-    organization,
-    trendFunction,
-    trendParameter
-  );
-
-  const handleClickAnalytics = () => {
-    trackAnalytics('performance_views.performance_change_explorer.summary_link_clicked', {
-      organization,
-      transaction: transaction.transaction,
-    });
-  };
-
-  return (
-    <HeaderWrapper data-test-id="pce-header">
-      <FireIcon regression={regression}>
-        <IconFire color="white" />
-      </FireIcon>
-      <HeaderTextWrapper>
-        <ChangeType regression={regression}>
-          {regression ? t('Ongoing Regression') : t('Ongoing Improvement')}
-        </ChangeType>
-        <TransactionNameWrapper>
-          <TransactionName>{transaction.transaction}</TransactionName>
-          <ViewTransactionButton
-            borderless
-            to={normalizeUrl(transactionSummaryLink)}
-            icon={<IconOpen />}
-            aria-label={t('View transaction summary')}
-            onClick={handleClickAnalytics}
-          />
-        </TransactionNameWrapper>
-      </HeaderTextWrapper>
-    </HeaderWrapper>
-  );
-}
-
-function getTransactionSummaryLink(
-  eventView: TrendView,
-  transaction: NormalizedTrendsTransaction,
-  projects: Project[],
-  organization: Organization,
-  currentTrendFunction: string,
-  trendParameter: TrendParameter
-) {
-  const summaryView = eventView.clone();
-  const projectID = getTrendProjectId(transaction, projects);
-  const target = transactionSummaryRouteWithQuery({
-    orgSlug: organization.slug,
-    transaction: String(transaction.transaction),
-    query: summaryView.generateQueryStringObject(),
-    projectID,
-    display: DisplayModes.TREND,
-    trendFunction: currentTrendFunction,
-    additionalQuery: {
-      trendParameter: trendParameter.column,
-    },
-  });
-  return target;
-}
-
-const PanelBodyWrapper = styled('div')`
-  padding: 0 ${space(2)};
-  margin-top: ${space(1)};
-`;
-
-const HeaderWrapper = styled('div')`
-  display: flex;
-  flex-wrap: nowrap;
-  margin-bottom: ${space(3)};
-`;
-const HeaderTextWrapper = styled('div')`
-  ${p => p.theme.overflowEllipsis};
-`;
-type ChangeTypeProps = {regression: boolean};
-
-const ChangeType = styled('p')<ChangeTypeProps>`
-  color: ${p => (p.regression ? p.theme.danger : p.theme.success)};
-  margin-bottom: 0;
-`;
-
-const FireIcon = styled('div')<ChangeTypeProps>`
-  padding: ${space(1.5)};
-  background-color: ${p => (p.regression ? p.theme.danger : p.theme.success)};
-  border-radius: ${space(0.5)};
-  margin-right: ${space(2)};
-  float: left;
-  height: 40px;
-`;
-
-const TransactionName = styled('h4')`
-  margin-right: ${space(1)};
-  margin-bottom: 0;
-  ${p => p.theme.overflowEllipsis};
-`;
-
-const TransactionNameWrapper = styled('div')`
-  display: flex;
-  align-items: center;
-  margin-bottom: ${space(3)};
-  max-width: fit-content;
-`;
-
-const ViewTransactionButton = styled(LinkButton)`
-  padding: 0;
-  height: min-content;
-  min-height: 0px;
-`;
-
-const InfoLabel = styled('strong')`
-  color: ${p => p.theme.gray300};
-`;
-const InfoText = styled('h3')`
-  font-weight: ${p => p.theme.fontWeightNormal};
-`;
-const GraphPanel = styled('div')`
-  border: 1px solid ${p => p.theme.border};
-  border-radius: ${p => p.theme.panelBorderRadius};
-  margin-bottom: ${space(2)};
-  padding: ${space(3)};
-  display: block;
-`;
-
-export const ExplorerText = styled('p')<{
-  align?: string;
-  color?: string;
-  margin?: string;
-}>`
-  margin-bottom: ${p => (p.margin ? p.margin : 0)};
-  color: ${p => p.color};
-  text-align: ${p => p.align};
-`;

+ 0 - 449
static/app/views/performance/trends/changeExplorerUtils/functionsList.tsx

@@ -1,449 +0,0 @@
-import {Fragment, useMemo} from 'react';
-import type {Location} from 'history';
-import moment from 'moment-timezone';
-
-import EmptyStateWarning from 'sentry/components/emptyStateWarning';
-import LoadingIndicator from 'sentry/components/loadingIndicator';
-import {IconWarning} from 'sentry/icons';
-import {t, tct} from 'sentry/locale';
-import {space} from 'sentry/styles/space';
-import type {Organization} from 'sentry/types/organization';
-import type {Project} from 'sentry/types/project';
-import {defined} from 'sentry/utils';
-import {trackAnalytics} from 'sentry/utils/analytics';
-import {useDiscoverQuery} from 'sentry/utils/discover/discoverQuery';
-import {DiscoverDatasets} from 'sentry/utils/discover/types';
-import {parsePeriodToHours} from 'sentry/utils/duration/parsePeriodToHours';
-import type {EventsResultsDataRow, Sort} from 'sentry/utils/profiling/hooks/types';
-import {useProfileFunctions} from 'sentry/utils/profiling/hooks/useProfileFunctions';
-import {generateProfileFlamechartRouteWithQuery} from 'sentry/utils/profiling/routes';
-import {MutableSearch} from 'sentry/utils/tokenizeSearch';
-import useProjects from 'sentry/utils/useProjects';
-import {
-  getQueryParams,
-  relativeChange,
-} from 'sentry/views/performance/trends/changeExplorerUtils/metricsTable';
-import {
-  ErrorWrapper,
-  ListItemWrapper,
-  ListLink,
-  TimeDifference,
-} from 'sentry/views/performance/trends/changeExplorerUtils/spansList';
-import type {
-  NormalizedTrendsTransaction,
-  TrendView,
-} from 'sentry/views/performance/trends/types';
-import {TrendChangeType} from 'sentry/views/performance/trends/types';
-import {getTrendProjectId} from 'sentry/views/performance/trends/utils';
-
-type FunctionsListProps = {
-  breakpoint: number;
-  location: Location;
-  organization: Organization;
-  transaction: NormalizedTrendsTransaction;
-  trendChangeType: TrendChangeType;
-  trendView: TrendView;
-};
-
-type AveragedSuspectFunction = EventsResultsDataRow<FunctionsField> & {
-  avgSumExclusiveTime: number;
-};
-
-type ChangedSuspectFunction = AveragedSuspectFunction & {
-  avgTimeDifference: number;
-  changeType: string;
-  percentChange: number;
-};
-
-type NumberedFunctionsListProps = {
-  isError: boolean;
-  isLoading: boolean;
-  limit: number;
-  organization: Organization;
-  transactionName: string;
-  functions?: ChangedSuspectFunction[];
-  project?: Project;
-};
-
-const functionsFields = [
-  'package',
-  'function',
-  'count()',
-  'p75()',
-  'sum()',
-  'examples()',
-] as const;
-
-export type FunctionsField = (typeof functionsFields)[number];
-
-export const FunctionChangeType = {
-  added: t('Added'),
-  removed: t('Removed'),
-  regressed: t('Regressed'),
-  improved: t('Improved'),
-};
-
-export function FunctionsList(props: FunctionsListProps) {
-  const {trendView, location, organization, breakpoint, transaction, trendChangeType} =
-    props;
-
-  const hours = trendView.statsPeriod ? parsePeriodToHours(trendView.statsPeriod) : 0;
-  const startTime = useMemo(
-    () =>
-      trendView.start ? trendView.start : moment().subtract(hours, 'h').toISOString(),
-    [hours, trendView.start]
-  );
-  const breakpointTime = breakpoint ? new Date(breakpoint * 1000).toISOString() : '';
-  const endTime = useMemo(
-    () => (trendView.end ? trendView.end : moment().toISOString()),
-    [trendView.end]
-  );
-
-  const {projects} = useProjects();
-  const projectID = getTrendProjectId(transaction, projects);
-
-  const functionsSort: Sort<FunctionsField> = {key: 'sum()', order: 'desc'};
-
-  const query = useMemo(() => {
-    const conditions = new MutableSearch('');
-    conditions.setFilterValues('transaction', [transaction.transaction]);
-    return conditions.formatString();
-  }, [transaction.transaction]);
-
-  const {
-    data: totalTransactionsBefore,
-    isPending: transactionsLoadingBefore,
-    isError: transactionsErrorBefore,
-  } = useDiscoverQuery(
-    getQueryParams(
-      startTime,
-      breakpointTime,
-      ['count'],
-      'transaction',
-      DiscoverDatasets.METRICS,
-      organization,
-      trendView,
-      transaction.transaction,
-      location
-    )
-  );
-
-  const transactionCountBefore = totalTransactionsBefore?.data
-    ? (totalTransactionsBefore?.data[0]['count()'] as number)
-    : 0;
-
-  const {
-    data: totalTransactionsAfter,
-    isPending: transactionsLoadingAfter,
-    isError: transactionsErrorAfter,
-  } = useDiscoverQuery(
-    getQueryParams(
-      breakpointTime,
-      endTime,
-      ['count'],
-      'transaction',
-      DiscoverDatasets.METRICS,
-      organization,
-      trendView,
-      transaction.transaction,
-      location
-    )
-  );
-
-  const transactionCountAfter = totalTransactionsAfter?.data
-    ? (totalTransactionsAfter?.data[0]['count()'] as number)
-    : 0;
-
-  const beforeFunctionsQuery = useProfileFunctions<FunctionsField>({
-    fields: functionsFields,
-    referrer: 'api.performance.performance-change-explorer',
-    sort: functionsSort,
-    query,
-    limit: 50,
-    datetime: {
-      end: breakpointTime,
-      start: startTime,
-      period: null,
-      utc: null,
-    },
-  });
-
-  const afterFunctionsQuery = useProfileFunctions<FunctionsField>({
-    fields: functionsFields,
-    referrer: 'api.performance.performance-change-explorer',
-    sort: functionsSort,
-    query,
-    limit: 50,
-    datetime: {
-      end: endTime,
-      start: breakpointTime,
-      period: null,
-      utc: null,
-    },
-  });
-
-  // need these averaged fields because comparing total self times may be inaccurate depending on
-  // where the breakpoint is
-  const functionsAveragedAfter = addAvgSumOfFunctions(
-    afterFunctionsQuery.data?.data,
-    transactionCountAfter
-  );
-  const functionsAveragedBefore = addAvgSumOfFunctions(
-    beforeFunctionsQuery.data?.data,
-    transactionCountBefore
-  );
-
-  const addedFunctions = addFunctionChangeFields(
-    findFunctionsNotIn(functionsAveragedAfter, functionsAveragedBefore),
-    true
-  );
-  const removedFunctions = addFunctionChangeFields(
-    findFunctionsNotIn(functionsAveragedBefore, functionsAveragedAfter),
-    false
-  );
-
-  const remainingFunctionsBefore = findFunctionsIn(
-    functionsAveragedBefore,
-    functionsAveragedAfter
-  );
-  const remainingFunctionsAfter = findFunctionsIn(
-    functionsAveragedAfter,
-    functionsAveragedBefore
-  );
-
-  const remainingFunctionsWithChange = addPercentChangeInFunctions(
-    remainingFunctionsBefore,
-    remainingFunctionsAfter
-  );
-
-  const allFunctionsUpdated = remainingFunctionsWithChange
-    ?.concat(addedFunctions ? addedFunctions : [])
-    .concat(removedFunctions ? removedFunctions : []);
-
-  // sorts all functions in descending order of avgTimeDifference (change in avg total self time)
-  const functionList = allFunctionsUpdated?.sort(
-    (a, b) => b.avgTimeDifference - a.avgTimeDifference
-  );
-
-  return (
-    <div>
-      <h6>{t('Relevant Suspect Functions')}</h6>
-      <NumberedFunctionsList
-        functions={
-          trendChangeType === TrendChangeType.REGRESSION
-            ? functionList
-            : functionList?.reverse()
-        }
-        project={projects.find(project => project.id === projectID)}
-        organization={organization}
-        transactionName={transaction.transaction}
-        limit={4}
-        isLoading={
-          transactionsLoadingBefore ||
-          transactionsLoadingAfter ||
-          beforeFunctionsQuery.isPending ||
-          afterFunctionsQuery.isPending
-        }
-        isError={
-          transactionsErrorBefore ||
-          transactionsErrorAfter ||
-          beforeFunctionsQuery.isError ||
-          afterFunctionsQuery.isError
-        }
-      />
-    </div>
-  );
-}
-
-/**
- *
- * adds an average of the sum() time so it is more comparable when the breakpoint
- * is not close to the middle of the timeseries
- */
-function addAvgSumOfFunctions(
-  suspectFunctions: EventsResultsDataRow<FunctionsField>[] | undefined,
-  transactionCount: number
-) {
-  return suspectFunctions?.map(susFunc => {
-    return {
-      ...susFunc,
-      avgSumExclusiveTime: (susFunc['sum()'] as number)
-        ? (susFunc['sum()'] as number) / transactionCount
-        : 0,
-    };
-  });
-}
-
-function findFunctionsNotIn(
-  initialFunctions: AveragedSuspectFunction[] | undefined,
-  comparingFunctions: AveragedSuspectFunction[] | undefined
-) {
-  return initialFunctions?.filter(initialValue => {
-    const functionInComparingSet = comparingFunctions?.find(
-      comparingValue =>
-        comparingValue.function === initialValue.function &&
-        comparingValue.package === initialValue.package
-    );
-    return functionInComparingSet === undefined;
-  });
-}
-
-function findFunctionsIn(
-  initialFunctions: AveragedSuspectFunction[] | undefined,
-  comparingFunctions: AveragedSuspectFunction[] | undefined
-) {
-  return initialFunctions?.filter(initialValue => {
-    const functionInComparingSet = comparingFunctions?.find(
-      comparingValue =>
-        comparingValue.function === initialValue.function &&
-        comparingValue.package === initialValue.package
-    );
-    return functionInComparingSet !== undefined;
-  });
-}
-
-function addFunctionChangeFields(
-  functions: AveragedSuspectFunction[] | undefined,
-  added: boolean
-): ChangedSuspectFunction[] | undefined {
-  // percent change is hardcoded to pass the 1% change threshold,
-  // avoid infinite values and reflect correct change type
-  return functions?.map(func => {
-    if (added) {
-      return {
-        ...func,
-        percentChange: 100,
-        avgTimeDifference: func.avgSumExclusiveTime,
-        changeType: FunctionChangeType.added,
-      };
-    }
-    return {
-      ...func,
-      percentChange: -100,
-      avgTimeDifference: 0 - func.avgSumExclusiveTime,
-      changeType: FunctionChangeType.removed,
-    };
-  });
-}
-
-function addPercentChangeInFunctions(
-  before: AveragedSuspectFunction[] | undefined,
-  after: AveragedSuspectFunction[] | undefined
-) {
-  return after?.map(functionAfter => {
-    const functionBefore = before?.find(
-      beforeValue =>
-        functionAfter.function === beforeValue.function &&
-        functionAfter.package === beforeValue.package
-    );
-    const percentageChange =
-      relativeChange(
-        functionBefore?.avgSumExclusiveTime || 0,
-        functionAfter.avgSumExclusiveTime
-      ) * 100;
-    return {
-      ...functionAfter,
-      percentChange: percentageChange,
-      avgTimeDifference:
-        functionAfter.avgSumExclusiveTime - (functionBefore?.avgSumExclusiveTime || 0),
-      changeType:
-        percentageChange < 0 ? FunctionChangeType.improved : FunctionChangeType.regressed,
-    };
-  });
-}
-
-export function NumberedFunctionsList(props: NumberedFunctionsListProps) {
-  const {project, transactionName, organization, limit, isLoading, isError, functions} =
-    props;
-
-  if (isLoading) {
-    return <LoadingIndicator />;
-  }
-
-  if (isError) {
-    return (
-      <ErrorWrapper>
-        <IconWarning
-          data-test-id="error-indicator-functions"
-          color="gray200"
-          size="xxl"
-        />
-        <p>{t('There was an issue finding suspect functions for this transaction')}</p>
-      </ErrorWrapper>
-    );
-  }
-
-  if (functions?.length === 0 || !functions) {
-    return (
-      <EmptyStateWarning>
-        <p data-test-id="functions-no-results">
-          {t('No results found for suspect functions')}
-        </p>
-      </EmptyStateWarning>
-    );
-  }
-
-  // percent change of a function must be more than 1%
-  const formattedFunctions = functions
-    ?.filter(func => (functions.length > 10 ? Math.abs(func.percentChange) >= 1 : true))
-    .slice(0, limit)
-    .map((func, index) => {
-      const profiles = func['examples()'] as string[];
-
-      let rendered = <Fragment>{func.function}</Fragment>;
-      if (defined(profiles[0])) {
-        const functionSummaryView = generateProfileFlamechartRouteWithQuery({
-          orgSlug: organization.slug,
-          projectSlug: project?.slug || '',
-          profileId: profiles[0],
-          query: {
-            frameName: func.function as string,
-            framePackage: func.package as string,
-          },
-        });
-
-        const handleClickAnalytics = () => {
-          trackAnalytics(
-            'performance_views.performance_change_explorer.function_link_clicked',
-            {
-              organization,
-              transaction: transactionName,
-              package: func.package as string,
-              function: func.function as string,
-              profile_id: profiles[0],
-            }
-          );
-        };
-
-        rendered = (
-          <ListLink to={functionSummaryView} onClick={handleClickAnalytics}>
-            {rendered}
-          </ListLink>
-        );
-      }
-
-      return (
-        <li key={`list-item-${index}`}>
-          <ListItemWrapper data-test-id="list-item">
-            <p style={{marginLeft: space(2)}}>
-              {tct('[changeType] suspect function', {changeType: func.changeType})}
-            </p>
-            {rendered}
-            <TimeDifference difference={func.avgTimeDifference / 1000000} />
-          </ListItemWrapper>
-        </li>
-      );
-    });
-
-  if (formattedFunctions?.length === 0) {
-    return (
-      <EmptyStateWarning>
-        <p data-test-id="functions-no-changes">
-          {t('No sizable changes in suspect functions')}
-        </p>
-      </EmptyStateWarning>
-    );
-  }
-
-  return <ol>{formattedFunctions}</ol>;
-}

+ 0 - 390
static/app/views/performance/trends/changeExplorerUtils/metricsTable.tsx

@@ -1,390 +0,0 @@
-import type {ReactNode} from 'react';
-import {useMemo} from 'react';
-import type {Location} from 'history';
-import moment from 'moment-timezone';
-
-import type {GridColumnOrder} from 'sentry/components/gridEditable';
-import GridEditable, {COL_WIDTH_UNDEFINED} from 'sentry/components/gridEditable';
-import SortLink from 'sentry/components/gridEditable/sortLink';
-import {t} from 'sentry/locale';
-import type {Organization} from 'sentry/types/organization';
-import type {TableData} from 'sentry/utils/discover/discoverQuery';
-import {useDiscoverQuery} from 'sentry/utils/discover/discoverQuery';
-import type EventView from 'sentry/utils/discover/eventView';
-import type {
-  AggregationKeyWithAlias,
-  ColumnType,
-  QueryFieldValue,
-} from 'sentry/utils/discover/fields';
-import {fieldAlignment} from 'sentry/utils/discover/fields';
-import {Container} from 'sentry/utils/discover/styles';
-import {DiscoverDatasets} from 'sentry/utils/discover/types';
-import {parsePeriodToHours} from 'sentry/utils/duration/parsePeriodToHours';
-import {formatPercentage} from 'sentry/utils/number/formatPercentage';
-import theme from 'sentry/utils/theme';
-import {MutableSearch} from 'sentry/utils/tokenizeSearch';
-import {TransactionThresholdMetric} from 'sentry/views/performance/transactionSummary/transactionThresholdModal';
-import {ExplorerText} from 'sentry/views/performance/trends/changeExplorer';
-import type {
-  NormalizedTrendsTransaction,
-  TrendsTransaction,
-  TrendView,
-} from 'sentry/views/performance/trends/types';
-import {TrendFunctionField} from 'sentry/views/performance/trends/types';
-
-type MetricsTableProps = {
-  isLoading: boolean;
-  location: Location;
-  organization: Organization;
-  transaction: NormalizedTrendsTransaction;
-  trendFunction: string;
-  trendView: TrendView;
-};
-
-const fieldsNeeded: AggregationKeyWithAlias[] = ['tps', 'p50', 'p95', 'failure_rate'];
-
-type MetricColumnKey = 'metric' | 'before' | 'after' | 'change';
-
-type MetricColumn = GridColumnOrder<MetricColumnKey>;
-
-type TableDataRow = Record<MetricColumnKey, any>;
-
-const MetricColumnOrder = ['metric', 'before', 'after', 'change'];
-
-export const COLUMNS: Record<MetricColumnKey, MetricColumn> = {
-  metric: {
-    key: 'metric',
-    name: t('Metric'),
-    width: COL_WIDTH_UNDEFINED,
-  },
-  before: {
-    key: 'before',
-    name: t('Before'),
-    width: COL_WIDTH_UNDEFINED,
-  },
-  after: {
-    key: 'after',
-    name: t('After'),
-    width: COL_WIDTH_UNDEFINED,
-  },
-  change: {
-    key: 'change',
-    name: t('Change'),
-    width: COL_WIDTH_UNDEFINED,
-  },
-};
-
-const COLUMN_TYPE: Record<MetricColumnKey, ColumnType> = {
-  metric: 'string',
-  before: 'duration',
-  after: 'duration',
-  change: 'percentage',
-};
-
-export function MetricsTable(props: MetricsTableProps) {
-  const {trendFunction, transaction, trendView, organization, location, isLoading} =
-    props;
-  const p50 =
-    trendFunction === TrendFunctionField.P50
-      ? getTrendsRowData(transaction, TrendFunctionField.P50)
-      : undefined;
-  const p95 =
-    trendFunction === TrendFunctionField.P95
-      ? getTrendsRowData(transaction, TrendFunctionField.P95)
-      : undefined;
-
-  const breakpoint = transaction.breakpoint;
-
-  const hours = trendView.statsPeriod ? parsePeriodToHours(trendView.statsPeriod) : 0;
-  const startTime = useMemo(
-    () =>
-      trendView.start ? trendView.start : moment().subtract(hours, 'h').toISOString(),
-    [hours, trendView.start]
-  );
-  const breakpointTime = breakpoint ? new Date(breakpoint * 1000).toISOString() : '';
-  const endTime = useMemo(
-    () => (trendView.end ? trendView.end : moment().toISOString()),
-    [trendView.end]
-  );
-
-  const {data: beforeBreakpoint, isPending: isLoadingBefore} = useDiscoverQuery(
-    getQueryParams(
-      startTime,
-      breakpointTime,
-      fieldsNeeded,
-      'transaction',
-      DiscoverDatasets.METRICS,
-      organization,
-      trendView,
-      transaction.transaction,
-      location
-    )
-  );
-
-  const {data: afterBreakpoint, isPending: isLoadingAfter} = useDiscoverQuery(
-    getQueryParams(
-      breakpointTime,
-      endTime,
-      fieldsNeeded,
-      'transaction',
-      DiscoverDatasets.METRICS,
-      organization,
-      trendView,
-      transaction.transaction,
-      location
-    )
-  );
-
-  const throughput: TableDataRow = getEventsRowData(
-    'tps()',
-    'Throughput',
-    'ps',
-    '-',
-    false,
-    beforeBreakpoint,
-    afterBreakpoint
-  );
-
-  const p50Events = !p50
-    ? getEventsRowData(
-        'p50()',
-        'P50',
-        'ms',
-        '-',
-        false,
-        beforeBreakpoint,
-        afterBreakpoint
-      )
-    : p50;
-
-  const p95Events = !p95
-    ? getEventsRowData(
-        'p95()',
-        'P95',
-        'ms',
-        '-',
-        false,
-        beforeBreakpoint,
-        afterBreakpoint
-      )
-    : p95;
-
-  const failureRate: TableDataRow = getEventsRowData(
-    'failure_rate()',
-    'Failure Rate',
-    '%',
-    0,
-    true,
-    beforeBreakpoint,
-    afterBreakpoint
-  );
-
-  const columnOrder = MetricColumnOrder.map(column => COLUMNS[column]);
-
-  return (
-    <GridEditable
-      data={[throughput, p50Events, p95Events, failureRate]}
-      columnOrder={columnOrder}
-      columnSortBy={[]}
-      grid={{
-        renderHeadCell,
-        renderBodyCell,
-      }}
-      isLoading={isLoadingBefore || isLoadingAfter || isLoading}
-    />
-  );
-}
-
-function getEventsRowData(
-  field: string,
-  rowTitle: string,
-  suffix: string,
-  nullValue: string | number,
-  percentage: boolean,
-  beforeData?: TableData,
-  afterData?: TableData
-): TableDataRow {
-  if (
-    beforeData?.data[0][field] !== undefined &&
-    afterData?.data[0][field] !== undefined
-  ) {
-    return {
-      metric: rowTitle,
-      before: !percentage
-        ? toFormattedNumber(beforeData.data[0][field].toString(), 1) + ' ' + suffix
-        : formatPercentage(beforeData.data[0][field] as number, 1),
-      after: !percentage
-        ? toFormattedNumber(afterData.data[0][field].toString(), 1) + ' ' + suffix
-        : formatPercentage(afterData.data[0][field] as number, 1),
-      change:
-        beforeData.data[0][field] && afterData.data[0][field]
-          ? formatPercentage(
-              relativeChange(
-                beforeData.data[0][field] as number,
-                afterData.data[0][field] as number
-              ),
-              1
-            )
-          : '-',
-    };
-  }
-  return {
-    metric: rowTitle,
-    before: nullValue,
-    after: nullValue,
-    change: '-',
-  };
-}
-
-function getTrendsRowData(
-  aggregateData: TrendsTransaction | undefined,
-  metric: TrendFunctionField
-): TableDataRow | undefined {
-  if (aggregateData) {
-    return {
-      metric: metric.toString().toUpperCase(),
-      before: aggregateData?.aggregate_range_1.toFixed(1) + ' ms',
-      after: aggregateData?.aggregate_range_2.toFixed(1) + ' ms',
-      change:
-        aggregateData?.trend_percentage !== 1
-          ? formatPercentage(aggregateData?.trend_percentage! - 1, 1)
-          : '-',
-    };
-  }
-  return undefined;
-}
-
-function getEventViewWithFields(
-  _organization: Organization,
-  eventView: EventView,
-  start: string,
-  end: string,
-  fields: AggregationKeyWithAlias[],
-  eventType: string,
-  transactionName: string,
-  dataset: DiscoverDatasets
-): EventView {
-  const newEventView = eventView.clone();
-  newEventView.start = start;
-  newEventView.end = end;
-  newEventView.statsPeriod = undefined;
-  newEventView.dataset = dataset;
-  newEventView.query = 'event.type:' + eventType + ' transaction:' + transactionName;
-  newEventView.additionalConditions = new MutableSearch('');
-
-  const chartFields: QueryFieldValue[] = fields.map(field => {
-    return {
-      kind: 'function',
-      function: [field, '', undefined, undefined],
-    };
-  });
-
-  return newEventView.withColumns(chartFields);
-}
-
-function toFormattedNumber(numberString: string, decimal: number) {
-  return parseFloat(numberString).toFixed(decimal);
-}
-
-export function relativeChange(before: number, after: number) {
-  return (after - before) / before;
-}
-
-function renderHeadCell(column: MetricColumn, _index: number): ReactNode {
-  const align = fieldAlignment(column.key, COLUMN_TYPE[column.key]);
-  return (
-    <SortLink
-      title={column.name}
-      align={align}
-      direction={undefined}
-      canSort={false}
-      generateSortLink={() => undefined}
-    />
-  );
-}
-
-export function renderBodyCell(
-  column: GridColumnOrder<MetricColumnKey>,
-  dataRow: TableDataRow
-) {
-  let data = '';
-  let color = '';
-  if (column.key === 'change') {
-    if (
-      dataRow[column.key] === '0%' ||
-      dataRow[column.key] === '+NaN%' ||
-      dataRow[column.key] === '-'
-    ) {
-      data = '-';
-    } else if (dataRow[column.key].charAt(0) !== '-') {
-      color = theme.red300;
-      data = '+' + dataRow[column.key];
-    } else {
-      color = theme.green300;
-      data = dataRow[column.key];
-    }
-  } else {
-    data = dataRow[column.key];
-  }
-
-  return (
-    <Container data-test-id={'pce-metrics-chart-row-' + column.key}>
-      <ExplorerText
-        data-test-id={'pce-metrics-text-' + column.key}
-        align={column.key !== 'metric' ? 'right' : 'left'}
-        color={color}
-      >
-        {data}
-      </ExplorerText>
-    </Container>
-  );
-}
-
-export function getQueryParams(
-  startTime: string,
-  endTime: string,
-  fields: AggregationKeyWithAlias[],
-  query: string,
-  dataset: DiscoverDatasets,
-  organization: Organization,
-  eventView: EventView,
-  transactionName: string,
-  location: Location
-) {
-  const newLocation = {
-    ...location,
-    start: startTime,
-    end: endTime,
-    statsPeriod: undefined,
-    dataset,
-    sort: undefined,
-    query: {
-      query: `event.type: ${query} transaction: ${transactionName}`,
-      statsPeriod: undefined,
-      start: startTime,
-      end: endTime,
-    },
-  };
-
-  const newEventView = getEventViewWithFields(
-    organization,
-    eventView,
-    startTime,
-    endTime,
-    fields,
-    query,
-    transactionName,
-    dataset
-  );
-
-  return {
-    eventView: newEventView,
-    location: newLocation,
-    orgSlug: organization.slug,
-    transactionName,
-    transactionThresholdMetric: TransactionThresholdMetric.TRANSACTION_DURATION,
-    options: {
-      refetchOnWindowFocus: false,
-    },
-  };
-}

+ 0 - 394
static/app/views/performance/trends/changeExplorerUtils/spansList.spec.tsx

@@ -1,394 +0,0 @@
-import moment from 'moment-timezone';
-
-import {initializeData} from 'sentry-test/performance/initializePerformanceData';
-import {render, screen, waitFor} from 'sentry-test/reactTestingLibrary';
-
-import type {ChangedSuspectSpan} from 'sentry/views/performance/trends/changeExplorerUtils/spansList';
-import {
-  NumberedSpansList,
-  SpanChangeType,
-} from 'sentry/views/performance/trends/changeExplorerUtils/spansList';
-import type {NormalizedTrendsTransaction} from 'sentry/views/performance/trends/types';
-
-const transaction: NormalizedTrendsTransaction = {
-  aggregate_range_1: 78.2757131147541,
-  aggregate_range_2: 110.50465131578949,
-  breakpoint: 1687262400,
-  project: 'sentry',
-  transaction: 'sentry.tasks.store.save_event',
-  trend_difference: 32.22893820103539,
-  trend_percentage: 1.411736117354651,
-  count: 3459,
-  received_at: moment(1601251200000),
-};
-
-const longSpanList: ChangedSuspectSpan[] = [
-  {
-    op: 'db',
-    group: '1',
-    description: 'span1',
-    frequency: 4,
-    count: 4,
-    avgOccurrences: undefined,
-    sumExclusiveTime: 345345,
-    p50ExclusiveTime: undefined,
-    p75ExclusiveTime: 25,
-    p95ExclusiveTime: undefined,
-    p99ExclusiveTime: undefined,
-    examples: [],
-    avgSumExclusiveTime: 34,
-    percentChange: 5,
-    avgTimeDifference: 20,
-    changeType: SpanChangeType.regressed,
-  },
-  {
-    op: 'db',
-    group: '2',
-    description: 'span2',
-    frequency: 4,
-    count: 4,
-    avgOccurrences: undefined,
-    sumExclusiveTime: 345345,
-    p50ExclusiveTime: undefined,
-    p75ExclusiveTime: 25,
-    p95ExclusiveTime: undefined,
-    p99ExclusiveTime: undefined,
-    examples: [],
-    avgSumExclusiveTime: 34,
-    percentChange: 2,
-    avgTimeDifference: 19,
-    changeType: SpanChangeType.regressed,
-  },
-  {
-    op: 'db',
-    group: '3',
-    description: 'span3',
-    frequency: 4,
-    count: 4,
-    avgOccurrences: undefined,
-    sumExclusiveTime: 345345,
-    p50ExclusiveTime: undefined,
-    p75ExclusiveTime: 25,
-    p95ExclusiveTime: undefined,
-    p99ExclusiveTime: undefined,
-    examples: [],
-    avgSumExclusiveTime: 34,
-    percentChange: 100,
-    avgTimeDifference: 18,
-    changeType: SpanChangeType.added,
-  },
-  {
-    op: 'db',
-    group: '4',
-    description: 'span4',
-    frequency: 4,
-    count: 4,
-    avgOccurrences: undefined,
-    sumExclusiveTime: 345345,
-    p50ExclusiveTime: undefined,
-    p75ExclusiveTime: 25,
-    p95ExclusiveTime: undefined,
-    p99ExclusiveTime: undefined,
-    examples: [],
-    avgSumExclusiveTime: 34,
-    percentChange: 0.5,
-    avgTimeDifference: 17,
-    changeType: SpanChangeType.regressed,
-  },
-  {
-    op: 'db',
-    group: '5',
-    description: 'span5',
-    frequency: 4,
-    count: 4,
-    avgOccurrences: undefined,
-    sumExclusiveTime: 345345,
-    p50ExclusiveTime: undefined,
-    p75ExclusiveTime: 25,
-    p95ExclusiveTime: undefined,
-    p99ExclusiveTime: undefined,
-    examples: [],
-    avgSumExclusiveTime: 34,
-    percentChange: 0.5,
-    avgTimeDifference: 16,
-    changeType: SpanChangeType.regressed,
-  },
-  {
-    op: 'db',
-    group: '6',
-    description: 'span6',
-    frequency: 4,
-    count: 4,
-    avgOccurrences: undefined,
-    sumExclusiveTime: 345345,
-    p50ExclusiveTime: undefined,
-    p75ExclusiveTime: 25,
-    p95ExclusiveTime: undefined,
-    p99ExclusiveTime: undefined,
-    examples: [],
-    avgSumExclusiveTime: 34,
-    percentChange: -5,
-    avgTimeDifference: -1,
-    changeType: SpanChangeType.improved,
-  },
-  {
-    op: 'db',
-    group: '7',
-    description: 'span7',
-    frequency: 4,
-    count: 4,
-    avgOccurrences: undefined,
-    sumExclusiveTime: 345345,
-    p50ExclusiveTime: undefined,
-    p75ExclusiveTime: 25,
-    p95ExclusiveTime: undefined,
-    p99ExclusiveTime: undefined,
-    examples: [],
-    avgSumExclusiveTime: 34,
-    percentChange: -0.5,
-    avgTimeDifference: -2,
-    changeType: SpanChangeType.improved,
-  },
-  {
-    op: 'db',
-    group: '8',
-    description: 'span8',
-    frequency: 4,
-    count: 4,
-    avgOccurrences: undefined,
-    sumExclusiveTime: 345345,
-    p50ExclusiveTime: undefined,
-    p75ExclusiveTime: 25,
-    p95ExclusiveTime: undefined,
-    p99ExclusiveTime: undefined,
-    examples: [],
-    avgSumExclusiveTime: 34,
-    percentChange: -100,
-    avgTimeDifference: -3,
-    changeType: SpanChangeType.removed,
-  },
-  {
-    op: 'db',
-    group: '9',
-    description: 'span9',
-    frequency: 4,
-    count: 4,
-    avgOccurrences: undefined,
-    sumExclusiveTime: 345345,
-    p50ExclusiveTime: undefined,
-    p75ExclusiveTime: 25,
-    p95ExclusiveTime: undefined,
-    p99ExclusiveTime: undefined,
-    examples: [],
-    avgSumExclusiveTime: 34,
-    percentChange: -100,
-    avgTimeDifference: -4,
-    changeType: SpanChangeType.removed,
-  },
-  {
-    op: 'db',
-    group: '10',
-    description: 'span10',
-    frequency: 4,
-    count: 4,
-    avgOccurrences: undefined,
-    sumExclusiveTime: 345345,
-    p50ExclusiveTime: undefined,
-    p75ExclusiveTime: 25,
-    p95ExclusiveTime: undefined,
-    p99ExclusiveTime: undefined,
-    examples: [],
-    avgSumExclusiveTime: 34,
-    percentChange: -3,
-    avgTimeDifference: -5,
-    changeType: SpanChangeType.improved,
-  },
-  {
-    op: 'db',
-    group: '11',
-    description: 'span11',
-    frequency: 4,
-    count: 4,
-    avgOccurrences: undefined,
-    sumExclusiveTime: 345345,
-    p50ExclusiveTime: undefined,
-    p75ExclusiveTime: 25,
-    p95ExclusiveTime: undefined,
-    p99ExclusiveTime: undefined,
-    examples: [],
-    avgSumExclusiveTime: 34,
-    percentChange: -0.5,
-    avgTimeDifference: -6,
-    changeType: SpanChangeType.improved,
-  },
-];
-
-const shortSpanList: ChangedSuspectSpan[] = [
-  {
-    op: 'db',
-    group: '1',
-    description: 'span1',
-    frequency: 4,
-    count: 4,
-    avgOccurrences: undefined,
-    sumExclusiveTime: 345345,
-    p50ExclusiveTime: undefined,
-    p75ExclusiveTime: 25,
-    p95ExclusiveTime: undefined,
-    p99ExclusiveTime: undefined,
-    examples: [],
-    avgSumExclusiveTime: 34,
-    percentChange: 5,
-    avgTimeDifference: 20,
-    changeType: SpanChangeType.regressed,
-  },
-  {
-    op: 'db',
-    group: '2',
-    description: 'span2',
-    frequency: 4,
-    count: 4,
-    avgOccurrences: undefined,
-    sumExclusiveTime: 345345,
-    p50ExclusiveTime: undefined,
-    p75ExclusiveTime: 25,
-    p95ExclusiveTime: undefined,
-    p99ExclusiveTime: undefined,
-    examples: [],
-    avgSumExclusiveTime: 34,
-    percentChange: 100,
-    avgTimeDifference: 19,
-    changeType: SpanChangeType.added,
-  },
-  {
-    op: 'db',
-    group: '3',
-    description: 'span3',
-    frequency: 4,
-    count: 4,
-    avgOccurrences: undefined,
-    sumExclusiveTime: 345345,
-    p50ExclusiveTime: undefined,
-    p75ExclusiveTime: 25,
-    p95ExclusiveTime: undefined,
-    p99ExclusiveTime: undefined,
-    examples: [],
-    avgSumExclusiveTime: 34,
-    percentChange: -0.5,
-    avgTimeDifference: -3,
-    changeType: SpanChangeType.improved,
-  },
-  {
-    op: 'db',
-    group: '4',
-    description: 'span4',
-    frequency: 4,
-    count: 4,
-    avgOccurrences: undefined,
-    sumExclusiveTime: 345345,
-    p50ExclusiveTime: undefined,
-    p75ExclusiveTime: 25,
-    p95ExclusiveTime: undefined,
-    p99ExclusiveTime: undefined,
-    examples: [],
-    avgSumExclusiveTime: 34,
-    percentChange: -100,
-    avgTimeDifference: -17,
-    changeType: SpanChangeType.removed,
-  },
-];
-
-describe('Performance > Trends > Performance Change Explorer > Spans List', function () {
-  it('renders spans list for regression', async () => {
-    const data = initializeData();
-
-    render(
-      <NumberedSpansList
-        spans={longSpanList}
-        location={data.location}
-        organization={data.organization}
-        transactionName={transaction.transaction}
-        limit={6}
-        isLoading={false}
-        isError={false}
-      />
-    );
-
-    await waitFor(() => {
-      expect(screen.getAllByTestId('list-item')[0]).toHaveTextContent('span1');
-      expect(screen.getAllByTestId('list-item')[0]).toHaveTextContent('Regressed');
-      expect(screen.getAllByTestId('list-item')[1]).toHaveTextContent('span2');
-      expect(screen.getAllByTestId('list-item')[1]).toHaveTextContent('Regressed');
-      expect(screen.getAllByTestId('list-item')[2]).toHaveTextContent('span3');
-      expect(screen.getAllByTestId('list-item')[2]).toHaveTextContent('Added');
-      expect(screen.getAllByTestId('list-item')[3]).toHaveTextContent('span6');
-      expect(screen.getAllByTestId('list-item')[3]).toHaveTextContent('Improved');
-      expect(screen.getAllByTestId('list-item')[4]).toHaveTextContent('span8');
-      expect(screen.getAllByTestId('list-item')[4]).toHaveTextContent('Removed');
-      expect(screen.getAllByTestId('list-item')[5]).toHaveTextContent('span9');
-      expect(screen.getAllByTestId('list-item')[5]).toHaveTextContent('Removed');
-      expect(screen.getAllByTestId('list-delta')).toHaveLength(6);
-    });
-  });
-
-  it('renders spans list for improvement', async () => {
-    const data = initializeData();
-
-    render(
-      <NumberedSpansList
-        spans={longSpanList.reverse()}
-        location={data.location}
-        organization={data.organization}
-        transactionName={transaction.transaction}
-        limit={6}
-        isLoading={false}
-        isError={false}
-      />
-    );
-
-    await waitFor(() => {
-      expect(screen.getAllByTestId('list-item')[0]).toHaveTextContent('span10');
-      expect(screen.getAllByTestId('list-item')[0]).toHaveTextContent('Improved');
-      expect(screen.getAllByTestId('list-item')[1]).toHaveTextContent('span9');
-      expect(screen.getAllByTestId('list-item')[1]).toHaveTextContent('Removed');
-      expect(screen.getAllByTestId('list-item')[2]).toHaveTextContent('span8');
-      expect(screen.getAllByTestId('list-item')[2]).toHaveTextContent('Removed');
-      expect(screen.getAllByTestId('list-item')[3]).toHaveTextContent('span6');
-      expect(screen.getAllByTestId('list-item')[3]).toHaveTextContent('Improved');
-      expect(screen.getAllByTestId('list-item')[4]).toHaveTextContent('span3');
-      expect(screen.getAllByTestId('list-item')[4]).toHaveTextContent('Added');
-      expect(screen.getAllByTestId('list-item')[5]).toHaveTextContent('span2');
-      expect(screen.getAllByTestId('list-item')[5]).toHaveTextContent('Regressed');
-      expect(screen.getAllByTestId('list-delta')).toHaveLength(6);
-    });
-  });
-
-  it('renders spans list for smaller changed spans list', async () => {
-    const data = initializeData();
-
-    render(
-      <NumberedSpansList
-        spans={shortSpanList}
-        location={data.location}
-        organization={data.organization}
-        transactionName={transaction.transaction}
-        limit={6}
-        isLoading={false}
-        isError={false}
-      />
-    );
-
-    await waitFor(() => {
-      expect(screen.getAllByTestId('list-item')[0]).toHaveTextContent('span1');
-      expect(screen.getAllByTestId('list-item')[0]).toHaveTextContent('Regressed');
-      expect(screen.getAllByTestId('list-item')[1]).toHaveTextContent('span2');
-      expect(screen.getAllByTestId('list-item')[1]).toHaveTextContent('Added');
-      expect(screen.getAllByTestId('list-item')[2]).toHaveTextContent('span3');
-      expect(screen.getAllByTestId('list-item')[2]).toHaveTextContent('Improved');
-      expect(screen.getAllByTestId('list-item')[3]).toHaveTextContent('span4');
-      expect(screen.getAllByTestId('list-item')[3]).toHaveTextContent('Removed');
-      expect(screen.getAllByTestId('list-item')[4]).toBeUndefined();
-      expect(screen.getAllByTestId('list-delta')).toHaveLength(4);
-    });
-  });
-});

+ 0 - 559
static/app/views/performance/trends/changeExplorerUtils/spansList.tsx

@@ -1,559 +0,0 @@
-import {useMemo} from 'react';
-import styled from '@emotion/styled';
-import type {Location} from 'history';
-import moment from 'moment-timezone';
-
-import EmptyStateWarning from 'sentry/components/emptyStateWarning';
-import Link from 'sentry/components/links/link';
-import LoadingIndicator from 'sentry/components/loadingIndicator';
-import {IconWarning} from 'sentry/icons';
-import {t, tct} from 'sentry/locale';
-import {space} from 'sentry/styles/space';
-import type {Organization} from 'sentry/types/organization';
-import {trackAnalytics} from 'sentry/utils/analytics';
-import {useDiscoverQuery} from 'sentry/utils/discover/discoverQuery';
-import {DiscoverDatasets} from 'sentry/utils/discover/types';
-import {parsePeriodToHours} from 'sentry/utils/duration/parsePeriodToHours';
-import SuspectSpansQuery from 'sentry/utils/performance/suspectSpans/suspectSpansQuery';
-import type {
-  SuspectSpan,
-  SuspectSpans,
-} from 'sentry/utils/performance/suspectSpans/types';
-import theme from 'sentry/utils/theme';
-import {MutableSearch} from 'sentry/utils/tokenizeSearch';
-import useProjects from 'sentry/utils/useProjects';
-import {spanDetailsRouteWithQuery} from 'sentry/views/performance/transactionSummary/transactionSpans/spanDetails/utils';
-import type {SpanSortOption} from 'sentry/views/performance/transactionSummary/transactionSpans/types';
-import {
-  SpanSortOthers,
-  SpanSortPercentiles,
-} from 'sentry/views/performance/transactionSummary/transactionSpans/types';
-import {
-  getSuspectSpanSortFromLocation,
-  SPAN_SORT_TO_FIELDS,
-} from 'sentry/views/performance/transactionSummary/transactionSpans/utils';
-import {
-  getQueryParams,
-  relativeChange,
-} from 'sentry/views/performance/trends/changeExplorerUtils/metricsTable';
-import type {
-  NormalizedTrendsTransaction,
-  TrendView,
-} from 'sentry/views/performance/trends/types';
-import {TrendChangeType} from 'sentry/views/performance/trends/types';
-import {getTrendProjectId} from 'sentry/views/performance/trends/utils';
-
-type SpansListProps = {
-  breakpoint: number;
-  location: Location;
-  organization: Organization;
-  transaction: NormalizedTrendsTransaction;
-  trendChangeType: TrendChangeType;
-  trendView: TrendView;
-};
-
-type AveragedSuspectSpan = SuspectSpan & {
-  avgSumExclusiveTime: number;
-};
-
-export type ChangedSuspectSpan = AveragedSuspectSpan & {
-  avgTimeDifference: number;
-  changeType: string;
-  percentChange: number;
-};
-
-type NumberedSpansListProps = {
-  isError: boolean;
-  isLoading: boolean;
-  limit: number;
-  location: Location;
-  organization: Organization;
-  transactionName: string;
-  projectID?: string;
-  spans?: ChangedSuspectSpan[];
-};
-
-export const SpanChangeType = {
-  added: t('Added'),
-  removed: t('Removed'),
-  regressed: t('Regressed'),
-  improved: t('Improved'),
-};
-
-export function SpansList(props: SpansListProps) {
-  const {trendView, location, organization, breakpoint, transaction, trendChangeType} =
-    props;
-
-  const hours = trendView.statsPeriod ? parsePeriodToHours(trendView.statsPeriod) : 0;
-  const startTime = useMemo(
-    () =>
-      trendView.start ? trendView.start : moment().subtract(hours, 'h').toISOString(),
-    [hours, trendView.start]
-  );
-  const breakpointTime = breakpoint ? new Date(breakpoint * 1000).toISOString() : '';
-  const endTime = useMemo(
-    () => (trendView.end ? trendView.end : moment().toISOString()),
-    [trendView.end]
-  );
-
-  const {projects} = useProjects();
-  const projectID = getTrendProjectId(transaction, projects);
-
-  const beforeLocation = updateLocation(
-    location,
-    startTime,
-    breakpointTime,
-    transaction,
-    projectID
-  );
-
-  const beforeSort = getSuspectSpanSortFromLocation(beforeLocation, 'spanSort');
-
-  const beforeEventView = updateEventView(
-    trendView,
-    startTime,
-    breakpointTime,
-    transaction,
-    beforeSort,
-    projectID
-  );
-
-  const beforeFields = SPAN_SORT_TO_FIELDS[beforeSort.field];
-  beforeEventView.fields = beforeFields ? beforeFields.map(field => ({field})) : [];
-
-  const afterLocation = updateLocation(
-    location,
-    startTime,
-    breakpointTime,
-    transaction,
-    projectID
-  );
-
-  const afterSort = getSuspectSpanSortFromLocation(afterLocation, 'spanSort');
-
-  const afterEventView = updateEventView(
-    trendView,
-    breakpointTime,
-    endTime,
-    transaction,
-    afterSort,
-    projectID
-  );
-
-  const afterFields = SPAN_SORT_TO_FIELDS[afterSort.field];
-  afterEventView.fields = afterFields ? afterFields.map(field => ({field})) : [];
-
-  const {
-    data: totalTransactionsBefore,
-    isPending: transactionsLoadingBefore,
-    isError: transactionsErrorBefore,
-  } = useDiscoverQuery(
-    getQueryParams(
-      startTime,
-      breakpointTime,
-      ['count'],
-      'transaction',
-      DiscoverDatasets.METRICS,
-      organization,
-      trendView,
-      transaction.transaction,
-      location
-    )
-  );
-
-  const transactionCountBefore = totalTransactionsBefore?.data
-    ? (totalTransactionsBefore?.data[0]['count()'] as number)
-    : 0;
-
-  const {
-    data: totalTransactionsAfter,
-    isPending: transactionsLoadingAfter,
-    isError: transactionsErrorAfter,
-  } = useDiscoverQuery(
-    getQueryParams(
-      breakpointTime,
-      endTime,
-      ['count'],
-      'transaction',
-      DiscoverDatasets.METRICS,
-      organization,
-      trendView,
-      transaction.transaction,
-      location
-    )
-  );
-
-  const transactionCountAfter = totalTransactionsAfter?.data
-    ? (totalTransactionsAfter?.data[0]['count()'] as number)
-    : 0;
-
-  return (
-    <SuspectSpansQuery
-      location={beforeLocation}
-      orgSlug={organization.slug}
-      eventView={beforeEventView}
-      limit={50}
-      perSuspect={0}
-    >
-      {({
-        suspectSpans: suspectSpansBefore,
-        isLoading: spansLoadingBefore,
-        error: spansErrorBefore,
-      }) => {
-        const hasSpansErrorBefore = spansErrorBefore !== null;
-        return (
-          <SuspectSpansQuery
-            location={afterLocation}
-            orgSlug={organization.slug}
-            eventView={afterEventView}
-            limit={50}
-            perSuspect={0}
-          >
-            {({
-              suspectSpans: suspectSpansAfter,
-              isLoading: spansLoadingAfter,
-              error: spansErrorAfter,
-            }) => {
-              const hasSpansErrorAfter = spansErrorAfter !== null;
-
-              // need these averaged fields because comparing total self times may be inaccurate depending on
-              // where the breakpoint is
-              const spansAveragedAfter = addAvgSumExclusiveTime(
-                suspectSpansAfter,
-                transactionCountAfter
-              );
-              const spansAveragedBefore = addAvgSumExclusiveTime(
-                suspectSpansBefore,
-                transactionCountBefore
-              );
-
-              const addedSpans = addSpanChangeFields(
-                findSpansNotIn(spansAveragedAfter, spansAveragedBefore),
-                true
-              );
-              const removedSpans = addSpanChangeFields(
-                findSpansNotIn(spansAveragedBefore, spansAveragedAfter),
-                false
-              );
-
-              const remainingSpansBefore = findSpansIn(
-                spansAveragedBefore,
-                spansAveragedAfter
-              );
-              const remainingSpansAfter = findSpansIn(
-                spansAveragedAfter,
-                spansAveragedBefore
-              );
-
-              const remainingSpansWithChange = addPercentChangeInSpans(
-                remainingSpansBefore,
-                remainingSpansAfter
-              );
-
-              const allSpansUpdated = remainingSpansWithChange
-                ?.concat(addedSpans ? addedSpans : [])
-                .concat(removedSpans ? removedSpans : []);
-
-              // sorts all spans in descending order of avgTimeDifference (change in avg total self time)
-              const spanList = allSpansUpdated?.sort(
-                (a, b) => b.avgTimeDifference - a.avgTimeDifference
-              );
-              // reverse the span list when trendChangeType is improvement so most negative (improved) change is first
-              return (
-                <div style={{marginTop: space(4)}}>
-                  <h6>{t('Relevant Suspect Spans')}</h6>
-                  <NumberedSpansList
-                    spans={
-                      trendChangeType === TrendChangeType.REGRESSION
-                        ? spanList
-                        : spanList?.reverse()
-                    }
-                    projectID={projectID}
-                    location={location}
-                    organization={organization}
-                    transactionName={transaction.transaction}
-                    limit={4}
-                    isLoading={
-                      transactionsLoadingBefore ||
-                      transactionsLoadingAfter ||
-                      spansLoadingBefore ||
-                      spansLoadingAfter
-                    }
-                    isError={
-                      transactionsErrorBefore ||
-                      transactionsErrorAfter ||
-                      hasSpansErrorBefore ||
-                      hasSpansErrorAfter
-                    }
-                  />
-                </div>
-              );
-            }}
-          </SuspectSpansQuery>
-        );
-      }}
-    </SuspectSpansQuery>
-  );
-}
-
-function updateLocation(
-  location: Location,
-  start: string,
-  end: string,
-  transaction: NormalizedTrendsTransaction,
-  projectID?: string
-) {
-  return {
-    ...location,
-    start,
-    end,
-    statsPeriod: undefined,
-    sort: SpanSortOthers.SUM_EXCLUSIVE_TIME,
-    project: projectID,
-    query: {
-      query: 'transaction:' + transaction.transaction,
-      statsPeriod: undefined,
-      start,
-      end,
-      project: projectID,
-    },
-  };
-}
-
-function updateEventView(
-  trendView: TrendView,
-  start: string,
-  end: string,
-  transaction: NormalizedTrendsTransaction,
-  sort: SpanSortOption,
-  projectID?: string
-) {
-  const newEventView = trendView.clone();
-  newEventView.start = start;
-  newEventView.end = end;
-  newEventView.statsPeriod = undefined;
-  newEventView.query = `event.type:transaction transaction:${transaction.transaction}`;
-  newEventView.project = projectID ? [parseInt(projectID, 10)] : [];
-  newEventView.additionalConditions = new MutableSearch('');
-  return newEventView
-    .withColumns(
-      [...Object.values(SpanSortOthers), ...Object.values(SpanSortPercentiles)].map(
-        field => ({kind: 'field', field})
-      )
-    )
-    .withSorts([{kind: 'desc', field: sort.field}]);
-}
-
-function findSpansNotIn(
-  initialSpans: AveragedSuspectSpan[] | undefined,
-  comparingSpans: AveragedSuspectSpan[] | undefined
-) {
-  return initialSpans?.filter(initialValue => {
-    const spanInComparingSet = comparingSpans?.find(
-      comparingValue =>
-        comparingValue.op === initialValue.op &&
-        comparingValue.group === initialValue.group
-    );
-    return spanInComparingSet === undefined;
-  });
-}
-
-function findSpansIn(
-  initialSpans: AveragedSuspectSpan[] | undefined,
-  comparingSpans: AveragedSuspectSpan[] | undefined
-) {
-  return initialSpans?.filter(initialValue => {
-    const spanInComparingSet = comparingSpans?.find(
-      comparingValue =>
-        comparingValue.op === initialValue.op &&
-        comparingValue.group === initialValue.group
-    );
-    return spanInComparingSet !== undefined;
-  });
-}
-
-/**
- *
- * adds an average of the sumExclusive time so it is more comparable when the breakpoint
- * is not close to the middle of the timeseries
- */
-function addAvgSumExclusiveTime(
-  suspectSpans: SuspectSpans | null,
-  transactionCount: number
-) {
-  return suspectSpans?.map(span => {
-    return {
-      ...span,
-      avgSumExclusiveTime: span.sumExclusiveTime
-        ? span.sumExclusiveTime / transactionCount
-        : 0,
-    };
-  });
-}
-
-function addPercentChangeInSpans(
-  before: AveragedSuspectSpan[] | undefined,
-  after: AveragedSuspectSpan[] | undefined
-) {
-  return after?.map(spanAfter => {
-    const spanBefore = before?.find(
-      beforeValue =>
-        spanAfter.op === beforeValue.op && spanAfter.group === beforeValue.group
-    );
-    const percentageChange =
-      relativeChange(
-        spanBefore?.avgSumExclusiveTime || 0,
-        spanAfter.avgSumExclusiveTime
-      ) * 100;
-    return {
-      ...spanAfter,
-      percentChange: percentageChange,
-      avgTimeDifference:
-        spanAfter.avgSumExclusiveTime - (spanBefore?.avgSumExclusiveTime || 0),
-      changeType:
-        percentageChange < 0 ? SpanChangeType.improved : SpanChangeType.regressed,
-    };
-  });
-}
-
-function addSpanChangeFields(
-  spans: AveragedSuspectSpan[] | undefined,
-  added: boolean
-): ChangedSuspectSpan[] | undefined {
-  // percent change is hardcoded to pass the 1% change threshold,
-  // avoid infinite values and reflect correct change type
-  return spans?.map(span => {
-    if (added) {
-      return {
-        ...span,
-        percentChange: 100,
-        avgTimeDifference: span.avgSumExclusiveTime,
-        changeType: SpanChangeType.added,
-      };
-    }
-    return {
-      ...span,
-      percentChange: -100,
-      avgTimeDifference: 0 - span.avgSumExclusiveTime,
-      changeType: SpanChangeType.removed,
-    };
-  });
-}
-
-export function TimeDifference({difference}: {difference: number}) {
-  const positive = difference >= 0;
-  const roundedDifference = difference.toPrecision(3);
-  return (
-    <p
-      style={{
-        alignSelf: 'end',
-        color: positive ? theme.red300 : theme.green300,
-        marginLeft: space(2),
-      }}
-      data-test-id="list-delta"
-    >
-      {positive ? `+${roundedDifference} ms` : `${roundedDifference} ms`}
-    </p>
-  );
-}
-
-export function NumberedSpansList(props: NumberedSpansListProps) {
-  const {
-    spans,
-    projectID,
-    location,
-    transactionName,
-    organization,
-    limit,
-    isLoading,
-    isError,
-  } = props;
-
-  if (isLoading) {
-    return <LoadingIndicator />;
-  }
-
-  if (isError) {
-    return (
-      <ErrorWrapper>
-        <IconWarning data-test-id="error-indicator-spans" color="gray200" size="xxl" />
-        <p>{t('There was an issue finding suspect spans for this transaction')}</p>
-      </ErrorWrapper>
-    );
-  }
-
-  if (spans?.length === 0 || !spans) {
-    return (
-      <EmptyStateWarning>
-        <p data-test-id="spans-no-results">{t('No results found for suspect spans')}</p>
-      </EmptyStateWarning>
-    );
-  }
-
-  // percent change of a span must be more than 1%
-  const formattedSpans = spans
-    ?.filter(span => (spans.length > 10 ? Math.abs(span.percentChange) >= 1 : true))
-    .slice(0, limit)
-    .map((span, index) => {
-      const spanDetailsPage = spanDetailsRouteWithQuery({
-        orgSlug: organization.slug,
-        transaction: transactionName,
-        query: location.query,
-        spanSlug: {op: span.op, group: span.group},
-        projectID,
-      });
-
-      const handleClickAnalytics = () => {
-        trackAnalytics(
-          'performance_views.performance_change_explorer.span_link_clicked',
-          {
-            organization,
-            transaction: transactionName,
-            op: span.op,
-            group: span.group,
-          }
-        );
-      };
-
-      return (
-        <li key={`list-item-${index}`}>
-          <ListItemWrapper data-test-id="list-item">
-            <p style={{marginLeft: space(2)}}>
-              {tct('[changeType] suspect span', {changeType: span.changeType})}
-            </p>
-            <ListLink to={spanDetailsPage} onClick={handleClickAnalytics}>
-              {span.description ? `${span.op} - ${span.description}` : span.op}
-            </ListLink>
-            <TimeDifference difference={span.avgTimeDifference} />
-          </ListItemWrapper>
-        </li>
-      );
-    });
-
-  if (formattedSpans?.length === 0) {
-    return (
-      <EmptyStateWarning>
-        <p data-test-id="spans-no-changes">{t('No sizable changes in suspect spans')}</p>
-      </EmptyStateWarning>
-    );
-  }
-
-  return <ol>{formattedSpans}</ol>;
-}
-
-export const ListLink = styled(Link)`
-  margin-left: ${space(1)};
-  ${p => p.theme.overflowEllipsis}
-`;
-export const ListItemWrapper = styled('div')`
-  display: flex;
-  white-space: nowrap;
-`;
-
-export const ErrorWrapper = styled('div')`
-  display: flex;
-  margin-top: ${space(4)};
-  flex-direction: column;
-  align-items: center;
-  gap: ${space(3)};
-`;

+ 4 - 39
static/app/views/performance/trends/changedTransactions.tsx

@@ -1,9 +1,8 @@
-import {Fragment, useCallback, useState} from 'react';
+import {Fragment, useCallback} from 'react';
 import styled from '@emotion/styled';
 import type {Location} from 'history';
 
 import type {Client} from 'sentry/api';
-import Feature from 'sentry/components/acl/feature';
 import {HeaderTitleLegend} from 'sentry/components/charts/styles';
 import Count from 'sentry/components/count';
 import {DropdownMenu} from 'sentry/components/dropdownMenu';
@@ -39,7 +38,6 @@ import {
   DisplayModes,
   transactionSummaryRouteWithQuery,
 } from 'sentry/views/performance/transactionSummary/utils';
-import {PerformanceChangeExplorer} from 'sentry/views/performance/trends/changeExplorer';
 import getSelectedQueryKey from 'sentry/views/performance/trends/utils/getSelectedQueryKey';
 import {getSelectedTransaction} from 'sentry/views/performance/utils/getSelectedTransaction';
 
@@ -47,9 +45,7 @@ import Chart from './chart';
 import type {
   NormalizedTrendsTransaction,
   TrendFunctionField,
-  TrendParameter,
   TrendParameterColumn,
-  TrendsStats,
   TrendView,
 } from './types';
 import {TrendChangeType} from './types';
@@ -323,14 +319,11 @@ function ChangedTransactions(props: Props) {
                           transactions={transactionsList}
                           location={location}
                           projects={projects}
-                          statsData={statsData}
                           handleSelectTransaction={handleChangeSelected(
                             location,
                             organization,
                             trendChangeType
                           )}
-                          isLoading={isLoading}
-                          trendParameter={trendParameter}
                         />
                       ))}
                     </Fragment>
@@ -360,15 +353,12 @@ type TrendsListItemProps = {
   currentTrendFunction: string;
   handleSelectTransaction: (transaction: NormalizedTrendsTransaction) => void;
   index: number;
-  isLoading: boolean;
   location: Location;
   organization: Organization;
   projects: Project[];
-  statsData: TrendsStats;
   transaction: NormalizedTrendsTransaction;
   transactions: NormalizedTrendsTransaction[];
   trendChangeType: TrendChangeType;
-  trendParameter: TrendParameter;
   trendView: TrendView;
 };
 
@@ -385,14 +375,9 @@ function TrendsListItem(props: TrendsListItemProps) {
     projects,
     handleSelectTransaction,
     trendView,
-    statsData,
-    isLoading,
-    trendParameter,
   } = props;
   const color = trendToColor[trendChangeType].default;
 
-  const [openedTransaction, setOpenedTransaction] = useState<null | string>(null);
-
   const selectedTransaction = getSelectedTransaction(
     location,
     trendChangeType,
@@ -471,7 +456,7 @@ function TrendsListItem(props: TrendsListItemProps) {
             </RadioLineItem>
           )}
         </ItemRadioContainer>
-        <TransactionSummaryLink {...props} onItemClicked={setOpenedTransaction} />
+        <TransactionSummaryLink {...props} />
         <ItemTransactionPercentage>
           <Tooltip title={percentChangeExplanation}>
             <Fragment>
@@ -540,22 +525,6 @@ function TrendsListItem(props: TrendsListItemProps) {
           <ValueDelta {...props} />
         </ItemTransactionStatus>
       </ListItemContainer>
-      <Feature features="performance-change-explorer">
-        <PerformanceChangeExplorer
-          collapsed={openedTransaction === null}
-          onClose={() => setOpenedTransaction(null)}
-          transaction={transaction}
-          trendChangeType={trendChangeType}
-          trendFunction={currentTrendFunction}
-          trendView={trendView}
-          statsData={statsData}
-          isLoading={isLoading}
-          organization={organization}
-          projects={projects}
-          trendParameter={trendParameter}
-          location={location}
-        />
-      </Feature>
     </Fragment>
   );
 }
@@ -592,9 +561,7 @@ function ValueDelta({transaction, trendChangeType}: TrendsListItemProps) {
   );
 }
 
-type TransactionSummaryLinkProps = TrendsListItemProps & {
-  onItemClicked: React.Dispatch<React.SetStateAction<null | string>>;
-};
+type TransactionSummaryLinkProps = TrendsListItemProps;
 
 function TransactionSummaryLink(props: TransactionSummaryLinkProps) {
   const {
@@ -604,7 +571,6 @@ function TransactionSummaryLink(props: TransactionSummaryLinkProps) {
     projects,
     location,
     currentTrendFunction,
-    onItemClicked: onTransactionSelection,
   } = props;
   const summaryView = eventView.clone();
   const projectID = getTrendProjectId(transaction, projects);
@@ -623,13 +589,12 @@ function TransactionSummaryLink(props: TransactionSummaryLinkProps) {
   const handleClick = useCallback<React.MouseEventHandler>(
     event => {
       event.preventDefault();
-      onTransactionSelection(transaction.transaction);
       trackAnalytics('performance_views.performance_change_explorer.open', {
         organization,
         transaction: transaction.transaction,
       });
     },
-    [onTransactionSelection, transaction.transaction, organization]
+    [transaction.transaction, organization]
   );
 
   if (organization.features.includes('performance-change-explorer')) {

+ 0 - 72
static/app/views/performance/trends/index.spec.tsx

@@ -267,43 +267,6 @@ describe('Performance > Trends', function () {
       },
     });
 
-    MockApiClient.addMockResponse({
-      url: '/organizations/org-slug/events/',
-      body: {
-        data: [
-          {
-            'p95()': 1010.9232499999998,
-            'p50()': 47.34580982348902,
-            'tps()': 3.7226926286168966,
-            'count()': 34872349,
-            'failure_rate()': 0.43428379,
-            'examples()': ['djk3w308er', '3298a9ui3h'],
-          },
-        ],
-        meta: {
-          fields: {
-            'p95()': 'duration',
-            '950()': 'duration',
-            'tps()': 'number',
-            'count()': 'number',
-            'failure_rate()': 'number',
-            'examples()': 'Array',
-          },
-          units: {
-            'p95()': 'millisecond',
-            'p50()': 'millisecond',
-            'tps()': null,
-            'count()': null,
-            'failure_rate()': null,
-            'examples()': null,
-          },
-          isMetricsData: true,
-          tips: {},
-          dataset: 'metrics',
-        },
-      },
-    });
-
     MockApiClient.addMockResponse({
       url: '/organizations/org-slug/events-spans-performance/',
       body: [],
@@ -370,41 +333,6 @@ describe('Performance > Trends', function () {
     );
   });
 
-  it('view summary menu action opens performance change explorer with feature flag', async function () {
-    const projects = [ProjectFixture({id: '1', slug: 'internal'}), ProjectFixture()];
-    const data = initializeTrendsData(projects, {project: ['1']}, true, [
-      'performance-change-explorer',
-    ]);
-
-    render(
-      <TrendsIndex location={data.router.location} organization={data.organization} />,
-      {
-        router: data.router,
-        organization: data.organization,
-      }
-    );
-
-    const transactions = await screen.findAllByTestId('trends-list-item-improved');
-    expect(transactions).toHaveLength(2);
-    const firstTransaction = transactions[0];
-
-    const summaryLink = within(firstTransaction).getByTestId('item-transaction-name');
-
-    expect(summaryLink.closest('a')).toHaveAttribute(
-      'href',
-      '/trends/?project=1&query=tpm%28%29%3A%3E0.01%20transaction.duration%3A%3E0%20transaction.duration%3A%3C15min'
-    );
-
-    await clickEl(summaryLink);
-    await waitFor(() => {
-      expect(screen.getByText('Ongoing Improvement')).toBeInTheDocument();
-      expect(screen.getByText('Throughput')).toBeInTheDocument();
-      expect(screen.getByText('P95')).toBeInTheDocument();
-      expect(screen.getByText('P50')).toBeInTheDocument();
-      expect(screen.getByText('Failure Rate')).toBeInTheDocument();
-    });
-  });
-
   it('hide from list menu action modifies query', async function () {
     const projects = [ProjectFixture({id: '1', slug: 'internal'}), ProjectFixture()];
     const data = initializeTrendsData(projects, {project: ['1']});