@patternfly/chatbot 6.4.0-prerelease.6 → 6.4.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.
@@ -9,6 +9,8 @@ export interface ChatbotConversationHistoryDropdownProps extends Omit<DropdownPr
9
9
  label?: string;
10
10
  /** Callback for when user selects item. */
11
11
  onSelect?: (event?: React.MouseEvent, value?: string | number) => void;
12
+ /** Id applied to dropdown menu toggle */
13
+ id?: string;
12
14
  }
13
15
  export declare const ChatbotConversationHistoryDropdown: FunctionComponent<ChatbotConversationHistoryDropdownProps>;
14
16
  export default ChatbotConversationHistoryDropdown;
@@ -9,11 +9,11 @@ const react_1 = require("react");
9
9
  // Import PatternFly components
10
10
  const react_core_1 = require("@patternfly/react-core");
11
11
  const ellipsis_v_icon_1 = __importDefault(require("@patternfly/react-icons/dist/esm/icons/ellipsis-v-icon"));
12
- const ChatbotConversationHistoryDropdown = ({ menuItems, menuClassName, onSelect, label }) => {
12
+ const ChatbotConversationHistoryDropdown = ({ menuItems, menuClassName, onSelect, label, id }) => {
13
13
  const [isOpen, setIsOpen] = (0, react_1.useState)(false);
14
14
  const toggle = (toggleRef) => ((0, jsx_runtime_1.jsx)(react_core_1.Tooltip, { className: "pf-chatbot__tooltip", content: label !== null && label !== void 0 ? label : 'Conversation options', position: "bottom",
15
15
  // prevents VO announcements of both aria label and tooltip
16
- aria: "none", children: (0, jsx_runtime_1.jsx)(react_core_1.MenuToggle, { className: "pf-chatbot__history-actions", variant: "plain", "aria-label": label !== null && label !== void 0 ? label : 'Conversation options', ref: toggleRef, isExpanded: isOpen, onClick: () => setIsOpen(!isOpen), children: (0, jsx_runtime_1.jsx)(ellipsis_v_icon_1.default, {}) }) }));
16
+ aria: "none", children: (0, jsx_runtime_1.jsx)(react_core_1.MenuToggle, { className: "pf-chatbot__history-actions", variant: "plain", "aria-label": label !== null && label !== void 0 ? label : 'Conversation options', ref: toggleRef, isExpanded: isOpen, onClick: () => setIsOpen(!isOpen), id: id, children: (0, jsx_runtime_1.jsx)(ellipsis_v_icon_1.default, {}) }) }));
17
17
  return ((0, jsx_runtime_1.jsx)(react_core_1.Dropdown, { className: `pf-chatbot__selections ${menuClassName !== null && menuClassName !== void 0 ? menuClassName : ''}`, isOpen: isOpen, onSelect: (props) => {
18
18
  onSelect === null || onSelect === void 0 ? void 0 : onSelect(props);
19
19
  setIsOpen((prev) => !prev);
@@ -1,6 +1,6 @@
1
1
  import type { FunctionComponent } from 'react';
2
2
  import { ButtonProps, DrawerProps, ListItemProps, DrawerPanelContentProps, DrawerContentProps, DrawerContentBodyProps, DrawerHeadProps, DrawerActionsProps, DrawerCloseButtonProps, DrawerPanelBodyProps, SkeletonProps, MenuProps, // Remove in next breaking change
3
- TitleProps, ListProps } from '@patternfly/react-core';
3
+ TitleProps, ListProps, SearchInputProps } from '@patternfly/react-core';
4
4
  import { ChatbotDisplayMode } from '../Chatbot/Chatbot';
5
5
  import { HistoryEmptyStateProps } from './EmptyState';
6
6
  export interface Conversation {
@@ -24,6 +24,8 @@ export interface Conversation {
24
24
  additionalProps?: ButtonProps;
25
25
  /** Additional props passed to conversation list item */
26
26
  listItemProps?: Omit<ListItemProps, 'children'>;
27
+ /** Custom dropdown ID to ensure uniqueness across demo instances */
28
+ dropdownId?: string;
27
29
  }
28
30
  export interface ChatbotConversationHistoryNavProps extends DrawerProps {
29
31
  /** Function called to toggle drawer */
@@ -57,6 +59,8 @@ export interface ChatbotConversationHistoryNavProps extends DrawerProps {
57
59
  searchInputPlaceholder?: string;
58
60
  /** Aria label for search input */
59
61
  searchInputAriaLabel?: string;
62
+ /** Additional props passed to search input */
63
+ searchInputProps?: SearchInputProps;
60
64
  /** A callback for when the input value changes. Omit to hide input field */
61
65
  handleTextInputChange?: (value: string) => void;
62
66
  /** Display mode of chatbot */
@@ -25,14 +25,14 @@ const ChatbotConversationHistoryDropdown_1 = __importDefault(require("./ChatbotC
25
25
  const LoadingState_1 = __importDefault(require("./LoadingState"));
26
26
  const EmptyState_1 = __importDefault(require("./EmptyState"));
27
27
  const ChatbotConversationHistoryNav = (_a) => {
28
- var { onDrawerToggle, isDrawerOpen, setIsDrawerOpen, activeItemId, onSelectActiveItem, conversations, titleProps, listProps, newChatButtonText = 'New chat', drawerContent, onNewChat, newChatButtonProps, searchInputPlaceholder = 'Search previous conversations...', searchInputAriaLabel = 'Filter menu items', handleTextInputChange, displayMode, reverseButtonOrder = false, drawerActionsTestId = 'chatbot-nav-drawer-actions', drawerPanelContentProps, drawerContentProps, drawerContentBodyProps, drawerHeadProps, drawerActionsProps, drawerCloseButtonProps, drawerPanelBodyProps, isLoading, loadingState, errorState, emptyState, noResultsState, isCompact, title = 'Chat history' } = _a, props = __rest(_a, ["onDrawerToggle", "isDrawerOpen", "setIsDrawerOpen", "activeItemId", "onSelectActiveItem", "conversations", "titleProps", "listProps", "newChatButtonText", "drawerContent", "onNewChat", "newChatButtonProps", "searchInputPlaceholder", "searchInputAriaLabel", "handleTextInputChange", "displayMode", "reverseButtonOrder", "drawerActionsTestId", "drawerPanelContentProps", "drawerContentProps", "drawerContentBodyProps", "drawerHeadProps", "drawerActionsProps", "drawerCloseButtonProps", "drawerPanelBodyProps", "isLoading", "loadingState", "errorState", "emptyState", "noResultsState", "isCompact", "title"]);
28
+ var { onDrawerToggle, isDrawerOpen, setIsDrawerOpen, activeItemId, onSelectActiveItem, conversations, titleProps, listProps, newChatButtonText = 'New chat', drawerContent, onNewChat, newChatButtonProps, searchInputPlaceholder = 'Search previous conversations...', searchInputAriaLabel = 'Filter menu items', searchInputProps, handleTextInputChange, displayMode, reverseButtonOrder = false, drawerActionsTestId = 'chatbot-nav-drawer-actions', drawerPanelContentProps, drawerContentProps, drawerContentBodyProps, drawerHeadProps, drawerActionsProps, drawerCloseButtonProps, drawerPanelBodyProps, isLoading, loadingState, errorState, emptyState, noResultsState, isCompact, title = 'Chat history' } = _a, props = __rest(_a, ["onDrawerToggle", "isDrawerOpen", "setIsDrawerOpen", "activeItemId", "onSelectActiveItem", "conversations", "titleProps", "listProps", "newChatButtonText", "drawerContent", "onNewChat", "newChatButtonProps", "searchInputPlaceholder", "searchInputAriaLabel", "searchInputProps", "handleTextInputChange", "displayMode", "reverseButtonOrder", "drawerActionsTestId", "drawerPanelContentProps", "drawerContentProps", "drawerContentBodyProps", "drawerHeadProps", "drawerActionsProps", "drawerCloseButtonProps", "drawerPanelBodyProps", "isLoading", "loadingState", "errorState", "emptyState", "noResultsState", "isCompact", "title"]);
29
29
  const drawerRef = (0, react_1.useRef)(null);
30
30
  const onExpand = () => {
31
31
  drawerRef.current && drawerRef.current.focus();
32
32
  };
33
33
  const getNavItem = (conversation) => {
34
34
  var _a;
35
- return ((0, jsx_runtime_1.jsx)(react_core_1.ListItem, Object.assign({ className: `pf-chatbot__conversation-list-item ${activeItemId && activeItemId === conversation.id ? 'pf-chatbot__conversation-list-item--active' : ''}` }, conversation.listItemProps, { children: (0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(react_core_1.Button, Object.assign({ className: "pf-chatbot__conversation-history-item", variant: "link" }, conversation.additionalProps, (conversation.noIcon ? {} : { icon: (_a = conversation.icon) !== null && _a !== void 0 ? _a : (0, jsx_runtime_1.jsx)(react_icons_1.OutlinedCommentAltIcon, {}) }), { onClick: (event) => onSelectActiveItem === null || onSelectActiveItem === void 0 ? void 0 : onSelectActiveItem(event, conversation.id), children: conversation.text })), conversation.menuItems && ((0, jsx_runtime_1.jsx)(ChatbotConversationHistoryDropdown_1.default, { menuClassName: conversation.menuClassName, onSelect: conversation.onSelect, menuItems: conversation.menuItems, label: conversation.label }))] }) }), conversation.id));
35
+ return ((0, jsx_runtime_1.jsx)(react_core_1.ListItem, Object.assign({ className: `pf-chatbot__conversation-list-item ${activeItemId && activeItemId === conversation.id ? 'pf-chatbot__conversation-list-item--active' : ''}` }, conversation.listItemProps, { children: (0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(react_core_1.Button, Object.assign({ className: "pf-chatbot__conversation-history-item", variant: "link" }, conversation.additionalProps, (conversation.noIcon ? {} : { icon: (_a = conversation.icon) !== null && _a !== void 0 ? _a : (0, jsx_runtime_1.jsx)(react_icons_1.OutlinedCommentAltIcon, {}) }), { onClick: (event) => onSelectActiveItem === null || onSelectActiveItem === void 0 ? void 0 : onSelectActiveItem(event, conversation.id), children: conversation.text })), conversation.menuItems && ((0, jsx_runtime_1.jsx)(ChatbotConversationHistoryDropdown_1.default, { menuClassName: conversation.menuClassName, onSelect: conversation.onSelect, menuItems: conversation.menuItems, label: conversation.label, id: conversation.dropdownId }))] }) }), conversation.id));
36
36
  };
37
37
  const buildConversations = () => {
38
38
  if (Array.isArray(conversations)) {
@@ -59,7 +59,7 @@ const ChatbotConversationHistoryNav = (_a) => {
59
59
  };
60
60
  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() })) }));
61
61
  const renderPanelContent = () => {
62
- const drawer = ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(react_core_1.DrawerHead, Object.assign({}, drawerHeadProps, { children: (0, jsx_runtime_1.jsxs)(react_core_1.DrawerActions, Object.assign({ "data-testid": drawerActionsTestId, className: reverseButtonOrder ? 'pf-v6-c-drawer__actions--reversed' : '' }, drawerActionsProps, { children: [(0, jsx_runtime_1.jsx)(react_core_1.DrawerCloseButton, Object.assign({ onClick: onDrawerToggle }, drawerCloseButtonProps)), onNewChat && ((0, jsx_runtime_1.jsx)(react_core_1.Button, Object.assign({ size: isCompact ? 'sm' : undefined, onClick: onNewChat, icon: (0, jsx_runtime_1.jsx)(react_icons_1.PenToSquareIcon, {}) }, newChatButtonProps, { children: newChatButtonText })))] })) })), (0, jsx_runtime_1.jsxs)("div", { className: "pf-chatbot__title-container", children: [(0, jsx_runtime_1.jsxs)(react_core_1.Title, { headingLevel: "h3", children: [(0, jsx_runtime_1.jsx)(react_core_1.Icon, { size: "lg", className: "pf-chatbot__title-icon", children: (0, jsx_runtime_1.jsx)(react_icons_1.OutlinedClockIcon, {}) }), title] }), !isLoading && handleTextInputChange && ((0, jsx_runtime_1.jsx)("div", { className: "pf-chatbot__input", children: (0, jsx_runtime_1.jsx)(react_core_1.SearchInput, { "aria-label": searchInputAriaLabel, onChange: (_event, value) => handleTextInputChange(value), placeholder: searchInputPlaceholder }) }))] }), isLoading ? (0, jsx_runtime_1.jsx)(LoadingState_1.default, Object.assign({}, loadingState)) : renderDrawerContent()] }));
62
+ const drawer = ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(react_core_1.DrawerHead, Object.assign({}, drawerHeadProps, { children: (0, jsx_runtime_1.jsxs)(react_core_1.DrawerActions, Object.assign({ "data-testid": drawerActionsTestId, className: reverseButtonOrder ? 'pf-v6-c-drawer__actions--reversed' : '' }, drawerActionsProps, { children: [(0, jsx_runtime_1.jsx)(react_core_1.DrawerCloseButton, Object.assign({ onClick: onDrawerToggle }, drawerCloseButtonProps)), onNewChat && ((0, jsx_runtime_1.jsx)(react_core_1.Button, Object.assign({ size: isCompact ? 'sm' : undefined, onClick: onNewChat, icon: (0, jsx_runtime_1.jsx)(react_icons_1.PenToSquareIcon, {}) }, newChatButtonProps, { children: newChatButtonText })))] })) })), (0, jsx_runtime_1.jsxs)("div", { className: "pf-chatbot__title-container", children: [(0, jsx_runtime_1.jsxs)(react_core_1.Title, { headingLevel: "h3", children: [(0, jsx_runtime_1.jsx)(react_core_1.Icon, { size: "lg", className: "pf-chatbot__title-icon", children: (0, jsx_runtime_1.jsx)(react_icons_1.OutlinedClockIcon, {}) }), title] }), !isLoading && handleTextInputChange && ((0, jsx_runtime_1.jsx)("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)) }))] }), isLoading ? (0, jsx_runtime_1.jsx)(LoadingState_1.default, Object.assign({}, loadingState)) : renderDrawerContent()] }));
63
63
  return ((0, jsx_runtime_1.jsx)(react_core_1.DrawerPanelContent, Object.assign({ "aria-live": "polite", focusTrap: { enabled: true }, defaultSize: "384px" }, drawerPanelContentProps, { children: drawer })));
64
64
  };
65
65
  // An onKeyDown property must be passed to the Drawer component to handle closing
@@ -219,4 +219,8 @@ describe('ChatbotConversationHistoryNav', () => {
219
219
  (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: [{ id: '1', text: 'ChatBot documentation', listItemProps: { className: 'test' } }] }));
220
220
  expect(react_1.screen.getByRole('listitem')).toHaveClass('test');
221
221
  });
222
+ it('should be able to spread search input props when searchInputProps is passed', () => {
223
+ (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, handleTextInputChange: jest.fn(), searchInputProps: { value: 'I am a sample search' } }));
224
+ expect(react_1.screen.getByRole('dialog', { name: /Chat history I am a sample search/i })).toBeInTheDocument();
225
+ });
222
226
  });
