@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.
- package/dist/cjs/Chatbot/Chatbot.js +1 -7
- package/dist/cjs/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.d.ts +2 -0
- package/dist/cjs/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.js +2 -2
- package/dist/cjs/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.d.ts +22 -2
- package/dist/cjs/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.js +15 -9
- package/dist/cjs/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.test.js +40 -2
- package/dist/cjs/ChatbotHeader/ChatbotHeaderMenu.js +1 -1
- package/dist/cjs/ChatbotHeader/ChatbotHeaderMenu.test.js +1 -1
- package/dist/cjs/ChatbotHeader/ChatbotHeaderNewChatButton.d.ts +18 -0
- package/dist/cjs/ChatbotHeader/ChatbotHeaderNewChatButton.js +25 -0
- package/dist/cjs/ChatbotHeader/ChatbotHeaderNewChatButton.test.d.ts +1 -0
- package/dist/cjs/ChatbotHeader/ChatbotHeaderNewChatButton.test.js +22 -0
- package/dist/cjs/ChatbotHeader/index.d.ts +1 -0
- package/dist/cjs/ChatbotHeader/index.js +1 -0
- package/dist/cjs/DeepThinking/DeepThinking.d.ts +18 -0
- package/dist/cjs/DeepThinking/DeepThinking.js +18 -0
- package/dist/cjs/DeepThinking/DeepThinking.test.d.ts +1 -0
- package/dist/cjs/DeepThinking/DeepThinking.test.js +48 -0
- package/dist/cjs/DeepThinking/index.d.ts +2 -0
- package/dist/cjs/DeepThinking/index.js +23 -0
- package/dist/cjs/FilePreview/FilePreview.d.ts +26 -0
- package/dist/cjs/FilePreview/FilePreview.js +26 -0
- package/dist/cjs/FilePreview/FilePreview.test.d.ts +1 -0
- package/dist/cjs/FilePreview/FilePreview.test.js +97 -0
- package/dist/cjs/FilePreview/index.d.ts +2 -0
- package/dist/cjs/FilePreview/index.js +23 -0
- package/dist/cjs/Message/CodeBlockMessage/CodeBlockMessage.js +3 -3
- package/dist/cjs/Message/LinkMessage/LinkMessage.d.ts +2 -1
- package/dist/cjs/Message/LinkMessage/LinkMessage.js +7 -3
- package/dist/cjs/Message/ListMessage/ListItemMessage.d.ts +1 -1
- package/dist/cjs/Message/ListMessage/ListItemMessage.js +16 -1
- package/dist/cjs/Message/Message.d.ts +15 -0
- package/dist/cjs/Message/Message.js +129 -32
- package/dist/cjs/Message/Message.test.js +71 -0
- package/dist/cjs/Message/SuperscriptMessage/SuperscriptMessage.d.ts +3 -0
- package/dist/cjs/Message/SuperscriptMessage/SuperscriptMessage.js +5 -0
- package/dist/cjs/Message/UserFeedback/UserFeedback.d.ts +15 -1
- package/dist/cjs/Message/UserFeedback/UserFeedback.js +4 -4
- package/dist/cjs/Message/UserFeedback/UserFeedback.test.js +44 -0
- package/dist/cjs/MessageBar/MessageBar.js +19 -4
- package/dist/cjs/MessageBox/JumpButton.d.ts +5 -0
- package/dist/cjs/MessageBox/JumpButton.js +1 -1
- package/dist/cjs/MessageBox/JumpButton.test.js +4 -4
- package/dist/cjs/MessageBox/MessageBox.d.ts +9 -0
- package/dist/cjs/MessageBox/MessageBox.js +2 -2
- package/dist/cjs/MessageBox/MessageBox.test.js +2 -2
- package/dist/cjs/SourcesCard/SourcesCard.d.ts +13 -1
- package/dist/cjs/SourcesCard/SourcesCard.js +6 -6
- package/dist/cjs/SourcesCard/SourcesCard.test.js +49 -0
- package/dist/cjs/ToolResponse/ToolResponse.d.ts +30 -0
- package/dist/cjs/ToolResponse/ToolResponse.js +18 -0
- package/dist/cjs/ToolResponse/ToolResponse.test.d.ts +1 -0
- package/dist/cjs/ToolResponse/ToolResponse.test.js +60 -0
- package/dist/cjs/ToolResponse/index.d.ts +2 -0
- package/dist/cjs/ToolResponse/index.js +23 -0
- package/dist/cjs/index.d.ts +6 -0
- package/dist/cjs/index.js +10 -1
- package/dist/css/main.css +273 -17
- package/dist/css/main.css.map +1 -1
- package/dist/dynamic/DeepThinking/package.json +1 -0
- package/dist/dynamic/FilePreview/package.json +1 -0
- package/dist/dynamic/ToolResponse/package.json +1 -0
- package/dist/esm/Chatbot/Chatbot.js +1 -7
- package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.d.ts +2 -0
- package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.js +2 -2
- package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.d.ts +22 -2
- package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.js +17 -11
- package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.test.js +41 -3
- package/dist/esm/ChatbotHeader/ChatbotHeaderMenu.js +1 -1
- package/dist/esm/ChatbotHeader/ChatbotHeaderMenu.test.js +1 -1
- package/dist/esm/ChatbotHeader/ChatbotHeaderNewChatButton.d.ts +18 -0
- package/dist/esm/ChatbotHeader/ChatbotHeaderNewChatButton.js +22 -0
- package/dist/esm/ChatbotHeader/ChatbotHeaderNewChatButton.test.d.ts +1 -0
- package/dist/esm/ChatbotHeader/ChatbotHeaderNewChatButton.test.js +20 -0
- package/dist/esm/ChatbotHeader/index.d.ts +1 -0
- package/dist/esm/ChatbotHeader/index.js +1 -0
- package/dist/esm/DeepThinking/DeepThinking.d.ts +18 -0
- package/dist/esm/DeepThinking/DeepThinking.js +14 -0
- package/dist/esm/DeepThinking/DeepThinking.test.d.ts +1 -0
- package/dist/esm/DeepThinking/DeepThinking.test.js +43 -0
- package/dist/esm/DeepThinking/index.d.ts +2 -0
- package/dist/esm/DeepThinking/index.js +2 -0
- package/dist/esm/FilePreview/FilePreview.d.ts +26 -0
- package/dist/esm/FilePreview/FilePreview.js +21 -0
- package/dist/esm/FilePreview/FilePreview.test.d.ts +1 -0
- package/dist/esm/FilePreview/FilePreview.test.js +92 -0
- package/dist/esm/FilePreview/index.d.ts +2 -0
- package/dist/esm/FilePreview/index.js +2 -0
- package/dist/esm/Message/CodeBlockMessage/CodeBlockMessage.js +5 -5
- package/dist/esm/Message/LinkMessage/LinkMessage.d.ts +2 -1
- package/dist/esm/Message/LinkMessage/LinkMessage.js +7 -3
- package/dist/esm/Message/ListMessage/ListItemMessage.d.ts +1 -1
- package/dist/esm/Message/ListMessage/ListItemMessage.js +16 -1
- package/dist/esm/Message/Message.d.ts +15 -0
- package/dist/esm/Message/Message.js +129 -32
- package/dist/esm/Message/Message.test.js +71 -0
- package/dist/esm/Message/SuperscriptMessage/SuperscriptMessage.d.ts +3 -0
- package/dist/esm/Message/SuperscriptMessage/SuperscriptMessage.js +3 -0
- package/dist/esm/Message/UserFeedback/UserFeedback.d.ts +15 -1
- package/dist/esm/Message/UserFeedback/UserFeedback.js +4 -4
- package/dist/esm/Message/UserFeedback/UserFeedback.test.js +45 -1
- package/dist/esm/MessageBar/MessageBar.js +19 -4
- package/dist/esm/MessageBox/JumpButton.d.ts +5 -0
- package/dist/esm/MessageBox/JumpButton.js +1 -1
- package/dist/esm/MessageBox/JumpButton.test.js +4 -4
- package/dist/esm/MessageBox/MessageBox.d.ts +9 -0
- package/dist/esm/MessageBox/MessageBox.js +2 -2
- package/dist/esm/MessageBox/MessageBox.test.js +2 -2
- package/dist/esm/SourcesCard/SourcesCard.d.ts +13 -1
- package/dist/esm/SourcesCard/SourcesCard.js +6 -6
- package/dist/esm/SourcesCard/SourcesCard.test.js +50 -1
- package/dist/esm/ToolResponse/ToolResponse.d.ts +30 -0
- package/dist/esm/ToolResponse/ToolResponse.js +14 -0
- package/dist/esm/ToolResponse/ToolResponse.test.d.ts +1 -0
- package/dist/esm/ToolResponse/ToolResponse.test.js +55 -0
- package/dist/esm/ToolResponse/index.d.ts +2 -0
- package/dist/esm/ToolResponse/index.js +2 -0
- package/dist/esm/index.d.ts +6 -0
- package/dist/esm/index.js +6 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +7 -6
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/BotMessage.tsx +101 -3
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/FilePreview.tsx +33 -0
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithDeepThinking.tsx +17 -0
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithFeedback.tsx +111 -85
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithSources.tsx +70 -0
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithToolResponse.tsx +135 -0
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/Messages.md +28 -4
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/UserMessage.tsx +107 -2
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/UserMessageWithExtraContent.tsx +616 -3
- package/patternfly-docs/content/extensions/chatbot/examples/UI/ChatbotConversationEditing.tsx +202 -0
- package/patternfly-docs/content/extensions/chatbot/examples/UI/ChatbotHeaderBasic.tsx +17 -3
- package/patternfly-docs/content/extensions/chatbot/examples/UI/ChatbotHeaderDrawer.tsx +36 -5
- package/patternfly-docs/content/extensions/chatbot/examples/UI/ChatbotHeaderDrawerWithPin.tsx +12 -2
- package/patternfly-docs/content/extensions/chatbot/examples/UI/UI.md +22 -3
- package/patternfly-docs/content/extensions/chatbot/examples/demos/Chatbot.md +1 -1
- package/patternfly-docs/patternfly-docs.config.js +1 -1
- package/src/Chatbot/Chatbot.scss +9 -2
- package/src/Chatbot/Chatbot.tsx +18 -31
- package/src/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.tsx +5 -1
- package/src/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.scss +28 -10
- package/src/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.test.tsx +132 -3
- package/src/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.tsx +80 -33
- package/src/ChatbotHeader/ChatbotHeaderMenu.test.tsx +1 -1
- package/src/ChatbotHeader/ChatbotHeaderMenu.tsx +2 -2
- package/src/ChatbotHeader/ChatbotHeaderNewChatButton.test.tsx +25 -0
- package/src/ChatbotHeader/ChatbotHeaderNewChatButton.tsx +64 -0
- package/src/ChatbotHeader/index.ts +1 -0
- package/src/ChatbotModal/ChatbotModal.scss +1 -1
- package/src/DeepThinking/DeepThinking.scss +24 -0
- package/src/DeepThinking/DeepThinking.test.tsx +61 -0
- package/src/DeepThinking/DeepThinking.tsx +68 -0
- package/src/DeepThinking/index.ts +3 -0
- package/src/FileDetails/__snapshots__/FileDetails.test.tsx.snap +6 -9
- package/src/FileDetailsLabel/__snapshots__/FileDetailsLabel.test.tsx.snap +6 -9
- package/src/FilePreview/FilePreview.scss +22 -0
- package/src/FilePreview/FilePreview.test.tsx +112 -0
- package/src/FilePreview/FilePreview.tsx +58 -0
- package/src/FilePreview/index.ts +3 -0
- package/src/Message/CodeBlockMessage/CodeBlockMessage.scss +2 -1
- package/src/Message/CodeBlockMessage/CodeBlockMessage.tsx +6 -5
- package/src/Message/LinkMessage/LinkMessage.tsx +6 -2
- package/src/Message/ListMessage/ListItemMessage.tsx +5 -1
- package/src/Message/ListMessage/ListMessage.scss +17 -0
- package/src/Message/Message.scss +44 -0
- package/src/Message/Message.test.tsx +90 -0
- package/src/Message/Message.tsx +171 -46
- package/src/Message/SuperscriptMessage/SuperscriptMessage.scss +8 -0
- package/src/Message/SuperscriptMessage/SuperscriptMessage.tsx +13 -0
- package/src/Message/TextMessage/TextMessage.scss +46 -5
- package/src/Message/UserFeedback/UserFeedback.test.tsx +107 -0
- package/src/Message/UserFeedback/UserFeedback.tsx +41 -6
- package/src/MessageBar/MessageBar.tsx +23 -3
- package/src/MessageBox/JumpButton.test.tsx +4 -4
- package/src/MessageBox/JumpButton.tsx +20 -4
- package/src/MessageBox/MessageBox.test.tsx +2 -2
- package/src/MessageBox/MessageBox.tsx +22 -1
- package/src/SourcesCard/SourcesCard.scss +17 -0
- package/src/SourcesCard/SourcesCard.test.tsx +93 -0
- package/src/SourcesCard/SourcesCard.tsx +116 -80
- package/src/ToolResponse/ToolResponse.scss +36 -0
- package/src/ToolResponse/ToolResponse.test.tsx +78 -0
- package/src/ToolResponse/ToolResponse.tsx +95 -0
- package/src/ToolResponse/index.ts +3 -0
- package/src/index.ts +9 -0
- package/src/main.scss +3 -0
package/src/Message/Message.tsx
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// ============================================================================
|
|
4
4
|
import { forwardRef, ReactNode, useEffect, useState } from 'react';
|
|
5
5
|
import type { FunctionComponent, HTMLProps, MouseEvent as ReactMouseEvent, Ref } from 'react';
|
|
6
|
-
import Markdown from 'react-markdown';
|
|
6
|
+
import Markdown, { Options } from 'react-markdown';
|
|
7
7
|
import remarkGfm from 'remark-gfm';
|
|
8
8
|
import {
|
|
9
9
|
AlertProps,
|
|
@@ -49,6 +49,9 @@ import LinkMessage from './LinkMessage/LinkMessage';
|
|
|
49
49
|
import ErrorMessage from './ErrorMessage/ErrorMessage';
|
|
50
50
|
import MessageInput from './MessageInput';
|
|
51
51
|
import { rehypeMoveImagesOutOfParagraphs } from './Plugins/rehypeMoveImagesOutOfParagraphs';
|
|
52
|
+
import ToolResponse, { ToolResponseProps } from '../ToolResponse';
|
|
53
|
+
import DeepThinking, { DeepThinkingProps } from '../DeepThinking';
|
|
54
|
+
import SuperscriptMessage from './SuperscriptMessage/SuperscriptMessage';
|
|
52
55
|
|
|
53
56
|
export interface MessageAttachment {
|
|
54
57
|
/** Name of file attached to the message */
|
|
@@ -161,6 +164,8 @@ export interface MessageProps extends Omit<HTMLProps<HTMLDivElement>, 'role'> {
|
|
|
161
164
|
tableProps?: Required<Pick<TableProps, 'aria-label'>> & TableProps;
|
|
162
165
|
/** Additional rehype plugins passed from the consumer */
|
|
163
166
|
additionalRehypePlugins?: PluggableList;
|
|
167
|
+
/** Additional remark plugins passed from the consumer */
|
|
168
|
+
additionalRemarkPlugins?: PluggableList;
|
|
164
169
|
/** Whether to open links in message in new tab. */
|
|
165
170
|
openLinkInNewTab?: boolean;
|
|
166
171
|
/** Optional inline error message that can be displayed in the message */
|
|
@@ -185,6 +190,16 @@ export interface MessageProps extends Omit<HTMLProps<HTMLDivElement>, 'role'> {
|
|
|
185
190
|
editFormProps?: FormProps;
|
|
186
191
|
/** Sets message to compact styling. */
|
|
187
192
|
isCompact?: boolean;
|
|
193
|
+
/** Disables markdown parsing for message, allowing only text input */
|
|
194
|
+
isMarkdownDisabled?: boolean;
|
|
195
|
+
/** Allows passing additional props down to markdown parser react-markdown, such as allowedElements and disallowedElements. See https://github.com/remarkjs/react-markdown?tab=readme-ov-file#options for options */
|
|
196
|
+
reactMarkdownProps?: Options;
|
|
197
|
+
/** Props for tool response card */
|
|
198
|
+
toolResponse?: ToolResponseProps;
|
|
199
|
+
/** Props for deep thinking card */
|
|
200
|
+
deepThinking?: DeepThinkingProps;
|
|
201
|
+
/** Allows passing additional props down to remark-gfm. See https://github.com/remarkjs/remark-gfm?tab=readme-ov-file#options for options */
|
|
202
|
+
remarkGfmProps?: Options;
|
|
188
203
|
}
|
|
189
204
|
|
|
190
205
|
export const MessageBase: FunctionComponent<MessageProps> = ({
|
|
@@ -213,6 +228,7 @@ export const MessageBase: FunctionComponent<MessageProps> = ({
|
|
|
213
228
|
tableProps,
|
|
214
229
|
openLinkInNewTab = true,
|
|
215
230
|
additionalRehypePlugins = [],
|
|
231
|
+
additionalRemarkPlugins = [],
|
|
216
232
|
linkProps,
|
|
217
233
|
error,
|
|
218
234
|
isEditable,
|
|
@@ -224,6 +240,11 @@ export const MessageBase: FunctionComponent<MessageProps> = ({
|
|
|
224
240
|
inputRef,
|
|
225
241
|
editFormProps,
|
|
226
242
|
isCompact,
|
|
243
|
+
isMarkdownDisabled,
|
|
244
|
+
reactMarkdownProps,
|
|
245
|
+
toolResponse,
|
|
246
|
+
deepThinking,
|
|
247
|
+
remarkGfmProps,
|
|
227
248
|
...props
|
|
228
249
|
}: MessageProps) => {
|
|
229
250
|
const [messageText, setMessageText] = useState(content);
|
|
@@ -250,6 +271,152 @@ export const MessageBase: FunctionComponent<MessageProps> = ({
|
|
|
250
271
|
const date = new Date();
|
|
251
272
|
const dateString = timestamp ?? `${date.toLocaleDateString()} ${date.toLocaleTimeString()}`;
|
|
252
273
|
|
|
274
|
+
const handleMarkdown = () => {
|
|
275
|
+
if (isMarkdownDisabled) {
|
|
276
|
+
return (
|
|
277
|
+
<TextMessage component={ContentVariants.p} {...props}>
|
|
278
|
+
{messageText}
|
|
279
|
+
</TextMessage>
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
return (
|
|
283
|
+
<Markdown
|
|
284
|
+
components={{
|
|
285
|
+
section: (props) => {
|
|
286
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
287
|
+
const { node, ...rest } = props;
|
|
288
|
+
return <section {...rest} className={`pf-chatbot__message-text ${rest?.className}`} />;
|
|
289
|
+
},
|
|
290
|
+
p: (props) => {
|
|
291
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
292
|
+
const { node, ...rest } = props;
|
|
293
|
+
return <TextMessage component={ContentVariants.p} {...rest} />;
|
|
294
|
+
},
|
|
295
|
+
code: ({ children, ...props }) => {
|
|
296
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
297
|
+
const { node, ...codeProps } = props;
|
|
298
|
+
return (
|
|
299
|
+
<CodeBlockMessage {...codeProps} {...codeBlockProps}>
|
|
300
|
+
{children}
|
|
301
|
+
</CodeBlockMessage>
|
|
302
|
+
);
|
|
303
|
+
},
|
|
304
|
+
h1: (props) => {
|
|
305
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
306
|
+
const { node, ...rest } = props;
|
|
307
|
+
return <TextMessage component={ContentVariants.h1} {...rest} />;
|
|
308
|
+
},
|
|
309
|
+
h2: (props) => {
|
|
310
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
311
|
+
const { node, ...rest } = props;
|
|
312
|
+
return <TextMessage component={ContentVariants.h2} {...rest} />;
|
|
313
|
+
},
|
|
314
|
+
h3: (props) => {
|
|
315
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
316
|
+
const { node, ...rest } = props;
|
|
317
|
+
return <TextMessage component={ContentVariants.h3} {...rest} />;
|
|
318
|
+
},
|
|
319
|
+
h4: (props) => {
|
|
320
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
321
|
+
const { node, ...rest } = props;
|
|
322
|
+
return <TextMessage component={ContentVariants.h4} {...rest} />;
|
|
323
|
+
},
|
|
324
|
+
h5: (props) => {
|
|
325
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
326
|
+
const { node, ...rest } = props;
|
|
327
|
+
return <TextMessage component={ContentVariants.h5} {...rest} />;
|
|
328
|
+
},
|
|
329
|
+
h6: (props) => {
|
|
330
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
331
|
+
const { node, ...rest } = props;
|
|
332
|
+
return <TextMessage component={ContentVariants.h6} {...rest} />;
|
|
333
|
+
},
|
|
334
|
+
blockquote: (props) => {
|
|
335
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
336
|
+
const { node, ...rest } = props;
|
|
337
|
+
return <TextMessage component={ContentVariants.blockquote} {...rest} />;
|
|
338
|
+
},
|
|
339
|
+
ul: (props) => {
|
|
340
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
341
|
+
const { node, ...rest } = props;
|
|
342
|
+
return <UnorderedListMessage {...rest} />;
|
|
343
|
+
},
|
|
344
|
+
ol: (props) => {
|
|
345
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
346
|
+
const { node, ...rest } = props;
|
|
347
|
+
return <OrderedListMessage {...rest} />;
|
|
348
|
+
},
|
|
349
|
+
li: (props) => {
|
|
350
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
351
|
+
const { node, ...rest } = props;
|
|
352
|
+
return <ListItemMessage {...rest} />;
|
|
353
|
+
},
|
|
354
|
+
// table requires node attribute for calculating headers for mobile breakpoint
|
|
355
|
+
table: (props) => <TableMessage {...props} {...tableProps} />,
|
|
356
|
+
tbody: (props) => {
|
|
357
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
358
|
+
const { node, ...rest } = props;
|
|
359
|
+
return <TbodyMessage {...rest} />;
|
|
360
|
+
},
|
|
361
|
+
thead: (props) => {
|
|
362
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
363
|
+
const { node, ...rest } = props;
|
|
364
|
+
return <TheadMessage {...rest} />;
|
|
365
|
+
},
|
|
366
|
+
tr: (props) => {
|
|
367
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
368
|
+
const { node, ...rest } = props;
|
|
369
|
+
return <TrMessage {...rest} />;
|
|
370
|
+
},
|
|
371
|
+
td: (props) => {
|
|
372
|
+
// Conflicts with Td type
|
|
373
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
374
|
+
const { node, width, ...rest } = props;
|
|
375
|
+
return <TdMessage {...rest} />;
|
|
376
|
+
},
|
|
377
|
+
th: (props) => {
|
|
378
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
379
|
+
const { node, ...rest } = props;
|
|
380
|
+
return <ThMessage {...rest} />;
|
|
381
|
+
},
|
|
382
|
+
img: (props) => {
|
|
383
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
384
|
+
const { node, ...rest } = props;
|
|
385
|
+
return <ImageMessage {...rest} />;
|
|
386
|
+
},
|
|
387
|
+
a: (props) => {
|
|
388
|
+
// node is just the details of the document structure - not needed
|
|
389
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
390
|
+
const { node, ...rest } = props;
|
|
391
|
+
return (
|
|
392
|
+
// some a types conflict with ButtonProps, but it's ok because we are using an a tag
|
|
393
|
+
// there are too many to handle manually
|
|
394
|
+
<LinkMessage {...(rest as any)} {...linkProps}>
|
|
395
|
+
{props.children}
|
|
396
|
+
</LinkMessage>
|
|
397
|
+
);
|
|
398
|
+
},
|
|
399
|
+
// used for footnotes
|
|
400
|
+
sup: (props) => {
|
|
401
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
402
|
+
const { node, ...rest } = props;
|
|
403
|
+
return <SuperscriptMessage {...rest} />;
|
|
404
|
+
}
|
|
405
|
+
}}
|
|
406
|
+
remarkPlugins={[[remarkGfm, { ...remarkGfmProps }], ...additionalRemarkPlugins]}
|
|
407
|
+
rehypePlugins={rehypePlugins}
|
|
408
|
+
{...reactMarkdownProps}
|
|
409
|
+
remarkRehypeOptions={{
|
|
410
|
+
// removes sr-only class from footnote labels applied by default
|
|
411
|
+
footnoteLabelProperties: { className: [''] },
|
|
412
|
+
...reactMarkdownProps?.remarkRehypeOptions
|
|
413
|
+
}}
|
|
414
|
+
>
|
|
415
|
+
{messageText}
|
|
416
|
+
</Markdown>
|
|
417
|
+
);
|
|
418
|
+
};
|
|
419
|
+
|
|
253
420
|
const renderMessage = () => {
|
|
254
421
|
if (isLoading) {
|
|
255
422
|
return <MessageLoading loadingWord={loadingWord} />;
|
|
@@ -277,51 +444,7 @@ export const MessageBase: FunctionComponent<MessageProps> = ({
|
|
|
277
444
|
return (
|
|
278
445
|
<>
|
|
279
446
|
{beforeMainContent && <>{beforeMainContent}</>}
|
|
280
|
-
{error ? (
|
|
281
|
-
<ErrorMessage {...error} />
|
|
282
|
-
) : (
|
|
283
|
-
<Markdown
|
|
284
|
-
components={{
|
|
285
|
-
p: (props) => <TextMessage component={ContentVariants.p} {...props} />,
|
|
286
|
-
code: ({ children, ...props }) => (
|
|
287
|
-
<CodeBlockMessage {...props} {...codeBlockProps}>
|
|
288
|
-
{children}
|
|
289
|
-
</CodeBlockMessage>
|
|
290
|
-
),
|
|
291
|
-
h1: (props) => <TextMessage component={ContentVariants.h1} {...props} />,
|
|
292
|
-
h2: (props) => <TextMessage component={ContentVariants.h2} {...props} />,
|
|
293
|
-
h3: (props) => <TextMessage component={ContentVariants.h3} {...props} />,
|
|
294
|
-
h4: (props) => <TextMessage component={ContentVariants.h4} {...props} />,
|
|
295
|
-
h5: (props) => <TextMessage component={ContentVariants.h5} {...props} />,
|
|
296
|
-
h6: (props) => <TextMessage component={ContentVariants.h6} {...props} />,
|
|
297
|
-
blockquote: (props) => <TextMessage component={ContentVariants.blockquote} {...props} />,
|
|
298
|
-
ul: (props) => <UnorderedListMessage {...props} />,
|
|
299
|
-
ol: (props) => <OrderedListMessage {...props} />,
|
|
300
|
-
li: (props) => <ListItemMessage {...props} />,
|
|
301
|
-
table: (props) => <TableMessage {...props} {...tableProps} />,
|
|
302
|
-
tbody: (props) => <TbodyMessage {...props} />,
|
|
303
|
-
thead: (props) => <TheadMessage {...props} />,
|
|
304
|
-
tr: (props) => <TrMessage {...props} />,
|
|
305
|
-
td: (props) => {
|
|
306
|
-
// Conflicts with Td type
|
|
307
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
308
|
-
const { width, ...rest } = props;
|
|
309
|
-
return <TdMessage {...rest} />;
|
|
310
|
-
},
|
|
311
|
-
th: (props) => <ThMessage {...props} />,
|
|
312
|
-
img: (props) => <ImageMessage {...props} />,
|
|
313
|
-
a: (props) => (
|
|
314
|
-
<LinkMessage href={props.href} rel={props.rel} target={props.target} {...linkProps}>
|
|
315
|
-
{props.children}
|
|
316
|
-
</LinkMessage>
|
|
317
|
-
)
|
|
318
|
-
}}
|
|
319
|
-
remarkPlugins={[remarkGfm]}
|
|
320
|
-
rehypePlugins={rehypePlugins}
|
|
321
|
-
>
|
|
322
|
-
{messageText}
|
|
323
|
-
</Markdown>
|
|
324
|
-
)}
|
|
447
|
+
{error ? <ErrorMessage {...error} /> : handleMarkdown()}
|
|
325
448
|
</>
|
|
326
449
|
);
|
|
327
450
|
};
|
|
@@ -360,6 +483,8 @@ export const MessageBase: FunctionComponent<MessageProps> = ({
|
|
|
360
483
|
<div className="pf-chatbot__message-and-actions">
|
|
361
484
|
{renderMessage()}
|
|
362
485
|
{afterMainContent && <>{afterMainContent}</>}
|
|
486
|
+
{toolResponse && <ToolResponse {...toolResponse} />}
|
|
487
|
+
{deepThinking && <DeepThinking {...deepThinking} />}
|
|
363
488
|
{!isLoading && sources && <SourcesCard {...sources} isCompact={isCompact} />}
|
|
364
489
|
{quickStarts && quickStarts.quickStart && (
|
|
365
490
|
<QuickStartTile
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// Chatbot Main - Message - Content - Superscript (like for footnotes)
|
|
3
|
+
// ============================================================================
|
|
4
|
+
|
|
5
|
+
import { ExtraProps } from 'react-markdown';
|
|
6
|
+
|
|
7
|
+
const SuperscriptMessage = ({ children }: JSX.IntrinsicElements['sup'] & ExtraProps) => (
|
|
8
|
+
<span className="pf-chatbot__message-superscript">
|
|
9
|
+
<sup>{children}</sup>
|
|
10
|
+
</span>
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
export default SuperscriptMessage;
|
|
@@ -17,9 +17,10 @@
|
|
|
17
17
|
width: fit-content;
|
|
18
18
|
padding: var(--pf-t--global--spacer--sm) 0 var(--pf-t--global--spacer--sm) 0;
|
|
19
19
|
border-radius: var(--pf-t--global--border--radius--small);
|
|
20
|
+
--pf-chatbot-message-text-font-size: var(--pf-t--global--font--size--md);
|
|
20
21
|
|
|
21
22
|
.pf-v6-c-button.pf-m-link {
|
|
22
|
-
font-size: var(--pf-
|
|
23
|
+
font-size: var(--pf-chatbot-message-text-font-size);
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
.pf-v6-c-content,
|
|
@@ -27,15 +28,49 @@
|
|
|
27
28
|
.pf-v6-c-content--blockquote,
|
|
28
29
|
p,
|
|
29
30
|
a {
|
|
30
|
-
--pf-v6-c-content--FontSize: var(--pf-
|
|
31
|
+
--pf-v6-c-content--FontSize: var(--pf-chatbot-message-text-font-size);
|
|
31
32
|
}
|
|
32
33
|
|
|
33
34
|
code {
|
|
34
35
|
background-color: var(--pf-t--global--background--color--tertiary--default);
|
|
35
|
-
font-size: var(--pf-
|
|
36
|
+
font-size: var(--pf-chatbot-message-text-inline-code-font-size);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Hide message text that contains sr-only content
|
|
40
|
+
// https://css-tricks.com/inclusively-hidden/
|
|
41
|
+
&:has(.sr-only) {
|
|
42
|
+
clip: rect(0 0 0 0);
|
|
43
|
+
clip-path: inset(50%);
|
|
44
|
+
height: 1px;
|
|
45
|
+
overflow: hidden;
|
|
46
|
+
position: absolute;
|
|
47
|
+
white-space: nowrap;
|
|
48
|
+
width: 1px;
|
|
36
49
|
}
|
|
37
50
|
}
|
|
38
51
|
|
|
52
|
+
// ============================================================================
|
|
53
|
+
// Footnote spacing styles
|
|
54
|
+
// ============================================================================
|
|
55
|
+
|
|
56
|
+
// Add spacing to paragraphs in multi-paragraph footnotes
|
|
57
|
+
// Only target p tags that are direct children of message-text spans (not inside blockquotes, etc.)
|
|
58
|
+
li[id*='user-content-fn-']:has(> span > .pf-chatbot__message-text + .pf-chatbot__message-text)
|
|
59
|
+
> span
|
|
60
|
+
> .pf-chatbot__message-text
|
|
61
|
+
> p {
|
|
62
|
+
margin-block-end: var(--pf-t--global--spacer--md);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Handle user message footnotes which may have extra span wrappers
|
|
66
|
+
li[id*='user-content-fn-']:has(> span > span > .pf-chatbot__message-text + .pf-chatbot__message-text)
|
|
67
|
+
> span
|
|
68
|
+
> span
|
|
69
|
+
> .pf-chatbot__message-text
|
|
70
|
+
> p {
|
|
71
|
+
margin-block-end: var(--pf-t--global--spacer--md);
|
|
72
|
+
}
|
|
73
|
+
|
|
39
74
|
.pf-chatbot__message--user {
|
|
40
75
|
.pf-chatbot__message-text {
|
|
41
76
|
background-color: var(--pf-t--global--color--brand--default);
|
|
@@ -54,6 +89,11 @@
|
|
|
54
89
|
color: var(--pf-t--global--text--color--on-brand--default);
|
|
55
90
|
}
|
|
56
91
|
}
|
|
92
|
+
|
|
93
|
+
.pf-chatbot__message-text > .pf-chatbot__message-text {
|
|
94
|
+
background-color: initial;
|
|
95
|
+
padding: initial;
|
|
96
|
+
}
|
|
57
97
|
}
|
|
58
98
|
|
|
59
99
|
// ============================================================================
|
|
@@ -62,8 +102,9 @@
|
|
|
62
102
|
.pf-chatbot.pf-m-compact {
|
|
63
103
|
// Need to inline shorter text
|
|
64
104
|
.pf-chatbot__message-text {
|
|
105
|
+
--pf-chatbot-message-text-font-size: var(--pf-t--global--font--size--sm);
|
|
65
106
|
.pf-v6-c-button.pf-m-link {
|
|
66
|
-
font-size: var(--pf-
|
|
107
|
+
font-size: var(--pf-chatbot-message-text-font-size);
|
|
67
108
|
}
|
|
68
109
|
|
|
69
110
|
.pf-v6-c-content,
|
|
@@ -71,7 +112,7 @@
|
|
|
71
112
|
.pf-v6-c-content--blockquote,
|
|
72
113
|
p,
|
|
73
114
|
a {
|
|
74
|
-
--pf-v6-c-content--FontSize: var(--pf-
|
|
115
|
+
--pf-v6-c-content--FontSize: var(--pf-chatbot-message-text-font-size);
|
|
75
116
|
}
|
|
76
117
|
|
|
77
118
|
.pf-v6-c-content--blockquote {
|
|
@@ -245,4 +245,111 @@ describe('UserFeedback', () => {
|
|
|
245
245
|
);
|
|
246
246
|
expect(screen.getByTestId('card')).toHaveClass('pf-m-compact');
|
|
247
247
|
});
|
|
248
|
+
it('should pass buttonProps to submit button', () => {
|
|
249
|
+
render(
|
|
250
|
+
<UserFeedback
|
|
251
|
+
timestamp="12/12/12"
|
|
252
|
+
onClose={jest.fn}
|
|
253
|
+
onSubmit={jest.fn}
|
|
254
|
+
quickResponses={MOCK_RESPONSES}
|
|
255
|
+
submitButtonProps={{ variant: 'secondary', isDisabled: true }}
|
|
256
|
+
/>
|
|
257
|
+
);
|
|
258
|
+
const submitButton = screen.getByRole('button', { name: /Submit/i });
|
|
259
|
+
expect(submitButton).toHaveClass('pf-v6-c-button pf-m-secondary');
|
|
260
|
+
expect(submitButton).toBeDisabled();
|
|
261
|
+
});
|
|
262
|
+
it('should pass cardHeaderProps to card header', () => {
|
|
263
|
+
render(
|
|
264
|
+
<UserFeedback
|
|
265
|
+
timestamp="12/12/12"
|
|
266
|
+
onClose={jest.fn}
|
|
267
|
+
onSubmit={jest.fn}
|
|
268
|
+
quickResponses={MOCK_RESPONSES}
|
|
269
|
+
cardHeaderProps={{ 'data-testid': 'card-header', className: 'custom-header' } as any}
|
|
270
|
+
/>
|
|
271
|
+
);
|
|
272
|
+
const cardHeader = screen.getByTestId('card-header');
|
|
273
|
+
expect(cardHeader).toHaveClass('custom-header');
|
|
274
|
+
});
|
|
275
|
+
it('should pass cardBodyProps to card body', () => {
|
|
276
|
+
render(
|
|
277
|
+
<UserFeedback
|
|
278
|
+
timestamp="12/12/12"
|
|
279
|
+
onClose={jest.fn}
|
|
280
|
+
onSubmit={jest.fn}
|
|
281
|
+
quickResponses={MOCK_RESPONSES}
|
|
282
|
+
cardBodyProps={{ 'data-testid': 'card-body', className: 'custom-body' } as any}
|
|
283
|
+
/>
|
|
284
|
+
);
|
|
285
|
+
const cardBody = screen.getByTestId('card-body');
|
|
286
|
+
expect(cardBody).toHaveClass('custom-body');
|
|
287
|
+
});
|
|
288
|
+
it('should pass headingLevelProps to title heading', () => {
|
|
289
|
+
render(
|
|
290
|
+
<UserFeedback
|
|
291
|
+
timestamp="12/12/12"
|
|
292
|
+
onClose={jest.fn}
|
|
293
|
+
onSubmit={jest.fn}
|
|
294
|
+
quickResponses={MOCK_RESPONSES}
|
|
295
|
+
headingLevelProps={{ className: 'custom-heading', id: 'feedback-title' }}
|
|
296
|
+
/>
|
|
297
|
+
);
|
|
298
|
+
const heading = screen.getByRole('heading', { level: 1, name: /Why did you choose this rating?/i });
|
|
299
|
+
expect(heading).toHaveClass('custom-heading');
|
|
300
|
+
expect(heading).toHaveAttribute('id', 'feedback-title');
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
it('should pass formProps to form', () => {
|
|
304
|
+
render(
|
|
305
|
+
<UserFeedback
|
|
306
|
+
timestamp="12/12/12"
|
|
307
|
+
onClose={jest.fn}
|
|
308
|
+
onSubmit={jest.fn}
|
|
309
|
+
quickResponses={MOCK_RESPONSES}
|
|
310
|
+
formProps={{ 'data-testid': 'feedback-form', className: 'custom-form' } as any}
|
|
311
|
+
/>
|
|
312
|
+
);
|
|
313
|
+
const form = screen.getByTestId('feedback-form');
|
|
314
|
+
expect(form).toHaveClass('custom-form');
|
|
315
|
+
});
|
|
316
|
+
it('should pass textAreaProps to text area when hasTextArea is true', () => {
|
|
317
|
+
render(
|
|
318
|
+
<UserFeedback
|
|
319
|
+
timestamp="12/12/12"
|
|
320
|
+
onClose={jest.fn}
|
|
321
|
+
onSubmit={jest.fn}
|
|
322
|
+
quickResponses={MOCK_RESPONSES}
|
|
323
|
+
hasTextArea
|
|
324
|
+
textAreaProps={{ 'data-testid': 'custom-textarea', rows: 5 } as any}
|
|
325
|
+
/>
|
|
326
|
+
);
|
|
327
|
+
const textArea = screen.getByTestId('custom-textarea');
|
|
328
|
+
expect(textArea).toHaveAttribute('rows', '5');
|
|
329
|
+
expect(textArea).toHaveAttribute('data-testid', 'custom-textarea');
|
|
330
|
+
});
|
|
331
|
+
it('should pass actionGroupProps to action group', () => {
|
|
332
|
+
render(
|
|
333
|
+
<UserFeedback
|
|
334
|
+
timestamp="12/12/12"
|
|
335
|
+
onClose={jest.fn}
|
|
336
|
+
onSubmit={jest.fn}
|
|
337
|
+
quickResponses={MOCK_RESPONSES}
|
|
338
|
+
actionGroupProps={{ 'data-testid': 'action-group', className: 'custom-actions' } as any}
|
|
339
|
+
/>
|
|
340
|
+
);
|
|
341
|
+
const actionGroup = screen.getByTestId('action-group');
|
|
342
|
+
expect(actionGroup).toHaveClass('custom-actions');
|
|
343
|
+
});
|
|
344
|
+
it('should render children', () => {
|
|
345
|
+
render(
|
|
346
|
+
<UserFeedback timestamp="12/12/12" onClose={jest.fn} onSubmit={jest.fn} quickResponses={MOCK_RESPONSES}>
|
|
347
|
+
<div data-testid="custom-content">Custom feedback content</div>
|
|
348
|
+
<p>Additional paragraph</p>
|
|
349
|
+
</UserFeedback>
|
|
350
|
+
);
|
|
351
|
+
expect(screen.getByTestId('custom-content')).toBeInTheDocument();
|
|
352
|
+
expect(screen.getByText('Custom feedback content')).toBeInTheDocument();
|
|
353
|
+
expect(screen.getByText('Additional paragraph')).toBeInTheDocument();
|
|
354
|
+
});
|
|
248
355
|
});
|
|
@@ -8,15 +8,21 @@ import { useState, useRef, useEffect } from 'react';
|
|
|
8
8
|
// Import PatternFly components
|
|
9
9
|
import {
|
|
10
10
|
ActionGroup,
|
|
11
|
+
ActionGroupProps,
|
|
11
12
|
Button,
|
|
13
|
+
ButtonProps,
|
|
12
14
|
Card,
|
|
13
15
|
CardBody,
|
|
16
|
+
CardBodyProps,
|
|
14
17
|
CardHeader,
|
|
18
|
+
CardHeaderProps,
|
|
15
19
|
CardProps,
|
|
16
20
|
Form,
|
|
21
|
+
FormProps,
|
|
17
22
|
LabelGroupProps,
|
|
18
23
|
OUIAProps,
|
|
19
|
-
TextArea
|
|
24
|
+
TextArea,
|
|
25
|
+
TextAreaProps
|
|
20
26
|
} from '@patternfly/react-core';
|
|
21
27
|
import QuickResponse from '../QuickResponse/QuickResponse';
|
|
22
28
|
import CloseButton from './CloseButton';
|
|
@@ -54,6 +60,20 @@ export interface UserFeedbackProps extends Omit<CardProps, 'onSubmit'>, OUIAProp
|
|
|
54
60
|
focusOnLoad?: boolean;
|
|
55
61
|
/** Timestamp passed in by Message for more context in aria announcements */
|
|
56
62
|
timestamp?: string;
|
|
63
|
+
/** Additional props passed to submit button */
|
|
64
|
+
submitButtonProps?: ButtonProps;
|
|
65
|
+
/** Additional props passed to card header */
|
|
66
|
+
cardHeaderProps?: CardHeaderProps;
|
|
67
|
+
/** Additional props passed to card body */
|
|
68
|
+
cardBodyProps?: CardBodyProps;
|
|
69
|
+
/** Additional props passed to title heading */
|
|
70
|
+
headingLevelProps?: React.HTMLAttributes<HTMLHeadingElement>;
|
|
71
|
+
/** Additional props passed to form */
|
|
72
|
+
formProps?: FormProps;
|
|
73
|
+
/** Additional props passed to text area */
|
|
74
|
+
textAreaProps?: TextAreaProps;
|
|
75
|
+
/** Additional props passed to action group */
|
|
76
|
+
actionGroupProps?: ActionGroupProps;
|
|
57
77
|
}
|
|
58
78
|
|
|
59
79
|
const UserFeedback: FunctionComponent<UserFeedbackProps> = ({
|
|
@@ -74,6 +94,14 @@ const UserFeedback: FunctionComponent<UserFeedbackProps> = ({
|
|
|
74
94
|
headingLevel: HeadingLevel = 'h1',
|
|
75
95
|
focusOnLoad = true,
|
|
76
96
|
isCompact,
|
|
97
|
+
children,
|
|
98
|
+
cardHeaderProps,
|
|
99
|
+
cardBodyProps,
|
|
100
|
+
headingLevelProps,
|
|
101
|
+
formProps,
|
|
102
|
+
textAreaProps,
|
|
103
|
+
actionGroupProps,
|
|
104
|
+
submitButtonProps,
|
|
77
105
|
...props
|
|
78
106
|
}: UserFeedbackProps) => {
|
|
79
107
|
const [selectedResponse, setSelectedResponse] = useState<string>();
|
|
@@ -94,11 +122,14 @@ const UserFeedback: FunctionComponent<UserFeedbackProps> = ({
|
|
|
94
122
|
actions={{
|
|
95
123
|
actions: <CloseButton onClose={onClose} ariaLabel={closeButtonAriaLabel} />
|
|
96
124
|
}}
|
|
125
|
+
{...cardHeaderProps}
|
|
97
126
|
>
|
|
98
|
-
<HeadingLevel className="pf-chatbot__feedback-card-title"
|
|
127
|
+
<HeadingLevel className="pf-chatbot__feedback-card-title" {...headingLevelProps}>
|
|
128
|
+
{title}
|
|
129
|
+
</HeadingLevel>
|
|
99
130
|
</CardHeader>
|
|
100
|
-
<CardBody>
|
|
101
|
-
<Form className={`pf-chatbot__feedback-card-form ${isCompact ? 'pf-m-compact' : ''}`}>
|
|
131
|
+
<CardBody {...cardBodyProps}>
|
|
132
|
+
<Form className={`pf-chatbot__feedback-card-form ${isCompact ? 'pf-m-compact' : ''}`} {...formProps}>
|
|
102
133
|
{quickResponses && (
|
|
103
134
|
<QuickResponse
|
|
104
135
|
quickResponses={quickResponses}
|
|
@@ -117,10 +148,14 @@ const UserFeedback: FunctionComponent<UserFeedbackProps> = ({
|
|
|
117
148
|
placeholder={textAreaPlaceholder}
|
|
118
149
|
aria-label={textAreaAriaLabel}
|
|
119
150
|
resizeOrientation="vertical"
|
|
151
|
+
{...textAreaProps}
|
|
120
152
|
/>
|
|
121
153
|
)}
|
|
122
|
-
|
|
123
|
-
|
|
154
|
+
{children}
|
|
155
|
+
<ActionGroup {...actionGroupProps}>
|
|
156
|
+
<Button onClick={() => onSubmit(selectedResponse, value)} {...submitButtonProps}>
|
|
157
|
+
{submitWord}
|
|
158
|
+
</Button>
|
|
124
159
|
</ActionGroup>
|
|
125
160
|
</Form>
|
|
126
161
|
</CardBody>
|
|
@@ -141,6 +141,7 @@ export const MessageBarBase: FunctionComponent<MessageBarProps> = ({
|
|
|
141
141
|
const [message, setMessage] = useState<string | number>(value ?? '');
|
|
142
142
|
const [isListeningMessage, setIsListeningMessage] = useState<boolean>(false);
|
|
143
143
|
const [hasSentMessage, setHasSentMessage] = useState(false);
|
|
144
|
+
const [isComposing, setIsComposing] = useState(false);
|
|
144
145
|
const inputRef = useRef<HTMLTextAreaElement>(null);
|
|
145
146
|
const textareaRef = (innerRef as React.RefObject<HTMLTextAreaElement>) ?? inputRef;
|
|
146
147
|
const attachButtonRef = useRef<HTMLButtonElement>(null);
|
|
@@ -285,21 +286,38 @@ export const MessageBarBase: FunctionComponent<MessageBarProps> = ({
|
|
|
285
286
|
|
|
286
287
|
const handleKeyDown = useCallback(
|
|
287
288
|
(event: ReactKeyboardEvent) => {
|
|
288
|
-
|
|
289
|
+
// Japanese and other languages may use IME for character input.
|
|
290
|
+
// In these cases, enter is used to select the final input, so we need to check for composition end instead.
|
|
291
|
+
// See more info at https://www.stum.de/2016/06/24/handling-ime-events-in-javascript/
|
|
292
|
+
// Chrome, Edge, and Firefox seem to work well with just the compose event.
|
|
293
|
+
// Safari is a little bit special. We need to handle 229 as well in this case.
|
|
294
|
+
const nativeEvent = event.nativeEvent as KeyboardEvent;
|
|
295
|
+
const isCompositionKey = nativeEvent.which === 229;
|
|
296
|
+
const isCurrentlyComposing = isComposing || isCompositionKey;
|
|
297
|
+
|
|
298
|
+
if (event.key === 'Enter' && !isCurrentlyComposing && !event.shiftKey) {
|
|
289
299
|
event.preventDefault();
|
|
290
300
|
if (!isSendButtonDisabled && !hasStopButton) {
|
|
291
301
|
handleSend(message);
|
|
292
302
|
}
|
|
293
303
|
}
|
|
294
|
-
if (event.key === 'Enter' && event.shiftKey) {
|
|
304
|
+
if (event.key === 'Enter' && !isCurrentlyComposing && event.shiftKey) {
|
|
295
305
|
if (textareaRef.current) {
|
|
296
306
|
handleNewLine(textareaRef.current);
|
|
297
307
|
}
|
|
298
308
|
}
|
|
299
309
|
},
|
|
300
|
-
[isSendButtonDisabled, hasStopButton, handleSend, message]
|
|
310
|
+
[isSendButtonDisabled, hasStopButton, handleSend, message, isComposing]
|
|
301
311
|
);
|
|
302
312
|
|
|
313
|
+
const handleCompositionStart = useCallback(() => {
|
|
314
|
+
setIsComposing(true);
|
|
315
|
+
}, []);
|
|
316
|
+
|
|
317
|
+
const handleCompositionEnd = useCallback(() => {
|
|
318
|
+
setIsComposing(false);
|
|
319
|
+
}, []);
|
|
320
|
+
|
|
303
321
|
const handleAttachMenuToggle = () => {
|
|
304
322
|
attachMenuProps?.setIsAttachMenuOpen && attachMenuProps?.setIsAttachMenuOpen(!attachMenuProps?.isAttachMenuOpen);
|
|
305
323
|
attachMenuProps?.onAttachMenuToggleClick();
|
|
@@ -402,6 +420,8 @@ export const MessageBarBase: FunctionComponent<MessageBarProps> = ({
|
|
|
402
420
|
placeholder={isListeningMessage ? listeningText : placeholder}
|
|
403
421
|
ref={textareaRef}
|
|
404
422
|
onKeyDown={handleKeyDown}
|
|
423
|
+
onCompositionStart={handleCompositionStart}
|
|
424
|
+
onCompositionEnd={handleCompositionEnd}
|
|
405
425
|
{...props}
|
|
406
426
|
/>
|
|
407
427
|
</div>
|