@patternfly/chatbot 6.5.0-prerelease.9 → 6.5.0

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 (314) hide show
  1. package/dist/cjs/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.d.ts +9 -1
  2. package/dist/cjs/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.js +9 -2
  3. package/dist/cjs/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.test.js +38 -0
  4. package/dist/cjs/ChatbotHeader/ChatbotHeaderMenu.js +29 -2
  5. package/dist/cjs/CodeModal/CodeModal.d.ts +2 -0
  6. package/dist/cjs/CodeModal/CodeModal.js +53 -12
  7. package/dist/cjs/DeepThinking/DeepThinking.d.ts +13 -0
  8. package/dist/cjs/DeepThinking/DeepThinking.js +31 -3
  9. package/dist/cjs/DeepThinking/DeepThinking.test.js +80 -0
  10. package/dist/cjs/MarkdownContent/MarkdownContent.d.ts +44 -0
  11. package/dist/cjs/MarkdownContent/MarkdownContent.js +181 -0
  12. package/dist/cjs/MarkdownContent/MarkdownContent.test.d.ts +1 -0
  13. package/dist/cjs/MarkdownContent/MarkdownContent.test.js +192 -0
  14. package/dist/cjs/MarkdownContent/index.d.ts +2 -0
  15. package/dist/cjs/MarkdownContent/index.js +23 -0
  16. package/dist/cjs/Message/CodeBlockMessage/CodeBlockMessage.d.ts +3 -1
  17. package/dist/cjs/Message/CodeBlockMessage/CodeBlockMessage.js +15 -4
  18. package/dist/cjs/Message/CodeBlockMessage/CodeBlockMessage.test.d.ts +1 -0
  19. package/dist/cjs/Message/CodeBlockMessage/CodeBlockMessage.test.js +131 -0
  20. package/dist/cjs/Message/ErrorMessage/ErrorMessage.d.ts +15 -1
  21. package/dist/cjs/Message/ErrorMessage/ErrorMessage.js +5 -3
  22. package/dist/cjs/Message/ErrorMessage/ErrorMessage.test.d.ts +1 -0
  23. package/dist/cjs/Message/ErrorMessage/ErrorMessage.test.js +30 -0
  24. package/dist/cjs/Message/LinkMessage/LinkMessage.d.ts +5 -1
  25. package/dist/cjs/Message/LinkMessage/LinkMessage.js +4 -3
  26. package/dist/cjs/Message/ListMessage/OrderedListMessage.d.ts +9 -1
  27. package/dist/cjs/Message/ListMessage/OrderedListMessage.js +2 -1
  28. package/dist/cjs/Message/ListMessage/UnorderedListMessage.d.ts +7 -1
  29. package/dist/cjs/Message/ListMessage/UnorderedListMessage.js +2 -1
  30. package/dist/cjs/Message/Message.d.ts +20 -3
  31. package/dist/cjs/Message/Message.js +7 -160
  32. package/dist/cjs/Message/Message.test.js +129 -2
  33. package/dist/cjs/Message/MessageAndActions/MessageAndActions.d.ts +14 -0
  34. package/dist/cjs/Message/MessageAndActions/MessageAndActions.js +22 -0
  35. package/dist/cjs/Message/MessageAndActions/MessageAndActions.test.d.ts +1 -0
  36. package/dist/cjs/Message/MessageAndActions/MessageAndActions.test.js +25 -0
  37. package/dist/cjs/Message/MessageAndActions/index.d.ts +1 -0
  38. package/dist/cjs/Message/MessageAndActions/index.js +17 -0
  39. package/dist/cjs/Message/MessageAttachments/MessageAttachmentItem.d.ts +13 -0
  40. package/dist/cjs/Message/MessageAttachments/MessageAttachmentItem.js +22 -0
  41. package/dist/cjs/Message/MessageAttachments/MessageAttachmentItem.test.d.ts +1 -0
  42. package/dist/cjs/Message/MessageAttachments/MessageAttachmentItem.test.js +25 -0
  43. package/dist/cjs/Message/MessageAttachments/MessageAttachmentsContainer.d.ts +13 -0
  44. package/dist/cjs/Message/MessageAttachments/MessageAttachmentsContainer.js +22 -0
  45. package/dist/cjs/Message/MessageAttachments/MessageAttachmentsContainer.test.d.ts +1 -0
  46. package/dist/cjs/Message/MessageAttachments/MessageAttachmentsContainer.test.js +25 -0
  47. package/dist/cjs/Message/MessageAttachments/index.d.ts +2 -0
  48. package/dist/cjs/Message/MessageAttachments/index.js +18 -0
  49. package/dist/cjs/Message/MessageInput.d.ts +1 -1
  50. package/dist/cjs/Message/MessageInput.js +3 -1
  51. package/dist/cjs/Message/MessageLoading.d.ts +13 -4
  52. package/dist/cjs/Message/MessageLoading.js +19 -5
  53. package/dist/cjs/Message/MessageLoading.test.d.ts +1 -0
  54. package/dist/cjs/Message/MessageLoading.test.js +25 -0
  55. package/dist/cjs/Message/QuickResponse/QuickResponse.js +3 -2
  56. package/dist/cjs/Message/QuickResponse/QuickResponse.test.d.ts +1 -0
  57. package/dist/cjs/Message/QuickResponse/QuickResponse.test.js +109 -0
  58. package/dist/cjs/Message/QuickResponse/index.d.ts +1 -0
  59. package/dist/cjs/Message/QuickResponse/index.js +17 -0
  60. package/dist/cjs/Message/QuickStarts/QuickStartTile.d.ts +1 -1
  61. package/dist/cjs/Message/QuickStarts/QuickStartTile.js +3 -2
  62. package/dist/cjs/Message/QuickStarts/index.d.ts +2 -0
  63. package/dist/cjs/Message/QuickStarts/index.js +18 -0
  64. package/dist/cjs/Message/TableMessage/TableMessage.d.ts +6 -1
  65. package/dist/cjs/Message/TableMessage/TableMessage.js +3 -2
  66. package/dist/cjs/Message/TextMessage/TextMessage.d.ts +8 -1
  67. package/dist/cjs/Message/TextMessage/TextMessage.js +3 -2
  68. package/dist/cjs/Message/UserFeedback/UserFeedback.d.ts +3 -1
  69. package/dist/cjs/Message/UserFeedback/UserFeedback.js +8 -6
  70. package/dist/cjs/Message/UserFeedback/UserFeedbackComplete.d.ts +1 -1
  71. package/dist/cjs/Message/UserFeedback/UserFeedbackComplete.js +3 -2
  72. package/dist/cjs/Message/UserFeedback/index.d.ts +2 -0
  73. package/dist/cjs/Message/UserFeedback/index.js +18 -0
  74. package/dist/cjs/Message/index.d.ts +8 -0
  75. package/dist/cjs/Message/index.js +8 -0
  76. package/dist/cjs/MessageBar/MessageBar.d.ts +4 -0
  77. package/dist/cjs/MessageBar/MessageBar.js +20 -5
  78. package/dist/cjs/MessageBar/MessageBar.test.js +8 -0
  79. package/dist/cjs/Onboarding/Onboarding.d.ts +36 -0
  80. package/dist/cjs/Onboarding/Onboarding.js +37 -0
  81. package/dist/cjs/Onboarding/Onboarding.test.d.ts +1 -0
  82. package/dist/cjs/Onboarding/Onboarding.test.js +80 -0
  83. package/dist/cjs/Onboarding/index.d.ts +2 -0
  84. package/dist/cjs/Onboarding/index.js +23 -0
  85. package/dist/cjs/ResponseActions/ResponseActions.d.ts +7 -0
  86. package/dist/cjs/ResponseActions/ResponseActions.js +28 -7
  87. package/dist/cjs/ResponseActions/ResponseActions.test.js +67 -12
  88. package/dist/cjs/ResponseActions/ResponseActionsGroups.d.ts +13 -0
  89. package/dist/cjs/ResponseActions/ResponseActionsGroups.js +22 -0
  90. package/dist/cjs/ResponseActions/ResponseActionsGroups.test.d.ts +1 -0
  91. package/dist/cjs/ResponseActions/ResponseActionsGroups.test.js +25 -0
  92. package/dist/cjs/ResponseActions/index.d.ts +1 -0
  93. package/dist/cjs/ResponseActions/index.js +1 -0
  94. package/dist/cjs/ToolCall/ToolCall.d.ts +11 -0
  95. package/dist/cjs/ToolCall/ToolCall.js +24 -3
  96. package/dist/cjs/ToolCall/ToolCall.test.js +57 -0
  97. package/dist/cjs/ToolResponse/ToolResponse.d.ts +17 -0
  98. package/dist/cjs/ToolResponse/ToolResponse.js +49 -3
  99. package/dist/cjs/ToolResponse/ToolResponse.test.js +100 -0
  100. package/dist/cjs/index.d.ts +4 -0
  101. package/dist/cjs/index.js +7 -1
  102. package/dist/css/main.css +264 -30
  103. package/dist/css/main.css.map +1 -1
  104. package/dist/dynamic/MarkdownContent/package.json +1 -0
  105. package/dist/dynamic/Onboarding/package.json +1 -0
  106. package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.d.ts +9 -1
  107. package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.js +10 -3
  108. package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.test.js +38 -0
  109. package/dist/esm/ChatbotHeader/ChatbotHeaderMenu.js +30 -3
  110. package/dist/esm/CodeModal/CodeModal.d.ts +2 -0
  111. package/dist/esm/CodeModal/CodeModal.js +54 -13
  112. package/dist/esm/DeepThinking/DeepThinking.d.ts +13 -0
  113. package/dist/esm/DeepThinking/DeepThinking.js +28 -3
  114. package/dist/esm/DeepThinking/DeepThinking.test.js +80 -0
  115. package/dist/esm/MarkdownContent/MarkdownContent.d.ts +44 -0
  116. package/dist/esm/MarkdownContent/MarkdownContent.js +174 -0
  117. package/dist/esm/MarkdownContent/MarkdownContent.test.d.ts +1 -0
  118. package/dist/esm/MarkdownContent/MarkdownContent.test.js +187 -0
  119. package/dist/esm/MarkdownContent/index.d.ts +2 -0
  120. package/dist/esm/MarkdownContent/index.js +2 -0
  121. package/dist/esm/Message/CodeBlockMessage/CodeBlockMessage.d.ts +3 -1
  122. package/dist/esm/Message/CodeBlockMessage/CodeBlockMessage.js +15 -4
  123. package/dist/esm/Message/CodeBlockMessage/CodeBlockMessage.test.d.ts +1 -0
  124. package/dist/esm/Message/CodeBlockMessage/CodeBlockMessage.test.js +126 -0
  125. package/dist/esm/Message/ErrorMessage/ErrorMessage.d.ts +15 -1
  126. package/dist/esm/Message/ErrorMessage/ErrorMessage.js +3 -3
  127. package/dist/esm/Message/ErrorMessage/ErrorMessage.test.d.ts +1 -0
  128. package/dist/esm/Message/ErrorMessage/ErrorMessage.test.js +25 -0
  129. package/dist/esm/Message/LinkMessage/LinkMessage.d.ts +5 -1
  130. package/dist/esm/Message/LinkMessage/LinkMessage.js +4 -3
  131. package/dist/esm/Message/ListMessage/OrderedListMessage.d.ts +9 -1
  132. package/dist/esm/Message/ListMessage/OrderedListMessage.js +2 -1
  133. package/dist/esm/Message/ListMessage/UnorderedListMessage.d.ts +7 -1
  134. package/dist/esm/Message/ListMessage/UnorderedListMessage.js +2 -1
  135. package/dist/esm/Message/Message.d.ts +20 -3
  136. package/dist/esm/Message/Message.js +8 -161
  137. package/dist/esm/Message/Message.test.js +129 -2
  138. package/dist/esm/Message/MessageAndActions/MessageAndActions.d.ts +14 -0
  139. package/dist/esm/Message/MessageAndActions/MessageAndActions.js +18 -0
  140. package/dist/esm/Message/MessageAndActions/MessageAndActions.test.d.ts +1 -0
  141. package/dist/esm/Message/MessageAndActions/MessageAndActions.test.js +20 -0
  142. package/dist/esm/Message/MessageAndActions/index.d.ts +1 -0
  143. package/dist/esm/Message/MessageAndActions/index.js +1 -0
  144. package/dist/esm/Message/MessageAttachments/MessageAttachmentItem.d.ts +13 -0
  145. package/dist/esm/Message/MessageAttachments/MessageAttachmentItem.js +18 -0
  146. package/dist/esm/Message/MessageAttachments/MessageAttachmentItem.test.d.ts +1 -0
  147. package/dist/esm/Message/MessageAttachments/MessageAttachmentItem.test.js +20 -0
  148. package/dist/esm/Message/MessageAttachments/MessageAttachmentsContainer.d.ts +13 -0
  149. package/dist/esm/Message/MessageAttachments/MessageAttachmentsContainer.js +18 -0
  150. package/dist/esm/Message/MessageAttachments/MessageAttachmentsContainer.test.d.ts +1 -0
  151. package/dist/esm/Message/MessageAttachments/MessageAttachmentsContainer.test.js +20 -0
  152. package/dist/esm/Message/MessageAttachments/index.d.ts +2 -0
  153. package/dist/esm/Message/MessageAttachments/index.js +2 -0
  154. package/dist/esm/Message/MessageInput.d.ts +1 -1
  155. package/dist/esm/Message/MessageInput.js +1 -1
  156. package/dist/esm/Message/MessageLoading.d.ts +13 -4
  157. package/dist/esm/Message/MessageLoading.js +16 -4
  158. package/dist/esm/Message/MessageLoading.test.d.ts +1 -0
  159. package/dist/esm/Message/MessageLoading.test.js +20 -0
  160. package/dist/esm/Message/QuickResponse/QuickResponse.js +3 -2
  161. package/dist/esm/Message/QuickResponse/QuickResponse.test.d.ts +1 -0
  162. package/dist/esm/Message/QuickResponse/QuickResponse.test.js +104 -0
  163. package/dist/esm/Message/QuickResponse/index.d.ts +1 -0
  164. package/dist/esm/Message/QuickResponse/index.js +1 -0
  165. package/dist/esm/Message/QuickStarts/QuickStartTile.d.ts +1 -1
  166. package/dist/esm/Message/QuickStarts/QuickStartTile.js +1 -1
  167. package/dist/esm/Message/QuickStarts/index.d.ts +2 -0
  168. package/dist/esm/Message/QuickStarts/index.js +2 -0
  169. package/dist/esm/Message/TableMessage/TableMessage.d.ts +6 -1
  170. package/dist/esm/Message/TableMessage/TableMessage.js +3 -2
  171. package/dist/esm/Message/TextMessage/TextMessage.d.ts +8 -1
  172. package/dist/esm/Message/TextMessage/TextMessage.js +3 -2
  173. package/dist/esm/Message/UserFeedback/UserFeedback.d.ts +3 -1
  174. package/dist/esm/Message/UserFeedback/UserFeedback.js +7 -7
  175. package/dist/esm/Message/UserFeedback/UserFeedbackComplete.d.ts +1 -1
  176. package/dist/esm/Message/UserFeedback/UserFeedbackComplete.js +1 -2
  177. package/dist/esm/Message/UserFeedback/index.d.ts +2 -0
  178. package/dist/esm/Message/UserFeedback/index.js +2 -0
  179. package/dist/esm/Message/index.d.ts +8 -0
  180. package/dist/esm/Message/index.js +8 -0
  181. package/dist/esm/MessageBar/MessageBar.d.ts +4 -0
  182. package/dist/esm/MessageBar/MessageBar.js +20 -5
  183. package/dist/esm/MessageBar/MessageBar.test.js +8 -0
  184. package/dist/esm/Onboarding/Onboarding.d.ts +36 -0
  185. package/dist/esm/Onboarding/Onboarding.js +30 -0
  186. package/dist/esm/Onboarding/Onboarding.test.d.ts +1 -0
  187. package/dist/esm/Onboarding/Onboarding.test.js +75 -0
  188. package/dist/esm/Onboarding/index.d.ts +2 -0
  189. package/dist/esm/Onboarding/index.js +2 -0
  190. package/dist/esm/ResponseActions/ResponseActions.d.ts +7 -0
  191. package/dist/esm/ResponseActions/ResponseActions.js +28 -7
  192. package/dist/esm/ResponseActions/ResponseActions.test.js +67 -12
  193. package/dist/esm/ResponseActions/ResponseActionsGroups.d.ts +13 -0
  194. package/dist/esm/ResponseActions/ResponseActionsGroups.js +18 -0
  195. package/dist/esm/ResponseActions/ResponseActionsGroups.test.d.ts +1 -0
  196. package/dist/esm/ResponseActions/ResponseActionsGroups.test.js +20 -0
  197. package/dist/esm/ResponseActions/index.d.ts +1 -0
  198. package/dist/esm/ResponseActions/index.js +1 -0
  199. package/dist/esm/ToolCall/ToolCall.d.ts +11 -0
  200. package/dist/esm/ToolCall/ToolCall.js +21 -3
  201. package/dist/esm/ToolCall/ToolCall.test.js +57 -0
  202. package/dist/esm/ToolResponse/ToolResponse.d.ts +17 -0
  203. package/dist/esm/ToolResponse/ToolResponse.js +46 -3
  204. package/dist/esm/ToolResponse/ToolResponse.test.js +100 -0
  205. package/dist/esm/index.d.ts +4 -0
  206. package/dist/esm/index.js +4 -0
  207. package/dist/tsconfig.tsbuildinfo +1 -1
  208. package/package.json +13 -3
  209. package/patternfly-docs/content/extensions/chatbot/chatbot.md +57 -0
  210. package/patternfly-docs/content/extensions/chatbot/design-guidelines.md +12 -12
  211. package/patternfly-docs/content/extensions/chatbot/examples/Analytics/Analytics.md +1 -1
  212. package/patternfly-docs/content/extensions/chatbot/examples/Customizing Messages/Customizing Messages.md +1 -1
  213. package/patternfly-docs/content/extensions/chatbot/examples/Messages/BotMessage.tsx +1 -0
  214. package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithCustomStructure.tsx +102 -0
  215. package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithDeepThinking.tsx +25 -11
  216. package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithFeedback.tsx +14 -1
  217. package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithMarkdownDeepThinking.tsx +26 -0
  218. package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithMarkdownToolCall.tsx +29 -0
  219. package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithMarkdownToolResponse.tsx +200 -0
  220. package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithMultipleActionGroups.tsx +61 -0
  221. package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithPersistedActions.tsx +22 -0
  222. package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithQuickResponses.tsx +11 -0
  223. package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithToolCall.tsx +14 -1
  224. package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithToolResponse.tsx +222 -105
  225. package/patternfly-docs/content/extensions/chatbot/examples/Messages/Messages.md +123 -14
  226. package/patternfly-docs/content/extensions/chatbot/examples/Messages/UserMessage.tsx +1 -0
  227. package/patternfly-docs/content/extensions/chatbot/examples/UI/ChatbotHeaderDrawerWithSearchActions.tsx +198 -0
  228. package/patternfly-docs/content/extensions/chatbot/examples/UI/ChatbotMessageBarIndicatorThinking.tsx +15 -0
  229. package/patternfly-docs/content/extensions/chatbot/examples/UI/CompactOnboarding.tsx +141 -0
  230. package/patternfly-docs/content/extensions/chatbot/examples/UI/Onboarding.tsx +151 -0
  231. package/patternfly-docs/content/extensions/chatbot/examples/UI/RH-Hat-Image.svg +9 -0
  232. package/patternfly-docs/content/extensions/chatbot/examples/UI/UI.md +67 -29
  233. package/patternfly-docs/content/extensions/chatbot/examples/demos/AttachmentDemos.md +18 -18
  234. package/patternfly-docs/content/extensions/chatbot/examples/demos/Chatbot.md +33 -19
  235. package/patternfly-docs/patternfly-docs.config.js +1 -1
  236. package/patternfly-docs/patternfly-docs.source.js +1 -1
  237. package/src/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.scss +43 -0
  238. package/src/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.test.tsx +95 -0
  239. package/src/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.tsx +51 -15
  240. package/src/ChatbotHeader/ChatbotHeaderMenu.tsx +56 -14
  241. package/src/ChatbotModal/ChatbotModal.scss +3 -0
  242. package/src/CodeModal/CodeModal.tsx +71 -26
  243. package/src/DeepThinking/DeepThinking.scss +1 -1
  244. package/src/DeepThinking/DeepThinking.test.tsx +109 -0
  245. package/src/DeepThinking/DeepThinking.tsx +54 -5
  246. package/src/MarkdownContent/MarkdownContent.test.tsx +207 -0
  247. package/src/MarkdownContent/MarkdownContent.tsx +269 -0
  248. package/src/MarkdownContent/index.ts +2 -0
  249. package/src/Message/CodeBlockMessage/CodeBlockMessage.scss +13 -0
  250. package/src/Message/CodeBlockMessage/CodeBlockMessage.test.tsx +171 -0
  251. package/src/Message/CodeBlockMessage/CodeBlockMessage.tsx +17 -4
  252. package/src/Message/ErrorMessage/ErrorMessage.test.tsx +38 -0
  253. package/src/Message/ErrorMessage/ErrorMessage.tsx +17 -2
  254. package/src/Message/LinkMessage/LinkMessage.scss +5 -0
  255. package/src/Message/LinkMessage/LinkMessage.tsx +24 -2
  256. package/src/Message/ListMessage/ListMessage.scss +8 -0
  257. package/src/Message/ListMessage/OrderedListMessage.tsx +16 -2
  258. package/src/Message/ListMessage/UnorderedListMessage.tsx +12 -2
  259. package/src/Message/Message.scss +11 -7
  260. package/src/Message/Message.test.tsx +202 -2
  261. package/src/Message/Message.tsx +129 -241
  262. package/src/Message/MessageAndActions/MessageAndActions.test.tsx +23 -0
  263. package/src/Message/MessageAndActions/MessageAndActions.tsx +22 -0
  264. package/src/Message/MessageAndActions/index.ts +1 -0
  265. package/src/Message/MessageAttachments/MessageAttachmentItem.test.tsx +23 -0
  266. package/src/Message/MessageAttachments/MessageAttachmentItem.tsx +25 -0
  267. package/src/Message/MessageAttachments/MessageAttachmentsContainer.test.tsx +23 -0
  268. package/src/Message/MessageAttachments/MessageAttachmentsContainer.tsx +25 -0
  269. package/src/Message/MessageAttachments/index.ts +2 -0
  270. package/src/Message/MessageInput.tsx +1 -1
  271. package/src/Message/MessageLoading.test.tsx +23 -0
  272. package/src/Message/MessageLoading.tsx +17 -2
  273. package/src/Message/QuickResponse/QuickResponse.scss +3 -1
  274. package/src/Message/QuickResponse/QuickResponse.test.tsx +131 -0
  275. package/src/Message/QuickResponse/QuickResponse.tsx +3 -2
  276. package/src/Message/QuickResponse/index.ts +1 -0
  277. package/src/Message/QuickStarts/QuickStartTile.tsx +1 -1
  278. package/src/Message/QuickStarts/index.ts +2 -0
  279. package/src/Message/TableMessage/TableMessage.scss +13 -1
  280. package/src/Message/TableMessage/TableMessage.tsx +18 -2
  281. package/src/Message/TextMessage/TextMessage.scss +12 -0
  282. package/src/Message/TextMessage/TextMessage.tsx +29 -2
  283. package/src/Message/UserFeedback/UserFeedback.scss +28 -1
  284. package/src/Message/UserFeedback/UserFeedback.tsx +23 -13
  285. package/src/Message/UserFeedback/UserFeedbackComplete.tsx +1 -4
  286. package/src/Message/UserFeedback/index.ts +2 -0
  287. package/src/Message/index.ts +8 -0
  288. package/src/MessageBar/AttachButton.scss +0 -1
  289. package/src/MessageBar/MessageBar.scss +48 -6
  290. package/src/MessageBar/MessageBar.test.tsx +12 -0
  291. package/src/MessageBar/MessageBar.tsx +38 -4
  292. package/src/MessageBar/MicrophoneButton.scss +0 -1
  293. package/src/MessageBar/SendButton.scss +0 -1
  294. package/src/MessageBar/StopButton.scss +0 -1
  295. package/src/Onboarding/Onboarding.scss +101 -0
  296. package/src/Onboarding/Onboarding.test.tsx +148 -0
  297. package/src/Onboarding/Onboarding.tsx +126 -0
  298. package/src/Onboarding/index.ts +3 -0
  299. package/src/ResponseActions/ResponseActions.scss +12 -1
  300. package/src/ResponseActions/ResponseActions.test.tsx +111 -12
  301. package/src/ResponseActions/ResponseActions.tsx +44 -10
  302. package/src/ResponseActions/ResponseActionsGroups.test.tsx +23 -0
  303. package/src/ResponseActions/ResponseActionsGroups.tsx +28 -0
  304. package/src/ResponseActions/index.ts +1 -0
  305. package/src/ToolCall/ToolCall.scss +1 -1
  306. package/src/ToolCall/ToolCall.test.tsx +91 -0
  307. package/src/ToolCall/ToolCall.tsx +49 -4
  308. package/src/ToolResponse/ToolResponse.scss +13 -3
  309. package/src/ToolResponse/ToolResponse.test.tsx +119 -0
  310. package/src/ToolResponse/ToolResponse.tsx +82 -7
  311. package/src/index.ts +6 -0
  312. package/src/main.scss +2 -0
  313. package/tsconfig.json +1 -1
  314. package/patternfly-docs/content/extensions/chatbot/about-chatbot.md +0 -44
