@patternfly/chatbot 6.3.1 → 6.4.0-prerelease.10

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 (135) hide show
  1. package/dist/cjs/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.d.ts +2 -0
  2. package/dist/cjs/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.js +2 -2
  3. package/dist/cjs/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.test.js +6 -6
  4. package/dist/cjs/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.d.ts +27 -4
  5. package/dist/cjs/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.js +8 -14
  6. package/dist/cjs/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.test.js +53 -2
  7. package/dist/cjs/ChatbotHeader/ChatbotHeaderMenu.js +1 -1
  8. package/dist/cjs/ChatbotHeader/ChatbotHeaderMenu.test.js +1 -1
  9. package/dist/cjs/ChatbotHeader/ChatbotHeaderNewChatButton.d.ts +18 -0
  10. package/dist/cjs/ChatbotHeader/ChatbotHeaderNewChatButton.js +25 -0
  11. package/dist/cjs/ChatbotHeader/ChatbotHeaderNewChatButton.test.d.ts +1 -0
  12. package/dist/cjs/ChatbotHeader/ChatbotHeaderNewChatButton.test.js +22 -0
  13. package/dist/cjs/ChatbotHeader/index.d.ts +1 -0
  14. package/dist/cjs/ChatbotHeader/index.js +1 -0
  15. package/dist/cjs/FileDropZone/FileDropZone.d.ts +1 -2
  16. package/dist/cjs/Message/Message.d.ts +9 -2
  17. package/dist/cjs/Message/Message.js +40 -34
  18. package/dist/cjs/Message/Message.test.js +37 -0
  19. package/dist/cjs/Message/MessageInput.d.ts +3 -1
  20. package/dist/cjs/Message/MessageInput.js +2 -2
  21. package/dist/cjs/MessageBar/AttachButton.d.ts +2 -2
  22. package/dist/cjs/MessageBar/MessageBar.d.ts +2 -2
  23. package/dist/cjs/MessageBar/MessageBar.js +19 -4
  24. package/dist/cjs/MessageBox/JumpButton.d.ts +5 -0
  25. package/dist/cjs/MessageBox/JumpButton.js +1 -1
  26. package/dist/cjs/MessageBox/JumpButton.test.js +4 -4
  27. package/dist/cjs/MessageBox/MessageBox.d.ts +9 -0
  28. package/dist/cjs/MessageBox/MessageBox.js +2 -2
  29. package/dist/cjs/MessageBox/MessageBox.test.js +2 -2
  30. package/dist/cjs/MessageDivider/MessageDivider.d.ts +9 -0
  31. package/dist/cjs/MessageDivider/MessageDivider.js +23 -0
  32. package/dist/cjs/MessageDivider/MessageDivider.test.d.ts +1 -0
  33. package/dist/cjs/MessageDivider/MessageDivider.test.js +29 -0
  34. package/dist/cjs/MessageDivider/index.d.ts +2 -0
  35. package/dist/cjs/MessageDivider/index.js +23 -0
  36. package/dist/cjs/ResponseActions/ResponseActions.d.ts +1 -0
  37. package/dist/cjs/ResponseActions/ResponseActions.js +4 -4
  38. package/dist/cjs/ResponseActions/ResponseActions.test.js +6 -1
  39. package/dist/cjs/index.d.ts +2 -0
  40. package/dist/cjs/index.js +4 -1
  41. package/dist/css/main.css +103 -81
  42. package/dist/css/main.css.map +1 -1
  43. package/dist/dynamic/MessageDivider/package.json +1 -0
  44. package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.d.ts +2 -0
  45. package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.js +2 -2
  46. package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.test.js +6 -6
  47. package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.d.ts +27 -4
  48. package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.js +10 -16
  49. package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.test.js +54 -3
  50. package/dist/esm/ChatbotHeader/ChatbotHeaderMenu.js +1 -1
  51. package/dist/esm/ChatbotHeader/ChatbotHeaderMenu.test.js +1 -1
  52. package/dist/esm/ChatbotHeader/ChatbotHeaderNewChatButton.d.ts +18 -0
  53. package/dist/esm/ChatbotHeader/ChatbotHeaderNewChatButton.js +22 -0
  54. package/dist/esm/ChatbotHeader/ChatbotHeaderNewChatButton.test.d.ts +1 -0
  55. package/dist/esm/ChatbotHeader/ChatbotHeaderNewChatButton.test.js +20 -0
  56. package/dist/esm/ChatbotHeader/index.d.ts +1 -0
  57. package/dist/esm/ChatbotHeader/index.js +1 -0
  58. package/dist/esm/FileDropZone/FileDropZone.d.ts +1 -2
  59. package/dist/esm/Message/Message.d.ts +9 -2
  60. package/dist/esm/Message/Message.js +40 -34
  61. package/dist/esm/Message/Message.test.js +37 -0
  62. package/dist/esm/Message/MessageInput.d.ts +3 -1
  63. package/dist/esm/Message/MessageInput.js +2 -2
  64. package/dist/esm/MessageBar/AttachButton.d.ts +2 -2
  65. package/dist/esm/MessageBar/MessageBar.d.ts +2 -2
  66. package/dist/esm/MessageBar/MessageBar.js +19 -4
  67. package/dist/esm/MessageBox/JumpButton.d.ts +5 -0
  68. package/dist/esm/MessageBox/JumpButton.js +1 -1
  69. package/dist/esm/MessageBox/JumpButton.test.js +4 -4
  70. package/dist/esm/MessageBox/MessageBox.d.ts +9 -0
  71. package/dist/esm/MessageBox/MessageBox.js +2 -2
  72. package/dist/esm/MessageBox/MessageBox.test.js +2 -2
  73. package/dist/esm/MessageDivider/MessageDivider.d.ts +9 -0
  74. package/dist/esm/MessageDivider/MessageDivider.js +21 -0
  75. package/dist/esm/MessageDivider/MessageDivider.test.d.ts +1 -0
  76. package/dist/esm/MessageDivider/MessageDivider.test.js +24 -0
  77. package/dist/esm/MessageDivider/index.d.ts +2 -0
  78. package/dist/esm/MessageDivider/index.js +2 -0
  79. package/dist/esm/ResponseActions/ResponseActions.d.ts +1 -0
  80. package/dist/esm/ResponseActions/ResponseActions.js +5 -5
  81. package/dist/esm/ResponseActions/ResponseActions.test.js +6 -1
  82. package/dist/esm/index.d.ts +2 -0
  83. package/dist/esm/index.js +2 -0
  84. package/dist/tsconfig.tsbuildinfo +1 -1
  85. package/package.json +9 -4
  86. package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithDividers.tsx +24 -0
  87. package/patternfly-docs/content/extensions/chatbot/examples/Messages/Messages.md +15 -1
  88. package/patternfly-docs/content/extensions/chatbot/examples/Messages/UserMessage.tsx +39 -7
  89. package/patternfly-docs/content/extensions/chatbot/examples/UI/ChatbotConversationEditing.tsx +202 -0
  90. package/patternfly-docs/content/extensions/chatbot/examples/UI/ChatbotHeaderBasic.tsx +17 -3
  91. package/patternfly-docs/content/extensions/chatbot/examples/UI/ChatbotHeaderDrawer.tsx +45 -5
  92. package/patternfly-docs/content/extensions/chatbot/examples/UI/ChatbotHeaderDrawerWithPin.tsx +206 -0
  93. package/patternfly-docs/content/extensions/chatbot/examples/UI/UI.md +30 -4
  94. package/patternfly-docs/content/extensions/chatbot/examples/demos/Chatbot.md +33 -1
  95. package/patternfly-docs/content/extensions/chatbot/examples/demos/ChatbotDisplayMode.tsx +486 -0
  96. package/patternfly-docs/content/extensions/chatbot/examples/demos/ChatbotTranscripts.tsx +565 -0
  97. package/src/Chatbot/Chatbot.scss +1 -1
  98. package/src/ChatbotContent/ChatbotContent.scss +1 -1
  99. package/src/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.test.tsx +6 -6
  100. package/src/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.tsx +5 -2
  101. package/src/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.scss +70 -32
  102. package/src/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.test.tsx +176 -3
  103. package/src/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.tsx +110 -60
  104. package/src/ChatbotFooter/ChatbotFooter.scss +1 -1
  105. package/src/ChatbotHeader/ChatbotHeader.scss +3 -3
  106. package/src/ChatbotHeader/ChatbotHeaderMenu.test.tsx +1 -1
  107. package/src/ChatbotHeader/ChatbotHeaderMenu.tsx +2 -2
  108. package/src/ChatbotHeader/ChatbotHeaderNewChatButton.test.tsx +25 -0
  109. package/src/ChatbotHeader/ChatbotHeaderNewChatButton.tsx +64 -0
  110. package/src/ChatbotHeader/index.ts +1 -0
  111. package/src/ChatbotModal/ChatbotModal.scss +1 -1
  112. package/src/ChatbotToggle/ChatbotToggle.scss +2 -2
  113. package/src/FileDetails/__snapshots__/FileDetails.test.tsx.snap +6 -9
  114. package/src/FileDetailsLabel/__snapshots__/FileDetailsLabel.test.tsx.snap +6 -9
  115. package/src/FileDropZone/FileDropZone.tsx +2 -2
  116. package/src/Message/Message.scss +9 -7
  117. package/src/Message/Message.test.tsx +54 -0
  118. package/src/Message/Message.tsx +70 -50
  119. package/src/Message/MessageInput.tsx +5 -1
  120. package/src/MessageBar/AttachButton.tsx +2 -2
  121. package/src/MessageBar/MessageBar.tsx +25 -5
  122. package/src/MessageBar/SendButton.scss +3 -3
  123. package/src/MessageBox/JumpButton.scss +1 -1
  124. package/src/MessageBox/JumpButton.test.tsx +4 -4
  125. package/src/MessageBox/JumpButton.tsx +20 -4
  126. package/src/MessageBox/MessageBox.test.tsx +2 -2
  127. package/src/MessageBox/MessageBox.tsx +23 -2
  128. package/src/MessageDivider/MessageDivider.scss +45 -0
  129. package/src/MessageDivider/MessageDivider.test.tsx +24 -0
  130. package/src/MessageDivider/MessageDivider.tsx +35 -0
  131. package/src/MessageDivider/index.ts +3 -0
  132. package/src/ResponseActions/ResponseActions.test.tsx +6 -1
  133. package/src/ResponseActions/ResponseActions.tsx +24 -3
  134. package/src/index.ts +3 -0
  135. package/src/main.scss +1 -52
