-
Notifications
You must be signed in to change notification settings - Fork 36
Expand file tree
/
Copy pathChatbotHeaderMenu.tsx
More file actions
104 lines (94 loc) · 3.2 KB
/
ChatbotHeaderMenu.tsx
File metadata and controls
104 lines (94 loc) · 3.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
import type { Ref, FunctionComponent } from 'react';
import { forwardRef, useEffect, useMemo, useRef, useState } from 'react';
import { Button, ButtonProps, Icon, Tooltip, TooltipProps } from '@patternfly/react-core';
import BarsIcon from '@patternfly/react-icons/dist/esm/icons/bars-icon';
export interface ChatbotHeaderMenuProps extends ButtonProps {
/** Callback function to attach to menu toggle on top right of chatbot header. */
onMenuToggle: () => void;
/** Custom classname for the header component */
className?: string;
/** Props spread to the PF Tooltip component wrapping the display mode dropdown */
tooltipProps?: TooltipProps;
/** Aria label for menu */
menuAriaLabel?: string;
/** Ref applied to menu */
innerRef?: React.Ref<HTMLButtonElement>;
/** Content used in tooltip */
tooltipContent?: string;
/** Sets menu to compact styling. */
isCompact?: boolean;
}
const ChatbotHeaderMenuBase: FunctionComponent<ChatbotHeaderMenuProps> = ({
className,
onMenuToggle,
tooltipProps,
menuAriaLabel = 'Chat history drawer',
innerRef,
tooltipContent = 'Chat history drawer',
isCompact,
...props
}: ChatbotHeaderMenuProps) => {
const [isDrawerAnimating, setIsDrawerAnimating] = useState(false);
// I'd like to use a prop here later if this works
const drawerState = props['aria-expanded'];
const isDrawerOpen = drawerState === true;
const prevDrawerStateRef = useRef<boolean | undefined>(isDrawerOpen);
const buttonRef = useRef<HTMLButtonElement | null>(null);
useEffect(() => {
if (drawerState !== undefined) {
const wasDrawerOpen = prevDrawerStateRef.current === true;
const isDrawerClosing = wasDrawerOpen && !isDrawerOpen;
setIsDrawerAnimating(true);
const timeout = setTimeout(() => {
setIsDrawerAnimating(false);
if (isDrawerClosing) {
requestAnimationFrame(() => {
buttonRef.current?.focus();
});
}
}, 350);
prevDrawerStateRef.current = isDrawerOpen;
return () => clearTimeout(timeout);
}
}, [drawerState, isDrawerOpen]);
const button = useMemo(
() => (
<Button
className={`pf-chatbot__button--toggle-menu ${isCompact ? 'pf-m-compact' : ''}`}
variant="plain"
onClick={onMenuToggle}
aria-label={menuAriaLabel}
ref={innerRef ?? buttonRef}
icon={
<Icon size={isCompact ? 'lg' : 'xl'} isInline>
<BarsIcon />
</Icon>
}
size={isCompact ? 'sm' : undefined}
{...props}
/>
),
// eslint-disable-next-line react-hooks/exhaustive-deps
[isCompact, menuAriaLabel, onMenuToggle, innerRef, buttonRef]
);
return (
<div className={`pf-chatbot__menu ${className}`}>
{isDrawerAnimating ? (
button
) : (
<Tooltip
content={tooltipContent}
position="bottom"
// prevents VO announcements of both aria label and tooltip
aria="none"
{...tooltipProps}
>
{button}
</Tooltip>
)}
</div>
);
};
export const ChatbotHeaderMenu = forwardRef((props: ChatbotHeaderMenuProps, ref: Ref<HTMLButtonElement>) => (
<ChatbotHeaderMenuBase innerRef={ref} {...props} />
));