@patternfly/chatbot 6.4.0-prerelease.2 → 6.4.0-prerelease.21

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 (225) hide show
  1. package/dist/cjs/Chatbot/Chatbot.js +1 -7
  2. package/dist/cjs/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.d.ts +2 -0
  3. package/dist/cjs/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.js +2 -2
  4. package/dist/cjs/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.d.ts +22 -2
  5. package/dist/cjs/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.js +15 -9
  6. package/dist/cjs/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.test.js +40 -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/DeepThinking/DeepThinking.d.ts +18 -0
  16. package/dist/cjs/DeepThinking/DeepThinking.js +18 -0
  17. package/dist/cjs/DeepThinking/DeepThinking.test.d.ts +1 -0
  18. package/dist/cjs/DeepThinking/DeepThinking.test.js +48 -0
  19. package/dist/cjs/DeepThinking/index.d.ts +2 -0
  20. package/dist/cjs/DeepThinking/index.js +23 -0
  21. package/dist/cjs/FileDetails/FileDetails.d.ts +22 -3
  22. package/dist/cjs/FileDetails/FileDetails.js +27 -912
  23. package/dist/cjs/FileDetails/FileDetails.test.js +16 -0
  24. package/dist/cjs/FileDetailsLabel/FileDetailsLabel.d.ts +8 -2
  25. package/dist/cjs/FileDetailsLabel/FileDetailsLabel.js +14 -2
  26. package/dist/cjs/FileDetailsLabel/FileDetailsLabel.test.js +19 -1
  27. package/dist/cjs/FilePreview/FilePreview.d.ts +26 -0
  28. package/dist/cjs/FilePreview/FilePreview.js +26 -0
  29. package/dist/cjs/FilePreview/FilePreview.test.d.ts +1 -0
  30. package/dist/cjs/FilePreview/FilePreview.test.js +97 -0
  31. package/dist/cjs/FilePreview/index.d.ts +2 -0
  32. package/dist/cjs/FilePreview/index.js +23 -0
  33. package/dist/cjs/ImagePreview/ImagePreview.d.ts +53 -0
  34. package/dist/cjs/ImagePreview/ImagePreview.js +47 -0
  35. package/dist/cjs/ImagePreview/ImagePreview.test.d.ts +1 -0
  36. package/dist/cjs/ImagePreview/ImagePreview.test.js +225 -0
  37. package/dist/cjs/ImagePreview/index.d.ts +2 -0
  38. package/dist/cjs/ImagePreview/index.js +23 -0
  39. package/dist/cjs/Message/CodeBlockMessage/CodeBlockMessage.js +3 -3
  40. package/dist/cjs/Message/LinkMessage/LinkMessage.d.ts +2 -1
  41. package/dist/cjs/Message/LinkMessage/LinkMessage.js +7 -3
  42. package/dist/cjs/Message/ListMessage/ListItemMessage.d.ts +1 -1
  43. package/dist/cjs/Message/ListMessage/ListItemMessage.js +16 -1
  44. package/dist/cjs/Message/Message.d.ts +15 -0
  45. package/dist/cjs/Message/Message.js +129 -32
  46. package/dist/cjs/Message/Message.test.js +71 -0
  47. package/dist/cjs/Message/SuperscriptMessage/SuperscriptMessage.d.ts +3 -0
  48. package/dist/cjs/Message/SuperscriptMessage/SuperscriptMessage.js +5 -0
  49. package/dist/cjs/Message/UserFeedback/UserFeedback.d.ts +15 -1
  50. package/dist/cjs/Message/UserFeedback/UserFeedback.js +4 -4
  51. package/dist/cjs/Message/UserFeedback/UserFeedback.test.js +44 -0
  52. package/dist/cjs/MessageBar/MessageBar.js +19 -4
  53. package/dist/cjs/MessageBox/JumpButton.d.ts +5 -0
  54. package/dist/cjs/MessageBox/JumpButton.js +1 -1
  55. package/dist/cjs/MessageBox/JumpButton.test.js +4 -4
  56. package/dist/cjs/MessageBox/MessageBox.d.ts +9 -0
  57. package/dist/cjs/MessageBox/MessageBox.js +2 -2
  58. package/dist/cjs/MessageBox/MessageBox.test.js +2 -2
  59. package/dist/cjs/SourcesCard/SourcesCard.d.ts +13 -1
  60. package/dist/cjs/SourcesCard/SourcesCard.js +6 -6
  61. package/dist/cjs/SourcesCard/SourcesCard.test.js +49 -0
  62. package/dist/cjs/ToolResponse/ToolResponse.d.ts +30 -0
  63. package/dist/cjs/ToolResponse/ToolResponse.js +18 -0
  64. package/dist/cjs/ToolResponse/ToolResponse.test.d.ts +1 -0
  65. package/dist/cjs/ToolResponse/ToolResponse.test.js +60 -0
  66. package/dist/cjs/ToolResponse/index.d.ts +2 -0
  67. package/dist/cjs/ToolResponse/index.js +23 -0
  68. package/dist/cjs/index.d.ts +8 -0
  69. package/dist/cjs/index.js +13 -1
  70. package/dist/css/main.css +339 -27
  71. package/dist/css/main.css.map +1 -1
  72. package/dist/dynamic/DeepThinking/package.json +1 -0
  73. package/dist/dynamic/FilePreview/package.json +1 -0
  74. package/dist/dynamic/ImagePreview/package.json +1 -0
  75. package/dist/dynamic/ToolResponse/package.json +1 -0
  76. package/dist/esm/Chatbot/Chatbot.js +1 -7
  77. package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.d.ts +2 -0
  78. package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.js +2 -2
  79. package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.d.ts +22 -2
  80. package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.js +17 -11
  81. package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.test.js +41 -3
  82. package/dist/esm/ChatbotHeader/ChatbotHeaderMenu.js +1 -1
  83. package/dist/esm/ChatbotHeader/ChatbotHeaderMenu.test.js +1 -1
  84. package/dist/esm/ChatbotHeader/ChatbotHeaderNewChatButton.d.ts +18 -0
  85. package/dist/esm/ChatbotHeader/ChatbotHeaderNewChatButton.js +22 -0
  86. package/dist/esm/ChatbotHeader/ChatbotHeaderNewChatButton.test.d.ts +1 -0
  87. package/dist/esm/ChatbotHeader/ChatbotHeaderNewChatButton.test.js +20 -0
  88. package/dist/esm/ChatbotHeader/index.d.ts +1 -0
  89. package/dist/esm/ChatbotHeader/index.js +1 -0
  90. package/dist/esm/DeepThinking/DeepThinking.d.ts +18 -0
  91. package/dist/esm/DeepThinking/DeepThinking.js +14 -0
  92. package/dist/esm/DeepThinking/DeepThinking.test.d.ts +1 -0
  93. package/dist/esm/DeepThinking/DeepThinking.test.js +43 -0
  94. package/dist/esm/DeepThinking/index.d.ts +2 -0
  95. package/dist/esm/DeepThinking/index.js +2 -0
  96. package/dist/esm/FileDetails/FileDetails.d.ts +22 -3
  97. package/dist/esm/FileDetails/FileDetails.js +27 -912
  98. package/dist/esm/FileDetails/FileDetails.test.js +16 -0
  99. package/dist/esm/FileDetailsLabel/FileDetailsLabel.d.ts +8 -2
  100. package/dist/esm/FileDetailsLabel/FileDetailsLabel.js +14 -2
  101. package/dist/esm/FileDetailsLabel/FileDetailsLabel.test.js +19 -1
  102. package/dist/esm/FilePreview/FilePreview.d.ts +26 -0
  103. package/dist/esm/FilePreview/FilePreview.js +21 -0
  104. package/dist/esm/FilePreview/FilePreview.test.d.ts +1 -0
  105. package/dist/esm/FilePreview/FilePreview.test.js +92 -0
  106. package/dist/esm/FilePreview/index.d.ts +2 -0
  107. package/dist/esm/FilePreview/index.js +2 -0
  108. package/dist/esm/ImagePreview/ImagePreview.d.ts +53 -0
  109. package/dist/esm/ImagePreview/ImagePreview.js +42 -0
  110. package/dist/esm/ImagePreview/ImagePreview.test.d.ts +1 -0
  111. package/dist/esm/ImagePreview/ImagePreview.test.js +220 -0
  112. package/dist/esm/ImagePreview/index.d.ts +2 -0
  113. package/dist/esm/ImagePreview/index.js +2 -0
  114. package/dist/esm/Message/CodeBlockMessage/CodeBlockMessage.js +5 -5
  115. package/dist/esm/Message/LinkMessage/LinkMessage.d.ts +2 -1
  116. package/dist/esm/Message/LinkMessage/LinkMessage.js +7 -3
  117. package/dist/esm/Message/ListMessage/ListItemMessage.d.ts +1 -1
  118. package/dist/esm/Message/ListMessage/ListItemMessage.js +16 -1
  119. package/dist/esm/Message/Message.d.ts +15 -0
  120. package/dist/esm/Message/Message.js +129 -32
  121. package/dist/esm/Message/Message.test.js +71 -0
  122. package/dist/esm/Message/SuperscriptMessage/SuperscriptMessage.d.ts +3 -0
  123. package/dist/esm/Message/SuperscriptMessage/SuperscriptMessage.js +3 -0
  124. package/dist/esm/Message/UserFeedback/UserFeedback.d.ts +15 -1
  125. package/dist/esm/Message/UserFeedback/UserFeedback.js +4 -4
  126. package/dist/esm/Message/UserFeedback/UserFeedback.test.js +45 -1
  127. package/dist/esm/MessageBar/MessageBar.js +19 -4
  128. package/dist/esm/MessageBox/JumpButton.d.ts +5 -0
  129. package/dist/esm/MessageBox/JumpButton.js +1 -1
  130. package/dist/esm/MessageBox/JumpButton.test.js +4 -4
  131. package/dist/esm/MessageBox/MessageBox.d.ts +9 -0
  132. package/dist/esm/MessageBox/MessageBox.js +2 -2
  133. package/dist/esm/MessageBox/MessageBox.test.js +2 -2
  134. package/dist/esm/SourcesCard/SourcesCard.d.ts +13 -1
  135. package/dist/esm/SourcesCard/SourcesCard.js +6 -6
  136. package/dist/esm/SourcesCard/SourcesCard.test.js +50 -1
  137. package/dist/esm/ToolResponse/ToolResponse.d.ts +30 -0
  138. package/dist/esm/ToolResponse/ToolResponse.js +14 -0
  139. package/dist/esm/ToolResponse/ToolResponse.test.d.ts +1 -0
  140. package/dist/esm/ToolResponse/ToolResponse.test.js +55 -0
  141. package/dist/esm/ToolResponse/index.d.ts +2 -0
  142. package/dist/esm/ToolResponse/index.js +2 -0
  143. package/dist/esm/index.d.ts +8 -0
  144. package/dist/esm/index.js +8 -0
  145. package/dist/tsconfig.tsbuildinfo +1 -1
  146. package/package.json +7 -6
  147. package/patternfly-docs/content/extensions/chatbot/examples/Messages/AttachmentEdit.tsx +1 -1
  148. package/patternfly-docs/content/extensions/chatbot/examples/Messages/BotMessage.tsx +101 -3
  149. package/patternfly-docs/content/extensions/chatbot/examples/Messages/FilePreview.tsx +33 -0
  150. package/patternfly-docs/content/extensions/chatbot/examples/Messages/ImagePreview.tsx +53 -0
  151. package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithDeepThinking.tsx +17 -0
  152. package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithFeedback.tsx +111 -85
  153. package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithSources.tsx +70 -0
  154. package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithToolResponse.tsx +135 -0
  155. package/patternfly-docs/content/extensions/chatbot/examples/Messages/Messages.md +38 -4
  156. package/patternfly-docs/content/extensions/chatbot/examples/Messages/PreviewAttachment.tsx +1 -1
  157. package/patternfly-docs/content/extensions/chatbot/examples/Messages/UserMessage.tsx +107 -2
  158. package/patternfly-docs/content/extensions/chatbot/examples/Messages/UserMessageWithExtraContent.tsx +616 -3
  159. package/patternfly-docs/content/extensions/chatbot/examples/Messages/file-preview.svg +9 -0
  160. package/patternfly-docs/content/extensions/chatbot/examples/UI/ChatbotConversationEditing.tsx +202 -0
  161. package/patternfly-docs/content/extensions/chatbot/examples/UI/ChatbotHeaderBasic.tsx +17 -3
  162. package/patternfly-docs/content/extensions/chatbot/examples/UI/ChatbotHeaderDrawer.tsx +36 -5
  163. package/patternfly-docs/content/extensions/chatbot/examples/UI/ChatbotHeaderDrawerWithPin.tsx +12 -2
  164. package/patternfly-docs/content/extensions/chatbot/examples/UI/UI.md +22 -3
  165. package/patternfly-docs/content/extensions/chatbot/examples/demos/Chatbot.md +1 -1
  166. package/patternfly-docs/patternfly-docs.config.js +1 -1
  167. package/src/Chatbot/Chatbot.scss +9 -2
  168. package/src/Chatbot/Chatbot.tsx +18 -31
  169. package/src/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.tsx +5 -1
  170. package/src/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.scss +16 -10
  171. package/src/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.test.tsx +132 -3
  172. package/src/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.tsx +80 -33
  173. package/src/ChatbotHeader/ChatbotHeaderMenu.test.tsx +1 -1
  174. package/src/ChatbotHeader/ChatbotHeaderMenu.tsx +2 -2
  175. package/src/ChatbotHeader/ChatbotHeaderNewChatButton.test.tsx +25 -0
  176. package/src/ChatbotHeader/ChatbotHeaderNewChatButton.tsx +64 -0
  177. package/src/ChatbotHeader/index.ts +1 -0
  178. package/src/ChatbotModal/ChatbotModal.scss +1 -1
  179. package/src/DeepThinking/DeepThinking.scss +24 -0
  180. package/src/DeepThinking/DeepThinking.test.tsx +61 -0
  181. package/src/DeepThinking/DeepThinking.tsx +68 -0
  182. package/src/DeepThinking/index.ts +3 -0
  183. package/src/FileDetails/FileDetails.scss +10 -0
  184. package/src/FileDetails/FileDetails.test.tsx +16 -0
  185. package/src/FileDetails/FileDetails.tsx +89 -32
  186. package/src/FileDetails/__snapshots__/FileDetails.test.tsx.snap +20 -14
  187. package/src/FileDetailsLabel/FileDetailsLabel.test.tsx +21 -1
  188. package/src/FileDetailsLabel/FileDetailsLabel.tsx +16 -3
  189. package/src/FileDetailsLabel/__snapshots__/FileDetailsLabel.test.tsx.snap +20 -14
  190. package/src/FilePreview/FilePreview.scss +22 -0
  191. package/src/FilePreview/FilePreview.test.tsx +112 -0
  192. package/src/FilePreview/FilePreview.tsx +58 -0
  193. package/src/FilePreview/index.ts +3 -0
  194. package/src/ImagePreview/ImagePreview.scss +61 -0
  195. package/src/ImagePreview/ImagePreview.test.tsx +253 -0
  196. package/src/ImagePreview/ImagePreview.tsx +200 -0
  197. package/src/ImagePreview/index.ts +3 -0
  198. package/src/Message/CodeBlockMessage/CodeBlockMessage.scss +2 -1
  199. package/src/Message/CodeBlockMessage/CodeBlockMessage.tsx +6 -5
  200. package/src/Message/LinkMessage/LinkMessage.tsx +6 -2
  201. package/src/Message/ListMessage/ListItemMessage.tsx +5 -1
  202. package/src/Message/ListMessage/ListMessage.scss +17 -0
  203. package/src/Message/Message.scss +44 -0
  204. package/src/Message/Message.test.tsx +90 -0
  205. package/src/Message/Message.tsx +171 -46
  206. package/src/Message/SuperscriptMessage/SuperscriptMessage.scss +8 -0
  207. package/src/Message/SuperscriptMessage/SuperscriptMessage.tsx +13 -0
  208. package/src/Message/TextMessage/TextMessage.scss +46 -5
  209. package/src/Message/UserFeedback/UserFeedback.test.tsx +107 -0
  210. package/src/Message/UserFeedback/UserFeedback.tsx +41 -6
  211. package/src/MessageBar/MessageBar.tsx +23 -3
  212. package/src/MessageBox/JumpButton.test.tsx +4 -4
  213. package/src/MessageBox/JumpButton.tsx +20 -4
  214. package/src/MessageBox/MessageBox.scss +0 -12
  215. package/src/MessageBox/MessageBox.test.tsx +2 -2
  216. package/src/MessageBox/MessageBox.tsx +23 -2
  217. package/src/SourcesCard/SourcesCard.scss +17 -0
  218. package/src/SourcesCard/SourcesCard.test.tsx +93 -0
  219. package/src/SourcesCard/SourcesCard.tsx +116 -80
  220. package/src/ToolResponse/ToolResponse.scss +36 -0
  221. package/src/ToolResponse/ToolResponse.test.tsx +78 -0
  222. package/src/ToolResponse/ToolResponse.tsx +95 -0
  223. package/src/ToolResponse/index.ts +3 -0
  224. package/src/index.ts +12 -0
  225. package/src/main.scss +16 -0
