@patternfly/chatbot 6.8.0-prerelease.6 → 6.8.0-prerelease.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
- import type { FunctionComponent } from 'react';
2
- import { ButtonProps, DrawerProps, DrawerPanelContentProps, DrawerContentProps, DrawerContentBodyProps, DrawerHeadProps, DrawerActionsProps, DrawerCloseButtonProps, DrawerPanelBodyProps, SkeletonProps, TitleProps, SearchInputProps, MenuProps, MenuListProps, MenuItemProps, MenuGroupProps, MenuContentProps } from '@patternfly/react-core';
1
+ import type { FunctionComponent, ReactNode } from 'react';
2
+ import { ButtonProps, DrawerProps, DrawerPanelContentProps, DrawerContentProps, DrawerContentBodyProps, DrawerHeadProps, DrawerActionsProps, DrawerCloseButtonProps, DrawerPanelBodyProps, SkeletonProps, TitleProps, SearchInputProps, MenuProps, MenuListProps, MenuItemProps, MenuGroupProps, MenuContentProps, ExpandableSectionProps, ExpandableSectionToggleProps } from '@patternfly/react-core';
3
3
  import { ChatbotDisplayMode } from '../Chatbot/Chatbot';
4
4
  import { HistoryEmptyStateProps } from './EmptyState';
