11import type { Ref , FunctionComponent } from 'react' ;
2- import { forwardRef } from 'react' ;
2+ import { forwardRef , useEffect , useMemo , useRef , useState } from 'react' ;
33
44import { Button , ButtonProps , Icon , Tooltip , TooltipProps } from '@patternfly/react-core' ;
55import BarsIcon from '@patternfly/react-icons/dist/esm/icons/bars-icon' ;
@@ -30,21 +30,43 @@ const ChatbotHeaderMenuBase: FunctionComponent<ChatbotHeaderMenuProps> = ({
3030 tooltipContent = 'Chat history menu' ,
3131 isCompact,
3232 ...props
33- } : ChatbotHeaderMenuProps ) => (
34- < div className = { `pf-chatbot__menu ${ className } ` } >
35- < Tooltip
36- content = { tooltipContent }
37- position = "bottom"
38- // prevents VO announcements of both aria label and tooltip
39- aria = "none"
40- { ...tooltipProps }
41- >
33+ } : ChatbotHeaderMenuProps ) => {
34+ const [ isDrawerAnimating , setIsDrawerAnimating ] = useState ( false ) ;
35+ // I'd like to use a prop here later if this works
36+ const drawerState = props [ 'aria-expanded' ] ;
37+ const isDrawerOpen = drawerState === true ;
38+ const prevDrawerStateRef = useRef < boolean | undefined > ( isDrawerOpen ) ;
39+ const buttonRef = useRef < HTMLButtonElement | null > ( null ) ;
40+
41+ useEffect ( ( ) => {
42+ if ( drawerState !== undefined ) {
43+ const wasDrawerOpen = prevDrawerStateRef . current === true ;
44+ const isDrawerClosing = wasDrawerOpen && ! isDrawerOpen ;
45+
46+ setIsDrawerAnimating ( true ) ;
47+ const timeout = setTimeout ( ( ) => {
48+ setIsDrawerAnimating ( false ) ;
49+
50+ if ( isDrawerClosing ) {
51+ requestAnimationFrame ( ( ) => {
52+ buttonRef . current ?. focus ( ) ;
53+ } ) ;
54+ }
55+ } , 350 ) ;
56+
57+ prevDrawerStateRef . current = isDrawerOpen ;
58+ return ( ) => clearTimeout ( timeout ) ;
59+ }
60+ } , [ drawerState , isDrawerOpen ] ) ;
61+
62+ const button = useMemo (
63+ ( ) => (
4264 < Button
4365 className = { `pf-chatbot__button--toggle-menu ${ isCompact ? 'pf-m-compact' : '' } ` }
4466 variant = "plain"
4567 onClick = { onMenuToggle }
4668 aria-label = { menuAriaLabel }
47- ref = { innerRef }
69+ ref = { innerRef ?? buttonRef }
4870 icon = {
4971 < Icon size = { isCompact ? 'lg' : 'xl' } isInline >
5072 < BarsIcon />
@@ -53,9 +75,29 @@ const ChatbotHeaderMenuBase: FunctionComponent<ChatbotHeaderMenuProps> = ({
5375 size = { isCompact ? 'sm' : undefined }
5476 { ...props }
5577 />
56- </ Tooltip >
57- </ div >
58- ) ;
78+ ) ,
79+ // eslint-disable-next-line react-hooks/exhaustive-deps
80+ [ isCompact , menuAriaLabel , onMenuToggle , innerRef , buttonRef ]
81+ ) ;
82+
83+ return (
84+ < div className = { `pf-chatbot__menu ${ className } ` } >
85+ { isDrawerAnimating ? (
86+ button
87+ ) : (
88+ < Tooltip
89+ content = { tooltipContent }
90+ position = "bottom"
91+ // prevents VO announcements of both aria label and tooltip
92+ aria = "none"
93+ { ...tooltipProps }
94+ >
95+ { button }
96+ </ Tooltip >
97+ ) }
98+ </ div >
99+ ) ;
100+ } ;
59101
60102export const ChatbotHeaderMenu = forwardRef ( ( props : ChatbotHeaderMenuProps , ref : Ref < HTMLButtonElement > ) => (
61103 < ChatbotHeaderMenuBase innerRef = { ref } { ...props } />
0 commit comments