@patternfly/chatbot 6.4.0-prerelease.2 → 6.4.0-prerelease.20

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 (186) hide show
  1. package/dist/cjs/Chatbot/Chatbot.js +1 -7
  2. package/dist/cjs/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.d.ts +2 -0
  3. package/dist/cjs/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.js +2 -2
  4. package/dist/cjs/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.d.ts +22 -2
  5. package/dist/cjs/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.js +15 -9
  6. package/dist/cjs/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.test.js +40 -2
  7. package/dist/cjs/ChatbotHeader/ChatbotHeaderMenu.js +1 -1
  8. package/dist/cjs/ChatbotHeader/ChatbotHeaderMenu.test.js +1 -1
  9. package/dist/cjs/ChatbotHeader/ChatbotHeaderNewChatButton.d.ts +18 -0
  10. package/dist/cjs/ChatbotHeader/ChatbotHeaderNewChatButton.js +25 -0
  11. package/dist/cjs/ChatbotHeader/ChatbotHeaderNewChatButton.test.d.ts +1 -0
  12. package/dist/cjs/ChatbotHeader/ChatbotHeaderNewChatButton.test.js +22 -0
  13. package/dist/cjs/ChatbotHeader/index.d.ts +1 -0
  14. package/dist/cjs/ChatbotHeader/index.js +1 -0
  15. package/dist/cjs/DeepThinking/DeepThinking.d.ts +18 -0
  16. package/dist/cjs/DeepThinking/DeepThinking.js +18 -0
  17. package/dist/cjs/DeepThinking/DeepThinking.test.d.ts +1 -0
  18. package/dist/cjs/DeepThinking/DeepThinking.test.js +48 -0
  19. package/dist/cjs/DeepThinking/index.d.ts +2 -0
  20. package/dist/cjs/DeepThinking/index.js +23 -0
  21. package/dist/cjs/FilePreview/FilePreview.d.ts +26 -0
  22. package/dist/cjs/FilePreview/FilePreview.js +26 -0
  23. package/dist/cjs/FilePreview/FilePreview.test.d.ts +1 -0
  24. package/dist/cjs/FilePreview/FilePreview.test.js +97 -0
  25. package/dist/cjs/FilePreview/index.d.ts +2 -0
  26. package/dist/cjs/FilePreview/index.js +23 -0
  27. package/dist/cjs/Message/CodeBlockMessage/CodeBlockMessage.js +3 -3
  28. package/dist/cjs/Message/LinkMessage/LinkMessage.d.ts +2 -1
  29. package/dist/cjs/Message/LinkMessage/LinkMessage.js +7 -3
  30. package/dist/cjs/Message/ListMessage/ListItemMessage.d.ts +1 -1
  31. package/dist/cjs/Message/ListMessage/ListItemMessage.js +16 -1
  32. package/dist/cjs/Message/Message.d.ts +15 -0
  33. package/dist/cjs/Message/Message.js +129 -32
  34. package/dist/cjs/Message/Message.test.js +71 -0
  35. package/dist/cjs/Message/SuperscriptMessage/SuperscriptMessage.d.ts +3 -0
  36. package/dist/cjs/Message/SuperscriptMessage/SuperscriptMessage.js +5 -0
  37. package/dist/cjs/Message/UserFeedback/UserFeedback.d.ts +15 -1
  38. package/dist/cjs/Message/UserFeedback/UserFeedback.js +4 -4
  39. package/dist/cjs/Message/UserFeedback/UserFeedback.test.js +44 -0
  40. package/dist/cjs/MessageBar/MessageBar.js +19 -4
  41. package/dist/cjs/MessageBox/JumpButton.d.ts +5 -0
  42. package/dist/cjs/MessageBox/JumpButton.js +1 -1
  43. package/dist/cjs/MessageBox/JumpButton.test.js +4 -4
  44. package/dist/cjs/MessageBox/MessageBox.d.ts +9 -0
  45. package/dist/cjs/MessageBox/MessageBox.js +2 -2
  46. package/dist/cjs/MessageBox/MessageBox.test.js +2 -2
  47. package/dist/cjs/SourcesCard/SourcesCard.d.ts +13 -1
  48. package/dist/cjs/SourcesCard/SourcesCard.js +6 -6
  49. package/dist/cjs/SourcesCard/SourcesCard.test.js +49 -0
  50. package/dist/cjs/ToolResponse/ToolResponse.d.ts +30 -0
  51. package/dist/cjs/ToolResponse/ToolResponse.js +18 -0
  52. package/dist/cjs/ToolResponse/ToolResponse.test.d.ts +1 -0
  53. package/dist/cjs/ToolResponse/ToolResponse.test.js +60 -0
  54. package/dist/cjs/ToolResponse/index.d.ts +2 -0
  55. package/dist/cjs/ToolResponse/index.js +23 -0
  56. package/dist/cjs/index.d.ts +6 -0
  57. package/dist/cjs/index.js +10 -1
  58. package/dist/css/main.css +273 -17
  59. package/dist/css/main.css.map +1 -1
  60. package/dist/dynamic/DeepThinking/package.json +1 -0
  61. package/dist/dynamic/FilePreview/package.json +1 -0
  62. package/dist/dynamic/ToolResponse/package.json +1 -0
  63. package/dist/esm/Chatbot/Chatbot.js +1 -7
  64. package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.d.ts +2 -0
  65. package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.js +2 -2
  66. package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.d.ts +22 -2
  67. package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.js +17 -11
  68. package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.test.js +41 -3
  69. package/dist/esm/ChatbotHeader/ChatbotHeaderMenu.js +1 -1
  70. package/dist/esm/ChatbotHeader/ChatbotHeaderMenu.test.js +1 -1
  71. package/dist/esm/ChatbotHeader/ChatbotHeaderNewChatButton.d.ts +18 -0
  72. package/dist/esm/ChatbotHeader/ChatbotHeaderNewChatButton.js +22 -0
  73. package/dist/esm/ChatbotHeader/ChatbotHeaderNewChatButton.test.d.ts +1 -0
  74. package/dist/esm/ChatbotHeader/ChatbotHeaderNewChatButton.test.js +20 -0
  75. package/dist/esm/ChatbotHeader/index.d.ts +1 -0
  76. package/dist/esm/ChatbotHeader/index.js +1 -0
  77. package/dist/esm/DeepThinking/DeepThinking.d.ts +18 -0
  78. package/dist/esm/DeepThinking/DeepThinking.js +14 -0
  79. package/dist/esm/DeepThinking/DeepThinking.test.d.ts +1 -0
  80. package/dist/esm/DeepThinking/DeepThinking.test.js +43 -0
  81. package/dist/esm/DeepThinking/index.d.ts +2 -0
  82. package/dist/esm/DeepThinking/index.js +2 -0
  83. package/dist/esm/FilePreview/FilePreview.d.ts +26 -0
  84. package/dist/esm/FilePreview/FilePreview.js +21 -0
  85. package/dist/esm/FilePreview/FilePreview.test.d.ts +1 -0
  86. package/dist/esm/FilePreview/FilePreview.test.js +92 -0
  87. package/dist/esm/FilePreview/index.d.ts +2 -0
  88. package/dist/esm/FilePreview/index.js +2 -0
  89. package/dist/esm/Message/CodeBlockMessage/CodeBlockMessage.js +5 -5
  90. package/dist/esm/Message/LinkMessage/LinkMessage.d.ts +2 -1
  91. package/dist/esm/Message/LinkMessage/LinkMessage.js +7 -3
  92. package/dist/esm/Message/ListMessage/ListItemMessage.d.ts +1 -1
  93. package/dist/esm/Message/ListMessage/ListItemMessage.js +16 -1
  94. package/dist/esm/Message/Message.d.ts +15 -0
  95. package/dist/esm/Message/Message.js +129 -32
  96. package/dist/esm/Message/Message.test.js +71 -0
  97. package/dist/esm/Message/SuperscriptMessage/SuperscriptMessage.d.ts +3 -0
  98. package/dist/esm/Message/SuperscriptMessage/SuperscriptMessage.js +3 -0
  99. package/dist/esm/Message/UserFeedback/UserFeedback.d.ts +15 -1
  100. package/dist/esm/Message/UserFeedback/UserFeedback.js +4 -4
  101. package/dist/esm/Message/UserFeedback/UserFeedback.test.js +45 -1
  102. package/dist/esm/MessageBar/MessageBar.js +19 -4
  103. package/dist/esm/MessageBox/JumpButton.d.ts +5 -0
  104. package/dist/esm/MessageBox/JumpButton.js +1 -1
  105. package/dist/esm/MessageBox/JumpButton.test.js +4 -4
  106. package/dist/esm/MessageBox/MessageBox.d.ts +9 -0
  107. package/dist/esm/MessageBox/MessageBox.js +2 -2
  108. package/dist/esm/MessageBox/MessageBox.test.js +2 -2
  109. package/dist/esm/SourcesCard/SourcesCard.d.ts +13 -1
  110. package/dist/esm/SourcesCard/SourcesCard.js +6 -6
  111. package/dist/esm/SourcesCard/SourcesCard.test.js +50 -1
  112. package/dist/esm/ToolResponse/ToolResponse.d.ts +30 -0
  113. package/dist/esm/ToolResponse/ToolResponse.js +14 -0
  114. package/dist/esm/ToolResponse/ToolResponse.test.d.ts +1 -0
  115. package/dist/esm/ToolResponse/ToolResponse.test.js +55 -0
  116. package/dist/esm/ToolResponse/index.d.ts +2 -0
  117. package/dist/esm/ToolResponse/index.js +2 -0
  118. package/dist/esm/index.d.ts +6 -0
  119. package/dist/esm/index.js +6 -0
  120. package/dist/tsconfig.tsbuildinfo +1 -1
  121. package/package.json +7 -6
  122. package/patternfly-docs/content/extensions/chatbot/examples/Messages/BotMessage.tsx +101 -3
  123. package/patternfly-docs/content/extensions/chatbot/examples/Messages/FilePreview.tsx +33 -0
  124. package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithDeepThinking.tsx +17 -0
  125. package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithFeedback.tsx +111 -85
  126. package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithSources.tsx +70 -0
  127. package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithToolResponse.tsx +135 -0
  128. package/patternfly-docs/content/extensions/chatbot/examples/Messages/Messages.md +28 -4
  129. package/patternfly-docs/content/extensions/chatbot/examples/Messages/UserMessage.tsx +107 -2
  130. package/patternfly-docs/content/extensions/chatbot/examples/Messages/UserMessageWithExtraContent.tsx +616 -3
  131. package/patternfly-docs/content/extensions/chatbot/examples/UI/ChatbotConversationEditing.tsx +202 -0
  132. package/patternfly-docs/content/extensions/chatbot/examples/UI/ChatbotHeaderBasic.tsx +17 -3
  133. package/patternfly-docs/content/extensions/chatbot/examples/UI/ChatbotHeaderDrawer.tsx +36 -5
  134. package/patternfly-docs/content/extensions/chatbot/examples/UI/ChatbotHeaderDrawerWithPin.tsx +12 -2
  135. package/patternfly-docs/content/extensions/chatbot/examples/UI/UI.md +22 -3
  136. package/patternfly-docs/content/extensions/chatbot/examples/demos/Chatbot.md +1 -1
  137. package/patternfly-docs/patternfly-docs.config.js +1 -1
  138. package/src/Chatbot/Chatbot.scss +9 -2
  139. package/src/Chatbot/Chatbot.tsx +18 -31
  140. package/src/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.tsx +5 -1
  141. package/src/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.scss +28 -10
  142. package/src/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.test.tsx +132 -3
  143. package/src/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.tsx +80 -33
  144. package/src/ChatbotHeader/ChatbotHeaderMenu.test.tsx +1 -1
  145. package/src/ChatbotHeader/ChatbotHeaderMenu.tsx +2 -2
  146. package/src/ChatbotHeader/ChatbotHeaderNewChatButton.test.tsx +25 -0
  147. package/src/ChatbotHeader/ChatbotHeaderNewChatButton.tsx +64 -0
  148. package/src/ChatbotHeader/index.ts +1 -0
  149. package/src/ChatbotModal/ChatbotModal.scss +1 -1
  150. package/src/DeepThinking/DeepThinking.scss +24 -0
  151. package/src/DeepThinking/DeepThinking.test.tsx +61 -0
  152. package/src/DeepThinking/DeepThinking.tsx +68 -0
  153. package/src/DeepThinking/index.ts +3 -0
  154. package/src/FileDetails/__snapshots__/FileDetails.test.tsx.snap +6 -9
  155. package/src/FileDetailsLabel/__snapshots__/FileDetailsLabel.test.tsx.snap +6 -9
  156. package/src/FilePreview/FilePreview.scss +22 -0
  157. package/src/FilePreview/FilePreview.test.tsx +112 -0
  158. package/src/FilePreview/FilePreview.tsx +58 -0
  159. package/src/FilePreview/index.ts +3 -0
  160. package/src/Message/CodeBlockMessage/CodeBlockMessage.scss +2 -1
  161. package/src/Message/CodeBlockMessage/CodeBlockMessage.tsx +6 -5
  162. package/src/Message/LinkMessage/LinkMessage.tsx +6 -2
  163. package/src/Message/ListMessage/ListItemMessage.tsx +5 -1
  164. package/src/Message/ListMessage/ListMessage.scss +17 -0
  165. package/src/Message/Message.scss +44 -0
  166. package/src/Message/Message.test.tsx +90 -0
  167. package/src/Message/Message.tsx +171 -46
  168. package/src/Message/SuperscriptMessage/SuperscriptMessage.scss +8 -0
  169. package/src/Message/SuperscriptMessage/SuperscriptMessage.tsx +13 -0
  170. package/src/Message/TextMessage/TextMessage.scss +46 -5
  171. package/src/Message/UserFeedback/UserFeedback.test.tsx +107 -0
  172. package/src/Message/UserFeedback/UserFeedback.tsx +41 -6
  173. package/src/MessageBar/MessageBar.tsx +23 -3
  174. package/src/MessageBox/JumpButton.test.tsx +4 -4
  175. package/src/MessageBox/JumpButton.tsx +20 -4
  176. package/src/MessageBox/MessageBox.test.tsx +2 -2
  177. package/src/MessageBox/MessageBox.tsx +22 -1
  178. package/src/SourcesCard/SourcesCard.scss +17 -0
  179. package/src/SourcesCard/SourcesCard.test.tsx +93 -0
  180. package/src/SourcesCard/SourcesCard.tsx +116 -80
  181. package/src/ToolResponse/ToolResponse.scss +36 -0
  182. package/src/ToolResponse/ToolResponse.test.tsx +78 -0
  183. package/src/ToolResponse/ToolResponse.tsx +95 -0
  184. package/src/ToolResponse/index.ts +3 -0
  185. package/src/index.ts +9 -0
  186. package/src/main.scss +3 -0