@@ -1,4 +1,4 @@
1
- import { useState, FunctionComponent } from 'react';
1
+ import { useState, useEffect, useRef, FunctionComponent } from 'react';
2
2
  import { ChatbotDisplayMode } from '@patternfly/chatbot/dist/dynamic/Chatbot';
3
3
  import ChatbotConversationHistoryNav, {
4
4
  Conversation
@@ -71,8 +71,28 @@ export const ChatbotHeaderTitleDemo: FunctionComponent = () => {
71
71
  const [hasError, setHasError] = useState(false);
72
72
  const [isEmpty, setIsEmpty] = useState(false);
73
73
  const [hasNoResults, setHasNoResults] = useState(false);
74
+ const [announcement, setAnnouncement] = useState('');
75
+ const [debouncedAnnouncement, setDebouncedAnnouncement] = useState('');
76
+ const announcementTimeoutRef = useRef<NodeJS.Timeout>();
74
77
  const displayMode = ChatbotDisplayMode.embedded;
75
78
 
79
+ // Debounce announcement updates to prevent screen reader overload
80
+ useEffect(() => {
81
+ if (announcementTimeoutRef.current) {
82
+ clearTimeout(announcementTimeoutRef.current);
83
+ }
84
+
85
+ announcementTimeoutRef.current = setTimeout(() => {
86
+ setDebouncedAnnouncement(announcement);
87
+ }, 500);
88
+
89
+ return () => {
90
+ if (announcementTimeoutRef.current) {
91
+ clearTimeout(announcementTimeoutRef.current);
92
+ }
93
+ };
94
+ }, [announcement]);
95
+
76
96
  const findMatchingItems = (targetValue: string) => {
77
97
  const filteredConversations = Object.entries(initialConversations).reduce((acc, [key, items]) => {
78
98
  const filteredItems = items.filter((item) => item.text.toLowerCase().includes(targetValue.toLowerCase()));
@@ -168,12 +188,23 @@ export const ChatbotHeaderTitleDemo: FunctionComponent = () => {
168
188
  handleTextInputChange={(value: string) => {
169
189
  if (value === '') {
170
190
  setConversations(initialConversations);
191
+ setAnnouncement('');
192
+ setDebouncedAnnouncement('');
193
+ setHasNoResults(false);
194
+ } else {
195
+ // this is where you would perform search on the items in the drawer
196
+ // and update the state
197
+ const newConversations: { [key: string]: Conversation[] } = findMatchingItems(value);
198
+ const totalCount = Object.values(newConversations).flat().length;
199
+ const newAnnouncement =
200
+ totalCount === 1
201
+ ? `${totalCount} conversation matches "${value}"`
202
+ : `${totalCount} conversations match "${value}"`;
203
+ setAnnouncement(newAnnouncement);
204
+ setConversations(newConversations);
171
205
  }
172
- // this is where you would perform search on the items in the drawer
173
- // and update the state
174
- const newConversations: { [key: string]: Conversation[] } = findMatchingItems(value);
175
- setConversations(newConversations);
176
206
  }}
207
+ searchInputScreenReaderText={debouncedAnnouncement}
177
208
  drawerContent={<div>Drawer content</div>}
178
209
  isLoading={isLoading}
179
210
  errorState={hasError ? ERROR : undefined}
@@ -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) {
@@ -61,7 +61,8 @@ ChatbotHeaderMenu,
61
61
  ChatbotHeaderActions,
62
62
  ChatbotHeaderTitle,
63
63
  ChatbotHeaderOptionsDropdown,
64
- ChatbotHeaderSelectorDropdown
64
+ ChatbotHeaderSelectorDropdown,
65
+ ChatbotHeaderNewChatButton
65
66
  } from '@patternfly/chatbot/dist/dynamic/ChatbotHeader';
66
67
  import { ChatbotFooter, ChatbotFootnote } from '@patternfly/chatbot/dist/dynamic/ChatbotFooter';
67
68
  import { MessageBar } from '@patternfly/chatbot/dist/dynamic/MessageBar';
@@ -78,13 +79,15 @@ import OutlinedWindowRestoreIcon from '@patternfly/react-icons/dist/esm/icons/ou
78
79
  import ExpandIcon from '@patternfly/react-icons/dist/esm/icons/expand-icon';
79
80
  import OpenDrawerRightIcon from '@patternfly/react-icons/dist/esm/icons/open-drawer-right-icon';
80
81
  import CogIcon from '@patternfly/react-icons/dist/esm/icons/cog-icon';
82
+ import PenToSquareIcon from '@patternfly/react-icons/dist/esm/icons/pen-to-square-icon';
81
83
  import PFHorizontalLogoColor from './PF-HorizontalLogo-Color.svg';
82
84
  import PFHorizontalLogoReverse from './PF-HorizontalLogo-Reverse.svg';
83
85
  import userAvatar from '../Messages/user_avatar.svg';
84
86
  import patternflyAvatar from '../Messages/patternfly_avatar.jpg';
85
87
  import termsAndConditionsHeader from './PF-TermsAndConditionsHeader.svg';
86
88
  import { CloseIcon, SearchIcon, OutlinedCommentsIcon } from '@patternfly/react-icons';
87
- import { FunctionComponent, FormEvent, useState, useRef, MouseEvent, isValidElement, cloneElement, Children, ReactNode, Ref, MouseEvent as ReactMouseEvent, CSSProperties} from 'react';
89
+ import { FunctionComponent, FormEvent, useState, useRef, MouseEvent, isValidElement, cloneElement, Children, ReactNode, Ref, MouseEvent as ReactMouseEvent, CSSProperties, useEffect} from 'react';
90
+ import FilePreview from '@patternfly/chatbot/dist/dynamic/FilePreview';
88
91
 
89
92
  ## Structure
90
93
 
@@ -184,9 +187,10 @@ The ChatBot header is persistent, and contains the title for the ChatBot window,
184
187
 
185
188
  The `<ChatbotHeader>` has 2 sections:
186
189
 
187
- - `<ChatbotHeaderMain>` contains the title and an optional menu toggle:
190
+ - `<ChatbotHeaderMain>` contains the title and an optional menu toggle or new chat button:
188
191
  - `<ChatbotHeaderTitle>` handles the layout and display of a title or image at different responsive sizes.
189
192
  - `<ChatbotHeaderMenu>` (optional) is placed on the left side of the header and used to toggle a chat history menu.
193
+ - `<ChatbotHeaderNewChatButton>` (optional) is placed on the left side of the header and used to initiate a new chat.
190
194
  - `<ChatbotHeaderActions>` contains any additional controls:
191
195
  - The `<ChatbotHeaderSelectorDropdown>` component is a standard PatternFly dropdown that matches the ChatBot styles.
192
196
  - The `<ChatbotHeaderOptionsDropdown>` component is a dropdown with a menu toggle that is intended to be used to update ChatBot settings (like the display mode).
@@ -197,6 +201,7 @@ Your `<ChatbotHeader>` code structure should look like this:
197
201
  <ChatbotHeader>
198
202
  <ChatbotHeaderMain>
199
203
  <ChatbotHeaderMenu ... />
204
+ <ChatbotHeaderNewChatButton ... />
200
205
  <ChatbotHeaderTitle ... />
201
206
  </ChatbotHeaderMain>
202
207
  <ChatbotHeaderActions>
@@ -221,6 +226,7 @@ There are a variety of options and customizations you can make to the header, to
221
226
  In this example, select the respective checkbox to toggle these features:
222
227
 
223
228
  - **Menu:** Users can select the menu toggle to open a menu of additional options or actions.
229
+ - **New chat button:** Used to start a new chat session. The header button can be used in addition to or in place of a new chat button within the [conversation history drawer](/patternfly-ai/chatbot/ui/#drawer-with-search-and-new-chat-button).
224
230
  - **Left-aligned logo**
225
231
  - **Centered logo**
226
232
  - **Selector dropdown:** Users can choose from preselected options in a dropdown menu. For example, they can toggle between AI models.
@@ -369,6 +375,19 @@ To help users track important conversations, add a "pin" option to the conversat
369
375
 
370
376
  ```
371
377
 
378
+ ### Renaming conversations in history drawer
379
+
380
+ You can allow users to rename a conversation in the history drawer by implementing a modal that opens upon clicking a "Rename" (or similar) action. When doing so, you must ensure the following:
381
+
382
+ - When the modal opens, focus is placed at the end of the text input.
383
+ - When the modal closes, focus goes back to the action toggle that was previously opened.
384
+ - Changes can be canceled via the **<kbd>Escape</kbd>** key or clicking a "Cancel" button.
385
+ - Changes can be saved via the **<kbd>Enter</kbd>** key or by clicking a "Save" button.
386
+
387
+ ```js file="./ChatbotConversationEditing.tsx"
388
+
389
+ ```
390
+
372
391
  ### Drawer with active conversation
373
392
 
374
393
  If you're showing a conversation that is already active, you can set the `activeItemId` prop on your `<ChatbotConversationHistoryNav>` to apply an active visual state.
@@ -45,7 +45,7 @@ ChatbotHeaderTitle,
45
45
  ChatbotHeaderActions,
46
46
  ChatbotHeaderSelectorDropdown,
47
47
  ChatbotHeaderOptionsDropdown,
48
- ChatbotHeaderCloseButton
48
+ ChatbotHeaderCloseButton,
49
49
  } from '@patternfly/chatbot/dist/dynamic/ChatbotHeader';
50
50
 
51
51
  import ExpandIcon from '@patternfly/react-icons/dist/esm/icons/expand-icon';
@@ -2,7 +2,7 @@
2
2
  module.exports = {
3
3
  sideNavItems: [{ section: 'PatternFly-AI' }],
4
4
  topNavItems: [],
5
- hasDarkThemeSwitcher: true,
5
+ hasThemeSwitcher: true,
6
6
  hasRTLSwitcher: true,
7
7
  port: 8006
8
8
  };
@@ -16,10 +16,17 @@
16
16
  z-index: var(--pf-t--global--z-index--md);
17
17
  -webkit-font-smoothing: antialiased;
18
18
  -moz-osx-font-smoothing: grayscale;
19
-
20
- // Hide chatbot
19
+ transition:
20
+ opacity 0.3s ease-out,
21
+ transform 0.3s ease-out;
21
22
  &--hidden {
22
23
  pointer-events: none;
24
+ opacity: 0;
25
+ transform: translateY(16px);
26
+ }
27
+ &--visible {
28
+ opacity: 1;
29
+ transform: translateY(0);
23
30
  }
24
31
 
25
32
  // 32 rem is the width of the overlay chatbot plus the insets
@@ -4,7 +4,6 @@
4
4
  import type { Ref, FunctionComponent } from 'react';
5
5
 
6
6
  import { forwardRef } from 'react';
7
- import { motion } from 'framer-motion';
8
7
 
9
8
  export interface ChatbotProps {
10
9
  /** Content to be displayed in the chatbot */
@@ -40,36 +39,24 @@ const ChatbotBase: FunctionComponent<ChatbotProps> = ({
40
39
  ariaLabel,
41
40
  isCompact,
42
41
  ...props
43
- }: ChatbotProps) => {
44
- // Configure animations
45
- const motionChatbot = {
46
- visible: { opacity: 1, y: 0 },
47
- hidden: { opacity: 0, y: '16px' }
48
- };
49
-
50
- return (
51
- <motion.div
52
- className={`pf-chatbot pf-chatbot--${displayMode} ${!isVisible ? 'pf-chatbot--hidden' : ''} ${isCompact ? 'pf-m-compact' : ''} ${className ?? ''}`}
53
- variants={motionChatbot}
54
- initial="hidden"
55
- animate={isVisible ? 'visible' : 'hidden'}
56
- {...props}
57
- >
58
- {/* Ref is intended for use with skip to chatbot links, etc. */}
59
- {/* Motion.div does not accept refs */}
60
- {isVisible ? (
61
- <section
62
- aria-label={ariaLabel ?? 'Chatbot'}
63
- className={`pf-chatbot-container pf-chatbot-container--${displayMode} ${!isVisible ? 'pf-chatbot-container--hidden' : ''}`}
64
- tabIndex={-1}
65
- ref={innerRef}
66
- >
67
- {children}
68
- </section>
69
- ) : undefined}
70
- </motion.div>
71
- );
72
- };
42
+ }: ChatbotProps) => (
43
+ <div
44
+ className={`pf-chatbot pf-chatbot--${displayMode} ${!isVisible ? 'pf-chatbot--hidden' : 'pf-chatbot--visible'} ${isCompact ? 'pf-m-compact' : ''} ${className ?? ''}`}
45
+ {...props}
46
+ >
47
+ {/* Ref is intended for use with skip to chatbot links, etc. */}
48
+ {isVisible ? (
49
+ <section
50
+ aria-label={ariaLabel ?? 'Chatbot'}
51
+ className={`pf-chatbot-container pf-chatbot-container--${displayMode} ${!isVisible ? 'pf-chatbot-container--hidden' : ''}`}
52
+ tabIndex={-1}
53
+ ref={innerRef}
54
+ >
55
+ {children}
56
+ </section>
57
+ ) : undefined}
58
+ </div>
59
+ );
73
60
 
74
61
  const Chatbot = forwardRef((props: ChatbotProps, ref: Ref<HTMLDivElement>) => (
75
62
  <ChatbotBase innerRef={ref} {...props} />
@@ -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
  role="menuitem"
48
52
  >
49
53
  <EllipsisIcon />
@@ -6,24 +6,27 @@
6
6
  position: absolute;
7
7
  border-radius: var(--pf-t--global--border--radius--medium);
8
8
  }
9
- // Drawer input
10
- // ----------------------------------------------------------------------------
11
- .pf-chatbot__input {
12
- }
9
+
13
10
  // Drawer title
14
11
  // ----------------------------------------------------------------------------
15
- .pf-chatbot__title-container {
12
+ .pf-chatbot__heading-container {
16
13
  padding-inline-start: var(--pf-t--global--spacer--lg);
17
14
  padding-inline-end: var(--pf-t--global--spacer--lg);
18
15
  display: flex;
19
16
  flex-direction: column;
20
17
  row-gap: var(--pf-t--global--spacer--sm);
21
18
  }
22
- // Drawer title icon
23
- // ----------------------------------------------------------------------------
24
- .pf-chatbot__title-icon {
25
- padding-inline-end: var(--pf-t--global--spacer--md);
26
- padding-inline-start: var(--pf-t--global--spacer--sm);
19
+ .pf-chatbot__title {
20
+ font-size: var(--pf-v6-c-title--m-h3--FontSize);
21
+ font-weight: var(--pf-v6-c-title--m-h3--FontWeight);
22
+ line-height: var(--pf-v6-c-title--m-h3--LineHeight);
23
+ }
24
+ .pf-chatbot__title-container {
25
+ display: flex;
26
+ flex-direction: row;
27
+ align-items: baseline;
28
+ justify-content: flex-start;
29
+ gap: var(--pf-t--global--spacer--gap--text-to-element--default);
27
30
  }
28
31
  // Drawer menu
29
32
  // ----------------------------------------------------------------------------
@@ -36,6 +39,7 @@
36
39
  .pf-v6-c-menu__item-main {
37
40
  --pf-v6-c-menu__item-main--ColumnGap: var(--pf-t--global--spacer--md);
38
41
  }
42
+
39
43
  .pf-chatbot__menu-item-header > .pf-v6-c-menu__group-title {
40
44
  color: var(--pf-t--global--text--color--subtle);
41
45
  font-weight: var(--pf-t--global--font--weight--body--bold);
@@ -48,6 +52,7 @@
48
52
  background-color: var(--pf-t--global--background--color--floating--default);
49
53
  z-index: var(--pf-t--global--z-index--md);
50
54
  }
55
+
51
56
  .pf-chatbot__menu-item {
52
57
  --pf-v6-c-menu__item--PaddingInlineStart: var(--pf-t--global--spacer--sm);
53
58
  --pf-v6-c-menu__item--PaddingInlineEnd: var(--pf-t--global--spacer--sm);
@@ -62,6 +67,7 @@
62
67
  .pf-v6-c-menu__list-item.pf-chatbot__menu-item {
63
68
  overflow: hidden;
64
69
  }
70
+
65
71
  .pf-chatbot__history-actions {
66
72
  transform: rotate(90deg);
67
73
  }
@@ -4,7 +4,7 @@ import { fireEvent, render, screen, waitFor } from '@testing-library/react';
4
4
  import { ChatbotDisplayMode } from '../Chatbot/Chatbot';
5
5
  import ChatbotConversationHistoryNav, { Conversation } from './ChatbotConversationHistoryNav';
6
6
  import { EmptyStateStatus, Spinner } from '@patternfly/react-core';
7
- import { OutlinedCommentsIcon, SearchIcon } from '@patternfly/react-icons';
7
+ import { BellIcon, OutlinedCommentsIcon, SearchIcon } from '@patternfly/react-icons';
8
8
  import { ComponentType } from 'react';
9
9
 
10
10
  const ERROR = {
@@ -348,7 +348,7 @@ describe('ChatbotConversationHistoryNav', () => {
348
348
  ).toBeTruthy();
349
349
  expect(screen.getByRole('button', { name: /Close drawer panel/i })).toBeTruthy();
350
350
  expect(screen.getByRole('button', { name: /Loading... Reload/i })).toBeTruthy();
351
- expect(screen.getByRole('textbox', { name: /Filter menu items/i })).toBeTruthy();
351
+ expect(screen.getByRole('textbox', { name: /Search previous conversations/i })).toBeTruthy();
352
352
  expect(screen.getByRole('heading', { name: /Could not load chat history/i })).toBeTruthy();
353
353
  });
354
354
 
@@ -372,7 +372,7 @@ describe('ChatbotConversationHistoryNav', () => {
372
372
  ).toBeTruthy();
373
373
  expect(screen.getByRole('button', { name: /Close drawer panel/i })).toBeTruthy();
374
374
  expect(screen.queryByRole('button', { name: /Loading... Reload/i })).toBeFalsy();
375
- expect(screen.getByRole('textbox', { name: /Filter menu items/i })).toBeTruthy();
375
+ expect(screen.getByRole('textbox', { name: /Search previous conversations/i })).toBeTruthy();
376
376
  expect(screen.getByRole('heading', { name: /Could not load chat history/i })).toBeTruthy();
377
377
  });
378
378
 
@@ -491,4 +491,133 @@ describe('ChatbotConversationHistoryNav', () => {
491
491
  const iconElement = container.querySelector('.pf-chatbot__title-icon');
492
492
  expect(iconElement).toBeInTheDocument();
493
493
  });
494
+
495
+ it('Passes menuProps to Menu', () => {
496
+ render(
497
+ <ChatbotConversationHistoryNav
498
+ onDrawerToggle={onDrawerToggle}
499
+ isDrawerOpen={true}
500
+ displayMode={ChatbotDisplayMode.fullscreen}
501
+ setIsDrawerOpen={jest.fn()}
502
+ conversations={initialConversations}
503
+ menuProps={{ className: 'test' }}
504
+ />
505
+ );
506
+
507
+ expect(screen.getByRole('menu').parentElement?.parentElement).toHaveClass('test');
508
+ });
509
+
510
+ it('Passes menuContentProps to MenuContent', () => {
511
+ render(
512
+ <ChatbotConversationHistoryNav
513
+ onDrawerToggle={onDrawerToggle}
514
+ isDrawerOpen={true}
515
+ displayMode={ChatbotDisplayMode.fullscreen}
516
+ setIsDrawerOpen={jest.fn()}
517
+ conversations={initialConversations}
518
+ menuContentProps={{ className: 'test' }}
519
+ />
520
+ );
521
+ expect(screen.getByRole('menu').parentElement).toHaveClass('test');
522
+ });
523
+
524
+ it('Passes menuListProps to MenuList when conversations is an array', () => {
525
+ render(
526
+ <ChatbotConversationHistoryNav
527
+ onDrawerToggle={onDrawerToggle}
528
+ isDrawerOpen={true}
529
+ displayMode={ChatbotDisplayMode.fullscreen}
530
+ setIsDrawerOpen={jest.fn()}
531
+ conversations={initialConversations}
532
+ menuListProps={{ className: 'test' }}
533
+ />
534
+ );
535
+ expect(screen.getByRole('menu')).toHaveClass('test');
536
+ });
537
+
538
+ it('Passes menuListProps to MenuList when conversations is an object', () => {
539
+ render(
540
+ <ChatbotConversationHistoryNav
541
+ onDrawerToggle={onDrawerToggle}
542
+ isDrawerOpen={true}
543
+ displayMode={ChatbotDisplayMode.fullscreen}
544
+ setIsDrawerOpen={jest.fn()}
545
+ conversations={{ Today: initialConversations }}
546
+ menuListProps={{ Today: { className: 'test' } }}
547
+ />
548
+ );
549
+ expect(screen.getByRole('menu')).toHaveClass('test');
550
+ });
551
+
552
+ it('Passes menuGroupProps to MenuGroup when conversations is an object', () => {
553
+ render(
554
+ <ChatbotConversationHistoryNav
555
+ onDrawerToggle={onDrawerToggle}
556
+ isDrawerOpen={true}
557
+ displayMode={ChatbotDisplayMode.fullscreen}
558
+ setIsDrawerOpen={jest.fn()}
559
+ conversations={{ Today: initialConversations }}
560
+ menuGroupProps={{ Today: { className: 'test' } }}
561
+ />
562
+ );
563
+ expect(screen.getByRole('menu').parentElement).toHaveClass('test');
564
+ });
565
+
566
+ it('Passes additionalProps to MenuItem', () => {
567
+ render(
568
+ <ChatbotConversationHistoryNav
569
+ onDrawerToggle={onDrawerToggle}
570
+ isDrawerOpen={true}
571
+ displayMode={ChatbotDisplayMode.fullscreen}
572
+ setIsDrawerOpen={jest.fn()}
573
+ conversations={[{ id: '1', text: 'ChatBot documentation', additionalProps: { className: 'test' } }]}
574
+ />
575
+ );
576
+ expect(screen.getByRole('menuitem')).toHaveClass('test');
577
+ });
578
+
579
+ it('should be able to spread search input props when searchInputProps is passed', () => {
580
+ render(
581
+ <ChatbotConversationHistoryNav
582
+ onDrawerToggle={onDrawerToggle}
583
+ isDrawerOpen={true}
584
+ displayMode={ChatbotDisplayMode.fullscreen}
585
+ setIsDrawerOpen={jest.fn()}
586
+ conversations={initialConversations}
587
+ handleTextInputChange={jest.fn()}
588
+ searchInputProps={{ value: 'I am a sample search' }}
589
+ />
590
+ );
591
+
592
+ expect(screen.getByRole('dialog', { name: /Chat history I am a sample search/i })).toBeInTheDocument();
593
+ });
594
+
595
+ it('overrides nav title heading level when navTitleProps.headingLevel is passed', () => {
596
+ render(
597
+ <ChatbotConversationHistoryNav
598
+ onDrawerToggle={onDrawerToggle}
599
+ isDrawerOpen={true}
600
+ displayMode={ChatbotDisplayMode.fullscreen}
601
+ setIsDrawerOpen={jest.fn()}
602
+ conversations={{ Today: initialConversations }}
603
+ navTitleProps={{ headingLevel: 'h1' }}
604
+ />
605
+ );
606
+ expect(screen.queryByRole('heading', { name: /Chat history/i, level: 2 })).not.toBeInTheDocument();
607
+ expect(screen.getByRole('heading', { name: /Chat history/i, level: 1 })).toBeInTheDocument();
608
+ });
609
+
610
+ it('overrides nav title icon when navTitleIcon is passed in', () => {
611
+ render(
612
+ <ChatbotConversationHistoryNav
613
+ onDrawerToggle={onDrawerToggle}
614
+ isDrawerOpen={true}
615
+ displayMode={ChatbotDisplayMode.fullscreen}
616
+ setIsDrawerOpen={jest.fn()}
617
+ conversations={initialConversations}
618
+ navTitleIcon={<BellIcon data-testid="bell" />}
619
+ />
620
+ );
621
+ expect(screen.getByTestId('bell')).toBeInTheDocument();
622
+ });
494
623
  });