@patternfly/chatbot 6.5.0-prerelease.21 → 6.5.0-prerelease.23

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 (97) hide show
  1. package/dist/cjs/DeepThinking/DeepThinking.d.ts +13 -0
  2. package/dist/cjs/DeepThinking/DeepThinking.js +31 -3
  3. package/dist/cjs/DeepThinking/DeepThinking.test.js +80 -0
  4. package/dist/cjs/MarkdownContent/MarkdownContent.d.ts +39 -0
  5. package/dist/cjs/MarkdownContent/MarkdownContent.js +181 -0
  6. package/dist/cjs/MarkdownContent/MarkdownContent.test.d.ts +1 -0
  7. package/dist/cjs/MarkdownContent/MarkdownContent.test.js +192 -0
  8. package/dist/cjs/MarkdownContent/index.d.ts +2 -0
  9. package/dist/cjs/MarkdownContent/index.js +23 -0
  10. package/dist/cjs/Message/CodeBlockMessage/CodeBlockMessage.d.ts +3 -1
  11. package/dist/cjs/Message/CodeBlockMessage/CodeBlockMessage.js +3 -2
  12. package/dist/cjs/Message/LinkMessage/LinkMessage.d.ts +5 -1
  13. package/dist/cjs/Message/LinkMessage/LinkMessage.js +4 -3
  14. package/dist/cjs/Message/ListMessage/OrderedListMessage.d.ts +9 -1
  15. package/dist/cjs/Message/ListMessage/OrderedListMessage.js +2 -1
  16. package/dist/cjs/Message/ListMessage/UnorderedListMessage.d.ts +7 -1
  17. package/dist/cjs/Message/ListMessage/UnorderedListMessage.js +2 -1
  18. package/dist/cjs/Message/Message.js +2 -155
  19. package/dist/cjs/Message/TableMessage/TableMessage.d.ts +6 -1
  20. package/dist/cjs/Message/TableMessage/TableMessage.js +3 -2
  21. package/dist/cjs/Message/TextMessage/TextMessage.d.ts +8 -1
  22. package/dist/cjs/Message/TextMessage/TextMessage.js +3 -2
  23. package/dist/cjs/ToolCall/ToolCall.d.ts +11 -0
  24. package/dist/cjs/ToolCall/ToolCall.js +24 -3
  25. package/dist/cjs/ToolCall/ToolCall.test.js +57 -0
  26. package/dist/cjs/ToolResponse/ToolResponse.d.ts +17 -0
  27. package/dist/cjs/ToolResponse/ToolResponse.js +49 -3
  28. package/dist/cjs/ToolResponse/ToolResponse.test.js +100 -0
  29. package/dist/cjs/index.d.ts +2 -0
  30. package/dist/cjs/index.js +4 -1
  31. package/dist/css/main.css +48 -0
  32. package/dist/css/main.css.map +1 -1
  33. package/dist/dynamic/MarkdownContent/package.json +1 -0
  34. package/dist/esm/DeepThinking/DeepThinking.d.ts +13 -0
  35. package/dist/esm/DeepThinking/DeepThinking.js +28 -3
  36. package/dist/esm/DeepThinking/DeepThinking.test.js +80 -0
  37. package/dist/esm/MarkdownContent/MarkdownContent.d.ts +39 -0
  38. package/dist/esm/MarkdownContent/MarkdownContent.js +174 -0
  39. package/dist/esm/MarkdownContent/MarkdownContent.test.d.ts +1 -0
  40. package/dist/esm/MarkdownContent/MarkdownContent.test.js +187 -0
  41. package/dist/esm/MarkdownContent/index.d.ts +2 -0
  42. package/dist/esm/MarkdownContent/index.js +2 -0
  43. package/dist/esm/Message/CodeBlockMessage/CodeBlockMessage.d.ts +3 -1
  44. package/dist/esm/Message/CodeBlockMessage/CodeBlockMessage.js +3 -2
  45. package/dist/esm/Message/LinkMessage/LinkMessage.d.ts +5 -1
  46. package/dist/esm/Message/LinkMessage/LinkMessage.js +4 -3
  47. package/dist/esm/Message/ListMessage/OrderedListMessage.d.ts +9 -1
  48. package/dist/esm/Message/ListMessage/OrderedListMessage.js +2 -1
  49. package/dist/esm/Message/ListMessage/UnorderedListMessage.d.ts +7 -1
  50. package/dist/esm/Message/ListMessage/UnorderedListMessage.js +2 -1
  51. package/dist/esm/Message/Message.js +3 -156
  52. package/dist/esm/Message/TableMessage/TableMessage.d.ts +6 -1
  53. package/dist/esm/Message/TableMessage/TableMessage.js +3 -2
  54. package/dist/esm/Message/TextMessage/TextMessage.d.ts +8 -1
  55. package/dist/esm/Message/TextMessage/TextMessage.js +3 -2
  56. package/dist/esm/ToolCall/ToolCall.d.ts +11 -0
  57. package/dist/esm/ToolCall/ToolCall.js +21 -3
  58. package/dist/esm/ToolCall/ToolCall.test.js +57 -0
  59. package/dist/esm/ToolResponse/ToolResponse.d.ts +17 -0
  60. package/dist/esm/ToolResponse/ToolResponse.js +46 -3
  61. package/dist/esm/ToolResponse/ToolResponse.test.js +100 -0
  62. package/dist/esm/index.d.ts +2 -0
  63. package/dist/esm/index.js +2 -0
  64. package/dist/tsconfig.tsbuildinfo +1 -1
  65. package/package.json +1 -1
  66. package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithDeepThinking.tsx +25 -11
  67. package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithMarkdownDeepThinking.tsx +26 -0
  68. package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithMarkdownToolCall.tsx +29 -0
  69. package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithMarkdownToolResponse.tsx +200 -0
  70. package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithToolCall.tsx +14 -1
  71. package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithToolResponse.tsx +222 -105
  72. package/patternfly-docs/content/extensions/chatbot/examples/Messages/Messages.md +32 -0
  73. package/patternfly-docs/content/extensions/chatbot/examples/demos/Chatbot.md +15 -1
  74. package/src/DeepThinking/DeepThinking.test.tsx +109 -0
  75. package/src/DeepThinking/DeepThinking.tsx +54 -5
  76. package/src/MarkdownContent/MarkdownContent.test.tsx +207 -0
  77. package/src/MarkdownContent/MarkdownContent.tsx +264 -0
  78. package/src/MarkdownContent/index.ts +2 -0
  79. package/src/Message/CodeBlockMessage/CodeBlockMessage.scss +4 -0
  80. package/src/Message/CodeBlockMessage/CodeBlockMessage.tsx +5 -1
  81. package/src/Message/LinkMessage/LinkMessage.scss +5 -0
  82. package/src/Message/LinkMessage/LinkMessage.tsx +24 -2
  83. package/src/Message/ListMessage/ListMessage.scss +8 -0
  84. package/src/Message/ListMessage/OrderedListMessage.tsx +16 -2
  85. package/src/Message/ListMessage/UnorderedListMessage.tsx +12 -2
  86. package/src/Message/Message.tsx +21 -181
  87. package/src/Message/TableMessage/TableMessage.scss +11 -0
  88. package/src/Message/TableMessage/TableMessage.tsx +18 -2
  89. package/src/Message/TextMessage/TextMessage.scss +8 -0
  90. package/src/Message/TextMessage/TextMessage.tsx +29 -2
  91. package/src/ToolCall/ToolCall.test.tsx +91 -0
  92. package/src/ToolCall/ToolCall.tsx +49 -4
  93. package/src/ToolResponse/ToolResponse.scss +10 -0
  94. package/src/ToolResponse/ToolResponse.test.tsx +119 -0
  95. package/src/ToolResponse/ToolResponse.tsx +82 -7
  96. package/src/index.ts +3 -0
  97. package/src/main.scss +1 -0