@@ -1,5 +1,6 @@
1
1
  import { ReactNode } from 'react';
2
2
  import type { FunctionComponent, HTMLProps, MouseEvent as ReactMouseEvent, Ref } from 'react';
3
+ import { Options } from 'react-markdown';
3
4
  import { AlertProps, AvatarProps, ButtonProps, ExpandableSectionProps, ExpandableSectionToggleProps, FormProps, LabelGroupProps } from '@patternfly/react-core';
4
5
  import { ActionProps } from '../ResponseActions/ResponseActions';
5
6
  import { SourcesCardProps } from '../SourcesCard';
@@ -9,6 +10,8 @@ import { UserFeedbackProps } from './UserFeedback/UserFeedback';
9
10
  import { UserFeedbackCompleteProps } from './UserFeedback/UserFeedbackComplete';
10
11
  import { TableProps } from '@patternfly/react-table';
11
12
  import { PluggableList } from 'unified';
13
+ import { ToolResponseProps } from '../ToolResponse';
14
+ import { DeepThinkingProps } from '../DeepThinking';
12
15
  export interface MessageAttachment {
13
16
  /** Name of file attached to the message */
14
17
  name: string;
@@ -116,6 +119,8 @@ export interface MessageProps extends Omit<HTMLProps<HTMLDivElement>, 'role'> {
116
119
  tableProps?: Required<Pick<TableProps, 'aria-label'>> & TableProps;
117
120
  /** Additional rehype plugins passed from the consumer */
118
121
  additionalRehypePlugins?: PluggableList;
122
+ /** Additional remark plugins passed from the consumer */
123
+ additionalRemarkPlugins?: PluggableList;
119
124
  /** Whether to open links in message in new tab. */
120
125
  openLinkInNewTab?: boolean;
121
126
  /** Optional inline error message that can be displayed in the message */
@@ -140,6 +145,16 @@ export interface MessageProps extends Omit<HTMLProps<HTMLDivElement>, 'role'> {
140
145
  editFormProps?: FormProps;
141
146
  /** Sets message to compact styling. */
142
147
  isCompact?: boolean;
148
+ /** Disables markdown parsing for message, allowing only text input */
149
+ isMarkdownDisabled?: boolean;
150
+ /** Allows passing additional props down to markdown parser react-markdown, such as allowedElements and disallowedElements. See https://github.com/remarkjs/react-markdown?tab=readme-ov-file#options for options */
151
+ reactMarkdownProps?: Options;
152
+ /** Props for tool response card */
153
+ toolResponse?: ToolResponseProps;
154
+ /** Props for deep thinking card */
155
+ deepThinking?: DeepThinkingProps;
156
+ /** Allows passing additional props down to remark-gfm. See https://github.com/remarkjs/remark-gfm?tab=readme-ov-file#options for options */
157
+ remarkGfmProps?: Options;
143
158
  }
144
159
  export declare const MessageBase: FunctionComponent<MessageProps>;
145
160
  declare const Message: import("react").ForwardRefExoticComponent<Omit<MessageProps, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
@@ -44,8 +44,11 @@ import LinkMessage from './LinkMessage/LinkMessage';
44
44
  import ErrorMessage from './ErrorMessage/ErrorMessage';
45
45
  import MessageInput from './MessageInput';
46
46
  import { rehypeMoveImagesOutOfParagraphs } from './Plugins/rehypeMoveImagesOutOfParagraphs';
47
+ import ToolResponse from '../ToolResponse';
48
+ import DeepThinking from '../DeepThinking';
49
+ import SuperscriptMessage from './SuperscriptMessage/SuperscriptMessage';
47
50
  export const MessageBase = (_a) => {
48
- var { role, content, extraContent, name, avatar, timestamp, isLoading, actions, sources, botWord = 'AI', loadingWord = 'Loading message', codeBlockProps, quickResponses, quickResponseContainerProps = { numLabels: 5 }, attachments, hasRoundAvatar = true, avatarProps, quickStarts, userFeedbackForm, userFeedbackComplete, isLiveRegion = true, innerRef, tableProps, openLinkInNewTab = true, additionalRehypePlugins = [], linkProps, error, isEditable, editPlaceholder = 'Edit prompt message...', updateWord = 'Update', cancelWord = 'Cancel', onEditUpdate, onEditCancel, inputRef, editFormProps, isCompact } = _a, props = __rest(_a, ["role", "content", "extraContent", "name", "avatar", "timestamp", "isLoading", "actions", "sources", "botWord", "loadingWord", "codeBlockProps", "quickResponses", "quickResponseContainerProps", "attachments", "hasRoundAvatar", "avatarProps", "quickStarts", "userFeedbackForm", "userFeedbackComplete", "isLiveRegion", "innerRef", "tableProps", "openLinkInNewTab", "additionalRehypePlugins", "linkProps", "error", "isEditable", "editPlaceholder", "updateWord", "cancelWord", "onEditUpdate", "onEditCancel", "inputRef", "editFormProps", "isCompact"]);
51
+ var { role, content, extraContent, name, avatar, timestamp, isLoading, actions, sources, botWord = 'AI', loadingWord = 'Loading message', codeBlockProps, quickResponses, quickResponseContainerProps = { numLabels: 5 }, attachments, hasRoundAvatar = true, avatarProps, quickStarts, userFeedbackForm, userFeedbackComplete, isLiveRegion = true, innerRef, tableProps, openLinkInNewTab = true, additionalRehypePlugins = [], additionalRemarkPlugins = [], linkProps, error, isEditable, editPlaceholder = 'Edit prompt message...', updateWord = 'Update', cancelWord = 'Cancel', onEditUpdate, onEditCancel, inputRef, editFormProps, isCompact, isMarkdownDisabled, reactMarkdownProps, toolResponse, deepThinking, remarkGfmProps } = _a, props = __rest(_a, ["role", "content", "extraContent", "name", "avatar", "timestamp", "isLoading", "actions", "sources", "botWord", "loadingWord", "codeBlockProps", "quickResponses", "quickResponseContainerProps", "attachments", "hasRoundAvatar", "avatarProps", "quickStarts", "userFeedbackForm", "userFeedbackComplete", "isLiveRegion", "innerRef", "tableProps", "openLinkInNewTab", "additionalRehypePlugins", "additionalRemarkPlugins", "linkProps", "error", "isEditable", "editPlaceholder", "updateWord", "cancelWord", "onEditUpdate", "onEditCancel", "inputRef", "editFormProps", "isCompact", "isMarkdownDisabled", "reactMarkdownProps", "toolResponse", "deepThinking", "remarkGfmProps"]);
49
52
  const [messageText, setMessageText] = useState(content);
50
53
  useEffect(() => {
51
54
  setMessageText(content);
@@ -67,6 +70,129 @@ export const MessageBase = (_a) => {
67
70
  // Keep timestamps consistent between Timestamp component and aria-label
68
71
  const date = new Date();
69
72
  const dateString = timestamp !== null && timestamp !== void 0 ? timestamp : `${date.toLocaleDateString()} ${date.toLocaleTimeString()}`;
73
+ const handleMarkdown = () => {
74
+ if (isMarkdownDisabled) {
75
+ return (_jsx(TextMessage, Object.assign({ component: ContentVariants.p }, props, { children: messageText })));
76
+ }
77
+ return (_jsx(Markdown, Object.assign({ components: {
78
+ section: (props) => {
79
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
80
+ const { node } = props, rest = __rest(props, ["node"]);
81
+ return _jsx("section", Object.assign({}, rest, { className: `pf-chatbot__message-text ${rest === null || rest === void 0 ? void 0 : rest.className}` }));
82
+ },
83
+ p: (props) => {
84
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
85
+ const { node } = props, rest = __rest(props, ["node"]);
86
+ return _jsx(TextMessage, Object.assign({ component: ContentVariants.p }, rest));
87
+ },
88
+ code: (_a) => {
89
+ var { children } = _a, props = __rest(_a, ["children"]);
90
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
91
+ const { node } = props, codeProps = __rest(props, ["node"]);
92
+ return (_jsx(CodeBlockMessage, Object.assign({}, codeProps, codeBlockProps, { children: children })));
93
+ },
94
+ h1: (props) => {
95
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
96
+ const { node } = props, rest = __rest(props, ["node"]);
97
+ return _jsx(TextMessage, Object.assign({ component: ContentVariants.h1 }, rest));
98
+ },
99
+ h2: (props) => {
100
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
101
+ const { node } = props, rest = __rest(props, ["node"]);
102
+ return _jsx(TextMessage, Object.assign({ component: ContentVariants.h2 }, rest));
103
+ },
104
+ h3: (props) => {
105
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
106
+ const { node } = props, rest = __rest(props, ["node"]);
107
+ return _jsx(TextMessage, Object.assign({ component: ContentVariants.h3 }, rest));
108
+ },
109
+ h4: (props) => {
110
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
111
+ const { node } = props, rest = __rest(props, ["node"]);
112
+ return _jsx(TextMessage, Object.assign({ component: ContentVariants.h4 }, rest));
113
+ },
114
+ h5: (props) => {
115
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
116
+ const { node } = props, rest = __rest(props, ["node"]);
117
+ return _jsx(TextMessage, Object.assign({ component: ContentVariants.h5 }, rest));
118
+ },
119
+ h6: (props) => {
120
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
121
+ const { node } = props, rest = __rest(props, ["node"]);
122
+ return _jsx(TextMessage, Object.assign({ component: ContentVariants.h6 }, rest));
123
+ },
124
+ blockquote: (props) => {
125
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
126
+ const { node } = props, rest = __rest(props, ["node"]);
127
+ return _jsx(TextMessage, Object.assign({ component: ContentVariants.blockquote }, rest));
128
+ },
129
+ ul: (props) => {
130
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
131
+ const { node } = props, rest = __rest(props, ["node"]);
132
+ return _jsx(UnorderedListMessage, Object.assign({}, rest));
133
+ },
134
+ ol: (props) => {
135
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
136
+ const { node } = props, rest = __rest(props, ["node"]);
137
+ return _jsx(OrderedListMessage, Object.assign({}, rest));
138
+ },
139
+ li: (props) => {
140
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
141
+ const { node } = props, rest = __rest(props, ["node"]);
142
+ return _jsx(ListItemMessage, Object.assign({}, rest));
143
+ },
144
+ // table requires node attribute for calculating headers for mobile breakpoint
145
+ table: (props) => _jsx(TableMessage, Object.assign({}, props, tableProps)),
146
+ tbody: (props) => {
147
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
148
+ const { node } = props, rest = __rest(props, ["node"]);
149
+ return _jsx(TbodyMessage, Object.assign({}, rest));
150
+ },
151
+ thead: (props) => {
152
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
153
+ const { node } = props, rest = __rest(props, ["node"]);
154
+ return _jsx(TheadMessage, Object.assign({}, rest));
155
+ },
156
+ tr: (props) => {
157
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
158
+ const { node } = props, rest = __rest(props, ["node"]);
159
+ return _jsx(TrMessage, Object.assign({}, rest));
160
+ },
161
+ td: (props) => {
162
+ // Conflicts with Td type
163
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
164
+ const { node, width } = props, rest = __rest(props, ["node", "width"]);
165
+ return _jsx(TdMessage, Object.assign({}, rest));
166
+ },
167
+ th: (props) => {
168
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
169
+ const { node } = props, rest = __rest(props, ["node"]);
170
+ return _jsx(ThMessage, Object.assign({}, rest));
171
+ },
172
+ img: (props) => {
173
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
174
+ const { node } = props, rest = __rest(props, ["node"]);
175
+ return _jsx(ImageMessage, Object.assign({}, rest));
176
+ },
177
+ a: (props) => {
178
+ // node is just the details of the document structure - not needed
179
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
180
+ const { node } = props, rest = __rest(props, ["node"]);
181
+ return (
182
+ // some a types conflict with ButtonProps, but it's ok because we are using an a tag
183
+ // there are too many to handle manually
184
+ _jsx(LinkMessage, Object.assign({}, rest, linkProps, { children: props.children })));
185
+ },
186
+ // used for footnotes
187
+ sup: (props) => {
188
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
189
+ const { node } = props, rest = __rest(props, ["node"]);
190
+ return _jsx(SuperscriptMessage, Object.assign({}, rest));
191
+ }
192
+ }, remarkPlugins: [[remarkGfm, Object.assign({}, remarkGfmProps)], ...additionalRemarkPlugins], rehypePlugins: rehypePlugins }, reactMarkdownProps, { remarkRehypeOptions: Object.assign({
193
+ // removes sr-only class from footnote labels applied by default
194
+ footnoteLabelProperties: { className: [''] } }, reactMarkdownProps === null || reactMarkdownProps === void 0 ? void 0 : reactMarkdownProps.remarkRehypeOptions), children: messageText })));
195
+ };
70
196
  const renderMessage = () => {
71
197
  if (isLoading) {
72
198
  return _jsx(MessageLoading, { loadingWord: loadingWord });
@@ -77,38 +203,9 @@ export const MessageBase = (_a) => {
77
203
  setMessageText(value);
78
204
  }, onEditCancel: onEditCancel, inputRef: inputRef }, editFormProps))] }));
79
205
  }
