1
- import * as React from 'react' ;
2
1
import styled from '@emotion/styled' ;
3
2
import * as qs from 'query-string' ;
4
3
5
4
import { openDashboardWidgetQuerySelectorModal } from 'sentry/actionCreators/modal' ;
6
5
import { parseArithmetic } from 'sentry/components/arithmeticInput/parser' ;
7
- import Confirm from 'sentry/components/confirm' ;
8
- import Link from 'sentry/components/links/link' ;
9
- import MenuItem from 'sentry/components/menuItem' ;
6
+ import { openConfirmModal } from 'sentry/components/confirm' ;
7
+ import DropdownMenuControlV2 from 'sentry/components/dropdownMenuControlV2' ;
8
+ import { MenuItemProps } from 'sentry/components/dropdownMenuItemV2' ;
9
+ import {
10
+ IconCopy ,
11
+ IconDelete ,
12
+ IconEdit ,
13
+ IconEllipsis ,
14
+ IconIssues ,
15
+ IconTelescope ,
16
+ } from 'sentry/icons' ;
10
17
import { t } from 'sentry/locale' ;
11
18
import space from 'sentry/styles/space' ;
12
19
import { Organization , PageFilters } from 'sentry/types' ;
@@ -17,7 +24,6 @@ import {DisplayModes} from 'sentry/utils/discover/types';
17
24
import { eventViewFromWidget } from 'sentry/views/dashboardsV2/utils' ;
18
25
import { DisplayType } from 'sentry/views/dashboardsV2/widgetBuilder/utils' ;
19
26
20
- import ContextMenu from '../contextMenu' ;
21
27
import { Widget , WidgetType } from '../types' ;
22
28
23
29
type Props = {
@@ -47,16 +53,29 @@ function WidgetCardContextMenu({
47
53
return null ;
48
54
}
49
55
50
- const menuOptions : React . ReactNode [ ] = [ ] ;
56
+ const menuOptions : MenuItemProps [ ] = [ ] ;
57
+ const disabledKeys : string [ ] = [ ] ;
51
58
52
59
if ( isPreview ) {
53
60
return (
54
61
< ContextWrapper >
55
- < ContextMenu >
56
- < PreviewMessage >
57
- { t ( 'This is a preview only. To edit, you must add this dashboard.' ) }
58
- </ PreviewMessage >
59
- </ ContextMenu >
62
+ < DropdownMenuControlV2
63
+ items = { [
64
+ {
65
+ key : 'preview' ,
66
+ label : t ( 'This is a preview only. To edit, you must add this dashboard.' ) ,
67
+ } ,
68
+ ] }
69
+ triggerProps = { {
70
+ 'aria-label' : t ( 'Widget actions' ) ,
71
+ size : 'xsmall' ,
72
+ borderless : true ,
73
+ showChevron : false ,
74
+ icon : < IconEllipsis direction = "down" size = "sm" /> ,
75
+ } }
76
+ placement = "bottom right"
77
+ disabledKeys = { [ 'preview' ] }
78
+ />
60
79
</ ContextWrapper >
61
80
) ;
62
81
}
@@ -118,38 +137,28 @@ function WidgetCardContextMenu({
118
137
const discoverPath = `${ discoverLocation . pathname } ?${ qs . stringify ( {
119
138
...discoverLocation . query ,
120
139
} ) } `;
121
- if ( widget . queries . length === 1 ) {
122
- menuOptions . push (
123
- < Link
124
- key = "open-discover-link"
125
- to = { discoverPath }
126
- onClick = { ( ) => {
127
- trackAdvancedAnalyticsEvent ( 'dashboards_views.open_in_discover.opened' , {
128
- organization,
129
- widget_type : widget . displayType ,
130
- } ) ;
131
- } }
132
- >
133
- < StyledMenuItem key = "open-discover" > { t ( 'Open in Discover' ) } </ StyledMenuItem >
134
- </ Link >
135
- ) ;
136
- } else {
137
- menuOptions . push (
138
- < StyledMenuItem
139
- key = "open-discover"
140
- onClick = { event => {
141
- event . preventDefault ( ) ;
142
- trackAdvancedAnalyticsEvent ( 'dashboards_views.query_selector.opened' , {
143
- organization,
144
- widget_type : widget . displayType ,
145
- } ) ;
146
- openDashboardWidgetQuerySelectorModal ( { organization, widget} ) ;
147
- } }
148
- >
149
- { t ( 'Open in Discover' ) }
150
- </ StyledMenuItem >
151
- ) ;
152
- }
140
+
141
+ menuOptions . push ( {
142
+ key : 'open-in-discover' ,
143
+ label : t ( 'Open in Discover' ) ,
144
+ leadingItems : < IconTelescope /> ,
145
+ to : widget . queries . length === 1 ? discoverPath : undefined ,
146
+ onAction : ( ) => {
147
+ if ( widget . queries . length === 1 ) {
148
+ trackAdvancedAnalyticsEvent ( 'dashboards_views.open_in_discover.opened' , {
149
+ organization,
150
+ widget_type : widget . displayType ,
151
+ } ) ;
152
+ return ;
153
+ }
154
+
155
+ trackAdvancedAnalyticsEvent ( 'dashboards_views.query_selector.opened' , {
156
+ organization,
157
+ widget_type : widget . displayType ,
158
+ } ) ;
159
+ openDashboardWidgetQuerySelectorModal ( { organization, widget} ) ;
160
+ } ,
161
+ } ) ;
153
162
}
154
163
}
155
164
@@ -165,43 +174,42 @@ function WidgetCardContextMenu({
165
174
...datetime ,
166
175
} ) } `;
167
176
168
- menuOptions . push (
169
- < Link to = { issuesLocation } key = "open-issues-link" >
170
- < StyledMenuItem key = "open-issues" > { t ( 'Open in Issues' ) } </ StyledMenuItem >
171
- </ Link >
172
- ) ;
177
+ menuOptions . push ( {
178
+ key : 'open-in-issues' ,
179
+ label : t ( 'Open in Issues' ) ,
180
+ leadingItems : < IconIssues /> ,
181
+ to : issuesLocation ,
182
+ } ) ;
173
183
}
174
184
175
185
if ( organization . features . includes ( 'dashboards-edit' ) ) {
176
- menuOptions . push (
177
- < StyledMenuItem
178
- key = "duplicate-widget"
179
- data-test-id = "duplicate-widget"
180
- onSelect = { onDuplicate }
181
- disabled = { widgetLimitReached }
182
- >
183
- { t ( 'Duplicate Widget' ) }
184
- </ StyledMenuItem >
185
- ) ;
186
-
187
- menuOptions . push (
188
- < StyledMenuItem key = "edit-widget" data-test-id = "edit-widget" onSelect = { onEdit } >
189
- { t ( 'Edit Widget' ) }
190
- </ StyledMenuItem >
191
- ) ;
192
-
193
- menuOptions . push (
194
- < Confirm
195
- key = "delete-widget"
196
- priority = "danger"
197
- message = { t ( 'Are you sure you want to delete this widget?' ) }
198
- onConfirm = { onDelete }
199
- >
200
- < StyledMenuItem data-test-id = "delete-widget" danger >
201
- { t ( 'Delete Widget' ) }
202
- </ StyledMenuItem >
203
- </ Confirm >
204
- ) ;
186
+ menuOptions . push ( {
187
+ key : 'duplicate-widget' ,
188
+ label : t ( 'Duplicate Widget' ) ,
189
+ leadingItems : < IconCopy /> ,
190
+ onAction : ( ) => onDuplicate ( ) ,
191
+ } ) ;
192
+ widgetLimitReached && disabledKeys . push ( 'duplicate-widget' ) ;
193
+
194
+ menuOptions . push ( {
195
+ key : 'edit-widget' ,
196
+ label : t ( 'Edit Widget' ) ,
197
+ leadingItems : < IconEdit /> ,
198
+ onAction : ( ) => onEdit ( ) ,
199
+ } ) ;
200
+
201
+ menuOptions . push ( {
202
+ key : 'delete-widget' ,
203
+ label : t ( 'Delete Widget' ) ,
204
+ leadingItems : < IconDelete /> ,
205
+ onAction : ( ) => {
206
+ openConfirmModal ( {
207
+ message : t ( 'Are you sure you want to delete this widget?' ) ,
208
+ priority : 'danger' ,
209
+ onConfirm : ( ) => onDelete ( ) ,
210
+ } ) ;
211
+ } ,
212
+ } ) ;
205
213
}
206
214
207
215
if ( ! menuOptions . length ) {
@@ -210,26 +218,27 @@ function WidgetCardContextMenu({
210
218
211
219
return (
212
220
< ContextWrapper >
213
- < ContextMenu > { menuOptions } </ ContextMenu >
221
+ < DropdownMenuControlV2
222
+ items = { menuOptions }
223
+ triggerProps = { {
224
+ 'aria-label' : t ( 'Widget actions' ) ,
225
+ size : 'xsmall' ,
226
+ borderless : true ,
227
+ showChevron : false ,
228
+ icon : < IconEllipsis direction = "down" size = "sm" /> ,
229
+ } }
230
+ placement = "bottom right"
231
+ disabledKeys = { disabledKeys }
232
+ />
214
233
</ ContextWrapper >
215
234
) ;
216
235
}
217
236
218
237
export default WidgetCardContextMenu ;
219
238
220
239
const ContextWrapper = styled ( 'div' ) `
240
+ display: flex;
241
+ align-items: center;
242
+ height: ${ space ( 3 ) } ;
221
243
margin-left: ${ space ( 1 ) } ;
222
244
` ;
223
-
224
- const StyledMenuItem = styled ( MenuItem ) < { danger ?: boolean } > `
225
- white-space: nowrap;
226
- color: ${ p => ( p . danger ? p . theme . red300 : p . theme . textColor ) } ;
227
- :hover {
228
- color: ${ p => ( p . danger ? p . theme . red300 : p . theme . textColor ) } ;
229
- }
230
- ` ;
231
-
232
- const PreviewMessage = styled ( 'span' ) `
233
- padding: ${ space ( 1 ) } ;
234
- display: block;
235
- ` ;
0 commit comments