From db2f6468bb36106c8f77561712827ee543a11a72 Mon Sep 17 00:00:00 2001 From: Edward Gou Date: Tue, 25 Nov 2025 11:38:53 -0500 Subject: [PATCH] adds action menu to prebuilt dashboard widgets --- .../app/views/dashboards/dashboard.spec.tsx | 39 +++++++++++++++ static/app/views/dashboards/dashboard.tsx | 3 ++ static/app/views/dashboards/detail.tsx | 2 + .../app/views/dashboards/sortableWidget.tsx | 4 +- .../app/views/dashboards/widgetCard/index.tsx | 50 +++++++++++++++---- .../widgetCard/visualizationWidget.tsx | 25 ++++------ 6 files changed, 97 insertions(+), 26 deletions(-) diff --git a/static/app/views/dashboards/dashboard.spec.tsx b/static/app/views/dashboards/dashboard.spec.tsx index 4489c75bfb3e8b..81416ef581e64a 100644 --- a/static/app/views/dashboards/dashboard.spec.tsx +++ b/static/app/views/dashboards/dashboard.spec.tsx @@ -289,6 +289,45 @@ describe('Dashboards > Dashboard', () => { expect(mockOnUpdate).toHaveBeenCalledWith(expectedWidgets); }); + it('hides widget context menu when dashboard is embedded', async () => { + const dashboardWithOneWidget = { + ...mockDashboard, + widgets: [ + WidgetFixture({ + id: '1', + title: 'Test Widget', + layout: { + h: 1, + w: 1, + x: 0, + y: 0, + minH: 1, + }, + }), + ], + }; + + render( + + + undefined} + handleUpdateWidgetList={() => undefined} + handleAddCustomWidget={() => undefined} + widgetLimitReached={false} + isEmbedded + widgetLegendState={widgetLegendState} + /> + + + ); + + await screen.findByText('Test Widget'); + expect(screen.queryByLabelText('Widget actions')).not.toBeInTheDocument(); + }); + describe('Issue Widgets', () => { beforeEach(() => { MemberListStore.init(); diff --git a/static/app/views/dashboards/dashboard.tsx b/static/app/views/dashboards/dashboard.tsx index 26e271a90bc033..4d7a8371e76222 100644 --- a/static/app/views/dashboards/dashboard.tsx +++ b/static/app/views/dashboards/dashboard.tsx @@ -81,6 +81,7 @@ type Props = { widgetLegendState: WidgetLegendSelectionState; widgetLimitReached: boolean; handleChangeSplitDataset?: (widget: Widget, index: number) => void; + isEmbedded?: boolean; isPreview?: boolean; newWidget?: Widget; newlyAddedWidget?: Widget; @@ -104,6 +105,7 @@ function Dashboard({ onUpdate, widgetLegendState, widgetLimitReached, + isEmbedded, isPreview, newWidget, newlyAddedWidget, @@ -426,6 +428,7 @@ function Dashboard({ onEdit={handleEditWidget(index)} onDuplicate={handleDuplicateWidget(widget)} onSetTransactionsDataset={() => handleChangeSplitDataset(widget, index)} + isEmbedded={isEmbedded} isPreview={isPreview} dashboardFilters={getDashboardFiltersFromURL(location) ?? dashboard.filters} dashboardPermissions={dashboard.permissions} diff --git a/static/app/views/dashboards/detail.tsx b/static/app/views/dashboards/detail.tsx index eba28e7ebd878c..561eecacccf99d 100644 --- a/static/app/views/dashboards/detail.tsx +++ b/static/app/views/dashboards/detail.tsx @@ -986,6 +986,7 @@ class DashboardDetail extends Component { onUpdate={this.onUpdateWidget} handleUpdateWidgetList={this.handleUpdateWidgetList} handleAddCustomWidget={this.handleAddCustomWidget} + isEmbedded={this.isEmbedded} isPreview={this.isPreview} widgetLegendState={this.state.widgetLegendState} /> @@ -1269,6 +1270,7 @@ class DashboardDetail extends Component { onAddWidget={this.onAddWidget} newWidget={newWidget} onSetNewWidget={onSetNewWidget} + isEmbedded={this.isEmbedded} isPreview={this.isPreview} widgetLegendState={this.state.widgetLegendState} onEditWidget={this.onEditWidget} diff --git a/static/app/views/dashboards/sortableWidget.tsx b/static/app/views/dashboards/sortableWidget.tsx index a89bbb6f8d4c5d..8ec30c7a97bb76 100644 --- a/static/app/views/dashboards/sortableWidget.tsx +++ b/static/app/views/dashboards/sortableWidget.tsx @@ -40,6 +40,7 @@ type Props = { dashboardCreator?: User; dashboardFilters?: DashboardFilters; dashboardPermissions?: DashboardPermissions; + isEmbedded?: boolean; isMobile?: boolean; isPreview?: boolean; newlyAddedWidget?: Widget; @@ -60,6 +61,7 @@ function SortableWidget(props: Props) { onEdit, onDuplicate, onSetTransactionsDataset, + isEmbedded, isPreview, isMobile, windowWidth, @@ -120,7 +122,7 @@ function SortableWidget(props: Props) { onEdit, onDuplicate, onSetTransactionsDataset, - showContextMenu: true, + showContextMenu: !isEmbedded, isPreview, index, dashboardFilters, diff --git a/static/app/views/dashboards/widgetCard/index.tsx b/static/app/views/dashboards/widgetCard/index.tsx index 6e0ed8a82445ef..7fc03b5c5ab829 100644 --- a/static/app/views/dashboards/widgetCard/index.tsx +++ b/static/app/views/dashboards/widgetCard/index.tsx @@ -315,17 +315,45 @@ function WidgetCard(props: Props) { {t('Error loading widget data')}} > - + 0 + } + disabled={Number(props.index) !== 0} + > + + + + ); } diff --git a/static/app/views/dashboards/widgetCard/visualizationWidget.tsx b/static/app/views/dashboards/widgetCard/visualizationWidget.tsx index 4bcfe0c2e7845d..0be5c697f10377 100644 --- a/static/app/views/dashboards/widgetCard/visualizationWidget.tsx +++ b/static/app/views/dashboards/widgetCard/visualizationWidget.tsx @@ -6,7 +6,6 @@ import {useReleaseStats} from 'sentry/utils/useReleaseStats'; import type {DashboardFilters, Widget} from 'sentry/views/dashboards/types'; import type {TabularColumn} from 'sentry/views/dashboards/widgets/common/types'; import {TimeSeriesWidgetVisualization} from 'sentry/views/dashboards/widgets/timeSeriesWidget/timeSeriesWidgetVisualization'; -import {Widget as CommonWidget} from 'sentry/views/dashboards/widgets/widget/widget'; import type {LoadableChartWidgetProps} from 'sentry/views/insights/common/components/widgets/types'; import {WidgetCardDataLoader} from './widgetCardDataLoader'; @@ -69,20 +68,18 @@ export function VisualizationWidget({ const errorDisplay = renderErrorMessage && errorMessage ? renderErrorMessage(errorMessage) : null; + if (loading) { + return ; + } + if (errorDisplay) { + return errorDisplay; + } + return ( - } - Visualization={ - errorDisplay || loading ? ( - errorDisplay - ) : ( - - ) - } + ); }}