@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
|
@@ -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 ?
|
|
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 = ``;
|
|
140
153
|
const INLINE_IMAGE = `inline text `;
|
|
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
|
});
|
|
@@ -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' : ''}
|
|
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
|
-
|
|
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": `
|
|
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: /
|
|
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: /
|
|
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: /
|
|
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: /
|
|
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 */
|