@patternfly/chatbot 6.5.0-prerelease.27 → 6.5.0-prerelease.28

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 (138) hide show
  1. package/dist/cjs/MarkdownContent/MarkdownContent.d.ts +7 -2
  2. package/dist/cjs/Message/ErrorMessage/ErrorMessage.d.ts +15 -1
  3. package/dist/cjs/Message/ErrorMessage/ErrorMessage.js +5 -3
  4. package/dist/cjs/Message/ErrorMessage/ErrorMessage.test.d.ts +1 -0
  5. package/dist/cjs/Message/ErrorMessage/ErrorMessage.test.js +30 -0
  6. package/dist/cjs/Message/Message.d.ts +2 -0
  7. package/dist/cjs/Message/Message.js +5 -5
  8. package/dist/cjs/Message/MessageAndActions/MessageAndActions.d.ts +14 -0
  9. package/dist/cjs/Message/MessageAndActions/MessageAndActions.js +22 -0
  10. package/dist/cjs/Message/MessageAndActions/MessageAndActions.test.d.ts +1 -0
  11. package/dist/cjs/Message/MessageAndActions/MessageAndActions.test.js +25 -0
  12. package/dist/cjs/Message/MessageAndActions/index.d.ts +1 -0
  13. package/dist/cjs/Message/MessageAndActions/index.js +17 -0
  14. package/dist/cjs/Message/MessageAttachments/MessageAttachmentItem.d.ts +13 -0
  15. package/dist/cjs/Message/MessageAttachments/MessageAttachmentItem.js +22 -0
  16. package/dist/cjs/Message/MessageAttachments/MessageAttachmentItem.test.d.ts +1 -0
  17. package/dist/cjs/Message/MessageAttachments/MessageAttachmentItem.test.js +25 -0
  18. package/dist/cjs/Message/MessageAttachments/MessageAttachmentsContainer.d.ts +13 -0
  19. package/dist/cjs/Message/MessageAttachments/MessageAttachmentsContainer.js +22 -0
  20. package/dist/cjs/Message/MessageAttachments/MessageAttachmentsContainer.test.d.ts +1 -0
  21. package/dist/cjs/Message/MessageAttachments/MessageAttachmentsContainer.test.js +25 -0
  22. package/dist/cjs/Message/MessageAttachments/index.d.ts +2 -0
  23. package/dist/cjs/Message/MessageAttachments/index.js +18 -0
  24. package/dist/cjs/Message/MessageInput.d.ts +1 -1
  25. package/dist/cjs/Message/MessageInput.js +3 -1
  26. package/dist/cjs/Message/MessageLoading.d.ts +13 -4
  27. package/dist/cjs/Message/MessageLoading.js +19 -5
  28. package/dist/cjs/Message/MessageLoading.test.d.ts +1 -0
  29. package/dist/cjs/Message/MessageLoading.test.js +25 -0
  30. package/dist/cjs/Message/QuickResponse/QuickResponse.js +3 -2
  31. package/dist/cjs/Message/QuickResponse/QuickResponse.test.d.ts +1 -0
  32. package/dist/cjs/Message/QuickResponse/QuickResponse.test.js +109 -0
  33. package/dist/cjs/Message/QuickResponse/index.d.ts +1 -0
  34. package/dist/cjs/Message/QuickResponse/index.js +17 -0
  35. package/dist/cjs/Message/QuickStarts/QuickStartTile.d.ts +1 -1
  36. package/dist/cjs/Message/QuickStarts/QuickStartTile.js +3 -2
  37. package/dist/cjs/Message/QuickStarts/index.d.ts +2 -0
  38. package/dist/cjs/Message/QuickStarts/index.js +18 -0
  39. package/dist/cjs/Message/UserFeedback/UserFeedback.d.ts +1 -1
  40. package/dist/cjs/Message/UserFeedback/UserFeedback.js +3 -1
  41. package/dist/cjs/Message/UserFeedback/UserFeedbackComplete.d.ts +1 -1
  42. package/dist/cjs/Message/UserFeedback/UserFeedbackComplete.js +3 -2
  43. package/dist/cjs/Message/UserFeedback/index.d.ts +2 -0
  44. package/dist/cjs/Message/UserFeedback/index.js +18 -0
  45. package/dist/cjs/Message/index.d.ts +8 -0
  46. package/dist/cjs/Message/index.js +8 -0
  47. package/dist/cjs/ResponseActions/ResponseActions.d.ts +4 -0
  48. package/dist/cjs/ResponseActions/ResponseActionsGroups.d.ts +13 -0
  49. package/dist/cjs/ResponseActions/ResponseActionsGroups.js +22 -0
  50. package/dist/cjs/ResponseActions/ResponseActionsGroups.test.d.ts +1 -0
  51. package/dist/cjs/ResponseActions/ResponseActionsGroups.test.js +25 -0
  52. package/dist/cjs/ResponseActions/index.d.ts +1 -0
  53. package/dist/cjs/ResponseActions/index.js +1 -0
  54. package/dist/esm/MarkdownContent/MarkdownContent.d.ts +7 -2
  55. package/dist/esm/Message/ErrorMessage/ErrorMessage.d.ts +15 -1
  56. package/dist/esm/Message/ErrorMessage/ErrorMessage.js +3 -3
  57. package/dist/esm/Message/ErrorMessage/ErrorMessage.test.d.ts +1 -0
  58. package/dist/esm/Message/ErrorMessage/ErrorMessage.test.js +25 -0
  59. package/dist/esm/Message/Message.d.ts +2 -0
  60. package/dist/esm/Message/Message.js +5 -5
  61. package/dist/esm/Message/MessageAndActions/MessageAndActions.d.ts +14 -0
  62. package/dist/esm/Message/MessageAndActions/MessageAndActions.js +18 -0
  63. package/dist/esm/Message/MessageAndActions/MessageAndActions.test.d.ts +1 -0
  64. package/dist/esm/Message/MessageAndActions/MessageAndActions.test.js +20 -0
  65. package/dist/esm/Message/MessageAndActions/index.d.ts +1 -0
  66. package/dist/esm/Message/MessageAndActions/index.js +1 -0
  67. package/dist/esm/Message/MessageAttachments/MessageAttachmentItem.d.ts +13 -0
  68. package/dist/esm/Message/MessageAttachments/MessageAttachmentItem.js +18 -0
  69. package/dist/esm/Message/MessageAttachments/MessageAttachmentItem.test.d.ts +1 -0
  70. package/dist/esm/Message/MessageAttachments/MessageAttachmentItem.test.js +20 -0
  71. package/dist/esm/Message/MessageAttachments/MessageAttachmentsContainer.d.ts +13 -0
  72. package/dist/esm/Message/MessageAttachments/MessageAttachmentsContainer.js +18 -0
  73. package/dist/esm/Message/MessageAttachments/MessageAttachmentsContainer.test.d.ts +1 -0
  74. package/dist/esm/Message/MessageAttachments/MessageAttachmentsContainer.test.js +20 -0
  75. package/dist/esm/Message/MessageAttachments/index.d.ts +2 -0
  76. package/dist/esm/Message/MessageAttachments/index.js +2 -0
  77. package/dist/esm/Message/MessageInput.d.ts +1 -1
  78. package/dist/esm/Message/MessageInput.js +1 -1
  79. package/dist/esm/Message/MessageLoading.d.ts +13 -4
  80. package/dist/esm/Message/MessageLoading.js +16 -4
  81. package/dist/esm/Message/MessageLoading.test.d.ts +1 -0
  82. package/dist/esm/Message/MessageLoading.test.js +20 -0
  83. package/dist/esm/Message/QuickResponse/QuickResponse.js +3 -2
  84. package/dist/esm/Message/QuickResponse/QuickResponse.test.d.ts +1 -0
  85. package/dist/esm/Message/QuickResponse/QuickResponse.test.js +104 -0
  86. package/dist/esm/Message/QuickResponse/index.d.ts +1 -0
  87. package/dist/esm/Message/QuickResponse/index.js +1 -0
  88. package/dist/esm/Message/QuickStarts/QuickStartTile.d.ts +1 -1
  89. package/dist/esm/Message/QuickStarts/QuickStartTile.js +1 -1
  90. package/dist/esm/Message/QuickStarts/index.d.ts +2 -0
  91. package/dist/esm/Message/QuickStarts/index.js +2 -0
  92. package/dist/esm/Message/UserFeedback/UserFeedback.d.ts +1 -1
  93. package/dist/esm/Message/UserFeedback/UserFeedback.js +1 -1
  94. package/dist/esm/Message/UserFeedback/UserFeedbackComplete.d.ts +1 -1
  95. package/dist/esm/Message/UserFeedback/UserFeedbackComplete.js +1 -2
  96. package/dist/esm/Message/UserFeedback/index.d.ts +2 -0
  97. package/dist/esm/Message/UserFeedback/index.js +2 -0
  98. package/dist/esm/Message/index.d.ts +8 -0
  99. package/dist/esm/Message/index.js +8 -0
  100. package/dist/esm/ResponseActions/ResponseActions.d.ts +4 -0
  101. package/dist/esm/ResponseActions/ResponseActionsGroups.d.ts +13 -0
  102. package/dist/esm/ResponseActions/ResponseActionsGroups.js +18 -0
  103. package/dist/esm/ResponseActions/ResponseActionsGroups.test.d.ts +1 -0
  104. package/dist/esm/ResponseActions/ResponseActionsGroups.test.js +20 -0
  105. package/dist/esm/ResponseActions/index.d.ts +1 -0
  106. package/dist/esm/ResponseActions/index.js +1 -0
  107. package/dist/tsconfig.tsbuildinfo +1 -1
  108. package/package.json +1 -1
  109. package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithCustomStructure.tsx +102 -0
  110. package/patternfly-docs/content/extensions/chatbot/examples/Messages/Messages.md +55 -10
  111. package/src/MarkdownContent/MarkdownContent.tsx +7 -2
  112. package/src/Message/ErrorMessage/ErrorMessage.test.tsx +38 -0
  113. package/src/Message/ErrorMessage/ErrorMessage.tsx +17 -2
  114. package/src/Message/Message.tsx +75 -64
  115. package/src/Message/MessageAndActions/MessageAndActions.test.tsx +23 -0
  116. package/src/Message/MessageAndActions/MessageAndActions.tsx +22 -0
  117. package/src/Message/MessageAndActions/index.ts +1 -0
  118. package/src/Message/MessageAttachments/MessageAttachmentItem.test.tsx +23 -0
  119. package/src/Message/MessageAttachments/MessageAttachmentItem.tsx +25 -0
  120. package/src/Message/MessageAttachments/MessageAttachmentsContainer.test.tsx +23 -0
  121. package/src/Message/MessageAttachments/MessageAttachmentsContainer.tsx +25 -0
  122. package/src/Message/MessageAttachments/index.ts +2 -0
  123. package/src/Message/MessageInput.tsx +1 -1
  124. package/src/Message/MessageLoading.test.tsx +23 -0
  125. package/src/Message/MessageLoading.tsx +17 -2
  126. package/src/Message/QuickResponse/QuickResponse.test.tsx +131 -0
  127. package/src/Message/QuickResponse/QuickResponse.tsx +3 -2
  128. package/src/Message/QuickResponse/index.ts +1 -0
  129. package/src/Message/QuickStarts/QuickStartTile.tsx +1 -1
  130. package/src/Message/QuickStarts/index.ts +2 -0
  131. package/src/Message/UserFeedback/UserFeedback.tsx +1 -1
  132. package/src/Message/UserFeedback/UserFeedbackComplete.tsx +1 -4
  133. package/src/Message/UserFeedback/index.ts +2 -0
  134. package/src/Message/index.ts +8 -0
  135. package/src/ResponseActions/ResponseActions.tsx +6 -0
  136. package/src/ResponseActions/ResponseActionsGroups.test.tsx +23 -0
  137. package/src/ResponseActions/ResponseActionsGroups.tsx +28 -0
  138. package/src/ResponseActions/index.ts +1 -0