5
5
  export interface Conversation {
@@ -24,6 +24,49 @@ export interface Conversation {
24
24
  /** Custom dropdown ID to ensure uniqueness across demo instances */
25
25
  dropdownId?: string;
26
26
  }
27
+ export interface ConversationGroupShowAll {
28
+ /** Number of items visible when collapsed */
29
+ visibleCount: number;
30
+ /** Whether all items are shown */
31
+ isExpanded: boolean;
32
+ /** Callback when show all / show less is toggled */
33
+ onToggle: (isExpanded: boolean) => void;
34
+ /** Custom content rendered in the show all / show less toggle. Defaults to "Show all" when collapsed and "Show less" when expanded. */
35
+ label?: ReactNode;
36
+ }
37
+ export interface ConversationGroupExpandable {
38
+ /** Whether the group content is expanded */
39
+ isExpanded: boolean;
40
+ /** Callback when the group is toggled */
41
+ onToggle: (isExpanded: boolean) => void;
42
+ /** Additional props applied to ExpandableSection */
43
+ expandableSectionProps?: Omit<ExpandableSectionProps, 'ref'>;
44
+ /** Additional props applied to ExpandableSectionToggle */
45
+ expandableSectionToggleProps?: ExpandableSectionToggleProps;
46
+ }
47
+ export interface ConversationGroup {
48
+ /** Unique group id */
49
+ id: string;
50
+ /** Group label rendered as a MenuGroup heading or expandable toggle label */
51
+ label: ReactNode;
52
+ /** Conversation items or custom menu content such as a "Show all" action */
53
+ items: (Conversation | ReactNode)[];
54
+ /** Content rendered after the group's menu list */
55
+ footer?: ReactNode;
56
+ /** Custom group header that replaces the default label or expandable toggle */
57
+ header?: ReactNode;
58
+ /** When set, the group renders as an expandable section */
59
+ expandable?: ConversationGroupExpandable;
60
+ /** When set, truncates the list with an expandable show all / show less section */
61
+ showAll?: ConversationGroupShowAll;
62
+ /** Additional props applied to the conversation menu group */
63
+ menuGroupProps?: MenuGroupProps;
64
+ /** Additional props applied to the conversation list */
65
+ menuListProps?: Omit<MenuListProps, 'children'>;
66
+ }
67
+ export type Conversations = (Conversation | ReactNode)[] | ConversationGroup[] | {
68
+ [key: string]: (Conversation | ReactNode)[];
69
+ };
27
70
  export interface ChatbotConversationHistoryNavProps extends DrawerProps {
28
71
  /** Function called to toggle drawer */
29
72
  onDrawerToggle: (event: React.KeyboardEvent | React.MouseEvent | React.TransitionEvent) => void;
@@ -35,9 +78,7 @@ export interface ChatbotConversationHistoryNavProps extends DrawerProps {
35
78
  /** Callback function for when an item is selected */
36
79
  onSelectActiveItem?: (event?: React.MouseEvent, itemId?: string | number) => void;
37
80
  /** Items shown in chat history */
38
- conversations: Conversation[] | {
39
- [key: string]: Conversation[];
40
- };
81
+ conversations: Conversations;
41
82
  /** Additional button props for new chat button. */
42
83
  newChatButtonProps?: ButtonProps;
43
84
  /** Additional props applied to conversation menu group. If conversations is an object, you should pass an object of MenuGroupProps for each group. */
@@ -24,35 +24,144 @@ const Chatbot_1 = require("../Chatbot/Chatbot");
24
24
  const ChatbotConversationHistoryDropdown_1 = __importDefault(require("./ChatbotConversationHistoryDropdown"));
25
25
  const LoadingState_1 = __importDefault(require("./LoadingState"));
26
26
  const EmptyState_1 = __importDefault(require("./EmptyState"));
27
+ const isConversation = (item) => Boolean(item && typeof item === 'object' && 'id' in item && 'text' in item && !('items' in item));
28
+ const isConversationGroup = (item) => Boolean(item && typeof item === 'object' && 'id' in item && 'label' in item && 'items' in item);
29
+ const isConversationGroupArray = (items) => items.length > 0 && isConversationGroup(items[0]);
30
+ const getExpandableGroupContentId = (groupId) => `chatbot-nav-group-${groupId}-content`;
31
+ const getGroupLabelId = (groupId) => `chatbot-nav-group-${groupId}-label`;
32
+ const getExpandableGroupToggleId = (groupId) => `chatbot-nav-group-${groupId}-toggle`;
33
+ const getShowAllToggleId = (groupId) => `chatbot-nav-group-${groupId}-show-all-toggle`;
34
+ const getStaticMenuLabelId = (group) => { var _a, _b; return (_b = (_a = group.menuGroupProps) === null || _a === void 0 ? void 0 : _a.titleId) !== null && _b !== void 0 ? _b : getGroupLabelId(group.id); };
35
+ const getMenuListLabelledBy = (group) => {
36
+ var _a, _b, _c, _d;
37
+ if (group.expandable) {
38
+ return (_b = (_a = group.menuGroupProps) === null || _a === void 0 ? void 0 : _a['aria-labelledby']) !== null && _b !== void 0 ? _b : getExpandableGroupToggleId(group.id);
39
+ }
40
+ return (_d = (_c = group.menuGroupProps) === null || _c === void 0 ? void 0 : _c['aria-labelledby']) !== null && _d !== void 0 ? _d : getStaticMenuLabelId(group);
41
+ };
42
+ const getMenuListProps = (group) => {
43
+ var _a, _b;
44
+ return (Object.assign(Object.assign({}, group.menuListProps), { 'aria-labelledby': (_b = (_a = group.menuListProps) === null || _a === void 0 ? void 0 : _a['aria-labelledby']) !== null && _b !== void 0 ? _b : getMenuListLabelledBy(group) }));
45
+ };
46
+ const focusElementById = (id) => {
47
+ var _a;
48
+ (_a = document.getElementById(id)) === null || _a === void 0 ? void 0 : _a.focus();
49
+ };
50
+ // Matches PatternFly's Menu "view more" example: expanding moves focus to the first
51
+ // newly revealed item; collapsing keeps focus on the toggle as it updates to "Show all".
52
+ const useShowAllFocusManagement = (isExpanded, toggleId, firstOverflowItemRef, hasOverflowItems) => {
53
+ const wasExpandedRef = (0, react_1.useRef)(isExpanded);
54
+ (0, react_1.useEffect)(() => {
55
+ var _a;
56
+ if (hasOverflowItems && isExpanded !== wasExpandedRef.current) {
57
+ if (isExpanded) {
58
+ (_a = firstOverflowItemRef.current) === null || _a === void 0 ? void 0 : _a.focus();
59
+ }
60
+ else {
61
+ focusElementById(toggleId);
62
+ }
63
+ }
64
+ wasExpandedRef.current = isExpanded;
65
+ }, [isExpanded, toggleId, firstOverflowItemRef, hasOverflowItems]);
66
+ };
67
+ // The toggle is rendered as a real MenuItem (rather than a standalone button) so it
68
+ // participates in the Menu's built-in roving tabindex and arrow-key navigation just
69
+ // like any other conversation item, instead of being skipped over or interrupting
70
+ // arrow-key handling. Since it keeps a stable key and DOM position regardless of how
71
+ // many overflow items are shown, it also keeps focus across clicks without being
72
+ // unmounted or recreated.
73
+ const ShowAllGroupBody = ({ group, getNavItem }) => {
74
+ const { visibleCount, isExpanded, onToggle, label } = group.showAll;
75
+ const conversationItems = group.items.filter(isConversation);
76
+ const alwaysVisibleItems = conversationItems.slice(0, visibleCount);
77
+ const overflowItems = conversationItems.slice(visibleCount);
78
+ const hasOverflowItems = overflowItems.length > 0;
79
+ const toggleId = getShowAllToggleId(group.id);
80
+ const firstOverflowItemRef = (0, react_1.useRef)(null);
81
+ const toggleLabel = label !== null && label !== void 0 ? label : (isExpanded ? 'Show less' : 'Show all');
82
+ useShowAllFocusManagement(isExpanded, toggleId, firstOverflowItemRef, hasOverflowItems);
83
+ return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)(react_core_1.MenuList, Object.assign({}, getMenuListProps(group), { children: [alwaysVisibleItems.map((chat) => ((0, jsx_runtime_1.jsx)(react_1.Fragment, { children: getNavItem(chat) }, chat.id))), isExpanded &&
84
+ overflowItems.map((chat, index) => ((0, jsx_runtime_1.jsx)(react_1.Fragment, { children: getNavItem(chat, undefined, index === 0 ? firstOverflowItemRef : undefined) }, chat.id))), hasOverflowItems && ((0, jsx_runtime_1.jsx)(react_core_1.MenuItem, { id: toggleId, itemId: toggleId, className: "pf-chatbot__menu-show-all-toggle", onClick: () => onToggle(!isExpanded), children: toggleLabel }, "show-all-toggle"))] })), group.footer] }));
85
+ };
86
+ const ExpandableConversationGroup = ({ group, children }) => {
87
+ var _a, _b;
88
+ const toggleId = getExpandableGroupToggleId(group.id);
89
+ const contentId = getExpandableGroupContentId(group.id);
90
+ const { isExpanded, onToggle, expandableSectionProps, expandableSectionToggleProps } = group.expandable;
91
+ return ((0, jsx_runtime_1.jsxs)("div", { className: `pf-chatbot__menu-item-header pf-chatbot__menu-item-header--expandable ${(_b = (_a = group.menuGroupProps) === null || _a === void 0 ? void 0 : _a.className) !== null && _b !== void 0 ? _b : ''}`, children: [(0, jsx_runtime_1.jsx)(react_core_1.ExpandableSectionToggle, Object.assign({ toggleId: toggleId, contentId: contentId, isExpanded: isExpanded, onToggle: onToggle, toggleWrapper: "h3", className: "pf-chatbot__menu-group-toggle" }, expandableSectionToggleProps, { children: group.label })), (0, jsx_runtime_1.jsx)(react_core_1.ExpandableSection, Object.assign({ isDetached: true, isExpanded: isExpanded, toggleId: toggleId, contentId: contentId }, expandableSectionProps, { children: isExpanded && children }))] }));
92
+ };
93
+ const buildConversationMenuSegments = (groups) => groups.reduce((segments, group) => {
94
+ if (group.expandable) {
95
+ return [...segments, { type: 'expandable', group }];
96
+ }
97
+ const last = segments[segments.length - 1];
98
+ if ((last === null || last === void 0 ? void 0 : last.type) === 'static') {
99
+ last.groups.push(group);
100
+ return segments;
101
+ }
102
+ return [...segments, { type: 'static', groups: [group] }];
103
+ }, []);
27
104
  const ChatbotConversationHistoryNav = (_a) => {
28
105
  var { onDrawerToggle, isDrawerOpen, setIsDrawerOpen, activeItemId, onSelectActiveItem, conversations, menuListProps, newChatButtonText = 'New chat', drawerContent, onNewChat, newChatButtonProps, searchInputPlaceholder = 'Search previous conversations...', searchInputAriaLabel = 'Search previous conversations', searchInputProps, handleTextInputChange, displayMode, reverseButtonOrder = false, drawerActionsTestId = 'chatbot-nav-drawer-actions', drawerPanelContentProps, drawerContentProps, drawerContentBodyProps, drawerHeadProps, drawerActionsProps, drawerCloseButtonProps, drawerPanelBodyProps, hasDrawerHeadDivider, isLoading, loadingState, errorState, emptyState, noResultsState, isCompact, title = 'Chat history', navTitleProps, navTitleIcon = (0, jsx_runtime_1.jsx)(react_icons_1.RhUiClockIcon, {}), searchInputScreenReaderText, searchActionStart, searchActionEnd, searchToolbar, menuProps, menuGroupProps, menuContentProps } = _a, props = __rest(_a, ["onDrawerToggle", "isDrawerOpen", "setIsDrawerOpen", "activeItemId", "onSelectActiveItem", "conversations", "menuListProps", "newChatButtonText", "drawerContent", "onNewChat", "newChatButtonProps", "searchInputPlaceholder", "searchInputAriaLabel", "searchInputProps", "handleTextInputChange", "displayMode", "reverseButtonOrder", "drawerActionsTestId", "drawerPanelContentProps", "drawerContentProps", "drawerContentBodyProps", "drawerHeadProps", "drawerActionsProps", "drawerCloseButtonProps", "drawerPanelBodyProps", "hasDrawerHeadDivider", "isLoading", "loadingState", "errorState", "emptyState", "noResultsState", "isCompact", "title", "navTitleProps", "navTitleIcon", "searchInputScreenReaderText", "searchActionStart", "searchActionEnd", "searchToolbar", "menuProps", "menuGroupProps", "menuContentProps"]);
29
106
  const drawerRef = (0, react_1.useRef)(null);
30
107
  const onExpand = () => {
31
108
  drawerRef.current && drawerRef.current.focus();
32
109
  };
33
- const isConversation = (item) => item && typeof item === 'object' && 'id' in item && 'text' in item;
34
- const getNavItem = (conversation) => {
110
+ const getNavItem = (conversation, itemOverrides, itemRef) => {
35
111
  var _a;
36
- return ((0, jsx_runtime_1.jsx)(react_core_1.MenuItem, Object.assign({ className: `pf-chatbot__menu-item ${activeItemId && activeItemId === conversation.id ? 'pf-chatbot__menu-item--active' : ''}`, itemId: conversation.id }, (conversation.noIcon ? {} : { icon: (_a = conversation.icon) !== null && _a !== void 0 ? _a : (0, jsx_runtime_1.jsx)(react_icons_1.RhUiCommentIcon, {}) }), (conversation.menuItems
112
+ return ((0, jsx_runtime_1.jsx)(react_core_1.MenuItem, Object.assign({ className: `pf-chatbot__menu-item ${activeItemId && activeItemId === conversation.id ? 'pf-chatbot__menu-item--active' : ''}`, itemId: conversation.id, ref: itemRef }, (conversation.noIcon ? {} : { icon: (_a = conversation.icon) !== null && _a !== void 0 ? _a : (0, jsx_runtime_1.jsx)(react_icons_1.RhUiCommentIcon, {}) }), (conversation.menuItems
37
113
  ? {
38
114
  actions: ((0, jsx_runtime_1.jsx)(ChatbotConversationHistoryDropdown_1.default, { menuClassName: conversation.menuClassName, onSelect: conversation.onSelect, menuItems: conversation.menuItems, label: conversation.label }))
39
115
  }
40
- : {}), conversation.additionalProps, { children: conversation.text })));
116
+ : {}), conversation.additionalProps, itemOverrides, { children: conversation.text })));
41
117
  };
118
+ const renderConversationItems = (items, keyPrefix = '') => items.map((item, index) => {
119
+ if (isConversation(item)) {
120
+ return (0, jsx_runtime_1.jsx)(react_1.Fragment, { children: getNavItem(item) }, item.id);
121
+ }
122
+ const key = (0, react_1.isValidElement)(item) && item.key != null ? String(item.key) : `${keyPrefix}-${index}`;
123
+ return (0, jsx_runtime_1.jsx)(react_1.Fragment, { children: item }, key);
124
+ });
125
+ const renderGroupBody = (group) => {
126
+ if (group.showAll) {
127
+ return (0, jsx_runtime_1.jsx)(ShowAllGroupBody, { group: group, getNavItem: getNavItem });
128
+ }
129
+ return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(react_core_1.MenuList, Object.assign({}, getMenuListProps(group), { children: renderConversationItems(group.items, group.id) })), group.footer] }));
130
+ };
131
+ const renderConversationGroup = (group) => {
132
+ var _a, _b, _c, _d;
133
+ if (group.header) {
134
+ return ((0, jsx_runtime_1.jsxs)("div", { className: `pf-chatbot__menu-item-header ${(_b = (_a = group.menuGroupProps) === null || _a === void 0 ? void 0 : _a.className) !== null && _b !== void 0 ? _b : ''}`, children: [group.header, renderGroupBody(group)] }, group.id));
135
+ }
136
+ const labelId = getStaticMenuLabelId(group);
137
+ return ((0, jsx_runtime_1.jsx)(react_core_1.MenuGroup, Object.assign({ className: "pf-chatbot__menu-item-header", label: group.label, labelHeadingLevel: "h3" }, group.menuGroupProps, { titleId: labelId, "aria-labelledby": (_d = (_c = group.menuGroupProps) === null || _c === void 0 ? void 0 : _c['aria-labelledby']) !== null && _d !== void 0 ? _d : labelId, children: renderGroupBody(group) }), group.id));
138
+ };
139
+ const renderConversationMenu = (content, key) => ((0, jsx_runtime_1.jsx)(react_core_1.Menu, Object.assign({ className: "pf-chatbot__history-menu", isPlain: true, onSelect: onSelectActiveItem, activeItemId: activeItemId }, menuProps, { children: (0, jsx_runtime_1.jsx)(react_core_1.MenuContent, Object.assign({}, menuContentProps, { children: content })) }), key));
140
+ const renderConversationMenuSegment = (segment) => {
141
+ if (segment.type === 'static') {
142
+ return renderConversationMenu((0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: segment.groups.map(renderConversationGroup) }), segment.groups.map((group) => group.id).join('-'));
143
+ }
144
+ return ((0, jsx_runtime_1.jsx)(ExpandableConversationGroup, { group: segment.group, children: renderConversationMenu(renderGroupBody(segment.group)) }, segment.group.id));
145
+ };
146
+ const normalizeObjectGroups = (groupedConversations) => Object.keys(groupedConversations).map((groupKey) => ({
147
+ id: groupKey,
148
+ label: groupKey,
149
+ items: groupedConversations[groupKey],
150
+ menuGroupProps: menuGroupProps && typeof menuGroupProps === 'object' && groupKey in menuGroupProps
151
+ ? menuGroupProps[groupKey]
152
+ : undefined,
153
+ menuListProps: menuListProps && typeof menuListProps === 'object' && groupKey in menuListProps
154
+ ? menuListProps[groupKey]
155
+ : undefined
156
+ }));
42
157
  const buildConversations = () => {
43
158
  if (Array.isArray(conversations)) {
44
- return ((0, jsx_runtime_1.jsx)(react_core_1.MenuList, Object.assign({}, menuListProps, { children: conversations.map((conversation) => {
45
- if (isConversation(conversation)) {
46
- return (0, jsx_runtime_1.jsx)(react_1.Fragment, { children: getNavItem(conversation) }, conversation.id);
47
- }
48
- else {
49
- return conversation;
50
- }
51
- }) })));
52
- }
53
- else {
54
- return ((0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: Object.keys(conversations).map((navGroup) => ((0, jsx_runtime_1.jsx)(react_core_1.MenuGroup, Object.assign({ className: "pf-chatbot__menu-item-header", label: navGroup, labelHeadingLevel: "h3" }, menuGroupProps === null || menuGroupProps === void 0 ? void 0 : menuGroupProps[navGroup], { children: (0, jsx_runtime_1.jsx)(react_core_1.MenuList, Object.assign({}, menuListProps === null || menuListProps === void 0 ? void 0 : menuListProps[navGroup], { children: conversations[navGroup].map((conversation) => ((0, jsx_runtime_1.jsx)(react_1.Fragment, { children: getNavItem(conversation) }, conversation.id))) })) }), navGroup))) }));
159
+ if (isConversationGroupArray(conversations)) {
160
+ return (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: buildConversationMenuSegments(conversations).map(renderConversationMenuSegment) });
161
+ }
162
+ return renderConversationMenu((0, jsx_runtime_1.jsx)(react_core_1.MenuList, Object.assign({}, menuListProps, { children: renderConversationItems(conversations) })));
55
163
  }
