@patternfly/chatbot 2.2.0-prerelease.24 → 2.2.0-prerelease.26

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 (34) hide show
  1. package/dist/cjs/Message/ImageMessage/ImageMessage.d.ts +4 -0
  2. package/dist/cjs/Message/ImageMessage/ImageMessage.js +25 -0
  3. package/dist/cjs/Message/Message.d.ts +11 -1
  4. package/dist/cjs/Message/Message.js +38 -30
  5. package/dist/cjs/Message/Message.test.js +68 -0
  6. package/dist/cjs/MessageBar/MessageBar.d.ts +5 -1
  7. package/dist/cjs/MessageBar/MicrophoneButton.d.ts +1 -1
  8. package/dist/cjs/__mocks__/rehype-unwrap-images.d.ts +2 -0
  9. package/dist/cjs/__mocks__/rehype-unwrap-images.js +4 -0
  10. package/dist/css/main.css +8 -0
  11. package/dist/css/main.css.map +1 -1
  12. package/dist/esm/Message/ImageMessage/ImageMessage.d.ts +4 -0
  13. package/dist/esm/Message/ImageMessage/ImageMessage.js +20 -0
  14. package/dist/esm/Message/Message.d.ts +11 -1
  15. package/dist/esm/Message/Message.js +38 -30
  16. package/dist/esm/Message/Message.test.js +68 -0
  17. package/dist/esm/MessageBar/MessageBar.d.ts +5 -1
  18. package/dist/esm/MessageBar/MicrophoneButton.d.ts +1 -1
  19. package/dist/esm/__mocks__/rehype-unwrap-images.d.ts +2 -0
  20. package/dist/esm/__mocks__/rehype-unwrap-images.js +2 -0
  21. package/dist/tsconfig.tsbuildinfo +1 -1
  22. package/package.json +2 -1
  23. package/patternfly-docs/content/extensions/chatbot/examples/Messages/BotMessage.tsx +11 -0
  24. package/patternfly-docs/content/extensions/chatbot/examples/Messages/Messages.md +11 -0
  25. package/patternfly-docs/content/extensions/chatbot/examples/Messages/UserMessage.tsx +11 -0
  26. package/patternfly-docs/content/extensions/chatbot/examples/Messages/UserMessageWithExtraContent.tsx +54 -0
  27. package/src/Message/ImageMessage/ImageMessage.scss +9 -0
  28. package/src/Message/ImageMessage/ImageMessage.tsx +14 -0
  29. package/src/Message/Message.test.tsx +120 -0
  30. package/src/Message/Message.tsx +59 -35
  31. package/src/MessageBar/MessageBar.tsx +10 -4
  32. package/src/MessageBar/MicrophoneButton.tsx +1 -1
  33. package/src/__mocks__/rehype-unwrap-images.tsx +3 -0
  34. package/src/main.scss +1 -0
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import { ButtonProps, DropEvent, TextAreaProps } from '@patternfly/react-core';
2
+ import { ButtonProps, DropEvent, TextAreaProps, TooltipProps } from '@patternfly/react-core';
3
3
  import { ChatbotDisplayMode } from '../Chatbot';
