1
1
import * as React from 'react' ;
2
- import { Dropdown , DropdownItem } from '@patternfly/react-core' ;
2
+ import { Menu , DropdownItem } from '@patternfly/react-core' ;
3
3
import AngleRightIcon from '@patternfly/react-icons/dist/esm/icons/angle-right-icon' ;
4
4
import { css } from '@patternfly/react-styles' ;
5
5
import topologyStyles from '../../css/topology-components' ;
@@ -11,6 +11,17 @@ interface ContextSubMenuItemProps {
11
11
children : React . ReactNode [ ] ;
12
12
}
13
13
14
+ /**
15
+ * Check if an event target implements the [DOM Node interface][1].
16
+ * Needed to prevent runtime errors where the event target is not a DOM node,
17
+ * but `contains` is still called on it.
18
+ *
19
+ * [1]: https://developer.mozilla.org/en-US/docs/Web/API/Node
20
+ */
21
+ const implementsDOMNode = ( node : any ) : boolean => {
22
+ return node && typeof node === 'object' && node . nodeType && node . nodeName ;
23
+ } ;
24
+
14
25
const ContextSubMenuItem : React . FunctionComponent < ContextSubMenuItemProps > = ( { label, children, ...other } ) => {
15
26
const nodeRef = React . useRef < HTMLButtonElement > ( null ) ;
16
27
const subMenuRef = React . useRef < HTMLDivElement > ( null ) ;
@@ -29,7 +40,10 @@ const ContextSubMenuItem: React.FunctionComponent<ContextSubMenuItemProps> = ({
29
40
onMouseEnter = { ( ) => setOpen ( true ) }
30
41
onMouseLeave = { ( e ) => {
31
42
// if the mouse leaves this item, close the sub menu only if the mouse did not enter the sub menu itself
32
- if ( ! subMenuRef . current || ! subMenuRef . current . contains ( e . relatedTarget as Node ) ) {
43
+ if (
44
+ ! subMenuRef . current ||
45
+ ( implementsDOMNode ( e . relatedTarget ) && ! subMenuRef . current . contains ( e . relatedTarget as Node ) )
46
+ ) {
33
47
setOpen ( false ) ;
34
48
}
35
49
} }
@@ -51,22 +65,21 @@ const ContextSubMenuItem: React.FunctionComponent<ContextSubMenuItemProps> = ({
51
65
closeOnOutsideClick
52
66
onRequestClose = { ( e ) => {
53
67
// only close the sub menu if clicking anywhere outside the menu item that owns the sub menu
54
- if ( ! e || ! nodeRef . current || ! nodeRef . current . contains ( e . target as Node ) ) {
68
+ if ( ! e || ! nodeRef . current || ( implementsDOMNode ( e . target ) && ! nodeRef . current . contains ( e . target as Node ) ) ) {
55
69
setOpen ( false ) ;
56
70
}
57
71
} }
58
72
reference = { referenceCb }
59
- // use the parent node to capture the li
60
- container = { nodeRef . current ? nodeRef . current . parentElement : nodeRef . current }
73
+ container = { nodeRef . current ?. closest ( '.pf-v6-c-menu__content' ) }
61
74
returnFocus
62
75
>
63
- < div
64
- ref = { subMenuRef }
65
- role = "presentation"
66
- className = "pf-v6-c-dropdown pf-m-expanded"
76
+ < Menu
67
77
onMouseLeave = { ( e ) => {
68
78
// only close the sub menu if the mouse does not enter the item
69
- if ( ! nodeRef . current || ! nodeRef . current . contains ( e . relatedTarget as Node ) ) {
79
+ if (
80
+ ! nodeRef . current ||
81
+ ( implementsDOMNode ( e . relatedTarget ) && ! nodeRef . current . contains ( e . relatedTarget as Node ) )
82
+ ) {
70
83
setOpen ( false ) ;
71
84
}
72
85
} }
@@ -77,11 +90,12 @@ const ContextSubMenuItem: React.FunctionComponent<ContextSubMenuItemProps> = ({
77
90
e . stopPropagation ( ) ;
78
91
}
79
92
} }
93
+ ref = { subMenuRef }
94
+ role = "presentation"
95
+ isNavFlyout
80
96
>
81
- < Dropdown toggle = { ( ) => < > </ > } className = { css ( topologyStyles . topologyContextMenuCDropdownMenu ) } >
82
- { children }
83
- </ Dropdown >
84
- </ div >
97
+ { children }
98
+ </ Menu >
85
99
</ Popper >
86
100
</ >
87
101
) ;
0 commit comments