@@ -44,7 +44,8 @@ ChatbotHeaderMenu,
44
44
  ChatbotHeaderTitle,
45
45
  ChatbotHeaderActions,
46
46
  ChatbotHeaderSelectorDropdown,
47
- ChatbotHeaderOptionsDropdown
47
+ ChatbotHeaderOptionsDropdown,
48
+ ChatbotHeaderCloseButton,
48
49
  } from '@patternfly/chatbot/dist/dynamic/ChatbotHeader';
49
50
 
50
51
  import ExpandIcon from '@patternfly/react-icons/dist/esm/icons/expand-icon';
@@ -59,6 +60,7 @@ import userAvatar from '../Messages/user_avatar.svg';
59
60
  import patternflyAvatar from '../Messages/patternfly_avatar.jpg';
60
61
  import { getTrackingProviders } from "@patternfly/chatbot/dist/dynamic/tracking";
61
62
  import { useEffect,useCallback, useRef, useState, FunctionComponent, MouseEvent } from 'react';
63
+ import saveAs from 'file-saver';
62
64
 
63
65
  ### Basic ChatBot
64
66
 
@@ -129,6 +131,22 @@ This demo displays a ChatBot in a static, inline drawer. This demo includes:
129
131
 
130
132
  ```
131
133
 
134
+ ### Display mode switcher
135
+
136
+ This demo showcases how the ChatBot can be rendered in different display modes to suit various application layouts. It demonstrates how to dynamically change the page structure in response to the user's selection. This demo includes:
137
+
138
+ 1. The ability to switch between overlay, drawer, and fullscreen modes using the [`<ChatbotHeaderOptionsDropdown>`](/patternfly-ai/chatbot/ui#header-options) in the header.
139
+ 2. A conditional page layout that renders the ChatBot for each display mode option:
140
+ - **Overlay:** As a floating window on top of the page content.
141
+ - **Drawer:** Inside an inline PatternFly `<Drawer>` as a side panel.
142
+ - **Fullscreen:** As a top-level component that covers the entire screen for an embedded experience.
143
+ 3. Logic to show or hide the `<ChatbotToggle>` button, which is only present in the default overlay mode.
144
+ 4. A [basic ChatBot](#basic-chatbot) with a header, welcome prompt, and message bar to populate the different layouts.
145
+
146
+ ```js file="./ChatbotDisplayMode.tsx" isFullscreen
147
+
148
+ ```
149
+
132
150
  ### Comparing ChatBots
133
151
 
134
152
  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.
@@ -149,3 +167,17 @@ Your code structure should look like this:
149
167
  ```js file="./EmbeddedComparisonChatbot.tsx" isFullscreen
