18
18
*/
19
19
import {
20
20
forwardRef ,
21
- Key ,
22
21
ReactNode ,
23
22
RefObject ,
24
23
useCallback ,
@@ -43,18 +42,18 @@ import {
43
42
useTheme ,
44
43
} from '@superset-ui/core' ;
45
44
import { RootState } from 'src/dashboard/types' ;
46
- import { Menu } from '@superset-ui/core/components/Menu' ;
45
+ import { MenuItem } from '@superset-ui/core/components/Menu' ;
47
46
import { usePermissions } from 'src/hooks/usePermissions' ;
48
47
import { Dropdown } from '@superset-ui/core/components' ;
49
48
import { updateDataMask } from 'src/dataMask/actions' ;
50
49
import DrillByModal from 'src/components/Chart/DrillBy/DrillByModal' ;
51
50
import { useDatasetDrillInfo } from 'src/hooks/apiResources/datasets' ;
52
51
import { ResourceStatus } from 'src/hooks/apiResources/apiResources' ;
53
- import { DrillDetailMenuItems } from '../DrillDetail ' ;
52
+ import { useDrillDetailMenuItems } from '../useDrillDetailMenuItems ' ;
54
53
import { getMenuAdjustedY } from '../utils' ;
55
- import { MenuItemTooltip } from '../DisabledMenuItemTooltip' ;
56
- import { DrillByMenuItems } from '../DrillBy/DrillByMenuItems' ;
54
+ import { DrillBySubmenu } from '../DrillBy/DrillBySubmenu' ;
57
55
import DrillDetailModal from '../DrillDetail/DrillDetailModal' ;
56
+ import { MenuItemTooltip } from '../DisabledMenuItemTooltip' ;
58
57
59
58
export enum ContextMenuItem {
60
59
CrossFilter ,
@@ -94,8 +93,8 @@ const ChartContextMenu = (
94
93
} : ChartContextMenuProps ,
95
94
ref : RefObject < ChartContextMenuRef > ,
96
95
) => {
97
- const theme = useTheme ( ) ;
98
96
const dispatch = useDispatch ( ) ;
97
+ const theme = useTheme ( ) ;
99
98
const { canDrillToDetail, canDrillBy, canDownload } = usePermissions ( ) ;
100
99
101
100
const crossFiltersEnabled = useSelector < RootState , boolean > (
@@ -104,7 +103,6 @@ const ChartContextMenu = (
104
103
const dashboardId = useSelector < RootState , number > (
105
104
( { dashboardInfo } ) => dashboardInfo . id ,
106
105
) ;
107
- const [ openKeys , setOpenKeys ] = useState < Key [ ] > ( [ ] ) ;
108
106
109
107
const [ modalFilters , setFilters ] = useState < BinaryQueryObjectFilterClause [ ] > (
110
108
[ ] ,
@@ -160,7 +158,6 @@ const ChartContextMenu = (
160
158
161
159
const closeContextMenu = useCallback ( ( ) => {
162
160
setVisible ( false ) ;
163
- setOpenKeys ( [ ] ) ;
164
161
onClose ( ) ;
165
162
} , [ onClose ] ) ;
166
163
@@ -177,7 +174,7 @@ const ChartContextMenu = (
177
174
setShowDrillByModal ( false ) ;
178
175
} , [ ] ) ;
179
176
180
- const menuItems : React . JSX . Element [ ] = [ ] ;
177
+ const menuItems : MenuItem [ ] = [ ] ;
181
178
182
179
const showDrillToDetail =
183
180
isFeatureEnabled ( FeatureFlag . DrillToDetail ) &&
@@ -264,6 +261,20 @@ const ChartContextMenu = (
264
261
itemsCount = 1 ; // "No actions" appears if no actions in menu
265
262
}
266
263
264
+ const drillDetailMenuItems = useDrillDetailMenuItems ( {
265
+ formData : drillFormData ,
266
+ filters : filters ?. drillToDetail ,
267
+ setFilters,
268
+ isContextMenu : true ,
269
+ contextMenuY : clientY ,
270
+ onSelection,
271
+ submenuIndex : showCrossFilters ? 2 : 1 ,
272
+ setShowModal : setDrillModalIsOpen ,
273
+ dataset : filteredDataset ,
274
+ isLoadingDataset,
275
+ ...( additionalConfig ?. drillToDetail || { } ) ,
276
+ } ) ;
277
+
267
278
if ( showCrossFilters ) {
268
279
const isCrossFilterDisabled =
269
280
! isCrossFilteringSupportedByChart ||
@@ -305,74 +316,65 @@ const ChartContextMenu = (
305
316
</ >
306
317
) ;
307
318
}
319
+
308
320
menuItems . push (
309
- < >
310
- < Menu . Item
311
- key = "cross-filtering-menu-item"
312
- disabled = { isCrossFilterDisabled }
313
- onClick = { ( ) => {
314
- if ( filters ?. crossFilter ) {
315
- dispatch ( updateDataMask ( id , filters . crossFilter . dataMask ) ) ;
316
- }
317
- } }
318
- >
319
- { filters ?. crossFilter ?. isCurrentValueSelected ? (
320
- t ( 'Remove cross-filter' )
321
- ) : (
322
- < div >
323
- { t ( 'Add cross-filter' ) }
324
- < MenuItemTooltip
325
- title = { crossFilteringTooltipTitle }
326
- color = { ! isCrossFilterDisabled ? theme . colorIcon : undefined }
327
- />
328
- </ div >
329
- ) }
330
- </ Menu . Item >
331
- { itemsCount > 1 && < Menu . Divider /> }
332
- </ > ,
321
+ {
322
+ key : 'cross-filtering-menu-item' ,
323
+ label : filters ?. crossFilter ?. isCurrentValueSelected ? (
324
+ t ( 'Remove cross-filter' )
325
+ ) : (
326
+ < span >
327
+ { t ( 'Add cross-filter' ) }
328
+ < MenuItemTooltip
329
+ title = { crossFilteringTooltipTitle }
330
+ color = { ! isCrossFilterDisabled ? theme . colorIcon : undefined }
331
+ />
332
+ </ span >
333
+ ) ,
334
+ disabled : isCrossFilterDisabled ,
335
+ onClick : ( ) => {
336
+ if ( filters ?. crossFilter ) {
337
+ dispatch ( updateDataMask ( id , filters . crossFilter . dataMask ) ) ;
338
+ }
339
+ } ,
340
+ } ,
341
+ ...( itemsCount > 1
342
+ ? [ { key : 'divider-1' , type : 'divider' as const } ]
343
+ : [ ] ) ,
333
344
) ;
334
345
}
335
346
if ( showDrillToDetail ) {
336
- menuItems . push (
337
- < DrillDetailMenuItems
338
- formData = { drillFormData }
339
- filters = { filters ?. drillToDetail }
340
- setFilters = { setFilters }
341
- isContextMenu
342
- contextMenuY = { clientY }
343
- onSelection = { onSelection }
344
- submenuIndex = { showCrossFilters ? 2 : 1 }
345
- setShowModal = { setDrillModalIsOpen }
346
- dataset = { filteredDataset }
347
- isLoadingDataset = { isLoadingDataset }
348
- { ...( additionalConfig ?. drillToDetail || { } ) }
349
- /> ,
350
- ) ;
347
+ menuItems . push ( ...drillDetailMenuItems ) ;
351
348
}
349
+
352
350
if ( showDrillBy ) {
353
- let submenuIndex = 0 ;
354
- if ( showCrossFilters ) {
355
- submenuIndex += 1 ;
356
- }
357
- if ( showDrillToDetail ) {
358
- submenuIndex += 2 ;
351
+ if ( menuItems . length > 0 ) {
352
+ menuItems . push ( { key : 'divider-drill-by' , type : 'divider' as const } ) ;
359
353
}
360
- menuItems . push (
361
- < DrillByMenuItems
362
- drillByConfig = { enhancedFilters ?. drillBy }
363
- onSelection = { onSelection }
364
- onCloseMenu = { closeContextMenu }
365
- formData = { formData }
366
- contextMenuY = { clientY }
367
- submenuIndex = { submenuIndex }
368
- open = { openKeys . includes ( 'drill-by-submenu' ) }
369
- key = "drill-by-submenu"
370
- onDrillBy = { handleDrillBy }
371
- dataset = { filteredDataset }
372
- isLoadingDataset = { isLoadingDataset }
373
- { ...( additionalConfig ?. drillBy || { } ) }
374
- /> ,
375
- ) ;
354
+
355
+ const hasDrillBy = enhancedFilters ?. drillBy ?. groupbyFieldName ;
356
+ const handlesDimensionContextMenu = getChartMetadataRegistry ( )
357
+ . get ( formData . viz_type )
358
+ ?. behaviors . find ( behavior => behavior === Behavior . DrillBy ) ;
359
+ const isDrillByDisabled = ! handlesDimensionContextMenu || ! hasDrillBy ;
360
+
361
+ // Add a custom render component for DrillBy submenu to support react-window
362
+ menuItems . push ( {
363
+ key : 'drill-by-submenu' ,
364
+ disabled : isDrillByDisabled ,
365
+ label : (
366
+ < DrillBySubmenu
367
+ drillByConfig = { enhancedFilters ?. drillBy }
368
+ onSelection = { onSelection }
369
+ onCloseMenu = { closeContextMenu }
370
+ formData = { formData }
371
+ onDrillBy = { handleDrillBy }
372
+ dataset = { filteredDataset }
373
+ isLoadingDataset = { isLoadingDataset }
374
+ { ...( additionalConfig ?. drillBy || { } ) }
375
+ />
376
+ ) ,
377
+ } ) ;
376
378
}
377
379
378
380
const open = useCallback (
@@ -404,30 +406,22 @@ const ChartContextMenu = (
404
406
return ReactDOM . createPortal (
405
407
< >
406
408
< Dropdown
407
- popupRender = { ( ) => (
408
- < Menu
409
- className = "chart-context-menu"
410
- data-test = "chart-context-menu"
411
- onOpenChange = { setOpenKeys }
412
- onClick = { ( ) => {
413
- setVisible ( false ) ;
414
- setOpenKeys ( [ ] ) ;
415
- onClose ( ) ;
416
- } }
417
- >
418
- { menuItems . length ? (
419
- menuItems
420
- ) : (
421
- < Menu . Item disabled > { t ( 'No actions' ) } </ Menu . Item >
422
- ) }
423
- </ Menu >
409
+ menu = { {
410
+ items :
411
+ menuItems . length > 0
412
+ ? menuItems
413
+ : [ { key : 'no-actions' , label : t ( 'No actions' ) , disabled : true } ] ,
414
+ onClick : ( ) => {
415
+ setVisible ( false ) ;
416
+ onClose ( ) ;
417
+ } ,
418
+ } }
419
+ dropdownRender = { menu => (
420
+ < div data-test = "chart-context-menu" > { menu } </ div >
424
421
) }
425
422
trigger = { [ 'click' ] }
426
423
onOpenChange = { value => {
427
424
setVisible ( value ) ;
428
- if ( ! value ) {
429
- setOpenKeys ( [ ] ) ;
430
- }
431
425
} }
432
426
open = { visible }
433
427
>
0 commit comments