164
+ return ((0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: buildConversationMenuSegments(normalizeObjectGroups(conversations)).map(renderConversationMenuSegment) }));
56
165
  };
57
166
  // Menu Content
58
167
  // - Consumers should pass an array to <Chatbot> of the list of conversations
@@ -67,7 +176,7 @@ const ChatbotConversationHistoryNav = (_a) => {
67
176
  if (noResultsState) {
68
177
  return (0, jsx_runtime_1.jsx)(EmptyState_1.default, Object.assign({}, noResultsState));
69
178
  }
70
- return ((0, jsx_runtime_1.jsx)(react_core_1.Menu, Object.assign({ className: "pf-chatbot__history-menu", isPlain: true, onSelect: onSelectActiveItem, activeItemId: activeItemId }, menuProps, { children: (0, jsx_runtime_1.jsx)(react_core_1.MenuContent, Object.assign({}, menuContentProps, { children: buildConversations() })) })));
179
+ return buildConversations();
71
180
  };
72
181
  const renderDrawerContent = () => ((0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: (0, jsx_runtime_1.jsx)(react_core_1.DrawerPanelBody, Object.assign({}, drawerPanelBodyProps, { children: renderMenuContent() })) }));
73
182
  const searchInputContainer = handleTextInputChange && ((0, jsx_runtime_1.jsxs)("div", { className: "pf-chatbot__input", children: [(0, jsx_runtime_1.jsx)(react_core_1.SearchInput, Object.assign({ "aria-label": searchInputAriaLabel, onChange: (_event, value) => handleTextInputChange(value), placeholder: searchInputPlaceholder }, searchInputProps)), searchInputScreenReaderText && ((0, jsx_runtime_1.jsx)("div", { className: "pf-chatbot__filter-announcement pf-chatbot-m-hidden", children: searchInputScreenReaderText }))] }));
