@patternfly/chatbot 6.5.0-prerelease.1 → 6.5.0-prerelease.11
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 +8 -2
- package/dist/cjs/AttachMenu/AttachMenu.js +2 -2
- package/dist/cjs/ChatbotContent/ChatbotContent.d.ts +2 -0
- package/dist/cjs/ChatbotContent/ChatbotContent.js +2 -2
- package/dist/cjs/ChatbotContent/ChatbotContent.test.js +4 -0
- package/dist/cjs/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.d.ts +3 -1
- package/dist/cjs/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.js +3 -3
- package/dist/cjs/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.test.js +4 -0
- 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/CodeModal/CodeModal.js +40 -4
- package/dist/cjs/FileDetailsLabel/FileDetailsLabel.d.ts +2 -1
- package/dist/cjs/Message/CodeBlockMessage/CodeBlockMessage.d.ts +5 -1
- package/dist/cjs/Message/CodeBlockMessage/CodeBlockMessage.js +3 -3
- package/dist/cjs/Message/Message.d.ts +7 -20
- package/dist/cjs/Message/Message.js +11 -8
- package/dist/cjs/Message/Message.test.js +42 -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/AttachButton.d.ts +2 -0
- package/dist/cjs/MessageBar/AttachButton.js +2 -2
- package/dist/cjs/MessageBar/AttachButton.test.js +4 -0
- package/dist/cjs/MessageBar/MessageBar.d.ts +16 -6
- package/dist/cjs/MessageBar/MessageBar.js +6 -5
- package/dist/cjs/MessageBar/MessageBar.test.js +62 -0
- package/dist/cjs/__mocks__/monaco-editor.d.ts +11 -0
- package/dist/cjs/__mocks__/monaco-editor.js +18 -0
- package/dist/cjs/__mocks__/rehype-highlight.d.ts +2 -0
- package/dist/cjs/__mocks__/rehype-highlight.js +4 -0
- package/dist/css/main.css +84 -13
- package/dist/css/main.css.map +1 -1
- package/dist/esm/AttachMenu/AttachMenu.d.ts +8 -2
- package/dist/esm/AttachMenu/AttachMenu.js +2 -2
- package/dist/esm/ChatbotContent/ChatbotContent.d.ts +2 -0
- package/dist/esm/ChatbotContent/ChatbotContent.js +2 -2
- package/dist/esm/ChatbotContent/ChatbotContent.test.js +4 -0
- package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.d.ts +3 -1
- package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.js +3 -3
- package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.test.js +4 -0
- 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/CodeModal/CodeModal.js +42 -6
- package/dist/esm/FileDetailsLabel/FileDetailsLabel.d.ts +2 -1
- package/dist/esm/Message/CodeBlockMessage/CodeBlockMessage.d.ts +5 -1
- package/dist/esm/Message/CodeBlockMessage/CodeBlockMessage.js +3 -3
- package/dist/esm/Message/Message.d.ts +7 -20
- package/dist/esm/Message/Message.js +11 -8
- package/dist/esm/Message/Message.test.js +43 -1
- 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/AttachButton.d.ts +2 -0
- package/dist/esm/MessageBar/AttachButton.js +2 -2
- package/dist/esm/MessageBar/AttachButton.test.js +4 -0
- package/dist/esm/MessageBar/MessageBar.d.ts +16 -6
- package/dist/esm/MessageBar/MessageBar.js +6 -5
- package/dist/esm/MessageBar/MessageBar.test.js +62 -0
- package/dist/esm/__mocks__/monaco-editor.d.ts +11 -0
- package/dist/esm/__mocks__/monaco-editor.js +18 -0
- package/dist/esm/__mocks__/rehype-highlight.d.ts +2 -0
- package/dist/esm/__mocks__/rehype-highlight.js +2 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +5 -2
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/BotMessage.tsx +1 -0
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/UserMessage.tsx +1 -0
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/UserMessageWithExtraContent.tsx +3 -1
- package/patternfly-docs/content/extensions/chatbot/examples/UI/ChatbotHeaderDrawerWithActions.tsx +14 -14
- package/patternfly-docs/content/extensions/chatbot/examples/UI/ChatbotHeaderDrawerWithSelection.tsx +14 -14
- package/patternfly-docs/content/extensions/chatbot/examples/UI/ChatbotMessageBarAttach.tsx +2 -2
- package/patternfly-docs/content/extensions/chatbot/examples/UI/ChatbotMessageBarIndicatorThinking.tsx +15 -0
- package/patternfly-docs/content/extensions/chatbot/examples/UI/Settings.tsx +1 -1
- package/patternfly-docs/content/extensions/chatbot/examples/UI/UI.md +10 -0
- package/patternfly-docs/content/extensions/chatbot/examples/demos/Chatbot.md +12 -4
- package/patternfly-docs/content/extensions/chatbot/examples/demos/ChatbotAttachmentMenu.tsx +2 -2
- package/patternfly-docs/content/extensions/chatbot/examples/demos/ChatbotTranscripts.tsx +1 -1
- package/patternfly-docs/content/extensions/chatbot/examples/demos/WhiteEmbeddedChatbot.tsx +451 -0
- package/patternfly-docs/patternfly-docs.config.js +1 -0
- package/src/AttachMenu/AttachMenu.tsx +26 -11
- package/src/Chatbot/Chatbot.scss +23 -1
- package/src/ChatbotContent/ChatbotContent.scss +4 -0
- package/src/ChatbotContent/ChatbotContent.test.tsx +5 -0
- package/src/ChatbotContent/ChatbotContent.tsx +4 -1
- package/src/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.test.tsx +5 -0
- package/src/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.tsx +7 -4
- package/src/ChatbotFooter/ChatbotFooter.scss +21 -0
- package/src/ChatbotFooter/ChatbotFooter.test.tsx +10 -1
- package/src/ChatbotFooter/ChatbotFooter.tsx +10 -3
- package/src/ChatbotHeader/ChatbotHeader.scss +19 -0
- package/src/CodeModal/CodeModal.tsx +58 -7
- package/src/FileDetailsLabel/FileDetailsLabel.tsx +2 -2
- package/src/Message/CodeBlockMessage/CodeBlockMessage.scss +7 -2
- package/src/Message/CodeBlockMessage/CodeBlockMessage.tsx +9 -2
- package/src/Message/Message.scss +3 -3
- package/src/Message/Message.test.tsx +60 -1
- package/src/Message/Message.tsx +23 -33
- package/src/Message/MessageLoading.scss +7 -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 +12 -0
- package/src/Message/TextMessage/TextMessage.tsx +11 -2
- package/src/Message/UserFeedback/UserFeedback.scss +2 -1
- package/src/MessageBar/AttachButton.test.tsx +4 -0
- package/src/MessageBar/AttachButton.tsx +4 -1
- package/src/MessageBar/MessageBar.scss +11 -5
- package/src/MessageBar/MessageBar.test.tsx +102 -1
- package/src/MessageBar/MessageBar.tsx +44 -11
- package/src/__mocks__/monaco-editor.ts +19 -0
- package/src/__mocks__/rehype-highlight.ts +3 -0
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { FunctionComponent } from 'react';
|
|
2
|
-
import { DropdownProps, DropdownToggleProps, PopperOptions } from '@patternfly/react-core';
|
|
2
|
+
import { DropdownProps, DropdownToggleProps, PopperOptions, MenuSearchInputProps, SearchInputProps, MenuSearchProps } from '@patternfly/react-core';
|
|
3
3
|
export interface AttachMenuProps extends DropdownProps {
|
|
4
4
|
/** Items in menu */
|
|
5
5
|
filteredItems: React.ReactNode;
|
|
6
6
|
/** A callback for when the input value changes. */
|
|
7
|
-
handleTextInputChange
|
|
7
|
+
handleTextInputChange?: (value: string) => void;
|
|
8
8
|
/** Flag to indicate if menu is opened. */
|
|
9
9
|
isOpen: boolean;
|
|
10
10
|
/** Additional properties to pass to the Popper */
|
|
@@ -21,6 +21,12 @@ export interface AttachMenuProps extends DropdownProps {
|
|
|
21
21
|
searchInputAriaLabel?: string;
|
|
22
22
|
/** Toggle to be rendered */
|
|
23
23
|
toggle: DropdownToggleProps | ((toggleRef: React.RefObject<any>) => React.ReactNode);
|
|
24
|
+
/** Additional props passed to MenuSearch component */
|
|
25
|
+
menuSearchProps?: Omit<MenuSearchProps, 'ref'>;
|
|
26
|
+
/** Additional props passed to MenuSearchInput component */
|
|
27
|
+
menuSearchInputProps?: Omit<MenuSearchInputProps, 'ref'>;
|
|
28
|
+
/** Additional props passed to SearchInput component */
|
|
29
|
+
searchInputProps?: SearchInputProps;
|
|
24
30
|
}
|
|
25
31
|
export declare const AttachMenu: FunctionComponent<AttachMenuProps>;
|
|
26
32
|
export default AttachMenu;
|
|
@@ -13,7 +13,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
13
13
|
// Import PatternFly components
|
|
14
14
|
import { MenuSearch, MenuSearchInput, SearchInput, Dropdown } from '@patternfly/react-core';
|
|
15
15
|
export const AttachMenu = (_a) => {
|
|
16
|
-
var { className, filteredItems, handleTextInputChange, isOpen, popperProps = undefined, onOpenChange, onOpenChangeKeys, onSelect, searchInputPlaceholder, searchInputAriaLabel = 'Filter menu items', toggle } = _a, props = __rest(_a, ["className", "filteredItems", "handleTextInputChange", "isOpen", "popperProps", "onOpenChange", "onOpenChangeKeys", "onSelect", "searchInputPlaceholder", "searchInputAriaLabel", "toggle"]);
|
|
17
|
-
return (_jsxs(Dropdown, Object.assign({ className: `pf-chatbot__menu ${className !== null && className !== void 0 ? className : ''}`, isOpen: isOpen, onOpenChange: (isOpen) => onOpenChange(isOpen), onOpenChangeKeys: onOpenChangeKeys !== null && onOpenChangeKeys !== void 0 ? onOpenChangeKeys : ['Esc'], toggle: toggle, popperProps: popperProps, onSelect: onSelect }, props, { children: [_jsx(MenuSearch, { children: _jsx(MenuSearchInput, { children: _jsx(SearchInput, { "aria-label": searchInputAriaLabel, onChange: (_event, value) => handleTextInputChange(value), placeholder: searchInputPlaceholder }) }) }), filteredItems] })));
|
|
16
|
+
var { className, filteredItems, handleTextInputChange, isOpen, popperProps = undefined, onOpenChange, onOpenChangeKeys, onSelect, searchInputPlaceholder, searchInputAriaLabel = 'Filter menu items', toggle, menuSearchProps, menuSearchInputProps, searchInputProps } = _a, props = __rest(_a, ["className", "filteredItems", "handleTextInputChange", "isOpen", "popperProps", "onOpenChange", "onOpenChangeKeys", "onSelect", "searchInputPlaceholder", "searchInputAriaLabel", "toggle", "menuSearchProps", "menuSearchInputProps", "searchInputProps"]);
|
|
17
|
+
return (_jsxs(Dropdown, Object.assign({ className: `pf-chatbot__menu ${className !== null && className !== void 0 ? className : ''}`, isOpen: isOpen, onOpenChange: (isOpen) => onOpenChange(isOpen), onOpenChangeKeys: onOpenChangeKeys !== null && onOpenChangeKeys !== void 0 ? onOpenChangeKeys : ['Esc'], toggle: toggle, popperProps: popperProps, onSelect: onSelect }, props, { children: [handleTextInputChange && (_jsx(MenuSearch, Object.assign({}, menuSearchProps, { children: _jsx(MenuSearchInput, Object.assign({}, menuSearchInputProps, { children: _jsx(SearchInput, Object.assign({ "aria-label": searchInputAriaLabel, onChange: (_event, value) => handleTextInputChange(value), placeholder: searchInputPlaceholder }, searchInputProps)) })) }))), filteredItems] })));
|
|
18
18
|
};
|
|
19
19
|
export default AttachMenu;
|
|
@@ -4,6 +4,8 @@ export interface ChatbotContentProps extends HTMLProps<HTMLDivElement> {
|
|
|
4
4
|
children: React.ReactNode;
|
|
5
5
|
/** Custom classname for the ChatbotContent component */
|
|
6
6
|
className?: string;
|
|
7
|
+
/** Sets background color to primary */
|
|
8
|
+
isPrimary?: boolean;
|
|
7
9
|
}
|
|
8
10
|
export declare const ChatbotContent: FunctionComponent<ChatbotContentProps>;
|
|
9
11
|
export default ChatbotContent;
|
|
@@ -11,7 +11,7 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
11
11
|
};
|
|
12
12
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
13
13
|
export const ChatbotContent = (_a) => {
|
|
14
|
-
var { children, className } = _a, props = __rest(_a, ["children", "className"]);
|
|
15
|
-
return (_jsx("div", Object.assign({ className: `pf-chatbot__content ${className !== null && className !== void 0 ? className : ''}` }, props, { children: children })));
|
|
14
|
+
var { children, className, isPrimary } = _a, props = __rest(_a, ["children", "className", "isPrimary"]);
|
|
15
|
+
return (_jsx("div", Object.assign({ className: `pf-chatbot__content ${isPrimary ? 'pf-m-primary' : ''} ${className !== null && className !== void 0 ? className : ''}` }, props, { children: children })));
|
|
16
16
|
};
|
|
17
17
|
export default ChatbotContent;
|
|
@@ -10,4 +10,8 @@ describe('ChatbotContent', () => {
|
|
|
10
10
|
const { container } = render(_jsx(ChatbotContent, { className: "custom-class", children: "Chatbot Content" }));
|
|
11
11
|
expect(container.querySelector('.custom-class')).toBeTruthy();
|
|
12
12
|
});
|
|
13
|
+
it('should render ChatbotContent with primary class', () => {
|
|
14
|
+
const { container } = render(_jsx(ChatbotContent, { isPrimary: true, children: "Chatbot Content" }));
|
|
15
|
+
expect(container.querySelector('.pf-m-primary')).toBeTruthy();
|
|
16
|
+
});
|
|
13
17
|
});
|
|
@@ -5,8 +5,10 @@ export interface ChatbotConversationHistoryDropdownProps extends Omit<DropdownPr
|
|
|
5
5
|
menuItems: React.ReactNode;
|
|
6
6
|
/** Optional classname applied to conversation settings dropdown */
|
|
7
7
|
menuClassName?: string;
|
|
8
|
-
/** Tooltip content
|
|
8
|
+
/** Tooltip content applied to conversation settings dropdown */
|
|
9
9
|
label?: string;
|
|
10
|
+
/** Aria-label applied to conversation settings dropdown */
|
|
11
|
+
'aria-label'?: string;
|
|
10
12
|
/** Callback for when user selects item. */
|
|
11
13
|
onSelect?: (event?: React.MouseEvent, value?: string | number) => void;
|
|
12
14
|
/** Id applied to dropdown menu toggle */
|
|
@@ -3,11 +3,11 @@ import { useState } from 'react';
|
|
|
3
3
|
// Import PatternFly components
|
|
4
4
|
import { Tooltip, MenuToggle, Dropdown } from '@patternfly/react-core';
|
|
5
5
|
import EllipsisIcon from '@patternfly/react-icons/dist/esm/icons/ellipsis-v-icon';
|
|
6
|
-
export const ChatbotConversationHistoryDropdown = ({ menuItems, menuClassName, onSelect, label, id }) => {
|
|
6
|
+
export const ChatbotConversationHistoryDropdown = ({ menuItems, menuClassName, onSelect, label = 'Conversation options', 'aria-label': ariaLabel, id }) => {
|
|
7
7
|
const [isOpen, setIsOpen] = useState(false);
|
|
8
|
-
const toggle = (toggleRef) => (_jsx(Tooltip, { className: "pf-chatbot__tooltip", content: label
|
|
8
|
+
const toggle = (toggleRef) => (_jsx(Tooltip, { className: "pf-chatbot__tooltip", content: label, position: "bottom",
|
|
9
9
|
// prevents VO announcements of both aria label and tooltip
|
|
10
|
-
aria: "none", children: _jsx(MenuToggle, { className: "pf-chatbot__history-actions", variant: "plain", "aria-label":
|
|
10
|
+
aria: "none", children: _jsx(MenuToggle, { className: "pf-chatbot__history-actions", variant: "plain", "aria-label": ariaLabel !== null && ariaLabel !== void 0 ? ariaLabel : label, ref: toggleRef, isExpanded: isOpen, onClick: () => setIsOpen(!isOpen), id: id, role: "menuitem", children: _jsx(EllipsisIcon, {}) }) }));
|
|
11
11
|
return (_jsx(Dropdown, { className: `pf-chatbot__selections ${menuClassName !== null && menuClassName !== void 0 ? menuClassName : ''}`, isOpen: isOpen, onSelect: (props) => {
|
|
12
12
|
onSelect === null || onSelect === void 0 ? void 0 : onSelect(props);
|
|
13
13
|
setIsOpen((prev) => !prev);
|
|
@@ -63,4 +63,8 @@ describe('ChatbotConversationHistoryDropdown', () => {
|
|
|
63
63
|
expect(screen.queryByText('Actions dropdown')).toBeInTheDocument();
|
|
64
64
|
});
|
|
65
65
|
}));
|
|
66
|
+
it('should be able to set a custom aria-label', () => {
|
|
67
|
+
render(_jsx(ChatbotConversationHistoryDropdown, { menuItems: menuItems, "aria-label": "Custom conversation options" }));
|
|
68
|
+
expect(screen.queryByRole('menuitem', { name: /Custom conversation options/i })).toBeInTheDocument();
|
|
69
|
+
});
|
|
66
70
|
});
|
|
@@ -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
|
});
|
|
@@ -10,17 +10,46 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
10
10
|
return t;
|
|
11
11
|
};
|
|
12
12
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
13
|
-
import { useState } from 'react';
|
|
13
|
+
import { useState, useEffect, useRef } from 'react';
|
|
14
14
|
import path from 'path-browserify';
|
|
15
|
+
import * as monaco from 'monaco-editor';
|
|
16
|
+
import { loader } from '@monaco-editor/react';
|
|
15
17
|
// Import PatternFly components
|
|
16
18
|
import { CodeEditor } from '@patternfly/react-code-editor';
|
|
17
|
-
import { Button, ModalBody, ModalFooter, ModalHeader, Stack, StackItem } from '@patternfly/react-core';
|
|
19
|
+
import { Button, getResizeObserver, ModalBody, ModalFooter, ModalHeader, Stack, StackItem } from '@patternfly/react-core';
|
|
18
20
|
import FileDetails, { extensionToLanguage } from '../FileDetails';
|
|
19
21
|
import { ChatbotDisplayMode } from '../Chatbot';
|
|
20
22
|
import ChatbotModal from '../ChatbotModal/ChatbotModal';
|
|
23
|
+
// Configure Monaco loader to use the npm package instead of CDN
|
|
24
|
+
loader.config({ monaco });
|
|
21
25
|
export const CodeModal = (_a) => {
|
|
22
26
|
var { fileName, code, codeEditorControlClassName: codeEditorClassName, handleModalToggle, isCopyEnabled, isLineNumbersVisible, isModalOpen, isReadOnly, onPrimaryAction, onSecondaryAction, primaryActionBtn, secondaryActionBtn, title, displayMode = ChatbotDisplayMode.default, isCompact, modalHeaderClassName, modalBodyClassName, modalFooterClassName } = _a, props = __rest(_a, ["fileName", "code", "codeEditorControlClassName", "handleModalToggle", "isCopyEnabled", "isLineNumbersVisible", "isModalOpen", "isReadOnly", "onPrimaryAction", "onSecondaryAction", "primaryActionBtn", "secondaryActionBtn", "title", "displayMode", "isCompact", "modalHeaderClassName", "modalBodyClassName", "modalFooterClassName"]);
|
|
23
27
|
const [newCode, setNewCode] = useState(code);
|
|
28
|
+
const [editorInstance, setEditorInstance] = useState(null);
|
|
29
|
+
const [isEditorReady, setIsEditorReady] = useState(false);
|
|
30
|
+
const containerRef = useRef(null);
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
if (!isModalOpen || !isEditorReady || !editorInstance || !containerRef.current) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
const handleResize = () => {
|
|
36
|
+
if (editorInstance && isEditorReady && isModalOpen) {
|
|
37
|
+
try {
|
|
38
|
+
window.requestAnimationFrame(() => {
|
|
39
|
+
editorInstance.layout();
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
// eslint-disable-next-line no-console
|
|
44
|
+
console.error('ChatBot code modal layout error:', error);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
const observer = getResizeObserver(containerRef.current, handleResize);
|
|
49
|
+
return () => {
|
|
50
|
+
observer();
|
|
51
|
+
};
|
|
52
|
+
}, [editorInstance, isEditorReady, isModalOpen]);
|
|
24
53
|
const handlePrimaryAction = (_event) => {
|
|
25
54
|
handleModalToggle(_event);
|
|
26
55
|
if (!isReadOnly) {
|
|
@@ -35,18 +64,25 @@ export const CodeModal = (_a) => {
|
|
|
35
64
|
onSecondaryAction(_event);
|
|
36
65
|
};
|
|
37
66
|
const onEditorDidMount = (editor, monaco) => {
|
|
38
|
-
editor
|
|
39
|
-
editor.focus();
|
|
67
|
+
setEditorInstance(editor);
|
|
40
68
|
monaco.editor.getModels()[0].updateOptions({ tabSize: 5 });
|
|
69
|
+
if (containerRef.current) {
|
|
70
|
+
setIsEditorReady(true);
|
|
71
|
+
editor.layout();
|
|
72
|
+
editor.focus();
|
|
73
|
+
}
|
|
41
74
|
};
|
|
42
75
|
const onCodeChange = (value) => {
|
|
43
76
|
if (!isReadOnly) {
|
|
44
77
|
setNewCode(value);
|
|
45
78
|
}
|
|
46
79
|
};
|
|
47
|
-
const modal = (_jsxs(ChatbotModal, { isOpen: isModalOpen, onClose: handleModalToggle, ouiaId: "CodeModal", "aria-labelledby": "code-modal-title", "aria-describedby": "code-modal", className: `pf-chatbot__code-modal ${isCompact ? 'pf-m-compact' : ''} pf-chatbot__code-modal--${displayMode}`, displayMode: displayMode, isCompact: isCompact, children: [_jsx(ModalHeader, { className: modalHeaderClassName, title: title, labelId: "code-modal-title" }), _jsx(ModalBody, { className: modalBodyClassName, id: "code-modal-body", children: _jsxs(Stack, { className: "pf-chatbot__code-modal-body", children: [_jsx(StackItem, { className: "pf-chatbot__code-modal-file-details", children: _jsx(FileDetails, { fileName: fileName }) }), _jsx(
|
|
80
|
+
const modal = (_jsxs(ChatbotModal, { isOpen: isModalOpen, onClose: handleModalToggle, ouiaId: "CodeModal", "aria-labelledby": "code-modal-title", "aria-describedby": "code-modal", className: `pf-chatbot__code-modal ${isCompact ? 'pf-m-compact' : ''} pf-chatbot__code-modal--${displayMode}`, displayMode: displayMode, isCompact: isCompact, children: [_jsx(ModalHeader, { className: modalHeaderClassName, title: title, labelId: "code-modal-title" }), _jsx(ModalBody, { className: modalBodyClassName, id: "code-modal-body", children: _jsxs(Stack, { className: "pf-chatbot__code-modal-body", children: [_jsx(StackItem, { className: "pf-chatbot__code-modal-file-details", children: _jsx(FileDetails, { fileName: fileName }) }), _jsx("div", { className: "pf-v6-l-stack__item pf-chatbot__code-modal-editor", ref: containerRef, children: _jsx(CodeEditor, Object.assign({ isDarkTheme: true, isLineNumbersVisible: isLineNumbersVisible, isLanguageLabelVisible: true, isCopyEnabled: isCopyEnabled, isReadOnly: isReadOnly, code: newCode, language: extensionToLanguage[path.extname(fileName).slice(1)], onEditorDidMount: onEditorDidMount, onCodeChange: onCodeChange, className: codeEditorClassName, isFullHeight: true, options: {
|
|
48
81
|
glyphMargin: false,
|
|
49
|
-
folding: false
|
|
82
|
+
folding: false,
|
|
83
|
+
// prevents Monaco from handling resizing itself
|
|
84
|
+
// was causing ResizeObserver issues
|
|
85
|
+
automaticLayout: false
|
|
50
86
|
} }, props)) })] }) }), _jsxs(ModalFooter, { className: modalFooterClassName, children: [_jsx(Button, { isBlock: true, variant: "primary", onClick: handlePrimaryAction, form: "code-modal-form", children: primaryActionBtn }, "code-modal-primary"), _jsx(Button, { isBlock: true, variant: "link", onClick: handleSecondaryAction, children: secondaryActionBtn }, "code-modal-secondary")] })] }));
|
|
51
87
|
return modal;
|
|
52
88
|
};
|
|
@@ -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 */
|
|
@@ -16,6 +16,10 @@ export interface CodeBlockMessageProps {
|
|
|
16
16
|
expandedText?: string;
|
|
17
17
|
/** Link text applied to expandable toggle when collapsed */
|
|
18
18
|
collapsedText?: string;
|
|
19
|
+
/** Custom actions added to header of code block, after any default actions such as the "copy" action. */
|
|
20
|
+
customActions?: React.ReactNode;
|
|
21
|
+
/** Sets background colors to be appropriate on primary chatbot background */
|
|
22
|
+
isPrimary?: boolean;
|
|
19
23
|
}
|
|
20
|
-
declare const CodeBlockMessage: ({ children, className, "aria-label": ariaLabel, isExpandable, expandableSectionProps, expandableSectionToggleProps, expandedText, collapsedText, ...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;
|
|
21
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 } = _a, props = __rest(_a, ["children", "className", 'aria-label', "isExpandable", "expandableSectionProps", "expandableSectionToggleProps", "expandedText", "collapsedText"]);
|
|
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,10 +59,10 @@ 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
|
-
const actions = (
|
|
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] }));
|
|
66
66
|
return (_jsx("div", { className: "pf-chatbot__message-code-block", ref: codeBlockRef, children: _jsxs(CodeBlock, { actions: actions, children: [_jsx(CodeBlockCode, { children: _jsx(_Fragment, { children: isExpandable ? (_jsx(ExpandableSection, Object.assign({ variant: ExpandableSectionVariant.truncate, isExpanded: isExpanded, isDetached: true, toggleId: toggleId, contentId: contentId }, expandableSectionProps, { children: children }))) : (children) }) }), isExpandable && (_jsx(ExpandableSectionToggle, Object.assign({ isExpanded: isExpanded, onToggle: onToggle, direction: "up", toggleId: toggleId, contentId: contentId, hasTruncatedContent: true, className: "pf-chatbot__message-code-toggle" }, expandableSectionToggleProps, { children: isExpanded ? finalExpandedText : finalCollapsedText })))] }) }));
|
|
67
67
|
};
|
|
68
68
|
export default CodeBlockMessage;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { ReactNode } from 'react';
|
|
2
2
|
import type { FunctionComponent, HTMLProps, MouseEvent as ReactMouseEvent, Ref } from 'react';
|
|
3
3
|
import { Options } from 'react-markdown';
|
|
4
|
-
import { AlertProps, AvatarProps, ButtonProps,
|
|
4
|
+
import { AlertProps, AvatarProps, ButtonProps, FormProps, LabelGroupProps } from '@patternfly/react-core';
|
|
5
|
+
import { CodeBlockMessageProps } from './CodeBlockMessage/CodeBlockMessage';
|
|
5
6
|
import { ActionProps } from '../ResponseActions/ResponseActions';
|
|
6
7
|
import { SourcesCardProps } from '../SourcesCard';
|
|
7
8
|
import { QuickStart, QuickstartAction } from './QuickStarts/types';
|
|
@@ -9,6 +10,7 @@ import QuickResponse from './QuickResponse/QuickResponse';
|
|
|
9
10
|
import { UserFeedbackProps } from './UserFeedback/UserFeedback';
|
|
10
11
|
import { UserFeedbackCompleteProps } from './UserFeedback/UserFeedbackComplete';
|
|
11
12
|
import { TableProps } from '@patternfly/react-table';
|
|
13
|
+
import 'highlight.js/styles/vs2015.css';
|
|
12
14
|
import { PluggableList } from 'unified';
|
|
13
15
|
import { ToolResponseProps } from '../ToolResponse';
|
|
14
16
|
import { DeepThinkingProps } from '../DeepThinking';
|
|
@@ -51,7 +53,7 @@ export interface MessageProps extends Omit<HTMLProps<HTMLDivElement>, 'role'> {
|
|
|
51
53
|
/** Name of the user */
|
|
52
54
|
name?: string;
|
|
53
55
|
/** Avatar src for the user */
|
|
54
|
-
avatar
|
|
56
|
+
avatar?: string;
|
|
55
57
|
/** Timestamp for the message */
|
|
56
58
|
timestamp?: string;
|
|
57
59
|
/** Set this to true if message is being loaded */
|
|
@@ -69,24 +71,7 @@ export interface MessageProps extends Omit<HTMLProps<HTMLDivElement>, 'role'> {
|
|
|
69
71
|
/** Label for the English "Loading message," displayed to screenreaders when loading a message */
|
|
70
72
|
loadingWord?: string;
|
|
71
73
|
/** Props for code blocks */
|
|
72
|
-
codeBlockProps?:
|
|
73
|
-
/** Aria label applied to code blocks */
|
|
74
|
-
'aria-label'?: string;
|
|
75
|
-
/** Class name applied to code blocks */
|
|
76
|
-
className?: string;
|
|
77
|
-
/** Whether code blocks are expandable */
|
|
78
|
-
isExpandable?: boolean;
|
|
79
|
-
/** Length of text initially shown in expandable code blocks; defaults to 10 characters */
|
|
80
|
-
maxLength?: number;
|
|
81
|
-
/** Additional props passed to expandable section if isExpandable is applied */
|
|
82
|
-
expandableSectionProps?: Omit<ExpandableSectionProps, 'ref'>;
|
|
83
|
-
/** Additional props passed to expandable toggle if isExpandable is applied */
|
|
84
|
-
expandableSectionToggleProps?: ExpandableSectionToggleProps;
|
|
85
|
-
/** Link text applied to expandable toggle when expanded */
|
|
86
|
-
expandedText?: string;
|
|
87
|
-
/** Link text applied to expandable toggle when collapsed */
|
|
88
|
-
collapsedText?: string;
|
|
89
|
-
};
|
|
74
|
+
codeBlockProps?: CodeBlockMessageProps;
|
|
90
75
|
/** Props for quick responses */
|
|
91
76
|
quickResponses?: QuickResponse[];
|
|
92
77
|
/** Props for quick responses container */
|
|
@@ -160,6 +145,8 @@ export interface MessageProps extends Omit<HTMLProps<HTMLDivElement>, 'role'> {
|
|
|
160
145
|
toolCall?: ToolCallProps;
|
|
161
146
|
/** Whether user messages default to stripping out images in markdown */
|
|
162
147
|
hasNoImagesInUserMessages?: boolean;
|
|
148
|
+
/** Sets background colors to be appropriate on primary chatbot background */
|
|
149
|
+
isPrimary?: boolean;
|
|
163
150
|
}
|
|
164
151
|
export declare const MessageBase: FunctionComponent<MessageProps>;
|
|
165
152
|
declare const Message: import("react").ForwardRefExoticComponent<Omit<MessageProps, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
|
|
@@ -40,6 +40,9 @@ import ImageMessage from './ImageMessage/ImageMessage';
|
|
|
40
40
|
import rehypeUnwrapImages from 'rehype-unwrap-images';
|
|
41
41
|
import rehypeExternalLinks from 'rehype-external-links';
|
|
42
42
|
import rehypeSanitize from 'rehype-sanitize';
|
|
43
|
+
import rehypeHighlight from 'rehype-highlight';
|
|
44
|
+
// see the full list of styles here: https://highlightjs.org/examples
|
|
45
|
+
import 'highlight.js/styles/vs2015.css';
|
|
43
46
|
import LinkMessage from './LinkMessage/LinkMessage';
|
|
44
47
|
import ErrorMessage from './ErrorMessage/ErrorMessage';
|
|
45
48
|
import MessageInput from './MessageInput';
|
|
@@ -49,13 +52,13 @@ import DeepThinking from '../DeepThinking';
|
|
|
49
52
|
import SuperscriptMessage from './SuperscriptMessage/SuperscriptMessage';
|
|
50
53
|
import ToolCall from '../ToolCall';
|
|
51
54
|
export const MessageBase = (_a) => {
|
|
52
|
-
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"]);
|
|
53
56
|
const [messageText, setMessageText] = useState(content);
|
|
54
57
|
useEffect(() => {
|
|
55
58
|
setMessageText(content);
|
|
56
59
|
}, [content]);
|
|
57
60
|
const { beforeMainContent, afterMainContent, endContent } = extraContent || {};
|
|
58
|
-
let rehypePlugins = [rehypeUnwrapImages, rehypeMoveImagesOutOfParagraphs];
|
|
61
|
+
let rehypePlugins = [rehypeUnwrapImages, rehypeMoveImagesOutOfParagraphs, rehypeHighlight];
|
|
59
62
|
if (openLinkInNewTab) {
|
|
60
63
|
rehypePlugins = rehypePlugins.concat([[rehypeExternalLinks, { target: '_blank' }, rehypeSanitize]]);
|
|
61
64
|
}
|
|
@@ -88,13 +91,13 @@ export const MessageBase = (_a) => {
|
|
|
88
91
|
p: (props) => {
|
|
89
92
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
90
93
|
const { node } = props, rest = __rest(props, ["node"]);
|
|
91
|
-
return _jsx(TextMessage, Object.assign({ component: ContentVariants.p }, rest));
|
|
94
|
+
return _jsx(TextMessage, Object.assign({ component: ContentVariants.p }, rest, { isPrimary: isPrimary }));
|
|
92
95
|
},
|
|
93
96
|
code: (_a) => {
|
|
94
97
|
var { children } = _a, props = __rest(_a, ["children"]);
|
|
95
98
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
96
99
|
const { node } = props, codeProps = __rest(props, ["node"]);
|
|
97
|
-
return (_jsx(CodeBlockMessage, Object.assign({}, codeProps, codeBlockProps, { children: children })));
|
|
100
|
+
return (_jsx(CodeBlockMessage, Object.assign({}, codeProps, codeBlockProps, { isPrimary: isPrimary, children: children })));
|
|
98
101
|
},
|
|
99
102
|
h1: (props) => {
|
|
100
103
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
@@ -147,7 +150,7 @@ export const MessageBase = (_a) => {
|
|
|
147
150
|
return _jsx(ListItemMessage, Object.assign({}, rest));
|
|
148
151
|
},
|
|
149
152
|
// table requires node attribute for calculating headers for mobile breakpoint
|
|
150
|
-
table: (props) => _jsx(TableMessage, Object.assign({}, props, tableProps)),
|
|
153
|
+
table: (props) => _jsx(TableMessage, Object.assign({}, props, tableProps, { isPrimary: isPrimary })),
|
|
151
154
|
tbody: (props) => {
|
|
152
155
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
153
156
|
const { node } = props, rest = __rest(props, ["node"]);
|
|
@@ -200,7 +203,7 @@ export const MessageBase = (_a) => {
|
|
|
200
203
|
};
|
|
201
204
|
const renderMessage = () => {
|
|
202
205
|
if (isLoading) {
|
|
203
|
-
return _jsx(MessageLoading, { loadingWord: loadingWord });
|
|
206
|
+
return _jsx(MessageLoading, { loadingWord: loadingWord, isPrimary: isPrimary });
|
|
204
207
|
}
|
|
205
208
|
if (isEditable) {
|
|
206
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) => {
|
|
@@ -210,9 +213,9 @@ export const MessageBase = (_a) => {
|
|
|
210
213
|
}
|
|
211
214
|
return (_jsxs(_Fragment, { children: [beforeMainContent && _jsx(_Fragment, { children: beforeMainContent }), error ? _jsx(ErrorMessage, Object.assign({}, error)) : handleMarkdown()] }));
|
|
212
215
|
};
|
|
213
|
-
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) => {
|
|
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: [avatar && (_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) => {
|
|
214
217
|
var _a;
|
|
215
|
-
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));
|
|
216
219
|
}) })), !isLoading && endContent && _jsx(_Fragment, { children: endContent })] })] })] })));
|
|
217
220
|
};
|
|
218
221
|
const Message = forwardRef((props, ref) => (_jsx(MessageBase, Object.assign({ innerRef: ref }, props))));
|
|
@@ -16,7 +16,7 @@ import userEvent from '@testing-library/user-event';
|
|
|
16
16
|
import { monitorSampleAppQuickStart } from './QuickStarts/monitor-sampleapp-quickstart';
|
|
17
17
|
import { monitorSampleAppQuickStartWithImage } from './QuickStarts/monitor-sampleapp-quickstart-with-image';
|
|
18
18
|
import rehypeExternalLinks from '../__mocks__/rehype-external-links';
|
|
19
|
-
import { AlertActionLink } from '@patternfly/react-core';
|
|
19
|
+
import { AlertActionLink, Button, CodeBlockAction } from '@patternfly/react-core';
|
|
20
20
|
const ALL_ACTIONS = [
|
|
21
21
|
{ label: /Good response/i },
|
|
22
22
|
{ label: /Bad response/i },
|
|
@@ -204,6 +204,10 @@ describe('Message', () => {
|
|
|
204
204
|
render(_jsx(Message, { avatar: "./testImg", role: "bot", name: "Bot", content: "Hi" }));
|
|
205
205
|
expect(screen.getByRole('img')).toHaveAttribute('src', './testImg');
|
|
206
206
|
});
|
|
207
|
+
it('should not render avatar if no avatar prop is passed', () => {
|
|
208
|
+
render(_jsx(Message, { role: "bot", name: "Bot", content: "Hi" }));
|
|
209
|
+
expect(screen.queryByRole('img')).not.toBeInTheDocument();
|
|
210
|
+
});
|
|
207
211
|
it('should render botWord correctly', () => {
|
|
208
212
|
render(_jsx(Message, { avatar: "./img", role: "bot", name: "Bot", content: "Hi", botWord: "\u4EBA\u5DE5\u77E5\u80FD" }));
|
|
209
213
|
expect(screen.getByText('Bot')).toBeTruthy();
|
|
@@ -488,6 +492,12 @@ describe('Message', () => {
|
|
|
488
492
|
render(_jsx(Message, { avatar: "./img", role: "user", name: "User", content: CODE_MESSAGE, codeBlockProps: { 'aria-label': 'test' } }));
|
|
489
493
|
expect(screen.getByRole('button', { name: 'test' })).toBeTruthy();
|
|
490
494
|
});
|
|
495
|
+
it('should be able to add custom actions to CodeMessage', () => {
|
|
496
|
+
render(_jsx(Message, { avatar: "./img", role: "user", name: "User", content: CODE_MESSAGE, codeBlockProps: {
|
|
497
|
+
customActions: (_jsx(CodeBlockAction, { children: _jsx(Button, { children: "New custom action" }) }))
|
|
498
|
+
} }));
|
|
499
|
+
expect(screen.getByRole('button', { name: /New custom action/i })).toBeTruthy();
|
|
500
|
+
});
|
|
491
501
|
it('should handle hasRoundAvatar correctly when it is true', () => {
|
|
492
502
|
render(_jsx(Message, { avatar: "./img", role: "user", name: "User", content: "Hi", hasRoundAvatar: true }));
|
|
493
503
|
expect(screen.getByRole('img')).toBeTruthy();
|
|
@@ -820,4 +830,36 @@ describe('Message', () => {
|
|
|
820
830
|
expect(screen.getByText('Thought for 3 seconds')).toBeTruthy();
|
|
821
831
|
expect(screen.getByText("Here's why I said this.")).toBeTruthy();
|
|
822
832
|
});
|
|
833
|
+
it('should handle isPrimary correctly for inline code when it is true', () => {
|
|
834
|
+
const { container } = render(_jsx(Message, { avatar: "./img", role: "user", name: "User", content: INLINE_CODE, isPrimary: true }));
|
|
835
|
+
expect(container.querySelector('.pf-m-primary')).toBeTruthy();
|
|
836
|
+
});
|
|
837
|
+
it('should handle isPrimary correctly for inline code when it is false', () => {
|
|
838
|
+
const { container } = render(_jsx(Message, { avatar: "./img", role: "user", name: "User", content: INLINE_CODE }));
|
|
839
|
+
expect(container.querySelector('.pf-m-primary')).toBeFalsy();
|
|
840
|
+
});
|
|
841
|
+
it('should handle isPrimary correctly for table when it is true', () => {
|
|
842
|
+
const { container } = render(_jsx(Message, { avatar: "./img", role: "user", name: "User", content: TABLE, isPrimary: true }));
|
|
843
|
+
expect(container.querySelector('.pf-m-primary')).toBeTruthy();
|
|
844
|
+
});
|
|
845
|
+
it('should handle isPrimary correctly for table when it is false', () => {
|
|
846
|
+
const { container } = render(_jsx(Message, { avatar: "./img", role: "user", name: "User", content: TABLE }));
|
|
847
|
+
expect(container.querySelector('.pf-m-primary')).toBeFalsy();
|
|
848
|
+
});
|
|
849
|
+
it('should handle isPrimary correctly for loading when it is true', () => {
|
|
850
|
+
const { container } = render(_jsx(Message, { avatar: "./img", role: "user", name: "User", content: "", isPrimary: true, isLoading: true }));
|
|
851
|
+
expect(container.querySelector('.pf-m-primary')).toBeTruthy();
|
|
852
|
+
});
|
|
853
|
+
it('should handle isPrimary correctly for loading when it is false', () => {
|
|
854
|
+
const { container } = render(_jsx(Message, { avatar: "./img", role: "user", name: "User", content: "", isLoading: true }));
|
|
855
|
+
expect(container.querySelector('.pf-m-primary')).toBeFalsy();
|
|
856
|
+
});
|
|
857
|
+
it('should handle isPrimary correctly for attachments when it is true', () => {
|
|
858
|
+
const { container } = render(_jsx(Message, { avatar: "./img", role: "user", name: "User", content: "", isPrimary: true, attachments: [{ name: 'testAttachment' }] }));
|
|
859
|
+
expect(container.querySelector('.pf-m-outline')).toBeTruthy();
|
|
860
|
+
});
|
|
861
|
+
it('should handle isPrimary correctly for attachments when it is false', () => {
|
|
862
|
+
const { container } = render(_jsx(Message, { avatar: "./img", role: "user", name: "User", content: "", attachments: [{ name: 'testAttachment' }] }));
|
|
863
|
+
expect(container.querySelector('.pf-m-outline')).toBeFalsy();
|
|
864
|
+
});
|
|
823
865
|
});
|
|
@@ -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;
|
|
@@ -41,5 +41,7 @@ export interface AttachButtonProps extends ButtonProps {
|
|
|
41
41
|
validator?: <T extends File>(file: T) => FileError | readonly FileError[] | null;
|
|
42
42
|
/** Additional props passed to react-dropzone */
|
|
43
43
|
dropzoneProps?: DropzoneOptions;
|
|
44
|
+
/** Icon displayed in attach button */
|
|
45
|
+
icon?: React.ReactNode;
|
|
44
46
|
}
|
|
45
47
|
export declare const AttachButton: import("react").ForwardRefExoticComponent<AttachButtonProps & import("react").RefAttributes<any>>;
|