@patternfly/chatbot 6.5.0-prerelease.3 → 6.5.0-prerelease.5
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/AttachMenu/AttachMenu.d.ts +7 -1
- package/dist/cjs/AttachMenu/AttachMenu.js +2 -2
- package/dist/cjs/ChatbotFooter/ChatbotFooter.d.ts +5 -2
- package/dist/cjs/ChatbotFooter/ChatbotFooter.js +2 -2
- package/dist/cjs/ChatbotFooter/ChatbotFooter.test.js +5 -1
- package/dist/cjs/FileDetailsLabel/FileDetailsLabel.d.ts +2 -1
- package/dist/cjs/Message/CodeBlockMessage/CodeBlockMessage.d.ts +3 -1
- package/dist/cjs/Message/CodeBlockMessage/CodeBlockMessage.js +2 -2
- package/dist/cjs/Message/Message.d.ts +2 -0
- package/dist/cjs/Message/Message.js +6 -6
- package/dist/cjs/Message/Message.test.js +32 -0
- package/dist/cjs/Message/MessageLoading.d.ts +2 -1
- package/dist/cjs/Message/MessageLoading.js +1 -1
- package/dist/cjs/Message/TableMessage/TableMessage.d.ts +4 -1
- package/dist/cjs/Message/TableMessage/TableMessage.js +2 -2
- package/dist/cjs/Message/TextMessage/TextMessage.d.ts +4 -1
- package/dist/cjs/Message/TextMessage/TextMessage.js +2 -2
- package/dist/cjs/MessageBar/MessageBar.d.ts +9 -1
- package/dist/cjs/MessageBar/MessageBar.js +3 -3
- package/dist/cjs/MessageBar/MessageBar.test.js +37 -0
- package/dist/css/main.css +27 -0
- package/dist/css/main.css.map +1 -1
- package/dist/esm/AttachMenu/AttachMenu.d.ts +7 -1
- package/dist/esm/AttachMenu/AttachMenu.js +2 -2
- package/dist/esm/ChatbotFooter/ChatbotFooter.d.ts +5 -2
- package/dist/esm/ChatbotFooter/ChatbotFooter.js +2 -2
- package/dist/esm/ChatbotFooter/ChatbotFooter.test.js +5 -1
- package/dist/esm/FileDetailsLabel/FileDetailsLabel.d.ts +2 -1
- package/dist/esm/Message/CodeBlockMessage/CodeBlockMessage.d.ts +3 -1
- package/dist/esm/Message/CodeBlockMessage/CodeBlockMessage.js +2 -2
- package/dist/esm/Message/Message.d.ts +2 -0
- package/dist/esm/Message/Message.js +6 -6
- package/dist/esm/Message/Message.test.js +32 -0
- package/dist/esm/Message/MessageLoading.d.ts +2 -1
- package/dist/esm/Message/MessageLoading.js +1 -1
- package/dist/esm/Message/TableMessage/TableMessage.d.ts +4 -1
- package/dist/esm/Message/TableMessage/TableMessage.js +2 -2
- package/dist/esm/Message/TextMessage/TextMessage.d.ts +4 -1
- package/dist/esm/Message/TextMessage/TextMessage.js +2 -2
- package/dist/esm/MessageBar/MessageBar.d.ts +9 -1
- package/dist/esm/MessageBar/MessageBar.js +3 -3
- package/dist/esm/MessageBar/MessageBar.test.js +37 -0
- package/package.json +1 -1
- package/patternfly-docs/content/extensions/chatbot/examples/demos/Chatbot.md +12 -4
- package/patternfly-docs/content/extensions/chatbot/examples/demos/WhiteEmbeddedChatbot.tsx +451 -0
- package/src/AttachMenu/AttachMenu.tsx +16 -3
- package/src/ChatbotFooter/ChatbotFooter.scss +4 -0
- package/src/ChatbotFooter/ChatbotFooter.test.tsx +10 -1
- package/src/ChatbotFooter/ChatbotFooter.tsx +10 -3
- package/src/FileDetailsLabel/FileDetailsLabel.tsx +2 -2
- package/src/Message/CodeBlockMessage/CodeBlockMessage.scss +4 -0
- package/src/Message/CodeBlockMessage/CodeBlockMessage.tsx +4 -1
- package/src/Message/Message.test.tsx +37 -0
- package/src/Message/Message.tsx +8 -4
- package/src/Message/MessageLoading.scss +4 -0
- package/src/Message/MessageLoading.tsx +2 -2
- package/src/Message/TableMessage/TableMessage.scss +4 -0
- package/src/Message/TableMessage/TableMessage.tsx +6 -2
- package/src/Message/TextMessage/TextMessage.scss +6 -0
- package/src/Message/TextMessage/TextMessage.tsx +11 -2
- package/src/MessageBar/MessageBar.scss +4 -0
- package/src/MessageBar/MessageBar.test.tsx +62 -1
- package/src/MessageBar/MessageBar.tsx +24 -2
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import type { HTMLProps, FunctionComponent } from 'react';
|
|
2
2
|
export interface ChatbotFooterProps extends HTMLProps<HTMLDivElement> {
|
|
3
|
-
/** Children for the
|
|
3
|
+
/** Children for the footer - supports MessageBar and FootNote components*/
|
|
4
4
|
children?: React.ReactNode;
|
|
5
|
-
/** Custom classname for the
|
|
5
|
+
/** Custom classname for the footer component */
|
|
6
6
|
className?: string;
|
|
7
|
+
/** Sets footer to compact styling. */
|
|
7
8
|
isCompact?: boolean;
|
|
9
|
+
/** Sets background color to primary */
|
|
10
|
+
isPrimary?: boolean;
|
|
8
11
|
}
|
|
9
12
|
export declare const ChatbotFooter: FunctionComponent<ChatbotFooterProps>;
|
|
10
13
|
export default ChatbotFooter;
|
|
@@ -12,7 +12,7 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
12
12
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
13
13
|
import { Divider } from '@patternfly/react-core';
|
|
14
14
|
export const ChatbotFooter = (_a) => {
|
|
15
|
-
var { children, className, isCompact } = _a, props = __rest(_a, ["children", "className", "isCompact"]);
|
|
16
|
-
return (_jsxs("div", Object.assign({ className: `pf-chatbot__footer ${isCompact ? 'pf-m-compact' : ''} ${className !== null && className !== void 0 ? className : ''}` }, props, { children: [_jsx(Divider, {}), _jsx("div", { className: "pf-chatbot__footer-container", children: children })] })));
|
|
15
|
+
var { children, className, isCompact, isPrimary } = _a, props = __rest(_a, ["children", "className", "isCompact", "isPrimary"]);
|
|
16
|
+
return (_jsxs("div", Object.assign({ className: `pf-chatbot__footer ${isCompact ? 'pf-m-compact' : ''} ${isPrimary ? 'pf-m-primary' : ''} ${className !== null && className !== void 0 ? className : ''}` }, props, { children: [_jsx(Divider, {}), _jsx("div", { className: "pf-chatbot__footer-container", children: children })] })));
|
|
17
17
|
};
|
|
18
18
|
export default ChatbotFooter;
|
|
@@ -12,7 +12,11 @@ describe('ChatbotFooter', () => {
|
|
|
12
12
|
expect(container.querySelector('.custom-class')).toBeTruthy();
|
|
13
13
|
});
|
|
14
14
|
it('should handle isCompact', () => {
|
|
15
|
-
render(_jsx(ChatbotFooter, {
|
|
15
|
+
render(_jsx(ChatbotFooter, { isCompact: true, "data-testid": "footer", children: "Chatbot Content" }));
|
|
16
16
|
expect(screen.getByTestId('footer')).toHaveClass('pf-m-compact');
|
|
17
17
|
});
|
|
18
|
+
it('should handle isPrimary', () => {
|
|
19
|
+
render(_jsx(ChatbotFooter, { isPrimary: true, "data-testid": "footer", children: "Chatbot Content" }));
|
|
20
|
+
expect(screen.getByTestId('footer')).toHaveClass('pf-m-primary');
|
|
21
|
+
});
|
|
18
22
|
});
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { PropsWithChildren } from 'react';
|
|
2
|
-
|
|
2
|
+
import { LabelProps } from '@patternfly/react-core';
|
|
3
|
+
export interface FileDetailsLabelProps extends Omit<LabelProps, 'onClose' | 'onClick'> {
|
|
3
4
|
/** Name of file, including extension */
|
|
4
5
|
fileName: string;
|
|
5
6
|
/** Unique id of file */
|
|
@@ -18,6 +18,8 @@ export interface CodeBlockMessageProps {
|
|
|
18
18
|
collapsedText?: string;
|
|
19
19
|
/** Custom actions added to header of code block, after any default actions such as the "copy" action. */
|
|
20
20
|
customActions?: React.ReactNode;
|
|
21
|
+
/** Sets background colors to be appropriate on primary chatbot background */
|
|
22
|
+
isPrimary?: boolean;
|
|
21
23
|
}
|
|
22
|
-
declare const CodeBlockMessage: ({ children, className, "aria-label": ariaLabel, isExpandable, expandableSectionProps, expandableSectionToggleProps, expandedText, collapsedText, customActions, ...props }: CodeBlockMessageProps) => import("react/jsx-runtime").JSX.Element;
|
|
24
|
+
declare const CodeBlockMessage: ({ children, className, "aria-label": ariaLabel, isExpandable, expandableSectionProps, expandableSectionToggleProps, expandedText, collapsedText, customActions, isPrimary, ...props }: CodeBlockMessageProps) => import("react/jsx-runtime").JSX.Element;
|
|
23
25
|
export default CodeBlockMessage;
|
|
@@ -22,7 +22,7 @@ const DEFAULT_EXPANDED_TEXT = 'Show less';
|
|
|
22
22
|
const DEFAULT_COLLAPSED_TEXT = 'Show more';
|
|
23
23
|
const CodeBlockMessage = (_a) => {
|
|
24
24
|
var _b;
|
|
25
|
-
var { children, className, 'aria-label': ariaLabel, isExpandable = false, expandableSectionProps, expandableSectionToggleProps, expandedText = DEFAULT_EXPANDED_TEXT, collapsedText = DEFAULT_COLLAPSED_TEXT, customActions } = _a, props = __rest(_a, ["children", "className", 'aria-label', "isExpandable", "expandableSectionProps", "expandableSectionToggleProps", "expandedText", "collapsedText", "customActions"]);
|
|
25
|
+
var { children, className, 'aria-label': ariaLabel, isExpandable = false, expandableSectionProps, expandableSectionToggleProps, expandedText = DEFAULT_EXPANDED_TEXT, collapsedText = DEFAULT_COLLAPSED_TEXT, customActions, isPrimary } = _a, props = __rest(_a, ["children", "className", 'aria-label', "isExpandable", "expandableSectionProps", "expandableSectionToggleProps", "expandedText", "collapsedText", "customActions", "isPrimary"]);
|
|
26
26
|
const [copied, setCopied] = useState(false);
|
|
27
27
|
const [isExpanded, setIsExpanded] = useState(false);
|
|
28
28
|
const buttonRef = useRef();
|
|
@@ -59,7 +59,7 @@ const CodeBlockMessage = (_a) => {
|
|
|
59
59
|
}
|
|
60
60
|
});
|
|
61
61
|
if (!String(children).includes('\n')) {
|
|
62
|
-
return (_jsx("code", Object.assign({}, props, { className:
|
|
62
|
+
return (_jsx("code", Object.assign({}, props, { className: `pf-chatbot__message-inline-code ${isPrimary ? 'pf-m-primary' : ''}`, children: children })));
|
|
63
63
|
}
|
|
64
64
|
// Setup code block header
|
|
65
65
|
const actions = (_jsxs(_Fragment, { children: [_jsxs(CodeBlockAction, { className: "pf-chatbot__message-code-block-default-action", children: [language && _jsx("div", { className: "pf-chatbot__message-code-block-language", children: language }), _jsx(Button, { ref: buttonRef, "aria-label": ariaLabel !== null && ariaLabel !== void 0 ? ariaLabel : 'Copy code', variant: "plain", className: "pf-chatbot__button--copy", onClick: (event) => handleCopy(event, children), children: copied ? _jsx(CheckIcon, {}) : _jsx(CopyIcon, {}) }), _jsx(Tooltip, { id: tooltipID, content: "Copy", position: "top", triggerRef: buttonRef })] }), customActions] }));
|
|
@@ -145,6 +145,8 @@ export interface MessageProps extends Omit<HTMLProps<HTMLDivElement>, 'role'> {
|
|
|
145
145
|
toolCall?: ToolCallProps;
|
|
146
146
|
/** Whether user messages default to stripping out images in markdown */
|
|
147
147
|
hasNoImagesInUserMessages?: boolean;
|
|
148
|
+
/** Sets background colors to be appropriate on primary chatbot background */
|
|
149
|
+
isPrimary?: boolean;
|
|
148
150
|
}
|
|
149
151
|
export declare const MessageBase: FunctionComponent<MessageProps>;
|
|
150
152
|
declare const Message: import("react").ForwardRefExoticComponent<Omit<MessageProps, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
|
|
@@ -52,7 +52,7 @@ import DeepThinking from '../DeepThinking';
|
|
|
52
52
|
import SuperscriptMessage from './SuperscriptMessage/SuperscriptMessage';
|
|
53
53
|
import ToolCall from '../ToolCall';
|
|
54
54
|
export const MessageBase = (_a) => {
|
|
55
|
-
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, toolCall, hasNoImagesInUserMessages = true } = _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", "toolCall", "hasNoImagesInUserMessages"]);
|
|
55
|
+
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, toolCall, hasNoImagesInUserMessages = true, isPrimary } = _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", "toolCall", "hasNoImagesInUserMessages", "isPrimary"]);
|
|
56
56
|
const [messageText, setMessageText] = useState(content);
|
|
57
57
|
useEffect(() => {
|
|
58
58
|
setMessageText(content);
|
|
@@ -91,13 +91,13 @@ export const MessageBase = (_a) => {
|
|
|
91
91
|
p: (props) => {
|
|
92
92
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
93
93
|
const { node } = props, rest = __rest(props, ["node"]);
|
|
94
|
-
return _jsx(TextMessage, Object.assign({ component: ContentVariants.p }, rest));
|
|
94
|
+
return _jsx(TextMessage, Object.assign({ component: ContentVariants.p }, rest, { isPrimary: isPrimary }));
|
|
95
95
|
},
|
|
96
96
|
code: (_a) => {
|
|
97
97
|
var { children } = _a, props = __rest(_a, ["children"]);
|
|
98
98
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
99
99
|
const { node } = props, codeProps = __rest(props, ["node"]);
|
|
100
|
-
return (_jsx(CodeBlockMessage, Object.assign({}, codeProps, codeBlockProps, { children: children })));
|
|
100
|
+
return (_jsx(CodeBlockMessage, Object.assign({}, codeProps, codeBlockProps, { isPrimary: isPrimary, children: children })));
|
|
101
101
|
},
|
|
102
102
|
h1: (props) => {
|
|
103
103
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
@@ -150,7 +150,7 @@ export const MessageBase = (_a) => {
|
|
|
150
150
|
return _jsx(ListItemMessage, Object.assign({}, rest));
|
|
151
151
|
},
|
|
152
152
|
// table requires node attribute for calculating headers for mobile breakpoint
|
|
153
|
-
table: (props) => _jsx(TableMessage, Object.assign({}, props, tableProps)),
|
|
153
|
+
table: (props) => _jsx(TableMessage, Object.assign({}, props, tableProps, { isPrimary: isPrimary })),
|
|
154
154
|
tbody: (props) => {
|
|
155
155
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
156
156
|
const { node } = props, rest = __rest(props, ["node"]);
|
|
@@ -203,7 +203,7 @@ export const MessageBase = (_a) => {
|
|
|
203
203
|
};
|
|
204
204
|
const renderMessage = () => {
|
|
205
205
|
if (isLoading) {
|
|
206
|
-
return _jsx(MessageLoading, { loadingWord: loadingWord });
|
|
206
|
+
return _jsx(MessageLoading, { loadingWord: loadingWord, isPrimary: isPrimary });
|
|
207
207
|
}
|
|
208
208
|
if (isEditable) {
|
|
209
209
|
return (_jsxs(_Fragment, { children: [beforeMainContent && _jsx(_Fragment, { children: beforeMainContent }), _jsx(MessageInput, Object.assign({ content: messageText, editPlaceholder: editPlaceholder, updateWord: updateWord, cancelWord: cancelWord, onEditUpdate: (event, value) => {
|
|
@@ -215,7 +215,7 @@ export const MessageBase = (_a) => {
|
|
|
215
215
|
};
|
|
216
216
|
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)), toolCall && _jsx(ToolCall, Object.assign({}, toolCall)), !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) => {
|
|
217
217
|
var _a;
|
|
218
|
-
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));
|
|
218
|
+
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, variant: isPrimary ? 'outline' : undefined }) }, (_a = attachment.id) !== null && _a !== void 0 ? _a : attachment.name));
|
|
219
219
|
}) })), !isLoading && endContent && _jsx(_Fragment, { children: endContent })] })] })] })));
|
|
220
220
|
};
|
|
221
221
|
const Message = forwardRef((props, ref) => (_jsx(MessageBase, Object.assign({ innerRef: ref }, props))));
|
|
@@ -826,4 +826,36 @@ describe('Message', () => {
|
|
|
826
826
|
expect(screen.getByText('Thought for 3 seconds')).toBeTruthy();
|
|
827
827
|
expect(screen.getByText("Here's why I said this.")).toBeTruthy();
|
|
828
828
|
});
|
|
829
|
+
it('should handle isPrimary correctly for inline code when it is true', () => {
|
|
830
|
+
const { container } = render(_jsx(Message, { avatar: "./img", role: "user", name: "User", content: INLINE_CODE, isPrimary: true }));
|
|
831
|
+
expect(container.querySelector('.pf-m-primary')).toBeTruthy();
|
|
832
|
+
});
|
|
833
|
+
it('should handle isPrimary correctly for inline code when it is false', () => {
|
|
834
|
+
const { container } = render(_jsx(Message, { avatar: "./img", role: "user", name: "User", content: INLINE_CODE }));
|
|
835
|
+
expect(container.querySelector('.pf-m-primary')).toBeFalsy();
|
|
836
|
+
});
|
|
837
|
+
it('should handle isPrimary correctly for table when it is true', () => {
|
|
838
|
+
const { container } = render(_jsx(Message, { avatar: "./img", role: "user", name: "User", content: TABLE, isPrimary: true }));
|
|
839
|
+
expect(container.querySelector('.pf-m-primary')).toBeTruthy();
|
|
840
|
+
});
|
|
841
|
+
it('should handle isPrimary correctly for table when it is false', () => {
|
|
842
|
+
const { container } = render(_jsx(Message, { avatar: "./img", role: "user", name: "User", content: TABLE }));
|
|
843
|
+
expect(container.querySelector('.pf-m-primary')).toBeFalsy();
|
|
844
|
+
});
|
|
845
|
+
it('should handle isPrimary correctly for loading when it is true', () => {
|
|
846
|
+
const { container } = render(_jsx(Message, { avatar: "./img", role: "user", name: "User", content: "", isPrimary: true, isLoading: true }));
|
|
847
|
+
expect(container.querySelector('.pf-m-primary')).toBeTruthy();
|
|
848
|
+
});
|
|
849
|
+
it('should handle isPrimary correctly for loading when it is false', () => {
|
|
850
|
+
const { container } = render(_jsx(Message, { avatar: "./img", role: "user", name: "User", content: "", isLoading: true }));
|
|
851
|
+
expect(container.querySelector('.pf-m-primary')).toBeFalsy();
|
|
852
|
+
});
|
|
853
|
+
it('should handle isPrimary correctly for attachments when it is true', () => {
|
|
854
|
+
const { container } = render(_jsx(Message, { avatar: "./img", role: "user", name: "User", content: "", isPrimary: true, attachments: [{ name: 'testAttachment' }] }));
|
|
855
|
+
expect(container.querySelector('.pf-m-outline')).toBeTruthy();
|
|
856
|
+
});
|
|
857
|
+
it('should handle isPrimary correctly for attachments when it is false', () => {
|
|
858
|
+
const { container } = render(_jsx(Message, { avatar: "./img", role: "user", name: "User", content: "", attachments: [{ name: 'testAttachment' }] }));
|
|
859
|
+
expect(container.querySelector('.pf-m-outline')).toBeFalsy();
|
|
860
|
+
});
|
|
829
861
|
});
|
|
@@ -2,5 +2,5 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
2
2
|
// ============================================================================
|
|
3
3
|
// Chatbot Main - Message - Processing
|
|
4
4
|
// ============================================================================
|
|
5
|
-
const MessageLoading = ({ loadingWord }) => (_jsx("div", { className:
|
|
5
|
+
const MessageLoading = ({ loadingWord, isPrimary }) => (_jsx("div", { className: `pf-chatbot__message-loading ${isPrimary ? 'pf-m-primary' : ''}`, children: _jsx("span", { className: "pf-chatbot__message-loading-dots", children: _jsx("span", { className: "pf-v6-screen-reader", children: loadingWord }) }) }));
|
|
6
6
|
export default MessageLoading;
|
|
@@ -15,5 +15,8 @@ export interface TableNode {
|
|
|
15
15
|
tagName: string;
|
|
16
16
|
type: string;
|
|
17
17
|
}
|
|
18
|
-
|
|
18
|
+
export interface TableMessageProps {
|
|
19
|
+
isPrimary?: boolean;
|
|
20
|
+
}
|
|
21
|
+
declare const TableMessage: ({ children, isPrimary, ...props }: Omit<TableProps, "ref"> & ExtraProps & TableMessageProps) => import("react/jsx-runtime").JSX.Element;
|
|
19
22
|
export default TableMessage;
|
|
@@ -17,7 +17,7 @@ import { Children, cloneElement } from 'react';
|
|
|
17
17
|
import { Table } from '@patternfly/react-table';
|
|
18
18
|
const TableMessage = (_a) => {
|
|
19
19
|
var _b;
|
|
20
|
-
var { children } = _a, props = __rest(_a, ["children"]);
|
|
20
|
+
var { children, isPrimary } = _a, props = __rest(_a, ["children", "isPrimary"]);
|
|
21
21
|
const { className } = props, rest = __rest(props, ["className"]);
|
|
22
22
|
// This allows us to parse the nested data we get back from the 3rd party Markdown parser
|
|
23
23
|
// Open to feedback here if there is a better way to do this
|
|
@@ -58,6 +58,6 @@ const TableMessage = (_a) => {
|
|
|
58
58
|
}
|
|
59
59
|
return (
|
|
60
60
|
// gridBreakPoint is so we show mobile-styled-PF table
|
|
61
|
-
_jsx(Table, Object.assign({ "aria-label": props['aria-label'], gridBreakPoint: "grid", className: `pf-chatbot__message-table ${className ? className : ''}` }, rest, { children: modifyChildren(children) })));
|
|
61
|
+
_jsx(Table, Object.assign({ "aria-label": props['aria-label'], gridBreakPoint: "grid", className: `pf-chatbot__message-table ${isPrimary ? 'pf-m-primary' : ''} ${className ? className : ''}` }, rest, { children: modifyChildren(children) })));
|
|
62
62
|
};
|
|
63
63
|
export default TableMessage;
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { ExtraProps } from 'react-markdown';
|
|
2
2
|
import { ContentProps } from '@patternfly/react-core';
|
|
3
|
-
|
|
3
|
+
export interface TextMessageProps {
|
|
4
|
+
isPrimary?: boolean;
|
|
5
|
+
}
|
|
6
|
+
declare const TextMessage: ({ component, children, isPrimary, ...props }: Omit<ContentProps, "ref"> & ExtraProps & TextMessageProps) => import("react/jsx-runtime").JSX.Element;
|
|
4
7
|
export default TextMessage;
|
|
@@ -12,7 +12,7 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
12
12
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
13
13
|
import { Content } from '@patternfly/react-core';
|
|
14
14
|
const TextMessage = (_a) => {
|
|
15
|
-
var { component, children } = _a, props = __rest(_a, ["component", "children"]);
|
|
16
|
-
return (_jsx("span", { className:
|
|
15
|
+
var { component, children, isPrimary } = _a, props = __rest(_a, ["component", "children", "isPrimary"]);
|
|
16
|
+
return (_jsx("span", { className: `pf-chatbot__message-text ${isPrimary ? 'pf-m-primary' : ''}`, children: _jsx(Content, Object.assign({ component: component }, props, { children: children })) }));
|
|
17
17
|
};
|
|
18
18
|
export default TextMessage;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { FunctionComponent } from 'react';
|
|
2
2
|
import { Accept, DropEvent, DropzoneOptions, FileError, FileRejection } from 'react-dropzone';
|
|
3
|
-
import { ButtonProps, TextAreaProps, TooltipProps } from '@patternfly/react-core';
|
|
3
|
+
import { ButtonProps, MenuSearchInputProps, MenuSearchProps, SearchInputProps, TextAreaProps, TooltipProps } from '@patternfly/react-core';
|
|
4
4
|
import { ChatbotDisplayMode } from '../Chatbot';
|
|
5
5
|
export interface MessageBarWithAttachMenuProps {
|
|
6
6
|
/** Flag to enable whether attach menu is open */
|
|
@@ -21,6 +21,12 @@ export interface MessageBarWithAttachMenuProps {
|
|
|
21
21
|
onAttachMenuOnOpenChangeKeys?: string[];
|
|
22
22
|
/** Callback to change the open state of the menu. Triggered by clicking outside of the menu. */
|
|
23
23
|
onAttachMenuOpenChange?: (isOpen: boolean) => void;
|
|
24
|
+
/** Additional props passed to MenuSearch component in attach menu */
|
|
25
|
+
menuSearchProps?: Omit<MenuSearchProps, 'ref'>;
|
|
26
|
+
/** Additional props passed to MenuSearchInput component in attach menu */
|
|
27
|
+
menuSearchInputProps?: Omit<MenuSearchInputProps, 'ref'>;
|
|
28
|
+
/** Additional props passed to SearchInput component in attach menu */
|
|
29
|
+
searchInputProps?: SearchInputProps;
|
|
24
30
|
}
|
|
25
31
|
export interface MessageBarProps extends Omit<TextAreaProps, 'innerRef'> {
|
|
26
32
|
/** Callback to get the value of input message by user */
|
|
@@ -105,6 +111,8 @@ export interface MessageBarProps extends Omit<TextAreaProps, 'innerRef'> {
|
|
|
105
111
|
isCompact?: boolean;
|
|
106
112
|
/** Ref applied to message bar textarea, for use with focus or other custom behaviors */
|
|
107
113
|
innerRef?: React.Ref<HTMLTextAreaElement>;
|
|
114
|
+
/** Sets background color to primary */
|
|
115
|
+
isPrimary?: boolean;
|
|
108
116
|
}
|
|
109
117
|
export declare const MessageBarBase: FunctionComponent<MessageBarProps>;
|
|
110
118
|
declare const MessageBar: import("react").ForwardRefExoticComponent<MessageBarProps & import("react").RefAttributes<HTMLTextAreaElement>>;
|
|
@@ -20,7 +20,7 @@ import AttachMenu from '../AttachMenu';
|
|
|
20
20
|
import StopButton from './StopButton';
|
|
21
21
|
export const MessageBarBase = (_a) => {
|
|
22
22
|
var _b;
|
|
23
|
-
var { onSendMessage, className, alwayShowSendButton, placeholder = 'Send a message...', hasAttachButton = true, hasMicrophoneButton, listeningText = 'Listening', handleAttach, attachMenuProps, isSendButtonDisabled, handleStopButton, hasStopButton, buttonProps, onChange, displayMode, value, isCompact = false, allowedFileTypes, minSize, maxSize, maxFiles, isAttachmentDisabled, onAttach, onAttachRejected, validator, dropzoneProps, innerRef } = _a, props = __rest(_a, ["onSendMessage", "className", "alwayShowSendButton", "placeholder", "hasAttachButton", "hasMicrophoneButton", "listeningText", "handleAttach", "attachMenuProps", "isSendButtonDisabled", "handleStopButton", "hasStopButton", "buttonProps", "onChange", "displayMode", "value", "isCompact", "allowedFileTypes", "minSize", "maxSize", "maxFiles", "isAttachmentDisabled", "onAttach", "onAttachRejected", "validator", "dropzoneProps", "innerRef"]);
|
|
23
|
+
var { onSendMessage, className, alwayShowSendButton, placeholder = 'Send a message...', hasAttachButton = true, hasMicrophoneButton, listeningText = 'Listening', handleAttach, attachMenuProps, isSendButtonDisabled, handleStopButton, hasStopButton, buttonProps, onChange, displayMode, value, isCompact = false, allowedFileTypes, minSize, maxSize, maxFiles, isAttachmentDisabled, onAttach, onAttachRejected, validator, dropzoneProps, innerRef, isPrimary } = _a, props = __rest(_a, ["onSendMessage", "className", "alwayShowSendButton", "placeholder", "hasAttachButton", "hasMicrophoneButton", "listeningText", "handleAttach", "attachMenuProps", "isSendButtonDisabled", "handleStopButton", "hasStopButton", "buttonProps", "onChange", "displayMode", "value", "isCompact", "allowedFileTypes", "minSize", "maxSize", "maxFiles", "isAttachmentDisabled", "onAttach", "onAttachRejected", "validator", "dropzoneProps", "innerRef", "isPrimary"]);
|
|
24
24
|
// Text Input
|
|
25
25
|
// --------------------------------------------------------------------------
|
|
26
26
|
const [message, setMessage] = useState(value !== null && value !== void 0 ? value : '');
|
|
@@ -200,9 +200,9 @@ export const MessageBarBase = (_a) => {
|
|
|
200
200
|
(_a = attachButtonRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
201
201
|
attachMenuProps === null || attachMenuProps === void 0 ? void 0 : attachMenuProps.setIsAttachMenuOpen(isAttachMenuOpen);
|
|
202
202
|
(attachMenuProps === null || attachMenuProps === void 0 ? void 0 : attachMenuProps.onAttachMenuOpenChange) && (attachMenuProps === null || attachMenuProps === void 0 ? void 0 : attachMenuProps.onAttachMenuOpenChange(isAttachMenuOpen));
|
|
203
|
-
}, onOpenChangeKeys: attachMenuProps === null || attachMenuProps === void 0 ? void 0 : attachMenuProps.onAttachMenuOnOpenChangeKeys, onSelect: attachMenuProps === null || attachMenuProps === void 0 ? void 0 : attachMenuProps.onAttachMenuSelect }, (attachMenuProps && { handleTextInputChange: attachMenuProps.onAttachMenuInputChange }), { popperProps: { direction: 'up', distance: 8 }, searchInputPlaceholder: attachMenuProps === null || attachMenuProps === void 0 ? void 0 : attachMenuProps.attachMenuInputPlaceholder })));
|
|
203
|
+
}, onOpenChangeKeys: attachMenuProps === null || attachMenuProps === void 0 ? void 0 : attachMenuProps.onAttachMenuOnOpenChangeKeys, onSelect: attachMenuProps === null || attachMenuProps === void 0 ? void 0 : attachMenuProps.onAttachMenuSelect }, (attachMenuProps && { handleTextInputChange: attachMenuProps.onAttachMenuInputChange }), { popperProps: { direction: 'up', distance: 8 }, searchInputPlaceholder: attachMenuProps === null || attachMenuProps === void 0 ? void 0 : attachMenuProps.attachMenuInputPlaceholder }, attachMenuProps)));
|
|
204
204
|
}
|
|
205
|
-
return _jsx("div", { className: `pf-chatbot__message-bar ${className !== null && className !== void 0 ? className : ''}`, children: messageBarContents });
|
|
205
|
+
return (_jsx("div", { className: `pf-chatbot__message-bar ${isPrimary ? 'pf-m-primary' : ''} ${className !== null && className !== void 0 ? className : ''}`, children: messageBarContents }));
|
|
206
206
|
};
|
|
207
207
|
const MessageBar = forwardRef((props, ref) => (_jsx(MessageBarBase, Object.assign({ innerRef: ref }, props))));
|
|
208
208
|
export { MessageBar };
|
|
@@ -154,6 +154,39 @@ describe('Message bar', () => {
|
|
|
154
154
|
yield userEvent.click(attachButton);
|
|
155
155
|
expect(attachToggleClickSpy).toHaveBeenCalledTimes(1);
|
|
156
156
|
}));
|
|
157
|
+
it('can pass searchInputProps to search input in AttachMenu', () => {
|
|
158
|
+
render(_jsx(MessageBar, { onSendMessage: jest.fn, value: "test", attachMenuProps: {
|
|
159
|
+
isAttachMenuOpen: true,
|
|
160
|
+
setIsAttachMenuOpen: jest.fn(),
|
|
161
|
+
onAttachMenuToggleClick: jest.fn(),
|
|
162
|
+
onAttachMenuInputChange: jest.fn(),
|
|
163
|
+
attachMenuItems: ATTACH_MENU_ITEMS,
|
|
164
|
+
searchInputProps: { isDisabled: true }
|
|
165
|
+
} }));
|
|
166
|
+
expect(screen.getByRole('textbox', { name: /Filter menu items/i })).toBeDisabled();
|
|
167
|
+
});
|
|
168
|
+
it('can pass menuSearchProps to search input in AttachMenu', () => {
|
|
169
|
+
render(_jsx(MessageBar, { onSendMessage: jest.fn, value: "test", attachMenuProps: {
|
|
170
|
+
isAttachMenuOpen: true,
|
|
171
|
+
setIsAttachMenuOpen: jest.fn(),
|
|
172
|
+
onAttachMenuToggleClick: jest.fn(),
|
|
173
|
+
onAttachMenuInputChange: jest.fn(),
|
|
174
|
+
attachMenuItems: ATTACH_MENU_ITEMS,
|
|
175
|
+
menuSearchProps: { 'data-testid': 'menu-search' }
|
|
176
|
+
} }));
|
|
177
|
+
expect(screen.getByTestId('menu-search')).toBeTruthy();
|
|
178
|
+
});
|
|
179
|
+
it('can pass menuSearchInputProps to search input in AttachMenu', () => {
|
|
180
|
+
render(_jsx(MessageBar, { onSendMessage: jest.fn, value: "test", attachMenuProps: {
|
|
181
|
+
isAttachMenuOpen: true,
|
|
182
|
+
setIsAttachMenuOpen: jest.fn(),
|
|
183
|
+
onAttachMenuToggleClick: jest.fn(),
|
|
184
|
+
onAttachMenuInputChange: jest.fn(),
|
|
185
|
+
attachMenuItems: ATTACH_MENU_ITEMS,
|
|
186
|
+
menuSearchInputProps: { 'data-testid': 'menu-search-input' }
|
|
187
|
+
} }));
|
|
188
|
+
expect(screen.getByTestId('menu-search-input')).toBeTruthy();
|
|
189
|
+
});
|
|
157
190
|
it('can hide attach button', () => {
|
|
158
191
|
render(_jsx(MessageBar, { onSendMessage: jest.fn, hasAttachButton: false }));
|
|
159
192
|
expect(screen.queryByRole('button', { name: 'Attach' })).toBeFalsy();
|
|
@@ -264,4 +297,8 @@ describe('Message bar', () => {
|
|
|
264
297
|
(_a = ref.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
265
298
|
expect(document.activeElement).toBe(screen.getByRole('textbox'));
|
|
266
299
|
});
|
|
300
|
+
it('should handle isPrimary', () => {
|
|
301
|
+
const { container } = render(_jsx(MessageBar, { isPrimary: true, onSendMessage: jest.fn }));
|
|
302
|
+
expect(container.querySelector('.pf-m-primary')).toBeTruthy();
|
|
303
|
+
});
|
|
267
304
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@patternfly/chatbot",
|
|
3
|
-
"version": "6.5.0-prerelease.
|
|
3
|
+
"version": "6.5.0-prerelease.5",
|
|
4
4
|
"description": "This library provides React components based on PatternFly 6 that can be used to build chatbots.",
|
|
5
5
|
"main": "dist/cjs/index.js",
|
|
6
6
|
"module": "dist/esm/index.js",
|
|
@@ -131,15 +131,23 @@ This demo displays a ChatBot in a static, inline drawer. This demo includes:
|
|
|
131
131
|
|
|
132
132
|
```
|
|
133
133
|
|
|
134
|
+
### Primary color background
|
|
135
|
+
|
|
136
|
+
This demo displays an embedded ChatBot with a [primary background color](/design-foundations/colors#background-colors). This example includes the same features as the [Embedded ChatBot demo](/patternfly-ai/chatbot/overview/demo/#embedded-chatbot)—the only differences are that the background color is adjusted via the `isPrimary` prop and some of the sample Messages have changed. You can use the same logic to adjust the background color in any ChatBot layout.
|
|
137
|
+
|
|
138
|
+
```js file="./WhiteEmbeddedChatbot.tsx" isFullscreen
|
|
139
|
+
|
|
140
|
+
```
|
|
141
|
+
|
|
134
142
|
### Display mode switcher
|
|
135
143
|
|
|
136
144
|
This demo showcases how the ChatBot can be rendered in different display modes to suit various application layouts. It demonstrates how to dynamically change the page structure in response to the user's selection. This demo includes:
|
|
137
145
|
|
|
138
146
|
1. The ability to switch between overlay, drawer, and fullscreen modes using the [`<ChatbotHeaderOptionsDropdown>`](/patternfly-ai/chatbot/ui#header-options) in the header.
|
|
139
147
|
2. A conditional page layout that renders the ChatBot for each display mode option:
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
148
|
+
- **Overlay:** As a floating window on top of the page content.
|
|
149
|
+
- **Drawer:** Inside an inline PatternFly `<Drawer>` as a side panel.
|
|
150
|
+
- **Fullscreen:** As a top-level component that covers the entire screen for an embedded experience.
|
|
143
151
|
3. Logic to show or hide the `<ChatbotToggle>` button, which is only present in the default overlay mode.
|
|
144
152
|
4. A [basic ChatBot](#basic-chatbot) with a header, welcome prompt, and message bar to populate the different layouts.
|
|
145
153
|
|
|
@@ -170,7 +178,7 @@ Your code structure should look like this:
|
|
|
170
178
|
|
|
171
179
|
### Chat transcripts
|
|
172
180
|
|
|
173
|
-
This demo illustrates how you could add downloadable transcripts to your ChatBot, which outline conversation details in a Markdown file. This approach allows users to easily share information from a conversation with others.
|
|
181
|
+
This demo illustrates how you could add downloadable transcripts to your ChatBot, which outline conversation details in a Markdown file. This approach allows users to easily share information from a conversation with others.
|
|
174
182
|
|
|
175
183
|
A message transcript includes details from a single chat message. To download a sample message transcript in this demo, click the "Download" action under a bot message.
|
|
176
184
|
|