1414 * limitations under the License.
1515 */
1616
17- import { useMemo } from 'react' ;
18- import type { CSSProperties } from 'react' ;
17+ import { useEffect , useMemo , useRef } from 'react' ;
18+ import type { ComponentType , CSSProperties } from 'react' ;
1919import { HeaderDropdownComponent } from './HeaderDropdownComponent' ;
2020import { useDropdownManager } from '../../hooks' ;
2121import HelpOutlineIcon from '@mui/icons-material/HelpOutline' ;
2222import { useHelpDropdownMountPoints } from '../../hooks/useHelpDropdownMountPoints' ;
2323import { MenuSection } from './MenuSection' ;
24+ import { DropdownEmptyState } from './DropdownEmptyState' ;
25+ import SupportAgentIcon from '@mui/icons-material/SupportAgent' ;
26+ import { useValidComponentTracker } from '../../hooks/useValidComponentTracker' ;
2427
2528/**
2629 * @public
27- * Props for Help Dropdown
2830 */
2931export interface HelpDropdownProps {
3032 layout ?: CSSProperties ;
3133}
3234
35+ const ValidityTracker = ( {
36+ Component,
37+ props,
38+ componentId,
39+ onValidityChange,
40+ } : {
41+ Component : ComponentType < any > ;
42+ props : any ;
43+ componentId : string ;
44+ onValidityChange : ( componentId : string , isValid : boolean ) => void ;
45+ } ) => {
46+ const contentRef = useRef < HTMLDivElement > ( null ) ;
47+
48+ useEffect ( ( ) => {
49+ const checkContent = ( ) => {
50+ if ( ! contentRef . current ) return ;
51+
52+ const element = contentRef . current ;
53+ const hasText = ( element . textContent ?. trim ( ) . length ?? 0 ) > 0 ;
54+ const hasChildren = element . children . length > 0 ;
55+ const hasChildNodes = element . childNodes . length > 0 ;
56+
57+ // A component is valid if it renders ANY content at all
58+ const componentIsValid = hasText || hasChildren || hasChildNodes ;
59+
60+ onValidityChange ( componentId , componentIsValid ) ;
61+ } ;
62+
63+ // Check after component has had time to render (longer timeout for lazy components)
64+ const timer1 = setTimeout ( checkContent , 500 ) ;
65+ const timer2 = setTimeout ( checkContent , 1500 ) ; // Double check later
66+
67+ return ( ) => {
68+ clearTimeout ( timer1 ) ;
69+ clearTimeout ( timer2 ) ;
70+ } ;
71+ } , [ componentId , onValidityChange ] ) ;
72+
73+ try {
74+ return (
75+ < div ref = { contentRef } >
76+ < Component { ...props } />
77+ </ div >
78+ ) ;
79+ } catch ( error ) {
80+ onValidityChange ( componentId , false ) ;
81+ return null ;
82+ }
83+ } ;
84+
3385export const HelpDropdown = ( { layout } : HelpDropdownProps ) => {
3486 const { anchorEl, handleOpen, handleClose } = useDropdownManager ( ) ;
35-
3687 const helpDropdownMountPoints = useHelpDropdownMountPoints ( ) ;
3788
38- const menuItems = useMemo ( ( ) => {
89+ const { shouldShowEmpty, updateComponentValidity } = useValidComponentTracker (
90+ helpDropdownMountPoints ?. length ?? 0 ,
91+ ) ;
92+
93+ // Create all mount point items with validity tracking
94+ const allMenuItems = useMemo ( ( ) => {
3995 return ( helpDropdownMountPoints ?? [ ] )
40- . map ( mp => ( {
41- Component : mp . Component ,
42- icon : mp . config ?. props ?. icon ,
43- label : mp . config ?. props ?. title ,
44- link : mp . config ?. props ?. link ,
45- tooltip : mp . config ?. props ?. tooltip ,
46- style : mp . config ?. style ,
47- priority : mp . config ?. priority ?? 0 ,
48- } ) )
96+ . map ( ( mp , index ) => {
97+ const componentId = `${ mp . config ?. props ?. title || 'helpItem' } -${
98+ mp . config ?. priority || 0
99+ } -${ index } `;
100+
101+ return {
102+ componentId,
103+ Component : ( ) => (
104+ < ValidityTracker
105+ Component = { mp . Component }
106+ props = { mp . config ?. props || { } }
107+ componentId = { componentId }
108+ onValidityChange = { updateComponentValidity }
109+ />
110+ ) ,
111+ icon : mp . config ?. props ?. icon ,
112+ label : mp . config ?. props ?. title ,
113+ link : mp . config ?. props ?. link ,
114+ tooltip : mp . config ?. props ?. tooltip ,
115+ style : mp . config ?. style ,
116+ priority : mp . config ?. priority ?? 0 ,
117+ } ;
118+ } )
49119 . sort ( ( a , b ) => ( b . priority ?? 0 ) - ( a . priority ?? 0 ) ) ;
50- } , [ helpDropdownMountPoints ] ) ;
120+ } , [ helpDropdownMountPoints , updateComponentValidity ] ) ;
51121
52- if ( menuItems . length === 0 ) {
53- return null ;
54- }
122+ const menuItems = allMenuItems ;
55123
56124 return (
57125 < HeaderDropdownComponent
@@ -66,7 +134,17 @@ export const HelpDropdown = ({ layout }: HelpDropdownProps) => {
66134 onClose = { handleClose }
67135 anchorEl = { anchorEl }
68136 >
69- < MenuSection hideDivider items = { menuItems } handleClose = { handleClose } />
137+ { ! shouldShowEmpty ? (
138+ < MenuSection hideDivider items = { menuItems } handleClose = { handleClose } />
139+ ) : (
140+ < DropdownEmptyState
141+ title = "No support links"
142+ subTitle = "Your administrator needs to set up support links."
143+ icon = {
144+ < SupportAgentIcon sx = { { fontSize : 64 , color : 'text.disabled' } } />
145+ }
146+ />
147+ ) }
70148 </ HeaderDropdownComponent >
71149 ) ;
72150} ;
0 commit comments