@patternfly/chatbot 2.2.0-prerelease.10 → 2.2.0-prerelease.11

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.
Files changed (39) hide show
  1. package/dist/cjs/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.d.ts +15 -1
  2. package/dist/cjs/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.js +8 -8
  3. package/dist/cjs/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.test.js +36 -0
  4. package/dist/cjs/Compare/Compare.d.ts +17 -0
  5. package/dist/cjs/Compare/Compare.js +50 -0
  6. package/dist/cjs/Compare/Compare.test.d.ts +1 -0
  7. package/dist/cjs/Compare/Compare.test.js +20 -0
  8. package/dist/cjs/Compare/index.d.ts +2 -0
  9. package/dist/cjs/Compare/index.js +23 -0
  10. package/dist/cjs/index.d.ts +2 -0
  11. package/dist/cjs/index.js +4 -1
  12. package/dist/css/main.css +75 -0
  13. package/dist/css/main.css.map +1 -1
  14. package/dist/dynamic/Compare/package.json +1 -0
  15. package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.d.ts +15 -1
  16. package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.js +8 -8
  17. package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.test.js +36 -0
  18. package/dist/esm/Compare/Compare.d.ts +17 -0
  19. package/dist/esm/Compare/Compare.js +43 -0
  20. package/dist/esm/Compare/Compare.test.d.ts +1 -0
  21. package/dist/esm/Compare/Compare.test.js +15 -0
  22. package/dist/esm/Compare/index.d.ts +2 -0
  23. package/dist/esm/Compare/index.js +2 -0
  24. package/dist/esm/index.d.ts +2 -0
  25. package/dist/esm/index.js +2 -0
  26. package/dist/tsconfig.tsbuildinfo +1 -1
  27. package/package.json +1 -1
  28. package/patternfly-docs/content/extensions/chatbot/examples/UI/ChatbotHeaderDrawerResizable.tsx +94 -0
  29. package/patternfly-docs/content/extensions/chatbot/examples/UI/UI.md +10 -0
  30. package/patternfly-docs/content/extensions/chatbot/examples/demos/Chatbot.md +24 -1
  31. package/patternfly-docs/content/extensions/chatbot/examples/demos/EmbeddedComparisonChatbot.tsx +206 -0
  32. package/src/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.test.tsx +109 -0
  33. package/src/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.tsx +36 -7
  34. package/src/Compare/Compare.scss +72 -0
  35. package/src/Compare/Compare.test.tsx +31 -0
  36. package/src/Compare/Compare.tsx +98 -0
  37. package/src/Compare/index.ts +2 -0
  38. package/src/index.ts +3 -0
  39. package/src/main.scss +1 -0
@@ -24,6 +24,8 @@ export { default as ChatbotWelcomePrompt } from './ChatbotWelcomePrompt';
24
24
  export * from './ChatbotWelcomePrompt';
25
25
  export { default as CodeModal } from './CodeModal';
26
26
  export * from './CodeModal';
27
+ export { default as Compare } from './Compare';
28
+ export * from './Compare';
27
29
  export { default as FileDetails } from './FileDetails';
28
30
  export * from './FileDetails';
29
31
  export { default as FileDetailsLabel } from './FileDetailsLabel';
package/dist/esm/index.js CHANGED
@@ -25,6 +25,8 @@ export { default as ChatbotWelcomePrompt } from './ChatbotWelcomePrompt';
25
25
  export * from './ChatbotWelcomePrompt';
26
26
  export { default as CodeModal } from './CodeModal';
27
27
  export * from './CodeModal';
28
+ export { default as Compare } from './Compare';
29
+ export * from './Compare';
28
30
  export { default as FileDetails } from './FileDetails';
29
31
  export * from './FileDetails';
30
32
  export { default as FileDetailsLabel } from './FileDetailsLabel';