@@ -23,113 +23,230 @@ export const MessageWithToolResponseExample: FunctionComponent = () => {
23
23
  };
24
24
 
25
25
  return (
26
- <Message
27
- name="Bot"
28
- role="bot"
29
- avatar={patternflyAvatar}
30
- content="This example has a body description that's within the recommended limit of 2 lines:"
31
- toolResponse={{
32
- toggleContent: 'Tool response: toolName',
33
- subheading: 'Thought for 3 seconds',
34
- body: "Here's the summary for your toolName response:",
35
- cardTitle: (
36
- <Flex alignItems={{ default: 'alignItemsCenter' }} justifyContent={{ default: 'justifyContentSpaceBetween' }}>
37
- <FlexItem>
38
- <Flex direction={{ default: 'column' }} gap={{ default: 'gapXs' }}>
39
- <FlexItem grow={{ default: 'grow' }}>
40
- <Flex gap={{ default: 'gapXs' }}>
41
- <FlexItem>
42
- <WrenchIcon style={{ color: 'var(--pf-t--global--icon--color--brand--default' }} />
43
- </FlexItem>
44
- <FlexItem>toolName</FlexItem>
45
- </Flex>
46
- </FlexItem>
47
- <FlexItem>
48
- <Flex gap={{ default: 'gapSm' }} style={{ fontSize: '12px', fontWeight: '400' }}>
49
- <FlexItem>Execution time:</FlexItem>
50
- <FlexItem>0.12 seconds</FlexItem>
51
- </Flex>
52
- </FlexItem>
53
- </Flex>
54
- </FlexItem>
55
- <FlexItem>
56
- <Button
57
- variant="plain"
58
- aria-label="Copy tool response to clipboard"
59
- icon={<CopyIcon style={{ color: 'var(--pf-t--global--icon--color--subtle)' }} />}
60
- ></Button>
61
- </FlexItem>
62
- </Flex>
63
- ),
64
- cardBody: (
65
- <>
66
- <DescriptionList
67
- style={{ '--pf-v6-c-description-list--RowGap': 'var(--pf-t--global--spacer--md)' } as any}
68
- aria-label="Tool response"
26
+ <>
27
+ <Message
28
+ name="Bot"
29
+ role="bot"
30
+ avatar={patternflyAvatar}
31
+ content="This message has a body description that's within the recommended limit of 2 lines:"
32
+ toolResponse={{
33
+ toggleContent: 'Tool response: toolName',
34
+ subheading: 'Thought for 3 seconds',
35
+ body: "Here's the summary for your toolName response:",
36
+ cardTitle: (
37
+ <Flex
38
+ alignItems={{ default: 'alignItemsCenter' }}
39
+ justifyContent={{ default: 'justifyContentSpaceBetween' }}
69
40
  >
70
- <DescriptionListGroup
71
- style={{ '--pf-v6-c-description-list__group--RowGap': 'var(--pf-t--global--spacer--xs)' } as any}
41
+ <FlexItem>
42
+ <Flex direction={{ default: 'column' }} gap={{ default: 'gapXs' }}>
43
+ <FlexItem grow={{ default: 'grow' }}>
44
+ <Flex gap={{ default: 'gapXs' }}>
45
+ <FlexItem>
46
+ <WrenchIcon style={{ color: 'var(--pf-t--global--icon--color--brand--default' }} />
47
+ </FlexItem>
48
+ <FlexItem>toolName</FlexItem>
49
+ </Flex>
50
+ </FlexItem>
51
+ <FlexItem>
52
+ <Flex gap={{ default: 'gapSm' }} style={{ fontSize: '12px', fontWeight: '400' }}>
53
+ <FlexItem>Execution time:</FlexItem>
54
+ <FlexItem>0.12 seconds</FlexItem>
55
+ </Flex>
56
+ </FlexItem>
57
+ </Flex>
58
+ </FlexItem>
59
+ <FlexItem>
60
+ <Button
61
+ variant="plain"
62
+ aria-label="Copy tool response to clipboard"
63
+ icon={<CopyIcon style={{ color: 'var(--pf-t--global--icon--color--subtle)' }} />}
64
+ ></Button>
65
+ </FlexItem>
66
+ </Flex>
67
+ ),
68
+ cardBody: (
69
+ <>
70
+ <DescriptionList
71
+ style={{ '--pf-v6-c-description-list--RowGap': 'var(--pf-t--global--spacer--md)' } as any}
72
+ aria-label="Tool response"
72
73
  >
73
- <DescriptionListTerm>Parameters</DescriptionListTerm>
74
- <DescriptionListDescription>
75
- <Flex direction={{ default: 'column' }}>
76
- <FlexItem>Optional description text for parameters.</FlexItem>
77
- <FlexItem>
78
- <Flex gap={{ default: 'gapSm' }}>
79
- <FlexItem>
80
- <Label variant="outline" color="blue">
81
- type
82
- </Label>
83
- </FlexItem>
84
- <FlexItem>
85
- <Label variant="outline" color="blue">
86
- properties
87
- </Label>
88
- </FlexItem>
89
- <FlexItem>
90
- <Label variant="outline" color="blue">
91
- label
92
- </Label>
93
- </FlexItem>
94
- <FlexItem>
95
- <Label variant="outline" color="blue">
96
- label
97
- </Label>
98
- </FlexItem>
99
- </Flex>
100
- </FlexItem>
101
- </Flex>
102
- </DescriptionListDescription>
103
- </DescriptionListGroup>
104
- <DescriptionListGroup
105
- style={{ '--pf-v6-c-description-list__group--RowGap': 'var(--pf-t--global--spacer--xs)' } as any}
74
+ <DescriptionListGroup
75
+ style={{ '--pf-v6-c-description-list__group--RowGap': 'var(--pf-t--global--spacer--xs)' } as any}
76
+ >
77
+ <DescriptionListTerm>Parameters</DescriptionListTerm>
78
+ <DescriptionListDescription>
79
+ <Flex direction={{ default: 'column' }}>
80
+ <FlexItem>Optional description text for parameters.</FlexItem>
81
+ <FlexItem>
82
+ <Flex gap={{ default: 'gapSm' }}>
83
+ <FlexItem>
84
+ <Label variant="outline" color="blue">
85
+ type
86
+ </Label>
87
+ </FlexItem>
88
+ <FlexItem>
89
+ <Label variant="outline" color="blue">
90
+ properties
91
+ </Label>
92
+ </FlexItem>
93
+ <FlexItem>
94
+ <Label variant="outline" color="blue">
95
+ label
96
+ </Label>
97
+ </FlexItem>
98
+ <FlexItem>
99
+ <Label variant="outline" color="blue">
100
+ label
101
+ </Label>
102
+ </FlexItem>
103
+ </Flex>
104
+ </FlexItem>
105
+ </Flex>
106
+ </DescriptionListDescription>
107
+ </DescriptionListGroup>
108
+ <DescriptionListGroup
109
+ style={{ '--pf-v6-c-description-list__group--RowGap': 'var(--pf-t--global--spacer--xs)' } as any}
110
+ >
111
+ <DescriptionListTerm>Response</DescriptionListTerm>
112
+ <DescriptionListDescription>
113
+ <ExpandableSection
114
+ variant={ExpandableSectionVariant.truncate}
115
+ toggleTextExpanded="show less of response"
116
+ toggleTextCollapsed="show more of response"
117
+ onToggle={onToggle}
118
+ isExpanded={isExpanded}
119
+ style={
120
+ {
121
+ '--pf-v6-c-expandable-section__content--Opacity': '1',
122
+ '--pf-v6-c-expandable-section__content--PaddingInlineStart': 0,
123
+ '--pf-v6-c-expandable-section__content--TranslateY': 0,
124
+ '--pf-v6-c-expandable-section--m-expand-top__content--TranslateY': 0
125
+ } as any
126
+ }
127
+ >
128
+ Descriptive text about the tool response, including completion status, details on the data that
129
+ was processed, or anything else relevant to the use case.
130
+ </ExpandableSection>
131
+ </DescriptionListDescription>
132
+ </DescriptionListGroup>
133
+ </DescriptionList>
134
+ </>
135
+ )
136
+ }}
137
+ />
138
+ <Message
139
+ name="Bot"
140
+ role="bot"
141
+ avatar={patternflyAvatar}
142
+ content="This message has a tool response that is collapsed by default:"
143
+ toolResponse={{
144
+ isDefaultExpanded: false,
145
+ toggleContent: 'Tool response: toolName',
146
+ subheading: 'Thought for 3 seconds',
147
+ body: "Here's the summary for your toolName response:",
148
+ cardTitle: (
149
+ <Flex
150
+ alignItems={{ default: 'alignItemsCenter' }}
151
+ justifyContent={{ default: 'justifyContentSpaceBetween' }}
152
+ >
153
+ <FlexItem>
154
+ <Flex direction={{ default: 'column' }} gap={{ default: 'gapXs' }}>
155
+ <FlexItem grow={{ default: 'grow' }}>
156
+ <Flex gap={{ default: 'gapXs' }}>
157
+ <FlexItem>
158
+ <WrenchIcon style={{ color: 'var(--pf-t--global--icon--color--brand--default' }} />
159
+ </FlexItem>
160
+ <FlexItem>toolName</FlexItem>
161
+ </Flex>
162
+ </FlexItem>
163
+ <FlexItem>
164
+ <Flex gap={{ default: 'gapSm' }} style={{ fontSize: '12px', fontWeight: '400' }}>
165
+ <FlexItem>Execution time:</FlexItem>
166
+ <FlexItem>0.12 seconds</FlexItem>
167
+ </Flex>
168
+ </FlexItem>
169
+ </Flex>
170
+ </FlexItem>
171
+ <FlexItem>
172
+ <Button
173
+ variant="plain"
174
+ aria-label="Copy tool response to clipboard"
175
+ icon={<CopyIcon style={{ color: 'var(--pf-t--global--icon--color--subtle)' }} />}
176
+ ></Button>
177
+ </FlexItem>
178
+ </Flex>
179
+ ),
180
+ cardBody: (
181
+ <>
182
+ <DescriptionList
183
+ style={{ '--pf-v6-c-description-list--RowGap': 'var(--pf-t--global--spacer--md)' } as any}
184
+ aria-label="Tool response"
106
185
  >
107
- <DescriptionListTerm>Response</DescriptionListTerm>
108
- <DescriptionListDescription>
109
- <ExpandableSection
110
- variant={ExpandableSectionVariant.truncate}
111
- toggleTextExpanded="show less of response"
112
- toggleTextCollapsed="show more of response"
113
- onToggle={onToggle}
114
- isExpanded={isExpanded}
115
- style={
116
- {
117
- '--pf-v6-c-expandable-section__content--Opacity': '1',
118
- '--pf-v6-c-expandable-section__content--PaddingInlineStart': 0,
119
- '--pf-v6-c-expandable-section__content--TranslateY': 0,
120
- '--pf-v6-c-expandable-section--m-expand-top__content--TranslateY': 0
121
- } as any
122
- }
123
- >
124
- Descriptive text about the tool response, including completion status, details on the data that was
125
- processed, or anything else relevant to the use case.
126
- </ExpandableSection>
127
- </DescriptionListDescription>
128
- </DescriptionListGroup>
129
- </DescriptionList>
130
- </>
131
- )
132
- }}
133
- />
186
+ <DescriptionListGroup
187
+ style={{ '--pf-v6-c-description-list__group--RowGap': 'var(--pf-t--global--spacer--xs)' } as any}
188
+ >
189
+ <DescriptionListTerm>Parameters</DescriptionListTerm>
190
+ <DescriptionListDescription>
191
+ <Flex direction={{ default: 'column' }}>
192
+ <FlexItem>Optional description text for parameters.</FlexItem>
193
+ <FlexItem>
194
+ <Flex gap={{ default: 'gapSm' }}>
195
+ <FlexItem>
196
+ <Label variant="outline" color="blue">
197
+ type
198
+ </Label>
199
+ </FlexItem>
200
+ <FlexItem>
201
+ <Label variant="outline" color="blue">
202
+ properties
203
+ </Label>
204
+ </FlexItem>
205
+ <FlexItem>
206
+ <Label variant="outline" color="blue">
207
+ label
208
+ </Label>
209
+ </FlexItem>
210
+ <FlexItem>
211
+ <Label variant="outline" color="blue">
212
+ label
213
+ </Label>
214
+ </FlexItem>
215
+ </Flex>
216
+ </FlexItem>
217
+ </Flex>
218
+ </DescriptionListDescription>
219
+ </DescriptionListGroup>
220
+ <DescriptionListGroup
221
+ style={{ '--pf-v6-c-description-list__group--RowGap': 'var(--pf-t--global--spacer--xs)' } as any}
222
+ >
223
+ <DescriptionListTerm>Response</DescriptionListTerm>
224
+ <DescriptionListDescription>
225
+ <ExpandableSection
226
+ variant={ExpandableSectionVariant.truncate}
227
+ toggleTextExpanded="show less of response"
228
+ toggleTextCollapsed="show more of response"
229
+ onToggle={onToggle}
230
+ isExpanded={isExpanded}
231
+ style={
232
+ {
233
+ '--pf-v6-c-expandable-section__content--Opacity': '1',
234
+ '--pf-v6-c-expandable-section__content--PaddingInlineStart': 0,
235
+ '--pf-v6-c-expandable-section__content--TranslateY': 0,
236
+ '--pf-v6-c-expandable-section--m-expand-top__content--TranslateY': 0
237
+ } as any
238
+ }
239
+ >
240
+ Descriptive text about the tool response, including completion status, details on the data that
241
+ was processed, or anything else relevant to the use case.
242
+ </ExpandableSection>
243
+ </DescriptionListDescription>
244
+ </DescriptionListGroup>
245
+ </DescriptionList>
246
+ </>
247
+ )
248
+ }}
249
+ />
250
+ </>
134
251
  );
