@patternfly/chatbot 6.5.0-prerelease.1 → 6.5.0-prerelease.10
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 +6 -19
- package/dist/cjs/Message/Message.js +10 -7
- package/dist/cjs/Message/Message.test.js +38 -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 +81 -10
- 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 +6 -19
- package/dist/esm/Message/Message.js +10 -7
- package/dist/esm/Message/Message.test.js +39 -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/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.test.tsx +56 -1
- package/src/Message/Message.tsx +14 -26
- 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
|
@@ -11,6 +11,10 @@
|
|
|
11
11
|
flex-direction: column;
|
|
12
12
|
row-gap: var(--pf-chatbot__footer--RowGap);
|
|
13
13
|
position: relative; // this is so focus ring on parent chatbot doesn't include footer
|
|
14
|
+
|
|
15
|
+
&.pf-m-primary {
|
|
16
|
+
background-color: var(--pf-t--global--background--color--primary--default);
|
|
17
|
+
}
|
|
14
18
|
}
|
|
15
19
|
.pf-chatbot__footer-container {
|
|
16
20
|
padding: 0 var(--pf-t--global--spacer--lg) var(--pf-t--global--spacer--lg) var(--pf-t--global--spacer--lg);
|
|
@@ -62,3 +66,20 @@
|
|
|
62
66
|
padding: 0 var(--pf-t--global--spacer--sm) var(--pf-t--global--spacer--sm) var(--pf-t--global--spacer--sm);
|
|
63
67
|
row-gap: var(--pf-t--global--spacer--sm);
|
|
64
68
|
}
|
|
69
|
+
|
|
70
|
+
// ============================================================================
|
|
71
|
+
// High contrast
|
|
72
|
+
// ============================================================================
|
|
73
|
+
:root:where(.pf-v6-theme-high-contrast) {
|
|
74
|
+
// Chatbot Display Mode - Fullscreen and Embedded
|
|
75
|
+
@media screen and (min-width: 64rem) {
|
|
76
|
+
.pf-chatbot--embedded,
|
|
77
|
+
.pf-chatbot--fullscreen {
|
|
78
|
+
.pf-chatbot__footer {
|
|
79
|
+
.pf-v6-c-divider {
|
|
80
|
+
display: var(--pf-v6-hidden-visible--Display);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -15,10 +15,19 @@ describe('ChatbotFooter', () => {
|
|
|
15
15
|
|
|
16
16
|
it('should handle isCompact', () => {
|
|
17
17
|
render(
|
|
18
|
-
<ChatbotFooter
|
|
18
|
+
<ChatbotFooter isCompact data-testid="footer">
|
|
19
19
|
Chatbot Content
|
|
20
20
|
</ChatbotFooter>
|
|
21
21
|
);
|
|
22
22
|
expect(screen.getByTestId('footer')).toHaveClass('pf-m-compact');
|
|
23
23
|
});
|
|
24
|
+
|
|
25
|
+
it('should handle isPrimary', () => {
|
|
26
|
+
render(
|
|
27
|
+
<ChatbotFooter isPrimary data-testid="footer">
|
|
28
|
+
Chatbot Content
|
|
29
|
+
</ChatbotFooter>
|
|
30
|
+
);
|
|
31
|
+
expect(screen.getByTestId('footer')).toHaveClass('pf-m-primary');
|
|
32
|
+
});
|
|
24
33
|
});
|
|
@@ -13,20 +13,27 @@ import type { HTMLProps, FunctionComponent } from 'react';
|
|
|
13
13
|
import { Divider } from '@patternfly/react-core';
|
|
14
14
|
|
|
15
15
|
export interface ChatbotFooterProps extends HTMLProps<HTMLDivElement> {
|
|
16
|
-
/** Children for the
|
|
16
|
+
/** Children for the footer - supports MessageBar and FootNote components*/
|
|
17
17
|
children?: React.ReactNode;
|
|
18
|
-
/** Custom classname for the
|
|
18
|
+
/** Custom classname for the footer component */
|
|
19
19
|
className?: string;
|
|
20
|
+
/** Sets footer to compact styling. */
|
|
20
21
|
isCompact?: boolean;
|
|
22
|
+
/** Sets background color to primary */
|
|
23
|
+
isPrimary?: boolean;
|
|
21
24
|
}
|
|
22
25
|
|
|
23
26
|
export const ChatbotFooter: FunctionComponent<ChatbotFooterProps> = ({
|
|
24
27
|
children,
|
|
25
28
|
className,
|
|
26
29
|
isCompact,
|
|
30
|
+
isPrimary,
|
|
27
31
|
...props
|
|
28
32
|
}: ChatbotFooterProps) => (
|
|
29
|
-
<div
|
|
33
|
+
<div
|
|
34
|
+
className={`pf-chatbot__footer ${isCompact ? 'pf-m-compact' : ''} ${isPrimary ? 'pf-m-primary' : ''} ${className ?? ''}`}
|
|
35
|
+
{...props}
|
|
36
|
+
>
|
|
30
37
|
<Divider />
|
|
31
38
|
<div className="pf-chatbot__footer-container">{children}</div>
|
|
32
39
|
</div>
|
|
@@ -91,6 +91,10 @@
|
|
|
91
91
|
align-items: center;
|
|
92
92
|
justify-content: center;
|
|
93
93
|
|
|
94
|
+
&::before {
|
|
95
|
+
border-radius: inherit;
|
|
96
|
+
}
|
|
97
|
+
|
|
94
98
|
.pf-v6-c-button__icon,
|
|
95
99
|
.pf-v6-c-menu-toggle__icon,
|
|
96
100
|
.pf-v6-c-icon__content {
|
|
@@ -162,3 +166,18 @@
|
|
|
162
166
|
.pf-chatbot__header .pf-chatbot__actions .pf-v6-c-menu-toggle.pf-m-secondary.pf-m-compact {
|
|
163
167
|
width: initial;
|
|
164
168
|
}
|
|
169
|
+
|
|
170
|
+
// ============================================================================
|
|
171
|
+
// High contrast
|
|
172
|
+
// ============================================================================
|
|
173
|
+
:root:where(.pf-v6-theme-high-contrast) {
|
|
174
|
+
// Chatbot Display Mode - Fullscreen and Embedded
|
|
175
|
+
@media screen and (min-width: 64rem) {
|
|
176
|
+
.pf-chatbot--fullscreen,
|
|
177
|
+
.pf-chatbot--embedded {
|
|
178
|
+
.pf-chatbot__header__divider {
|
|
179
|
+
display: var(--pf-v6-hidden-visible--Display);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
@@ -1,17 +1,31 @@
|
|
|
1
1
|
// ============================================================================
|
|
2
2
|
// Code Modal - Chatbot Modal with Code Editor
|
|
3
3
|
// ============================================================================
|
|
4
|
+
|
|
4
5
|
import type { FunctionComponent, MouseEvent } from 'react';
|
|
5
|
-
import { useState } from 'react';
|
|
6
|
+
import { useState, useEffect, useRef } from 'react';
|
|
6
7
|
import path from 'path-browserify';
|
|
8
|
+
import * as monaco from 'monaco-editor';
|
|
9
|
+
import { loader } from '@monaco-editor/react';
|
|
7
10
|
|
|
8
11
|
// Import PatternFly components
|
|
9
12
|
import { CodeEditor } from '@patternfly/react-code-editor';
|
|
10
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
Button,
|
|
15
|
+
getResizeObserver,
|
|
16
|
+
ModalBody,
|
|
17
|
+
ModalFooter,
|
|
18
|
+
ModalHeader,
|
|
19
|
+
Stack,
|
|
20
|
+
StackItem
|
|
21
|
+
} from '@patternfly/react-core';
|
|
11
22
|
import FileDetails, { extensionToLanguage } from '../FileDetails';
|
|
12
23
|
import { ChatbotDisplayMode } from '../Chatbot';
|
|
13
24
|
import ChatbotModal from '../ChatbotModal/ChatbotModal';
|
|
14
25
|
|
|
26
|
+
// Configure Monaco loader to use the npm package instead of CDN
|
|
27
|
+
loader.config({ monaco });
|
|
28
|
+
|
|
15
29
|
export interface CodeModalProps {
|
|
16
30
|
/** Class applied to code editor */
|
|
17
31
|
codeEditorControlClassName?: string;
|
|
@@ -73,6 +87,34 @@ export const CodeModal: FunctionComponent<CodeModalProps> = ({
|
|
|
73
87
|
...props
|
|
74
88
|
}: CodeModalProps) => {
|
|
75
89
|
const [newCode, setNewCode] = useState(code);
|
|
90
|
+
const [editorInstance, setEditorInstance] = useState<monaco.editor.IStandaloneCodeEditor | null>(null);
|
|
91
|
+
const [isEditorReady, setIsEditorReady] = useState(false);
|
|
92
|
+
const containerRef = useRef<HTMLDivElement>(null);
|
|
93
|
+
|
|
94
|
+
useEffect(() => {
|
|
95
|
+
if (!isModalOpen || !isEditorReady || !editorInstance || !containerRef.current) {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const handleResize = () => {
|
|
100
|
+
if (editorInstance && isEditorReady && isModalOpen) {
|
|
101
|
+
try {
|
|
102
|
+
window.requestAnimationFrame(() => {
|
|
103
|
+
editorInstance.layout();
|
|
104
|
+
});
|
|
105
|
+
} catch (error) {
|
|
106
|
+
// eslint-disable-next-line no-console
|
|
107
|
+
console.error('ChatBot code modal layout error:', error);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
const observer = getResizeObserver(containerRef.current, handleResize);
|
|
113
|
+
|
|
114
|
+
return () => {
|
|
115
|
+
observer();
|
|
116
|
+
};
|
|
117
|
+
}, [editorInstance, isEditorReady, isModalOpen]);
|
|
76
118
|
|
|
77
119
|
const handlePrimaryAction = (_event: MouseEvent | MouseEvent | KeyboardEvent) => {
|
|
78
120
|
handleModalToggle(_event);
|
|
@@ -89,9 +131,15 @@ export const CodeModal: FunctionComponent<CodeModalProps> = ({
|
|
|
89
131
|
};
|
|
90
132
|
|
|
91
133
|
const onEditorDidMount = (editor, monaco) => {
|
|
92
|
-
editor
|
|
93
|
-
|
|
134
|
+
setEditorInstance(editor);
|
|
135
|
+
|
|
94
136
|
monaco.editor.getModels()[0].updateOptions({ tabSize: 5 });
|
|
137
|
+
|
|
138
|
+
if (containerRef.current) {
|
|
139
|
+
setIsEditorReady(true);
|
|
140
|
+
editor.layout();
|
|
141
|
+
editor.focus();
|
|
142
|
+
}
|
|
95
143
|
};
|
|
96
144
|
|
|
97
145
|
const onCodeChange = (value: string) => {
|
|
@@ -117,7 +165,7 @@ export const CodeModal: FunctionComponent<CodeModalProps> = ({
|
|
|
117
165
|
<StackItem className="pf-chatbot__code-modal-file-details">
|
|
118
166
|
<FileDetails fileName={fileName} />
|
|
119
167
|
</StackItem>
|
|
120
|
-
<
|
|
168
|
+
<div className="pf-v6-l-stack__item pf-chatbot__code-modal-editor" ref={containerRef}>
|
|
121
169
|
<CodeEditor
|
|
122
170
|
isDarkTheme
|
|
123
171
|
isLineNumbersVisible={isLineNumbersVisible}
|
|
@@ -132,11 +180,14 @@ export const CodeModal: FunctionComponent<CodeModalProps> = ({
|
|
|
132
180
|
isFullHeight
|
|
133
181
|
options={{
|
|
134
182
|
glyphMargin: false,
|
|
135
|
-
folding: false
|
|
183
|
+
folding: false,
|
|
184
|
+
// prevents Monaco from handling resizing itself
|
|
185
|
+
// was causing ResizeObserver issues
|
|
186
|
+
automaticLayout: false
|
|
136
187
|
}}
|
|
137
188
|
{...props}
|
|
138
189
|
/>
|
|
139
|
-
</
|
|
190
|
+
</div>
|
|
140
191
|
</Stack>
|
|
141
192
|
</ModalBody>
|
|
142
193
|
<ModalFooter className={modalFooterClassName}>
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { PropsWithChildren } from 'react';
|
|
2
|
-
import { Button, Label } from '@patternfly/react-core';
|
|
2
|
+
import { Button, Label, LabelProps } from '@patternfly/react-core';
|
|
3
3
|
import FileDetails from '../FileDetails';
|
|
4
4
|
import { Spinner } from '@patternfly/react-core';
|
|
5
5
|
import { TimesIcon } from '@patternfly/react-icons';
|
|
6
6
|
|
|
7
|
-
export interface FileDetailsLabelProps {
|
|
7
|
+
export interface FileDetailsLabelProps extends Omit<LabelProps, 'onClose' | 'onClick'> {
|
|
8
8
|
/** Name of file, including extension */
|
|
9
9
|
fileName: string;
|
|
10
10
|
/** Unique id of file */
|
|
@@ -30,7 +30,8 @@
|
|
|
30
30
|
font-family: var(--pf-t--global--font--family--body);
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
// we are overriding some default PatternFly positioning here - it's only necessary for the first two items
|
|
34
|
+
.pf-chatbot__message-code-block-default-action {
|
|
34
35
|
display: flex;
|
|
35
36
|
align-items: center;
|
|
36
37
|
justify-content: space-between;
|
|
@@ -39,7 +40,7 @@
|
|
|
39
40
|
font-weight: var(--pf-t--global--font--weight--body--bold);
|
|
40
41
|
}
|
|
41
42
|
|
|
42
|
-
.pf-
|
|
43
|
+
.pf-v6-c-code-block__actions-item > .pf-v6-c-button.pf-m-plain {
|
|
43
44
|
color: var(--pf-t--color--white); // same in light + dark theme
|
|
44
45
|
|
|
45
46
|
&:hover,
|
|
@@ -80,6 +81,10 @@
|
|
|
80
81
|
--pf-chatbot-message-text-inline-code-font-size: var(--pf-t--global--font--size--body--default);
|
|
81
82
|
background-color: var(--pf-t--global--background--color--tertiary--default);
|
|
82
83
|
font-size: var(--pf-chatbot-message-text-inline-code-font-size);
|
|
84
|
+
|
|
85
|
+
&.pf-m-primary {
|
|
86
|
+
background-color: var(--pf-t--global--background--color--secondary--default);
|
|
87
|
+
}
|
|
83
88
|
}
|
|
84
89
|
|
|
85
90
|
.pf-chatbot__message-code-toggle {
|
|
@@ -37,6 +37,10 @@ export interface CodeBlockMessageProps {
|
|
|
37
37
|
expandedText?: string;
|
|
38
38
|
/** Link text applied to expandable toggle when collapsed */
|
|
39
39
|
collapsedText?: string;
|
|
40
|
+
/** Custom actions added to header of code block, after any default actions such as the "copy" action. */
|
|
41
|
+
customActions?: React.ReactNode;
|
|
42
|
+
/** Sets background colors to be appropriate on primary chatbot background */
|
|
43
|
+
isPrimary?: boolean;
|
|
40
44
|
}
|
|
41
45
|
|
|
42
46
|
const DEFAULT_EXPANDED_TEXT = 'Show less';
|
|
@@ -51,6 +55,8 @@ const CodeBlockMessage = ({
|
|
|
51
55
|
expandableSectionToggleProps,
|
|
52
56
|
expandedText = DEFAULT_EXPANDED_TEXT,
|
|
53
57
|
collapsedText = DEFAULT_COLLAPSED_TEXT,
|
|
58
|
+
customActions,
|
|
59
|
+
isPrimary,
|
|
54
60
|
...props
|
|
55
61
|
}: CodeBlockMessageProps) => {
|
|
56
62
|
const [copied, setCopied] = useState(false);
|
|
@@ -105,7 +111,7 @@ const CodeBlockMessage = ({
|
|
|
105
111
|
|
|
106
112
|
if (!String(children).includes('\n')) {
|
|
107
113
|
return (
|
|
108
|
-
<code {...props} className=
|
|
114
|
+
<code {...props} className={`pf-chatbot__message-inline-code ${isPrimary ? 'pf-m-primary' : ''}`}>
|
|
109
115
|
{children}
|
|
110
116
|
</code>
|
|
111
117
|
);
|
|
@@ -114,7 +120,7 @@ const CodeBlockMessage = ({
|
|
|
114
120
|
// Setup code block header
|
|
115
121
|
const actions = (
|
|
116
122
|
<>
|
|
117
|
-
<CodeBlockAction>
|
|
123
|
+
<CodeBlockAction className="pf-chatbot__message-code-block-default-action">
|
|
118
124
|
{language && <div className="pf-chatbot__message-code-block-language">{language}</div>}
|
|
119
125
|
<Button
|
|
120
126
|
ref={buttonRef}
|
|
@@ -127,6 +133,7 @@ const CodeBlockMessage = ({
|
|
|
127
133
|
</Button>
|
|
128
134
|
<Tooltip id={tooltipID} content="Copy" position="top" triggerRef={buttonRef} />
|
|
129
135
|
</CodeBlockAction>
|
|
136
|
+
{customActions}
|
|
130
137
|
</>
|
|
131
138
|
);
|
|
132
139
|
|
|
@@ -6,7 +6,7 @@ import userEvent from '@testing-library/user-event';
|
|
|
6
6
|
import { monitorSampleAppQuickStart } from './QuickStarts/monitor-sampleapp-quickstart';
|
|
7
7
|
import { monitorSampleAppQuickStartWithImage } from './QuickStarts/monitor-sampleapp-quickstart-with-image';
|
|
8
8
|
import rehypeExternalLinks from '../__mocks__/rehype-external-links';
|
|
9
|
-
import { AlertActionLink } from '@patternfly/react-core';
|
|
9
|
+
import { AlertActionLink, Button, CodeBlockAction } from '@patternfly/react-core';
|
|
10
10
|
import { DeepThinkingProps } from '../DeepThinking';
|
|
11
11
|
|
|
12
12
|
const ALL_ACTIONS = [
|
|
@@ -612,6 +612,24 @@ describe('Message', () => {
|
|
|
612
612
|
);
|
|
613
613
|
expect(screen.getByRole('button', { name: 'test' })).toBeTruthy();
|
|
614
614
|
});
|
|
615
|
+
it('should be able to add custom actions to CodeMessage', () => {
|
|
616
|
+
render(
|
|
617
|
+
<Message
|
|
618
|
+
avatar="./img"
|
|
619
|
+
role="user"
|
|
620
|
+
name="User"
|
|
621
|
+
content={CODE_MESSAGE}
|
|
622
|
+
codeBlockProps={{
|
|
623
|
+
customActions: (
|
|
624
|
+
<CodeBlockAction>
|
|
625
|
+
<Button>New custom action</Button>
|
|
626
|
+
</CodeBlockAction>
|
|
627
|
+
)
|
|
628
|
+
}}
|
|
629
|
+
/>
|
|
630
|
+
);
|
|
631
|
+
expect(screen.getByRole('button', { name: /New custom action/i })).toBeTruthy();
|
|
632
|
+
});
|
|
615
633
|
it('should handle hasRoundAvatar correctly when it is true', () => {
|
|
616
634
|
render(<Message avatar="./img" role="user" name="User" content="Hi" hasRoundAvatar />);
|
|
617
635
|
expect(screen.getByRole('img')).toBeTruthy();
|
|
@@ -1075,4 +1093,41 @@ describe('Message', () => {
|
|
|
1075
1093
|
expect(screen.getByText('Thought for 3 seconds')).toBeTruthy();
|
|
1076
1094
|
expect(screen.getByText("Here's why I said this.")).toBeTruthy();
|
|
1077
1095
|
});
|
|
1096
|
+
it('should handle isPrimary correctly for inline code when it is true', () => {
|
|
1097
|
+
const { container } = render(<Message avatar="./img" role="user" name="User" content={INLINE_CODE} isPrimary />);
|
|
1098
|
+
expect(container.querySelector('.pf-m-primary')).toBeTruthy();
|
|
1099
|
+
});
|
|
1100
|
+
it('should handle isPrimary correctly for inline code when it is false', () => {
|
|
1101
|
+
const { container } = render(<Message avatar="./img" role="user" name="User" content={INLINE_CODE} />);
|
|
1102
|
+
expect(container.querySelector('.pf-m-primary')).toBeFalsy();
|
|
1103
|
+
});
|
|
1104
|
+
it('should handle isPrimary correctly for table when it is true', () => {
|
|
1105
|
+
const { container } = render(<Message avatar="./img" role="user" name="User" content={TABLE} isPrimary />);
|
|
1106
|
+
expect(container.querySelector('.pf-m-primary')).toBeTruthy();
|
|
1107
|
+
});
|
|
1108
|
+
it('should handle isPrimary correctly for table when it is false', () => {
|
|
1109
|
+
const { container } = render(<Message avatar="./img" role="user" name="User" content={TABLE} />);
|
|
1110
|
+
expect(container.querySelector('.pf-m-primary')).toBeFalsy();
|
|
1111
|
+
});
|
|
1112
|
+
it('should handle isPrimary correctly for loading when it is true', () => {
|
|
1113
|
+
const { container } = render(<Message avatar="./img" role="user" name="User" content="" isPrimary isLoading />);
|
|
1114
|
+
expect(container.querySelector('.pf-m-primary')).toBeTruthy();
|
|
1115
|
+
});
|
|
1116
|
+
it('should handle isPrimary correctly for loading when it is false', () => {
|
|
1117
|
+
const { container } = render(<Message avatar="./img" role="user" name="User" content="" isLoading />);
|
|
1118
|
+
|
|
1119
|
+
expect(container.querySelector('.pf-m-primary')).toBeFalsy();
|
|
1120
|
+
});
|
|
1121
|
+
it('should handle isPrimary correctly for attachments when it is true', () => {
|
|
1122
|
+
const { container } = render(
|
|
1123
|
+
<Message avatar="./img" role="user" name="User" content="" isPrimary attachments={[{ name: 'testAttachment' }]} />
|
|
1124
|
+
);
|
|
1125
|
+
expect(container.querySelector('.pf-m-outline')).toBeTruthy();
|
|
1126
|
+
});
|
|
1127
|
+
it('should handle isPrimary correctly for attachments when it is false', () => {
|
|
1128
|
+
const { container } = render(
|
|
1129
|
+
<Message avatar="./img" role="user" name="User" content="" attachments={[{ name: 'testAttachment' }]} />
|
|
1130
|
+
);
|
|
1131
|
+
expect(container.querySelector('.pf-m-outline')).toBeFalsy();
|
|
1132
|
+
});
|
|
1078
1133
|
});
|
package/src/Message/Message.tsx
CHANGED
|
@@ -11,8 +11,6 @@ import {
|
|
|
11
11
|
AvatarProps,
|
|
12
12
|
ButtonProps,
|
|
13
13
|
ContentVariants,
|
|
14
|
-
ExpandableSectionProps,
|
|
15
|
-
ExpandableSectionToggleProps,
|
|
16
14
|
FormProps,
|
|
17
15
|
Label,
|
|
18
16
|
LabelGroupProps,
|
|
@@ -20,7 +18,7 @@ import {
|
|
|
20
18
|
Truncate
|
|
21
19
|
} from '@patternfly/react-core';
|
|
22
20
|
import MessageLoading from './MessageLoading';
|
|
23
|
-
import CodeBlockMessage from './CodeBlockMessage/CodeBlockMessage';
|
|
21
|
+
import CodeBlockMessage, { CodeBlockMessageProps } from './CodeBlockMessage/CodeBlockMessage';
|
|
24
22
|
import TextMessage from './TextMessage/TextMessage';
|
|
25
23
|
import FileDetailsLabel from '../FileDetailsLabel/FileDetailsLabel';
|
|
26
24
|
import ResponseActions, { ActionProps } from '../ResponseActions/ResponseActions';
|
|
@@ -44,6 +42,9 @@ import ImageMessage from './ImageMessage/ImageMessage';
|
|
|
44
42
|
import rehypeUnwrapImages from 'rehype-unwrap-images';
|
|
45
43
|
import rehypeExternalLinks from 'rehype-external-links';
|
|
46
44
|
import rehypeSanitize from 'rehype-sanitize';
|
|
45
|
+
import rehypeHighlight from 'rehype-highlight';
|
|
46
|
+
// see the full list of styles here: https://highlightjs.org/examples
|
|
47
|
+
import 'highlight.js/styles/vs2015.css';
|
|
47
48
|
import { PluggableList } from 'unified';
|
|
48
49
|
import LinkMessage from './LinkMessage/LinkMessage';
|
|
49
50
|
import ErrorMessage from './ErrorMessage/ErrorMessage';
|
|
@@ -114,24 +115,7 @@ export interface MessageProps extends Omit<HTMLProps<HTMLDivElement>, 'role'> {
|
|
|
114
115
|
/** Label for the English "Loading message," displayed to screenreaders when loading a message */
|
|
115
116
|
loadingWord?: string;
|
|
116
117
|
/** Props for code blocks */
|
|
117
|
-
codeBlockProps?:
|
|
118
|
-
/** Aria label applied to code blocks */
|
|
119
|
-
'aria-label'?: string;
|
|
120
|
-
/** Class name applied to code blocks */
|
|
121
|
-
className?: string;
|
|
122
|
-
/** Whether code blocks are expandable */
|
|
123
|
-
isExpandable?: boolean;
|
|
124
|
-
/** Length of text initially shown in expandable code blocks; defaults to 10 characters */
|
|
125
|
-
maxLength?: number;
|
|
126
|
-
/** Additional props passed to expandable section if isExpandable is applied */
|
|
127
|
-
expandableSectionProps?: Omit<ExpandableSectionProps, 'ref'>;
|
|
128
|
-
/** Additional props passed to expandable toggle if isExpandable is applied */
|
|
129
|
-
expandableSectionToggleProps?: ExpandableSectionToggleProps;
|
|
130
|
-
/** Link text applied to expandable toggle when expanded */
|
|
131
|
-
expandedText?: string;
|
|
132
|
-
/** Link text applied to expandable toggle when collapsed */
|
|
133
|
-
collapsedText?: string;
|
|
134
|
-
};
|
|
118
|
+
codeBlockProps?: CodeBlockMessageProps;
|
|
135
119
|
/** Props for quick responses */
|
|
136
120
|
quickResponses?: QuickResponse[];
|
|
137
121
|
/** Props for quick responses container */
|
|
@@ -205,6 +189,8 @@ export interface MessageProps extends Omit<HTMLProps<HTMLDivElement>, 'role'> {
|
|
|
205
189
|
toolCall?: ToolCallProps;
|
|
206
190
|
/** Whether user messages default to stripping out images in markdown */
|
|
207
191
|
hasNoImagesInUserMessages?: boolean;
|
|
192
|
+
/** Sets background colors to be appropriate on primary chatbot background */
|
|
193
|
+
isPrimary?: boolean;
|
|
208
194
|
}
|
|
209
195
|
|
|
210
196
|
export const MessageBase: FunctionComponent<MessageProps> = ({
|
|
@@ -252,6 +238,7 @@ export const MessageBase: FunctionComponent<MessageProps> = ({
|
|
|
252
238
|
remarkGfmProps,
|
|
253
239
|
toolCall,
|
|
254
240
|
hasNoImagesInUserMessages = true,
|
|
241
|
+
isPrimary,
|
|
255
242
|
...props
|
|
256
243
|
}: MessageProps) => {
|
|
257
244
|
const [messageText, setMessageText] = useState(content);
|
|
@@ -261,7 +248,7 @@ export const MessageBase: FunctionComponent<MessageProps> = ({
|
|
|
261
248
|
}, [content]);
|
|
262
249
|
|
|
263
250
|
const { beforeMainContent, afterMainContent, endContent } = extraContent || {};
|
|
264
|
-
let rehypePlugins: PluggableList = [rehypeUnwrapImages, rehypeMoveImagesOutOfParagraphs];
|
|
251
|
+
let rehypePlugins: PluggableList = [rehypeUnwrapImages, rehypeMoveImagesOutOfParagraphs, rehypeHighlight];
|
|
265
252
|
if (openLinkInNewTab) {
|
|
266
253
|
rehypePlugins = rehypePlugins.concat([[rehypeExternalLinks, { target: '_blank' }, rehypeSanitize]]);
|
|
267
254
|
}
|
|
@@ -302,13 +289,13 @@ export const MessageBase: FunctionComponent<MessageProps> = ({
|
|
|
302
289
|
p: (props) => {
|
|
303
290
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
304
291
|
const { node, ...rest } = props;
|
|
305
|
-
return <TextMessage component={ContentVariants.p} {...rest} />;
|
|
292
|
+
return <TextMessage component={ContentVariants.p} {...rest} isPrimary={isPrimary} />;
|
|
306
293
|
},
|
|
307
294
|
code: ({ children, ...props }) => {
|
|
308
295
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
309
296
|
const { node, ...codeProps } = props;
|
|
310
297
|
return (
|
|
311
|
-
<CodeBlockMessage {...codeProps} {...codeBlockProps}>
|
|
298
|
+
<CodeBlockMessage {...codeProps} {...codeBlockProps} isPrimary={isPrimary}>
|
|
312
299
|
{children}
|
|
313
300
|
</CodeBlockMessage>
|
|
314
301
|
);
|
|
@@ -364,7 +351,7 @@ export const MessageBase: FunctionComponent<MessageProps> = ({
|
|
|
364
351
|
return <ListItemMessage {...rest} />;
|
|
365
352
|
},
|
|
366
353
|
// table requires node attribute for calculating headers for mobile breakpoint
|
|
367
|
-
table: (props) => <TableMessage {...props} {...tableProps} />,
|
|
354
|
+
table: (props) => <TableMessage {...props} {...tableProps} isPrimary={isPrimary} />,
|
|
368
355
|
tbody: (props) => {
|
|
369
356
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
370
357
|
const { node, ...rest } = props;
|
|
@@ -432,7 +419,7 @@ export const MessageBase: FunctionComponent<MessageProps> = ({
|
|
|
432
419
|
|
|
433
420
|
const renderMessage = () => {
|
|
434
421
|
if (isLoading) {
|
|
435
|
-
return <MessageLoading loadingWord={loadingWord} />;
|
|
422
|
+
return <MessageLoading loadingWord={loadingWord} isPrimary={isPrimary} />;
|
|
436
423
|
}
|
|
437
424
|
if (isEditable) {
|
|
438
425
|
return (
|
|
@@ -538,6 +525,7 @@ export const MessageBase: FunctionComponent<MessageProps> = ({
|
|
|
538
525
|
closeButtonAriaLabel={attachment.closeButtonAriaLabel}
|
|
539
526
|
languageTestId={attachment.languageTestId}
|
|
540
527
|
spinnerTestId={attachment.spinnerTestId}
|
|
528
|
+
variant={isPrimary ? 'outline' : undefined}
|
|
541
529
|
/>
|
|
542
530
|
</div>
|
|
543
531
|
))}
|
|
@@ -6,6 +6,9 @@
|
|
|
6
6
|
padding: var(--pf-t--global--spacer--sm);
|
|
7
7
|
background-color: var(--pf-t--global--background--color--tertiary--default);
|
|
8
8
|
border-radius: var(--pf-t--global--border--radius--small);
|
|
9
|
+
// for high contrast support
|
|
10
|
+
border: var(--pf-t--global--border--width--high-contrast--regular) solid
|
|
11
|
+
var(--pf-t--global--border--color--high-contrast);
|
|
9
12
|
|
|
10
13
|
&-dots {
|
|
11
14
|
position: relative;
|
|
@@ -50,4 +53,8 @@
|
|
|
50
53
|
background-color: rgba(41, 41, 41, 0.25);
|
|
51
54
|
}
|
|
52
55
|
}
|
|
56
|
+
|
|
57
|
+
&.pf-m-primary {
|
|
58
|
+
background-color: var(--pf-t--global--background--color--secondary--default);
|
|
59
|
+
}
|
|
53
60
|
}
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
// Chatbot Main - Message - Processing
|
|
3
3
|
// ============================================================================
|
|
4
4
|
|
|
5
|
-
const MessageLoading = ({ loadingWord }) => (
|
|
6
|
-
<div className=
|
|
5
|
+
const MessageLoading = ({ loadingWord, isPrimary }) => (
|
|
6
|
+
<div className={`pf-chatbot__message-loading ${isPrimary ? 'pf-m-primary' : ''}`}>
|
|
7
7
|
<span className="pf-chatbot__message-loading-dots">
|
|
8
8
|
<span className="pf-v6-screen-reader">{loadingWord}</span>
|
|
9
9
|
</span>
|
|
@@ -19,7 +19,11 @@ export interface TableNode {
|
|
|
19
19
|
type: string;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
export interface TableMessageProps {
|
|
23
|
+
isPrimary?: boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const TableMessage = ({ children, isPrimary, ...props }: Omit<TableProps, 'ref'> & ExtraProps & TableMessageProps) => {
|
|
23
27
|
const { className, ...rest } = props;
|
|
24
28
|
|
|
25
29
|
// This allows us to parse the nested data we get back from the 3rd party Markdown parser
|
|
@@ -72,7 +76,7 @@ const TableMessage = ({ children, ...props }: Omit<TableProps, 'ref'> & ExtraPro
|
|
|
72
76
|
<Table
|
|
73
77
|
aria-label={props['aria-label']}
|
|
74
78
|
gridBreakPoint="grid"
|
|
75
|
-
className={`pf-chatbot__message-table ${className ? className : ''}`}
|
|
79
|
+
className={`pf-chatbot__message-table ${isPrimary ? 'pf-m-primary' : ''} ${className ? className : ''}`}
|
|
76
80
|
{...rest}
|
|
77
81
|
>
|
|
78
82
|
{modifyChildren(children)}
|
|
@@ -47,6 +47,12 @@
|
|
|
47
47
|
white-space: nowrap;
|
|
48
48
|
width: 1px;
|
|
49
49
|
}
|
|
50
|
+
|
|
51
|
+
&.pf-m-primary {
|
|
52
|
+
code {
|
|
53
|
+
background-color: var(--pf-t--global--background--color--secondary--default);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
50
56
|
}
|
|
51
57
|
|
|
52
58
|
// ============================================================================
|
|
@@ -71,6 +77,12 @@ li[id*='user-content-fn-']:has(> span > span > .pf-chatbot__message-text + .pf-c
|
|
|
71
77
|
margin-block-end: var(--pf-t--global--spacer--md);
|
|
72
78
|
}
|
|
73
79
|
|
|
80
|
+
.pf-chatbot__message-text.footnotes {
|
|
81
|
+
.data-footnote-backref {
|
|
82
|
+
width: fit-content;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
74
86
|
.pf-chatbot__message--user {
|
|
75
87
|
.pf-chatbot__message-text {
|
|
76
88
|
background-color: var(--pf-t--global--color--brand--default);
|
|
@@ -5,8 +5,17 @@
|
|
|
5
5
|
import { ExtraProps } from 'react-markdown';
|
|
6
6
|
import { Content, ContentProps } from '@patternfly/react-core';
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
export interface TextMessageProps {
|
|
9
|
+
isPrimary?: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const TextMessage = ({
|
|
13
|
+
component,
|
|
14
|
+
children,
|
|
15
|
+
isPrimary,
|
|
16
|
+
...props
|
|
17
|
+
}: Omit<ContentProps, 'ref'> & ExtraProps & TextMessageProps) => (
|
|
18
|
+
<span className={`pf-chatbot__message-text ${isPrimary ? 'pf-m-primary' : ''}`}>
|
|
10
19
|
<Content component={component} {...props}>
|
|
11
20
|
{children}
|
|
12
21
|
</Content>
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
// shared
|
|
2
2
|
.pf-chatbot__feedback-card {
|
|
3
3
|
box-shadow: var(--pf-t--global--box-shadow--sm);
|
|
4
|
-
|
|
4
|
+
// we want to override to 0 usually and assume the default border for high contrast support
|
|
5
|
+
--pf-v6-c-card--BorderWidth: var(--pf-t--global--border--width--high-contrast--regular);
|
|
5
6
|
max-width: 27.5rem; // fixme address mobile vs desktop
|
|
6
7
|
}
|
|
7
8
|
|
|
@@ -173,4 +173,8 @@ describe('Attach button', () => {
|
|
|
173
173
|
expect(validator).toHaveBeenCalledWith(file);
|
|
174
174
|
expect(onAttachRejected).toHaveBeenCalled();
|
|
175
175
|
});
|
|
176
|
+
it('should handle icon prop', () => {
|
|
177
|
+
render(<AttachButton icon={<img alt="" src="" />} />);
|
|
178
|
+
expect(screen.getByRole('img')).toBeVisible();
|
|
179
|
+
});
|
|
176
180
|
});
|