@@ -1 +1 @@
1
- {"root":["../src/index.ts","../src/AttachMenu/AttachMenu.tsx","../src/AttachMenu/index.ts","../src/AttachmentEdit/AttachmentEdit.tsx","../src/AttachmentEdit/index.ts","../src/Chatbot/Chatbot.tsx","../src/Chatbot/index.ts","../src/ChatbotAlert/ChatbotAlert.tsx","../src/ChatbotAlert/index.ts","../src/ChatbotContent/ChatbotContent.tsx","../src/ChatbotContent/index.ts","../src/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.test.tsx","../src/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.tsx","../src/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.test.tsx","../src/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.tsx","../src/ChatbotConversationHistoryNav/index.ts","../src/ChatbotFooter/ChatbotFooter.tsx","../src/ChatbotFooter/ChatbotFootnote.tsx","../src/ChatbotFooter/index.ts","../src/ChatbotHeader/ChatbotHeader.tsx","../src/ChatbotHeader/ChatbotHeaderActions.tsx","../src/ChatbotHeader/ChatbotHeaderCloseButton.tsx","../src/ChatbotHeader/ChatbotHeaderMain.tsx","../src/ChatbotHeader/ChatbotHeaderMenu.tsx","../src/ChatbotHeader/ChatbotHeaderOptionsDropdown.tsx","../src/ChatbotHeader/ChatbotHeaderSelectorDropdown.tsx","../src/ChatbotHeader/ChatbotHeaderTitle.tsx","../src/ChatbotHeader/index.ts","../src/ChatbotModal/ChatbotModal.tsx","../src/ChatbotModal/index.ts","../src/ChatbotPopover/ChatbotPopover.tsx","../src/ChatbotPopover/index.ts","../src/ChatbotToggle/ChatbotToggle.test.tsx","../src/ChatbotToggle/ChatbotToggle.tsx","../src/ChatbotToggle/index.ts","../src/ChatbotWelcomePrompt/ChatbotWelcomePrompt.test.tsx","../src/ChatbotWelcomePrompt/ChatbotWelcomePrompt.tsx","../src/ChatbotWelcomePrompt/index.ts","../src/CodeModal/CodeModal.tsx","../src/CodeModal/index.ts","../src/FileDetails/FileDetails.test.tsx","../src/FileDetails/FileDetails.tsx","../src/FileDetails/index.ts","../src/FileDetailsLabel/FileDetailsLabel.test.tsx","../src/FileDetailsLabel/FileDetailsLabel.tsx","../src/FileDetailsLabel/index.ts","../src/FileDropZone/FileDropZone.test.tsx","../src/FileDropZone/FileDropZone.tsx","../src/FileDropZone/index.ts","../src/LoadingMessage/LoadingMessage.test.tsx","../src/LoadingMessage/LoadingMessage.tsx","../src/LoadingMessage/index.ts","../src/Message/Message.test.tsx","../src/Message/Message.tsx","../src/Message/MessageLoading.tsx","../src/Message/index.ts","../src/Message/CodeBlockMessage/CodeBlockMessage.tsx","../src/Message/ListMessage/ListItemMessage.tsx","../src/Message/ListMessage/OrderedListMessage.tsx","../src/Message/ListMessage/UnorderedListMessage.tsx","../src/Message/QuickResponse/QuickResponse.tsx","../src/Message/QuickStarts/FallbackImg.tsx","../src/Message/QuickStarts/QuickStartTile.tsx","../src/Message/QuickStarts/QuickStartTileDescription.test.tsx","../src/Message/QuickStarts/QuickStartTileDescription.tsx","../src/Message/QuickStarts/QuickStartTileHeader.tsx","../src/Message/QuickStarts/monitor-sampleapp-quickstart-with-image.ts","../src/Message/QuickStarts/monitor-sampleapp-quickstart.ts","../src/Message/QuickStarts/types.ts","../src/Message/TextMessage/TextMessage.tsx","../src/MessageBar/AttachButton.test.tsx","../src/MessageBar/AttachButton.tsx","../src/MessageBar/MessageBar.test.tsx","../src/MessageBar/MessageBar.tsx","../src/MessageBar/MicrophoneButton.tsx","../src/MessageBar/SendButton.test.tsx","../src/MessageBar/SendButton.tsx","../src/MessageBar/StopButton.test.tsx","../src/MessageBar/StopButton.tsx","../src/MessageBar/index.ts","../src/MessageBox/JumpButton.test.tsx","../src/MessageBox/JumpButton.tsx","../src/MessageBox/MessageBox.tsx","../src/MessageBox/index.ts","../src/PreviewAttachment/PreviewAttachment.tsx","../src/PreviewAttachment/index.ts","../src/ResponseActions/ResponseActionButton.test.tsx","../src/ResponseActions/ResponseActionButton.tsx","../src/ResponseActions/ResponseActions.test.tsx","../src/ResponseActions/ResponseActions.tsx","../src/ResponseActions/index.ts","../src/Settings/SettingsForm.tsx","../src/Settings/index.ts","../src/SourceDetailsMenuItem/SourceDetailsMenuItem.tsx","../src/SourceDetailsMenuItem/index.ts","../src/SourcesCard/SourcesCard.test.tsx","../src/SourcesCard/SourcesCard.tsx","../src/SourcesCard/index.ts","../src/TermsOfUse/TermsOfUse.test.tsx","../src/TermsOfUse/TermsOfUse.tsx","../src/TermsOfUse/index.ts"],"version":"5.6.3"}
1
+ {"root":["../src/index.ts","../src/AttachMenu/AttachMenu.tsx","../src/AttachMenu/index.ts","../src/AttachmentEdit/AttachmentEdit.tsx","../src/AttachmentEdit/index.ts","../src/Chatbot/Chatbot.tsx","../src/Chatbot/index.ts","../src/ChatbotAlert/ChatbotAlert.tsx","../src/ChatbotAlert/index.ts","../src/ChatbotContent/ChatbotContent.tsx","../src/ChatbotContent/index.ts","../src/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.test.tsx","../src/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.tsx","../src/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.test.tsx","../src/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.tsx","../src/ChatbotConversationHistoryNav/index.ts","../src/ChatbotFooter/ChatbotFooter.tsx","../src/ChatbotFooter/ChatbotFootnote.tsx","../src/ChatbotFooter/index.ts","../src/ChatbotHeader/ChatbotHeader.tsx","../src/ChatbotHeader/ChatbotHeaderActions.tsx","../src/ChatbotHeader/ChatbotHeaderCloseButton.tsx","../src/ChatbotHeader/ChatbotHeaderMain.tsx","../src/ChatbotHeader/ChatbotHeaderMenu.tsx","../src/ChatbotHeader/ChatbotHeaderOptionsDropdown.tsx","../src/ChatbotHeader/ChatbotHeaderSelectorDropdown.tsx","../src/ChatbotHeader/ChatbotHeaderTitle.tsx","../src/ChatbotHeader/index.ts","../src/ChatbotModal/ChatbotModal.tsx","../src/ChatbotModal/index.ts","../src/ChatbotPopover/ChatbotPopover.tsx","../src/ChatbotPopover/index.ts","../src/ChatbotToggle/ChatbotToggle.test.tsx","../src/ChatbotToggle/ChatbotToggle.tsx","../src/ChatbotToggle/index.ts","../src/ChatbotWelcomePrompt/ChatbotWelcomePrompt.test.tsx","../src/ChatbotWelcomePrompt/ChatbotWelcomePrompt.tsx","../src/ChatbotWelcomePrompt/index.ts","../src/CodeModal/CodeModal.tsx","../src/CodeModal/index.ts","../src/Compare/Compare.test.tsx","../src/Compare/Compare.tsx","../src/Compare/index.ts","../src/FileDetails/FileDetails.test.tsx","../src/FileDetails/FileDetails.tsx","../src/FileDetails/index.ts","../src/FileDetailsLabel/FileDetailsLabel.test.tsx","../src/FileDetailsLabel/FileDetailsLabel.tsx","../src/FileDetailsLabel/index.ts","../src/FileDropZone/FileDropZone.test.tsx","../src/FileDropZone/FileDropZone.tsx","../src/FileDropZone/index.ts","../src/LoadingMessage/LoadingMessage.test.tsx","../src/LoadingMessage/LoadingMessage.tsx","../src/LoadingMessage/index.ts","../src/Message/Message.test.tsx","../src/Message/Message.tsx","../src/Message/MessageLoading.tsx","../src/Message/index.ts","../src/Message/CodeBlockMessage/CodeBlockMessage.tsx","../src/Message/ListMessage/ListItemMessage.tsx","../src/Message/ListMessage/OrderedListMessage.tsx","../src/Message/ListMessage/UnorderedListMessage.tsx","../src/Message/QuickResponse/QuickResponse.tsx","../src/Message/QuickStarts/FallbackImg.tsx","../src/Message/QuickStarts/QuickStartTile.tsx","../src/Message/QuickStarts/QuickStartTileDescription.test.tsx","../src/Message/QuickStarts/QuickStartTileDescription.tsx","../src/Message/QuickStarts/QuickStartTileHeader.tsx","../src/Message/QuickStarts/monitor-sampleapp-quickstart-with-image.ts","../src/Message/QuickStarts/monitor-sampleapp-quickstart.ts","../src/Message/QuickStarts/types.ts","../src/Message/TextMessage/TextMessage.tsx","../src/MessageBar/AttachButton.test.tsx","../src/MessageBar/AttachButton.tsx","../src/MessageBar/MessageBar.test.tsx","../src/MessageBar/MessageBar.tsx","../src/MessageBar/MicrophoneButton.tsx","../src/MessageBar/SendButton.test.tsx","../src/MessageBar/SendButton.tsx","../src/MessageBar/StopButton.test.tsx","../src/MessageBar/StopButton.tsx","../src/MessageBar/index.ts","../src/MessageBox/JumpButton.test.tsx","../src/MessageBox/JumpButton.tsx","../src/MessageBox/MessageBox.tsx","../src/MessageBox/index.ts","../src/PreviewAttachment/PreviewAttachment.tsx","../src/PreviewAttachment/index.ts","../src/ResponseActions/ResponseActionButton.test.tsx","../src/ResponseActions/ResponseActionButton.tsx","../src/ResponseActions/ResponseActions.test.tsx","../src/ResponseActions/ResponseActions.tsx","../src/ResponseActions/index.ts","../src/Settings/SettingsForm.tsx","../src/Settings/index.ts","../src/SourceDetailsMenuItem/SourceDetailsMenuItem.tsx","../src/SourceDetailsMenuItem/index.ts","../src/SourcesCard/SourcesCard.test.tsx","../src/SourcesCard/SourcesCard.tsx","../src/SourcesCard/index.ts","../src/TermsOfUse/TermsOfUse.test.tsx","../src/TermsOfUse/TermsOfUse.tsx","../src/TermsOfUse/index.ts"],"version":"5.6.3"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@patternfly/chatbot",
3
- "version": "2.2.0-prerelease.10",
3
+ "version": "2.2.0-prerelease.11",
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",
@@ -0,0 +1,94 @@
1
+ import React from 'react';
2
+ import { ChatbotDisplayMode } from '@patternfly/chatbot/dist/dynamic/Chatbot';
3
+ import ChatbotConversationHistoryNav, {
4
+ Conversation
5
+ } from '@patternfly/chatbot/dist/dynamic/ChatbotConversationHistoryNav';
6
+ import { Checkbox } from '@patternfly/react-core';
7
+
8
+ const initialConversations: { [key: string]: Conversation[] } = {
9
+ Today: [{ id: '1', text: 'Red Hat products and services' }],
10
+ 'This month': [
11
+ {
12
+ id: '2',
13
+ text: 'Enterprise Linux installation and setup'
14
+ },
15
+ { id: '3', text: 'Troubleshoot system crash' }
16
+ ],
17
+ March: [
18
+ { id: '4', text: 'Ansible security and updates' },
19
+ { id: '5', text: 'Red Hat certification' },
20
+ { id: '6', text: 'Lightspeed user documentation' }
21
+ ],
22
+ February: [
23
+ { id: '7', text: 'Crashing pod assistance' },
24
+ { id: '8', text: 'OpenShift AI pipelines' },
25
+ { id: '9', text: 'Updating subscription plan' },
26
+ { id: '10', text: 'Red Hat licensing options' }
27
+ ],
28
+ January: [
29
+ { id: '11', text: 'RHEL system performance' },
30
+ { id: '12', text: 'Manage user accounts' }
31
+ ]
32
+ };
33
+
34
+ export const ChatbotHeaderDrawerResizableDemo: React.FunctionComponent = () => {
35
+ const [isOpen, setIsOpen] = React.useState(true);
36
+ const [conversations, setConversations] = React.useState<Conversation[] | { [key: string]: Conversation[] }>(
37
+ initialConversations
38
+ );
39
+ const displayMode = ChatbotDisplayMode.embedded;
40
+
41
+ const findMatchingItems = (targetValue: string) => {
42
+ let filteredConversations = Object.entries(initialConversations).reduce((acc, [key, items]) => {
43
+ const filteredItems = items.filter((item) => item.text.toLowerCase().includes(targetValue.toLowerCase()));
44
+ if (filteredItems.length > 0) {
45
+ acc[key] = filteredItems;
46
+ }
47
+ return acc;
48
+ }, {});
49
+
50
+ // append message if no items are found
51
+ if (Object.keys(filteredConversations).length === 0) {
52
+ filteredConversations = [{ id: '13', noIcon: true, text: 'No results found' }];
53
+ }
54
+ return filteredConversations;
55
+ };
56
+
57
+ return (
58
+ <>
59
+ <Checkbox
60
+ label="Display drawer"
61
+ isChecked={isOpen}
62
+ onChange={() => {
63
+ setIsOpen(!isOpen);
64
+ setConversations(initialConversations);
65
+ }}
66
+ id="drawer-visible"
67
+ name="drawer-visible"
68
+ />
69
+ <ChatbotConversationHistoryNav
70
+ displayMode={displayMode}
71
+ onDrawerToggle={() => setIsOpen(!isOpen)}
72
+ isDrawerOpen={isOpen}
73
+ setIsDrawerOpen={setIsOpen}
74
+ // eslint-disable-next-line no-console
75
+ onSelectActiveItem={(e, selectedItem) => console.log(`Selected history item with id ${selectedItem}`)}
76
+ conversations={conversations}
77
+ onNewChat={() => {
78
+ setIsOpen(!isOpen);
79
+ }}
80
+ handleTextInputChange={(value: string) => {
81
+ if (value === '') {
82
+ setConversations(initialConversations);
83
+ }
84
+ // this is where you would perform search on the items in the drawer
85
+ // and update the state
86
+ const newConversations: { [key: string]: Conversation[] } = findMatchingItems(value);
87
+ setConversations(newConversations);
88
+ }}
89
+ drawerContent={<div>Drawer content</div>}
90
+ drawerPanelContentProps={{ isResizable: true, minSize: '200px' }}
91
+ />
92
+ </>
93
+ );
94
+ };
@@ -371,6 +371,16 @@ If you're showing a conversation that is already active, you can set the `active
371
371
 
372
372
  ```
373
373
 
374
+ ### Resizable drawer
375
+
376
+ By default, the conversation history drawer has a fixed width (384px) and a focus trap. To provide users with more flexibility as they navigate their conversation history, or to better support embedded ChatBots on tablet-sized devices or smaller browser windows, you can instead make the drawer resizable. By default, even resizable drawers will still open to their full width on mobile devices.
377
+
378
+ In this example, the drawer can be resized up to the max size of the parent and resized down to 200px wide. To customize this behavior further (including width, style, and focus behavior) use PatternFly [`<Drawer>` props](/components/drawer#props), [`<DrawerPanelContent>` props](/components/drawer/#drawerpanelcontent), or any other drawer subcomponents.
379
+
380
+ ```js file="./ChatbotHeaderDrawerResizable.tsx"
381
+
382
+ ```
383
+
374
384
  ### Drawer with simple menu
375
385
 
376
386
  The drawer can also be used to display a list of basic menu items.
@@ -21,7 +21,8 @@ propComponents:
21
21
  'ChatbotFootnote',
22
22
  'MessageBox',
23
23
  'Message',
24
- 'MessageBarWithAttachMenuProps'
24
+ 'MessageBarWithAttachMenuProps',
25
+ 'CompareProps'
25
26
  ]
26
27
  sortValue: 2
27
28
  ---
@@ -34,6 +35,7 @@ import ChatbotFooter, { ChatbotFootnote } from '@patternfly/chatbot/dist/dynamic
34
35
  import MessageBar from '@patternfly/chatbot/dist/dynamic/MessageBar';
35
36
  import MessageBox from '@patternfly/chatbot/dist/dynamic/MessageBox';
36
37
  import Message from '@patternfly/chatbot/dist/dynamic/Message';
38
+ import Compare from '@patternfly/chatbot/dist/dynamic/Compare';
37
39
  import ChatbotConversationHistoryNav from '@patternfly/chatbot/dist/dynamic/ChatbotConversationHistoryNav';
38
40
 
39
41
  import ChatbotHeader, {
@@ -103,3 +105,24 @@ This demo displays an embedded ChatBot. Embedded ChatBots are meant to be placed
103
105
  ```js file="./EmbeddedChatbot.tsx" isFullscreen
104
106
 
105
107
  ```
108
+
109
+ ### Comparing ChatBots
110
+
111
+ To let users compare how different ChatBots respond to the same prompt, you can add multiple ChatBots within the same window. The following demo illustrates a comparison view pattern that allows users to toggle between different conversations in a single ChatBot window.
112
+ <br /><br />
113
+ Your code structure should look like this:
114
+
115
+ ```noLive
116
+ <Page ... >
117
+ <div className="pf-chatbot__compare-container">
118
+ <Compare ... />
119
+ <ChatbotFooter ... >
120
+ <MessageBar ... />
121
+ </ChatbotFooter>
122
+ </div>
123
+ </Page>
124
+ ```
125
+
126
+ ```js file="./EmbeddedComparisonChatbot.tsx" isFullscreen
127
+
128
+ ```
@@ -0,0 +1,206 @@
1
+ import React from 'react';
2
+
3
+ import {
4
+ Page,
5
+ Masthead,
6
+ MastheadMain,
7
+ MastheadBrand,
8
+ MastheadLogo,
9
+ PageSidebarBody,
10
+ PageSidebar,
11
+ MastheadToggle,
12
+ PageToggleButton
13
+ } from '@patternfly/react-core';
14
+ import Chatbot, { ChatbotDisplayMode } from '@patternfly/chatbot/dist/dynamic/Chatbot';
15
+ import ChatbotContent from '@patternfly/chatbot/dist/dynamic/ChatbotContent';
16
+ import ChatbotWelcomePrompt from '@patternfly/chatbot/dist/dynamic/ChatbotWelcomePrompt';
17
+ import ChatbotFooter from '@patternfly/chatbot/dist/dynamic/ChatbotFooter';
18
+ import MessageBar from '@patternfly/chatbot/dist/dynamic/MessageBar';
19
+ import MessageBox from '@patternfly/chatbot/dist/dynamic/MessageBox';
20
+ import Message, { MessageProps } from '@patternfly/chatbot/dist/dynamic/Message';
21
+ import ChatbotHeader, { ChatbotHeaderMain } from '@patternfly/chatbot/dist/dynamic/ChatbotHeader';
22
+ import Compare from '@patternfly/chatbot/dist/dynamic/Compare';
23
+ import { BarsIcon } from '@patternfly/react-icons';
24
+ import userAvatar from '../Messages/user_avatar.svg';
25
+ import patternflyAvatar from '../Messages/patternfly_avatar.jpg';
26
+
27
+ export const CompareChild = ({ name, input, hasNewInput, setIsSendButtonDisabled }) => {
28
+ const [messages, setMessages] = React.useState<MessageProps[]>([]);
29
+ const [announcement, setAnnouncement] = React.useState<string>();
30
+ const scrollToBottomRef = React.useRef<HTMLDivElement>(null);
31
+ const displayMode = ChatbotDisplayMode.embedded;
32
+
33
+ // you will likely want to come up with your own unique id function; this is for demo purposes only
34
+ const generateId = () => {
35
+ const id = Date.now() + Math.random();
36
+ return id.toString();
37
+ };
38
+
39
+ const handleSend = (input: string) => {
40
+ const date = new Date();
41
+ const newMessages: MessageProps[] = [];
42
+ messages.forEach((message) => newMessages.push(message));
43
+ newMessages.push({
44
+ avatar: userAvatar,
45
+ avatarProps: { isBordered: true },
46
+ id: generateId(),
47
+ name: 'You',
48
+ role: 'user',
49
+ content: input,
50
+ timestamp: `${date?.toLocaleDateString()} ${date?.toLocaleTimeString()}`
51
+ });
52
+ newMessages.push({
53
+ avatar: patternflyAvatar,
54
+ id: generateId(),
55
+ name,
56
+ role: 'bot',
57
+ timestamp: `${date?.toLocaleDateString()} ${date?.toLocaleTimeString()}`,
58
+ isLoading: true
59
+ });
60
+ setMessages(newMessages);
61
+ // make announcement to assistive devices that new messages have been added
62
+ setAnnouncement(`Message from You: ${input}. Message from ${name} is loading.`);
63
+
64
+ // this is for demo purposes only; in a real situation, there would be an API response we would wait for
65
+ setTimeout(() => {
66
+ const loadedMessages: MessageProps[] = [];
67
+ // we can't use structuredClone since messages contains functions, but we can't mutate
68
+ // items that are going into state or the UI won't update correctly
69
+ newMessages.forEach((message) => loadedMessages.push(message));
70
+ loadedMessages.pop();
71
+ loadedMessages.push({
72
+ id: generateId(),
73
+ role: 'bot',
74
+ content: `API response from ${name} goes here`,
75
+ name,
76
+ avatar: patternflyAvatar,
77
+ isLoading: false,
78
+ actions: {
79
+ // eslint-disable-next-line no-console
80
+ positive: { onClick: () => console.log('Good response') },
81
+ // eslint-disable-next-line no-console
82
+ negative: { onClick: () => console.log('Bad response') },
83
+ // eslint-disable-next-line no-console
84
+ copy: { onClick: () => console.log('Copy') },
85
+ // eslint-disable-next-line no-console
86
+ share: { onClick: () => console.log('Share') },
87
+ // eslint-disable-next-line no-console
88
+ listen: { onClick: () => console.log('Listen') }
89
+ },
90
+ timestamp: date.toLocaleString()
91
+ });
92
+ setMessages(loadedMessages);
93
+ // make announcement to assistive devices that new message has loaded
94
+ setAnnouncement(`Message from ${name}: API response goes here`);
95
+ setIsSendButtonDisabled(false);
96
+ }, 5000);
97
+ };
98
+
99
+ React.useEffect(() => {
100
+ if (input) {
101
+ handleSend(input);
102
+ }
103
+ }, [hasNewInput]);
104
+
105
+ // Auto-scrolls to the latest message
106
+ React.useEffect(() => {
107
+ // don't scroll the first load, but scroll if there's a current stream or a new source has popped up
108
+ if (messages.length > 0) {
109
+ scrollToBottomRef.current?.scrollIntoView();
110
+ }
111
+ }, [messages]);
112
+
113
+ return (
114
+ <Chatbot displayMode={displayMode}>
115
+ <ChatbotHeader>
116
+ <ChatbotHeaderMain>{name}</ChatbotHeaderMain>
117
+ </ChatbotHeader>
118
+ <ChatbotContent>
119
+ <MessageBox ariaLabel={`Scrollable message log for ${name}`} announcement={announcement}>
120
+ <ChatbotWelcomePrompt title="Hello, Chatbot User" description="How may I help you today?" />
121
+ {messages.map((message) => (
122
+ <Message key={message.id} {...message} />
123
+ ))}
124
+ <div ref={scrollToBottomRef}></div>
125
+ </MessageBox>
126
+ </ChatbotContent>
127
+ </Chatbot>
128
+ );
129
+ };
130
+
131
+ export const EmbeddedComparisonChatbotDemo: React.FunctionComponent = () => {
132
+ const [input, setInput] = React.useState<string>();
133
+ const [hasNewInput, setHasNewInput] = React.useState(false);
134
+ const [isSidebarOpen, setIsSidebarOpen] = React.useState(false);
135
+ const [isSendButtonDisabled, setIsSendButtonDisabled] = React.useState(false);
136
+
137
+ const handleSend = (value: string) => {
138
+ setInput(value);
139
+ setHasNewInput(!hasNewInput);
140
+ setIsSendButtonDisabled(true);
141
+ };
142
+
143
+ const masthead = (
144
+ <Masthead>
145
+ <MastheadMain>
146
+ <MastheadToggle>
147
+ <PageToggleButton
148
+ variant="plain"
149
+ aria-label="Global navigation"
150
+ isSidebarOpen={isSidebarOpen}
151
+ onSidebarToggle={() => setIsSidebarOpen(!isSidebarOpen)}
152
+ id="fill-nav-toggle"
153
+ >
154
+ <BarsIcon />
155
+ </PageToggleButton>
156
+ </MastheadToggle>
157
+ <MastheadBrand>
158
+ <MastheadLogo href="https://patternfly.org" target="_blank">
159
+ Logo
160
+ </MastheadLogo>
161
+ </MastheadBrand>
162
+ </MastheadMain>
163
+ </Masthead>
164
+ );
165
+
166
+ const sidebar = (
167
+ <PageSidebar isSidebarOpen={isSidebarOpen} id="fill-sidebar">
168
+ <PageSidebarBody>Navigation</PageSidebarBody>
169
+ </PageSidebar>
170
+ );
171
+
172
+ return (
173
+ <Page masthead={masthead} sidebar={sidebar} isContentFilled>
174
+ <div className="pf-chatbot__compare-container">
175
+ <Compare
176
+ firstChild={
177
+ <CompareChild
178
+ input={input}
179
+ hasNewInput={hasNewInput}
180
+ name="ChatBot 1"
181
+ setIsSendButtonDisabled={setIsSendButtonDisabled}
182
+ />
183
+ }
184
+ secondChild={
185
+ <CompareChild
186
+ input={input}
187
+ hasNewInput={hasNewInput}
188
+ name="ChatBot 2"
189
+ setIsSendButtonDisabled={setIsSendButtonDisabled}
190
+ />
191
+ }
192
+ firstChildDisplayName="ChatBot 1"
193
+ secondChildDisplayName="ChatBot 2"
194
+ />
195
+ <ChatbotFooter>
196
+ <MessageBar
197
+ onSendMessage={handleSend}
198
+ hasAttachButton={false}
199
+ alwayShowSendButton
200
+ isSendButtonDisabled={isSendButtonDisabled}
201
+ />
202
+ </ChatbotFooter>
203
+ </div>
204
+ </Page>
205
+ );
206
+ };
@@ -123,4 +123,113 @@ describe('ChatbotConversationHistoryNav', () => {
123
123
  expect(screen.queryByText('ChatBot documentation')).not.toBeInTheDocument();
124
124
  });
125
125
  });
126
+
127
+ it('should be resizable', () => {
128
+ render(
129
+ <ChatbotConversationHistoryNav
130
+ onDrawerToggle={onDrawerToggle}
131
+ isDrawerOpen={true}
132
+ displayMode={ChatbotDisplayMode.fullscreen}
133
+ setIsDrawerOpen={jest.fn()}
134
+ conversations={initialConversations}
135
+ drawerPanelContentProps={{ isResizable: true, minSize: '200px' }}
136
+ />
137
+ );
138
+ expect(screen.getByRole('dialog', { name: /Resize/i })).toBeTruthy();
139
+ expect(screen.getByRole('separator', { name: /Resize/i })).toBeTruthy();
140
+ expect(screen.getByRole('dialog', { name: /Resize/i })).toHaveAttribute(
141
+ 'style',
142
+ '--pf-v6-c-drawer__panel--md--FlexBasis: 384px; --pf-v6-c-drawer__panel--md--FlexBasis--min: 200px;'
143
+ );
144
+ });
145
+
146
+ it('should accept drawerContentProps', () => {
147
+ const { container } = render(
148
+ <ChatbotConversationHistoryNav
149
+ onDrawerToggle={onDrawerToggle}
150
+ isDrawerOpen={true}
151
+ displayMode={ChatbotDisplayMode.fullscreen}
152
+ setIsDrawerOpen={jest.fn()}
153
+ conversations={initialConversations}
154
+ drawerContentProps={{ className: 'test' }}
155
+ />
156
+ );
157
+ const element = container.querySelector('.test');
158
+ expect(element).toBeInTheDocument();
159
+ });
160
+
161
+ it('should accept drawerContentBodyProps', () => {
162
+ const { container } = render(
163
+ <ChatbotConversationHistoryNav
164
+ onDrawerToggle={onDrawerToggle}
165
+ isDrawerOpen={true}
166
+ displayMode={ChatbotDisplayMode.fullscreen}
167
+ setIsDrawerOpen={jest.fn()}
168
+ conversations={initialConversations}
169
+ drawerContentBodyProps={{ className: 'test' }}
170
+ />
171
+ );
172
+ const element = container.querySelector('.test');
173
+ expect(element).toBeInTheDocument();
174
+ });
175
+
176
+ it('should accept drawerHeadProps', () => {
177
+ const { container } = render(
178
+ <ChatbotConversationHistoryNav
179
+ onDrawerToggle={onDrawerToggle}
180
+ isDrawerOpen={true}
181
+ displayMode={ChatbotDisplayMode.fullscreen}
182
+ setIsDrawerOpen={jest.fn()}
183
+ conversations={initialConversations}
184
+ drawerHeadProps={{ className: 'test' }}
185
+ />
186
+ );
187
+ const element = container.querySelector('.test');
188
+ expect(element).toBeInTheDocument();
189
+ });
190
+
191
+ it('should accept drawerActionsProps', () => {
192
+ const { container } = render(
193
+ <ChatbotConversationHistoryNav
194
+ onDrawerToggle={onDrawerToggle}
195
+ isDrawerOpen={true}
196
+ displayMode={ChatbotDisplayMode.fullscreen}
197
+ setIsDrawerOpen={jest.fn()}
198
+ conversations={initialConversations}
199
+ drawerActionsProps={{ className: 'test' }}
200
+ />
201
+ );
202
+ const element = container.querySelector('.test');
203
+ expect(element).toBeInTheDocument();
204
+ });
205
+
206
+ it('should accept drawerCloseButtonProps', () => {
207
+ const { container } = render(
208
+ <ChatbotConversationHistoryNav
209
+ onDrawerToggle={onDrawerToggle}
210
+ isDrawerOpen={true}
211
+ displayMode={ChatbotDisplayMode.fullscreen}
212
+ setIsDrawerOpen={jest.fn()}
213
+ conversations={initialConversations}
214
+ drawerCloseButtonProps={{ className: 'test' }}
215
+ />
216
+ );
217
+ const element = container.querySelector('.test');
218
+ expect(element).toBeInTheDocument();
219
+ });
220
+
221
+ it('should accept drawerPanelBodyProps', () => {
222
+ const { container } = render(
223
+ <ChatbotConversationHistoryNav
224
+ onDrawerToggle={onDrawerToggle}
225
+ isDrawerOpen={true}
226
+ displayMode={ChatbotDisplayMode.fullscreen}
227
+ setIsDrawerOpen={jest.fn()}
228
+ conversations={initialConversations}
229
+ drawerPanelBodyProps={{ className: 'test' }}
230
+ />
231
+ );
232
+ const element = container.querySelector('.test');
233
+ expect(element).toBeInTheDocument();
234
+ });
126
235
  });
@@ -22,7 +22,14 @@ import {
22
22
  MenuItem,
23
23
  MenuContent,
24
24
  MenuItemProps,
25
- MenuProps
25
+ MenuProps,
26
+ DrawerPanelContentProps,
27
+ DrawerContentProps,
28
+ DrawerContentBodyProps,
29
+ DrawerHeadProps,
30
+ DrawerActionsProps,
31
+ DrawerCloseButtonProps,
32
+ DrawerPanelBodyProps
26
33
  } from '@patternfly/react-core';
27
34
 
28
35
  import { OutlinedCommentAltIcon } from '@patternfly/react-icons';
@@ -82,6 +89,20 @@ export interface ChatbotConversationHistoryNavProps extends DrawerProps {
82
89
  drawerActionsTestId?: string;
83
90
  /** Additional props applied to menu */
84
91
  menuProps?: MenuProps;
92
+ /** Additional props applied to panel */
93
+ drawerPanelContentProps?: DrawerPanelContentProps;
94
+ /** Additional props applied to drawer content */
95
+ drawerContentProps?: Omit<DrawerContentProps, 'panelContent'>;
96
+ /** Additional props applied to drawer content body */
97
+ drawerContentBodyProps?: DrawerContentBodyProps;
98
+ /** Additional props applied to drawer head */
99
+ drawerHeadProps?: DrawerHeadProps;
100
+ /** Additional props applied to drawer actions */
101
+ drawerActionsProps?: DrawerActionsProps;
102
+ /** Additional props applied to drawer close button */
103
+ drawerCloseButtonProps?: DrawerCloseButtonProps;
104
+ /** Additional props appleid to drawer panel body */
105
+ drawerPanelBodyProps?: DrawerPanelBodyProps;
85
106
  }
86
107
 
87
108
  export const ChatbotConversationHistoryNav: React.FunctionComponent<ChatbotConversationHistoryNavProps> = ({
@@ -101,6 +122,13 @@ export const ChatbotConversationHistoryNav: React.FunctionComponent<ChatbotConve
101
122
  reverseButtonOrder = false,
102
123
  drawerActionsTestId = 'chatbot-nav-drawer-actions',
103
124
  menuProps,
125
+ drawerPanelContentProps,
126
+ drawerContentProps,
127
+ drawerContentBodyProps,
128
+ drawerHeadProps,
129
+ drawerActionsProps,
130
+ drawerCloseButtonProps,
131
+ drawerPanelBodyProps,
104
132
  ...props
105
133
  }: ChatbotConversationHistoryNavProps) => {
106
134
  const drawerRef = React.useRef<HTMLDivElement>(null);
@@ -173,13 +201,14 @@ export const ChatbotConversationHistoryNav: React.FunctionComponent<ChatbotConve
173
201
  );
174
202
 
175
203
  const panelContent = (
176
- <DrawerPanelContent focusTrap={{ enabled: true }} minSize="384px" maxSize="384px">
177
- <DrawerHead>
204
+ <DrawerPanelContent focusTrap={{ enabled: true }} defaultSize="384px" {...drawerPanelContentProps}>
205
+ <DrawerHead {...drawerHeadProps}>
178
206
  <DrawerActions
179
207
  data-testid={drawerActionsTestId}
180
208
  className={reverseButtonOrder ? 'pf-v6-c-drawer__actions--reversed' : ''}
209
+ {...drawerActionsProps}
181
210
  >
182
- <DrawerCloseButton onClick={onDrawerToggle} />
211
+ <DrawerCloseButton onClick={onDrawerToggle} {...drawerCloseButtonProps} />
183
212
  {onNewChat && <Button onClick={onNewChat}>{newChatButtonText}</Button>}
184
213
  </DrawerActions>
185
214
  </DrawerHead>
@@ -192,7 +221,7 @@ export const ChatbotConversationHistoryNav: React.FunctionComponent<ChatbotConve
192
221
  />
193
222
  </div>
194
223
  )}
195
- <DrawerPanelBody>{menuContent}</DrawerPanelBody>
224
+ <DrawerPanelBody {...drawerPanelBodyProps}>{menuContent}</DrawerPanelBody>
196
225
  </DrawerPanelContent>
197
226
  );
198
227
 
@@ -217,8 +246,8 @@ export const ChatbotConversationHistoryNav: React.FunctionComponent<ChatbotConve
217
246
  isInline={displayMode === ChatbotDisplayMode.fullscreen || displayMode === ChatbotDisplayMode.embedded}
218
247
  {...props}
219
248
  >
220
- <DrawerContent panelContent={panelContent}>
221
- <DrawerContentBody>
249
+ <DrawerContent panelContent={panelContent} {...drawerContentProps}>
250
+ <DrawerContentBody {...drawerContentBodyProps}>
222
251
  <>
223
252
  <div
224
253
  className={`${isDrawerOpen && (displayMode === ChatbotDisplayMode.default || displayMode === ChatbotDisplayMode.docked) ? 'pf-v6-c-backdrop pf-chatbot__drawer-backdrop' : undefined} `}