@@ -0,0 +1,102 @@
1
+ import { FunctionComponent } from 'react';
2
+ import Message, {
3
+ ErrorMessage,
4
+ MessageAndActions,
5
+ MessageAttachmentItem,
6
+ MessageAttachmentsContainer,
7
+ MessageLoading
8
+ } from '@patternfly/chatbot/dist/dynamic/Message';
9
+ import MarkdownContent from '@patternfly/chatbot/dist/dynamic/MarkdownContent';
10
+ import ToolCall from '@patternfly/chatbot/dist/dynamic/ToolCall';
11
+ import ToolResponse from '@patternfly/chatbot/dist/dynamic/ToolResponse';
12
+ import FileDetailsLabel from '@patternfly/chatbot/dist/dynamic/FileDetailsLabel';
13
+ import ResponseActions, { ResponseActionsGroups } from '@patternfly/chatbot/dist/dynamic/ResponseActions';
14
+ import patternflyAvatar from './patternfly_avatar.jpg';
15
+ import userAvatar from './user_avatar.svg';
16
+
17
+ const handlePositiveResponse = () => {
18
+ // Handle positive response
19
+ };
20
+
21
+ const handleNegativeResponse = () => {
22
+ // Handle negative response
23
+ };
24
+
25
+ const handleCopy = () => {
26
+ // Handle copy action
27
+ };
28
+
29
+ const handleDownload = () => {
30
+ // Handle download action
31
+ };
32
+
33
+ const handleListen = () => {
34
+ // Handle listen action
35
+ };
36
+
37
+ export const MessageWithCustomStructure: FunctionComponent = () => (
38
+ <>
39
+ <Message name="Bot" role="bot" avatar={patternflyAvatar}>
40
+ <MessageAndActions>
41
+ <MarkdownContent
42
+ content={`This is a basic message with a more custom, fine-tuned structure. You can pass markdown to the MarkdownContent component, such as **bold content with double asterisks** or _italic content with single underscores_.`}
43
+ />
44
+ <ToolCall titleText="Calling 'awesome_tool'" loadingText="Loading 'awesome_tool'" isLoading={true} />
45
+ <ToolResponse
46
+ toggleContent="Tool response: fetch_user_data"
47
+ subheading="Executed in 0.3 seconds"
48
+ body="Successfully retrieved user data from the database."
49
+ cardTitle="User Data Response"
50
+ cardBody="The tool returned 150 user records matching the specified criteria."
51
+ />
52
+ <ErrorMessage title="An issue placed within this custom structure." />
53
+ <MarkdownContent
54
+ isMarkdownDisabled
55
+ textComponent={`You can also pass plain text without markdown to the MarkdownContent component by passing the isMarkdownDisabled prop. Optionally, you can also use the textComponent prop instead of content.`}
56
+ />
57
+ <ToolCall titleText="Calling 'more_awesome_tool'" loadingText="Loading 'more_awesome_tool'" isLoading={true} />
58
+ <ToolCall titleText="Calling 'even_more_awesome_tool'" loadingText="Loading 'even_more_awesome_tool'" />
59
+ <MarkdownContent content={`You can even place a message loading state in the middle of a message:`} />
60
+ <MessageLoading loadingWord="Loading something in the middle of a custom structured message" />
61
+ <ResponseActionsGroups>
62
+ <ResponseActions
63
+ actions={{
64
+ positive: { onClick: handlePositiveResponse, ariaLabel: 'Good response' },
65
+ negative: { onClick: handleNegativeResponse, ariaLabel: 'Bad response' }
66
+ }}
67
+ persistActionSelection={true}
68
+ />
69
+ <ResponseActions
70
+ actions={{
71
+ copy: { onClick: handleCopy, ariaLabel: 'Copy' },
72
+ download: { onClick: handleDownload, ariaLabel: 'Download' }
73
+ }}
74
+ persistActionSelection={false}
75
+ />
76
+ <ResponseActions
77
+ actions={{
78
+ listen: { onClick: handleListen, ariaLabel: 'Listen' }
79
+ }}
80
+ persistActionSelection={true}
81
+ />
82
+ </ResponseActionsGroups>
83
+ </MessageAndActions>
84
+ </Message>
85
+ <Message name="User" role="user" avatar={userAvatar}>
86
+ <MessageAndActions>
87
+ <MarkdownContent content="This message is in the MessageAndActions container, and the file attachments below are in their own separate MessageAttachmentsContainer!" />
88
+ </MessageAndActions>
89
+ <MessageAttachmentsContainer>
90
+ <MessageAttachmentItem>
91
+ <FileDetailsLabel fileName="project-report.pdf" />
92
+ </MessageAttachmentItem>
93
+ <MessageAttachmentItem>
94
+ <FileDetailsLabel fileName="data-analysis.csv" />
95
+ </MessageAttachmentItem>
96
+ <MessageAttachmentItem>
97
+ <FileDetailsLabel fileName="presentation-slides.pptx" />
98
+ </MessageAttachmentItem>
99
+ </MessageAttachmentsContainer>
100
+ </Message>
101
+ </>
102
+ );
@@ -14,24 +14,40 @@ propComponents:
14
14
  [
15
15
  'AttachMenu',
16
16
  'AttachmentEdit',
17
- 'FileDetailsProps',
18
- 'FileDetailsLabelProps',
19
17
  'FileDropZone',
20
- 'PreviewAttachment',
21
18
  'Message',
22
- 'MessageExtraContent',
23
- 'PreviewAttachment',
19
+ 'ErrorMessage',
20
+ 'MessageLoadingProps',
21
+ 'MessageInputProps',
22
+ 'MessageAndActionsProps',
23
+ 'MarkdownContent',
24
+ 'QuickResponseProps',
25
+ 'QuickStartTileProps',
26
+ 'UserFeedback',
27
+ 'UserFeedbackComplete',
28
+ 'DeepThinking',
29
+ 'ToolCall',
30
+ 'ToolResponse',
31
+ 'SourcesCard',
32
+ 'ResponseActionsGroupsProps',
33
+ 'ResponseActionProps',
24
34
  'ActionProps',
25
- 'SourcesCardProps',
26
- 'UserFeedbackProps',
27
- 'UserFeedbackCompleteProps',
28
- 'QuickResponseProps'
35
+ 'MessageAttachmentsContainerProps',
36
+ 'MessageAttachmentItemProps',
37
+ 'FileDetailsProps',
38
+ 'FileDetailsLabelProps',
39
+ 'MessageExtraContent',
40
+ 'PreviewAttachment'
29
41
  ]