135
252
  };
@@ -349,3 +349,35 @@ An attachment dropzone allows users to upload files via drag and drop.
349
349
  ```js file="./FileDropZone.tsx"
350
350
 
351
351
  ```
352
+
353
+ ## Examples with Markdown
354
+
355
+ The ChatBot supports Markdown formatting in several message components, allowing you to display rich, formatted content. This is particularly useful when you need to include code snippets, lists, emphasis, or other formatted text. The following examples demonstrate different ways you can use Markdown in a few of the ChatBot components, but this is not an exhaustive list of all Markdown customizations you can make.
356
+
357
+ To enable Markdown rendering, use the appropriate Markdown flag prop (such as `isBodyMarkdown`, `isSubheadingMarkdown`, or `isExpandableContentMarkdown`) depending on the component and content you're formatting.
358
+
359
+ **Important:** When using Markdown in these components, set `shouldRetainStyles: true` to retain the styling of the context the Markdown is used in. This ensures that Markdown content maintains the proper font sizes, colors, and other styling properties of its parent component. For example, Markdown passed into a toggle will retain the ChatBot toggle styling, while Markdown in a card body will maintain the appropriate card body styling. Without this prop, the Markdown may override the contextual styles and create inconsistencies with the rest of the ChatBot interface.
360
+
361
+ ### Tool calls with Markdown
362
+
363
+ When displaying tool call information, you can use Markdown in the expandable content to provide formatted details about what the tool is processing. This is useful for showing structured data, code snippets, or formatted lists.
364
+
365
+ ```ts file="./MessageWithMarkdownToolCall.tsx"
366
+
367
+ ```
368
+
369
+ ### Deep thinking with Markdown
370
+
371
+ Deep thinking content can include Markdown formatting in both the subheading and body to better communicate the LLM's reasoning process. This allows you to emphasize key points, structure thought processes with lists, or include other formatting.
372
+
373
+ ```ts file="./MessageWithMarkdownDeepThinking.tsx"
374
+
375
+ ```
376
+
377
+ ### Tool responses with Markdown
378
+
379
+ Tool response cards support Markdown in multiple areas, including the toggle content, subheading, and body. Use `shouldRetainStyles: true` along with the appropriate Markdown flag props to ensure proper formatting and spacing.
380
+
381
+ ```ts file="./MessageWithMarkdownToolResponse.tsx"
382
+
383
+ ```
@@ -52,6 +52,20 @@ import ExpandIcon from '@patternfly/react-icons/dist/esm/icons/expand-icon';
52
52
  import OpenDrawerRightIcon from '@patternfly/react-icons/dist/esm/icons/open-drawer-right-icon';