@@ -0,0 +1,207 @@
1
+ import { render, screen } from '@testing-library/react';
2
+ import '@testing-library/jest-dom';
3
+ import MarkdownContent from './MarkdownContent';
4
+ import rehypeExternalLinks from '../__mocks__/rehype-external-links';
5
+
6
+ const BOLD_TEXT = '**Bold text**';
7
+ const ITALIC_TEXT = '*Italic text*';
8
+ const INLINE_CODE = 'Here is inline code: `const x = 5`';
9
+ const CODE_BLOCK = `\`\`\`javascript
10
+ function hello() {
11
+ console.log('Hello, world!');
12
+ }
13
+ \`\`\``;
14
+ const HEADING = '# Heading 1';
15
+ const LINK = '[PatternFly](https://www.patternfly.org/)';
16
+ const UNORDERED_LIST = `
17
+ * Item 1
18
+ * Item 2
19
+ * Item 3
20
+ `;
21
+ const ORDERED_LIST = `
22
+ 1. First item
23
+ 2. Second item
24
+ 3. Third item
25
+ `;
26
+ const TABLE = `
27
+ | Column 1 | Column 2 |
28
+ |----------|----------|
29
+ | Cell 1 | Cell 2 |
30
+ | Cell 3 | Cell 4 |
31
+ `;
32
+ const BLOCKQUOTE = '> This is a blockquote';
33
+ const IMAGE = '![Alt text](https://example.com/image.png)';
34
+
35
+ describe('MarkdownContent', () => {
36
+ beforeEach(() => {
37
+ jest.clearAllMocks();
38
+ });
39
+
40
+ it('should render bold text correctly', () => {
41
+ const { container } = render(<MarkdownContent content={BOLD_TEXT} />);
42
+ expect(container.querySelector('strong')).toBeTruthy();
43
+ expect(screen.getByText('Bold text')).toBeTruthy();
44
+ });
45
+
46
+ it('should render italic text correctly', () => {
47
+ const { container } = render(<MarkdownContent content={ITALIC_TEXT} />);
48
+ expect(container.querySelector('em')).toBeTruthy();
49
+ expect(screen.getByText('Italic text')).toBeTruthy();
50
+ });
51
+
52
+ it('should render inline code correctly', () => {
53
+ render(<MarkdownContent content={INLINE_CODE} />);
54
+ expect(screen.getByText(/const x = 5/)).toBeTruthy();
55
+ });
56
+
57
+ it('should render code blocks correctly', () => {
58
+ render(<MarkdownContent content={CODE_BLOCK} />);
59
+
60
+ expect(screen.getByText(/function hello/)).toBeVisible();
61
+ expect(screen.getByText(/console.log/)).toBeVisible();
62
+ expect(screen.getByRole('button', { name: 'Copy code' })).toBeVisible();
63
+ });
64
+
65
+ it('should render headings correctly', () => {
66
+ render(<MarkdownContent content={HEADING} />);
67
+ expect(screen.getByRole('heading', { name: /Heading 1/i })).toBeTruthy();
68
+ });
69
+
70
+ it('should render links correctly', () => {
71
+ render(<MarkdownContent content={LINK} />);
72
+ expect(screen.getByRole('link', { name: /PatternFly/i })).toBeTruthy();
73
+ });
74
+
75
+ it('should render unordered lists correctly', () => {
76
+ render(<MarkdownContent content={UNORDERED_LIST} />);
77
+ expect(screen.getByText('Item 1')).toBeTruthy();
78
+ expect(screen.getByText('Item 2')).toBeTruthy();
79
+ expect(screen.getByText('Item 3')).toBeTruthy();
80
+ expect(screen.getAllByRole('listitem')).toHaveLength(3);
81
+ });
82
+
83
+ it('should render ordered lists correctly', () => {
84
+ render(<MarkdownContent content={ORDERED_LIST} />);
85
+ expect(screen.getByText('First item')).toBeTruthy();
86
+ expect(screen.getByText('Second item')).toBeTruthy();
87
+ expect(screen.getByText('Third item')).toBeTruthy();
88
+ expect(screen.getAllByRole('listitem')).toHaveLength(3);
89
+ });
90
+
91
+ it('should render tables correctly', () => {
92
+ render(<MarkdownContent content={TABLE} tableProps={{ 'aria-label': 'Test table' }} />);
93
+ expect(screen.getByRole('grid', { name: /Test table/i })).toBeTruthy();
94
+ expect(screen.getByRole('columnheader', { name: /Column 1/i })).toBeTruthy();
95
+ expect(screen.getByRole('columnheader', { name: /Column 2/i })).toBeTruthy();
96
+ expect(screen.getByRole('cell', { name: /Cell 1/i })).toBeTruthy();
97
+ expect(screen.getByRole('cell', { name: /Cell 2/i })).toBeTruthy();
98
+ });
99
+
100
+ it('should render blockquotes correctly', () => {
101
+ render(<MarkdownContent content={BLOCKQUOTE} />);
102
+
103
+ const quote = screen.getByText(/This is a blockquote/);
104
+ expect(quote).toBeVisible();
105
+ expect(quote.closest('.pf-v6-c-content--blockquote')?.tagName).toBe('BLOCKQUOTE');
106
+ });
107
+
108
+ it('should render images when hasNoImages is false', () => {
109
+ render(<MarkdownContent content={IMAGE} hasNoImages={false} />);
110
+ expect(screen.getByRole('img', { name: /Alt text/i })).toBeTruthy();
111
+ });
112
+
113
+ it('should not render images when hasNoImages is true', () => {
114
+ render(<MarkdownContent content={IMAGE} hasNoImages />);
115
+ expect(screen.queryByRole('img', { name: /Alt text/i })).toBeFalsy();
116
+ });
117
+
118
+ it('should disable markdown rendering when isMarkdownDisabled is true', () => {
119
+ render(<MarkdownContent content={BOLD_TEXT} isMarkdownDisabled />);
120
+ expect(screen.getByText('**Bold text**')).toBeTruthy();
121
+ });
122
+
123
+ it('should render text component when isMarkdownDisabled is true and textComponent is provided', () => {
124
+ const textComponent = <div data-testid="custom-text">Custom text component</div>;
125
+ render(<MarkdownContent content={BOLD_TEXT} isMarkdownDisabled textComponent={textComponent} />);
126
+ expect(screen.getByTestId('custom-text')).toBeTruthy();
127
+ expect(screen.getByText('Custom text component')).toBeTruthy();
128
+ });
129
+
130
+ it('should apply isPrimary prop to elements', () => {
131
+ const { container } = render(<MarkdownContent content={INLINE_CODE} isPrimary />);
132
+ expect(container.querySelector('.pf-m-primary')).toBeTruthy();
133
+ });
134
+
135
+ it('should apply shouldRetainStyles prop to elements', () => {
136
+ const { container } = render(<MarkdownContent content={BOLD_TEXT} shouldRetainStyles />);
137
+ expect(container.querySelector('.pf-m-markdown')).toBeTruthy();
138
+ });
139
+
140
+ it('should pass codeBlockProps to code blocks', () => {
141
+ render(<MarkdownContent content={CODE_BLOCK} codeBlockProps={{ 'aria-label': 'Custom code block' }} />);
142
+ expect(screen.getByRole('button', { name: /Custom code block/i })).toBeTruthy();
143
+ });
144
+
145
+ it('should pass tableProps to tables', () => {
146
+ render(<MarkdownContent content={TABLE} tableProps={{ 'aria-label': 'Custom table label' }} />);
147
+ expect(screen.getByRole('grid', { name: /Custom table label/i })).toBeTruthy();
148
+ });
149
+
150
+ it('should open links in new tab when openLinkInNewTab is true', () => {
151
+ render(<MarkdownContent content={LINK} openLinkInNewTab />);
152
+ expect(rehypeExternalLinks).toHaveBeenCalledTimes(1);
153
+ });
154
+
155
+ it('should not open links in new tab when openLinkInNewTab is false', () => {
156
+ render(<MarkdownContent content={LINK} openLinkInNewTab={false} />);
157
+ expect(rehypeExternalLinks).not.toHaveBeenCalled();
158
+ });
159
+
160
+ it('should pass linkProps to links', async () => {
161
+ const onClick = jest.fn();
162
+ render(<MarkdownContent content={LINK} linkProps={{ onClick }} />);
163
+ const link = screen.getByRole('link', { name: /PatternFly/i });
164
+ link.click();
165
+ expect(onClick).toHaveBeenCalledTimes(1);
166
+ });
167
+
168
+ it('should handle reactMarkdownProps.disallowedElements', () => {
169
+ render(<MarkdownContent content={CODE_BLOCK} reactMarkdownProps={{ disallowedElements: ['code'] }} />);
170
+ // Code block should not render when disallowed
171
+ expect(screen.queryByRole('button', { name: /Copy code/i })).toBeFalsy();
172
+ });
173
+
174
+ it('should render plain text when no markdown is present', () => {
175
+ render(<MarkdownContent content="Plain text without markdown" />);
176
+ expect(screen.getByText('Plain text without markdown')).toBeTruthy();
177
+ });
178
+
179
+ it('should handle empty content', () => {
180
+ const { container } = render(<MarkdownContent content="" />);
181
+ expect(container.textContent).toBe('');
182
+ });
183
+
184
+ it('should handle undefined content', () => {
185
+ const { container } = render(<MarkdownContent />);
186
+ expect(container.textContent).toBe('');
187
+ });
188
+
189
+ it('should render multiple markdown elements together', () => {
190
+ const content = `# Heading
191
+
192
+ **Bold text** and *italic text*
193
+
194
+ \`\`\`javascript
195
+ const x = 5;
196
+ \`\`\`
197
+
198
+ [Link](https://example.com)`;
199
+
200
+ render(<MarkdownContent content={content} />);
201
+ expect(screen.getByRole('heading', { name: /Heading/i })).toBeTruthy();
202
+ expect(screen.getByText('Bold text')).toBeTruthy();
203
+ expect(screen.getByText('italic text')).toBeTruthy();
204
+ expect(screen.getByText(/const x = 5/)).toBeTruthy();
205
+ expect(screen.getByRole('link', { name: /Link/i })).toBeTruthy();
206
+ });
207
+ });
@@ -0,0 +1,269 @@
1
+ // ============================================================================
2
+ // Markdown Content - Shared component for rendering markdown
3
+ // ============================================================================
4
+ import { type FunctionComponent, ReactNode } from 'react';
5
+ import Markdown, { Options } from 'react-markdown';
6
+ import remarkGfm from 'remark-gfm';
7
+ import { ContentVariants } from '@patternfly/react-core';
8
+ import CodeBlockMessage, { CodeBlockMessageProps } from '../Message/CodeBlockMessage/CodeBlockMessage';
9
+ import TextMessage from '../Message/TextMessage/TextMessage';
10
+ import ListItemMessage from '../Message/ListMessage/ListItemMessage';
11
+ import UnorderedListMessage from '../Message/ListMessage/UnorderedListMessage';
12
+ import OrderedListMessage from '../Message/ListMessage/OrderedListMessage';
13
+ import TableMessage from '../Message/TableMessage/TableMessage';
14
+ import TrMessage from '../Message/TableMessage/TrMessage';
15
+ import TdMessage from '../Message/TableMessage/TdMessage';
16
+ import TbodyMessage from '../Message/TableMessage/TbodyMessage';
17
+ import TheadMessage from '../Message/TableMessage/TheadMessage';
18
+ import ThMessage from '../Message/TableMessage/ThMessage';
19
+ import { TableProps } from '@patternfly/react-table';
20
+ import ImageMessage from '../Message/ImageMessage/ImageMessage';
21
+ import rehypeUnwrapImages from 'rehype-unwrap-images';
22
+ import rehypeExternalLinks from 'rehype-external-links';
23
+ import rehypeSanitize from 'rehype-sanitize';
24
+ import rehypeHighlight from 'rehype-highlight';
25
+ import 'highlight.js/styles/vs2015.css';
26
+ import { PluggableList } from 'unified';
27
+ import LinkMessage from '../Message/LinkMessage/LinkMessage';
28
+ import { rehypeMoveImagesOutOfParagraphs } from '../Message/Plugins/rehypeMoveImagesOutOfParagraphs';
29
+ import SuperscriptMessage from '../Message/SuperscriptMessage/SuperscriptMessage';
30
+ import { ButtonProps } from '@patternfly/react-core';
31
+ import { css } from '@patternfly/react-styles';
32
+
33
+ /**
34
+ * MarkdownContent renders content either as plain text or with content with markdown support.
35
+ *
36
+ * Use this component when passing children to Message to customize its structure.
37
+ */
38
+ export interface MarkdownContentProps {
39
+ /** The content to render. Supports markdown formatting by default, or plain text when isMarkdownDisabled is true. */
40
+ content?: string;
41
+ /** Disables markdown parsing, allowing only plain text input */
42
+ isMarkdownDisabled?: boolean;
43
+ /** Props for code blocks */
44
+ codeBlockProps?: CodeBlockMessageProps;
45
+ /** Props for table message. It is important to include a detailed aria-label that describes the purpose of the table. */
46
+ tableProps?: Required<Pick<TableProps, 'aria-label'>> & TableProps;
47
+ /** Additional rehype plugins passed from the consumer */
48
+ additionalRehypePlugins?: PluggableList;
49
+ /** Additional remark plugins passed from the consumer */
50
+ additionalRemarkPlugins?: PluggableList;
51
+ /** Whether to open links in message in new tab. */
52
+ openLinkInNewTab?: boolean;
53
+ /** Props for links */
54
+ linkProps?: ButtonProps;
55
+ /** Allows passing additional props down to markdown parser react-markdown, such as allowedElements and disallowedElements. See https://github.com/remarkjs/react-markdown?tab=readme-ov-file#options for options */
56
+ reactMarkdownProps?: Options;
57
+ /** Allows passing additional props down to remark-gfm. See https://github.com/remarkjs/remark-gfm?tab=readme-ov-file#options for options */
58
+ remarkGfmProps?: Options;
59
+ /** Whether to strip out images in markdown */
60
+ hasNoImages?: boolean;
61
+ /** Sets background colors to be appropriate on primary chatbot background */
62
+ isPrimary?: boolean;
63
+ /** Custom component to render when markdown is disabled */
64
+ textComponent?: ReactNode;
65
+ /** Flag indicating whether content should retain various styles of its context (typically font-size and text color). */
66
+ shouldRetainStyles?: boolean;
67
+ }
68
+
69
+ export const MarkdownContent: FunctionComponent<MarkdownContentProps> = ({
70
+ content,
71
+ isMarkdownDisabled,
72
+ codeBlockProps,
73
+ tableProps,
74
+ openLinkInNewTab = true,
75
+ additionalRehypePlugins = [],
76
+ additionalRemarkPlugins = [],
77
+ linkProps,
78
+ reactMarkdownProps,
79
+ remarkGfmProps,
80
+ hasNoImages = false,
81
+ isPrimary,
82
+ textComponent,
83
+ shouldRetainStyles
84
+ }: MarkdownContentProps) => {
85
+ let rehypePlugins: PluggableList = [rehypeUnwrapImages, rehypeMoveImagesOutOfParagraphs, rehypeHighlight];
86
+ if (openLinkInNewTab) {
87
+ rehypePlugins = rehypePlugins.concat([[rehypeExternalLinks, { target: '_blank' }, rehypeSanitize]]);
88
+ }
89
+ if (additionalRehypePlugins) {
90
+ rehypePlugins.push(...additionalRehypePlugins);
91
+ }
92
+
93
+ const disallowedElements = hasNoImages ? ['img'] : [];
94
+ if (reactMarkdownProps && reactMarkdownProps.disallowedElements) {
95
+ disallowedElements.push(...reactMarkdownProps.disallowedElements);
96
+ }
97
+
98
+ if (isMarkdownDisabled) {
99
+ if (textComponent) {
100
+ return <>{textComponent}</>;
101
+ }
102
+ return (
103
+ <TextMessage component={ContentVariants.p} isPrimary={isPrimary}>
104
+ {content}
105
+ </TextMessage>
106
+ );
107
+ }
108
+
109
+ return (
110
+ <Markdown
111
+ components={{
112
+ section: (props) => {
113
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
114
+ const { node, ...rest } = props;
115
+ return (
116
+ <section
117
+ {...rest}
118
+ className={css('pf-chatbot__message-text', shouldRetainStyles && 'pf-m-markdown', rest?.className)}
119
+ />
120
+ );
121
+ },
122
+ p: (props) => {
123
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
124
+ const { node, ...rest } = props;
125
+ return (
126
+ <TextMessage
127
+ shouldRetainStyles={shouldRetainStyles}
128
+ component={ContentVariants.p}
129
+ {...rest}
130
+ isPrimary={isPrimary}
131
+ />
132
+ );
133
+ },
134
+ code: ({ children, ...props }) => {
135
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
136
+ const { node, ...codeProps } = props;
137
+ return (
138
+ <CodeBlockMessage
139
+ {...codeProps}
140
+ {...codeBlockProps}
141
+ isPrimary={isPrimary}
142
+ shouldRetainStyles={shouldRetainStyles}
143
+ >
144
+ {children}
145
+ </CodeBlockMessage>
146
+ );
147
+ },
148
+ h1: (props) => {
149
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
150
+ const { node, ...rest } = props;
151
+ return <TextMessage shouldRetainStyles={shouldRetainStyles} component={ContentVariants.h1} {...rest} />;
152
+ },
153
+ h2: (props) => {
154
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
155
+ const { node, ...rest } = props;
156
+ return <TextMessage shouldRetainStyles={shouldRetainStyles} component={ContentVariants.h2} {...rest} />;
157
+ },
158
+ h3: (props) => {
159
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
160
+ const { node, ...rest } = props;
161
+ return <TextMessage shouldRetainStyles={shouldRetainStyles} component={ContentVariants.h3} {...rest} />;
162
+ },
163
+ h4: (props) => {
164
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
165
+ const { node, ...rest } = props;
166
+ return <TextMessage shouldRetainStyles={shouldRetainStyles} component={ContentVariants.h4} {...rest} />;
167
+ },
168
+ h5: (props) => {
169
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
170
+ const { node, ...rest } = props;
171
+ return <TextMessage shouldRetainStyles={shouldRetainStyles} component={ContentVariants.h5} {...rest} />;
172
+ },
173
+ h6: (props) => {
174
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
175
+ const { node, ...rest } = props;
176
+ return <TextMessage shouldRetainStyles={shouldRetainStyles} component={ContentVariants.h6} {...rest} />;
177
+ },
178
+ blockquote: (props) => {
179
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
180
+ const { node, ...rest } = props;
181
+ return (
182
+ <TextMessage shouldRetainStyles={shouldRetainStyles} component={ContentVariants.blockquote} {...rest} />
183
+ );
184
+ },
185
+ ul: (props) => {
186
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
187
+ const { node, ...rest } = props;
188
+ return <UnorderedListMessage shouldRetainStyles={shouldRetainStyles} {...rest} />;
189
+ },
190
+ ol: (props) => {
191
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
192
+ const { node, ...rest } = props;
193
+ return <OrderedListMessage shouldRetainStyles={shouldRetainStyles} {...rest} />;
194
+ },
195
+ li: (props) => {
196
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
197
+ const { node, ...rest } = props;
198
+ return <ListItemMessage {...rest} />;
199
+ },
200
+ // table requires node attribute for calculating headers for mobile breakpoint
201
+ table: (props) => (
202
+ <TableMessage shouldRetainStyles={shouldRetainStyles} {...props} {...tableProps} isPrimary={isPrimary} />
203
+ ),
204
+ tbody: (props) => {
205
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
206
+ const { node, ...rest } = props;
207
+ return <TbodyMessage {...rest} />;
208
+ },
209
+ thead: (props) => {
210
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
211
+ const { node, ...rest } = props;
212
+ return <TheadMessage {...rest} />;
213
+ },
214
+ tr: (props) => {
215
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
216
+ const { node, ...rest } = props;
217
+ return <TrMessage {...rest} />;
218
+ },
219
+ td: (props) => {
220
+ // Conflicts with Td type
221
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
222
+ const { node, width, ...rest } = props;
223
+ return <TdMessage {...rest} />;
224
+ },
225
+ th: (props) => {
226
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
227
+ const { node, ...rest } = props;
228
+ return <ThMessage {...rest} />;
229
+ },
230
+ img: (props) => {
231
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
232
+ const { node, ...rest } = props;
233
+ return <ImageMessage {...rest} />;
234
+ },
235
+ a: (props) => {
236
+ // node is just the details of the document structure - not needed
237
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
238
+ const { node, ...rest } = props;
239
+ return (
240
+ // some a types conflict with ButtonProps, but it's ok because we are using an a tag
241
+ // there are too many to handle manually
242
+ <LinkMessage shouldRetainStyles={shouldRetainStyles} {...(rest as any)} {...linkProps}>
243
+ {props.children}
244
+ </LinkMessage>
245
+ );
246
+ },
247
+ // used for footnotes
248
+ sup: (props) => {
249
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
250
+ const { node, ...rest } = props;
251
+ return <SuperscriptMessage {...rest} />;
252
+ }
253
+ }}
254
+ remarkPlugins={[[remarkGfm, { ...remarkGfmProps }], ...additionalRemarkPlugins]}
255
+ rehypePlugins={rehypePlugins}
256
+ {...reactMarkdownProps}
257
+ remarkRehypeOptions={{
258
+ // removes sr-only class from footnote labels applied by default
259
+ footnoteLabelProperties: { className: [''] },
260
+ ...reactMarkdownProps?.remarkRehypeOptions
261
+ }}
262
+ disallowedElements={disallowedElements}
263
+ >
264
+ {content}
265
+ </Markdown>
266
+ );
267
+ };
268
+
269
+ export default MarkdownContent;
@@ -0,0 +1,2 @@
1
+ export { default } from './MarkdownContent';
2
+ export * from './MarkdownContent';
@@ -75,12 +75,25 @@
75
75
  overflow: hidden !important;