30
42
  sortValue: 3
31
43
  ---
32
44
 
33
- import Message from '@patternfly/chatbot/dist/dynamic/Message';
45
+ import Message, { ErrorMessage, MessageAndActions, MessageLoading, MessageAttachmentItem, MessageAttachmentsContainer } from '@patternfly/chatbot/dist/dynamic/Message';
46
+ import MarkdownContent from '@patternfly/chatbot/dist/dynamic/MarkdownContent';
34
47
  import MessageDivider from '@patternfly/chatbot/dist/dynamic/MessageDivider';
48
+ import ToolCall from '@patternfly/chatbot/dist/dynamic/ToolCall';
49
+ import ResponseActions, { ResponseActionsGroups } from '@patternfly/chatbot/dist/dynamic/ResponseActions';
50
+ import ToolResponse from '@patternfly/chatbot/dist/dynamic/ToolResponse';
35
51
  import { rehypeCodeBlockToggle } from '@patternfly/chatbot/dist/esm/Message/Plugins/rehypeCodeBlockToggle';
36
52
  import SourcesCard from '@patternfly/chatbot/dist/dynamic/SourcesCard';
37
53
  import { ArrowCircleDownIcon, ArrowRightIcon, CheckCircleIcon, CopyIcon, CubeIcon, CubesIcon, DownloadIcon, InfoCircleIcon, OutlinedQuestionCircleIcon, RedoIcon, RobotIcon, WrenchIcon } from '@patternfly/react-icons';