53
53
  import OutlinedWindowRestoreIcon from '@patternfly/react-icons/dist/esm/icons/outlined-window-restore-icon';
54
54
  import { BarsIcon } from '@patternfly/react-icons/dist/esm/icons/bars-icon';
55
+ import { CopyIcon } from '@patternfly/react-icons/dist/esm/icons/copy-icon';
56
+ import { WrenchIcon } from '@patternfly/react-icons/dist/esm/icons/wrench-icon';
57
+ import {
58
+ Button,
59
+ DescriptionList,
60
+ DescriptionListDescription,
61
+ DescriptionListGroup,
62
+ DescriptionListTerm,
63
+ ExpandableSection,
64
+ ExpandableSectionVariant,
65
+ Flex,
66
+ FlexItem,
67
+ Label
68
+ } from '@patternfly/react-core';
55
69
  import PFHorizontalLogoColor from '../UI/PF-HorizontalLogo-Color.svg';
56
70
  import PFHorizontalLogoReverse from '../UI/PF-HorizontalLogo-Reverse.svg';
57
71
  import PFIconLogoColor from '../UI/PF-IconLogo-Color.svg';
@@ -59,7 +73,7 @@ import PFIconLogoReverse from '../UI/PF-IconLogo-Reverse.svg';
59
73
  import userAvatar from '../Messages/user_avatar.svg';