76
76
  }
77
77
  }
78
+
79
+ &.pf-m-markdown .pf-v6-c-code-block__code {
80
+ font-size: inherit;
81
+ }
78
82
  }
79
83
 
80
84
  .pf-chatbot__message-inline-code {
81
85
  --pf-chatbot-message-text-inline-code-font-size: var(--pf-t--global--font--size--body--default);
86
+
82
87
  background-color: var(--pf-t--global--background--color--tertiary--default);
83
88
  font-size: var(--pf-chatbot-message-text-inline-code-font-size);
89
+ border: var(--pf-t--global--border--width--high-contrast--regular) solid
90
+ var(--pf-t--global--border--color--high-contrast);
91
+ border-radius: var(--pf-t--global--border--radius--small);
92
+ padding-block-start: var(--pf-t--global--spacer--xs);
93
+ padding-block-end: var(--pf-t--global--spacer--xs);
94
+ padding-inline-start: var(--pf-t--global--spacer--xs);
95
+ padding-inline-end: var(--pf-t--global--spacer--xs);
96
+ word-break: break-word;
84
97
 
85
98
  &.pf-m-primary {
86
99
  background-color: var(--pf-t--global--background--color--secondary--default);
@@ -0,0 +1,171 @@
1
+ import '@testing-library/jest-dom';
2
+ import { render, screen } from '@testing-library/react';
3
+ import userEvent from '@testing-library/user-event';
4
+ import CodeBlockMessage from './CodeBlockMessage';
5
+
6
+ // Mock clipboard API
7
+ Object.assign(navigator, {
8
+ clipboard: {
9
+ writeText: jest.fn()
10
+ }
11
+ });
12
+
13
+ describe('CodeBlockMessage', () => {
14
+ beforeEach(() => {
15
+ jest.clearAllMocks();
16
+ });
17
+
18
+ it('should render inline code for single-line content', () => {
19
+ render(<CodeBlockMessage className="language-javascript">const x = 5;</CodeBlockMessage>);
20
+ const code = screen.getByText('const x = 5;');
21
+ expect(code.tagName).toBe('CODE');
22
+ expect(code).toHaveClass('pf-chatbot__message-inline-code');
23
+ });
24
+
25
+ it('should render code block for multi-line content', () => {
26
+ const multilineCode = 'const x = 5;\nconst y = 10;';
27
+ const { container } = render(<CodeBlockMessage className="language-javascript">{multilineCode}</CodeBlockMessage>);
28
+ const codeElement = container.querySelector('code');
29
+ expect(codeElement?.textContent).toBe(multilineCode);
30
+ });
31
+
32
+ it('should display language label', () => {
33
+ const code = 'const x = 5;\nconst y = 10;';
34
+ render(<CodeBlockMessage className="language-javascript">{code}</CodeBlockMessage>);
35
+ expect(screen.getByText('javascript')).toBeInTheDocument();
36
+ });
37
+
38
+ it('should render copy button', () => {
39
+ const code = 'const x = 5;\nconst y = 10;';
40
+ render(<CodeBlockMessage>{code}</CodeBlockMessage>);
41
+ expect(screen.getByRole('button', { name: 'Copy code' })).toBeInTheDocument();
42
+ });
43
+
44
+ it('should copy plain string content to clipboard', async () => {
45
+ const code = 'const x = 5;\nconst y = 10;';
46
+ render(<CodeBlockMessage>{code}</CodeBlockMessage>);
47
+
48
+ const copyButton = screen.getByRole('button', { name: 'Copy code' });
49
+ await userEvent.click(copyButton);
50
+
51
+ expect(navigator.clipboard.writeText).toHaveBeenCalledWith(code);
52
+ });
53
+
54
+ it('should extract text content from React elements when copying', async () => {
55
+ // Simulate what happens with syntax highlighting - children become React elements
56
+ const { container } = render(
57
+ <CodeBlockMessage className="language-javascript">
58
+ <span className="hljs-keyword">const</span> x = 5;{'\n'}
59
+ <span className="hljs-keyword">const</span> y = 10;
60
+ </CodeBlockMessage>
61
+ );
62
+
63
+ const copyButton = screen.getByRole('button', { name: 'Copy code' });
64
+ await userEvent.click(copyButton);
65
+
66
+ // Should extract actual text content from DOM, not "[object Object]"
67
+ const codeElement = container.querySelector('code');
68
+ const expectedText = codeElement?.textContent || '';
69
+ expect(navigator.clipboard.writeText).toHaveBeenCalledWith(expectedText);
70
+ expect(expectedText).not.toContain('[object Object]');
71
+ });
72
+
73
+ it('should show check icon after copying', async () => {
74
+ const code = 'const x = 5;\nconst y = 10;';
75
+ render(<CodeBlockMessage>{code}</CodeBlockMessage>);
76
+
77
+ const copyButton = screen.getByRole('button', { name: 'Copy code' });
78
+ await userEvent.click(copyButton);
79
+
80
+ // Check icon should be visible (we can verify by checking if CopyIcon is not present)
81
+ const svgElement = copyButton.querySelector('svg');
82
+ expect(svgElement).toBeInTheDocument();
83
+ });
84
+
85
+ it('should render expandable section when isExpandable is true', () => {
86
+ const code = 'const x = 5;\nconst y = 10;';
87
+ render(<CodeBlockMessage isExpandable>{code}</CodeBlockMessage>);
88
+
89
+ expect(screen.getByRole('button', { name: 'Show more' })).toBeInTheDocument();
90
+ });
91
+
92
+ it('should toggle expandable section', async () => {
93
+ const code = 'const x = 5;\nconst y = 10;';
94
+ render(<CodeBlockMessage isExpandable>{code}</CodeBlockMessage>);
95
+
96
+ const toggleButton = screen.getByRole('button', { name: 'Show more' });
97
+ await userEvent.click(toggleButton);
98
+
99
+ expect(screen.getByRole('button', { name: 'Show less' })).toBeInTheDocument();
100
+ });
101
+
102
+ it('should use custom expanded/collapsed text', () => {
103
+ const code = 'const x = 5;\nconst y = 10;';
104
+ render(
105
+ <CodeBlockMessage isExpandable expandedText="Hide" collapsedText="Reveal">
106
+ {code}
107
+ </CodeBlockMessage>
108
+ );
109
+
110
+ expect(screen.getByRole('button', { name: 'Reveal' })).toBeInTheDocument();
111
+ });
112
+
113
+ it('should pass through expandableSectionProps', () => {
114
+ const code = 'const x = 5;\nconst y = 10;';
115
+ const { container } = render(
116
+ <CodeBlockMessage isExpandable expandableSectionProps={{ className: 'custom-expandable-class' }}>
117
+ {code}
118
+ </CodeBlockMessage>
119
+ );
120
+
121
+ const expandableSection = container.querySelector('.pf-v6-c-expandable-section.custom-expandable-class');
122
+ expect(expandableSection).toBeInTheDocument();
123
+ });
124
+
125
+ it('should render custom actions', () => {
126
+ const code = 'const x = 5;\nconst y = 10;';
127
+ const customAction = <button aria-label="Custom action">Custom</button>;
128
+ render(<CodeBlockMessage customActions={customAction}>{code}</CodeBlockMessage>);
129
+
130
+ expect(screen.getByRole('button', { name: 'Custom action' })).toBeInTheDocument();
131
+ });
132
+
133
+ it('should apply isPrimary class to inline code', () => {
134
+ render(<CodeBlockMessage isPrimary>const x = 5;</CodeBlockMessage>);
135
+ const code = screen.getByText('const x = 5;');
136
+ expect(code).toHaveClass('pf-m-primary');
137
+ });
138
+
139
+ it('should apply shouldRetainStyles class to code block', () => {
140
+ const code = 'const x = 5;\nconst y = 10;';
141
+ const { container } = render(<CodeBlockMessage shouldRetainStyles>{code}</CodeBlockMessage>);
142
+
143
+ const codeBlockDiv = container.querySelector('.pf-chatbot__message-code-block');
144
+ expect(codeBlockDiv).toHaveClass('pf-m-markdown');
145
+ });
146
+
147
+ it('should use custom aria-label for copy button', () => {
148
+ const code = 'const x = 5;\nconst y = 10;';
149
+ render(<CodeBlockMessage aria-label="Copy this code">{code}</CodeBlockMessage>);
150
+
151
+ expect(screen.getByRole('button', { name: 'Copy this code' })).toBeInTheDocument();
152
+ });
153
+
154
+ it('should prioritize data-expanded-text over expandedText prop', () => {
155
+ const code = 'const x = 5;\nconst y = 10;';
156
+ const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(jest.fn());
157
+
158
+ render(
159
+ <CodeBlockMessage isExpandable expandedText="Custom Expanded" data-expanded-text="Data Expanded">
160
+ {code}
161
+ </CodeBlockMessage>
162
+ );
163
+
164
+ expect(consoleErrorSpy).toHaveBeenCalledWith(
165
+ 'Message:',
166
+ expect.stringContaining('data-expanded-text or data-collapsed-text will override')
167
+ );
168
+
169
+ consoleErrorSpy.mockRestore();
170
+ });
171
+ });