@@ -271,6 +287,35 @@ You can add custom content to specific parts of a `<Message>` via the `extraCont
271
287
 
272
288
  ```
273
289
 
290
+ ### Custom message structure
291
+
292
+ For more advanced use cases, you can build completely custom message structures by passing children directly to `<Message>`. This approach is useful when you need to customize the order or structure of message elements beyond what the standard props allow.
293
+
294
+ When creating custom message structures, you must follow an intended composable structure.
295
+
296
+ 1. **Message content and actions:** Wrap in `<MessageAndActions>`. This includes, but is not limited to:
297
+
298
+ - `<MarkdownContent>`: For rendering markdown or plain text content
299
+ - `<ErrorMessage>`
300
+ - `<MessageLoading>`
301
+ - `<MessageInput>`
302
+ - `<ToolCall>`
303
+ - `<ToolResponse>`
304
+ - `<DeepThinking>`
305
+ - `<QuickResponse>`
306
+ - `<QuickStartTile>`
307
+ - `<UserFeedback>` and `<UserFeedbackComplete>`
308
+ - `<SourcesCard>`
309
+ - `<ResponseActionsGroups>` and `<ResponseActions>`
310
+
311
+ 2. **File attachments:** Placed outside `<MessageAndActions>` and wrapped in attachment containers:
312
+ - `<MessageAttachmentsContainer>`: Container for all attachments
313
+ - `<MessageAttachmentItem>`: Individual attachment wrapper (contains `<FileDetailsLabel>` or other attachment components)
314
+
315
+ ```ts file="./MessageWithCustomStructure.tsx"
316
+
317
+ ```
318
+
274
319
  ## File attachments