80
- return (_jsxs(_Fragment, { children: [beforeMainContent && _jsx(_Fragment, { children: beforeMainContent }), error ? (_jsx(ErrorMessage, Object.assign({}, error))) : (_jsx(Markdown, { components: {
81
- p: (props) => _jsx(TextMessage, Object.assign({ component: ContentVariants.p }, props)),
82
- code: (_a) => {
83
- var { children } = _a, props = __rest(_a, ["children"]);
84
- return (_jsx(CodeBlockMessage, Object.assign({}, props, codeBlockProps, { children: children })));
85
- },
86
- h1: (props) => _jsx(TextMessage, Object.assign({ component: ContentVariants.h1 }, props)),
87
- h2: (props) => _jsx(TextMessage, Object.assign({ component: ContentVariants.h2 }, props)),
88
- h3: (props) => _jsx(TextMessage, Object.assign({ component: ContentVariants.h3 }, props)),
89
- h4: (props) => _jsx(TextMessage, Object.assign({ component: ContentVariants.h4 }, props)),
90
- h5: (props) => _jsx(TextMessage, Object.assign({ component: ContentVariants.h5 }, props)),
91
- h6: (props) => _jsx(TextMessage, Object.assign({ component: ContentVariants.h6 }, props)),
92
- blockquote: (props) => _jsx(TextMessage, Object.assign({ component: ContentVariants.blockquote }, props)),
93
- ul: (props) => _jsx(UnorderedListMessage, Object.assign({}, props)),
94
- ol: (props) => _jsx(OrderedListMessage, Object.assign({}, props)),
95
- li: (props) => _jsx(ListItemMessage, Object.assign({}, props)),
96
- table: (props) => _jsx(TableMessage, Object.assign({}, props, tableProps)),
97
- tbody: (props) => _jsx(TbodyMessage, Object.assign({}, props)),
98
- thead: (props) => _jsx(TheadMessage, Object.assign({}, props)),
99
- tr: (props) => _jsx(TrMessage, Object.assign({}, props)),
100
- td: (props) => {
101
- // Conflicts with Td type
102
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
103
- const { width } = props, rest = __rest(props, ["width"]);
104
- return _jsx(TdMessage, Object.assign({}, rest));
105
- },
106
- th: (props) => _jsx(ThMessage, Object.assign({}, props)),
107
- img: (props) => _jsx(ImageMessage, Object.assign({}, props)),
108
- a: (props) => (_jsx(LinkMessage, Object.assign({ href: props.href, rel: props.rel, target: props.target }, linkProps, { children: props.children })))
109
- }, remarkPlugins: [remarkGfm], rehypePlugins: rehypePlugins, children: messageText }))] }));
206
+ return (_jsxs(_Fragment, { children: [beforeMainContent && _jsx(_Fragment, { children: beforeMainContent }), error ? _jsx(ErrorMessage, Object.assign({}, error)) : handleMarkdown()] }));
110
207
  };