150
168
 
151
169
  ```
170
+
171
+ ### Chat transcripts
172
+
173
+ This demo illustrates how you could add downloadable transcripts to your ChatBot, which outline conversation details in a Markdown file. This approach allows users to easily share information from a conversation with others.
174
+
175
+ A message transcript includes details from a single chat message. To download a sample message transcript in this demo, click the "Download" action under a bot message.
176
+
177
+ A conversation transcript includes details from the entirety of a ChatBot conversation. To download a sample conversation transcript in this demo, open the chat history menu and click "Download" in the options menu for the conversation.
178
+
179
+ In this example, file download is implemented with [file-saver](https://www.npmjs.com/package/file-saver).
180
+
181
+ ```js file="./ChatbotTranscripts.tsx" isFullscreen
182
+
183
+ ```
@@ -0,0 +1,486 @@
1
+ import React, { FunctionComponent, MouseEvent, useEffect, useRef, useState } from 'react';
2
+
3
+ import {
4
+ Brand,
5
+ DropdownList,
6
+ DropdownItem,
7
+ Page,
8
+ Masthead,
9
+ MastheadMain,
10
+ MastheadBrand,
11
+ MastheadLogo,
12
+ PageSidebarBody,
13
+ PageSidebar,
14
+ MastheadToggle,
15
+ PageToggleButton,
16
+ SkipToContent,
17
+ Drawer,
18
+ DrawerContent,
19
+ DrawerContentBody,
20
+ DrawerPanelContent,
21
+ DropdownGroup
22
+ } from '@patternfly/react-core';
23
+
24
+ import ChatbotToggle from '@patternfly/chatbot/dist/dynamic/ChatbotToggle';
25
+ import Chatbot, { ChatbotDisplayMode } from '@patternfly/chatbot/dist/dynamic/Chatbot';
26
+ import ChatbotContent from '@patternfly/chatbot/dist/dynamic/ChatbotContent';
27
+ import ChatbotWelcomePrompt from '@patternfly/chatbot/dist/dynamic/ChatbotWelcomePrompt';
28
+ import ChatbotFooter, { ChatbotFootnote } from '@patternfly/chatbot/dist/dynamic/ChatbotFooter';
29
+ import MessageBar from '@patternfly/chatbot/dist/dynamic/MessageBar';
30
+ import MessageBox from '@patternfly/chatbot/dist/dynamic/MessageBox';
31
+ import Message, { MessageProps } from '@patternfly/chatbot/dist/dynamic/Message';
32
+ import ChatbotConversationHistoryNav, {
33
+ Conversation
34
+ } from '@patternfly/chatbot/dist/dynamic/ChatbotConversationHistoryNav';
35
+ import ChatbotHeader, {
36
+ ChatbotHeaderMenu,
37
+ ChatbotHeaderMain,
38
+ ChatbotHeaderTitle,
39
+ ChatbotHeaderActions,
40
+ ChatbotHeaderSelectorDropdown,
41
+ ChatbotHeaderOptionsDropdown,
42
+ ChatbotHeaderCloseButton
43
+ } from '@patternfly/chatbot/dist/dynamic/ChatbotHeader';
44
+ import PFIconLogoColor from '../UI/PF-IconLogo-Color.svg';
45
+ import PFIconLogoReverse from '../UI/PF-IconLogo-Reverse.svg';
46
+ import { BarsIcon } from '@patternfly/react-icons';
47
+ import ExpandIcon from '@patternfly/react-icons/dist/esm/icons/expand-icon';
48
+ import OpenDrawerRightIcon from '@patternfly/react-icons/dist/esm/icons/open-drawer-right-icon';
49
+ import OutlinedWindowRestoreIcon from '@patternfly/react-icons/dist/esm/icons/outlined-window-restore-icon';
50
+ import userAvatar from '../Messages/user_avatar.svg';
51
+ import patternflyAvatar from '../Messages/patternfly_avatar.jpg';
52
+ import '@patternfly/react-core/dist/styles/base.css';
53
+ import '@patternfly/chatbot/dist/css/main.css';
54
+
55
+ const footnoteProps = {
56
+ label: 'ChatBot uses AI. Check for mistakes.',
57
+ popover: {
58
+ title: 'Verify accuracy',
59
+ description: `While ChatBot strives for accuracy, there's always a possibility of errors. It's a good practice to verify critical information from reliable sources, especially if it's crucial for decision-making or actions.`,
60
+ bannerImage: {
61
+ src: 'https://cdn.dribbble.com/userupload/10651749/file/original-8a07b8e39d9e8bf002358c66fce1223e.gif',
62
+ alt: 'Example image for footnote popover'
63
+ },
64
+ cta: {
65
+ label: 'Got it',
66
+ onClick: () => {
67
+ alert('Do something!');
68
+ }
69
+ },
70
+ link: {
71
+ label: 'Learn more',
72
+ url: 'https://www.redhat.com/'
73
+ }
74
+ }
75
+ };
76
+
77
+ const markdown = `A paragraph with *emphasis* and **strong importance**.`;
78
+
79
+ const date = new Date();
80
+
81
+ const initialMessages: MessageProps[] = [
82
+ {
83
+ id: '1',
84
+ role: 'user',
85
+ content: 'Hello, can you give me an example of what you can do?',
86
+ name: 'User',
87
+ avatar: userAvatar,
88
+ timestamp: date.toLocaleString(),
89
+ avatarProps: { isBordered: true }
90
+ },
91
+ {
92
+ id: '2',
93
+ role: 'bot',
94
+ content: markdown,
95
+ name: 'Bot',
96
+ avatar: patternflyAvatar,
97
+ timestamp: date.toLocaleString(),
98
+ actions: {
99
+ // eslint-disable-next-line no-console
100
+ positive: { onClick: () => console.log('Good response') },
101
+ // eslint-disable-next-line no-console
102
+ negative: { onClick: () => console.log('Bad response') },
103
+ // eslint-disable-next-line no-console
104
+ copy: { onClick: () => console.log('Copy') },
105
+ // eslint-disable-next-line no-console
106
+ download: { onClick: () => console.log('Download') },
107
+ // eslint-disable-next-line no-console
108
+ listen: { onClick: () => console.log('Listen') }
109
+ }
110
+ }
111
+ ];
112
+
113
+ const welcomePrompts = [
114
+ {
115
+ title: 'Topic 1',
116
+ message: 'Helpful prompt for Topic 1'
117
+ },
118
+ {
119
+ title: 'Topic 2',
120
+ message: 'Helpful prompt for Topic 2'
121
+ }
122
+ ];
123
+
124
+ const initialConversations = {
125
+ Today: [{ id: '1', text: 'Hello, can you give me an example of what you can do?' }],
126
+ 'This month': [
127
+ {
128
+ id: '2',
129
+ text: 'Enterprise Linux installation and setup'
130
+ }
131
+ ]
132
+ };
133
+
134
+ export const ChatbotDisplayModeDemo: FunctionComponent = () => {
135
+ const [messages, setMessages] = useState<MessageProps[]>(initialMessages);
136
+ const [selectedModel, setSelectedModel] = useState('Granite 7B');
137
+ const [isSendButtonDisabled, setIsSendButtonDisabled] = useState(false);
138
+ const [isDrawerOpen, setIsDrawerOpen] = useState(false);
139
+ const [conversations, setConversations] = useState<Conversation[] | { [key: string]: Conversation[] }>(
140
+ initialConversations
141
+ );
142
+ const [isSidebarOpen, setIsSidebarOpen] = useState(false);
143
+ const [announcement, setAnnouncement] = useState<string>();
144
+ const scrollToBottomRef = useRef<HTMLDivElement>(null);
145
+ const historyRef = useRef<HTMLButtonElement>(null);
146
+ const drawerRef = useRef<HTMLDivElement>();
147
+ const [chatbotVisible, setChatbotVisible] = useState<boolean>(true);
148
+ const toggleRef = useRef<HTMLButtonElement>(null);
149
+ const chatbotRef = useRef<HTMLDivElement>(null);
150
+ const [shouldFocusOptions, setShouldFocusOptions] = useState(false);
151
+
152
+ const [displayMode, setDisplayMode] = useState<ChatbotDisplayMode>(ChatbotDisplayMode.default);
153
+
154
+ const isDrawerPanelExpanded = displayMode === ChatbotDisplayMode.drawer;
155
+
156
+ const onExpand = () => {
157
+ drawerRef.current && drawerRef.current.focus();
158
+ };
159
+
160
+ useEffect(() => {
161
+ if (messages.length > 2) {
162
+ scrollToBottomRef.current?.scrollIntoView({ behavior: 'smooth' });
163
+ }
164
+ }, [messages]);
165
+
166
+ // Focus options dropdown after display mode changes
167
+ useEffect(() => {
168
+ if (shouldFocusOptions) {
169
+ // Use a more reliable timeout to ensure DOM has updated
170
+ const timeoutId = setTimeout(() => {
171
+ // Find the options dropdown button via CSS selector since ref forwarding isn't supported
172
+ const optionsButton = document.querySelector('.pf-chatbot__button--toggle-options');
173
+ if (optionsButton instanceof HTMLElement) {
174
+ optionsButton.focus();
175
+ }
176
+ setShouldFocusOptions(false);
177
+ }, 100);
178
+
179
+ return () => clearTimeout(timeoutId);
180
+ }
181
+ }, [displayMode, shouldFocusOptions]);
182
+
183
+ const onSelectModel = (_event: MouseEvent<Element, MouseEvent> | undefined, value: string | number | undefined) => {
184
+ setSelectedModel(value as string);
185
+ };
186
+
187
+ const onSelectDisplayMode = (
188
+ _event: MouseEvent<Element, MouseEvent> | undefined,
189
+ value: string | number | undefined
190
+ ) => {
191
+ setDisplayMode(value as ChatbotDisplayMode);
192
+ // Flag to focus options dropdown after render
193
+ setShouldFocusOptions(true);
194
+ };
195
+
196
+ const generateId = () => {
197
+ const id = Date.now() + Math.random();
198
+ return id.toString();
199
+ };
200
+
201
+ const handleSend = (message: string) => {
202
+ setIsSendButtonDisabled(true);
203
+ const newMessages: MessageProps[] = [...messages];
204
+ const date = new Date();
205
+ newMessages.push({
206
+ id: generateId(),
207
+ role: 'user',
208
+ content: message,
209
+ name: 'User',
210
+ avatar: userAvatar,
211
+ timestamp: date.toLocaleString(),
212
+ avatarProps: { isBordered: true }
213
+ });
214
+ newMessages.push({
215
+ id: generateId(),
216
+ role: 'bot',
217
+ content: 'API response goes here',
218
+ name: 'Bot',
219
+ avatar: patternflyAvatar,
220
+ isLoading: true,
221
+ timestamp: date.toLocaleString()
222
+ });
223
+ setMessages(newMessages);
224
+ setAnnouncement(`Message from User: ${message}. Message from Bot is loading.`);
225
+
226
+ setTimeout(() => {
227
+ const loadedMessages: MessageProps[] = [...newMessages];
228
+ loadedMessages.pop();
229
+ loadedMessages.push({
230
+ id: generateId(),
231
+ role: 'bot',
232
+ content: 'API response goes here',
233
+ name: 'Bot',
234
+ avatar: patternflyAvatar,
235
+ isLoading: false,
236
+ actions: {
237
+ // eslint-disable-next-line no-console
238
+ positive: { onClick: () => console.log('Good response') },
239
+ // eslint-disable-next-line no-console
240
+ negative: { onClick: () => console.log('Bad response') },
241
+ // eslint-disable-next-line no-console
242
+ copy: { onClick: () => console.log('Copy') },
243
+ // eslint-disable-next-line no-console
244
+ download: { onClick: () => console.log('Download') },
245
+ // eslint-disable-next-line no-console
246
+ listen: { onClick: () => console.log('Listen') }
247
+ },
248
+ timestamp: date.toLocaleString()
249
+ });
250
+ setMessages(loadedMessages);
251
+ setAnnouncement(`Message from Bot: API response goes here`);
252
+ setIsSendButtonDisabled(false);
253
+ }, 2000);
254
+ };
255
+
256
+ const findMatchingItems = (targetValue: string) => {
257
+ let filteredConversations = Object.entries(initialConversations).reduce((acc, [key, items]) => {
258
+ const filteredItems = items.filter((item) => item.text.toLowerCase().includes(targetValue.toLowerCase()));
259
+ if (filteredItems.length > 0) {
260
+ acc[key] = filteredItems;
261
+ }
262
+ return acc;
263
+ }, {});
264
+
265
+ if (Object.keys(filteredConversations).length === 0) {
266
+ filteredConversations = [{ id: '13', noIcon: true, text: 'No results found' }];
267
+ }
268
+ return filteredConversations;
269
+ };
270
+
271
+ const iconLogo = (
272
+ <>
273
+ <Brand className="show-light" src={PFIconLogoColor} alt="PatternFly" />
274
+ <Brand className="show-dark" src={PFIconLogoReverse} alt="PatternFly" />
275
+ </>
276
+ );
277
+ const masthead = (
278
+ <Masthead>
279
+ <MastheadMain>
280
+ <MastheadToggle>
281
+ <PageToggleButton
282
+ variant="plain"
283
+ aria-label="Global navigation"
284
+ isSidebarOpen={isSidebarOpen}
285
+ onSidebarToggle={() => setIsSidebarOpen(!isSidebarOpen)}
286
+ id="fill-nav-toggle"
287
+ >
288
+ <BarsIcon />
289
+ </PageToggleButton>
290
+ </MastheadToggle>
291
+ <MastheadBrand>
292
+ <MastheadLogo href="https://patternfly.org" target="_blank">
293
+ Logo
294
+ </MastheadLogo>
295
+ </MastheadBrand>
296
+ </MastheadMain>
297
+ </Masthead>
298
+ );
299
+
300
+ const sidebar = (
301
+ <PageSidebar isSidebarOpen={isSidebarOpen} id="fill-sidebar">
302
+ <PageSidebarBody>Navigation</PageSidebarBody>
303
+ </PageSidebar>
304
+ );
305
+
306
+ const skipToChatbot = (e: MouseEvent) => {
307
+ e.preventDefault();
308
+ /* eslint-disable indent */
309
+ switch (displayMode) {
310
+ case ChatbotDisplayMode.default:
311
+ if (!chatbotVisible && toggleRef.current) {
312
+ toggleRef.current.focus();
313
+ }
314
+ if (chatbotVisible && chatbotRef.current) {
315
+ chatbotRef.current.focus();
316
+ }
317
+ break;
318
+ default:
319
+ if (historyRef.current) {
320
+ historyRef.current.focus();
321
+ }
322
+ break;
323
+ }
324
+ /* eslint-enable indent */
325
+ };
326
+
327
+ const skipToContent = (
328
+ <SkipToContent href="#" onClick={skipToChatbot}>
329
+ Skip to chatbot
330
+ </SkipToContent>
331
+ );
332
+
333
+ const handleChatbotVisibilityDrawer = () => {
334
+ setChatbotVisible(!chatbotVisible);
335
+ setDisplayMode(ChatbotDisplayMode.default);
336
+ };
337
+
338
+ const chatbotComponent = (
339
+ <Chatbot isVisible={chatbotVisible} displayMode={displayMode} ref={chatbotRef}>
340
+ <ChatbotConversationHistoryNav
341
+ displayMode={displayMode}
342
+ onDrawerToggle={() => {
343
+ setIsDrawerOpen(!isDrawerOpen);
344
+ setConversations(initialConversations);
345
+ }}
346
+ isDrawerOpen={isDrawerOpen}
347
+ setIsDrawerOpen={setIsDrawerOpen}
348
+ activeItemId="1"
349
+ conversations={conversations}
350
+ onNewChat={() => {
351
+ setIsDrawerOpen(!isDrawerOpen);
352
+ setMessages([]);
353
+ setConversations(initialConversations);
354
+ }}
355
+ handleTextInputChange={(value: string) => {
356
+ if (value === '') {
357
+ setConversations(initialConversations);
358
+ }
359
+ const newConversations: { [key: string]: Conversation[] } = findMatchingItems(value);
360
+ setConversations(newConversations);
361
+ }}
362
+ drawerContent={
363
+ <>
364
+ <ChatbotHeader>
365
+ <ChatbotHeaderMain>
366
+ <ChatbotHeaderMenu
367
+ ref={historyRef}
368
+ aria-expanded={isDrawerOpen}
369
+ onMenuToggle={() => setIsDrawerOpen(!isDrawerOpen)}
370
+ />
371
+ <ChatbotHeaderTitle>{iconLogo}</ChatbotHeaderTitle>
372
+ </ChatbotHeaderMain>
373
+ <ChatbotHeaderActions>
374
+ <ChatbotHeaderSelectorDropdown value={selectedModel} onSelect={onSelectModel}>
375
+ <DropdownList>
376
+ <DropdownItem value="Granite 7B" key="granite">
377
+ Granite 7B
378
+ </DropdownItem>
379
+ <DropdownItem value="Llama 3.0" key="llama">
380
+ Llama 3.0
381
+ </DropdownItem>
382
+ </DropdownList>
383
+ </ChatbotHeaderSelectorDropdown>
384
+ <ChatbotHeaderOptionsDropdown onSelect={onSelectDisplayMode}>
385
+ <DropdownGroup label="Display mode">
386
+ <DropdownList>
387
+ <DropdownItem
388
+ value={ChatbotDisplayMode.default}
389
+ key="switchDisplayOverlay"
390
+ icon={<OutlinedWindowRestoreIcon aria-hidden />}
391
+ isSelected={displayMode === ChatbotDisplayMode.default}
392
+ >
393
+ <span>Overlay</span>
394
+ </DropdownItem>
395
+ <DropdownItem
396
+ value={ChatbotDisplayMode.drawer}
397
+ key="switchDisplayDock"
398
+ icon={<OpenDrawerRightIcon aria-hidden />}
399
+ isSelected={displayMode === ChatbotDisplayMode.drawer}
400
+ >
401
+ <span>Drawer</span>
402
+ </DropdownItem>
403
+ <DropdownItem
404
+ value={ChatbotDisplayMode.fullscreen}
405
+ key="switchDisplayFullscreen"
406
+ icon={<ExpandIcon aria-hidden />}
407
+ isSelected={displayMode === ChatbotDisplayMode.fullscreen}
408
+ >
409
+ <span>Fullscreen</span>
410
+ </DropdownItem>
411
+ </DropdownList>
412
+ </DropdownGroup>
413
+ </ChatbotHeaderOptionsDropdown>
414
+ {(displayMode === ChatbotDisplayMode.drawer || displayMode === ChatbotDisplayMode.fullscreen) && (
415
+ <ChatbotHeaderCloseButton onClick={handleChatbotVisibilityDrawer} />
416
+ )}
417
+ </ChatbotHeaderActions>
418
+ </ChatbotHeader>
419
+ <ChatbotContent>
420
+ <MessageBox announcement={announcement}>
421
+ <ChatbotWelcomePrompt
422
+ title="Hello, Chatbot User"
423
+ description="How may I help you today?"
424
+ prompts={welcomePrompts}
425
+ />
426
+ {messages.map((message, index) => {
427
+ if (index === messages.length - 1) {
428
+ return (
429
+ <React.Fragment key={message.id}>
430
+ <div ref={scrollToBottomRef}></div>
431
+ <Message {...message} />
432
+ </React.Fragment>
433
+ );
434
+ }
435
+ return <Message key={message.id} {...message} />;
436
+ })}
437
+ </MessageBox>
438
+ </ChatbotContent>
439
+ <ChatbotFooter>
440
+ <MessageBar onSendMessage={handleSend} hasMicrophoneButton isSendButtonDisabled={isSendButtonDisabled} />
441
+ <ChatbotFootnote {...footnoteProps} />
442
+ </ChatbotFooter>
443
+ </>
444
+ }
445
+ />
446
+ </Chatbot>
447
+ );
448
+
449
+ const chatbotToggle =
450
+ displayMode === ChatbotDisplayMode.default ? (
451
+ <ChatbotToggle
452
+ tooltipLabel="Chatbot"
453
+ isChatbotVisible={chatbotVisible}
454
+ onToggleChatbot={() => {
455
+ setChatbotVisible(!chatbotVisible);
456
+ }}
457
+ id="chatbot-toggle"
458
+ ref={toggleRef}
459
+ />
460
+ ) : null;
461
+
462
+ const panelContent = <DrawerPanelContent>{chatbotComponent}</DrawerPanelContent>;
463
+
464
+ const pageContent = (
465
+ <Page skipToContent={skipToContent} masthead={masthead} sidebar={sidebar} isContentFilled>
466
+ {displayMode === ChatbotDisplayMode.default && chatbotToggle}
467
+ </Page>
468
+ );
469
+
470
+ if (displayMode === ChatbotDisplayMode.drawer) {
471
+ return (
472
+ <Drawer isExpanded={isDrawerPanelExpanded} isInline onExpand={onExpand}>
473
+ <DrawerContent panelContent={panelContent}>
474
+ <DrawerContentBody>{pageContent}</DrawerContentBody>
475
+ </DrawerContent>
476
+ </Drawer>
477
+ );
478
+ }
479
+
480
+ return (
481
+ <>
482
+ {pageContent}
483
+ {chatbotComponent}
484
+ </>
485
+ );
486
+ };