60
74
  import patternflyAvatar from '../Messages/patternfly_avatar.jpg';
61
75
  import { getTrackingProviders } from "@patternfly/chatbot/dist/dynamic/tracking";
62
- import { useEffect,useCallback, useRef, useState, FunctionComponent, MouseEvent } from 'react';
76
+ import { useEffect,useCallback, useRef, useState, FunctionComponent, MouseEvent, MouseEvent as ReactMouseEvent } from 'react';
63
77
  import saveAs from 'file-saver';
64
78
 
65
79
  ### Basic ChatBot
@@ -1,4 +1,5 @@
1
1
  import { render, screen } from '@testing-library/react';
2
+ import userEvent from '@testing-library/user-event';
2
3
  import '@testing-library/jest-dom';
3
4
  import DeepThinking from './DeepThinking';
4
5
 
@@ -58,4 +59,112 @@ describe('DeepThinking', () => {
58
59
  const subheadingContainer = container.querySelector('.pf-chatbot__tool-response-subheading');
59
60
  expect(subheadingContainer).toBeFalsy();
60
61
  });
62
+
63
+ it('should pass through cardBodyProps', () => {
64
+ render(
65
+ <DeepThinking {...defaultProps} body="Thinking content" cardBodyProps={{ className: 'custom-card-body-class' }} />
66
+ );
67
+
68
+ const cardBody = screen.getByText('Thinking content').closest('.pf-v6-c-card__body');
69
+ expect(cardBody).toHaveClass('custom-card-body-class');
70
+ });
71
+
72
+ it('Renders expanded by default', () => {
73
+ render(<DeepThinking {...defaultProps} body="Thinking content" />);
74
+
75
+ expect(screen.getByRole('button', { name: defaultProps.toggleContent })).toHaveAttribute('aria-expanded', 'true');
76
+ expect(screen.getByText('Thinking content')).toBeVisible();
77
+ });
78
+
79
+ it('Renders collapsed when isDefaultExpanded is false', () => {
80
+ render(<DeepThinking isDefaultExpanded={false} {...defaultProps} body="Thinking content" />);
81
+
82
+ expect(screen.getByRole('button', { name: defaultProps.toggleContent })).toHaveAttribute('aria-expanded', 'false');
83
+ expect(screen.getByText('Thinking content')).not.toBeVisible();
84
+ });
85
+
86
+ it('expandableSectionProps.isExpanded overrides isDefaultExpanded', () => {
87
+ render(
88
+ <DeepThinking
89
+ {...defaultProps}
90
+ isDefaultExpanded={false}
91
+ body="Thinking content"
92
+ expandableSectionProps={{ isExpanded: true }}
93
+ />
94
+ );
95
+
96
+ expect(screen.getByRole('button', { name: defaultProps.toggleContent })).toHaveAttribute('aria-expanded', 'true');
97
+ expect(screen.getByText('Thinking content')).toBeVisible();
98
+ });
99
+
100
+ it('expandableSectionProps.onToggle overrides internal onToggle behavior', async () => {
101
+ const user = userEvent.setup();
102
+ const customOnToggle = jest.fn();
103
+
104
+ render(
105
+ <DeepThinking
106
+ {...defaultProps}
107
+ isDefaultExpanded={false}
108
+ body="Thinking content"
109
+ expandableSectionProps={{ onToggle: customOnToggle }}
110
+ />
111
+ );
112
+
113
+ const toggleButton = screen.getByRole('button', { name: defaultProps.toggleContent });
114
+ expect(toggleButton).toHaveAttribute('aria-expanded', 'false');
115
+
116
+ await user.click(toggleButton);
117
+
118
+ expect(customOnToggle).toHaveBeenCalled();
119
+ expect(toggleButton).toHaveAttribute('aria-expanded', 'false');
120
+ expect(screen.getByText('Thinking content')).not.toBeVisible();
121
+ });
122
+
123
+ it('should render toggleContent as markdown when isToggleContentMarkdown is true', () => {
124
+ const toggleContent = '**Bold thinking**';
125
+ const { container } = render(<DeepThinking toggleContent={toggleContent} isToggleContentMarkdown />);
126
+ expect(container.querySelector('strong')).toBeTruthy();
127
+ expect(screen.getByText('Bold thinking')).toBeTruthy();
128
+ });
129
+
130
+ it('should not render toggleContent as markdown when isToggleContentMarkdown is false', () => {
131
+ const toggleContent = '**Bold thinking**';
132
+ const { container } = render(<DeepThinking toggleContent={toggleContent} />);
133
+ expect(container.querySelector('strong')).toBeFalsy();
134
+ expect(screen.getByText('**Bold thinking**')).toBeTruthy();
135
+ });
136
+
137
+ it('should render subheading as markdown when isSubheadingMarkdown is true', () => {
138
+ const subheading = '**Bold subheading**';
139
+ const { container } = render(<DeepThinking {...defaultProps} subheading={subheading} isSubheadingMarkdown />);
140
+ expect(container.querySelector('strong')).toBeTruthy();
141
+ expect(screen.getByText('Bold subheading')).toBeTruthy();
142
+ });
143
+
144
+ it('should not render subheading as markdown when isSubheadingMarkdown is false', () => {
145
+ const subheading = '**Bold subheading**';
146
+ render(<DeepThinking {...defaultProps} subheading={subheading} />);
147
+ expect(screen.getByText('**Bold subheading**')).toBeTruthy();
148
+ });
149
+
150
+ it('should render body as markdown when isBodyMarkdown is true', () => {
151
+ const body = '**Bold body**';
152
+ const { container } = render(<DeepThinking {...defaultProps} body={body} isBodyMarkdown />);
153
+ expect(container.querySelector('strong')).toBeTruthy();
154
+ expect(screen.getByText('Bold body')).toBeTruthy();
155
+ });
156
+
157
+ it('should not render body as markdown when isBodyMarkdown is false', () => {
158
+ const body = '**Bold body**';
159
+ render(<DeepThinking {...defaultProps} body={body} />);
160
+ expect(screen.getByText('**Bold body**')).toBeTruthy();
161
+ });
162
+
163
+ it('should pass markdownContentProps to MarkdownContent component', () => {
164
+ const body = '**Bold body**';
165
+ const { container } = render(
166
+ <DeepThinking {...defaultProps} body={body} isBodyMarkdown markdownContentProps={{ isPrimary: true }} />
167
+ );
168
+ expect(container.querySelector('.pf-m-primary')).toBeTruthy();
169
+ });
61
170
  });