111
- return (_jsxs("section", Object.assign({ "aria-label": `Message from ${role} - ${dateString}`, className: `pf-chatbot__message pf-chatbot__message--${role}`, "aria-live": isLiveRegion ? 'polite' : undefined, "aria-atomic": isLiveRegion ? false : undefined, ref: innerRef }, props, { children: [_jsx(Avatar, Object.assign({ className: `pf-chatbot__message-avatar ${hasRoundAvatar ? 'pf-chatbot__message-avatar--round' : ''} ${avatarClassName ? avatarClassName : ''}`, src: avatar, alt: "" }, avatarProps)), _jsxs("div", { className: "pf-chatbot__message-contents", children: [_jsxs("div", { className: "pf-chatbot__message-meta", children: [name && (_jsx("span", { className: "pf-chatbot__message-name", children: _jsx(Truncate, { content: name }) })), role === 'bot' && (_jsx(Label, { variant: "outline", isCompact: true, children: botWord })), _jsx(Timestamp, { date: date, children: timestamp })] }), _jsxs("div", { className: "pf-chatbot__message-response", children: [_jsxs("div", { className: "pf-chatbot__message-and-actions", children: [renderMessage(), afterMainContent && _jsx(_Fragment, { children: afterMainContent }), !isLoading && sources && _jsx(SourcesCard, Object.assign({}, sources, { isCompact: isCompact })), quickStarts && quickStarts.quickStart && (_jsx(QuickStartTile, { quickStart: quickStarts.quickStart, onSelectQuickStart: quickStarts.onSelectQuickStart, minuteWord: quickStarts.minuteWord, minuteWordPlural: quickStarts.minuteWordPlural, prerequisiteWord: quickStarts.prerequisiteWord, prerequisiteWordPlural: quickStarts.prerequisiteWordPlural, quickStartButtonAriaLabel: quickStarts.quickStartButtonAriaLabel, isCompact: isCompact })), !isLoading && !isEditable && actions && _jsx(ResponseActions, { actions: actions }), userFeedbackForm && _jsx(UserFeedback, Object.assign({}, userFeedbackForm, { timestamp: dateString, isCompact: isCompact })), userFeedbackComplete && (_jsx(UserFeedbackComplete, Object.assign({}, userFeedbackComplete, { timestamp: dateString, isCompact: isCompact }))), !isLoading && quickResponses && (_jsx(QuickResponse, { quickResponses: quickResponses, quickResponseContainerProps: quickResponseContainerProps, isCompact: isCompact }))] }), attachments && (_jsx("div", { className: "pf-chatbot__message-attachments-container", children: attachments.map((attachment) => {
208
+ return (_jsxs("section", Object.assign({ "aria-label": `Message from ${role} - ${dateString}`, className: `pf-chatbot__message pf-chatbot__message--${role}`, "aria-live": isLiveRegion ? 'polite' : undefined, "aria-atomic": isLiveRegion ? false : undefined, ref: innerRef }, props, { children: [_jsx(Avatar, Object.assign({ className: `pf-chatbot__message-avatar ${hasRoundAvatar ? 'pf-chatbot__message-avatar--round' : ''} ${avatarClassName ? avatarClassName : ''}`, src: avatar, alt: "" }, avatarProps)), _jsxs("div", { className: "pf-chatbot__message-contents", children: [_jsxs("div", { className: "pf-chatbot__message-meta", children: [name && (_jsx("span", { className: "pf-chatbot__message-name", children: _jsx(Truncate, { content: name }) })), role === 'bot' && (_jsx(Label, { variant: "outline", isCompact: true, children: botWord })), _jsx(Timestamp, { date: date, children: timestamp })] }), _jsxs("div", { className: "pf-chatbot__message-response", children: [_jsxs("div", { className: "pf-chatbot__message-and-actions", children: [renderMessage(), afterMainContent && _jsx(_Fragment, { children: afterMainContent }), toolResponse && _jsx(ToolResponse, Object.assign({}, toolResponse)), deepThinking && _jsx(DeepThinking, Object.assign({}, deepThinking)), !isLoading && sources && _jsx(SourcesCard, Object.assign({}, sources, { isCompact: isCompact })), quickStarts && quickStarts.quickStart && (_jsx(QuickStartTile, { quickStart: quickStarts.quickStart, onSelectQuickStart: quickStarts.onSelectQuickStart, minuteWord: quickStarts.minuteWord, minuteWordPlural: quickStarts.minuteWordPlural, prerequisiteWord: quickStarts.prerequisiteWord, prerequisiteWordPlural: quickStarts.prerequisiteWordPlural, quickStartButtonAriaLabel: quickStarts.quickStartButtonAriaLabel, isCompact: isCompact })), !isLoading && !isEditable && actions && _jsx(ResponseActions, { actions: actions }), userFeedbackForm && _jsx(UserFeedback, Object.assign({}, userFeedbackForm, { timestamp: dateString, isCompact: isCompact })), userFeedbackComplete && (_jsx(UserFeedbackComplete, Object.assign({}, userFeedbackComplete, { timestamp: dateString, isCompact: isCompact }))), !isLoading && quickResponses && (_jsx(QuickResponse, { quickResponses: quickResponses, quickResponseContainerProps: quickResponseContainerProps, isCompact: isCompact }))] }), attachments && (_jsx("div", { className: "pf-chatbot__message-attachments-container", children: attachments.map((attachment) => {
112
209
  var _a;
113
210
  return (_jsx("div", { className: "pf-chatbot__message-attachment", children: _jsx(FileDetailsLabel, { fileName: attachment.name, fileId: attachment.id, onClose: attachment.onClose, onClick: attachment.onClick, isLoading: attachment.isLoading, closeButtonAriaLabel: attachment.closeButtonAriaLabel, languageTestId: attachment.languageTestId, spinnerTestId: attachment.spinnerTestId }) }, (_a = attachment.id) !== null && _a !== void 0 ? _a : attachment.name));
114
211
  }) })), !isLoading && endContent && _jsx(_Fragment, { children: endContent })] })] })] })));
@@ -136,8 +136,26 @@ const EMPTY_TABLE = `
136
136
  | |
137
137
 
138
138
  `;
139
+ const FOOTNOTE = `This is some text with a footnote[^1] and here's a longer one.[^bignote]
140
+
141
+ You can also reference the same footnote multiple times[^1].
142
+
143
+ [^1]: This is the full footnote text. You can click the arrow to go back up.
144
+
145
+ [^bignote]: Here's one with multiple paragraphs and **formatting**.
146
+
147
+ Indent paragraphs to include them in the footnote.
148
+
149
+ Add as many paragraphs as you like. You can include *italic text*, **bold text**, and even \`code\`.
150
+
151
+ > You can even include blockquotes in footnotes!`;
139
152
  const IMAGE = `![Multi-colored wavy lines on a black background](https://cdn.dribbble.com/userupload/10651749/file/original-8a07b8e39d9e8bf002358c66fce1223e.gif)`;
140
153
  const INLINE_IMAGE = `inline text ![Multi-colored wavy lines on a black background](https://cdn.dribbble.com/userupload/10651749/file/original-8a07b8e39d9e8bf002358c66fce1223e.gif)`;
154
+ const DEEP_THINKING = {
155
+ toggleContent: 'Show thinking',
156
+ subheading: 'Thought for 3 seconds',
157
+ body: "Here's why I said this."
158
+ };
141
159
  const ERROR = {
142
160
  title: 'Could not load chat',
143
161
  children: 'Wait a few minutes and check your network settings. If the issue persists: ',
@@ -521,6 +539,20 @@ describe('Message', () => {
521
539
  } }));
522
540
  expect(screen.getAllByRole('img')[1]).toHaveAttribute('src', 'test.png');
523
541
  }));
542
+ it('should handle tool response correctly', () => __awaiter(void 0, void 0, void 0, function* () {
543
+ render(_jsx(Message, { avatar: "./img", role: "user", name: "User", content: "Hi", toolResponse: {
544
+ toggleContent: 'Tool response: Name',
545
+ subheading: 'Thought for 3 seconds',
546
+ body: 'Lorem ipsum dolor sit amet',
547
+ cardTitle: 'Card title',
548
+ cardBody: 'Card body'
549
+ } }));
550
+ expect(screen.getByRole('button', { name: /Tool response: Name/i })).toBeTruthy();
551
+ expect(screen.getByText('Thought for 3 seconds')).toBeTruthy();
552
+ expect(screen.getByText('Lorem ipsum dolor sit amet')).toBeTruthy();
553
+ expect(screen.getByText('Card title')).toBeTruthy();
554
+ expect(screen.getByText('Card body')).toBeTruthy();
555
+ }));
524
556
  it('should handle block quote correctly', () => {
525
557
  render(_jsx(Message, { avatar: "./img", role: "user", name: "User", content: BLOCK_QUOTES }));
526
558
  expect(screen.getByText(/Blockquotes can also be nested.../)).toBeTruthy();
@@ -586,6 +618,28 @@ describe('Message', () => {
586
618
  render(_jsx(Message, { avatar: "./img", role: "user", name: "User", content: TABLE, tableProps: { 'aria-label': 'Test' } }));
587
619
  expect(screen.getByRole('grid', { name: /Test/i })).toBeTruthy();
588
620
  });
621
+ it('should render footnote correctly', () => {
622
+ render(_jsx(Message, { avatar: "./img", role: "user", name: "User", content: FOOTNOTE }));
623
+ expect(screen.getByText(/This is some text with a footnote/i)).toBeTruthy();
624
+ expect(screen.getByText(/and here's a longer one./i)).toBeTruthy();
625
+ expect(screen.getByText(/You can also reference the same footnote multiple times./i)).toBeTruthy();
626
+ expect(screen.getByRole('heading', { name: /Footnotes/i })).toBeTruthy();
627
+ expect(screen.getByText(/This is the full footnote text. You can click the arrow to go back up./i)).toBeTruthy();
628
+ expect(screen.getByText(/Here's one with multiple paragraphs and/i)).toBeTruthy();
629
+ expect(screen.getByText(/formatting/i)).toBeTruthy();
630
+ expect(screen.getByText(/Indent paragraphs to include them in the footnote./i)).toBeTruthy();
631
+ expect(screen.getByText(/Add as many paragraphs as you like. You can include/i)).toBeTruthy();
632
+ expect(screen.getByText(/italic text/i)).toBeTruthy();
633
+ expect(screen.getByText(/bold text/i)).toBeTruthy();
634
+ expect(screen.getByText(/, and even/i)).toBeTruthy();
635
+ expect(screen.getByText(/code/i)).toBeTruthy();
636
+ expect(screen.getByText(/You can even include blockquotes in footnotes!/i)).toBeTruthy();
637
+ expect(screen.getAllByRole('link', { name: '1' })).toHaveLength(2);
638
+ expect(screen.getAllByRole('link', { name: '2' })).toBeTruthy();
639
+ expect(screen.getByRole('link', { name: 'Back to reference 1' })).toBeTruthy();
640
+ expect(screen.getByRole('link', { name: 'Back to reference 1-2' })).toBeTruthy();
641
+ expect(screen.getByRole('link', { name: /Back to reference 2/i })).toBeTruthy();
642
+ });
589
643
  it('should render beforeMainContent with main content', () => {
590
644
  const mainContent = 'Main message content';
591
645
  const beforeMainContentText = 'Before main content';
@@ -734,4 +788,21 @@ describe('Message', () => {
734
788
  const form = container.querySelector('form');
735
789
  expect(form).toHaveClass('test');
736
790
  });
791
+ it('should be able to disable markdown parsing', () => {
792
+ render(_jsx(Message, { avatar: "./img", role: "user", name: "User", content: CODE_MESSAGE, isMarkdownDisabled: true }));
793
+ // this is looking for markdown syntax that is ordinarily stripped
794
+ expect(screen.getByText(/~~~yaml/i)).toBeTruthy();
795
+ });
796
+ it('should be able to pass props to react-markdown, such as disabling tags', () => {
797
+ render(_jsx(Message, { avatar: "./img", role: "user", name: "User", content: CODE_MESSAGE, reactMarkdownProps: { disallowedElements: ['code'] } }));
798
+ expect(screen.getByText('Here is some YAML code:')).toBeTruthy();
799
+ // code block isn't rendering
800
+ expect(screen.queryByRole('button', { name: 'Copy code' })).toBeFalsy();
801
+ });
802
+ it('should render deep thinking section correctly', () => {
803
+ render(_jsx(Message, { avatar: "./img", role: "user", name: "User", content: "", deepThinking: DEEP_THINKING }));
804
+ expect(screen.getByRole('button', { name: /Show thinking/i })).toBeTruthy();
805
+ expect(screen.getByText('Thought for 3 seconds')).toBeTruthy();
806
+ expect(screen.getByText("Here's why I said this.")).toBeTruthy();
807
+ });
737
808
  });
@@ -0,0 +1,3 @@
1
+ import { ExtraProps } from 'react-markdown';
2
+ declare const SuperscriptMessage: ({ children }: JSX.IntrinsicElements["sup"] & ExtraProps) => import("react/jsx-runtime").JSX.Element;
3
+ export default SuperscriptMessage;
@@ -0,0 +1,3 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ const SuperscriptMessage = ({ children }) => (_jsx("span", { className: "pf-chatbot__message-superscript", children: _jsx("sup", { children: children }) }));
3
+ export default SuperscriptMessage;
@@ -1,5 +1,5 @@
1
1
  import type { FunctionComponent } from 'react';
2
- import { CardProps, LabelGroupProps, OUIAProps } from '@patternfly/react-core';
2
+ import { ActionGroupProps, ButtonProps, CardBodyProps, CardHeaderProps, CardProps, FormProps, LabelGroupProps, OUIAProps, TextAreaProps } from '@patternfly/react-core';
3
3
  import QuickResponse from '../QuickResponse/QuickResponse';
4
4
  export interface UserFeedbackProps extends Omit<CardProps, 'onSubmit'>, OUIAProps {
5
5
  /** Additional classes for the pagination navigation container. */
@@ -34,6 +34,20 @@ export interface UserFeedbackProps extends Omit<CardProps, 'onSubmit'>, OUIAProp
34
34
  focusOnLoad?: boolean;
35
35
  /** Timestamp passed in by Message for more context in aria announcements */
36
36
  timestamp?: string;
37
+ /** Additional props passed to submit button */
38
+ submitButtonProps?: ButtonProps;
39
+ /** Additional props passed to card header */
40
+ cardHeaderProps?: CardHeaderProps;
41
+ /** Additional props passed to card body */
42
+ cardBodyProps?: CardBodyProps;
43
+ /** Additional props passed to title heading */
44
+ headingLevelProps?: React.HTMLAttributes<HTMLHeadingElement>;
45
+ /** Additional props passed to form */
46
+ formProps?: FormProps;
47
+ /** Additional props passed to text area */
48
+ textAreaProps?: TextAreaProps;
49
+ /** Additional props passed to action group */
50
+ actionGroupProps?: ActionGroupProps;
37
51
  }
38
52
  declare const UserFeedback: FunctionComponent<UserFeedbackProps>;
39
53
  export default UserFeedback;
@@ -16,7 +16,7 @@ import { ActionGroup, Button, Card, CardBody, CardHeader, Form, TextArea } from
16
16
  import QuickResponse from '../QuickResponse/QuickResponse';
17
17
  import CloseButton from './CloseButton';
18
18
  const UserFeedback = (_a) => {
19
- var { className, timestamp, title = 'Why did you choose this rating?', hasTextArea, textAreaAriaLabel = `Provide optional additional feedback for message received at ${timestamp}`, textAreaPlaceholder = 'Provide optional additional feedback', onTextAreaChange, submitWord = 'Submit', quickResponses, quickResponseContainerProps = { 'aria-label': `Quick feedback for message received at ${timestamp}` }, onSubmit, onClose, closeButtonAriaLabel = `Close feedback for message received at ${timestamp}`, id, headingLevel: HeadingLevel = 'h1', focusOnLoad = true, isCompact } = _a, props = __rest(_a, ["className", "timestamp", "title", "hasTextArea", "textAreaAriaLabel", "textAreaPlaceholder", "onTextAreaChange", "submitWord", "quickResponses", "quickResponseContainerProps", "onSubmit", "onClose", "closeButtonAriaLabel", "id", "headingLevel", "focusOnLoad", "isCompact"]);
19
+ var { className, timestamp, title = 'Why did you choose this rating?', hasTextArea, textAreaAriaLabel = `Provide optional additional feedback for message received at ${timestamp}`, textAreaPlaceholder = 'Provide optional additional feedback', onTextAreaChange, submitWord = 'Submit', quickResponses, quickResponseContainerProps = { 'aria-label': `Quick feedback for message received at ${timestamp}` }, onSubmit, onClose, closeButtonAriaLabel = `Close feedback for message received at ${timestamp}`, id, headingLevel: HeadingLevel = 'h1', focusOnLoad = true, isCompact, children, cardHeaderProps, cardBodyProps, headingLevelProps, formProps, textAreaProps, actionGroupProps, submitButtonProps } = _a, props = __rest(_a, ["className", "timestamp", "title", "hasTextArea", "textAreaAriaLabel", "textAreaPlaceholder", "onTextAreaChange", "submitWord", "quickResponses", "quickResponseContainerProps", "onSubmit", "onClose", "closeButtonAriaLabel", "id", "headingLevel", "focusOnLoad", "isCompact", "children", "cardHeaderProps", "cardBodyProps", "headingLevelProps", "formProps", "textAreaProps", "actionGroupProps", "submitButtonProps"]);
20
20
  const [selectedResponse, setSelectedResponse] = useState();
21
21
  const [value, setValue] = useState('');
22
22
  const divRef = useRef(null);
@@ -28,11 +28,11 @@ const UserFeedback = (_a) => {
28
28
  }, []);
29
29
  return (
30
30
  /* card does not have ref forwarding; hence wrapper div */
31
- _jsx("div", { ref: divRef, id: id, tabIndex: 0, "aria-label": title, children: _jsxs(Card, Object.assign({ isCompact: isCompact, className: `pf-chatbot__feedback-card ${className ? className : ''}` }, props, { children: [_jsx(CardHeader, { actions: {
31
+ _jsx("div", { ref: divRef, id: id, tabIndex: 0, "aria-label": title, children: _jsxs(Card, Object.assign({ isCompact: isCompact, className: `pf-chatbot__feedback-card ${className ? className : ''}` }, props, { children: [_jsx(CardHeader, Object.assign({ actions: {
32
32
  actions: _jsx(CloseButton, { onClose: onClose, ariaLabel: closeButtonAriaLabel })
33
- }, children: _jsx(HeadingLevel, { className: "pf-chatbot__feedback-card-title", children: title }) }), _jsx(CardBody, { children: _jsxs(Form, { className: `pf-chatbot__feedback-card-form ${isCompact ? 'pf-m-compact' : ''}`, children: [quickResponses && (_jsx(QuickResponse, { quickResponses: quickResponses, quickResponseContainerProps: quickResponseContainerProps, onSelect: (id) => setSelectedResponse(id), isCompact: isCompact })), hasTextArea && (_jsx(TextArea, { value: value, onChange: (_event, value) => {
33
+ } }, cardHeaderProps, { children: _jsx(HeadingLevel, Object.assign({ className: "pf-chatbot__feedback-card-title" }, headingLevelProps, { children: title })) })), _jsx(CardBody, Object.assign({}, cardBodyProps, { children: _jsxs(Form, Object.assign({ className: `pf-chatbot__feedback-card-form ${isCompact ? 'pf-m-compact' : ''}` }, formProps, { children: [quickResponses && (_jsx(QuickResponse, { quickResponses: quickResponses, quickResponseContainerProps: quickResponseContainerProps, onSelect: (id) => setSelectedResponse(id), isCompact: isCompact })), hasTextArea && (_jsx(TextArea, Object.assign({ value: value, onChange: (_event, value) => {
34
34
  setValue(value);
35
35
  onTextAreaChange && onTextAreaChange(_event, value);
36
- }, placeholder: textAreaPlaceholder, "aria-label": textAreaAriaLabel, resizeOrientation: "vertical" })), _jsx(ActionGroup, { children: _jsx(Button, { onClick: () => onSubmit(selectedResponse, value), children: submitWord }) })] }) })] })) }));
36
+ }, placeholder: textAreaPlaceholder, "aria-label": textAreaAriaLabel, resizeOrientation: "vertical" }, textAreaProps))), children, _jsx(ActionGroup, Object.assign({}, actionGroupProps, { children: _jsx(Button, Object.assign({ onClick: () => onSubmit(selectedResponse, value) }, submitButtonProps, { children: submitWord })) }))] })) }))] })) }));
37
37
  };
38
38
  export default UserFeedback;
@@ -7,7 +7,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
7
7
  step((generator = generator.apply(thisArg, _arguments || [])).next());
8
8
  });
9
9
  };
10
- import { jsx as _jsx } from "react/jsx-runtime";
10
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
11
11
  import { render, screen } from '@testing-library/react';
12
12
  import '@testing-library/jest-dom';
13
13
  import userEvent from '@testing-library/user-event';
@@ -129,4 +129,48 @@ describe('UserFeedback', () => {
129
129
  render(_jsx(UserFeedback, { timestamp: "12/12/12", onClose: jest.fn, onSubmit: jest.fn, quickResponses: MOCK_RESPONSES, "data-testid": "card", isCompact: true }));
130
130
  expect(screen.getByTestId('card')).toHaveClass('pf-m-compact');
131
131
  });
132
+ it('should pass buttonProps to submit button', () => {
133
+ render(_jsx(UserFeedback, { timestamp: "12/12/12", onClose: jest.fn, onSubmit: jest.fn, quickResponses: MOCK_RESPONSES, submitButtonProps: { variant: 'secondary', isDisabled: true } }));
134
+ const submitButton = screen.getByRole('button', { name: /Submit/i });
135
+ expect(submitButton).toHaveClass('pf-v6-c-button pf-m-secondary');
136
+ expect(submitButton).toBeDisabled();
137
+ });
138
+ it('should pass cardHeaderProps to card header', () => {
139
+ render(_jsx(UserFeedback, { timestamp: "12/12/12", onClose: jest.fn, onSubmit: jest.fn, quickResponses: MOCK_RESPONSES, cardHeaderProps: { 'data-testid': 'card-header', className: 'custom-header' } }));
140
+ const cardHeader = screen.getByTestId('card-header');
141
+ expect(cardHeader).toHaveClass('custom-header');
142
+ });
143
+ it('should pass cardBodyProps to card body', () => {
144
+ render(_jsx(UserFeedback, { timestamp: "12/12/12", onClose: jest.fn, onSubmit: jest.fn, quickResponses: MOCK_RESPONSES, cardBodyProps: { 'data-testid': 'card-body', className: 'custom-body' } }));
145
+ const cardBody = screen.getByTestId('card-body');
146
+ expect(cardBody).toHaveClass('custom-body');
147
+ });
148
+ it('should pass headingLevelProps to title heading', () => {
149
+ render(_jsx(UserFeedback, { timestamp: "12/12/12", onClose: jest.fn, onSubmit: jest.fn, quickResponses: MOCK_RESPONSES, headingLevelProps: { className: 'custom-heading', id: 'feedback-title' } }));
150
+ const heading = screen.getByRole('heading', { level: 1, name: /Why did you choose this rating?/i });
151
+ expect(heading).toHaveClass('custom-heading');
152
+ expect(heading).toHaveAttribute('id', 'feedback-title');
153
+ });
154
+ it('should pass formProps to form', () => {
155
+ render(_jsx(UserFeedback, { timestamp: "12/12/12", onClose: jest.fn, onSubmit: jest.fn, quickResponses: MOCK_RESPONSES, formProps: { 'data-testid': 'feedback-form', className: 'custom-form' } }));
156
+ const form = screen.getByTestId('feedback-form');
157
+ expect(form).toHaveClass('custom-form');
158
+ });
159
+ it('should pass textAreaProps to text area when hasTextArea is true', () => {
160
+ render(_jsx(UserFeedback, { timestamp: "12/12/12", onClose: jest.fn, onSubmit: jest.fn, quickResponses: MOCK_RESPONSES, hasTextArea: true, textAreaProps: { 'data-testid': 'custom-textarea', rows: 5 } }));
161
+ const textArea = screen.getByTestId('custom-textarea');
162
+ expect(textArea).toHaveAttribute('rows', '5');
163
+ expect(textArea).toHaveAttribute('data-testid', 'custom-textarea');
164
+ });
165
+ it('should pass actionGroupProps to action group', () => {
166
+ render(_jsx(UserFeedback, { timestamp: "12/12/12", onClose: jest.fn, onSubmit: jest.fn, quickResponses: MOCK_RESPONSES, actionGroupProps: { 'data-testid': 'action-group', className: 'custom-actions' } }));
167
+ const actionGroup = screen.getByTestId('action-group');
168
+ expect(actionGroup).toHaveClass('custom-actions');
169
+ });
170
+ it('should render children', () => {
171
+ render(_jsxs(UserFeedback, { timestamp: "12/12/12", onClose: jest.fn, onSubmit: jest.fn, quickResponses: MOCK_RESPONSES, children: [_jsx("div", { "data-testid": "custom-content", children: "Custom feedback content" }), _jsx("p", { children: "Additional paragraph" })] }));
172
+ expect(screen.getByTestId('custom-content')).toBeInTheDocument();
173
+ expect(screen.getByText('Custom feedback content')).toBeInTheDocument();
174
+ expect(screen.getByText('Additional paragraph')).toBeInTheDocument();
175
+ });
132
176
  });
@@ -26,6 +26,7 @@ export const MessageBarBase = (_a) => {
26
26
  const [message, setMessage] = useState(value !== null && value !== void 0 ? value : '');
27
27
  const [isListeningMessage, setIsListeningMessage] = useState(false);
28
28
  const [hasSentMessage, setHasSentMessage] = useState(false);
29
+ const [isComposing, setIsComposing] = useState(false);
29
30
  const inputRef = useRef(null);
30
31
  const textareaRef = (_b = innerRef) !== null && _b !== void 0 ? _b : inputRef;
31
32
  const attachButtonRef = useRef(null);
@@ -151,18 +152,32 @@ export const MessageBarBase = (_a) => {
151
152
  setMessage('');
152
153
  }, [onSendMessage]);
153
154
  const handleKeyDown = useCallback((event) => {
154
- if (event.key === 'Enter' && !event.shiftKey) {
155
+ // Japanese and other languages may use IME for character input.
156
+ // In these cases, enter is used to select the final input, so we need to check for composition end instead.
157
+ // See more info at https://www.stum.de/2016/06/24/handling-ime-events-in-javascript/
158
+ // Chrome, Edge, and Firefox seem to work well with just the compose event.
159
+ // Safari is a little bit special. We need to handle 229 as well in this case.
160
+ const nativeEvent = event.nativeEvent;
161
+ const isCompositionKey = nativeEvent.which === 229;
162
+ const isCurrentlyComposing = isComposing || isCompositionKey;
163
+ if (event.key === 'Enter' && !isCurrentlyComposing && !event.shiftKey) {
155
164
  event.preventDefault();
156
165
  if (!isSendButtonDisabled && !hasStopButton) {
157
166
  handleSend(message);
158
167
  }
159
168
  }
160
- if (event.key === 'Enter' && event.shiftKey) {
169
+ if (event.key === 'Enter' && !isCurrentlyComposing && event.shiftKey) {
161
170
  if (textareaRef.current) {
162
171
  handleNewLine(textareaRef.current);
163
172
  }
164
173
  }
165
- }, [isSendButtonDisabled, hasStopButton, handleSend, message]);
174
+ }, [isSendButtonDisabled, hasStopButton, handleSend, message, isComposing]);
175
+ const handleCompositionStart = useCallback(() => {
176
+ setIsComposing(true);
177
+ }, []);
178
+ const handleCompositionEnd = useCallback(() => {
179
+ setIsComposing(false);
180
+ }, []);
166
181
  const handleAttachMenuToggle = () => {
167
182
  (attachMenuProps === null || attachMenuProps === void 0 ? void 0 : attachMenuProps.setIsAttachMenuOpen) && (attachMenuProps === null || attachMenuProps === void 0 ? void 0 : attachMenuProps.setIsAttachMenuOpen(!(attachMenuProps === null || attachMenuProps === void 0 ? void 0 : attachMenuProps.isAttachMenuOpen)));
168
183
  attachMenuProps === null || attachMenuProps === void 0 ? void 0 : attachMenuProps.onAttachMenuToggleClick();
@@ -178,7 +193,7 @@ export const MessageBarBase = (_a) => {
178
193
  }
179
194
  return (_jsxs(_Fragment, { children: [attachMenuProps && (_jsx(AttachButton, Object.assign({ ref: attachButtonRef, onClick: handleAttachMenuToggle, isDisabled: isListeningMessage, tooltipContent: (_d = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.attach) === null || _d === void 0 ? void 0 : _d.tooltipContent, isCompact: isCompact, tooltipProps: (_e = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.attach) === null || _e === void 0 ? void 0 : _e.tooltipProps, allowedFileTypes: allowedFileTypes, minSize: minSize, maxSize: maxSize, maxFiles: maxFiles, isAttachmentDisabled: isAttachmentDisabled, onAttach: onAttach, onAttachRejected: onAttachRejected, validator: validator, dropzoneProps: dropzoneProps }, (_f = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.attach) === null || _f === void 0 ? void 0 : _f.props))), !attachMenuProps && hasAttachButton && (_jsx(AttachButton, Object.assign({ onAttachAccepted: handleAttach, isDisabled: isListeningMessage, tooltipContent: (_g = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.attach) === null || _g === void 0 ? void 0 : _g.tooltipContent, inputTestId: (_h = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.attach) === null || _h === void 0 ? void 0 : _h.inputTestId, isCompact: isCompact, tooltipProps: (_j = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.attach) === null || _j === void 0 ? void 0 : _j.tooltipProps, allowedFileTypes: allowedFileTypes, minSize: minSize, maxSize: maxSize, maxFiles: maxFiles, isAttachmentDisabled: isAttachmentDisabled, onAttach: onAttach, onAttachRejected: onAttachRejected, validator: validator, dropzoneProps: dropzoneProps }, (_k = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.attach) === null || _k === void 0 ? void 0 : _k.props))), hasMicrophoneButton && (_jsx(MicrophoneButton, Object.assign({ isListening: isListeningMessage, onIsListeningChange: setIsListeningMessage, onSpeechRecognition: handleSpeechRecognition, tooltipContent: (_l = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.microphone) === null || _l === void 0 ? void 0 : _l.tooltipContent, language: (_m = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.microphone) === null || _m === void 0 ? void 0 : _m.language, isCompact: isCompact, tooltipProps: (_o = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.microphone) === null || _o === void 0 ? void 0 : _o.tooltipProps }, (_p = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.microphone) === null || _p === void 0 ? void 0 : _p.props))), (alwayShowSendButton || message) && (_jsx(SendButton, Object.assign({ value: message, onClick: () => handleSend(message), isDisabled: isSendButtonDisabled, tooltipContent: (_q = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.send) === null || _q === void 0 ? void 0 : _q.tooltipContent, isCompact: isCompact, tooltipProps: (_r = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.send) === null || _r === void 0 ? void 0 : _r.tooltipProps }, (_s = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.send) === null || _s === void 0 ? void 0 : _s.props)))] }));
180
195
  };
181
- const messageBarContents = (_jsxs(_Fragment, { children: [_jsx("div", { className: `pf-chatbot__message-bar-input ${isCompact ? 'pf-m-compact' : ''}`, children: _jsx(TextArea, Object.assign({ className: "pf-chatbot__message-textarea", value: message, onChange: handleChange, "aria-label": isListeningMessage ? listeningText : placeholder, placeholder: isListeningMessage ? listeningText : placeholder, ref: textareaRef, onKeyDown: handleKeyDown }, props)) }), _jsx("div", { className: "pf-chatbot__message-bar-actions", children: renderButtons() })] }));
196
+ const messageBarContents = (_jsxs(_Fragment, { children: [_jsx("div", { className: `pf-chatbot__message-bar-input ${isCompact ? 'pf-m-compact' : ''}`, children: _jsx(TextArea, Object.assign({ className: "pf-chatbot__message-textarea", value: message, onChange: handleChange, "aria-label": isListeningMessage ? listeningText : placeholder, placeholder: isListeningMessage ? listeningText : placeholder, ref: textareaRef, onKeyDown: handleKeyDown, onCompositionStart: handleCompositionStart, onCompositionEnd: handleCompositionEnd }, props)) }), _jsx("div", { className: "pf-chatbot__message-bar-actions", children: renderButtons() })] }));
182
197
  if (attachMenuProps) {
183
198
  return (_jsx(AttachMenu, Object.assign({ toggle: (toggleRef) => (_jsx("div", { ref: toggleRef, className: `pf-chatbot__message-bar ${className !== null && className !== void 0 ? className : ''}`, children: messageBarContents })), filteredItems: attachMenuProps === null || attachMenuProps === void 0 ? void 0 : attachMenuProps.attachMenuItems }, (attachMenuProps && { isOpen: attachMenuProps.isAttachMenuOpen }), { onOpenChange: (isAttachMenuOpen) => {
184
199
  var _a;
@@ -1,4 +1,5 @@
1
1
  import type { FunctionComponent } from 'react';
2
+ import { TooltipProps, ButtonProps } from '@patternfly/react-core';
2
3
  export interface JumpButtonProps {
3
4
  /** Position of the Jump Button(top/bottom) */
4
5
  position: 'top' | 'bottom';
@@ -6,6 +7,10 @@ export interface JumpButtonProps {
6
7
  onClick: () => void;
7
8
  /** Flag to change the visibilty of the button */
8
9
  isHidden?: boolean;
10
+ /** Additional props passed to jump buttons */
11
+ jumpButtonProps?: ButtonProps;
12
+ /** Additional props passed to tooltip */
13
+ jumpButtonTooltipProps?: TooltipProps;
9
14
  }
10
15
  declare const JumpButton: FunctionComponent<JumpButtonProps>;
11
16
  export default JumpButton;
@@ -3,5 +3,5 @@ import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import { Button, Tooltip, Icon } from '@patternfly/react-core';
4
4
  import { ArrowUpIcon } from '@patternfly/react-icons/dist/esm/icons/arrow-up-icon';
5
5
  import { ArrowDownIcon } from '@patternfly/react-icons/dist/esm/icons/arrow-down-icon';
6
- const JumpButton = ({ position, isHidden, onClick }) => isHidden ? null : (_jsx(Tooltip, { id: `pf-chatbot__tooltip--jump-${position}`, content: `Back to ${position}`, position: "top", children: _jsx(Button, { variant: "plain", className: `pf-chatbot__jump pf-chatbot__jump--${position}`, "aria-label": `Jump ${position}`, onClick: onClick, children: _jsx(Icon, { iconSize: "lg", isInline: true, children: position === 'top' ? _jsx(ArrowUpIcon, {}) : _jsx(ArrowDownIcon, {}) }) }) }));
6
+ const JumpButton = ({ position, isHidden, onClick, jumpButtonProps, jumpButtonTooltipProps }) => isHidden ? null : (_jsx(Tooltip, Object.assign({ id: `pf-chatbot__tooltip--jump-${position}`, content: `Back to ${position}`, position: "top" }, jumpButtonTooltipProps, { children: _jsx(Button, Object.assign({ variant: "plain", className: `pf-chatbot__jump pf-chatbot__jump--${position}`, "aria-label": `Back to ${position}`, onClick: onClick }, jumpButtonProps, { children: _jsx(Icon, { iconSize: "lg", isInline: true, children: position === 'top' ? _jsx(ArrowUpIcon, {}) : _jsx(ArrowDownIcon, {}) }) })) })));
7
7
  export default JumpButton;
@@ -15,20 +15,20 @@ import userEvent from '@testing-library/user-event';
15
15
  describe('JumpButton', () => {
16
16
  it('should render top button correctly', () => {
17
17
  render(_jsx(JumpButton, { position: "top", onClick: jest.fn() }));
18
- expect(screen.getByRole('button', { name: /Jump top/i })).toBeTruthy();
18
+ expect(screen.getByRole('button', { name: /Back to top/i })).toBeTruthy();
19
19
  });
20
20
  it('should render bottom button correctly', () => {
21
21
  render(_jsx(JumpButton, { position: "bottom", onClick: jest.fn() }));
22
- expect(screen.getByRole('button', { name: /Jump bottom/i })).toBeTruthy();
22
+ expect(screen.getByRole('button', { name: /Back to bottom/i })).toBeTruthy();
23
23
  });
24
24
  it('should call onClick appropriately', () => __awaiter(void 0, void 0, void 0, function* () {
25
25
  const spy = jest.fn();
26
26
  render(_jsx(JumpButton, { position: "bottom", onClick: spy }));
27
- yield userEvent.click(screen.getByRole('button', { name: /Jump bottom/i }));
27
+ yield userEvent.click(screen.getByRole('button', { name: /Back to bottom/i }));
28
28
  expect(spy).toHaveBeenCalledTimes(1);
29
29
  }));
30
30
  it('should be hidden if isHidden prop is used', () => __awaiter(void 0, void 0, void 0, function* () {
31
31
  render(_jsx(JumpButton, { position: "bottom", onClick: jest.fn(), isHidden: true }));
32
- expect(screen.queryByRole('button', { name: /Jump bottom/i })).toBeFalsy();
32
+ expect(screen.queryByRole('button', { name: /Back to bottom/i })).toBeFalsy();
33
33
  }));
34
34
  });
@@ -1,4 +1,5 @@
1
1
  import { HTMLProps, ReactNode } from 'react';
2
+ import { ButtonProps, TooltipProps } from '@patternfly/react-core';
2
3
  export interface MessageBoxProps extends HTMLProps<HTMLDivElement> {
3
4
  /** Content that can be announced, such as a new message, for screen readers */
4
5
  announcement?: string;
@@ -18,6 +19,14 @@ export interface MessageBoxProps extends HTMLProps<HTMLDivElement> {
18
19
  onScrollToBottomClick?: () => void;
19
20
  /** Flag to enable automatic scrolling when new messages are added */
20
21
  enableSmartScroll?: boolean;
22
+ /** Props passed to top jump button */
23
+ jumpButtonTopProps?: ButtonProps;
24
+ /** Props passed to bottom jump button */
25
+ jumpButtonBottomProps?: ButtonProps;
26
+ /** Props passed to top jump button tooltip */
27
+ jumpButtonTopTooltipProps?: TooltipProps;
28
+ /** Props passed to top jump button tooltip */
29
+ jumpButtonBottomTooltipProps?: TooltipProps;
21
30
  }
22
31
  export interface MessageBoxHandle extends HTMLDivElement {
23
32
  /** Scrolls to the top of the message box */