@patternfly/chatbot 6.5.0-prerelease.2 → 6.5.0-prerelease.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/AttachMenu/AttachMenu.d.ts +8 -2
- package/dist/cjs/AttachMenu/AttachMenu.js +2 -2
- package/dist/cjs/ChatbotFooter/ChatbotFooter.d.ts +5 -2
- package/dist/cjs/ChatbotFooter/ChatbotFooter.js +2 -2
- package/dist/cjs/ChatbotFooter/ChatbotFooter.test.js +5 -1
- package/dist/cjs/ChatbotHeader/ChatbotHeaderMenu.js +29 -2
- package/dist/cjs/CodeModal/CodeModal.d.ts +2 -0
- package/dist/cjs/CodeModal/CodeModal.js +53 -8
- package/dist/cjs/FileDetailsLabel/FileDetailsLabel.d.ts +2 -1
- package/dist/cjs/Message/CodeBlockMessage/CodeBlockMessage.d.ts +3 -1
- package/dist/cjs/Message/CodeBlockMessage/CodeBlockMessage.js +2 -2
- package/dist/cjs/Message/Message.d.ts +7 -1
- package/dist/cjs/Message/Message.js +11 -8
- package/dist/cjs/Message/Message.test.js +36 -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/Onboarding/Onboarding.d.ts +36 -0
- package/dist/cjs/Onboarding/Onboarding.js +37 -0
- package/dist/cjs/Onboarding/Onboarding.test.d.ts +1 -0
- package/dist/cjs/Onboarding/Onboarding.test.js +80 -0
- package/dist/cjs/Onboarding/index.d.ts +2 -0
- package/dist/cjs/Onboarding/index.js +23 -0
- package/dist/cjs/ResponseActions/ResponseActions.d.ts +3 -0
- package/dist/cjs/ResponseActions/ResponseActions.js +28 -7
- package/dist/cjs/ResponseActions/ResponseActions.test.js +67 -12
- 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/cjs/index.d.ts +2 -0
- package/dist/cjs/index.js +4 -1
- package/dist/css/main.css +219 -21
- package/dist/css/main.css.map +1 -1
- package/dist/dynamic/Onboarding/package.json +1 -0
- package/dist/esm/AttachMenu/AttachMenu.d.ts +8 -2
- package/dist/esm/AttachMenu/AttachMenu.js +2 -2
- package/dist/esm/ChatbotFooter/ChatbotFooter.d.ts +5 -2
- package/dist/esm/ChatbotFooter/ChatbotFooter.js +2 -2
- package/dist/esm/ChatbotFooter/ChatbotFooter.test.js +5 -1
- package/dist/esm/ChatbotHeader/ChatbotHeaderMenu.js +30 -3
- package/dist/esm/CodeModal/CodeModal.d.ts +2 -0
- package/dist/esm/CodeModal/CodeModal.js +54 -9
- package/dist/esm/FileDetailsLabel/FileDetailsLabel.d.ts +2 -1
- package/dist/esm/Message/CodeBlockMessage/CodeBlockMessage.d.ts +3 -1
- package/dist/esm/Message/CodeBlockMessage/CodeBlockMessage.js +2 -2
- package/dist/esm/Message/Message.d.ts +7 -1
- package/dist/esm/Message/Message.js +11 -8
- package/dist/esm/Message/Message.test.js +36 -0
- package/dist/esm/Message/MessageLoading.d.ts +2 -1
- package/dist/esm/Message/MessageLoading.js +1 -1
- package/dist/esm/Message/TableMessage/TableMessage.d.ts +4 -1
- package/dist/esm/Message/TableMessage/TableMessage.js +2 -2
- package/dist/esm/Message/TextMessage/TextMessage.d.ts +4 -1
- package/dist/esm/Message/TextMessage/TextMessage.js +2 -2
- package/dist/esm/MessageBar/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/Onboarding/Onboarding.d.ts +36 -0
- package/dist/esm/Onboarding/Onboarding.js +30 -0
- package/dist/esm/Onboarding/Onboarding.test.d.ts +1 -0
- package/dist/esm/Onboarding/Onboarding.test.js +75 -0
- package/dist/esm/Onboarding/index.d.ts +2 -0
- package/dist/esm/Onboarding/index.js +2 -0
- package/dist/esm/ResponseActions/ResponseActions.d.ts +3 -0
- package/dist/esm/ResponseActions/ResponseActions.js +28 -7
- package/dist/esm/ResponseActions/ResponseActions.test.js +67 -12
- 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/esm/index.d.ts +2 -0
- package/dist/esm/index.js +2 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +14 -2
- package/patternfly-docs/content/extensions/chatbot/chatbot.md +57 -0
- package/patternfly-docs/content/extensions/chatbot/design-guidelines.md +12 -12
- package/patternfly-docs/content/extensions/chatbot/examples/Analytics/Analytics.md +1 -1
- package/patternfly-docs/content/extensions/chatbot/examples/Customizing Messages/Customizing Messages.md +1 -1
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/BotMessage.tsx +1 -0
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithPersistedActions.tsx +22 -0
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithQuickResponses.tsx +11 -0
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/Messages.md +18 -4
- 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/ChatbotMessageBarIndicatorThinking.tsx +15 -0
- package/patternfly-docs/content/extensions/chatbot/examples/UI/CompactOnboarding.tsx +141 -0
- package/patternfly-docs/content/extensions/chatbot/examples/UI/Onboarding.tsx +151 -0
- package/patternfly-docs/content/extensions/chatbot/examples/UI/RH-Hat-Image.svg +9 -0
- package/patternfly-docs/content/extensions/chatbot/examples/UI/Settings.tsx +1 -1
- package/patternfly-docs/content/extensions/chatbot/examples/UI/UI.md +55 -27
- package/patternfly-docs/content/extensions/chatbot/examples/demos/AttachmentDemos.md +18 -18
- package/patternfly-docs/content/extensions/chatbot/examples/demos/Chatbot.md +29 -21
- package/patternfly-docs/content/extensions/chatbot/examples/demos/WhiteEmbeddedChatbot.tsx +451 -0
- package/patternfly-docs/patternfly-docs.config.js +2 -1
- package/patternfly-docs/patternfly-docs.source.js +1 -1
- package/src/AttachMenu/AttachMenu.tsx +26 -11
- package/src/Chatbot/Chatbot.scss +23 -1
- package/src/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.scss +25 -0
- 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/ChatbotHeader/ChatbotHeaderMenu.tsx +56 -14
- package/src/ChatbotModal/ChatbotModal.scss +3 -0
- package/src/CodeModal/CodeModal.tsx +72 -23
- package/src/DeepThinking/DeepThinking.scss +1 -1
- package/src/FileDetailsLabel/FileDetailsLabel.tsx +2 -2
- package/src/Message/CodeBlockMessage/CodeBlockMessage.scss +12 -0
- package/src/Message/CodeBlockMessage/CodeBlockMessage.tsx +4 -1
- package/src/Message/Message.scss +11 -7
- package/src/Message/Message.test.tsx +41 -0
- package/src/Message/Message.tsx +28 -13
- package/src/Message/MessageLoading.scss +7 -0
- package/src/Message/MessageLoading.tsx +2 -2
- package/src/Message/TableMessage/TableMessage.scss +6 -1
- package/src/Message/TableMessage/TableMessage.tsx +6 -2
- package/src/Message/TextMessage/TextMessage.scss +10 -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 +40 -3
- package/src/MessageBar/MessageBar.test.tsx +102 -1
- package/src/MessageBar/MessageBar.tsx +44 -11
- package/src/Onboarding/Onboarding.scss +101 -0
- package/src/Onboarding/Onboarding.test.tsx +148 -0
- package/src/Onboarding/Onboarding.tsx +126 -0
- package/src/Onboarding/index.ts +3 -0
- package/src/ResponseActions/ResponseActions.scss +1 -1
- package/src/ResponseActions/ResponseActions.test.tsx +111 -12
- package/src/ResponseActions/ResponseActions.tsx +38 -10
- package/src/ToolCall/ToolCall.scss +1 -1
- package/src/ToolResponse/ToolResponse.scss +3 -3
- package/src/__mocks__/monaco-editor.ts +19 -0
- package/src/__mocks__/rehype-highlight.ts +3 -0
- package/src/index.ts +3 -0
- package/src/main.scss +1 -0
- package/tsconfig.json +1 -1
- package/patternfly-docs/content/extensions/chatbot/about-chatbot.md +0 -44
|
@@ -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,5 +1,5 @@
|
|
|
1
1
|
import type { Ref, FunctionComponent } from 'react';
|
|
2
|
-
import { forwardRef } from 'react';
|
|
2
|
+
import { forwardRef, useEffect, useMemo, useRef, useState } from 'react';
|
|
3
3
|
|
|
4
4
|
import { Button, ButtonProps, Icon, Tooltip, TooltipProps } from '@patternfly/react-core';
|
|
5
5
|
import BarsIcon from '@patternfly/react-icons/dist/esm/icons/bars-icon';
|
|
@@ -30,21 +30,43 @@ const ChatbotHeaderMenuBase: FunctionComponent<ChatbotHeaderMenuProps> = ({
|
|
|
30
30
|
tooltipContent = 'Chat history menu',
|
|
31
31
|
isCompact,
|
|
32
32
|
...props
|
|
33
|
-
}: ChatbotHeaderMenuProps) =>
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
33
|
+
}: ChatbotHeaderMenuProps) => {
|
|
34
|
+
const [isDrawerAnimating, setIsDrawerAnimating] = useState(false);
|
|
35
|
+
// I'd like to use a prop here later if this works
|
|
36
|
+
const drawerState = props['aria-expanded'];
|
|
37
|
+
const isDrawerOpen = drawerState === true;
|
|
38
|
+
const prevDrawerStateRef = useRef<boolean | undefined>(isDrawerOpen);
|
|
39
|
+
const buttonRef = useRef<HTMLButtonElement | null>(null);
|
|
40
|
+
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
if (drawerState !== undefined) {
|
|
43
|
+
const wasDrawerOpen = prevDrawerStateRef.current === true;
|
|
44
|
+
const isDrawerClosing = wasDrawerOpen && !isDrawerOpen;
|
|
45
|
+
|
|
46
|
+
setIsDrawerAnimating(true);
|
|
47
|
+
const timeout = setTimeout(() => {
|
|
48
|
+
setIsDrawerAnimating(false);
|
|
49
|
+
|
|
50
|
+
if (isDrawerClosing) {
|
|
51
|
+
requestAnimationFrame(() => {
|
|
52
|
+
buttonRef.current?.focus();
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}, 350);
|
|
56
|
+
|
|
57
|
+
prevDrawerStateRef.current = isDrawerOpen;
|
|
58
|
+
return () => clearTimeout(timeout);
|
|
59
|
+
}
|
|
60
|
+
}, [drawerState, isDrawerOpen]);
|
|
61
|
+
|
|
62
|
+
const button = useMemo(
|
|
63
|
+
() => (
|
|
42
64
|
<Button
|
|
43
65
|
className={`pf-chatbot__button--toggle-menu ${isCompact ? 'pf-m-compact' : ''}`}
|
|
44
66
|
variant="plain"
|
|
45
67
|
onClick={onMenuToggle}
|
|
46
68
|
aria-label={menuAriaLabel}
|
|
47
|
-
ref={innerRef}
|
|
69
|
+
ref={innerRef ?? buttonRef}
|
|
48
70
|
icon={
|
|
49
71
|
<Icon size={isCompact ? 'lg' : 'xl'} isInline>
|
|
50
72
|
<BarsIcon />
|
|
@@ -53,9 +75,29 @@ const ChatbotHeaderMenuBase: FunctionComponent<ChatbotHeaderMenuProps> = ({
|
|
|
53
75
|
size={isCompact ? 'sm' : undefined}
|
|
54
76
|
{...props}
|
|
55
77
|
/>
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
78
|
+
),
|
|
79
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
80
|
+
[isCompact, menuAriaLabel, onMenuToggle, innerRef, buttonRef]
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
return (
|
|
84
|
+
<div className={`pf-chatbot__menu ${className}`}>
|
|
85
|
+
{isDrawerAnimating ? (
|
|
86
|
+
button
|
|
87
|
+
) : (
|
|
88
|
+
<Tooltip
|
|
89
|
+
content={tooltipContent}
|
|
90
|
+
position="bottom"
|
|
91
|
+
// prevents VO announcements of both aria label and tooltip
|
|
92
|
+
aria="none"
|
|
93
|
+
{...tooltipProps}
|
|
94
|
+
>
|
|
95
|
+
{button}
|
|
96
|
+
</Tooltip>
|
|
97
|
+
)}
|
|
98
|
+
</div>
|
|
99
|
+
);
|
|
100
|
+
};
|
|
59
101
|
|
|
60
102
|
export const ChatbotHeaderMenu = forwardRef((props: ChatbotHeaderMenuProps, ref: Ref<HTMLButtonElement>) => (
|
|
61
103
|
<ChatbotHeaderMenuBase innerRef={ref} {...props} />
|
|
@@ -18,7 +18,10 @@
|
|
|
18
18
|
.pf-v6-c-modal-box__footer {
|
|
19
19
|
padding-block-start: var(--pf-t--global--spacer--xl);
|
|
20
20
|
padding-block-end: var(--pf-t--global--spacer--xl);
|
|
21
|
+
border-top: var(--pf-t--global--border--width--high-contrast--regular) solid
|
|
22
|
+
var(--pf-t--global--border--color--high-contrast);
|
|
21
23
|
}
|
|
24
|
+
|
|
22
25
|
.pf-v6-c-modal-box__header {
|
|
23
26
|
padding-block-end: var(--pf-t--global--spacer--sm);
|
|
24
27
|
}
|
|
@@ -5,16 +5,17 @@
|
|
|
5
5
|
import type { FunctionComponent, MouseEvent } from 'react';
|
|
6
6
|
import { useState, useEffect, useRef } from 'react';
|
|
7
7
|
import path from 'path-browserify';
|
|
8
|
-
import type monaco from 'monaco-editor';
|
|
9
8
|
|
|
10
9
|
// Import PatternFly components
|
|
11
10
|
import { CodeEditor } from '@patternfly/react-code-editor';
|
|
12
11
|
import {
|
|
12
|
+
Bullseye,
|
|
13
13
|
Button,
|
|
14
14
|
getResizeObserver,
|
|
15
15
|
ModalBody,
|
|
16
16
|
ModalFooter,
|
|
17
17
|
ModalHeader,
|
|
18
|
+
Spinner,
|
|
18
19
|
Stack,
|
|
19
20
|
StackItem
|
|
20
21
|
} from '@patternfly/react-core';
|
|
@@ -22,6 +23,17 @@ import FileDetails, { extensionToLanguage } from '../FileDetails';
|
|
|
22
23
|
import { ChatbotDisplayMode } from '../Chatbot';
|
|
23
24
|
import ChatbotModal from '../ChatbotModal/ChatbotModal';
|
|
24
25
|
|
|
26
|
+
// Try to lazy load - some consumers need to be below a certain bundle size, but can't use the CDN and don't have webpack
|
|
27
|
+
let monacoInstance: typeof import('monaco-editor') | null = null;
|
|
28
|
+
const loadMonaco = async () => {
|
|
29
|
+
if (!monacoInstance) {
|
|
30
|
+
const [monaco, { loader }] = await Promise.all([import('monaco-editor'), import('@monaco-editor/react')]);
|
|
31
|
+
monacoInstance = monaco;
|
|
32
|
+
loader.config({ monaco });
|
|
33
|
+
}
|
|
34
|
+
return monacoInstance;
|
|
35
|
+
};
|
|
36
|
+
|
|
25
37
|
export interface CodeModalProps {
|
|
26
38
|
/** Class applied to code editor */
|
|
27
39
|
codeEditorControlClassName?: string;
|
|
@@ -59,6 +71,8 @@ export interface CodeModalProps {
|
|
|
59
71
|
modalBodyClassName?: string;
|
|
60
72
|
/** Class applied to modal footer */
|
|
61
73
|
modalFooterClassName?: string;
|
|
74
|
+
/** Aria label applied to spinner when loading Monaco */
|
|
75
|
+
spinnerAriaLabel?: string;
|
|
62
76
|
}
|
|
63
77
|
|
|
64
78
|
export const CodeModal: FunctionComponent<CodeModalProps> = ({
|
|
@@ -80,13 +94,32 @@ export const CodeModal: FunctionComponent<CodeModalProps> = ({
|
|
|
80
94
|
modalHeaderClassName,
|
|
81
95
|
modalBodyClassName,
|
|
82
96
|
modalFooterClassName,
|
|
97
|
+
spinnerAriaLabel = 'Loading',
|
|
83
98
|
...props
|
|
84
99
|
}: CodeModalProps) => {
|
|
85
100
|
const [newCode, setNewCode] = useState(code);
|
|
86
|
-
const [editorInstance, setEditorInstance] = useState<
|
|
101
|
+
const [editorInstance, setEditorInstance] = useState<any>(null);
|
|
87
102
|
const [isEditorReady, setIsEditorReady] = useState(false);
|
|
103
|
+
const [isMonacoLoading, setIsMonacoLoading] = useState(false);
|
|
104
|
+
const [isMonacoLoaded, setIsMonacoLoaded] = useState(false);
|
|
88
105
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
89
106
|
|
|
107
|
+
useEffect(() => {
|
|
108
|
+
if (isModalOpen && !isMonacoLoaded && !isMonacoLoading) {
|
|
109
|
+
setIsMonacoLoading(true);
|
|
110
|
+
loadMonaco()
|
|
111
|
+
.then(() => {
|
|
112
|
+
setIsMonacoLoaded(true);
|
|
113
|
+
setIsMonacoLoading(false);
|
|
114
|
+
})
|
|
115
|
+
.catch((error) => {
|
|
116
|
+
// eslint-disable-next-line no-console
|
|
117
|
+
console.error('Failed to load Monaco editor:', error);
|
|
118
|
+
setIsMonacoLoading(false);
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
}, [isModalOpen, isMonacoLoaded, isMonacoLoading]);
|
|
122
|
+
|
|
90
123
|
useEffect(() => {
|
|
91
124
|
if (!isModalOpen || !isEditorReady || !editorInstance || !containerRef.current) {
|
|
92
125
|
return;
|
|
@@ -144,6 +177,42 @@ export const CodeModal: FunctionComponent<CodeModalProps> = ({
|
|
|
144
177
|
}
|
|
145
178
|
};
|
|
146
179
|
|
|
180
|
+
const renderMonacoEditor = () => {
|
|
181
|
+
if (isMonacoLoading) {
|
|
182
|
+
return (
|
|
183
|
+
<Bullseye>
|
|
184
|
+
<Spinner aria-label={spinnerAriaLabel} />
|
|
185
|
+
</Bullseye>
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
if (isMonacoLoaded) {
|
|
189
|
+
return (
|
|
190
|
+
<CodeEditor
|
|
191
|
+
isDarkTheme
|
|
192
|
+
isLineNumbersVisible={isLineNumbersVisible}
|
|
193
|
+
isLanguageLabelVisible
|
|
194
|
+
isCopyEnabled={isCopyEnabled}
|
|
195
|
+
isReadOnly={isReadOnly}
|
|
196
|
+
code={newCode}
|
|
197
|
+
language={extensionToLanguage[path.extname(fileName).slice(1)]}
|
|
198
|
+
onEditorDidMount={onEditorDidMount}
|
|
199
|
+
onCodeChange={onCodeChange}
|
|
200
|
+
className={codeEditorClassName}
|
|
201
|
+
isFullHeight
|
|
202
|
+
options={{
|
|
203
|
+
glyphMargin: false,
|
|
204
|
+
folding: false,
|
|
205
|
+
// prevents Monaco from handling resizing itself
|
|
206
|
+
// was causing ResizeObserver issues
|
|
207
|
+
automaticLayout: false
|
|
208
|
+
}}
|
|
209
|
+
{...props}
|
|
210
|
+
/>
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
return null;
|
|
214
|
+
};
|
|
215
|
+
|
|
147
216
|
const modal = (
|
|
148
217
|
<ChatbotModal
|
|
149
218
|
isOpen={isModalOpen}
|
|
@@ -162,27 +231,7 @@ export const CodeModal: FunctionComponent<CodeModalProps> = ({
|
|
|
162
231
|
<FileDetails fileName={fileName} />
|
|
163
232
|
</StackItem>
|
|
164
233
|
<div className="pf-v6-l-stack__item pf-chatbot__code-modal-editor" ref={containerRef}>
|
|
165
|
-
|
|
166
|
-
isDarkTheme
|
|
167
|
-
isLineNumbersVisible={isLineNumbersVisible}
|
|
168
|
-
isLanguageLabelVisible
|
|
169
|
-
isCopyEnabled={isCopyEnabled}
|
|
170
|
-
isReadOnly={isReadOnly}
|
|
171
|
-
code={newCode}
|
|
172
|
-
language={extensionToLanguage[path.extname(fileName).slice(1)]}
|
|
173
|
-
onEditorDidMount={onEditorDidMount}
|
|
174
|
-
onCodeChange={onCodeChange}
|
|
175
|
-
className={codeEditorClassName}
|
|
176
|
-
isFullHeight
|
|
177
|
-
options={{
|
|
178
|
-
glyphMargin: false,
|
|
179
|
-
folding: false,
|
|
180
|
-
// prevents Monaco from handling resizing itself
|
|
181
|
-
// was causing ResizeObserver issues
|
|
182
|
-
automaticLayout: false
|
|
183
|
-
}}
|
|
184
|
-
{...props}
|
|
185
|
-
/>
|
|
234
|
+
{renderMonacoEditor()}
|
|
186
235
|
</div>
|
|
187
236
|
</Stack>
|
|
188
237
|
</ModalBody>
|
|
@@ -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 */
|
|
@@ -79,8 +79,20 @@
|
|
|
79
79
|
|
|
80
80
|
.pf-chatbot__message-inline-code {
|
|
81
81
|
--pf-chatbot-message-text-inline-code-font-size: var(--pf-t--global--font--size--body--default);
|
|
82
|
+
|
|
82
83
|
background-color: var(--pf-t--global--background--color--tertiary--default);
|
|
83
84
|
font-size: var(--pf-chatbot-message-text-inline-code-font-size);
|
|
85
|
+
border: var(--pf-t--global--border--width--high-contrast--regular) solid
|
|
86
|
+
var(--pf-t--global--border--color--high-contrast);
|
|
87
|
+
border-radius: var(--pf-t--global--border--radius--small);
|
|
88
|
+
padding-block-start: var(--pf-t--global--spacer--xs);
|
|
89
|
+
padding-block-end: var(--pf-t--global--spacer--xs);
|
|
90
|
+
padding-inline-start: var(--pf-t--global--spacer--xs);
|
|
91
|
+
padding-inline-end: var(--pf-t--global--spacer--xs);
|
|
92
|
+
|
|
93
|
+
&.pf-m-primary {
|
|
94
|
+
background-color: var(--pf-t--global--background--color--secondary--default);
|
|
95
|
+
}
|
|
84
96
|
}
|
|
85
97
|
|
|
86
98
|
.pf-chatbot__message-code-toggle {
|
|
@@ -39,6 +39,8 @@ export interface CodeBlockMessageProps {
|
|
|
39
39
|
collapsedText?: string;
|
|
40
40
|
/** Custom actions added to header of code block, after any default actions such as the "copy" action. */
|
|
41
41
|
customActions?: React.ReactNode;
|
|
42
|
+
/** Sets background colors to be appropriate on primary chatbot background */
|
|
43
|
+
isPrimary?: boolean;
|
|
42
44
|
}
|
|
43
45
|
|
|
44
46
|
const DEFAULT_EXPANDED_TEXT = 'Show less';
|
|
@@ -54,6 +56,7 @@ const CodeBlockMessage = ({
|
|
|
54
56
|
expandedText = DEFAULT_EXPANDED_TEXT,
|
|
55
57
|
collapsedText = DEFAULT_COLLAPSED_TEXT,
|
|
56
58
|
customActions,
|
|
59
|
+
isPrimary,
|
|
57
60
|
...props
|
|
58
61
|
}: CodeBlockMessageProps) => {
|
|
59
62
|
const [copied, setCopied] = useState(false);
|
|
@@ -108,7 +111,7 @@ const CodeBlockMessage = ({
|
|
|
108
111
|
|
|
109
112
|
if (!String(children).includes('\n')) {
|
|
110
113
|
return (
|
|
111
|
-
<code {...props} className=
|
|
114
|
+
<code {...props} className={`pf-chatbot__message-inline-code ${isPrimary ? 'pf-m-primary' : ''}`}>
|
|
112
115
|
{children}
|
|
113
116
|
</code>
|
|
114
117
|
);
|
package/src/Message/Message.scss
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
display: flex;
|
|
6
6
|
align-items: flex-start;
|
|
7
7
|
gap: var(--pf-t--global--spacer--lg);
|
|
8
|
-
padding-bottom: var(--pf-t--global--spacer--
|
|
8
|
+
padding-bottom: var(--pf-t--global--spacer--lg);
|
|
9
9
|
|
|
10
10
|
// Avatar
|
|
11
11
|
// --------------------------------------------------------------------------
|
|
@@ -18,8 +18,10 @@
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
&-avatar.pf-chatbot__message-avatar--round.pf-v6-c-avatar {
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
&:not(.pf-m-xl, .pf-m-lg, .pf-m-md, .pf-m-sm) {
|
|
22
|
+
--pf-v6-c-avatar--Width: 3rem;
|
|
23
|
+
--pf-v6-c-avatar--Height: 3rem;
|
|
24
|
+
}
|
|
23
25
|
--pf-v6-c-avatar--BorderRadius: var(--pf-t--global--border--radius--pill);
|
|
24
26
|
}
|
|
25
27
|
|
|
@@ -156,8 +158,8 @@
|
|
|
156
158
|
// ============================================================================
|
|
157
159
|
.pf-chatbot.pf-m-compact {
|
|
158
160
|
.pf-chatbot__message {
|
|
159
|
-
gap: var(--pf-t--global--spacer--
|
|
160
|
-
padding-bottom: var(--pf-t--global--spacer--
|
|
161
|
+
gap: var(--pf-t--global--spacer--sm);
|
|
162
|
+
padding-bottom: var(--pf-t--global--spacer--md);
|
|
161
163
|
|
|
162
164
|
.pf-chatbot__message-contents {
|
|
163
165
|
gap: var(--pf-t--global--spacer--xs);
|
|
@@ -169,8 +171,10 @@
|
|
|
169
171
|
}
|
|
170
172
|
|
|
171
173
|
.pf-chatbot__message-avatar.pf-chatbot__message-avatar--round.pf-v6-c-avatar {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
+
&:not(.pf-m-xl, .pf-m-lg, .pf-m-md, .pf-m-sm) {
|
|
175
|
+
--pf-v6-c-avatar--Width: 1.5rem;
|
|
176
|
+
--pf-v6-c-avatar--Height: 1.5rem;
|
|
177
|
+
}
|
|
174
178
|
}
|
|
175
179
|
|
|
176
180
|
.pf-chatbot__message-contents {
|
|
@@ -228,6 +228,10 @@ describe('Message', () => {
|
|
|
228
228
|
render(<Message avatar="./testImg" role="bot" name="Bot" content="Hi" />);
|
|
229
229
|
expect(screen.getByRole('img')).toHaveAttribute('src', './testImg');
|
|
230
230
|
});
|
|
231
|
+
it('should not render avatar if no avatar prop is passed', () => {
|
|
232
|
+
render(<Message role="bot" name="Bot" content="Hi" />);
|
|
233
|
+
expect(screen.queryByRole('img')).not.toBeInTheDocument();
|
|
234
|
+
});
|
|
231
235
|
it('should render botWord correctly', () => {
|
|
232
236
|
render(<Message avatar="./img" role="bot" name="Bot" content="Hi" botWord="人工知能" />);
|
|
233
237
|
expect(screen.getByText('Bot')).toBeTruthy();
|
|
@@ -1093,4 +1097,41 @@ describe('Message', () => {
|
|
|
1093
1097
|
expect(screen.getByText('Thought for 3 seconds')).toBeTruthy();
|
|
1094
1098
|
expect(screen.getByText("Here's why I said this.")).toBeTruthy();
|
|
1095
1099
|
});
|
|
1100
|
+
it('should handle isPrimary correctly for inline code when it is true', () => {
|
|
1101
|
+
const { container } = render(<Message avatar="./img" role="user" name="User" content={INLINE_CODE} isPrimary />);
|
|
1102
|
+
expect(container.querySelector('.pf-m-primary')).toBeTruthy();
|
|
1103
|
+
});
|
|
1104
|
+
it('should handle isPrimary correctly for inline code when it is false', () => {
|
|
1105
|
+
const { container } = render(<Message avatar="./img" role="user" name="User" content={INLINE_CODE} />);
|
|
1106
|
+
expect(container.querySelector('.pf-m-primary')).toBeFalsy();
|
|
1107
|
+
});
|
|
1108
|
+
it('should handle isPrimary correctly for table when it is true', () => {
|
|
1109
|
+
const { container } = render(<Message avatar="./img" role="user" name="User" content={TABLE} isPrimary />);
|
|
1110
|
+
expect(container.querySelector('.pf-m-primary')).toBeTruthy();
|
|
1111
|
+
});
|
|
1112
|
+
it('should handle isPrimary correctly for table when it is false', () => {
|
|
1113
|
+
const { container } = render(<Message avatar="./img" role="user" name="User" content={TABLE} />);
|
|
1114
|
+
expect(container.querySelector('.pf-m-primary')).toBeFalsy();
|
|
1115
|
+
});
|
|
1116
|
+
it('should handle isPrimary correctly for loading when it is true', () => {
|
|
1117
|
+
const { container } = render(<Message avatar="./img" role="user" name="User" content="" isPrimary isLoading />);
|
|
1118
|
+
expect(container.querySelector('.pf-m-primary')).toBeTruthy();
|
|
1119
|
+
});
|
|
1120
|
+
it('should handle isPrimary correctly for loading when it is false', () => {
|
|
1121
|
+
const { container } = render(<Message avatar="./img" role="user" name="User" content="" isLoading />);
|
|
1122
|
+
|
|
1123
|
+
expect(container.querySelector('.pf-m-primary')).toBeFalsy();
|
|
1124
|
+
});
|
|
1125
|
+
it('should handle isPrimary correctly for attachments when it is true', () => {
|
|
1126
|
+
const { container } = render(
|
|
1127
|
+
<Message avatar="./img" role="user" name="User" content="" isPrimary attachments={[{ name: 'testAttachment' }]} />
|
|
1128
|
+
);
|
|
1129
|
+
expect(container.querySelector('.pf-m-outline')).toBeTruthy();
|
|
1130
|
+
});
|
|
1131
|
+
it('should handle isPrimary correctly for attachments when it is false', () => {
|
|
1132
|
+
const { container } = render(
|
|
1133
|
+
<Message avatar="./img" role="user" name="User" content="" attachments={[{ name: 'testAttachment' }]} />
|
|
1134
|
+
);
|
|
1135
|
+
expect(container.querySelector('.pf-m-outline')).toBeFalsy();
|
|
1136
|
+
});
|
|
1096
1137
|
});
|
package/src/Message/Message.tsx
CHANGED
|
@@ -42,6 +42,9 @@ import ImageMessage from './ImageMessage/ImageMessage';
|
|
|
42
42
|
import rehypeUnwrapImages from 'rehype-unwrap-images';
|
|
43
43
|
import rehypeExternalLinks from 'rehype-external-links';
|
|
44
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';
|
|
45
48
|
import { PluggableList } from 'unified';
|
|
46
49
|
import LinkMessage from './LinkMessage/LinkMessage';
|
|
47
50
|
import ErrorMessage from './ErrorMessage/ErrorMessage';
|
|
@@ -94,7 +97,7 @@ export interface MessageProps extends Omit<HTMLProps<HTMLDivElement>, 'role'> {
|
|
|
94
97
|
/** Name of the user */
|
|
95
98
|
name?: string;
|
|
96
99
|
/** Avatar src for the user */
|
|
97
|
-
avatar
|
|
100
|
+
avatar?: string;
|
|
98
101
|
/** Timestamp for the message */
|
|
99
102
|
timestamp?: string;
|
|
100
103
|
/** Set this to true if message is being loaded */
|
|
@@ -105,6 +108,9 @@ export interface MessageProps extends Omit<HTMLProps<HTMLDivElement>, 'role'> {
|
|
|
105
108
|
actions?: {
|
|
106
109
|
[key: string]: ActionProps;
|
|
107
110
|
};
|
|
111
|
+
/** When true, the selected action will persist even when clicking outside the component.
|
|
112
|
+
* When false (default), clicking outside or clicking another action will deselect the current selection. */
|
|
113
|
+
persistActionSelection?: boolean;
|
|
108
114
|
/** Sources for message */
|
|
109
115
|
sources?: SourcesCardProps;
|
|
110
116
|
/** Label for the English word "AI," used to tag messages with role "bot" */
|
|
@@ -186,6 +192,8 @@ export interface MessageProps extends Omit<HTMLProps<HTMLDivElement>, 'role'> {
|
|
|
186
192
|
toolCall?: ToolCallProps;
|
|
187
193
|
/** Whether user messages default to stripping out images in markdown */
|
|
188
194
|
hasNoImagesInUserMessages?: boolean;
|
|
195
|
+
/** Sets background colors to be appropriate on primary chatbot background */
|
|
196
|
+
isPrimary?: boolean;
|
|
189
197
|
}
|
|
190
198
|
|
|
191
199
|
export const MessageBase: FunctionComponent<MessageProps> = ({
|
|
@@ -197,6 +205,7 @@ export const MessageBase: FunctionComponent<MessageProps> = ({
|
|
|
197
205
|
timestamp,
|
|
198
206
|
isLoading,
|
|
199
207
|
actions,
|
|
208
|
+
persistActionSelection,
|
|
200
209
|
sources,
|
|
201
210
|
botWord = 'AI',
|
|
202
211
|
loadingWord = 'Loading message',
|
|
@@ -233,6 +242,7 @@ export const MessageBase: FunctionComponent<MessageProps> = ({
|
|
|
233
242
|
remarkGfmProps,
|
|
234
243
|
toolCall,
|
|
235
244
|
hasNoImagesInUserMessages = true,
|
|
245
|
+
isPrimary,
|
|
236
246
|
...props
|
|
237
247
|
}: MessageProps) => {
|
|
238
248
|
const [messageText, setMessageText] = useState(content);
|
|
@@ -242,7 +252,7 @@ export const MessageBase: FunctionComponent<MessageProps> = ({
|
|
|
242
252
|
}, [content]);
|
|
243
253
|
|
|
244
254
|
const { beforeMainContent, afterMainContent, endContent } = extraContent || {};
|
|
245
|
-
let rehypePlugins: PluggableList = [rehypeUnwrapImages, rehypeMoveImagesOutOfParagraphs];
|
|
255
|
+
let rehypePlugins: PluggableList = [rehypeUnwrapImages, rehypeMoveImagesOutOfParagraphs, rehypeHighlight];
|
|
246
256
|
if (openLinkInNewTab) {
|
|
247
257
|
rehypePlugins = rehypePlugins.concat([[rehypeExternalLinks, { target: '_blank' }, rehypeSanitize]]);
|
|
248
258
|
}
|
|
@@ -283,13 +293,13 @@ export const MessageBase: FunctionComponent<MessageProps> = ({
|
|
|
283
293
|
p: (props) => {
|
|
284
294
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
285
295
|
const { node, ...rest } = props;
|
|
286
|
-
return <TextMessage component={ContentVariants.p} {...rest} />;
|
|
296
|
+
return <TextMessage component={ContentVariants.p} {...rest} isPrimary={isPrimary} />;
|
|
287
297
|
},
|
|
288
298
|
code: ({ children, ...props }) => {
|
|
289
299
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
290
300
|
const { node, ...codeProps } = props;
|
|
291
301
|
return (
|
|
292
|
-
<CodeBlockMessage {...codeProps} {...codeBlockProps}>
|
|
302
|
+
<CodeBlockMessage {...codeProps} {...codeBlockProps} isPrimary={isPrimary}>
|
|
293
303
|
{children}
|
|
294
304
|
</CodeBlockMessage>
|
|
295
305
|
);
|
|
@@ -345,7 +355,7 @@ export const MessageBase: FunctionComponent<MessageProps> = ({
|
|
|
345
355
|
return <ListItemMessage {...rest} />;
|
|
346
356
|
},
|
|
347
357
|
// table requires node attribute for calculating headers for mobile breakpoint
|
|
348
|
-
table: (props) => <TableMessage {...props} {...tableProps} />,
|
|
358
|
+
table: (props) => <TableMessage {...props} {...tableProps} isPrimary={isPrimary} />,
|
|
349
359
|
tbody: (props) => {
|
|
350
360
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
351
361
|
const { node, ...rest } = props;
|
|
@@ -413,7 +423,7 @@ export const MessageBase: FunctionComponent<MessageProps> = ({
|
|
|
413
423
|
|
|
414
424
|
const renderMessage = () => {
|
|
415
425
|
if (isLoading) {
|
|
416
|
-
return <MessageLoading loadingWord={loadingWord} />;
|
|
426
|
+
return <MessageLoading loadingWord={loadingWord} isPrimary={isPrimary} />;
|
|
417
427
|
}
|
|
418
428
|
if (isEditable) {
|
|
419
429
|
return (
|
|
@@ -453,12 +463,14 @@ export const MessageBase: FunctionComponent<MessageProps> = ({
|
|
|
453
463
|
{...props}
|
|
454
464
|
>
|
|
455
465
|
{/* We are using an empty alt tag intentionally in order to reduce noise on screen readers */}
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
466
|
+
{avatar && (
|
|
467
|
+
<Avatar
|
|
468
|
+
className={`pf-chatbot__message-avatar ${hasRoundAvatar ? 'pf-chatbot__message-avatar--round' : ''} ${avatarClassName ? avatarClassName : ''}`}
|
|
469
|
+
src={avatar}
|
|
470
|
+
alt=""
|
|
471
|
+
{...avatarProps}
|
|
472
|
+
/>
|
|
473
|
+
)}
|
|
462
474
|
<div className="pf-chatbot__message-contents">
|
|
463
475
|
<div className="pf-chatbot__message-meta">
|
|
464
476
|
{name && (
|
|
@@ -493,7 +505,9 @@ export const MessageBase: FunctionComponent<MessageProps> = ({
|
|
|
493
505
|
isCompact={isCompact}
|
|
494
506
|
/>
|
|
495
507
|
)}
|
|
496
|
-
{!isLoading && !isEditable && actions &&
|
|
508
|
+
{!isLoading && !isEditable && actions && (
|
|
509
|
+
<ResponseActions actions={actions} persistActionSelection={persistActionSelection} />
|
|
510
|
+
)}
|
|
497
511
|
{userFeedbackForm && <UserFeedback {...userFeedbackForm} timestamp={dateString} isCompact={isCompact} />}
|
|
498
512
|
{userFeedbackComplete && (
|
|
499
513
|
<UserFeedbackComplete {...userFeedbackComplete} timestamp={dateString} isCompact={isCompact} />
|
|
@@ -519,6 +533,7 @@ export const MessageBase: FunctionComponent<MessageProps> = ({
|
|
|
519
533
|
closeButtonAriaLabel={attachment.closeButtonAriaLabel}
|
|
520
534
|
languageTestId={attachment.languageTestId}
|
|
521
535
|
spinnerTestId={attachment.spinnerTestId}
|
|
536
|
+
variant={isPrimary ? 'outline' : undefined}
|
|
522
537
|
/>
|
|
523
538
|
</div>
|
|
524
539
|
))}
|
|
@@ -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
|
}
|