@@ -10,10 +10,14 @@ import {
10
10
  ExpandableSectionProps
11
11
  } from '@patternfly/react-core';
12
12
  import { useState, type FunctionComponent } from 'react';
13
+ import MarkdownContent from '../MarkdownContent';
14
+ import type { MarkdownContentProps } from '../MarkdownContent';
13
15
 
14
16
  export interface DeepThinkingProps {
15
17
  /** Toggle content shown for expandable section */
16
18
  toggleContent: React.ReactNode;
19
+ /** Flag indicating whether the expandable content is expanded by default. */
20
+ isDefaultExpanded?: boolean;
17
21
  /** Additional props passed to expandable section */
18
22
  expandableSectionProps?: Omit<ExpandableSectionProps, 'ref'>;
19
23
  /** Subheading rendered inside expandable section */
@@ -24,6 +28,16 @@ export interface DeepThinkingProps {
24
28
  cardProps?: CardProps;
25
29
  /** Additional props passed to main card body */
26
30
  cardBodyProps?: CardBodyProps;
31
+ /** Whether to enable markdown rendering for toggleContent. When true and toggleContent is a string, it will be parsed as markdown. */
32
+ isToggleContentMarkdown?: boolean;
33
+ /** Whether to enable markdown rendering for subheading. When true, subheading will be parsed as markdown. */
34
+ isSubheadingMarkdown?: boolean;
35
+ /** Whether to enable markdown rendering for body. When true and body is a string, it will be parsed as markdown. */
36
+ isBodyMarkdown?: boolean;
37
+ /** Props passed to MarkdownContent component when markdown is enabled */
38
+ markdownContentProps?: Omit<MarkdownContentProps, 'content'>;
39
+ /** Whether to retain styles in the MarkdownContent component. Defaults to false. */
40
+ shouldRetainStyles?: boolean;
27
41
  }
28
42
 
29
43
  export const DeepThinking: FunctionComponent<DeepThinkingProps> = ({
@@ -32,19 +46,54 @@ export const DeepThinking: FunctionComponent<DeepThinkingProps> = ({
32
46
  expandableSectionProps,
33
47
  subheading,
34
48
  toggleContent,
35
- cardBodyProps
49
+ isDefaultExpanded = true,
50
+ cardBodyProps,
51
+ isToggleContentMarkdown,
52
+ isSubheadingMarkdown,
53
+ isBodyMarkdown,
54
+ markdownContentProps,
55
+ shouldRetainStyles = false
36
56
  }: DeepThinkingProps) => {
37
- const [isExpanded, setIsExpanded] = useState(true);
57
+ const [isExpanded, setIsExpanded] = useState(isDefaultExpanded);
38
58
 
39
59
  const onToggle = (_event: React.MouseEvent, isExpanded: boolean) => {
40
60
  setIsExpanded(isExpanded);
41
61
  };
42
62
 
63
+ const renderToggleContent = () => {
64
+ if (isToggleContentMarkdown && typeof toggleContent === 'string') {
65
+ return (
66
+ <MarkdownContent shouldRetainStyles={shouldRetainStyles} content={toggleContent} {...markdownContentProps} />
67
+ );
68
+ }
69
+ return toggleContent;
70
+ };
71
+
72
+ const renderSubheading = () => {
73
+ if (!subheading) {
74
+ return null;
75
+ }
76
+ if (isSubheadingMarkdown) {
77
+ return <MarkdownContent shouldRetainStyles={shouldRetainStyles} content={subheading} {...markdownContentProps} />;
78
+ }
79
+ return subheading;
80
+ };
81
+
82
+ const renderBody = () => {
83
+ if (!body) {
84
+ return null;
85
+ }
86
+ if (isBodyMarkdown && typeof body === 'string') {
87
+ return <MarkdownContent shouldRetainStyles={shouldRetainStyles} content={body} {...markdownContentProps} />;
88
+ }
89
+ return body;
90
+ };
91
+
43
92
  return (
44
93
  <Card isCompact className="pf-chatbot__deep-thinking" {...cardProps}>
45
94
  <CardBody {...cardBodyProps}>
46
95
  <ExpandableSection
47
- toggleContent={toggleContent}
96
+ toggleContent={renderToggleContent()}
48
97
  onToggle={onToggle}
49
98
  isExpanded={isExpanded}
50
99
  isIndented
@@ -54,10 +103,10 @@ export const DeepThinking: FunctionComponent<DeepThinkingProps> = ({
54
103
  <div className="pf-chatbot__deep-thinking-section">
55
104
  {subheading && (
56
105
  <div className="pf-chatbot__deep-thinking-subheading">
57
- <span>{subheading}</span>
106
+ <span>{renderSubheading()}</span>
58
107
  </div>
59
108
  )}
60
- {body && <div className="pf-chatbot__deep-thinking-body">{body}</div>}
109
+ {body && <div className="pf-chatbot__deep-thinking-body">{renderBody()}</div>}
61
110
  </div>
62
111
  </ExpandableSection>
63
112
  </CardBody>