@@ -19,6 +19,7 @@ const Chatbot_1 = require("../Chatbot/Chatbot");
19
19
  const ChatbotConversationHistoryNav_1 = __importDefault(require("./ChatbotConversationHistoryNav"));
20
20
  const react_core_1 = require("@patternfly/react-core");
21
21
  const react_icons_1 = require("@patternfly/react-icons");
22
+ const react_2 = require("react");
22
23
  const ERROR = {
23
24
  bodyText: ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: ["To try again, check your connection and reload this page. If the issue persists,", ' ', (0, jsx_runtime_1.jsx)("a", { href: "", children: "contact the support team" }), "."] })),
24
25
  buttonText: 'Reload',
@@ -274,4 +275,327 @@ describe('ChatbotConversationHistoryNav', () => {
274
275
  (0, react_1.render)((0, jsx_runtime_1.jsx)(ChatbotConversationHistoryNav_1.default, { onDrawerToggle: onDrawerToggle, isDrawerOpen: true, displayMode: Chatbot_1.ChatbotDisplayMode.fullscreen, setIsDrawerOpen: jest.fn(), conversations: initialConversations, navTitleIcon: (0, jsx_runtime_1.jsx)(react_icons_1.BellIcon, { "data-testid": "bell" }) }));
275
276
  expect(react_1.screen.getByTestId('bell')).toBeInTheDocument();
276
277
  });