4
4
  export interface MessageBarWithAttachMenuProps {
5
5
  /** Flag to enable whether attach menu is open */
@@ -48,14 +48,17 @@ export interface MessageBarProps extends TextAreaProps {
48
48
  tooltipContent?: string;
49
49
  props?: ButtonProps;
50
50
  inputTestId?: string;
51
+ tooltipProps?: Omit<TooltipProps, 'content'>;
51
52
  };
52
53
  stop?: {
53
54
  tooltipContent?: string;
54
55
  props?: ButtonProps;
56
+ tooltipProps?: Omit<TooltipProps, 'content'>;
55
57
  };
56
58
  send?: {
57
59
  tooltipContent?: string;
58
60
  props?: ButtonProps;
61
+ tooltipProps?: Omit<TooltipProps, 'content'>;
59
62
  };
60
63
  microphone?: {
61
64
  tooltipContent?: {
@@ -64,6 +67,7 @@ export interface MessageBarProps extends TextAreaProps {
64
67
  };
65
68
  language?: string;
66
69
  props?: ButtonProps;
70
+ tooltipProps?: Omit<TooltipProps, 'content'>;
67
71
  };
68
72
  };
69
73
  /** A callback for when the text area value changes. */
@@ -10,7 +10,7 @@ export interface MicrophoneButtonProps extends ButtonProps {
10
10
  /** Callback to update the message value once speech recognition is complete */
11
11
  onSpeechRecognition: React.Dispatch<React.SetStateAction<string>>;
12
12
  /** Props to control the PF Tooltip component */
13
- tooltipProps?: TooltipProps;
13
+ tooltipProps?: Omit<TooltipProps, 'content'>;
14
14
  /** English text "Use microphone" and "Stop listening" used in the tooltip */
15
15
  tooltipContent?: {
16
16
  active?: string;
@@ -0,0 +1,2 @@
1
+ declare const RehypeUnwrapImages: () => null;
2
+ export default RehypeUnwrapImages;
@@ -0,0 +1,2 @@
1
+ const RehypeUnwrapImages = () => null;
2
+ export default RehypeUnwrapImages;
@@ -1 +1 @@
1
- {"root":["../src/index.ts","../src/AttachMenu/AttachMenu.tsx","../src/AttachMenu/index.ts","../src/AttachmentEdit/AttachmentEdit.test.tsx","../src/AttachmentEdit/AttachmentEdit.tsx","../src/AttachmentEdit/index.ts","../src/Chatbot/Chatbot.test.tsx","../src/Chatbot/Chatbot.tsx","../src/Chatbot/index.ts","../src/ChatbotAlert/ChatbotAlert.test.tsx","../src/ChatbotAlert/ChatbotAlert.tsx","../src/ChatbotAlert/index.ts","../src/ChatbotContent/ChatbotContent.test.tsx","../src/ChatbotContent/ChatbotContent.tsx","../src/ChatbotContent/index.ts","../src/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.test.tsx","../src/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.tsx","../src/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.test.tsx","../src/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.tsx","../src/ChatbotConversationHistoryNav/index.ts","../src/ChatbotFooter/ChatbotFooter.test.tsx","../src/ChatbotFooter/ChatbotFooter.tsx","../src/ChatbotFooter/ChatbotFooternote.test.tsx","../src/ChatbotFooter/ChatbotFootnote.tsx","../src/ChatbotFooter/index.ts","../src/ChatbotHeader/ChatbotHeader.test.tsx","../src/ChatbotHeader/ChatbotHeader.tsx","../src/ChatbotHeader/ChatbotHeaderActions.test.tsx","../src/ChatbotHeader/ChatbotHeaderActions.tsx","../src/ChatbotHeader/ChatbotHeaderCloseButton.test.tsx","../src/ChatbotHeader/ChatbotHeaderCloseButton.tsx","../src/ChatbotHeader/ChatbotHeaderMain.test.tsx","../src/ChatbotHeader/ChatbotHeaderMain.tsx","../src/ChatbotHeader/ChatbotHeaderMenu.test.tsx","../src/ChatbotHeader/ChatbotHeaderMenu.tsx","../src/ChatbotHeader/ChatbotHeaderOptionsDropdown.test.tsx","../src/ChatbotHeader/ChatbotHeaderOptionsDropdown.tsx","../src/ChatbotHeader/ChatbotHeaderSelectorDropdown.test.tsx","../src/ChatbotHeader/ChatbotHeaderSelectorDropdown.tsx","../src/ChatbotHeader/ChatbotHeaderTitle.test.tsx","../src/ChatbotHeader/ChatbotHeaderTitle.tsx","../src/ChatbotHeader/index.ts","../src/ChatbotModal/ChatbotModal.tsx","../src/ChatbotModal/index.ts","../src/ChatbotPopover/ChatbotPopover.tsx","../src/ChatbotPopover/index.ts","../src/ChatbotToggle/ChatbotToggle.test.tsx","../src/ChatbotToggle/ChatbotToggle.tsx","../src/ChatbotToggle/index.ts","../src/ChatbotWelcomePrompt/ChatbotWelcomePrompt.test.tsx","../src/ChatbotWelcomePrompt/ChatbotWelcomePrompt.tsx","../src/ChatbotWelcomePrompt/index.ts","../src/CodeModal/CodeModal.tsx","../src/CodeModal/index.ts","../src/Compare/Compare.test.tsx","../src/Compare/Compare.tsx","../src/Compare/index.ts","../src/FileDetails/FileDetails.test.tsx","../src/FileDetails/FileDetails.tsx","../src/FileDetails/index.ts","../src/FileDetailsLabel/FileDetailsLabel.test.tsx","../src/FileDetailsLabel/FileDetailsLabel.tsx","../src/FileDetailsLabel/index.ts","../src/FileDropZone/FileDropZone.test.tsx","../src/FileDropZone/FileDropZone.tsx","../src/FileDropZone/index.ts","../src/LoadingMessage/LoadingMessage.test.tsx","../src/LoadingMessage/LoadingMessage.tsx","../src/LoadingMessage/index.ts","../src/Message/Message.test.tsx","../src/Message/Message.tsx","../src/Message/MessageLoading.tsx","../src/Message/index.ts","../src/Message/CodeBlockMessage/CodeBlockMessage.tsx","../src/Message/ListMessage/ListItemMessage.tsx","../src/Message/ListMessage/OrderedListMessage.tsx","../src/Message/ListMessage/UnorderedListMessage.tsx","../src/Message/QuickResponse/QuickResponse.tsx","../src/Message/QuickStarts/FallbackImg.tsx","../src/Message/QuickStarts/QuickStartTile.tsx","../src/Message/QuickStarts/QuickStartTileDescription.test.tsx","../src/Message/QuickStarts/QuickStartTileDescription.tsx","../src/Message/QuickStarts/QuickStartTileHeader.tsx","../src/Message/QuickStarts/monitor-sampleapp-quickstart-with-image.ts","../src/Message/QuickStarts/monitor-sampleapp-quickstart.ts","../src/Message/QuickStarts/types.ts","../src/Message/TableMessage/TableMessage.tsx","../src/Message/TableMessage/TbodyMessage.tsx","../src/Message/TableMessage/TdMessage.tsx","../src/Message/TableMessage/ThMessage.tsx","../src/Message/TableMessage/TheadMessage.tsx","../src/Message/TableMessage/TrMessage.tsx","../src/Message/TextMessage/TextMessage.tsx","../src/Message/UserFeedback/CloseButton.tsx","../src/Message/UserFeedback/UserFeedback.test.tsx","../src/Message/UserFeedback/UserFeedback.tsx","../src/Message/UserFeedback/UserFeedbackComplete.test.tsx","../src/Message/UserFeedback/UserFeedbackComplete.tsx","../src/MessageBar/AttachButton.test.tsx","../src/MessageBar/AttachButton.tsx","../src/MessageBar/MessageBar.test.tsx","../src/MessageBar/MessageBar.tsx","../src/MessageBar/MicrophoneButton.tsx","../src/MessageBar/SendButton.test.tsx","../src/MessageBar/SendButton.tsx","../src/MessageBar/StopButton.test.tsx","../src/MessageBar/StopButton.tsx","../src/MessageBar/index.ts","../src/MessageBox/JumpButton.test.tsx","../src/MessageBox/JumpButton.tsx","../src/MessageBox/MessageBox.test.tsx","../src/MessageBox/MessageBox.tsx","../src/MessageBox/index.ts","../src/PreviewAttachment/PreviewAttachment.test.tsx","../src/PreviewAttachment/PreviewAttachment.tsx","../src/PreviewAttachment/index.ts","../src/ResponseActions/ResponseActionButton.test.tsx","../src/ResponseActions/ResponseActionButton.tsx","../src/ResponseActions/ResponseActions.test.tsx","../src/ResponseActions/ResponseActions.tsx","../src/ResponseActions/index.ts","../src/Settings/SettingsForm.test.tsx","../src/Settings/SettingsForm.tsx","../src/Settings/index.ts","../src/SourceDetailsMenuItem/SourceDetailsMenuItem.tsx","../src/SourceDetailsMenuItem/index.ts","../src/SourcesCard/SourcesCard.test.tsx","../src/SourcesCard/SourcesCard.tsx","../src/SourcesCard/index.ts","../src/TermsOfUse/TermsOfUse.test.tsx","../src/TermsOfUse/TermsOfUse.tsx","../src/TermsOfUse/index.ts"],"version":"5.6.3"}
1
+ {"root":["../src/index.ts","../src/AttachMenu/AttachMenu.tsx","../src/AttachMenu/index.ts","../src/AttachmentEdit/AttachmentEdit.test.tsx","../src/AttachmentEdit/AttachmentEdit.tsx","../src/AttachmentEdit/index.ts","../src/Chatbot/Chatbot.test.tsx","../src/Chatbot/Chatbot.tsx","../src/Chatbot/index.ts","../src/ChatbotAlert/ChatbotAlert.test.tsx","../src/ChatbotAlert/ChatbotAlert.tsx","../src/ChatbotAlert/index.ts","../src/ChatbotContent/ChatbotContent.test.tsx","../src/ChatbotContent/ChatbotContent.tsx","../src/ChatbotContent/index.ts","../src/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.test.tsx","../src/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.tsx","../src/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.test.tsx","../src/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.tsx","../src/ChatbotConversationHistoryNav/index.ts","../src/ChatbotFooter/ChatbotFooter.test.tsx","../src/ChatbotFooter/ChatbotFooter.tsx","../src/ChatbotFooter/ChatbotFooternote.test.tsx","../src/ChatbotFooter/ChatbotFootnote.tsx","../src/ChatbotFooter/index.ts","../src/ChatbotHeader/ChatbotHeader.test.tsx","../src/ChatbotHeader/ChatbotHeader.tsx","../src/ChatbotHeader/ChatbotHeaderActions.test.tsx","../src/ChatbotHeader/ChatbotHeaderActions.tsx","../src/ChatbotHeader/ChatbotHeaderCloseButton.test.tsx","../src/ChatbotHeader/ChatbotHeaderCloseButton.tsx","../src/ChatbotHeader/ChatbotHeaderMain.test.tsx","../src/ChatbotHeader/ChatbotHeaderMain.tsx","../src/ChatbotHeader/ChatbotHeaderMenu.test.tsx","../src/ChatbotHeader/ChatbotHeaderMenu.tsx","../src/ChatbotHeader/ChatbotHeaderOptionsDropdown.test.tsx","../src/ChatbotHeader/ChatbotHeaderOptionsDropdown.tsx","../src/ChatbotHeader/ChatbotHeaderSelectorDropdown.test.tsx","../src/ChatbotHeader/ChatbotHeaderSelectorDropdown.tsx","../src/ChatbotHeader/ChatbotHeaderTitle.test.tsx","../src/ChatbotHeader/ChatbotHeaderTitle.tsx","../src/ChatbotHeader/index.ts","../src/ChatbotModal/ChatbotModal.tsx","../src/ChatbotModal/index.ts","../src/ChatbotPopover/ChatbotPopover.tsx","../src/ChatbotPopover/index.ts","../src/ChatbotToggle/ChatbotToggle.test.tsx","../src/ChatbotToggle/ChatbotToggle.tsx","../src/ChatbotToggle/index.ts","../src/ChatbotWelcomePrompt/ChatbotWelcomePrompt.test.tsx","../src/ChatbotWelcomePrompt/ChatbotWelcomePrompt.tsx","../src/ChatbotWelcomePrompt/index.ts","../src/CodeModal/CodeModal.tsx","../src/CodeModal/index.ts","../src/Compare/Compare.test.tsx","../src/Compare/Compare.tsx","../src/Compare/index.ts","../src/FileDetails/FileDetails.test.tsx","../src/FileDetails/FileDetails.tsx","../src/FileDetails/index.ts","../src/FileDetailsLabel/FileDetailsLabel.test.tsx","../src/FileDetailsLabel/FileDetailsLabel.tsx","../src/FileDetailsLabel/index.ts","../src/FileDropZone/FileDropZone.test.tsx","../src/FileDropZone/FileDropZone.tsx","../src/FileDropZone/index.ts","../src/LoadingMessage/LoadingMessage.test.tsx","../src/LoadingMessage/LoadingMessage.tsx","../src/LoadingMessage/index.ts","../src/Message/Message.test.tsx","../src/Message/Message.tsx","../src/Message/MessageLoading.tsx","../src/Message/index.ts","../src/Message/CodeBlockMessage/CodeBlockMessage.tsx","../src/Message/ImageMessage/ImageMessage.tsx","../src/Message/ListMessage/ListItemMessage.tsx","../src/Message/ListMessage/OrderedListMessage.tsx","../src/Message/ListMessage/UnorderedListMessage.tsx","../src/Message/QuickResponse/QuickResponse.tsx","../src/Message/QuickStarts/FallbackImg.tsx","../src/Message/QuickStarts/QuickStartTile.tsx","../src/Message/QuickStarts/QuickStartTileDescription.test.tsx","../src/Message/QuickStarts/QuickStartTileDescription.tsx","../src/Message/QuickStarts/QuickStartTileHeader.tsx","../src/Message/QuickStarts/monitor-sampleapp-quickstart-with-image.ts","../src/Message/QuickStarts/monitor-sampleapp-quickstart.ts","../src/Message/QuickStarts/types.ts","../src/Message/TableMessage/TableMessage.tsx","../src/Message/TableMessage/TbodyMessage.tsx","../src/Message/TableMessage/TdMessage.tsx","../src/Message/TableMessage/ThMessage.tsx","../src/Message/TableMessage/TheadMessage.tsx","../src/Message/TableMessage/TrMessage.tsx","../src/Message/TextMessage/TextMessage.tsx","../src/Message/UserFeedback/CloseButton.tsx","../src/Message/UserFeedback/UserFeedback.test.tsx","../src/Message/UserFeedback/UserFeedback.tsx","../src/Message/UserFeedback/UserFeedbackComplete.test.tsx","../src/Message/UserFeedback/UserFeedbackComplete.tsx","../src/MessageBar/AttachButton.test.tsx","../src/MessageBar/AttachButton.tsx","../src/MessageBar/MessageBar.test.tsx","../src/MessageBar/MessageBar.tsx","../src/MessageBar/MicrophoneButton.tsx","../src/MessageBar/SendButton.test.tsx","../src/MessageBar/SendButton.tsx","../src/MessageBar/StopButton.test.tsx","../src/MessageBar/StopButton.tsx","../src/MessageBar/index.ts","../src/MessageBox/JumpButton.test.tsx","../src/MessageBox/JumpButton.tsx","../src/MessageBox/MessageBox.test.tsx","../src/MessageBox/MessageBox.tsx","../src/MessageBox/index.ts","../src/PreviewAttachment/PreviewAttachment.test.tsx","../src/PreviewAttachment/PreviewAttachment.tsx","../src/PreviewAttachment/index.ts","../src/ResponseActions/ResponseActionButton.test.tsx","../src/ResponseActions/ResponseActionButton.tsx","../src/ResponseActions/ResponseActions.test.tsx","../src/ResponseActions/ResponseActions.tsx","../src/ResponseActions/index.ts","../src/Settings/SettingsForm.test.tsx","../src/Settings/SettingsForm.tsx","../src/Settings/index.ts","../src/SourceDetailsMenuItem/SourceDetailsMenuItem.tsx","../src/SourceDetailsMenuItem/index.ts","../src/SourcesCard/SourcesCard.test.tsx","../src/SourcesCard/SourcesCard.tsx","../src/SourcesCard/index.ts","../src/TermsOfUse/TermsOfUse.test.tsx","../src/TermsOfUse/TermsOfUse.tsx","../src/TermsOfUse/index.ts","../src/__mocks__/rehype-unwrap-images.tsx"],"version":"5.6.3"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@patternfly/chatbot",
3
- "version": "2.2.0-prerelease.24",
3
+ "version": "2.2.0-prerelease.26",
4
4
  "description": "This library provides React components based on PatternFly 6 that can be used to build chatbots.",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -39,6 +39,7 @@
39
39
  "react-markdown": "^9.0.1",
40
40
  "react-syntax-highlighter": "^15.5.0",
41
41
  "remark-gfm": "^4.0.0",
42
+ "rehype-unwrap-images": "^1.0.0",
42
43
  "path-browserify": "^1.0.1"
43
44
  },
44
45
  "peerDependencies": {
@@ -30,6 +30,8 @@ export const BotMessageExample: React.FunctionComponent = () => {
30
30
  return link;
31
31
  case 'table':
32
32
  return table;
33
+ case 'image':
34
+ return image;
33
35
  default:
34
36
  return;
35
37
  }
@@ -136,6 +138,8 @@ _Italic text, formatted with single underscores_
136
138
  | 3.0 | April 1, 2025 | Administrator
137
139
  `;
138
140
 
141
+ const image = `![Multi-colored wavy lines on a black background](https://cdn.dribbble.com/userupload/10651749/file/original-8a07b8e39d9e8bf002358c66fce1223e.gif)`;
142
+
139
143
  return (
140
144
  <>
141
145
  <Message name="Bot" role="bot" avatar={patternflyAvatar} content={`Text-based message from a bot named "Bot"`} />
@@ -234,6 +238,13 @@ _Italic text, formatted with single underscores_
234
238
  label="Table"
235
239
  id="table"
236
240
  />
241
+ <Radio
242
+ isChecked={variant === 'image'}
243
+ onChange={() => setVariant('image')}
244
+ name="bot-message-type"
245
+ label="Image"
246
+ id="image"
247
+ />
237
248
  </FormGroup>
238
249
  </Form>
239
250
  <Message
@@ -19,6 +19,7 @@ propComponents:
19
19
  'FileDropZone',
20
20
  'PreviewAttachment',
21
21
  'Message',
22
+ 'MessageExtraContent',
22
23
  'PreviewAttachment',
23
24
  'ActionProps',
24
25
  'SourcesCardProps',
@@ -165,6 +166,16 @@ Messages from users have a different background color to differentiate them from
165
166
 
166
167
  ```
167
168
 
169
+ ### Custom message content
170
+
171
+ **Caution:** Take care when using this feature. It can cause you to stray from accessibility and design best practice standards. If you frequently need add the same component via custom message content, reach out to the PatternFly team. If there's a consistent need for a certain component, we can look into adding native support for additional features.
172
+
173
+ You can add custom content to specific parts of a `<Message>` via the `extraContent` prop, including additional components (like timestamps, badges, or custom elements). This prop allows you to create dynamic and reusable elements for various use cases, without changing the default message layout.
174
+
175
+ ```js file="./UserMessageWithExtraContent.tsx"
176
+
177
+ ```
178
+
168
179
  ## File attachments
169
180
 
170
181
  ### Messages with attachments
@@ -30,6 +30,8 @@ export const UserMessageExample: React.FunctionComponent = () => {
30
30
  return link;
31
31
  case 'table':
32
32
  return table;
33
+ case 'image':
34
+ return image;
33
35
  default:
34
36
  return;
35
37
  }
@@ -136,6 +138,8 @@ _Italic text, formatted with single underscores_
136
138
  | 3.0 | April 1, 2025 | Administrator
137
139
  `;
138
140
 
141
+ const image = `![Multi-colored wavy lines on a black background](https://cdn.dribbble.com/userupload/10651749/file/original-8a07b8e39d9e8bf002358c66fce1223e.gif)`;
142
+
139
143
  return (
140
144
  <>
141
145
  <Message
@@ -224,6 +228,13 @@ _Italic text, formatted with single underscores_
224
228
  label="Table"
225
229
  id="user-table"
226
230
  />
231
+ <Radio
232
+ isChecked={variant === 'image'}
233
+ onChange={() => setVariant('image')}
234
+ name="user-message-type"
235
+ label="Image"
236
+ id="user-image"
237
+ />
227
238
  </FormGroup>
228
239
  </Form>
229
240
  <Message
@@ -0,0 +1,54 @@
1
+ import React from 'react';
2
+
3
+ import Message from '@patternfly/chatbot/dist/dynamic/Message';
4
+ import userAvatar from './user_avatar.svg';
5
+ import { Alert, Badge, Button, Card, CardBody, CardFooter, CardTitle } from '@patternfly/react-core';
6
+
7
+ const UserActionEndContent = () => {
8
+ // eslint-disable-next-line no-console
9
+ const onClick = () => console.log('custom button click');
10
+ return (
11
+ <React.Fragment>
12
+ <Button variant="secondary" ouiaId="Secondary" onClick={onClick}>
13
+ End content button
14
+ </Button>
15
+ <Alert variant="danger" title="Danger alert title" ouiaId="DangerAlert" />
16
+ </React.Fragment>
17
+ );
18
+ };
19
+
20
+ const CardInformationAfterMainContent = () => (
21
+ <Card ouiaId="BasicCard">
22
+ <CardTitle>This is content card after main content</CardTitle>
23
+ <CardBody>Body</CardBody>
24
+ <CardFooter>Footer</CardFooter>
25
+ </Card>
26
+ );
27
+
28
+ const BeforeMainContent = () => (
29
+ <div>
30
+ <Badge key={1} isRead>
31
+ 7
32
+ </Badge>
33
+ <Badge key={2} isRead>
34
+ 24
35
+ </Badge>
36
+ </div>
37
+ );
38
+
39
+ export const UserMessageWithExtraContent: React.FunctionComponent = () => (
40
+ <>
41
+ <Message
42
+ avatar={userAvatar}
43
+ name="User"
44
+ role="user"
45
+ content="This is a main message."
46
+ timestamp="1 hour ago"
47
+ extraContent={{
48
+ beforeMainContent: <BeforeMainContent />,
49
+ afterMainContent: <CardInformationAfterMainContent />,
50
+ endContent: <UserActionEndContent />
51
+ }}
52
+ />
53
+ </>
54
+ );
@@ -0,0 +1,9 @@
1
+ .pf-chatbot__message-image {
2
+ border-radius: var(--pf-t--global--border--radius--small);
3
+ max-width: 37.5rem; // 600px
4
+ max-height: 25rem; // 400px
5
+ min-height: 6.25rem; // 100px
6
+ // with rehypePlugins={[rehypeUnwrapImages]}, image is not wrapped in p if it is by itself
7
+ // however, if in a paragraph tag as part of other content, it should be a block element
8
+ display: block;
9
+ }
@@ -0,0 +1,14 @@
1
+ // ============================================================================
2
+ // Chatbot Main - Message - Content - Image
3
+ // ============================================================================
4
+
5
+ import React from 'react';
6
+ import { ExtraProps } from 'react-markdown';
7
+
8
+ const ImageMessage = ({ children, ...props }: JSX.IntrinsicElements['img'] & ExtraProps) => (
9
+ <img className="pf-chatbot__message-image" {...props}>
10
+ {children}
11
+ </img>
12
+ );
13
+
14
+ export default ImageMessage;
@@ -138,6 +138,8 @@ const EMPTY_TABLE = `
138
138
 
139
139
  `;
140
140
 
141
+ const IMAGE = `![Multi-colored wavy lines on a black background](https://cdn.dribbble.com/userupload/10651749/file/original-8a07b8e39d9e8bf002358c66fce1223e.gif)`;
142
+
141
143
  const checkListItemsRendered = () => {
142
144
  const items = ['Item 1', 'Item 2', 'Item 3'];
143
145
  expect(screen.getAllByRole('listitem')).toHaveLength(3);
@@ -627,4 +629,122 @@ describe('Message', () => {
627
629
  render(<Message avatar="./img" role="user" name="User" content={TABLE} tableProps={{ 'aria-label': 'Test' }} />);
628
630
  expect(screen.getByRole('grid', { name: /Test/i })).toBeTruthy();
629
631
  });
632
+ it('should render beforeMainContent with main content', () => {
633
+ const mainContent = 'Main message content';
634
+ const beforeMainContentText = 'Before main content';
635
+ const beforeMainContent = <div>{beforeMainContentText}</div>;
636
+
637
+ render(
638
+ <Message avatar="./img" role="user" name="User" content={mainContent} extraContent={{ beforeMainContent }} />
639
+ );
640
+
641
+ expect(screen.getByText(beforeMainContentText)).toBeTruthy();
642
+ expect(screen.getByText(mainContent)).toBeTruthy();
643
+ });
644
+ it('should render afterMainContent with main content', () => {
645
+ const mainContent = 'Main message content';
646
+ const afterMainContentText = 'After main content';
647
+ const afterMainContent = <div>{afterMainContentText}</div>;
648
+
649
+ render(
650
+ <Message avatar="./img" role="user" name="User" content={mainContent} extraContent={{ afterMainContent }} />
651
+ );
652
+
653
+ expect(screen.getByText(afterMainContentText)).toBeTruthy();
654
+ expect(screen.getByText(mainContent)).toBeTruthy();
655
+ });
656
+
657
+ it('should render endContent with main content', () => {
658
+ const mainContent = 'Main message content';
659
+ const endMainContentText = 'End content';
660
+ const endContent = <div>{endMainContentText}</div>;
661
+
662
+ render(<Message avatar="./img" role="user" name="User" content={mainContent} extraContent={{ endContent }} />);
663
+
664
+ expect(screen.getByText(endMainContentText)).toBeTruthy();
665
+ expect(screen.getByText(mainContent)).toBeTruthy();
666
+ });
667
+ it('should render all parts of extraContent with main content', () => {
668
+ const beforeMainContent = <div>Before main content</div>;
669
+ const afterMainContent = <div>After main content</div>;
670
+ const endContent = <div>End content</div>;
671
+
672
+ render(
673
+ <Message
674
+ avatar="./img"
675
+ role="user"
676
+ name="User"
677
+ content="Main message content"
678
+ extraContent={{ beforeMainContent, afterMainContent, endContent }}
679
+ />
680
+ );
681
+
682
+ expect(screen.getByText('Before main content')).toBeTruthy();
683
+ expect(screen.getByText('Main message content')).toBeTruthy();
684
+ expect(screen.getByText('After main content')).toBeTruthy();
685
+ expect(screen.getByText('End content')).toBeTruthy();
686
+ });
687
+
688
+ it('should not render extraContent when not provided', () => {
689
+ render(<Message avatar="./img" role="user" name="User" content="Main message content" />);
690
+
691
+ // Ensure no extraContent is rendered
692
+ expect(screen.getByText('Main message content')).toBeTruthy();
693
+ expect(screen.queryByText('Before main content')).toBeFalsy();
694
+ expect(screen.queryByText('After main content')).toBeFalsy();
695
+ expect(screen.queryByText('end message content')).toBeFalsy();
696
+ });
697
+
698
+ it('should handle undefined or null values in extraContent gracefully', () => {
699
+ render(
700
+ <Message
701
+ avatar="./img"
702
+ role="user"
703
+ name="User"
704
+ content="Main message content"
705
+ extraContent={{ beforeMainContent: null, afterMainContent: undefined, endContent: null }}
706
+ />
707
+ );
708
+
709
+ // Ensure that no extraContent is rendered if they are null or undefined
710
+ expect(screen.getByText('Main message content')).toBeTruthy();
711
+ expect(screen.queryByText('Before main content')).toBeFalsy();
712
+ expect(screen.queryByText('After main content')).toBeFalsy();
713
+ expect(screen.queryByText('end message content')).toBeFalsy();
714
+ });
715
+ it('should render JSX in extraContent correctly', () => {
716
+ const beforeMainContent = (
717
+ <div data-testid="before-main-content">
718
+ <strong>Bold before content</strong>
719
+ </div>
720
+ );
721
+ const afterMainContent = (
722
+ <div data-testid="after-main-content">
723
+ <strong>Bold after content</strong>
724
+ </div>
725
+ );
726
+ const endContent = (
727
+ <div data-testid="end-main-content">
728
+ <strong>Bold end content</strong>
729
+ </div>
730
+ );
731
+ render(
732
+ <Message
733
+ avatar="./img"
734
+ role="user"
735
+ name="User"
736
+ content="Main message content"
737
+ extraContent={{ beforeMainContent, afterMainContent, endContent }}
738
+ />
739
+ );
740
+
741
+ // Check that the JSX is correctly rendered
742
+ expect(screen.getByTestId('before-main-content')).toContainHTML('<strong>Bold before content</strong>');
743
+ expect(screen.getByTestId('after-main-content')).toContainHTML('<strong>Bold after content</strong>');
744
+ expect(screen.getByTestId('end-main-content')).toContainHTML('<strong>Bold end content</strong>');
745
+ });
746
+ it('should handle image correctly', () => {
747
+ render(<Message avatar="./img" role="user" name="User" content={IMAGE} />);
748
+ expect(screen.getByRole('img', { name: /Multi-colored wavy lines on a black background/i })).toBeTruthy();
749
+ });
630
750
  });
@@ -2,7 +2,7 @@
2
2
  // Chatbot Main - Message
3
3
  // ============================================================================
4
4
 
5
- import React from 'react';
5
+ import React, { ReactNode } from 'react';
6
6
 
7
7
  import Markdown from 'react-markdown';
8
8
  import remarkGfm from 'remark-gfm';
@@ -36,6 +36,8 @@ import TbodyMessage from './TableMessage/TbodyMessage';
36
36
  import TheadMessage from './TableMessage/TheadMessage';
37
37
  import ThMessage from './TableMessage/ThMessage';
38
38
  import { TableProps } from '@patternfly/react-table';
39
+ import ImageMessage from './ImageMessage/ImageMessage';
40
+ import rehypeUnwrapImages from 'rehype-unwrap-images';
39
41
 
40
42
  export interface MessageAttachment {
41
43
  /** Name of file attached to the message */
@@ -56,6 +58,17 @@ export interface MessageAttachment {
56
58
  spinnerTestId?: string;
57
59
  }
58
60
 
61
+ export interface MessageExtraContent {
62
+ /** Content to display before the main content */
63
+ beforeMainContent?: ReactNode;
64
+
65
+ /** Content to display after the main content */
66
+ afterMainContent?: ReactNode;
67
+
68
+ /** Content to display at the end */
69
+ endContent?: ReactNode;
70
+ }
71
+
59
72
  export interface MessageProps extends Omit<React.HTMLProps<HTMLDivElement>, 'role'> {
60
73
  /** Unique id for message */
61
74
  id?: string;
@@ -63,6 +76,8 @@ export interface MessageProps extends Omit<React.HTMLProps<HTMLDivElement>, 'rol
63
76
  role: 'user' | 'bot';
64
77
  /** Message content */
65
78
  content?: string;
79
+ /** Extra Message content */
80
+ extraContent?: MessageExtraContent;
66
81
  /** Name of the user */
67
82
  name?: string;
68
83
  /** Avatar src for the user */
@@ -123,6 +138,7 @@ export interface MessageProps extends Omit<React.HTMLProps<HTMLDivElement>, 'rol
123
138
  export const MessageBase: React.FunctionComponent<MessageProps> = ({
124
139
  role,
125
140
  content,
141
+ extraContent,
126
142
  name,
127
143
  avatar,
128
144
  timestamp,
@@ -145,6 +161,7 @@ export const MessageBase: React.FunctionComponent<MessageProps> = ({
145
161
  tableProps,
146
162
  ...props
147
163
  }: MessageProps) => {
164
+ const { beforeMainContent, afterMainContent, endContent } = extraContent || {};
148
165
  let avatarClassName;
149
166
  if (avatarProps && 'className' in avatarProps) {
150
167
  const { className, ...rest } = avatarProps;
@@ -189,40 +206,46 @@ export const MessageBase: React.FunctionComponent<MessageProps> = ({
189
206
  {isLoading ? (
190
207
  <MessageLoading loadingWord={loadingWord} />
191
208
  ) : (
192
- <Markdown
193
- components={{
194
- p: (props) => <TextMessage component={ContentVariants.p} {...props} />,
195
- code: ({ children, ...props }) => (
196
- <CodeBlockMessage {...props} {...codeBlockProps}>
197
- {children}
198
- </CodeBlockMessage>
199
- ),
200
- h1: (props) => <TextMessage component={ContentVariants.h1} {...props} />,
201
- h2: (props) => <TextMessage component={ContentVariants.h2} {...props} />,
202
- h3: (props) => <TextMessage component={ContentVariants.h3} {...props} />,
203
- h4: (props) => <TextMessage component={ContentVariants.h4} {...props} />,
204
- h5: (props) => <TextMessage component={ContentVariants.h5} {...props} />,
205
- h6: (props) => <TextMessage component={ContentVariants.h6} {...props} />,
206
- blockquote: (props) => <TextMessage component={ContentVariants.blockquote} {...props} />,
207
- ul: (props) => <UnorderedListMessage {...props} />,
208
- ol: (props) => <OrderedListMessage {...props} />,
209
- li: (props) => <ListItemMessage {...props} />,
210
- table: (props) => <TableMessage {...props} {...tableProps} />,
211
- tbody: (props) => <TbodyMessage {...props} />,
212
- thead: (props) => <TheadMessage {...props} />,
213
- tr: (props) => <TrMessage {...props} />,
214
- td: (props) => {
215
- // Conflicts with Td type
216
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
217
- const { width, ...rest } = props;
218
- return <TdMessage {...rest} />;
219
- },
220
- th: (props) => <ThMessage {...props} />
221
- }}
222
- remarkPlugins={[remarkGfm]}
223
- >
224
- {content}
225
- </Markdown>
209
+ <>
210
+ {beforeMainContent && <>{beforeMainContent}</>}
211
+ <Markdown
212
+ components={{
213
+ p: (props) => <TextMessage component={ContentVariants.p} {...props} />,
214
+ code: ({ children, ...props }) => (
215
+ <CodeBlockMessage {...props} {...codeBlockProps}>
216
+ {children}
217
+ </CodeBlockMessage>
218
+ ),
219
+ h1: (props) => <TextMessage component={ContentVariants.h1} {...props} />,
220
+ h2: (props) => <TextMessage component={ContentVariants.h2} {...props} />,
221
+ h3: (props) => <TextMessage component={ContentVariants.h3} {...props} />,
222
+ h4: (props) => <TextMessage component={ContentVariants.h4} {...props} />,
223
+ h5: (props) => <TextMessage component={ContentVariants.h5} {...props} />,
224
+ h6: (props) => <TextMessage component={ContentVariants.h6} {...props} />,
225
+ blockquote: (props) => <TextMessage component={ContentVariants.blockquote} {...props} />,
226
+ ul: (props) => <UnorderedListMessage {...props} />,
227
+ ol: (props) => <OrderedListMessage {...props} />,
228
+ li: (props) => <ListItemMessage {...props} />,
229
+ table: (props) => <TableMessage {...props} {...tableProps} />,
230
+ tbody: (props) => <TbodyMessage {...props} />,
231
+ thead: (props) => <TheadMessage {...props} />,
232
+ tr: (props) => <TrMessage {...props} />,
233
+ td: (props) => {
234
+ // Conflicts with Td type
235
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
236
+ const { width, ...rest } = props;
237
+ return <TdMessage {...rest} />;
238
+ },
239
+ th: (props) => <ThMessage {...props} />,
240
+ img: (props) => <ImageMessage {...props} />
241
+ }}
242
+ remarkPlugins={[remarkGfm]}
243
+ rehypePlugins={[rehypeUnwrapImages]}
244
+ >
245
+ {content}
246
+ </Markdown>
247
+ {afterMainContent && <>{afterMainContent}</>}
248
+ </>
226
249
  )}
227
250
  {!isLoading && sources && <SourcesCard {...sources} />}
228
251
  {quickStarts && quickStarts.quickStart && (
@@ -264,6 +287,7 @@ export const MessageBase: React.FunctionComponent<MessageProps> = ({
264
287
  ))}
265
288
  </div>
266
289
  )}
290
+ {!isLoading && endContent && <>{endContent}</>}
267
291
  </div>
268
292
  </div>
269
293
  </section>
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import { ButtonProps, DropEvent, TextArea, TextAreaProps } from '@patternfly/react-core';
2
+ import { ButtonProps, DropEvent, TextArea, TextAreaProps, TooltipProps } from '@patternfly/react-core';
3
3
 
4
4
  // Import Chatbot components
5
5
  import SendButton from './SendButton';
@@ -53,13 +53,19 @@ export interface MessageBarProps extends TextAreaProps {
53
53
  isSendButtonDisabled?: boolean;
54
54
  /** Prop to allow passage of additional props to buttons */
55
55
  buttonProps?: {
56
- attach?: { tooltipContent?: string; props?: ButtonProps; inputTestId?: string };
57
- stop?: { tooltipContent?: string; props?: ButtonProps };
58
- send?: { tooltipContent?: string; props?: ButtonProps };
56
+ attach?: {
57
+ tooltipContent?: string;
58
+ props?: ButtonProps;
59
+ inputTestId?: string;
60
+ tooltipProps?: Omit<TooltipProps, 'content'>;
61
+ };
62
+ stop?: { tooltipContent?: string; props?: ButtonProps; tooltipProps?: Omit<TooltipProps, 'content'> };
63
+ send?: { tooltipContent?: string; props?: ButtonProps; tooltipProps?: Omit<TooltipProps, 'content'> };
59
64
  microphone?: {
60
65
  tooltipContent?: { active?: string; inactive?: string };
61
66
  language?: string;
62
67
  props?: ButtonProps;
68
+ tooltipProps?: Omit<TooltipProps, 'content'>;
63
69
  };
64
70
  };
65
71
  /** A callback for when the text area value changes. */
@@ -19,7 +19,7 @@ export interface MicrophoneButtonProps extends ButtonProps {
19
19
  /** Callback to update the message value once speech recognition is complete */
20
20
  onSpeechRecognition: React.Dispatch<React.SetStateAction<string>>;
21
21
  /** Props to control the PF Tooltip component */
22
- tooltipProps?: TooltipProps;
22
+ tooltipProps?: Omit<TooltipProps, 'content'>;
23
23
  /** English text "Use microphone" and "Stop listening" used in the tooltip */
24
24
  tooltipContent?: { active?: string; inactive?: string };
25
25
  /** Locale code for language speech recognition is conducted in. This should be in the format 'en-US', a.k.a. the ISO 639-1 code, a dash, and the ISO_3166-1 code. */
@@ -0,0 +1,3 @@
1
+ const RehypeUnwrapImages = () => null;
2
+
3
+ export default RehypeUnwrapImages;
package/src/main.scss CHANGED
@@ -16,6 +16,7 @@
16
16
  @import './FileDropZone/FileDropZone';
17
17
  @import './Message/Message';
18
18
  @import './Message/CodeBlockMessage/CodeBlockMessage';
19
+ @import './Message/ImageMessage/ImageMessage';
19
20
  @import './Message/TextMessage/TextMessage';
20
21
  @import './Message/ListMessage/ListMessage';
21
22
  @import './Message/TableMessage/TableMessage';