From 974e8ab33d1dc02efb66bcf870ee100651243fac Mon Sep 17 00:00:00 2001 From: Arthur Knaus Date: Thu, 22 May 2025 09:55:06 +0200 Subject: [PATCH 1/2] ref(platform-insights): Make duration chart loadable --- static/app/components/charts/chartWidgetLoader.tsx | 4 ++++ .../widgets/overviewApiLatencyChartWidget.tsx} | 13 +++++++++---- .../insights/common/queries/useDiscoverSeries.ts | 10 ++++++++-- .../insights/pages/backend/backendOverviewPage.tsx | 4 ++-- .../views/insights/pages/platform/laravel/index.tsx | 4 ++-- .../views/insights/pages/platform/laravel/utils.tsx | 6 +++++- .../views/insights/pages/platform/nextjs/index.tsx | 4 ++-- .../pages/platform/shared/getReleaseBubbleProps.tsx | 7 +++++-- 8 files changed, 37 insertions(+), 15 deletions(-) rename static/app/views/insights/{pages/platform/shared/durationWidget.tsx => common/components/widgets/overviewApiLatencyChartWidget.tsx} (88%) diff --git a/static/app/components/charts/chartWidgetLoader.tsx b/static/app/components/charts/chartWidgetLoader.tsx index f4f00bac23ee3c..cadfcaa101726e 100644 --- a/static/app/components/charts/chartWidgetLoader.tsx +++ b/static/app/components/charts/chartWidgetLoader.tsx @@ -155,6 +155,10 @@ const CHART_MAP = { import( 'sentry/views/insights/common/components/widgets/httpDomainSummaryDurationChartWidget' ), + overviewApiLatencyChartWidget: () => + import( + 'sentry/views/insights/common/components/widgets/overviewApiLatencyChartWidget' + ), } satisfies Record Promise<{default: React.FC}>>; /** diff --git a/static/app/views/insights/pages/platform/shared/durationWidget.tsx b/static/app/views/insights/common/components/widgets/overviewApiLatencyChartWidget.tsx similarity index 88% rename from static/app/views/insights/pages/platform/shared/durationWidget.tsx rename to static/app/views/insights/common/components/widgets/overviewApiLatencyChartWidget.tsx index ef0d032f324046..32769bf080f049 100644 --- a/static/app/views/insights/pages/platform/shared/durationWidget.tsx +++ b/static/app/views/insights/common/components/widgets/overviewApiLatencyChartWidget.tsx @@ -8,6 +8,7 @@ import {TimeSeriesWidgetVisualization} from 'sentry/views/dashboards/widgets/tim import {Widget} from 'sentry/views/dashboards/widgets/widget/widget'; import {Mode} from 'sentry/views/explore/contexts/pageParamsContext/mode'; import {ChartType} from 'sentry/views/insights/common/components/chart'; +import type {LoadableChartWidgetProps} from 'sentry/views/insights/common/components/widgets/types'; import {useEAPSeries} from 'sentry/views/insights/common/queries/useDiscoverSeries'; import {convertSeriesToTimeseries} from 'sentry/views/insights/common/utils/convertSeriesToTimeseries'; import {Referrer} from 'sentry/views/insights/pages/platform/laravel/referrers'; @@ -18,11 +19,13 @@ import {ModalChartContainer} from 'sentry/views/insights/pages/platform/shared/s import {Toolbar} from 'sentry/views/insights/pages/platform/shared/toolbar'; import {useTransactionNameQuery} from 'sentry/views/insights/pages/platform/shared/useTransactionNameQuery'; -export function DurationWidget() { +export default function OverviewApiLatencyChartWidget(props: LoadableChartWidgetProps) { const organization = useOrganization(); const {query} = useTransactionNameQuery(); - const pageFilterChartParams = usePageFilterChartParams(); - const releaseBubbleProps = useReleaseBubbleProps(); + const pageFilterChartParams = usePageFilterChartParams({ + pageFilters: props.pageFilters, + }); + const releaseBubbleProps = useReleaseBubbleProps(props); const fullQuery = `span.op:http.server ${query}`.trim(); @@ -33,7 +36,8 @@ export function DurationWidget() { yAxis: ['avg(span.duration)', 'p95(span.duration)'], referrer: Referrer.DURATION_CHART, }, - Referrer.DURATION_CHART + Referrer.DURATION_CHART, + props.pageFilters ); const plottables = useMemo(() => { @@ -52,6 +56,7 @@ export function DurationWidget() { isEmpty={isEmpty} VisualizationType={TimeSeriesWidgetVisualization} visualizationProps={{ + id: 'overviewApiLatencyChartWidget', plottables, ...releaseBubbleProps, }} diff --git a/static/app/views/insights/common/queries/useDiscoverSeries.ts b/static/app/views/insights/common/queries/useDiscoverSeries.ts index a9f0442c456b8a..0ef00842e13bd2 100644 --- a/static/app/views/insights/common/queries/useDiscoverSeries.ts +++ b/static/app/views/insights/common/queries/useDiscoverSeries.ts @@ -66,9 +66,15 @@ export const useEAPSeries = < | string[], >( options: UseMetricsSeriesOptions = {}, - referrer: string + referrer: string, + pageFilters?: PageFilters ) => { - return useDiscoverSeries(options, DiscoverDatasets.SPANS_EAP_RPC, referrer); + return useDiscoverSeries( + options, + DiscoverDatasets.SPANS_EAP_RPC, + referrer, + pageFilters + ); }; export const useMetricsSeries = ( diff --git a/static/app/views/insights/pages/backend/backendOverviewPage.tsx b/static/app/views/insights/pages/backend/backendOverviewPage.tsx index 0bfc30b090b6aa..9affca35222259 100644 --- a/static/app/views/insights/pages/backend/backendOverviewPage.tsx +++ b/static/app/views/insights/pages/backend/backendOverviewPage.tsx @@ -25,6 +25,7 @@ import {limitMaxPickableDays} from 'sentry/views/explore/utils'; import * as ModuleLayout from 'sentry/views/insights/common/components/moduleLayout'; import {ToolRibbon} from 'sentry/views/insights/common/components/ribbon'; import {STARRED_SEGMENT_TABLE_QUERY_KEY} from 'sentry/views/insights/common/components/tableCells/starredSegmentCell'; +import OverviewApiLatencyChartWidget from 'sentry/views/insights/common/components/widgets/overviewApiLatencyChartWidget'; import {useEAPSpans} from 'sentry/views/insights/common/queries/useDiscover'; import {useOnboardingProject} from 'sentry/views/insights/common/queries/useOnboardingProject'; import {useInsightsEap} from 'sentry/views/insights/common/utils/useEap'; @@ -56,7 +57,6 @@ import { useIsNextJsInsightsEnabled, } from 'sentry/views/insights/pages/platform/nextjs/features'; import {NewNextJsExperienceButton} from 'sentry/views/insights/pages/platform/nextjs/newNextjsExperienceToggle'; -import {DurationWidget} from 'sentry/views/insights/pages/platform/shared/durationWidget'; import {IssuesWidget} from 'sentry/views/insights/pages/platform/shared/issuesWidget'; import {TrafficWidget} from 'sentry/views/insights/pages/platform/shared/trafficWidget'; import {TransactionNameSearchBar} from 'sentry/views/insights/pages/transactionNameSearchBar'; @@ -243,7 +243,7 @@ function EAPBackendOverviewPage() { trafficSeriesName={t('Requests')} baseQuery={'span.op:http.server'} /> - + diff --git a/static/app/views/insights/pages/platform/laravel/index.tsx b/static/app/views/insights/pages/platform/laravel/index.tsx index a1ef7db8fabbb8..ec4a15114c5a24 100644 --- a/static/app/views/insights/pages/platform/laravel/index.tsx +++ b/static/app/views/insights/pages/platform/laravel/index.tsx @@ -3,10 +3,10 @@ import {useEffect} from 'react'; import {t} from 'sentry/locale'; import {trackAnalytics} from 'sentry/utils/analytics'; import useOrganization from 'sentry/utils/useOrganization'; +import OverviewApiLatencyChartWidget from 'sentry/views/insights/common/components/widgets/overviewApiLatencyChartWidget'; import {CachesWidget} from 'sentry/views/insights/pages/platform/laravel/cachesWidget'; import {JobsWidget} from 'sentry/views/insights/pages/platform/laravel/jobsWidget'; import {QueriesWidget} from 'sentry/views/insights/pages/platform/laravel/queriesWidget'; -import {DurationWidget} from 'sentry/views/insights/pages/platform/shared/durationWidget'; import {IssuesWidget} from 'sentry/views/insights/pages/platform/shared/issuesWidget'; import {PlatformLandingPageLayout} from 'sentry/views/insights/pages/platform/shared/layout'; import {PathsTable} from 'sentry/views/insights/pages/platform/shared/pathsTable'; @@ -34,7 +34,7 @@ export function LaravelOverviewPage() { /> - + diff --git a/static/app/views/insights/pages/platform/laravel/utils.tsx b/static/app/views/insights/pages/platform/laravel/utils.tsx index 307fce430b2dec..15add445fb4f04 100644 --- a/static/app/views/insights/pages/platform/laravel/utils.tsx +++ b/static/app/views/insights/pages/platform/laravel/utils.tsx @@ -2,14 +2,18 @@ import {useMemo} from 'react'; import {type Fidelity, getInterval} from 'sentry/components/charts/utils'; import {normalizeDateTimeParams} from 'sentry/components/organizations/pageFilters/parse'; +import type {PageFilters} from 'sentry/types/core'; import usePageFilters from 'sentry/utils/usePageFilters'; export function usePageFilterChartParams({ granularity = 'spans', + pageFilters, }: { granularity?: Fidelity; + pageFilters?: PageFilters; } = {}) { - const {selection} = usePageFilters(); + const pageFilterContext = usePageFilters(); + const selection = pageFilters || pageFilterContext.selection; const normalizedDateTime = useMemo( () => normalizeDateTimeParams(selection.datetime), diff --git a/static/app/views/insights/pages/platform/nextjs/index.tsx b/static/app/views/insights/pages/platform/nextjs/index.tsx index 88a9ca5eb8c4d2..727fbc75478971 100644 --- a/static/app/views/insights/pages/platform/nextjs/index.tsx +++ b/static/app/views/insights/pages/platform/nextjs/index.tsx @@ -8,10 +8,10 @@ import {trackAnalytics} from 'sentry/utils/analytics'; import {useLocation} from 'sentry/utils/useLocation'; import {useNavigate} from 'sentry/utils/useNavigate'; import useOrganization from 'sentry/utils/useOrganization'; +import OverviewApiLatencyChartWidget from 'sentry/views/insights/common/components/widgets/overviewApiLatencyChartWidget'; import {DeadRageClicksWidget} from 'sentry/views/insights/pages/platform/nextjs/deadRageClickWidget'; import SSRTreeWidget from 'sentry/views/insights/pages/platform/nextjs/ssrTreeWidget'; import {WebVitalsWidget} from 'sentry/views/insights/pages/platform/nextjs/webVitalsWidget'; -import {DurationWidget} from 'sentry/views/insights/pages/platform/shared/durationWidget'; import {IssuesWidget} from 'sentry/views/insights/pages/platform/shared/issuesWidget'; import {PlatformLandingPageLayout} from 'sentry/views/insights/pages/platform/shared/layout'; import {PagesTable} from 'sentry/views/insights/pages/platform/shared/pagesTable'; @@ -97,7 +97,7 @@ export function NextJsOverviewPage({ /> - + diff --git a/static/app/views/insights/pages/platform/shared/getReleaseBubbleProps.tsx b/static/app/views/insights/pages/platform/shared/getReleaseBubbleProps.tsx index e5fd0f3be464d1..8418c7d8fb3c1d 100644 --- a/static/app/views/insights/pages/platform/shared/getReleaseBubbleProps.tsx +++ b/static/app/views/insights/pages/platform/shared/getReleaseBubbleProps.tsx @@ -1,8 +1,11 @@ import useOrganization from 'sentry/utils/useOrganization'; import usePageFilters from 'sentry/utils/usePageFilters'; import {useReleaseStats} from 'sentry/utils/useReleaseStats'; +import type {LoadableChartWidgetProps} from 'sentry/views/insights/common/components/widgets/types'; -export function useReleaseBubbleProps() { +type Params = Pick; + +export function useReleaseBubbleProps(params?: Params) { const organization = useOrganization(); const pageFilters = usePageFilters(); @@ -14,6 +17,6 @@ export function useReleaseBubbleProps() { })) ?? []; return organization.features.includes('release-bubbles-ui') - ? ({releases, showReleaseAs: 'bubble'} as const) + ? ({releases, showReleaseAs: params?.showReleaseAs ?? 'bubble'} as const) : {}; } From e9aa9283b902a5723b1d885691240b83a2fdcb61 Mon Sep 17 00:00:00 2001 From: Arthur Knaus Date: Thu, 22 May 2025 10:21:47 +0200 Subject: [PATCH 2/2] fix test --- ...hartWidgetLoader-unmocked-imports.spec.tsx | 15 ++++++++++ tests/js/fixtures/discoverSeries.ts | 30 +++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 tests/js/fixtures/discoverSeries.ts diff --git a/static/app/components/charts/chartWidgetLoader-unmocked-imports.spec.tsx b/static/app/components/charts/chartWidgetLoader-unmocked-imports.spec.tsx index 8b4c0fa9c89415..1b34948e065a27 100644 --- a/static/app/components/charts/chartWidgetLoader-unmocked-imports.spec.tsx +++ b/static/app/components/charts/chartWidgetLoader-unmocked-imports.spec.tsx @@ -2,6 +2,7 @@ import fs from 'node:fs'; // eslint-disable-next-line import/no-nodejs-modules import path from 'node:path'; +import {TimeSeriesFixture} from 'sentry-fixture/discoverSeries'; import {render, screen, waitFor} from 'sentry-test/reactTestingLibrary'; @@ -10,6 +11,12 @@ import {TimeSeriesWidgetVisualization} from 'sentry/views/dashboards/widgets/tim import type {ChartId} from './chartWidgetLoader'; import {ChartWidgetLoader} from './chartWidgetLoader'; +function mockDiscoverSeries(seriesName: string) { + return TimeSeriesFixture({ + seriesName, + }); +} + // Mock this component so it doesn't yell at us for no plottables jest.mock( 'sentry/views/dashboards/widgets/timeSeriesWidget/timeSeriesWidgetVisualization', @@ -146,6 +153,14 @@ jest.mock( }) ); jest.mock('sentry/views/insights/common/queries/useDiscoverSeries', () => ({ + useEAPSeries: jest.fn(() => ({ + data: { + 'avg(span.duration)': mockDiscoverSeries('avg(span.duration)'), + 'p95(span.duration)': mockDiscoverSeries('p95(span.duration)'), + }, + isPending: false, + error: null, + })), useMetricsSeries: jest.fn(() => ({ data: { 'performance_score(measurements.score.lcp)': { diff --git a/tests/js/fixtures/discoverSeries.ts b/tests/js/fixtures/discoverSeries.ts new file mode 100644 index 00000000000000..d99e11f410a851 --- /dev/null +++ b/tests/js/fixtures/discoverSeries.ts @@ -0,0 +1,30 @@ +import type {DiscoverSeries} from 'sentry/views/insights/common/queries/useDiscoverSeries'; + +export function TimeSeriesFixture(params: Partial = {}): DiscoverSeries { + return { + seriesName: 'avg(span.duration)', + data: [ + { + name: '2025-03-24T15:00:00-04:00', + value: 100, + }, + { + name: '2025-03-24T15:30:00-04:00', + value: 161, + }, + { + name: '2025-03-24T16:00:00-04:00', + value: 474, + }, + ], + meta: { + fields: { + 'avg(span.duration)': 'duration', + }, + units: { + 'avg(span.duration)': 'ms', + }, + }, + ...params, + }; +}