278
+ it('renders static and expandable groups from ConversationGroup[]', () => {
279
+ const groups = [
280
+ {
281
+ id: 'pinned',
282
+ label: 'Pinned chats',
283
+ items: initialConversations
284
+ },
285
+ {
286
+ id: 'chats',
287
+ label: 'Chats',
288
+ expandable: {
289
+ isExpanded: true,
290
+ onToggle: jest.fn()
291
+ },
292
+ items: [{ id: '2', text: 'Chatbot extension' }]
293
+ }
294
+ ];
295
+ (0, react_1.render)((0, jsx_runtime_1.jsx)(ChatbotConversationHistoryNav_1.default, { onDrawerToggle: onDrawerToggle, isDrawerOpen: true, displayMode: Chatbot_1.ChatbotDisplayMode.fullscreen, setIsDrawerOpen: jest.fn(), conversations: groups }));
296
+ expect(react_1.screen.getByRole('heading', { name: 'Pinned chats', level: 3 })).toBeInTheDocument();
297
+ expect(react_1.screen.queryByRole('button', { name: 'Pinned chats' })).not.toBeInTheDocument();
298
+ expect(react_1.screen.getByRole('button', { name: 'Chats' })).toBeInTheDocument();
299
+ });
300
+ it('labels each MenuList with aria-labelledby referencing its visible label', () => {
301
+ const groups = [
302
+ {
303
+ id: 'pinned',
304
+ label: 'Pinned chats',
305
+ items: initialConversations
306
+ },
307
+ {
308
+ id: 'chats',
309
+ label: 'Chats',
310
+ expandable: {
311
+ isExpanded: true,
312
+ onToggle: jest.fn()
313
+ },
314
+ items: [{ id: '2', text: 'Chatbot extension' }]
315
+ }
316
+ ];
317
+ (0, react_1.render)((0, jsx_runtime_1.jsx)(ChatbotConversationHistoryNav_1.default, { onDrawerToggle: onDrawerToggle, isDrawerOpen: true, displayMode: Chatbot_1.ChatbotDisplayMode.fullscreen, setIsDrawerOpen: jest.fn(), conversations: groups }));
318
+ const pinnedHeading = react_1.screen.getByRole('heading', { name: 'Pinned chats', level: 3 });
319
+ expect(pinnedHeading).toHaveAttribute('id', 'chatbot-nav-group-pinned-label');
320
+ expect(pinnedHeading.closest('section')).toHaveAttribute('aria-labelledby', 'chatbot-nav-group-pinned-label');
321
+ expect(react_1.screen.getByRole('menu', { name: 'Pinned chats' })).toHaveAttribute('aria-labelledby', 'chatbot-nav-group-pinned-label');
322
+ expect(document.querySelector('ul.pf-v6-c-menu__list[aria-labelledby="chatbot-nav-group-pinned-label"]')).toBeTruthy();
323
+ const chatsToggle = react_1.screen.getByRole('button', { name: 'Chats' });
324
+ expect(chatsToggle).toHaveAttribute('id', 'chatbot-nav-group-chats-toggle');
325
+ expect(react_1.screen.getByRole('menu', { name: 'Chats' })).toHaveAttribute('aria-labelledby', 'chatbot-nav-group-chats-toggle');
326
+ expect(document.querySelector('ul.pf-v6-c-menu__list[aria-labelledby="chatbot-nav-group-chats-toggle"]')).toBeTruthy();
327
+ });
328
+ it('labels grouped object conversations on the menu list element', () => {
329
+ (0, react_1.render)((0, jsx_runtime_1.jsx)(ChatbotConversationHistoryNav_1.default, { onDrawerToggle: onDrawerToggle, isDrawerOpen: true, displayMode: Chatbot_1.ChatbotDisplayMode.fullscreen, setIsDrawerOpen: jest.fn(), conversations: { Today: initialConversations } }));
330
+ expect(document.querySelector('ul.pf-v6-c-menu__list')).toHaveAttribute('aria-labelledby', 'chatbot-nav-group-Today-label');
331
+ });
332
+ it('labels each menu list in a shared static menu with its group title id', () => {
333
+ const groups = [
334
+ {
335
+ id: 'pinned',
336
+ label: 'Pinned chats',
337
+ items: initialConversations
338
+ },
339
+ {
340
+ id: 'recent',
341
+ label: 'Recent chats',
342
+ items: [{ id: '2', text: 'Chatbot extension' }]
343
+ }
344
+ ];
345
+ (0, react_1.render)((0, jsx_runtime_1.jsx)(ChatbotConversationHistoryNav_1.default, { onDrawerToggle: onDrawerToggle, isDrawerOpen: true, displayMode: Chatbot_1.ChatbotDisplayMode.fullscreen, setIsDrawerOpen: jest.fn(), conversations: groups }));
346
+ expect(react_1.screen.getByRole('menu', { name: 'Pinned chats' })).toHaveAttribute('aria-labelledby', 'chatbot-nav-group-pinned-label');
347
+ expect(react_1.screen.getByRole('menu', { name: 'Recent chats' })).toHaveAttribute('aria-labelledby', 'chatbot-nav-group-recent-label');
348
+ });
349
+ it('does not autofocus the first menu item when an expandable group is expanded', () => __awaiter(void 0, void 0, void 0, function* () {
350
+ const ExpandableGroupDemo = () => {
351
+ const [isExpanded, setIsExpanded] = (0, react_2.useState)(false);
352
+ return ((0, jsx_runtime_1.jsx)(ChatbotConversationHistoryNav_1.default, { onDrawerToggle: onDrawerToggle, isDrawerOpen: true, displayMode: Chatbot_1.ChatbotDisplayMode.embedded, setIsDrawerOpen: jest.fn(), conversations: [
353
+ {
354
+ id: 'saved-prompts',
355
+ label: 'Saved prompts',
356
+ expandable: {
357
+ isExpanded,
358
+ onToggle: setIsExpanded
359
+ },
360
+ items: [
361
+ { id: '7', text: 'Summarize this document' },
362
+ { id: '8', text: 'Draft a release announcement' }
363
+ ]
364
+ }
365
+ ] }));
366
+ };
367
+ (0, react_1.render)((0, jsx_runtime_1.jsx)(ExpandableGroupDemo, {}));
368
+ react_1.fireEvent.click(react_1.screen.getByRole('button', { name: 'Saved prompts' }));
369
+ yield (0, react_1.waitFor)(() => {
370
+ expect(react_1.screen.getByRole('menuitem', { name: 'Summarize this document' })).toBeInTheDocument();
371
+ });
372
+ expect(react_1.screen.getByRole('menuitem', { name: 'Summarize this document' })).not.toHaveFocus();
373
+ }));
374
+ it('does not render a collapsed expandable group menu', () => {
375
+ const groups = [
376
+ {
377
+ id: 'chats',
378
+ label: 'Chats',
379
+ items: [
380
+ { id: '2', text: 'Chat two' },
381
+ { id: '3', text: 'Chat three' }
382
+ ]
383
+ },
384
+ {
385
+ id: 'saved-prompts',
386
+ label: 'Saved prompts',
387
+ expandable: {
388
+ isExpanded: false,
389
+ onToggle: jest.fn()
390
+ },
391
+ items: [
392
+ { id: '7', text: 'Summarize this document' },
393
+ { id: '8', text: 'Draft a release announcement' }
394
+ ]
395
+ }
396
+ ];
397
+ (0, react_1.render)((0, jsx_runtime_1.jsx)(ChatbotConversationHistoryNav_1.default, { onDrawerToggle: onDrawerToggle, isDrawerOpen: true, displayMode: Chatbot_1.ChatbotDisplayMode.embedded, setIsDrawerOpen: jest.fn(), conversations: groups }));
398
+ // Collapsed expandable groups render their toggle outside the menu and omit the menu
399
+ // entirely until expanded, so their items are not part of arrow-key navigation.
400
+ expect(react_1.screen.getAllByRole('menu')).toHaveLength(1);
401
+ expect(react_1.screen.getByRole('menu', { name: 'Chats' })).toHaveAttribute('aria-labelledby', 'chatbot-nav-group-chats-label');
402
+ expect(react_1.screen.queryByRole('menuitem', { name: 'Summarize this document' })).not.toBeInTheDocument();
403
+ expect(react_1.screen.queryByRole('menuitem', { name: 'Draft a release announcement' })).not.toBeInTheDocument();
404
+ const firstItem = react_1.screen.getByRole('menuitem', { name: 'Chat two' });
405
+ const lastItem = react_1.screen.getByRole('menuitem', { name: 'Chat three' });
406
+ lastItem.focus();
407
+ react_1.fireEvent.keyDown(lastItem, { key: 'ArrowDown' });
408
+ expect(firstItem).toHaveFocus();
409
+ });
410
+ it('collapses and expands a group when expandable.onToggle is called', () => __awaiter(void 0, void 0, void 0, function* () {
411
+ const onToggle = jest.fn();
412
+ const groups = [
413
+ {
414
+ id: 'chats',
415
+ label: 'Chats',
416
+ expandable: {
417
+ isExpanded: true,
418
+ onToggle
419
+ },
420
+ items: [{ id: '2', text: 'Chatbot extension' }]
421
+ }
422
+ ];
423
+ const { rerender } = (0, react_1.render)((0, jsx_runtime_1.jsx)(ChatbotConversationHistoryNav_1.default, { onDrawerToggle: onDrawerToggle, isDrawerOpen: true, displayMode: Chatbot_1.ChatbotDisplayMode.fullscreen, setIsDrawerOpen: jest.fn(), conversations: groups }));
424
+ expect(react_1.screen.getByRole('menuitem', { name: /Chatbot extension/i })).toBeInTheDocument();
425
+ react_1.fireEvent.click(react_1.screen.getByRole('button', { name: 'Chats' }));
426
+ expect(onToggle).toHaveBeenCalledWith(false);
427
+ rerender((0, jsx_runtime_1.jsx)(ChatbotConversationHistoryNav_1.default, { onDrawerToggle: onDrawerToggle, isDrawerOpen: true, displayMode: Chatbot_1.ChatbotDisplayMode.fullscreen, setIsDrawerOpen: jest.fn(), conversations: [
428
+ Object.assign(Object.assign({}, groups[0]), { expandable: {
429
+ isExpanded: false,
430
+ onToggle
431
+ } })
432
+ ] }));
433
+ yield (0, react_1.waitFor)(() => {
434
+ expect(react_1.screen.queryByRole('menuitem', { name: /Chatbot extension/i })).not.toBeInTheDocument();
435
+ });
436
+ }));
437
+ it('renders custom menu items and footers supplied in ConversationGroup', () => {
438
+ const groups = [
439
+ {
440
+ id: 'chats',
441
+ label: 'Chats',
442
+ items: [
443
+ initialConversations[0],
444
+ (0, jsx_runtime_1.jsx)(react_core_1.MenuItem, { itemId: "show-all", children: "Show all" }, "show-all")
445
+ ],
446
+ footer: (0, jsx_runtime_1.jsx)("div", { "data-testid": "group-footer", children: "Footer content" })
447
+ }
448
+ ];
449
+ (0, react_1.render)((0, jsx_runtime_1.jsx)(ChatbotConversationHistoryNav_1.default, { onDrawerToggle: onDrawerToggle, isDrawerOpen: true, displayMode: Chatbot_1.ChatbotDisplayMode.fullscreen, setIsDrawerOpen: jest.fn(), conversations: groups }));
450
+ expect(react_1.screen.getByRole('menuitem', { name: 'Show all' })).toBeInTheDocument();
451
+ expect(react_1.screen.getByTestId('group-footer')).toBeInTheDocument();
452
+ });
453
+ it('moves focus to the first overflow item when show all is expanded', () => __awaiter(void 0, void 0, void 0, function* () {
454
+ const recentChats = [
455
+ { id: '2', text: 'Chat two' },
456
+ { id: '3', text: 'Chat three' },
457
+ { id: '4', text: 'Chat four' },
458
+ { id: '5', text: 'Chat five' },
459
+ { id: '6', text: 'Chat six' }
460
+ ];
461
+ const VISIBLE_CHAT_COUNT = 3;
462
+ const ShowAllDemo = () => {
463
+ const [isShowingAllChats, setIsShowingAllChats] = (0, react_2.useState)(false);
464
+ return ((0, jsx_runtime_1.jsx)(ChatbotConversationHistoryNav_1.default, { onDrawerToggle: onDrawerToggle, isDrawerOpen: true, displayMode: Chatbot_1.ChatbotDisplayMode.embedded, setIsDrawerOpen: jest.fn(), conversations: [
465
+ {
466
+ id: 'chats',
467
+ label: 'Chats',
468
+ items: recentChats,
469
+ showAll: {
470
+ visibleCount: VISIBLE_CHAT_COUNT,
471
+ isExpanded: isShowingAllChats,
472
+ onToggle: setIsShowingAllChats
473
+ }
474
+ }
475
+ ] }));
476
+ };
477
+ (0, react_1.render)((0, jsx_runtime_1.jsx)(ShowAllDemo, {}));
478
+ react_1.fireEvent.click(react_1.screen.getByRole('menuitem', { name: /Show all/i }));
479
+ yield (0, react_1.waitFor)(() => {
480
+ expect(react_1.screen.getByRole('menuitem', { name: 'Chat five' })).toHaveFocus();
481
+ });
482
+ }));
483
+ it('keeps focus on the toggle when show all is collapsed', () => __awaiter(void 0, void 0, void 0, function* () {
484
+ const recentChats = [
485
+ { id: '2', text: 'Chat two' },
486
+ { id: '3', text: 'Chat three' },
487
+ { id: '4', text: 'Chat four' },
488
+ { id: '5', text: 'Chat five' }
489
+ ];
490
+ const ShowAllDemo = () => {
491
+ const [isShowingAllChats, setIsShowingAllChats] = (0, react_2.useState)(true);
492
+ return ((0, jsx_runtime_1.jsx)(ChatbotConversationHistoryNav_1.default, { onDrawerToggle: onDrawerToggle, isDrawerOpen: true, displayMode: Chatbot_1.ChatbotDisplayMode.embedded, setIsDrawerOpen: jest.fn(), conversations: [
493
+ {
494
+ id: 'chats',
495
+ label: 'Chats',
496
+ items: recentChats,
497
+ showAll: {
498
+ visibleCount: 2,
499
+ isExpanded: isShowingAllChats,
500
+ onToggle: setIsShowingAllChats
501
+ }
502
+ }
503
+ ] }));
504
+ };
505
+ (0, react_1.render)((0, jsx_runtime_1.jsx)(ShowAllDemo, {}));
506
+ react_1.fireEvent.click(react_1.screen.getByRole('menuitem', { name: 'Show less' }));
507
+ yield (0, react_1.waitFor)(() => {
508
+ expect(react_1.screen.getByRole('menuitem', { name: /Show all/i })).toHaveFocus();
509
+ });
510
+ }));
511
+ it('renders overflow items above the show all toggle so it stays in a fixed position', () => {
512
+ const recentChats = [
513
+ { id: '2', text: 'Chat two' },
514
+ { id: '3', text: 'Chat three' },
515
+ { id: '4', text: 'Chat four' }
516
+ ];
517
+ (0, react_1.render)((0, jsx_runtime_1.jsx)(ChatbotConversationHistoryNav_1.default, { onDrawerToggle: onDrawerToggle, isDrawerOpen: true, displayMode: Chatbot_1.ChatbotDisplayMode.embedded, setIsDrawerOpen: jest.fn(), conversations: [
518
+ {
519
+ id: 'chats',
520
+ label: 'Chats',
521
+ items: recentChats,
522
+ showAll: {
523
+ visibleCount: 1,
524
+ isExpanded: true,
525
+ onToggle: jest.fn()
526
+ }
527
+ }
528
+ ] }));
529
+ const overflowItem = react_1.screen.getByRole('menuitem', { name: 'Chat four' });
530
+ const toggle = react_1.screen.getByRole('menuitem', { name: 'Show less' });
531
+ const isToggleAfterOverflowItem = Boolean(
532
+ // eslint-disable-next-line no-bitwise
533
+ overflowItem.compareDocumentPosition(toggle) & Node.DOCUMENT_POSITION_FOLLOWING);
534
+ expect(isToggleAfterOverflowItem).toBe(true);
535
+ });
536
+ it('allows arrow key navigation to flow through the show all toggle like any other menu item', () => __awaiter(void 0, void 0, void 0, function* () {
537
+ const recentChats = [
538
+ { id: '2', text: 'Chat two' },
539
+ { id: '3', text: 'Chat three' },
540
+ { id: '4', text: 'Chat four' }
541
+ ];
542
+ (0, react_1.render)((0, jsx_runtime_1.jsx)(ChatbotConversationHistoryNav_1.default, { onDrawerToggle: onDrawerToggle, isDrawerOpen: true, displayMode: Chatbot_1.ChatbotDisplayMode.embedded, setIsDrawerOpen: jest.fn(), conversations: [
543
+ {
544
+ id: 'chats',
545
+ label: 'Chats',
546
+ items: recentChats,
547
+ showAll: {
548
+ visibleCount: 2,
549
+ isExpanded: false,
550
+ onToggle: jest.fn()
551
+ }
552
+ }
553
+ ] }));
554
+ const lastVisibleItem = react_1.screen.getByRole('menuitem', { name: 'Chat three' });
555
+ const toggle = react_1.screen.getByRole('menuitem', { name: /Show all/i });
556
+ lastVisibleItem.focus();
557
+ react_1.fireEvent.keyDown(lastVisibleItem, { key: 'ArrowDown' });
558
+ yield (0, react_1.waitFor)(() => {
559
+ expect(toggle).toHaveFocus();
560
+ });
561
+ react_1.fireEvent.keyDown(toggle, { key: 'ArrowUp' });
562
+ yield (0, react_1.waitFor)(() => {
563
+ expect(lastVisibleItem).toHaveFocus();
564
+ });
565
+ }));
566
+ it('preserves custom menu item identity when preceding items change', () => {
567
+ const renderShowAll = () => ((0, jsx_runtime_1.jsx)(react_core_1.MenuItem, { itemId: "show-all-chats", children: "Show all" }, "show-all-chats"));
568
+ const { rerender } = (0, react_1.render)((0, jsx_runtime_1.jsx)(ChatbotConversationHistoryNav_1.default, { onDrawerToggle: onDrawerToggle, isDrawerOpen: true, displayMode: Chatbot_1.ChatbotDisplayMode.fullscreen, setIsDrawerOpen: jest.fn(), conversations: [
569
+ {
570
+ id: 'chats',
571
+ label: 'Chats',
572
+ items: [initialConversations[0], renderShowAll()]
573
+ }
574
+ ] }));
575
+ const showAllBefore = react_1.screen.getByRole('menuitem', { name: 'Show all' });
576
+ rerender((0, jsx_runtime_1.jsx)(ChatbotConversationHistoryNav_1.default, { onDrawerToggle: onDrawerToggle, isDrawerOpen: true, displayMode: Chatbot_1.ChatbotDisplayMode.fullscreen, setIsDrawerOpen: jest.fn(), conversations: [
577
+ {
578
+ id: 'chats',
579
+ label: 'Chats',
580
+ items: [initialConversations[0], initialConversations[1], initialConversations[2], renderShowAll()]
581
+ }
582
+ ] }));
583
+ expect(react_1.screen.getByRole('menuitem', { name: 'Show all' })).toBe(showAllBefore);
584
+ });
585
+ it('passes expandableSectionProps from expandable ConversationGroup', () => {
586
+ const groups = [
587
+ {
588
+ id: 'saved',
589
+ label: 'Saved prompts',
590
+ expandable: {
591
+ isExpanded: true,
592
+ onToggle: jest.fn(),
593
+ expandableSectionProps: { className: 'test-expandable-section' }
594
+ },
595
+ items: initialConversations
596
+ }
597
+ ];
598
+ (0, react_1.render)((0, jsx_runtime_1.jsx)(ChatbotConversationHistoryNav_1.default, { onDrawerToggle: onDrawerToggle, isDrawerOpen: true, displayMode: Chatbot_1.ChatbotDisplayMode.fullscreen, setIsDrawerOpen: jest.fn(), conversations: groups }));
599
+ expect(document.querySelector('.test-expandable-section')).toBeInTheDocument();
600
+ });
277
601
  });
