@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
@@ -7,6 +7,7 @@ import { monitorSampleAppQuickStart } from './QuickStarts/monitor-sampleapp-quic
7
7
  import { monitorSampleAppQuickStartWithImage } from './QuickStarts/monitor-sampleapp-quickstart-with-image';
8
8
  import rehypeExternalLinks from '../__mocks__/rehype-external-links';
9
9
  import { AlertActionLink } from '@patternfly/react-core';
10
+ import { DeepThinkingProps } from '../DeepThinking';
10
11
 
11
12
  const ALL_ACTIONS = [
12
13
  { label: /Good response/i },
@@ -141,10 +142,30 @@ const EMPTY_TABLE = `
141
142
 
142
143
  `;
143
144
 
145
+ const FOOTNOTE = `This is some text with a footnote[^1] and here's a longer one.[^bignote]
146
+
147
+ You can also reference the same footnote multiple times[^1].
148
+
149
+ [^1]: This is the full footnote text. You can click the arrow to go back up.
150
+
151
+ [^bignote]: Here's one with multiple paragraphs and **formatting**.
152
+
153
+ Indent paragraphs to include them in the footnote.
154
+
155
+ Add as many paragraphs as you like. You can include *italic text*, **bold text**, and even \`code\`.
156
+
157
+ > You can even include blockquotes in footnotes!`;
158
+
144
159
  const IMAGE = `![Multi-colored wavy lines on a black background](https://cdn.dribbble.com/userupload/10651749/file/original-8a07b8e39d9e8bf002358c66fce1223e.gif)`;
145
160
 
146
161
  const INLINE_IMAGE = `inline text ![Multi-colored wavy lines on a black background](https://cdn.dribbble.com/userupload/10651749/file/original-8a07b8e39d9e8bf002358c66fce1223e.gif)`;
147
162
 
163
+ const DEEP_THINKING: DeepThinkingProps = {
164
+ toggleContent: 'Show thinking',
165
+ subheading: 'Thought for 3 seconds',
166
+ body: "Here's why I said this."
167
+ };
168
+
148
169
  const ERROR = {
149
170
  title: 'Could not load chat',
150
171
  children: 'Wait a few minutes and check your network settings. If the issue persists: ',
@@ -675,6 +696,28 @@ describe('Message', () => {
675
696
  );
676
697
  expect(screen.getAllByRole('img')[1]).toHaveAttribute('src', 'test.png');
677
698
  });
699
+ it('should handle tool response correctly', async () => {
700
+ render(
701
+ <Message
702
+ avatar="./img"
703
+ role="user"
704
+ name="User"
705
+ content="Hi"
706
+ toolResponse={{
707
+ toggleContent: 'Tool response: Name',
708
+ subheading: 'Thought for 3 seconds',
709
+ body: 'Lorem ipsum dolor sit amet',
710
+ cardTitle: 'Card title',
711
+ cardBody: 'Card body'
712
+ }}
713
+ />
714
+ );
715
+ expect(screen.getByRole('button', { name: /Tool response: Name/i })).toBeTruthy();
716
+ expect(screen.getByText('Thought for 3 seconds')).toBeTruthy();
717
+ expect(screen.getByText('Lorem ipsum dolor sit amet')).toBeTruthy();
718
+ expect(screen.getByText('Card title')).toBeTruthy();
719
+ expect(screen.getByText('Card body')).toBeTruthy();
720
+ });
678
721
  it('should handle block quote correctly', () => {
679
722
  render(<Message avatar="./img" role="user" name="User" content={BLOCK_QUOTES} />);
680
723
  expect(screen.getByText(/Blockquotes can also be nested.../)).toBeTruthy();
@@ -740,6 +783,28 @@ describe('Message', () => {
740
783
  render(<Message avatar="./img" role="user" name="User" content={TABLE} tableProps={{ 'aria-label': 'Test' }} />);
741
784
  expect(screen.getByRole('grid', { name: /Test/i })).toBeTruthy();
742
785
  });
786
+ it('should render footnote correctly', () => {
787
+ render(<Message avatar="./img" role="user" name="User" content={FOOTNOTE} />);
788
+ expect(screen.getByText(/This is some text with a footnote/i)).toBeTruthy();
789
+ expect(screen.getByText(/and here's a longer one./i)).toBeTruthy();
790
+ expect(screen.getByText(/You can also reference the same footnote multiple times./i)).toBeTruthy();
791
+ expect(screen.getByRole('heading', { name: /Footnotes/i })).toBeTruthy();
792
+ expect(screen.getByText(/This is the full footnote text. You can click the arrow to go back up./i)).toBeTruthy();
793
+ expect(screen.getByText(/Here's one with multiple paragraphs and/i)).toBeTruthy();
794
+ expect(screen.getByText(/formatting/i)).toBeTruthy();
795
+ expect(screen.getByText(/Indent paragraphs to include them in the footnote./i)).toBeTruthy();
796
+ expect(screen.getByText(/Add as many paragraphs as you like. You can include/i)).toBeTruthy();
797
+ expect(screen.getByText(/italic text/i)).toBeTruthy();
798
+ expect(screen.getByText(/bold text/i)).toBeTruthy();
799
+ expect(screen.getByText(/, and even/i)).toBeTruthy();
800
+ expect(screen.getByText(/code/i)).toBeTruthy();
801
+ expect(screen.getByText(/You can even include blockquotes in footnotes!/i)).toBeTruthy();
802
+ expect(screen.getAllByRole('link', { name: '1' })).toHaveLength(2);
803
+ expect(screen.getAllByRole('link', { name: '2' })).toBeTruthy();
804
+ expect(screen.getByRole('link', { name: 'Back to reference 1' })).toBeTruthy();
805
+ expect(screen.getByRole('link', { name: 'Back to reference 1-2' })).toBeTruthy();
806
+ expect(screen.getByRole('link', { name: /Back to reference 2/i })).toBeTruthy();
807
+ });
743
808
  it('should render beforeMainContent with main content', () => {
744
809
  const mainContent = 'Main message content';
745
810
  const beforeMainContentText = 'Before main content';
@@ -962,4 +1027,29 @@ describe('Message', () => {
962
1027
  const form = container.querySelector('form');
963
1028
  expect(form).toHaveClass('test');
964
1029
  });
1030
+ it('should be able to disable markdown parsing', () => {
1031
+ render(<Message avatar="./img" role="user" name="User" content={CODE_MESSAGE} isMarkdownDisabled />);
1032
+ // this is looking for markdown syntax that is ordinarily stripped
1033
+ expect(screen.getByText(/~~~yaml/i)).toBeTruthy();
1034
+ });
1035
+ it('should be able to pass props to react-markdown, such as disabling tags', () => {
1036
+ render(
1037
+ <Message
1038
+ avatar="./img"
1039
+ role="user"
1040
+ name="User"
1041
+ content={CODE_MESSAGE}
1042
+ reactMarkdownProps={{ disallowedElements: ['code'] }}
1043
+ />
1044
+ );
1045
+ expect(screen.getByText('Here is some YAML code:')).toBeTruthy();
1046
+ // code block isn't rendering
1047
+ expect(screen.queryByRole('button', { name: 'Copy code' })).toBeFalsy();
1048
+ });
1049
+ it('should render deep thinking section correctly', () => {
1050
+ render(<Message avatar="./img" role="user" name="User" content="" deepThinking={DEEP_THINKING} />);
1051
+ expect(screen.getByRole('button', { name: /Show thinking/i })).toBeTruthy();
1052
+ expect(screen.getByText('Thought for 3 seconds')).toBeTruthy();
1053
+ expect(screen.getByText("Here's why I said this.")).toBeTruthy();
1054
+ });
965
1055
  });
@@ -3,7 +3,7 @@
3
3
  // ============================================================================
4
4
  import { forwardRef, ReactNode, useEffect, useState } from 'react';
5
5
  import type { FunctionComponent, HTMLProps, MouseEvent as ReactMouseEvent, Ref } from 'react';
6
- import Markdown from 'react-markdown';
6
+ import Markdown, { Options } from 'react-markdown';
7
7
  import remarkGfm from 'remark-gfm';
8
8
  import {
9
9
  AlertProps,
@@ -49,6 +49,9 @@ import LinkMessage from './LinkMessage/LinkMessage';
49
49
  import ErrorMessage from './ErrorMessage/ErrorMessage';
50
50
  import MessageInput from './MessageInput';
51
51
  import { rehypeMoveImagesOutOfParagraphs } from './Plugins/rehypeMoveImagesOutOfParagraphs';
52
+ import ToolResponse, { ToolResponseProps } from '../ToolResponse';
53
+ import DeepThinking, { DeepThinkingProps } from '../DeepThinking';
54
+ import SuperscriptMessage from './SuperscriptMessage/SuperscriptMessage';
52
55
 
53
56
  export interface MessageAttachment {
54
57
  /** Name of file attached to the message */
@@ -161,6 +164,8 @@ export interface MessageProps extends Omit<HTMLProps<HTMLDivElement>, 'role'> {
161
164
  tableProps?: Required<Pick<TableProps, 'aria-label'>> & TableProps;
162
165
  /** Additional rehype plugins passed from the consumer */
163
166
  additionalRehypePlugins?: PluggableList;
167
+ /** Additional remark plugins passed from the consumer */
168
+ additionalRemarkPlugins?: PluggableList;
164
169
  /** Whether to open links in message in new tab. */
165
170
  openLinkInNewTab?: boolean;
166
171
  /** Optional inline error message that can be displayed in the message */
@@ -185,6 +190,16 @@ export interface MessageProps extends Omit<HTMLProps<HTMLDivElement>, 'role'> {
185
190
  editFormProps?: FormProps;
186
191
  /** Sets message to compact styling. */
187
192
  isCompact?: boolean;
193
+ /** Disables markdown parsing for message, allowing only text input */
194
+ isMarkdownDisabled?: boolean;
195
+ /** 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 */
196
+ reactMarkdownProps?: Options;
197
+ /** Props for tool response card */
198
+ toolResponse?: ToolResponseProps;
199
+ /** Props for deep thinking card */
200
+ deepThinking?: DeepThinkingProps;
201
+ /** Allows passing additional props down to remark-gfm. See https://github.com/remarkjs/remark-gfm?tab=readme-ov-file#options for options */
202
+ remarkGfmProps?: Options;
188
203
  }
189
204
 
190
205
  export const MessageBase: FunctionComponent<MessageProps> = ({
@@ -213,6 +228,7 @@ export const MessageBase: FunctionComponent<MessageProps> = ({
213
228
  tableProps,
214
229
  openLinkInNewTab = true,
215
230
  additionalRehypePlugins = [],
231
+ additionalRemarkPlugins = [],
216
232
  linkProps,
217
233
  error,
218
234
  isEditable,
@@ -224,6 +240,11 @@ export const MessageBase: FunctionComponent<MessageProps> = ({
224
240
  inputRef,
225
241
  editFormProps,
226
242
  isCompact,
243
+ isMarkdownDisabled,
244
+ reactMarkdownProps,
245
+ toolResponse,
246
+ deepThinking,
247
+ remarkGfmProps,
227
248
  ...props
228
249
  }: MessageProps) => {
229
250
  const [messageText, setMessageText] = useState(content);
@@ -250,6 +271,152 @@ export const MessageBase: FunctionComponent<MessageProps> = ({
250
271
  const date = new Date();
251
272
  const dateString = timestamp ?? `${date.toLocaleDateString()} ${date.toLocaleTimeString()}`;
252
273
 
274
+ const handleMarkdown = () => {
275
+ if (isMarkdownDisabled) {
276
+ return (
277
+ <TextMessage component={ContentVariants.p} {...props}>
278
+ {messageText}
279
+ </TextMessage>
280
+ );
281
+ }
282
+ return (
283
+ <Markdown
284
+ components={{
285
+ section: (props) => {
286
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
287
+ const { node, ...rest } = props;
288
+ return <section {...rest} className={`pf-chatbot__message-text ${rest?.className}`} />;
289
+ },
290
+ p: (props) => {
291
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
292
+ const { node, ...rest } = props;
293
+ return <TextMessage component={ContentVariants.p} {...rest} />;
294
+ },
295
+ code: ({ children, ...props }) => {
296
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
297
+ const { node, ...codeProps } = props;
298
+ return (
299
+ <CodeBlockMessage {...codeProps} {...codeBlockProps}>
300
+ {children}
301
+ </CodeBlockMessage>
302
+ );
303
+ },
304
+ h1: (props) => {
305
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
306
+ const { node, ...rest } = props;
307
+ return <TextMessage component={ContentVariants.h1} {...rest} />;
308
+ },
309
+ h2: (props) => {
310
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
311
+ const { node, ...rest } = props;
312
+ return <TextMessage component={ContentVariants.h2} {...rest} />;
313
+ },
314
+ h3: (props) => {
315
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
316
+ const { node, ...rest } = props;
317
+ return <TextMessage component={ContentVariants.h3} {...rest} />;
318
+ },
319
+ h4: (props) => {
320
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
321
+ const { node, ...rest } = props;
322
+ return <TextMessage component={ContentVariants.h4} {...rest} />;
323
+ },
324
+ h5: (props) => {
325
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
326
+ const { node, ...rest } = props;
327
+ return <TextMessage component={ContentVariants.h5} {...rest} />;
328
+ },
329
+ h6: (props) => {
330
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
331
+ const { node, ...rest } = props;
332
+ return <TextMessage component={ContentVariants.h6} {...rest} />;
333
+ },
334
+ blockquote: (props) => {
335
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
336
+ const { node, ...rest } = props;
337
+ return <TextMessage component={ContentVariants.blockquote} {...rest} />;
338
+ },
339
+ ul: (props) => {
340
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
341
+ const { node, ...rest } = props;
342
+ return <UnorderedListMessage {...rest} />;
343
+ },
344
+ ol: (props) => {
345
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
346
+ const { node, ...rest } = props;
347
+ return <OrderedListMessage {...rest} />;
348
+ },
349
+ li: (props) => {
350
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
351
+ const { node, ...rest } = props;
352
+ return <ListItemMessage {...rest} />;
353
+ },
354
+ // table requires node attribute for calculating headers for mobile breakpoint
355
+ table: (props) => <TableMessage {...props} {...tableProps} />,
356
+ tbody: (props) => {
357
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
358
+ const { node, ...rest } = props;
359
+ return <TbodyMessage {...rest} />;
360
+ },
361
+ thead: (props) => {
362
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
363
+ const { node, ...rest } = props;
364
+ return <TheadMessage {...rest} />;
365
+ },
366
+ tr: (props) => {
367
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
368
+ const { node, ...rest } = props;
369
+ return <TrMessage {...rest} />;
370
+ },
371
+ td: (props) => {
372
+ // Conflicts with Td type
373
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
374
+ const { node, width, ...rest } = props;
375
+ return <TdMessage {...rest} />;
376
+ },
377
+ th: (props) => {
378
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
379
+ const { node, ...rest } = props;
380
+ return <ThMessage {...rest} />;
381
+ },
382
+ img: (props) => {
383
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
384
+ const { node, ...rest } = props;
385
+ return <ImageMessage {...rest} />;
386
+ },
387
+ a: (props) => {
388
+ // node is just the details of the document structure - not needed
389
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
390
+ const { node, ...rest } = props;
391
+ return (
392
+ // some a types conflict with ButtonProps, but it's ok because we are using an a tag
393
+ // there are too many to handle manually
394
+ <LinkMessage {...(rest as any)} {...linkProps}>
395
+ {props.children}
396
+ </LinkMessage>
397
+ );
398
+ },
399
+ // used for footnotes
400
+ sup: (props) => {
401
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
402
+ const { node, ...rest } = props;
403
+ return <SuperscriptMessage {...rest} />;
404
+ }
405
+ }}
406
+ remarkPlugins={[[remarkGfm, { ...remarkGfmProps }], ...additionalRemarkPlugins]}
407
+ rehypePlugins={rehypePlugins}
408
+ {...reactMarkdownProps}
409
+ remarkRehypeOptions={{
410
+ // removes sr-only class from footnote labels applied by default
411
+ footnoteLabelProperties: { className: [''] },
412
+ ...reactMarkdownProps?.remarkRehypeOptions
413
+ }}
414
+ >
415
+ {messageText}
416
+ </Markdown>
417
+ );
418
+ };
419
+
253
420
  const renderMessage = () => {
254
421
  if (isLoading) {
255
422
  return <MessageLoading loadingWord={loadingWord} />;
@@ -277,51 +444,7 @@ export const MessageBase: FunctionComponent<MessageProps> = ({
277
444
  return (
278
445
  <>
279
446
  {beforeMainContent && <>{beforeMainContent}</>}
280
- {error ? (
281
- <ErrorMessage {...error} />
282
- ) : (
283
- <Markdown
284
- components={{
285
- p: (props) => <TextMessage component={ContentVariants.p} {...props} />,
286
- code: ({ children, ...props }) => (
287
- <CodeBlockMessage {...props} {...codeBlockProps}>
288
- {children}
289
- </CodeBlockMessage>
290
- ),
291
- h1: (props) => <TextMessage component={ContentVariants.h1} {...props} />,
292
- h2: (props) => <TextMessage component={ContentVariants.h2} {...props} />,
293
- h3: (props) => <TextMessage component={ContentVariants.h3} {...props} />,
294
- h4: (props) => <TextMessage component={ContentVariants.h4} {...props} />,
295
- h5: (props) => <TextMessage component={ContentVariants.h5} {...props} />,
296
- h6: (props) => <TextMessage component={ContentVariants.h6} {...props} />,
297
- blockquote: (props) => <TextMessage component={ContentVariants.blockquote} {...props} />,
298
- ul: (props) => <UnorderedListMessage {...props} />,
299
- ol: (props) => <OrderedListMessage {...props} />,
300
- li: (props) => <ListItemMessage {...props} />,
301
- table: (props) => <TableMessage {...props} {...tableProps} />,
302
- tbody: (props) => <TbodyMessage {...props} />,
303
- thead: (props) => <TheadMessage {...props} />,
304
- tr: (props) => <TrMessage {...props} />,
305
- td: (props) => {
306
- // Conflicts with Td type
307
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
308
- const { width, ...rest } = props;
309
- return <TdMessage {...rest} />;
310
- },
311
- th: (props) => <ThMessage {...props} />,
312
- img: (props) => <ImageMessage {...props} />,
313
- a: (props) => (
314
- <LinkMessage href={props.href} rel={props.rel} target={props.target} {...linkProps}>
315
- {props.children}
316
- </LinkMessage>
317
- )
318
- }}
319
- remarkPlugins={[remarkGfm]}
320
- rehypePlugins={rehypePlugins}
321
- >
322
- {messageText}
323
- </Markdown>
324
- )}
447
+ {error ? <ErrorMessage {...error} /> : handleMarkdown()}
325
448
  </>
326
449
  );
327
450
  };
@@ -360,6 +483,8 @@ export const MessageBase: FunctionComponent<MessageProps> = ({
360
483
  <div className="pf-chatbot__message-and-actions">
361
484
  {renderMessage()}
362
485
  {afterMainContent && <>{afterMainContent}</>}
486
+ {toolResponse && <ToolResponse {...toolResponse} />}
487
+ {deepThinking && <DeepThinking {...deepThinking} />}
363
488
  {!isLoading && sources && <SourcesCard {...sources} isCompact={isCompact} />}
364
489
  {quickStarts && quickStarts.quickStart && (
365
490
  <QuickStartTile
@@ -0,0 +1,8 @@
1
+ .pf-chatbot__message-superscript {
2
+ font-size: smaller;
3
+ vertical-align: super;
4
+ .pf-v6-c-button.pf-m-link.pf-m-inline {
5
+ font-size: inherit;
6
+ vertical-align: inherit;
7
+ }
8
+ }
@@ -0,0 +1,13 @@
1
+ // ============================================================================
2
+ // Chatbot Main - Message - Content - Superscript (like for footnotes)
3
+ // ============================================================================
4
+
5
+ import { ExtraProps } from 'react-markdown';
6
+
7
+ const SuperscriptMessage = ({ children }: JSX.IntrinsicElements['sup'] & ExtraProps) => (
8
+ <span className="pf-chatbot__message-superscript">
9
+ <sup>{children}</sup>
10
+ </span>
11
+ );
12
+
13
+ export default SuperscriptMessage;
@@ -17,9 +17,10 @@
17
17
  width: fit-content;
18
18
  padding: var(--pf-t--global--spacer--sm) 0 var(--pf-t--global--spacer--sm) 0;
19
19
  border-radius: var(--pf-t--global--border--radius--small);
20
+ --pf-chatbot-message-text-font-size: var(--pf-t--global--font--size--md);
20
21
 
21
22
  .pf-v6-c-button.pf-m-link {
22
- font-size: var(--pf-t--global--font--size--md);
23
+ font-size: var(--pf-chatbot-message-text-font-size);
23
24
  }
24
25
 
25
26
  .pf-v6-c-content,
@@ -27,15 +28,49 @@
27
28
  .pf-v6-c-content--blockquote,
28
29
  p,
29
30
  a {
30
- --pf-v6-c-content--FontSize: var(--pf-t--global--font--size--md);
31
+ --pf-v6-c-content--FontSize: var(--pf-chatbot-message-text-font-size);
31
32
  }
32
33
 
33
34
  code {
34
35
  background-color: var(--pf-t--global--background--color--tertiary--default);
35
- font-size: var(--pf-t--global--font--size--body--default);
36
+ font-size: var(--pf-chatbot-message-text-inline-code-font-size);
37
+ }
38
+
39
+ // Hide message text that contains sr-only content
40
+ // https://css-tricks.com/inclusively-hidden/
41
+ &:has(.sr-only) {
42
+ clip: rect(0 0 0 0);
43
+ clip-path: inset(50%);
44
+ height: 1px;
45
+ overflow: hidden;
46
+ position: absolute;
47
+ white-space: nowrap;
48
+ width: 1px;
36
49
  }
37
50
  }
38
51
 
52
+ // ============================================================================
53
+ // Footnote spacing styles
54
+ // ============================================================================
55
+
56
+ // Add spacing to paragraphs in multi-paragraph footnotes
57
+ // Only target p tags that are direct children of message-text spans (not inside blockquotes, etc.)
58
+ li[id*='user-content-fn-']:has(> span > .pf-chatbot__message-text + .pf-chatbot__message-text)
59
+ > span
60
+ > .pf-chatbot__message-text
61
+ > p {
62
+ margin-block-end: var(--pf-t--global--spacer--md);
63
+ }
64
+
65
+ // Handle user message footnotes which may have extra span wrappers
66
+ li[id*='user-content-fn-']:has(> span > span > .pf-chatbot__message-text + .pf-chatbot__message-text)
67
+ > span
68
+ > span
69
+ > .pf-chatbot__message-text
70
+ > p {
71
+ margin-block-end: var(--pf-t--global--spacer--md);
72
+ }
73
+
39
74
  .pf-chatbot__message--user {
40
75
  .pf-chatbot__message-text {
41
76
  background-color: var(--pf-t--global--color--brand--default);
@@ -54,6 +89,11 @@
54
89
  color: var(--pf-t--global--text--color--on-brand--default);
55
90
  }
56
91
  }
92
+
93
+ .pf-chatbot__message-text > .pf-chatbot__message-text {
94
+ background-color: initial;
95
+ padding: initial;
96
+ }
57
97
  }
58
98
 
59
99
  // ============================================================================
@@ -62,8 +102,9 @@
62
102
  .pf-chatbot.pf-m-compact {
63
103
  // Need to inline shorter text
64
104
  .pf-chatbot__message-text {
105
+ --pf-chatbot-message-text-font-size: var(--pf-t--global--font--size--sm);
65
106
  .pf-v6-c-button.pf-m-link {
66
- font-size: var(--pf-t--global--font--size--sm);
107
+ font-size: var(--pf-chatbot-message-text-font-size);
67
108
  }
68
109
 
69
110
  .pf-v6-c-content,
@@ -71,7 +112,7 @@
71
112
  .pf-v6-c-content--blockquote,
72
113
  p,
73
114
  a {
74
- --pf-v6-c-content--FontSize: var(--pf-t--global--font--size--sm);
115
+ --pf-v6-c-content--FontSize: var(--pf-chatbot-message-text-font-size);
75
116
  }
76
117
 
77
118
  .pf-v6-c-content--blockquote {
@@ -245,4 +245,111 @@ describe('UserFeedback', () => {
245
245
  );
246
246
  expect(screen.getByTestId('card')).toHaveClass('pf-m-compact');
247
247
  });
248
+ it('should pass buttonProps to submit button', () => {
249
+ render(
250
+ <UserFeedback
251
+ timestamp="12/12/12"
252
+ onClose={jest.fn}
253
+ onSubmit={jest.fn}
254
+ quickResponses={MOCK_RESPONSES}
255
+ submitButtonProps={{ variant: 'secondary', isDisabled: true }}
256
+ />
257
+ );
258
+ const submitButton = screen.getByRole('button', { name: /Submit/i });
259
+ expect(submitButton).toHaveClass('pf-v6-c-button pf-m-secondary');
260
+ expect(submitButton).toBeDisabled();
261
+ });
262
+ it('should pass cardHeaderProps to card header', () => {
263
+ render(
264
+ <UserFeedback
265
+ timestamp="12/12/12"
266
+ onClose={jest.fn}
267
+ onSubmit={jest.fn}
268
+ quickResponses={MOCK_RESPONSES}
269
+ cardHeaderProps={{ 'data-testid': 'card-header', className: 'custom-header' } as any}
270
+ />
271
+ );
272
+ const cardHeader = screen.getByTestId('card-header');
273
+ expect(cardHeader).toHaveClass('custom-header');
274
+ });
275
+ it('should pass cardBodyProps to card body', () => {
276
+ render(
277
+ <UserFeedback
278
+ timestamp="12/12/12"
279
+ onClose={jest.fn}
280
+ onSubmit={jest.fn}
281
+ quickResponses={MOCK_RESPONSES}
282
+ cardBodyProps={{ 'data-testid': 'card-body', className: 'custom-body' } as any}
283
+ />
284
+ );
285
+ const cardBody = screen.getByTestId('card-body');
286
+ expect(cardBody).toHaveClass('custom-body');
287
+ });
288
+ it('should pass headingLevelProps to title heading', () => {
289
+ render(
290
+ <UserFeedback
291
+ timestamp="12/12/12"
292
+ onClose={jest.fn}
293
+ onSubmit={jest.fn}
294
+ quickResponses={MOCK_RESPONSES}
295
+ headingLevelProps={{ className: 'custom-heading', id: 'feedback-title' }}
296
+ />
297
+ );
298
+ const heading = screen.getByRole('heading', { level: 1, name: /Why did you choose this rating?/i });
299
+ expect(heading).toHaveClass('custom-heading');
300
+ expect(heading).toHaveAttribute('id', 'feedback-title');
301
+ });
302
+
303
+ it('should pass formProps to form', () => {
304
+ render(
305
+ <UserFeedback
306
+ timestamp="12/12/12"
307
+ onClose={jest.fn}
308
+ onSubmit={jest.fn}
309
+ quickResponses={MOCK_RESPONSES}
310
+ formProps={{ 'data-testid': 'feedback-form', className: 'custom-form' } as any}
311
+ />
312
+ );
313
+ const form = screen.getByTestId('feedback-form');
314
+ expect(form).toHaveClass('custom-form');
315
+ });
316
+ it('should pass textAreaProps to text area when hasTextArea is true', () => {
317
+ render(
318
+ <UserFeedback
319
+ timestamp="12/12/12"
320
+ onClose={jest.fn}
321
+ onSubmit={jest.fn}
322
+ quickResponses={MOCK_RESPONSES}
323
+ hasTextArea
324
+ textAreaProps={{ 'data-testid': 'custom-textarea', rows: 5 } as any}
325
+ />
326
+ );
327
+ const textArea = screen.getByTestId('custom-textarea');
328
+ expect(textArea).toHaveAttribute('rows', '5');
329
+ expect(textArea).toHaveAttribute('data-testid', 'custom-textarea');
330
+ });
331
+ it('should pass actionGroupProps to action group', () => {
332
+ render(
333
+ <UserFeedback
334
+ timestamp="12/12/12"
335
+ onClose={jest.fn}
336
+ onSubmit={jest.fn}
337
+ quickResponses={MOCK_RESPONSES}
338
+ actionGroupProps={{ 'data-testid': 'action-group', className: 'custom-actions' } as any}
339
+ />
340
+ );
341
+ const actionGroup = screen.getByTestId('action-group');
342
+ expect(actionGroup).toHaveClass('custom-actions');
343
+ });
344
+ it('should render children', () => {
345
+ render(
346
+ <UserFeedback timestamp="12/12/12" onClose={jest.fn} onSubmit={jest.fn} quickResponses={MOCK_RESPONSES}>
347
+ <div data-testid="custom-content">Custom feedback content</div>
348
+ <p>Additional paragraph</p>
349
+ </UserFeedback>
350
+ );
351
+ expect(screen.getByTestId('custom-content')).toBeInTheDocument();
352
+ expect(screen.getByText('Custom feedback content')).toBeInTheDocument();
353
+ expect(screen.getByText('Additional paragraph')).toBeInTheDocument();
354
+ });
248
355
  });