@@ -9,6 +9,8 @@ export interface ChatbotConversationHistoryDropdownProps extends Omit<DropdownPr
9
9
  label?: string;
10
10
  /** Callback for when user selects item. */
11
11
  onSelect?: (event?: React.MouseEvent, value?: string | number) => void;
12
+ /** Id applied to dropdown menu toggle */
13
+ id?: string;
12
14
  }
13
15
  export declare const ChatbotConversationHistoryDropdown: FunctionComponent<ChatbotConversationHistoryDropdownProps>;
14
16
  export default ChatbotConversationHistoryDropdown;
@@ -3,11 +3,11 @@ import { useState } from 'react';
3
3
  // Import PatternFly components
4
4
  import { Tooltip, MenuToggle, Dropdown } from '@patternfly/react-core';
5
5
  import EllipsisIcon from '@patternfly/react-icons/dist/esm/icons/ellipsis-v-icon';
6
- export const ChatbotConversationHistoryDropdown = ({ menuItems, menuClassName, onSelect, label }) => {
6
+ export const ChatbotConversationHistoryDropdown = ({ menuItems, menuClassName, onSelect, label, id }) => {
7
7
  const [isOpen, setIsOpen] = useState(false);
8
8
  const toggle = (toggleRef) => (_jsx(Tooltip, { className: "pf-chatbot__tooltip", content: label !== null && label !== void 0 ? label : 'Conversation options', position: "bottom",
9
9
  // prevents VO announcements of both aria label and tooltip
10
- aria: "none", children: _jsx(MenuToggle, { className: "pf-chatbot__history-actions", variant: "plain", "aria-label": label !== null && label !== void 0 ? label : 'Conversation options', ref: toggleRef, isExpanded: isOpen, onClick: () => setIsOpen(!isOpen), children: _jsx(EllipsisIcon, {}) }) }));
10
+ aria: "none", children: _jsx(MenuToggle, { className: "pf-chatbot__history-actions", variant: "plain", "aria-label": label !== null && label !== void 0 ? label : 'Conversation options', ref: toggleRef, isExpanded: isOpen, onClick: () => setIsOpen(!isOpen), id: id, children: _jsx(EllipsisIcon, {}) }) }));
11
11
  return (_jsx(Dropdown, { className: `pf-chatbot__selections ${menuClassName !== null && menuClassName !== void 0 ? menuClassName : ''}`, isOpen: isOpen, onSelect: (props) => {
12
12
  onSelect === null || onSelect === void 0 ? void 0 : onSelect(props);
13
13
  setIsOpen((prev) => !prev);
@@ -1,6 +1,6 @@
1
1
  import type { FunctionComponent } from 'react';
2
2
  import { ButtonProps, DrawerProps, ListItemProps, DrawerPanelContentProps, DrawerContentProps, DrawerContentBodyProps, DrawerHeadProps, DrawerActionsProps, DrawerCloseButtonProps, DrawerPanelBodyProps, SkeletonProps, MenuProps, // Remove in next breaking change
3
- TitleProps, ListProps } from '@patternfly/react-core';
3
+ TitleProps, ListProps, SearchInputProps } from '@patternfly/react-core';
4
4
  import { ChatbotDisplayMode } from '../Chatbot/Chatbot';
5
5
  import { HistoryEmptyStateProps } from './EmptyState';
6
6
  export interface Conversation {
@@ -24,6 +24,8 @@ export interface Conversation {
24
24
  additionalProps?: ButtonProps;
25
25
  /** Additional props passed to conversation list item */
26
26
  listItemProps?: Omit<ListItemProps, 'children'>;
27
+ /** Custom dropdown ID to ensure uniqueness across demo instances */
28
+ dropdownId?: string;
27
29
  }
28
30
  export interface ChatbotConversationHistoryNavProps extends DrawerProps {
29
31
  /** Function called to toggle drawer */
@@ -57,6 +59,8 @@ export interface ChatbotConversationHistoryNavProps extends DrawerProps {
57
59
  searchInputPlaceholder?: string;
58
60
  /** Aria label for search input */
59
61
  searchInputAriaLabel?: string;
62
+ /** Additional props passed to search input */
63
+ searchInputProps?: SearchInputProps;
60
64
  /** A callback for when the input value changes. Omit to hide input field */
61
65
  handleTextInputChange?: (value: string) => void;
62
66
  /** Display mode of chatbot */
@@ -19,14 +19,14 @@ import ConversationHistoryDropdown from './ChatbotConversationHistoryDropdown';
19
19
  import LoadingState from './LoadingState';
20
20
  import HistoryEmptyState from './EmptyState';
21
21
  export const ChatbotConversationHistoryNav = (_a) => {
22
- var { onDrawerToggle, isDrawerOpen, setIsDrawerOpen, activeItemId, onSelectActiveItem, conversations, titleProps, listProps, newChatButtonText = 'New chat', drawerContent, onNewChat, newChatButtonProps, searchInputPlaceholder = 'Search previous conversations...', searchInputAriaLabel = 'Filter menu items', handleTextInputChange, displayMode, reverseButtonOrder = false, drawerActionsTestId = 'chatbot-nav-drawer-actions', drawerPanelContentProps, drawerContentProps, drawerContentBodyProps, drawerHeadProps, drawerActionsProps, drawerCloseButtonProps, drawerPanelBodyProps, isLoading, loadingState, errorState, emptyState, noResultsState, isCompact, title = 'Chat history' } = _a, props = __rest(_a, ["onDrawerToggle", "isDrawerOpen", "setIsDrawerOpen", "activeItemId", "onSelectActiveItem", "conversations", "titleProps", "listProps", "newChatButtonText", "drawerContent", "onNewChat", "newChatButtonProps", "searchInputPlaceholder", "searchInputAriaLabel", "handleTextInputChange", "displayMode", "reverseButtonOrder", "drawerActionsTestId", "drawerPanelContentProps", "drawerContentProps", "drawerContentBodyProps", "drawerHeadProps", "drawerActionsProps", "drawerCloseButtonProps", "drawerPanelBodyProps", "isLoading", "loadingState", "errorState", "emptyState", "noResultsState", "isCompact", "title"]);
22
+ var { onDrawerToggle, isDrawerOpen, setIsDrawerOpen, activeItemId, onSelectActiveItem, conversations, titleProps, listProps, newChatButtonText = 'New chat', drawerContent, onNewChat, newChatButtonProps, searchInputPlaceholder = 'Search previous conversations...', searchInputAriaLabel = 'Filter menu items', searchInputProps, handleTextInputChange, displayMode, reverseButtonOrder = false, drawerActionsTestId = 'chatbot-nav-drawer-actions', drawerPanelContentProps, drawerContentProps, drawerContentBodyProps, drawerHeadProps, drawerActionsProps, drawerCloseButtonProps, drawerPanelBodyProps, isLoading, loadingState, errorState, emptyState, noResultsState, isCompact, title = 'Chat history' } = _a, props = __rest(_a, ["onDrawerToggle", "isDrawerOpen", "setIsDrawerOpen", "activeItemId", "onSelectActiveItem", "conversations", "titleProps", "listProps", "newChatButtonText", "drawerContent", "onNewChat", "newChatButtonProps", "searchInputPlaceholder", "searchInputAriaLabel", "searchInputProps", "handleTextInputChange", "displayMode", "reverseButtonOrder", "drawerActionsTestId", "drawerPanelContentProps", "drawerContentProps", "drawerContentBodyProps", "drawerHeadProps", "drawerActionsProps", "drawerCloseButtonProps", "drawerPanelBodyProps", "isLoading", "loadingState", "errorState", "emptyState", "noResultsState", "isCompact", "title"]);
23
23
  const drawerRef = useRef(null);
24
24
  const onExpand = () => {
25
25
  drawerRef.current && drawerRef.current.focus();
26
26
  };
27
27
  const getNavItem = (conversation) => {
28
28
  var _a;
29
- return (_jsx(ListItem, Object.assign({ className: `pf-chatbot__conversation-list-item ${activeItemId && activeItemId === conversation.id ? 'pf-chatbot__conversation-list-item--active' : ''}` }, conversation.listItemProps, { children: _jsxs(_Fragment, { children: [_jsx(Button, Object.assign({ className: "pf-chatbot__conversation-history-item", variant: "link" }, conversation.additionalProps, (conversation.noIcon ? {} : { icon: (_a = conversation.icon) !== null && _a !== void 0 ? _a : _jsx(OutlinedCommentAltIcon, {}) }), { onClick: (event) => onSelectActiveItem === null || onSelectActiveItem === void 0 ? void 0 : onSelectActiveItem(event, conversation.id), children: conversation.text })), conversation.menuItems && (_jsx(ConversationHistoryDropdown, { menuClassName: conversation.menuClassName, onSelect: conversation.onSelect, menuItems: conversation.menuItems, label: conversation.label }))] }) }), conversation.id));
29
+ return (_jsx(ListItem, Object.assign({ className: `pf-chatbot__conversation-list-item ${activeItemId && activeItemId === conversation.id ? 'pf-chatbot__conversation-list-item--active' : ''}` }, conversation.listItemProps, { children: _jsxs(_Fragment, { children: [_jsx(Button, Object.assign({ className: "pf-chatbot__conversation-history-item", variant: "link" }, conversation.additionalProps, (conversation.noIcon ? {} : { icon: (_a = conversation.icon) !== null && _a !== void 0 ? _a : _jsx(OutlinedCommentAltIcon, {}) }), { onClick: (event) => onSelectActiveItem === null || onSelectActiveItem === void 0 ? void 0 : onSelectActiveItem(event, conversation.id), children: conversation.text })), conversation.menuItems && (_jsx(ConversationHistoryDropdown, { menuClassName: conversation.menuClassName, onSelect: conversation.onSelect, menuItems: conversation.menuItems, label: conversation.label, id: conversation.dropdownId }))] }) }), conversation.id));
30
30
  };
31
31
  const buildConversations = () => {
32
32
  if (Array.isArray(conversations)) {
@@ -53,7 +53,7 @@ export const ChatbotConversationHistoryNav = (_a) => {
53
53
  };
54
54
  const renderDrawerContent = () => (_jsx(_Fragment, { children: _jsx(DrawerPanelBody, Object.assign({}, drawerPanelBodyProps, { children: renderMenuContent() })) }));
55
55
  const renderPanelContent = () => {
56
- const drawer = (_jsxs(_Fragment, { children: [_jsx(DrawerHead, Object.assign({}, drawerHeadProps, { children: _jsxs(DrawerActions, Object.assign({ "data-testid": drawerActionsTestId, className: reverseButtonOrder ? 'pf-v6-c-drawer__actions--reversed' : '' }, drawerActionsProps, { children: [_jsx(DrawerCloseButton, Object.assign({ onClick: onDrawerToggle }, drawerCloseButtonProps)), onNewChat && (_jsx(Button, Object.assign({ size: isCompact ? 'sm' : undefined, onClick: onNewChat, icon: _jsx(PenToSquareIcon, {}) }, newChatButtonProps, { children: newChatButtonText })))] })) })), _jsxs("div", { className: "pf-chatbot__title-container", children: [_jsxs(Title, { headingLevel: "h3", children: [_jsx(Icon, { size: "lg", className: "pf-chatbot__title-icon", children: _jsx(OutlinedClockIcon, {}) }), title] }), !isLoading && handleTextInputChange && (_jsx("div", { className: "pf-chatbot__input", children: _jsx(SearchInput, { "aria-label": searchInputAriaLabel, onChange: (_event, value) => handleTextInputChange(value), placeholder: searchInputPlaceholder }) }))] }), isLoading ? _jsx(LoadingState, Object.assign({}, loadingState)) : renderDrawerContent()] }));
56
+ const drawer = (_jsxs(_Fragment, { children: [_jsx(DrawerHead, Object.assign({}, drawerHeadProps, { children: _jsxs(DrawerActions, Object.assign({ "data-testid": drawerActionsTestId, className: reverseButtonOrder ? 'pf-v6-c-drawer__actions--reversed' : '' }, drawerActionsProps, { children: [_jsx(DrawerCloseButton, Object.assign({ onClick: onDrawerToggle }, drawerCloseButtonProps)), onNewChat && (_jsx(Button, Object.assign({ size: isCompact ? 'sm' : undefined, onClick: onNewChat, icon: _jsx(PenToSquareIcon, {}) }, newChatButtonProps, { children: newChatButtonText })))] })) })), _jsxs("div", { className: "pf-chatbot__title-container", children: [_jsxs(Title, { headingLevel: "h3", children: [_jsx(Icon, { size: "lg", className: "pf-chatbot__title-icon", children: _jsx(OutlinedClockIcon, {}) }), title] }), !isLoading && handleTextInputChange && (_jsx("div", { className: "pf-chatbot__input", children: _jsx(SearchInput, Object.assign({ "aria-label": searchInputAriaLabel, onChange: (_event, value) => handleTextInputChange(value), placeholder: searchInputPlaceholder }, searchInputProps)) }))] }), isLoading ? _jsx(LoadingState, Object.assign({}, loadingState)) : renderDrawerContent()] }));
57
57
  return (_jsx(DrawerPanelContent, Object.assign({ "aria-live": "polite", focusTrap: { enabled: true }, defaultSize: "384px" }, drawerPanelContentProps, { children: drawer })));
58
58
  };
59
59
  // An onKeyDown property must be passed to the Drawer component to handle closing
@@ -214,4 +214,8 @@ describe('ChatbotConversationHistoryNav', () => {
214
214
  render(_jsx(ChatbotConversationHistoryNav, { onDrawerToggle: onDrawerToggle, isDrawerOpen: true, displayMode: ChatbotDisplayMode.fullscreen, setIsDrawerOpen: jest.fn(), conversations: [{ id: '1', text: 'ChatBot documentation', listItemProps: { className: 'test' } }] }));
215
215
  expect(screen.getByRole('listitem')).toHaveClass('test');
216
216
  });
217
+ it('should be able to spread search input props when searchInputProps is passed', () => {
218
+ render(_jsx(ChatbotConversationHistoryNav, { onDrawerToggle: onDrawerToggle, isDrawerOpen: true, displayMode: ChatbotDisplayMode.fullscreen, setIsDrawerOpen: jest.fn(), conversations: initialConversations, handleTextInputChange: jest.fn(), searchInputProps: { value: 'I am a sample search' } }));
219
+ expect(screen.getByRole('dialog', { name: /Chat history I am a sample search/i })).toBeInTheDocument();
220
+ });
217
221
  });
package/package.json CHANGED
@@ -1,16 +1,18 @@
1
1
  {
2
2
  "name": "@patternfly/chatbot",
3
- "version": "6.4.0-prerelease.6",
3
+ "version": "6.4.0-prerelease.8",
4
4
  "description": "This library provides React components based on PatternFly 6 that can be used to build chatbots.",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",
7
7
  "scripts": {
8
- "build": "sass src/main.scss dist/css/main.css && npm run build:index && npm run build:js && npm run build:esm && npm run build:fed:packages",
8
+ "build": "npm run build:css && npm run build:index && npm run build:js && npm run build:esm && npm run build:fed:packages",
9
9
  "build:watch": "npm run build:js && npm run build:esm -- --watch && npm run build:fed:packages -- --watch",
10
10
  "build:esm": "tsc --build --verbose ./tsconfig.json",
11
11
  "build:fed:packages": "node generate-fed-package-json.js",
12
12
  "build:js": "tsc -p tsconfig.cjs.json",
13
13
  "build:index": "node generate-index.js",
14
+ "build:css": "sass src/main.scss dist/css/main.css",
15
+ "build:css:watch": "sass src/main.scss dist/css/main.css --watch",
14
16
  "clean": "rimraf dist",
15
17
  "docs:develop": "pf-docs-framework start",
16
18
  "docs:build": "pf-docs-framework build all --output public",
@@ -97,6 +97,14 @@ export const ChatbotHeaderPinDemo: FunctionComponent = () => {
97
97
  }
98
98
  return newPinned;
99
99
  });
100
+
101
+ // Focus the conversation input after pin/unpin action
102
+ setTimeout(() => {
103
+ const dropdown = document.getElementById(`pin-demo-${conversationId}-dropdown`);
104
+ if (dropdown) {
105
+ dropdown.focus();
106
+ }
107
+ }, 100);
100
108
  };
101
109
 
102
110
  const createMenuItems = (conversationId: string) => {
@@ -136,7 +144,8 @@ export const ChatbotHeaderPinDemo: FunctionComponent = () => {
136
144
  pinnedItems.push({
137
145
  ...conv,
138
146
  menuItems: createMenuItems(conv.id),
139
- icon: <ThumbtackIcon />
147
+ icon: <ThumbtackIcon />,
148
+ dropdownId: `pin-demo-${conv.id}-dropdown`
140
149
  });
141
150
  }
142
151
  });
@@ -153,7 +162,8 @@ export const ChatbotHeaderPinDemo: FunctionComponent = () => {
153
162
  .filter((conv) => !pinnedConversations.has(conv.id))
154
163
  .map((conv) => ({
155
164
  ...conv,
156
- menuItems: createMenuItems(conv.id)
165
+ menuItems: createMenuItems(conv.id),
166
+ dropdownId: `pin-demo-${conv.id}-dropdown`
157
167
  }));
158
168
 
159
169
  if (unpinnedConversations.length > 0) {
@@ -19,13 +19,16 @@ export interface ChatbotConversationHistoryDropdownProps extends Omit<DropdownPr
19
19
  label?: string;
20
20
  /** Callback for when user selects item. */
21
21
  onSelect?: (event?: React.MouseEvent, value?: string | number) => void;
22
+ /** Id applied to dropdown menu toggle */
23
+ id?: string;
22
24
  }
23
25
 
24
26
  export const ChatbotConversationHistoryDropdown: FunctionComponent<ChatbotConversationHistoryDropdownProps> = ({
25
27
  menuItems,
26
28
  menuClassName,
27
29
  onSelect,
28
- label
30
+ label,
31
+ id
29
32
  }: ChatbotConversationHistoryDropdownProps) => {
30
33
  const [isOpen, setIsOpen] = useState(false);
31
34
 
@@ -44,6 +47,7 @@ export const ChatbotConversationHistoryDropdown: FunctionComponent<ChatbotConver
44
47
  ref={toggleRef}
45
48
  isExpanded={isOpen}
46
49
  onClick={() => setIsOpen(!isOpen)}
50
+ id={id}
47
51
  >
48
52
  <EllipsisIcon />
49
53
  </MenuToggle>
@@ -561,4 +561,20 @@ describe('ChatbotConversationHistoryNav', () => {
561
561
  );
562
562
  expect(screen.getByRole('listitem')).toHaveClass('test');
563
563
  });
564
+
565
+ it('should be able to spread search input props when searchInputProps is passed', () => {
566
+ render(
567
+ <ChatbotConversationHistoryNav
568
+ onDrawerToggle={onDrawerToggle}
569
+ isDrawerOpen={true}
570
+ displayMode={ChatbotDisplayMode.fullscreen}
571
+ setIsDrawerOpen={jest.fn()}
572
+ conversations={initialConversations}
573
+ handleTextInputChange={jest.fn()}
574
+ searchInputProps={{ value: 'I am a sample search' }}
575
+ />
576
+ );
577
+
578
+ expect(screen.getByRole('dialog', { name: /Chat history I am a sample search/i })).toBeInTheDocument();
579
+ });
564
580
  });
@@ -34,7 +34,8 @@ import {
34
34
  Icon,
35
35
  MenuProps, // Remove in next breaking change
36
36
  TitleProps,
37
- ListProps
37
+ ListProps,
38
+ SearchInputProps
38
39
  } from '@patternfly/react-core';
39
40
 
40
41
  import { OutlinedClockIcon, OutlinedCommentAltIcon, PenToSquareIcon } from '@patternfly/react-icons';
@@ -64,6 +65,8 @@ export interface Conversation {
64
65
  additionalProps?: ButtonProps;
65
66
  /** Additional props passed to conversation list item */
66
67
  listItemProps?: Omit<ListItemProps, 'children'>;
68
+ /** Custom dropdown ID to ensure uniqueness across demo instances */
69
+ dropdownId?: string;
67
70
  }
68
71
  export interface ChatbotConversationHistoryNavProps extends DrawerProps {
69
72
  /** Function called to toggle drawer */
@@ -94,6 +97,8 @@ export interface ChatbotConversationHistoryNavProps extends DrawerProps {
94
97
  searchInputPlaceholder?: string;
95
98
  /** Aria label for search input */
96
99
  searchInputAriaLabel?: string;
100
+ /** Additional props passed to search input */
101
+ searchInputProps?: SearchInputProps;
97
102
  /** A callback for when the input value changes. Omit to hide input field */
98
103
  handleTextInputChange?: (value: string) => void;
99
104
  /** Display mode of chatbot */
@@ -149,6 +154,7 @@ export const ChatbotConversationHistoryNav: FunctionComponent<ChatbotConversatio
149
154
  newChatButtonProps,
150
155
  searchInputPlaceholder = 'Search previous conversations...',
151
156
  searchInputAriaLabel = 'Filter menu items',
157
+ searchInputProps,
152
158
  handleTextInputChange,
153
159
  displayMode,
154
160
  reverseButtonOrder = false,
@@ -198,6 +204,7 @@ export const ChatbotConversationHistoryNav: FunctionComponent<ChatbotConversatio
198
204
  onSelect={conversation.onSelect}
199
205
  menuItems={conversation.menuItems}
200
206
  label={conversation.label}
207
+ id={conversation.dropdownId}
201
208
  />
202
209
  )}
203
210
  </>
@@ -292,6 +299,7 @@ export const ChatbotConversationHistoryNav: FunctionComponent<ChatbotConversatio
292
299
  aria-label={searchInputAriaLabel}
293
300
  onChange={(_event, value) => handleTextInputChange(value)}
294
301
  placeholder={searchInputPlaceholder}
302
+ {...searchInputProps}
295
303
  />
296
304
  </div>
297
305
  )}