@@ -27,6 +27,8 @@ export interface MessageBoxProps extends HTMLProps<HTMLDivElement> {
27
27
  jumpButtonTopTooltipProps?: TooltipProps;
28
28
  /** Props passed to top jump button tooltip */
29
29
  jumpButtonBottomTooltipProps?: TooltipProps;
30
+ /** Whether scrollbar is on the page or message box in embedded mode */
31
+ hasOuterScrollbar?: boolean;
30
32
  }
31
33
  export interface MessageBoxHandle extends HTMLDivElement {
32
34
  /** Scrolls to the top of the message box */
@@ -22,6 +22,7 @@ const jsx_runtime_1 = require("react/jsx-runtime");
22
22
  const react_1 = require("react");
23
23
  const JumpButton_1 = __importDefault(require("./JumpButton"));
24
24
  const react_core_1 = require("@patternfly/react-core");
25
+ const react_styles_1 = require("@patternfly/react-styles");
25
26
  const SCROLL_EDGE_DELTA = 100;
26
27
  const getScrollMetrics = (element) => {
27
28
  const { scrollTop, scrollHeight, clientHeight } = element;
@@ -46,7 +47,7 @@ const shouldFollowContentGrowth = (metrics, enableSmartScroll, autoScroll, pause
46
47
  metrics.nearBottom ||
47
48
  (enableSmartScroll && autoScroll && !pauseAutoScroll && metrics.roundedScrollTop > 0);
48
49
  exports.MessageBox = (0, react_1.forwardRef)((_a, ref) => {
49
- var { announcement, ariaLabel = 'Scrollable message log', children, className, position = 'top', onScrollToTopClick, onScrollToBottomClick, enableSmartScroll = false, jumpButtonTopProps, jumpButtonBottomProps, jumpButtonBottomTooltipProps, jumpButtonTopTooltipProps } = _a, props = __rest(_a, ["announcement", "ariaLabel", "children", "className", "position", "onScrollToTopClick", "onScrollToBottomClick", "enableSmartScroll", "jumpButtonTopProps", "jumpButtonBottomProps", "jumpButtonBottomTooltipProps", "jumpButtonTopTooltipProps"]);
50
+ var { announcement, ariaLabel = 'Scrollable message log', children, className, position = 'top', onScrollToTopClick, onScrollToBottomClick, enableSmartScroll = false, jumpButtonTopProps, jumpButtonBottomProps, jumpButtonBottomTooltipProps, jumpButtonTopTooltipProps, hasOuterScrollbar = false } = _a, props = __rest(_a, ["announcement", "ariaLabel", "children", "className", "position", "onScrollToTopClick", "onScrollToBottomClick", "enableSmartScroll", "jumpButtonTopProps", "jumpButtonBottomProps", "jumpButtonBottomTooltipProps", "jumpButtonTopTooltipProps", "hasOuterScrollbar"]);
50
51
  const [atTop, setAtTop] = (0, react_1.useState)(true);
51
52
  const [atBottom, setAtBottom] = (0, react_1.useState)(false);
52
53
  const [isOverflowing, setIsOverflowing] = (0, react_1.useState)(false);
@@ -257,6 +258,6 @@ exports.MessageBox = (0, react_1.forwardRef)((_a, ref) => {
257
258
  onTouchMove,
258
259
  onTouchEnd
259
260
  };
260
- return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(JumpButton_1.default, { position: "top", isHidden: !hasMeasuredScroll || (isOverflowing && atTop), onClick: scrollToTop, jumpButtonProps: jumpButtonTopProps, jumpButtonTooltipProps: jumpButtonTopTooltipProps }), (0, jsx_runtime_1.jsx)(JumpButton_1.default, { position: "bottom", isHidden: !hasMeasuredScroll || (isOverflowing && atBottom), onClick: () => scrollToBottom({ resumeSmartScroll: true }), jumpButtonProps: jumpButtonBottomProps, jumpButtonTooltipProps: jumpButtonBottomTooltipProps }), (0, jsx_runtime_1.jsxs)("div", Object.assign({ role: "region", tabIndex: 0, "aria-label": ariaLabel, className: `pf-chatbot__messagebox ${position === 'bottom' ? 'pf-chatbot__messagebox--bottom' : ''} ${className !== null && className !== void 0 ? className : ''}`, ref: messageBoxRef }, props, (enableSmartScroll ? Object.assign({}, smartScrollHandlers) : {}), { children: [children, (0, jsx_runtime_1.jsx)("div", { className: "pf-chatbot__messagebox-announcement pf-chatbot-m-hidden", "aria-live": "polite", children: announcement })] }))] }));
261
+ return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(JumpButton_1.default, { position: "top", isHidden: !hasMeasuredScroll || (isOverflowing && atTop), onClick: scrollToTop, jumpButtonProps: jumpButtonTopProps, jumpButtonTooltipProps: jumpButtonTopTooltipProps }), (0, jsx_runtime_1.jsx)(JumpButton_1.default, { position: "bottom", isHidden: !hasMeasuredScroll || (isOverflowing && atBottom), onClick: () => scrollToBottom({ resumeSmartScroll: true }), jumpButtonProps: jumpButtonBottomProps, jumpButtonTooltipProps: jumpButtonBottomTooltipProps }), (0, jsx_runtime_1.jsxs)("div", Object.assign({ role: "region", tabIndex: 0, "aria-label": ariaLabel, className: (0, react_styles_1.css)('pf-chatbot__messagebox', position === 'bottom' && 'pf-chatbot__messagebox--bottom', hasOuterScrollbar && 'pf-chatbot__messagebox--outer-scroll', className), ref: messageBoxRef }, props, (enableSmartScroll ? Object.assign({}, smartScrollHandlers) : {}), { children: [children, (0, jsx_runtime_1.jsx)("div", { className: "pf-chatbot__messagebox-announcement pf-chatbot-m-hidden", "aria-live": "polite", children: announcement })] }))] }));
261
262
  });
262
263
  exports.default = exports.MessageBox;