From 735c96d27d015ce9a06c868aed3ec49611fd27de Mon Sep 17 00:00:00 2001 From: nikkikapadia Date: Fri, 10 Oct 2025 10:14:34 -0400 Subject: [PATCH 1/7] warning for span widgets --- static/app/views/dashboards/types.tsx | 13 ++++ .../app/views/dashboards/widgetCard/index.tsx | 3 + .../widgetCard/widgetCardContextMenu.tsx | 60 +++++++++++++++++++ 3 files changed, 76 insertions(+) diff --git a/static/app/views/dashboards/types.tsx b/static/app/views/dashboards/types.tsx index 7f609bb7ec79bf..bb0c091b7ce85c 100644 --- a/static/app/views/dashboards/types.tsx +++ b/static/app/views/dashboards/types.tsx @@ -94,11 +94,24 @@ export type WidgetQuery = { selectedAggregate?: number; }; +export type WidgetChangedReason = { + equations: Array<{ + equation: string; + reason: string | string[]; + }> | null; + orderby: Array<{ + orderby: string; + reason: string; + }> | null; + selected_columns: string[]; +}; + export type Widget = { displayType: DisplayType; interval: string; queries: WidgetQuery[]; title: string; + changedReason?: WidgetChangedReason[]; dashboardId?: string; datasetSource?: DatasetSource; description?: string; diff --git a/static/app/views/dashboards/widgetCard/index.tsx b/static/app/views/dashboards/widgetCard/index.tsx index abe2676ae4563f..331697840f87c6 100644 --- a/static/app/views/dashboards/widgetCard/index.tsx +++ b/static/app/views/dashboards/widgetCard/index.tsx @@ -47,6 +47,7 @@ import {WidgetViewerContext} from 'sentry/views/dashboards/widgetViewer/widgetVi import {useDashboardsMEPContext} from './dashboardsMEPContext'; import { getMenuOptions, + useDroppedColumnsWarning, useIndexedEventsWarning, useTransactionsDeprecationWarning, } from './widgetCardContextMenu'; @@ -192,6 +193,7 @@ function WidgetCard(props: Props) { const indexedEventsWarning = useIndexedEventsWarning(); const onDemandWarning = useOnDemandWarning({widget}); const transactionsDeprecationWarning = useTransactionsDeprecationWarning({widget}); + const droppedColumnsWarning = useDroppedColumnsWarning(widget); const sessionDurationWarning = hasSessionDuration ? SESSION_DURATION_ALERT_TEXT : null; const spanTimeRangeWarning = useTimeRangeWarning({widget}); @@ -263,6 +265,7 @@ function WidgetCard(props: Props) { sessionDurationWarning, spanTimeRangeWarning, transactionsDeprecationWarning, + droppedColumnsWarning, ].filter(Boolean) as string[]; const actionsDisabled = Boolean(props.isPreview); diff --git a/static/app/views/dashboards/widgetCard/widgetCardContextMenu.tsx b/static/app/views/dashboards/widgetCard/widgetCardContextMenu.tsx index 1fadddcc6672af..4d830c448aced1 100644 --- a/static/app/views/dashboards/widgetCard/widgetCardContextMenu.tsx +++ b/static/app/views/dashboards/widgetCard/widgetCardContextMenu.tsx @@ -71,6 +71,66 @@ export const useTransactionsDeprecationWarning = ({ return null; }; +export const useDroppedColumnsWarning = (widget: Widget): React.JSX.Element | null => { + if (!widget.changedReason) { + return null; + } + + const baseWarning = tct( + "This widget may look different from the original query. Here's why:", + {} + ); + const columnsWarning = []; + const equationsWarning = []; + const orderbyWarning = []; + for (const changedReason of widget.changedReason) { + if (changedReason.selected_columns.length > 0) { + columnsWarning.push( + tct(`- The following columns were dropped: [columns]`, { + columns: changedReason.selected_columns.join(', '), + }) + ); + } + if (changedReason.equations) { + equationsWarning.push( + ...changedReason.equations.map(equation => + tct(`- [equation] was dropped because [reason] is unsupported`, { + equation: equation.equation, + reason: equation.reason, + }) + ) + ); + } + if (changedReason.orderby) { + orderbyWarning.push( + ...changedReason.orderby.map(equation => + tct(`- [orderby] was dropped because [reason]`, { + orderby: equation.orderby, + reason: equation.reason, + }) + ) + ); + } + } + + if ( + columnsWarning.length > 0 || + equationsWarning.length > 0 || + orderbyWarning.length > 0 + ) { + return ( +
+ {baseWarning} + {columnsWarning} + {equationsWarning} + {orderbyWarning} +
+ ); + } + + return null; +}; + export function getMenuOptions( dashboardFilters: DashboardFilters | undefined, organization: Organization, From f14f287a1bc0a0fc79a57dac10a60c39147d65fc Mon Sep 17 00:00:00 2001 From: nikkikapadia Date: Fri, 10 Oct 2025 14:36:32 -0400 Subject: [PATCH 2/7] add explore dropped fields warning banner --- .../explore/hooks/useGetSavedQueries.tsx | 4 + .../app/views/explore/hooks/useSaveQuery.tsx | 13 +++ .../explore/spans/droppedFieldsAlert.tsx | 83 +++++++++++++++++++ static/app/views/explore/spans/spansTab.tsx | 3 + 4 files changed, 103 insertions(+) create mode 100644 static/app/views/explore/spans/droppedFieldsAlert.tsx diff --git a/static/app/views/explore/hooks/useGetSavedQueries.tsx b/static/app/views/explore/hooks/useGetSavedQueries.tsx index ba4a2696bf90e8..4f8177e71b5e2d 100644 --- a/static/app/views/explore/hooks/useGetSavedQueries.tsx +++ b/static/app/views/explore/hooks/useGetSavedQueries.tsx @@ -6,6 +6,7 @@ import {defined} from 'sentry/utils'; import {useApiQuery, useQueryClient} from 'sentry/utils/queryClient'; import useOrganization from 'sentry/utils/useOrganization'; import type {Mode} from 'sentry/views/explore/contexts/pageParamsContext/mode'; +import type {ExploreQueryChangedReason} from 'sentry/views/explore/hooks/useSaveQuery'; import {TraceItemDataset} from 'sentry/views/explore/types'; export type RawGroupBy = { @@ -89,6 +90,7 @@ export type SortOption = // Comes from ExploreSavedQueryModelSerializer type ReadableSavedQuery = { + changedReason: ExploreQueryChangedReason | null; dataset: 'logs' | 'spans' | 'segment_spans'; // ExploreSavedQueryDataset dateAdded: string; dateUpdated: string; @@ -109,6 +111,7 @@ type ReadableSavedQuery = { }; export class SavedQuery { + changedReason: ExploreQueryChangedReason | null; dateAdded: string; dateUpdated: string; id: number; @@ -128,6 +131,7 @@ export class SavedQuery { start?: string | DateString; constructor(savedQuery: ReadableSavedQuery) { + this.changedReason = savedQuery.changedReason; this.dateAdded = savedQuery.dateAdded; this.dateUpdated = savedQuery.dateUpdated; this.id = savedQuery.id; diff --git a/static/app/views/explore/hooks/useSaveQuery.tsx b/static/app/views/explore/hooks/useSaveQuery.tsx index 7f49987bd96456..4eda739f9a6ca4 100644 --- a/static/app/views/explore/hooks/useSaveQuery.tsx +++ b/static/app/views/explore/hooks/useSaveQuery.tsx @@ -22,11 +22,24 @@ import {isGroupBy} from 'sentry/views/explore/queryParams/groupBy'; import type {ReadableQueryParams} from 'sentry/views/explore/queryParams/readableQueryParams'; import {isVisualize} from 'sentry/views/explore/queryParams/visualize'; +export type ExploreQueryChangedReason = { + columns: string[]; + equations: Array<{ + equation: string; + reason: string | string[]; + }> | null; + orderby: Array<{ + orderby: string; + reason: string; + }> | null; +}; + // Request payload type that matches the backend ExploreSavedQuerySerializer type ExploreSavedQueryRequest = { dataset: 'logs' | 'spans' | 'segment_spans'; name: string; projects: number[]; + changedReason?: ExploreQueryChangedReason; end?: DateString; environment?: string[]; interval?: string; diff --git a/static/app/views/explore/spans/droppedFieldsAlert.tsx b/static/app/views/explore/spans/droppedFieldsAlert.tsx new file mode 100644 index 00000000000000..27c4f1290693b1 --- /dev/null +++ b/static/app/views/explore/spans/droppedFieldsAlert.tsx @@ -0,0 +1,83 @@ +import styled from '@emotion/styled'; + +import {Alert} from 'sentry/components/core/alert'; +import {Text} from 'sentry/components/core/text'; +import List from 'sentry/components/list'; +import ListItem from 'sentry/components/list/listItem'; +import {t, tct} from 'sentry/locale'; +import {useExploreId} from 'sentry/views/explore/contexts/pageParamsContext'; +import {useGetSavedQuery} from 'sentry/views/explore/hooks/useGetSavedQueries'; + +export function DroppedFieldsAlert(): React.JSX.Element | null { + const id = useExploreId(); + const {data: savedQuery} = useGetSavedQuery(id); + + if (!savedQuery) { + return null; + } + + if (!savedQuery.changedReason) { + return null; + } + + const baseWarning = t( + "This widget may look different from the original query. Here's why:" + ); + const columnsWarning = []; + const equationsWarning = []; + const orderbyWarning = []; + + const changedReason = savedQuery.changedReason; + if (changedReason.columns.length > 0) { + columnsWarning.push( + tct(`The following columns were dropped: [columns]`, { + columns: changedReason.columns.join(', '), + }) + ); + } + if (changedReason.equations) { + equationsWarning.push( + ...changedReason.equations.map(equation => + tct(`[equation] was dropped because [reason] is unsupported`, { + equation: equation.equation, + reason: equation.reason, + }) + ) + ); + } + if (changedReason.orderby) { + orderbyWarning.push( + ...changedReason.orderby.map(equation => + tct(`[orderby] was dropped because [reason]`, { + orderby: equation.orderby, + reason: equation.reason, + }) + ) + ); + } + + const allWarnings = [...columnsWarning, ...equationsWarning, ...orderbyWarning]; + + if (allWarnings.length > 0) { + return ( + + {baseWarning} + + {allWarnings.map((warning, index) => ( + {warning} + ))} + + + ); + } + + return null; +} + +const StyledText = styled(Text)` + padding-bottom: ${p => p.theme.space.xs}; +`; + +const StyledAlert = styled(Alert)` + margin-bottom: ${p => p.theme.space.md}; +`; diff --git a/static/app/views/explore/spans/spansTab.tsx b/static/app/views/explore/spans/spansTab.tsx index b9acdbbf2dd425..5c6466a9049b2f 100644 --- a/static/app/views/explore/spans/spansTab.tsx +++ b/static/app/views/explore/spans/spansTab.tsx @@ -67,6 +67,7 @@ import { useSetQueryParamsVisualizes, } from 'sentry/views/explore/queryParams/context'; import {ExploreCharts} from 'sentry/views/explore/spans/charts'; +import {DroppedFieldsAlert} from 'sentry/views/explore/spans/droppedFieldsAlert'; import {ExtrapolationEnabledAlert} from 'sentry/views/explore/spans/extrapolationEnabledAlert'; import {SettingsDropdown} from 'sentry/views/explore/spans/settingsDropdown'; import {SpansExport} from 'sentry/views/explore/spans/spansExport'; @@ -369,6 +370,7 @@ function SpanTabContentSection({ const visualizes = useQueryParamsVisualizes(); const setVisualizes = useSetQueryParamsVisualizes(); const extrapolate = useQueryParamsExtrapolate(); + const id = useExploreId(); const [tab, setTab] = useTab(); const [caseInsensitive] = useCaseInsensitivity(); @@ -487,6 +489,7 @@ function SpanTabContentSection({ + {defined(id) && } {!resultsLoading && !hasResults && ( )} From 3c6039402e0f3c1221a6896cff6f5e89ac5252a4 Mon Sep 17 00:00:00 2001 From: nikkikapadia Date: Fri, 10 Oct 2025 15:01:38 -0400 Subject: [PATCH 3/7] fix widget dropped columns tooltip content --- .../widgetCard/widgetCardContextMenu.tsx | 37 +++++++++++-------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/static/app/views/dashboards/widgetCard/widgetCardContextMenu.tsx b/static/app/views/dashboards/widgetCard/widgetCardContextMenu.tsx index 98537412e628d3..f3bedb5389d3cc 100644 --- a/static/app/views/dashboards/widgetCard/widgetCardContextMenu.tsx +++ b/static/app/views/dashboards/widgetCard/widgetCardContextMenu.tsx @@ -1,4 +1,5 @@ import {useMemo} from 'react'; +import styled from '@emotion/styled'; import type {Location} from 'history'; import qs from 'query-string'; @@ -8,7 +9,10 @@ import { } from 'sentry/actionCreators/modal'; import {openConfirmModal} from 'sentry/components/confirm'; import {Link} from 'sentry/components/core/link'; +import {Text} from 'sentry/components/core/text'; import type {MenuItemProps} from 'sentry/components/dropdownMenu'; +import List from 'sentry/components/list'; +import ListItem from 'sentry/components/list/listItem'; import {t, tct} from 'sentry/locale'; import type {PageFilters} from 'sentry/types/core'; import type {Organization} from 'sentry/types/organization'; @@ -111,9 +115,8 @@ export const useDroppedColumnsWarning = (widget: Widget): React.JSX.Element | nu return null; } - const baseWarning = tct( - "This widget may look different from the original query. Here's why:", - {} + const baseWarning = t( + "This widget may look different from its original query. Here's why:" ); const columnsWarning = []; const equationsWarning = []; @@ -121,7 +124,7 @@ export const useDroppedColumnsWarning = (widget: Widget): React.JSX.Element | nu for (const changedReason of widget.changedReason) { if (changedReason.selected_columns.length > 0) { columnsWarning.push( - tct(`- The following columns were dropped: [columns]`, { + tct(`The following columns were dropped: [columns].`, { columns: changedReason.selected_columns.join(', '), }) ); @@ -129,7 +132,7 @@ export const useDroppedColumnsWarning = (widget: Widget): React.JSX.Element | nu if (changedReason.equations) { equationsWarning.push( ...changedReason.equations.map(equation => - tct(`- [equation] was dropped because [reason] is unsupported`, { + tct(`[equation] was dropped because [reason] is unsupported.`, { equation: equation.equation, reason: equation.reason, }) @@ -139,7 +142,7 @@ export const useDroppedColumnsWarning = (widget: Widget): React.JSX.Element | nu if (changedReason.orderby) { orderbyWarning.push( ...changedReason.orderby.map(equation => - tct(`- [orderby] was dropped because [reason]`, { + tct(`[orderby] was dropped because [reason].`, { orderby: equation.orderby, reason: equation.reason, }) @@ -148,17 +151,15 @@ export const useDroppedColumnsWarning = (widget: Widget): React.JSX.Element | nu } } - if ( - columnsWarning.length > 0 || - equationsWarning.length > 0 || - orderbyWarning.length > 0 - ) { + const allWarnings = [...columnsWarning, ...equationsWarning, ...orderbyWarning]; + + if (allWarnings.length > 0) { return ( -
- {baseWarning} - {columnsWarning} - {equationsWarning} - {orderbyWarning} +
+ {baseWarning} + {allWarnings.map((warning, index) => ( + {warning} + ))}
); } @@ -166,6 +167,10 @@ export const useDroppedColumnsWarning = (widget: Widget): React.JSX.Element | nu return null; }; +const StyledText = styled(Text)` + padding-bottom: ${p => p.theme.space.xs}; +`; + export function getMenuOptions( dashboardFilters: DashboardFilters | undefined, organization: Organization, From 18cfa2fb8e443057dfb81791a21e77d94109be44 Mon Sep 17 00:00:00 2001 From: nikkikapadia Date: Fri, 10 Oct 2025 15:01:54 -0400 Subject: [PATCH 4/7] fix widget dropped columns tooltip content --- .../app/views/dashboards/widgetCard/widgetCardContextMenu.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/static/app/views/dashboards/widgetCard/widgetCardContextMenu.tsx b/static/app/views/dashboards/widgetCard/widgetCardContextMenu.tsx index f3bedb5389d3cc..bb616b43af8f9b 100644 --- a/static/app/views/dashboards/widgetCard/widgetCardContextMenu.tsx +++ b/static/app/views/dashboards/widgetCard/widgetCardContextMenu.tsx @@ -11,8 +11,6 @@ import {openConfirmModal} from 'sentry/components/confirm'; import {Link} from 'sentry/components/core/link'; import {Text} from 'sentry/components/core/text'; import type {MenuItemProps} from 'sentry/components/dropdownMenu'; -import List from 'sentry/components/list'; -import ListItem from 'sentry/components/list/listItem'; import {t, tct} from 'sentry/locale'; import type {PageFilters} from 'sentry/types/core'; import type {Organization} from 'sentry/types/organization'; From 71fd502fed784d978637ac467d0699e35252a9d1 Mon Sep 17 00:00:00 2001 From: nikkikapadia Date: Fri, 10 Oct 2025 15:09:26 -0400 Subject: [PATCH 5/7] change up wording a little --- .../app/views/dashboards/widgetCard/widgetCardContextMenu.tsx | 2 +- static/app/views/explore/spans/droppedFieldsAlert.tsx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/static/app/views/dashboards/widgetCard/widgetCardContextMenu.tsx b/static/app/views/dashboards/widgetCard/widgetCardContextMenu.tsx index bb616b43af8f9b..057101a582e609 100644 --- a/static/app/views/dashboards/widgetCard/widgetCardContextMenu.tsx +++ b/static/app/views/dashboards/widgetCard/widgetCardContextMenu.tsx @@ -122,7 +122,7 @@ export const useDroppedColumnsWarning = (widget: Widget): React.JSX.Element | nu for (const changedReason of widget.changedReason) { if (changedReason.selected_columns.length > 0) { columnsWarning.push( - tct(`The following columns were dropped: [columns].`, { + tct(`The following fields were dropped: [columns].`, { columns: changedReason.selected_columns.join(', '), }) ); diff --git a/static/app/views/explore/spans/droppedFieldsAlert.tsx b/static/app/views/explore/spans/droppedFieldsAlert.tsx index 27c4f1290693b1..0725188b6e9ca2 100644 --- a/static/app/views/explore/spans/droppedFieldsAlert.tsx +++ b/static/app/views/explore/spans/droppedFieldsAlert.tsx @@ -21,7 +21,7 @@ export function DroppedFieldsAlert(): React.JSX.Element | null { } const baseWarning = t( - "This widget may look different from the original query. Here's why:" + "This query may look different from the original query. Here's why:" ); const columnsWarning = []; const equationsWarning = []; @@ -30,7 +30,7 @@ export function DroppedFieldsAlert(): React.JSX.Element | null { const changedReason = savedQuery.changedReason; if (changedReason.columns.length > 0) { columnsWarning.push( - tct(`The following columns were dropped: [columns]`, { + tct(`The following fields were dropped: [columns]`, { columns: changedReason.columns.join(', '), }) ); From fdbe9bc1f90f51d083051c786ad0fcd4bc5800a7 Mon Sep 17 00:00:00 2001 From: nikkikapadia Date: Fri, 10 Oct 2025 15:18:57 -0400 Subject: [PATCH 6/7] change typing --- static/app/views/dashboards/types.tsx | 2 +- static/app/views/explore/hooks/useGetSavedQueries.tsx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/static/app/views/dashboards/types.tsx b/static/app/views/dashboards/types.tsx index c63eeb25b57489..2f0298193fbf46 100644 --- a/static/app/views/dashboards/types.tsx +++ b/static/app/views/dashboards/types.tsx @@ -94,7 +94,7 @@ export type WidgetQuery = { selectedAggregate?: number; }; -export type WidgetChangedReason = { +type WidgetChangedReason = { equations: Array<{ equation: string; reason: string | string[]; diff --git a/static/app/views/explore/hooks/useGetSavedQueries.tsx b/static/app/views/explore/hooks/useGetSavedQueries.tsx index 4f8177e71b5e2d..c6c54b6acefe19 100644 --- a/static/app/views/explore/hooks/useGetSavedQueries.tsx +++ b/static/app/views/explore/hooks/useGetSavedQueries.tsx @@ -90,7 +90,6 @@ export type SortOption = // Comes from ExploreSavedQueryModelSerializer type ReadableSavedQuery = { - changedReason: ExploreQueryChangedReason | null; dataset: 'logs' | 'spans' | 'segment_spans'; // ExploreSavedQueryDataset dateAdded: string; dateUpdated: string; @@ -102,6 +101,7 @@ type ReadableSavedQuery = { projects: number[]; query: [ReadableQuery, ...ReadableQuery[]]; starred: boolean; + changedReason?: ExploreQueryChangedReason | null; createdBy?: User; end?: string; environment?: string[]; @@ -111,7 +111,6 @@ type ReadableSavedQuery = { }; export class SavedQuery { - changedReason: ExploreQueryChangedReason | null; dateAdded: string; dateUpdated: string; id: number; @@ -123,6 +122,7 @@ export class SavedQuery { query: [SavedQueryQuery, ...SavedQueryQuery[]]; dataset: ReadableSavedQuery['dataset']; starred: boolean; + changedReason?: ExploreQueryChangedReason | null; createdBy?: User; end?: string | DateString; environment?: string[]; From 004b7ca443b49ec0afef3407c03d96315e90430f Mon Sep 17 00:00:00 2001 From: nikkikapadia Date: Tue, 14 Oct 2025 09:48:59 -0400 Subject: [PATCH 7/7] cursor fixes --- .../views/dashboards/widgetCard/widgetCardContextMenu.tsx | 5 ++++- static/app/views/explore/spans/droppedFieldsAlert.tsx | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/static/app/views/dashboards/widgetCard/widgetCardContextMenu.tsx b/static/app/views/dashboards/widgetCard/widgetCardContextMenu.tsx index 057101a582e609..cba09e92566b0a 100644 --- a/static/app/views/dashboards/widgetCard/widgetCardContextMenu.tsx +++ b/static/app/views/dashboards/widgetCard/widgetCardContextMenu.tsx @@ -132,7 +132,10 @@ export const useDroppedColumnsWarning = (widget: Widget): React.JSX.Element | nu ...changedReason.equations.map(equation => tct(`[equation] was dropped because [reason] is unsupported.`, { equation: equation.equation, - reason: equation.reason, + reason: + typeof equation.reason === 'string' + ? equation.reason + : equation.reason.join(', '), }) ) ); diff --git a/static/app/views/explore/spans/droppedFieldsAlert.tsx b/static/app/views/explore/spans/droppedFieldsAlert.tsx index 0725188b6e9ca2..b3fb4b9863a89b 100644 --- a/static/app/views/explore/spans/droppedFieldsAlert.tsx +++ b/static/app/views/explore/spans/droppedFieldsAlert.tsx @@ -40,7 +40,10 @@ export function DroppedFieldsAlert(): React.JSX.Element | null { ...changedReason.equations.map(equation => tct(`[equation] was dropped because [reason] is unsupported`, { equation: equation.equation, - reason: equation.reason, + reason: + typeof equation.reason === 'string' + ? equation.reason + : equation.reason.join(', '), }) ) );