275
320
 
276
321
  ### Messages with attachments
@@ -30,10 +30,15 @@ import SuperscriptMessage from '../Message/SuperscriptMessage/SuperscriptMessage
30
30
  import { ButtonProps } from '@patternfly/react-core';
31
31
  import { css } from '@patternfly/react-styles';
32
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
+ */
33
38
  export interface MarkdownContentProps {
34
- /** The markdown content to render */
39
+ /** The content to render. Supports markdown formatting by default, or plain text when isMarkdownDisabled is true. */
35
40
  content?: string;
36
- /** Disables markdown parsing, allowing only text input */
41
+ /** Disables markdown parsing, allowing only plain text input */
37
42
  isMarkdownDisabled?: boolean;
38
43
  /** Props for code blocks */
39
44
  codeBlockProps?: CodeBlockMessageProps;
@@ -0,0 +1,38 @@
1
+ import { render, screen } from '@testing-library/react';
2
+ import '@testing-library/jest-dom';
3
+ import ErrorMessage from './ErrorMessage';
4
+
5
+ test('Renders with title', () => {
6
+ render(<ErrorMessage title="Error occurred" />);
7
+
8
+ expect(screen.getByText('Error occurred')).toBeVisible();
9
+ });
10
+
11
+ test('Renders with children', () => {
12
+ render(<ErrorMessage title="Error occurred">This is the error message body</ErrorMessage>);
13
+
14
+ expect(screen.getByText('This is the error message body')).toBeVisible();
15
+ });
16
+
17
+ test('Renders with action links', () => {
18
+ const actionLinks = (
19
+ <a href="#retry" data-testid="retry-link">
20
+ Retry action link
21
+ </a>
22
+ );
23
+ render(<ErrorMessage title="Error occurred" actionLinks={actionLinks} />);
24
+
25
+ expect(screen.getByText('Retry action link')).toBeVisible();
26
+ });
27
+
28
+ test('Renders with custom className', () => {
29
+ render(<ErrorMessage title="Error occurred" className="custom-error-class" />);
30
+
31
+ expect(screen.getByText('Error occurred').parentElement).toHaveClass('custom-error-class');
32
+ });
33
+
34
+ test('Renders with spread props', () => {
35
+ render(<ErrorMessage title="Error occurred" id="test-error-id" />);
36
+
37
+ expect(screen.getByText('Error occurred').parentElement).toHaveAttribute('id', 'test-error-id');
38
+ });
@@ -4,8 +4,23 @@
4
4
 
5
5
  import { Alert, AlertProps } from '@patternfly/react-core';
6
6
 
7
- const ErrorMessage = ({ title, actionLinks, children, ...props }: AlertProps) => (
8
- <Alert isInline variant="danger" title={title} actionLinks={actionLinks} {...props}>
7
+ /**
8
+ * ErrorMessage displays an inline danger alert for error states in messages.
9
+ * Use this component when passing children to Message to display error information.
10
+ */
11
+ export interface ErrorMessageProps extends Partial<AlertProps> {
12
+ /** Content to display in the error alert body */
13
+ children?: React.ReactNode;
14
+ /** Additional classes for the error alert */
15
+ className?: string;
16
+ /** Title of the error alert */
17
+ title?: React.ReactNode;
18
+ /** Action links to display in the alert footer */
19
+ actionLinks?: React.ReactNode;
20
+ }
21
+
22
+ export const ErrorMessage = ({ title, actionLinks, children, className, ...props }: ErrorMessageProps) => (
23
+ <Alert isInline variant="danger" title={title} actionLinks={actionLinks} className={className} {...props}>
9
24
  {children}
10
25
  </Alert>
11
26
  );
@@ -67,6 +67,8 @@ export interface MessageExtraContent {
67
67
  }
68
68
 
69
69
  export interface MessageProps extends Omit<HTMLProps<HTMLDivElement>, 'role'> {
70
+ /** Children to render instead of the default message structure, allowing more fine-tuned message control. When provided, this will override the default rendering of content, toolResponse, deepThinking, toolCall, sources, quickStarts, actions, etc. */
71
+ children?: ReactNode;
70
72
  /** Unique id for message */
71
73
  id?: string;
72
74
  /** Role of the user sending the message */
@@ -193,6 +195,7 @@ export interface MessageProps extends Omit<HTMLProps<HTMLDivElement>, 'role'> {
193
195
  }
194
196
 
195
197
  export const MessageBase: FunctionComponent<MessageProps> = ({
198
+ children,
196
199
  role,
197
200
  content,
198
201
  extraContent,
@@ -341,74 +344,82 @@ export const MessageBase: FunctionComponent<MessageProps> = ({
341
344
  <Timestamp date={date}>{timestamp}</Timestamp>
342
345
  </div>
343
346
  <div className="pf-chatbot__message-response">
344
- <div className="pf-chatbot__message-and-actions">
345
- {renderMessage()}
346
- {afterMainContent && <>{afterMainContent}</>}
347
- {toolResponse && <ToolResponse {...toolResponse} />}
348
- {deepThinking && <DeepThinking {...deepThinking} />}
349
- {toolCall && <ToolCall {...toolCall} />}
350
- {!isLoading && sources && <SourcesCard {...sources} isCompact={isCompact} />}
351
- {quickStarts && quickStarts.quickStart && (
352
- <QuickStartTile
353
- quickStart={quickStarts.quickStart}
354
- onSelectQuickStart={quickStarts.onSelectQuickStart}
355
- minuteWord={quickStarts.minuteWord}
356
- minuteWordPlural={quickStarts.minuteWordPlural}
357
- prerequisiteWord={quickStarts.prerequisiteWord}
358
- prerequisiteWordPlural={quickStarts.prerequisiteWordPlural}
359
- quickStartButtonAriaLabel={quickStarts.quickStartButtonAriaLabel}
360
- isCompact={isCompact}
361
- />
362
- )}
363
- {!isLoading && !isEditable && actions && (
364
- <>
365
- {Array.isArray(actions) ? (
366
- <div className="pf-chatbot__response-actions-groups">
367
- {actions.map((actionGroup, index) => (
368
- <ResponseActions
369
- key={index}
370
- actions={actionGroup.actions || actionGroup}
371
- persistActionSelection={persistActionSelection || actionGroup.persistActionSelection}
372
- />
373
- ))}
374
- </div>
375
- ) : (
376
- <ResponseActions actions={actions} persistActionSelection={persistActionSelection} />
347
+ {children ? (
348
+ <>{children}</>
349
+ ) : (
350
+ <>
351
+ <div className="pf-chatbot__message-and-actions">
352
+ {renderMessage()}
353
+ {afterMainContent && <>{afterMainContent}</>}
354
+ {toolResponse && <ToolResponse {...toolResponse} />}
355
+ {deepThinking && <DeepThinking {...deepThinking} />}
356
+ {toolCall && <ToolCall {...toolCall} />}
357
+ {!isLoading && sources && <SourcesCard {...sources} isCompact={isCompact} />}
358
+ {quickStarts && quickStarts.quickStart && (
359
+ <QuickStartTile
360
+ quickStart={quickStarts.quickStart}
361
+ onSelectQuickStart={quickStarts.onSelectQuickStart}
362
+ minuteWord={quickStarts.minuteWord}
363
+ minuteWordPlural={quickStarts.minuteWordPlural}
364
+ prerequisiteWord={quickStarts.prerequisiteWord}
365
+ prerequisiteWordPlural={quickStarts.prerequisiteWordPlural}
366
+ quickStartButtonAriaLabel={quickStarts.quickStartButtonAriaLabel}
367
+ isCompact={isCompact}
368
+ />
369
+ )}
370
+ {!isLoading && !isEditable && actions && (
371
+ <>
372
+ {Array.isArray(actions) ? (
373
+ <div className="pf-chatbot__response-actions-groups">
374
+ {actions.map((actionGroup, index) => (
375
+ <ResponseActions
376
+ key={index}
377
+ actions={actionGroup.actions || actionGroup}
378
+ persistActionSelection={persistActionSelection || actionGroup.persistActionSelection}
379
+ />
380
+ ))}
381
+ </div>
382
+ ) : (
383
+ <ResponseActions actions={actions} persistActionSelection={persistActionSelection} />
384
+ )}
385
+ </>
386
+ )}
387
+ {userFeedbackForm && (
388
+ <UserFeedback {...userFeedbackForm} timestamp={dateString} isCompact={isCompact} />
377
389
  )}
378
- </>
379
- )}
380
- {userFeedbackForm && <UserFeedback {...userFeedbackForm} timestamp={dateString} isCompact={isCompact} />}
381
- {userFeedbackComplete && (
382
- <UserFeedbackComplete {...userFeedbackComplete} timestamp={dateString} isCompact={isCompact} />
383
- )}
384
- {!isLoading && quickResponses && (
385
- <QuickResponse
386
- quickResponses={quickResponses}
387
- quickResponseContainerProps={quickResponseContainerProps}
388
- isCompact={isCompact}
389
- />
390
- )}
391
- </div>
392
- {attachments && (
393
- <div className="pf-chatbot__message-attachments-container">
394
- {attachments.map((attachment) => (
395
- <div key={attachment.id ?? attachment.name} className="pf-chatbot__message-attachment">
396
- <FileDetailsLabel
397
- fileName={attachment.name}
398
- fileId={attachment.id}
399
- onClose={attachment.onClose}
400
- onClick={attachment.onClick}
401
- isLoading={attachment.isLoading}
402
- closeButtonAriaLabel={attachment.closeButtonAriaLabel}
403
- languageTestId={attachment.languageTestId}
404
- spinnerTestId={attachment.spinnerTestId}
405
- variant={isPrimary ? 'outline' : undefined}
390
+ {userFeedbackComplete && (
391
+ <UserFeedbackComplete {...userFeedbackComplete} timestamp={dateString} isCompact={isCompact} />
392
+ )}
393
+ {!isLoading && quickResponses && (
394
+ <QuickResponse
395
+ quickResponses={quickResponses}
396
+ quickResponseContainerProps={quickResponseContainerProps}
397
+ isCompact={isCompact}
406
398
  />
399
+ )}
400
+ </div>
401
+ {attachments && (
402
+ <div className="pf-chatbot__message-attachments-container">
403
+ {attachments.map((attachment) => (
404
+ <div key={attachment.id ?? attachment.name} className="pf-chatbot__message-attachment">
405
+ <FileDetailsLabel
406
+ fileName={attachment.name}
407
+ fileId={attachment.id}
408
+ onClose={attachment.onClose}
409
+ onClick={attachment.onClick}
410
+ isLoading={attachment.isLoading}
411
+ closeButtonAriaLabel={attachment.closeButtonAriaLabel}
412
+ languageTestId={attachment.languageTestId}
413
+ spinnerTestId={attachment.spinnerTestId}
414
+ variant={isPrimary ? 'outline' : undefined}
415
+ />
416
+ </div>
417
+ ))}
407
418
  </div>
408
- ))}
409
- </div>
419
+ )}
420
+ {!isLoading && endContent && <>{endContent}</>}
421
+ </>
410
422
  )}
411
- {!isLoading && endContent && <>{endContent}</>}
412
423
  </div>
413
424
  </div>
414
425
  </section>
@@ -0,0 +1,23 @@
1
+ import '@testing-library/jest-dom';
2
+ import { render, screen } from '@testing-library/react';
3
+ import MessageAndActions from './MessageAndActions';
4
+
5
+ test('Renders with children', () => {
6
+ render(<MessageAndActions>Test content</MessageAndActions>);
7
+ expect(screen.getByText('Test content')).toBeInTheDocument();
8
+ });
9
+
10
+ test('Renders with pf-chatbot__message-and-actions class by default', () => {
11
+ render(<MessageAndActions>Test content</MessageAndActions>);
12
+ expect(screen.getByText('Test content')).toHaveClass('pf-chatbot__message-and-actions', { exact: true });
13
+ });
14
+
15
+ test('Renders with custom className', () => {
16
+ render(<MessageAndActions className="custom-class">Test content</MessageAndActions>);
17
+ expect(screen.getByText('Test content')).toHaveClass('custom-class');
18
+ });
19
+
20
+ test('Spreads additional props', () => {
21
+ render(<MessageAndActions id="test-id">Test content</MessageAndActions>);
22
+ expect(screen.getByText('Test content')).toHaveAttribute('id', 'test-id');
23
+ });
@@ -0,0 +1,22 @@
1
+ import { FunctionComponent, HTMLProps, ReactNode } from 'react';
2
+ import { css } from '@patternfly/react-styles';
3
+
4
+ /**
5
+ * The container that wraps the primary message content and inline actions, such as ToolCall, ToolResponse, DeepThinking, ResponseActions, etc.
6
+ * Attachments should not be rendered inside this container.
7
+ * Use this component when passing children to Message to customize its structure.
8
+ */
9
+ export interface MessageAndActionsProps extends HTMLProps<HTMLDivElement> {
10
+ /** Content to render inside the message and actions container. */
11
+ children: ReactNode;
12
+ /** Additional classes applied to the message and actions container. */
13
+ className?: string;
14
+ }
15
+
16
+ export const MessageAndActions: FunctionComponent<MessageAndActionsProps> = ({ children, className, ...props }) => (
17
+ <div className={css('pf-chatbot__message-and-actions', className)} {...props}>
18
+ {children}
19
+ </div>
20
+ );
21
+
22
+ export default MessageAndActions;
@@ -0,0 +1 @@
1
+ export * from './MessageAndActions';
@@ -0,0 +1,23 @@
1
+ import '@testing-library/jest-dom';
2
+ import { render, screen } from '@testing-library/react';
3
+ import MessageAttachmentItem from './MessageAttachmentItem';
4
+
5
+ test('Renders with children', () => {
6
+ render(<MessageAttachmentItem>Test content</MessageAttachmentItem>);
7
+ expect(screen.getByText('Test content')).toBeInTheDocument();
8
+ });
9
+
10
+ test('Renders with pf-chatbot__message-attachment class by default', () => {
11
+ render(<MessageAttachmentItem>Test content</MessageAttachmentItem>);
12
+ expect(screen.getByText('Test content')).toHaveClass('pf-chatbot__message-attachment', { exact: true });
13
+ });
14
+
15
+ test('Renders with custom className', () => {
16
+ render(<MessageAttachmentItem className="custom-class">Test content</MessageAttachmentItem>);
17
+ expect(screen.getByText('Test content')).toHaveClass('custom-class');
18
+ });
19
+
20
+ test('Spreads additional props', () => {
21
+ render(<MessageAttachmentItem id="test-id">Test content</MessageAttachmentItem>);
22
+ expect(screen.getByText('Test content')).toHaveAttribute('id', 'test-id');
23
+ });
@@ -0,0 +1,25 @@
1
+ import { FunctionComponent, HTMLProps, ReactNode } from 'react';
2
+ import { css } from '@patternfly/react-styles';
3
+
4
+ /**
5
+ * The container for a single message attachment item, typically the FileDetailsLabel component. You must wrap any attachment components in this container.
6
+ * Use this component within MessageAttachmentsContainer when passing children to Message to customize its structure.
7
+ */
8
+ export interface MessageAttachmentItemProps extends HTMLProps<HTMLDivElement> {
9
+ /** Content to render inside a single attachment container */
10
+ children: ReactNode;
11
+ /** Additional classes applied to the attachment container. */
12
+ className?: string;
13
+ }
14
+
15
+ export const MessageAttachmentItem: FunctionComponent<MessageAttachmentItemProps> = ({
16
+ children,
17
+ className,
18
+ ...props
19
+ }) => (
20
+ <div className={css('pf-chatbot__message-attachment', className)} {...props}>
21
+ {children}
22
+ </div>
23
+ );
24
+
25
+ export default MessageAttachmentItem;
@@ -0,0 +1,23 @@
1
+ import '@testing-library/jest-dom';
2
+ import { render, screen } from '@testing-library/react';
3
+ import MessageAttachmentsContainer from './MessageAttachmentsContainer';
4
+
5
+ test('Renders with children', () => {
6
+ render(<MessageAttachmentsContainer>Test content</MessageAttachmentsContainer>);
7
+ expect(screen.getByText('Test content')).toBeInTheDocument();
8
+ });
9
+
10
+ test('Renders with pf-chatbot__message-attachments-container class by default', () => {
11
+ render(<MessageAttachmentsContainer>Test content</MessageAttachmentsContainer>);
12
+ expect(screen.getByText('Test content')).toHaveClass('pf-chatbot__message-attachments-container', { exact: true });
13
+ });
14
+
15
+ test('Renders with custom className', () => {
16
+ render(<MessageAttachmentsContainer className="custom-class">Test content</MessageAttachmentsContainer>);
17
+ expect(screen.getByText('Test content')).toHaveClass('custom-class');
18
+ });
19
+
20
+ test('Spreads additional props', () => {
21
+ render(<MessageAttachmentsContainer id="test-id">Test content</MessageAttachmentsContainer>);
22
+ expect(screen.getByText('Test content')).toHaveAttribute('id', 'test-id');
23
+ });
@@ -0,0 +1,25 @@
1
+ import { FunctionComponent, HTMLProps, ReactNode } from 'react';
2
+ import { css } from '@patternfly/react-styles';
3
+
4
+ /**
5
+ * The container to wrap MessageAttachment components. You must wrap any MessageAttachment components in this container.
6
+ * Use this component when passing children to Message to customize its structure.
7
+ */
8
+ export interface MessageAttachmentsContainerProps extends HTMLProps<HTMLDivElement> {
9
+ /** Content to render inside the attachments container */
10
+ children: ReactNode;
11
+ /** Additional classes applied to the attachments container. */
12
+ className?: string;
13
+ }
14
+
15
+ export const MessageAttachmentsContainer: FunctionComponent<MessageAttachmentsContainerProps> = ({
16
+ children,
17
+ className,
18
+ ...props
19
+ }) => (
20
+ <div className={css('pf-chatbot__message-attachments-container', className)} {...props}>
21
+ {children}
22
+ </div>
23
+ );
24
+
25
+ export default MessageAttachmentsContainer;
@@ -0,0 +1,2 @@
1
+ export * from './MessageAttachmentItem';
2
+ export * from './MessageAttachmentsContainer';
@@ -22,7 +22,7 @@ export interface MessageInputProps extends FormProps {
22
22
  content?: string;
23
23
  }
24
24
 
25
- const MessageInput: FunctionComponent<MessageInputProps> = ({
25
+ export const MessageInput: FunctionComponent<MessageInputProps> = ({
26
26
  editPlaceholder = 'Edit prompt message...',
27
27
  updateWord = 'Update',
28
28
  cancelWord = 'Cancel',
@@ -0,0 +1,23 @@
1
+ import '@testing-library/jest-dom';
2
+ import { render, screen } from '@testing-library/react';
3
+ import MessageLoading from './MessageLoading';
4
+
5
+ test('Renders with pf-chatbot__message-loading class by default', () => {
6
+ render(<MessageLoading data-testid="test-id" />);
7
+ expect(screen.getByTestId('test-id')).toHaveClass('pf-chatbot__message-loading', { exact: true });
8
+ });
9
+
10
+ test('Renders with pf-m-primary class when isPrimary is true', () => {
11
+ render(<MessageLoading data-testid="test-id" isPrimary />);
12
+ expect(screen.getByTestId('test-id')).toHaveClass('pf-chatbot__message-loading pf-m-primary');
13
+ });
14
+
15
+ test('Renders loading word when loadingWord is passed', () => {
16
+ render(<MessageLoading loadingWord="Loading message" />);
17
+ expect(screen.getByText('Loading message')).toBeInTheDocument();
18
+ });
19
+
20
+ test('Spreads additional props', () => {
21
+ render(<MessageLoading data-testid="test-id" id="custom-id" />);
22
+ expect(screen.getByTestId('test-id')).toHaveAttribute('id', 'custom-id');
23
+ });