@patternfly/chatbot 6.3.2 → 6.4.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/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.d.ts +2 -0
- package/dist/cjs/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.js +2 -2
- package/dist/cjs/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.test.js +6 -6
- package/dist/cjs/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.d.ts +27 -4
- package/dist/cjs/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.js +8 -14
- package/dist/cjs/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.test.js +53 -2
- package/dist/cjs/ChatbotHeader/ChatbotHeaderMenu.js +1 -1
- package/dist/cjs/ChatbotHeader/ChatbotHeaderMenu.test.js +1 -1
- package/dist/cjs/ChatbotHeader/ChatbotHeaderNewChatButton.d.ts +18 -0
- package/dist/cjs/ChatbotHeader/ChatbotHeaderNewChatButton.js +25 -0
- package/dist/cjs/ChatbotHeader/ChatbotHeaderNewChatButton.test.d.ts +1 -0
- package/dist/cjs/ChatbotHeader/ChatbotHeaderNewChatButton.test.js +22 -0
- package/dist/cjs/ChatbotHeader/index.d.ts +1 -0
- package/dist/cjs/ChatbotHeader/index.js +1 -0
- package/dist/cjs/FileDropZone/FileDropZone.d.ts +1 -2
- package/dist/cjs/Message/Message.d.ts +9 -2
- package/dist/cjs/Message/Message.js +40 -34
- package/dist/cjs/Message/Message.test.js +37 -0
- package/dist/cjs/Message/MessageInput.d.ts +3 -1
- package/dist/cjs/Message/MessageInput.js +2 -2
- package/dist/cjs/MessageBar/AttachButton.d.ts +2 -2
- package/dist/cjs/MessageBar/MessageBar.d.ts +2 -2
- package/dist/cjs/MessageBox/JumpButton.d.ts +5 -0
- package/dist/cjs/MessageBox/JumpButton.js +1 -1
- package/dist/cjs/MessageBox/JumpButton.test.js +4 -4
- package/dist/cjs/MessageBox/MessageBox.d.ts +9 -0
- package/dist/cjs/MessageBox/MessageBox.js +2 -2
- package/dist/cjs/MessageBox/MessageBox.test.js +2 -2
- package/dist/cjs/MessageDivider/MessageDivider.d.ts +9 -0
- package/dist/cjs/MessageDivider/MessageDivider.js +23 -0
- package/dist/cjs/MessageDivider/MessageDivider.test.d.ts +1 -0
- package/dist/cjs/MessageDivider/MessageDivider.test.js +29 -0
- package/dist/cjs/MessageDivider/index.d.ts +2 -0
- package/dist/cjs/MessageDivider/index.js +23 -0
- package/dist/cjs/ResponseActions/ResponseActions.d.ts +1 -0
- package/dist/cjs/ResponseActions/ResponseActions.js +4 -4
- package/dist/cjs/ResponseActions/ResponseActions.test.js +6 -1
- package/dist/cjs/index.d.ts +2 -0
- package/dist/cjs/index.js +4 -1
- package/dist/css/main.css +103 -81
- package/dist/css/main.css.map +1 -1
- package/dist/dynamic/MessageDivider/package.json +1 -0
- package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.d.ts +2 -0
- package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.js +2 -2
- package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.test.js +6 -6
- package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.d.ts +27 -4
- package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.js +10 -16
- package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.test.js +54 -3
- package/dist/esm/ChatbotHeader/ChatbotHeaderMenu.js +1 -1
- package/dist/esm/ChatbotHeader/ChatbotHeaderMenu.test.js +1 -1
- package/dist/esm/ChatbotHeader/ChatbotHeaderNewChatButton.d.ts +18 -0
- package/dist/esm/ChatbotHeader/ChatbotHeaderNewChatButton.js +22 -0
- package/dist/esm/ChatbotHeader/ChatbotHeaderNewChatButton.test.d.ts +1 -0
- package/dist/esm/ChatbotHeader/ChatbotHeaderNewChatButton.test.js +20 -0
- package/dist/esm/ChatbotHeader/index.d.ts +1 -0
- package/dist/esm/ChatbotHeader/index.js +1 -0
- package/dist/esm/FileDropZone/FileDropZone.d.ts +1 -2
- package/dist/esm/Message/Message.d.ts +9 -2
- package/dist/esm/Message/Message.js +40 -34
- package/dist/esm/Message/Message.test.js +37 -0
- package/dist/esm/Message/MessageInput.d.ts +3 -1
- package/dist/esm/Message/MessageInput.js +2 -2
- package/dist/esm/MessageBar/AttachButton.d.ts +2 -2
- package/dist/esm/MessageBar/MessageBar.d.ts +2 -2
- package/dist/esm/MessageBox/JumpButton.d.ts +5 -0
- package/dist/esm/MessageBox/JumpButton.js +1 -1
- package/dist/esm/MessageBox/JumpButton.test.js +4 -4
- package/dist/esm/MessageBox/MessageBox.d.ts +9 -0
- package/dist/esm/MessageBox/MessageBox.js +2 -2
- package/dist/esm/MessageBox/MessageBox.test.js +2 -2
- package/dist/esm/MessageDivider/MessageDivider.d.ts +9 -0
- package/dist/esm/MessageDivider/MessageDivider.js +21 -0
- package/dist/esm/MessageDivider/MessageDivider.test.d.ts +1 -0
- package/dist/esm/MessageDivider/MessageDivider.test.js +24 -0
- package/dist/esm/MessageDivider/index.d.ts +2 -0
- package/dist/esm/MessageDivider/index.js +2 -0
- package/dist/esm/ResponseActions/ResponseActions.d.ts +1 -0
- package/dist/esm/ResponseActions/ResponseActions.js +5 -5
- package/dist/esm/ResponseActions/ResponseActions.test.js +6 -1
- package/dist/esm/index.d.ts +2 -0
- package/dist/esm/index.js +2 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +9 -4
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithDividers.tsx +24 -0
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/Messages.md +18 -1
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/UserMessage.tsx +39 -7
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/UserMessageWithExtraContent.tsx +401 -3
- package/patternfly-docs/content/extensions/chatbot/examples/UI/ChatbotConversationEditing.tsx +202 -0
- package/patternfly-docs/content/extensions/chatbot/examples/UI/ChatbotHeaderBasic.tsx +17 -3
- package/patternfly-docs/content/extensions/chatbot/examples/UI/ChatbotHeaderDrawer.tsx +45 -5
- package/patternfly-docs/content/extensions/chatbot/examples/UI/ChatbotHeaderDrawerWithPin.tsx +206 -0
- package/patternfly-docs/content/extensions/chatbot/examples/UI/UI.md +30 -4
- package/patternfly-docs/content/extensions/chatbot/examples/demos/Chatbot.md +33 -1
- package/patternfly-docs/content/extensions/chatbot/examples/demos/ChatbotDisplayMode.tsx +486 -0
- package/patternfly-docs/content/extensions/chatbot/examples/demos/ChatbotTranscripts.tsx +565 -0
- package/src/Chatbot/Chatbot.scss +1 -1
- package/src/ChatbotContent/ChatbotContent.scss +1 -1
- package/src/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.test.tsx +6 -6
- package/src/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.tsx +5 -2
- package/src/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.scss +70 -32
- package/src/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.test.tsx +176 -3
- package/src/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.tsx +110 -60
- package/src/ChatbotFooter/ChatbotFooter.scss +1 -1
- package/src/ChatbotHeader/ChatbotHeader.scss +3 -3
- package/src/ChatbotHeader/ChatbotHeaderMenu.test.tsx +1 -1
- package/src/ChatbotHeader/ChatbotHeaderMenu.tsx +2 -2
- package/src/ChatbotHeader/ChatbotHeaderNewChatButton.test.tsx +25 -0
- package/src/ChatbotHeader/ChatbotHeaderNewChatButton.tsx +64 -0
- package/src/ChatbotHeader/index.ts +1 -0
- package/src/ChatbotModal/ChatbotModal.scss +1 -1
- package/src/ChatbotToggle/ChatbotToggle.scss +2 -2
- package/src/FileDetails/__snapshots__/FileDetails.test.tsx.snap +6 -9
- package/src/FileDetailsLabel/__snapshots__/FileDetailsLabel.test.tsx.snap +6 -9
- package/src/FileDropZone/FileDropZone.tsx +2 -2
- package/src/Message/Message.scss +9 -7
- package/src/Message/Message.test.tsx +54 -0
- package/src/Message/Message.tsx +70 -50
- package/src/Message/MessageInput.tsx +5 -1
- package/src/MessageBar/AttachButton.tsx +2 -2
- package/src/MessageBar/MessageBar.tsx +2 -2
- package/src/MessageBar/SendButton.scss +3 -3
- package/src/MessageBox/JumpButton.scss +1 -1
- package/src/MessageBox/JumpButton.test.tsx +4 -4
- package/src/MessageBox/JumpButton.tsx +20 -4
- package/src/MessageBox/MessageBox.test.tsx +2 -2
- package/src/MessageBox/MessageBox.tsx +23 -2
- package/src/MessageDivider/MessageDivider.scss +45 -0
- package/src/MessageDivider/MessageDivider.test.tsx +24 -0
- package/src/MessageDivider/MessageDivider.tsx +35 -0
- package/src/MessageDivider/index.ts +3 -0
- package/src/ResponseActions/ResponseActions.test.tsx +6 -1
- package/src/ResponseActions/ResponseActions.tsx +24 -3
- package/src/index.ts +3 -0
- package/src/main.scss +1 -52
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
grid-template-columns: 1fr auto;
|
|
10
10
|
gap: var(--pf-t--global--spacer--sm);
|
|
11
11
|
position: relative; // this is so focus ring on parent chatbot doesn't include header
|
|
12
|
-
background-color: var(--pf-t--
|
|
12
|
+
background-color: var(--pf-t--global--background--color--secondary--default);
|
|
13
13
|
justify-content: space-between;
|
|
14
14
|
padding: var(--pf-t--global--spacer--lg);
|
|
15
15
|
|
|
@@ -76,7 +76,7 @@
|
|
|
76
76
|
.pf-chatbot--drawer,
|
|
77
77
|
.pf-chatbot--docked {
|
|
78
78
|
.pf-chatbot__header {
|
|
79
|
-
background-color: var(--pf-t--
|
|
79
|
+
background-color: var(--pf-t--global--background--color--secondary--default);
|
|
80
80
|
}
|
|
81
81
|
}
|
|
82
82
|
|
|
@@ -144,7 +144,7 @@
|
|
|
144
144
|
.pf-chatbot.pf-m-compact {
|
|
145
145
|
.pf-chatbot__header {
|
|
146
146
|
gap: var(--pf-t--global--spacer--sm);
|
|
147
|
-
padding: var(--pf-t--global--spacer--
|
|
147
|
+
padding: var(--pf-t--global--spacer--md);
|
|
148
148
|
}
|
|
149
149
|
|
|
150
150
|
.pf-chatbot__header .pf-chatbot__title img {
|
|
@@ -12,7 +12,7 @@ describe('ChatbotHeaderMenu', () => {
|
|
|
12
12
|
it('should call onMenuToggle when ChatbotHeaderMenu button is clicked', () => {
|
|
13
13
|
const onMenuToggle = jest.fn();
|
|
14
14
|
render(<ChatbotHeaderMenu className="custom-header-menu" onMenuToggle={onMenuToggle} />);
|
|
15
|
-
fireEvent.click(screen.getByRole('button', { name: '
|
|
15
|
+
fireEvent.click(screen.getByRole('button', { name: 'Chat history menu' }));
|
|
16
16
|
|
|
17
17
|
expect(onMenuToggle).toHaveBeenCalled();
|
|
18
18
|
});
|
|
@@ -25,9 +25,9 @@ const ChatbotHeaderMenuBase: FunctionComponent<ChatbotHeaderMenuProps> = ({
|
|
|
25
25
|
className,
|
|
26
26
|
onMenuToggle,
|
|
27
27
|
tooltipProps,
|
|
28
|
-
menuAriaLabel = '
|
|
28
|
+
menuAriaLabel = 'Chat history menu',
|
|
29
29
|
innerRef,
|
|
30
|
-
tooltipContent = '
|
|
30
|
+
tooltipContent = 'Chat history menu',
|
|
31
31
|
isCompact,
|
|
32
32
|
...props
|
|
33
33
|
}: ChatbotHeaderMenuProps) => (
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { fireEvent, render, screen } from '@testing-library/react';
|
|
2
|
+
import { ChatbotHeaderNewChatButton } from './ChatbotHeaderNewChatButton';
|
|
3
|
+
import '@testing-library/jest-dom';
|
|
4
|
+
|
|
5
|
+
describe('ChatbotHeaderNewChatButton', () => {
|
|
6
|
+
it('should render ChatbotHeaderNewChatButton', () => {
|
|
7
|
+
const { container } = render(
|
|
8
|
+
<ChatbotHeaderNewChatButton className="custom-header-new-chat-button" onClick={jest.fn()} />
|
|
9
|
+
);
|
|
10
|
+
|
|
11
|
+
expect(container.querySelector('.custom-header-new-chat-button')).toBeTruthy();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('should call onClick handler when new chat button is pressed', () => {
|
|
15
|
+
const onClick = jest.fn();
|
|
16
|
+
render(<ChatbotHeaderNewChatButton className="custom-header-new-chat-button" onClick={onClick} />);
|
|
17
|
+
fireEvent.click(screen.getByRole('button', { name: 'New chat' }));
|
|
18
|
+
expect(onClick).toHaveBeenCalled();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('should render button with isCompact', () => {
|
|
22
|
+
render(<ChatbotHeaderNewChatButton data-testid="new-chat-button" onClick={jest.fn()} isCompact />);
|
|
23
|
+
expect(screen.getByTestId('new-chat-button')).toHaveClass('pf-m-compact');
|
|
24
|
+
});
|
|
25
|
+
});
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { Ref, FunctionComponent } from 'react';
|
|
2
|
+
import { forwardRef } from 'react';
|
|
3
|
+
|
|
4
|
+
import { Button, ButtonProps, Icon, Tooltip, TooltipProps } from '@patternfly/react-core';
|
|
5
|
+
import { PenToSquareIcon } from '@patternfly/react-icons/dist/esm/icons/pen-to-square-icon';
|
|
6
|
+
|
|
7
|
+
export interface ChatbotHeaderNewChatButtonProps extends ButtonProps {
|
|
8
|
+
/** Callback function for when button is clicked */
|
|
9
|
+
onClick: () => void;
|
|
10
|
+
/** Custom classname for the header component */
|
|
11
|
+
className?: string;
|
|
12
|
+
/** Props spread to the PF Tooltip component wrapping the display mode dropdown */
|
|
13
|
+
tooltipProps?: TooltipProps;
|
|
14
|
+
/** Aria label for menu */
|
|
15
|
+
menuAriaLabel?: string;
|
|
16
|
+
/** Ref applied to menu */
|
|
17
|
+
innerRef?: React.Ref<HTMLButtonElement>;
|
|
18
|
+
/** Content used in tooltip */
|
|
19
|
+
tooltipContent?: string;
|
|
20
|
+
/** Sets button to compact styling. */
|
|
21
|
+
isCompact?: boolean;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const ChatbotHeaderNewChatButtonBase: FunctionComponent<ChatbotHeaderNewChatButtonProps> = ({
|
|
25
|
+
className,
|
|
26
|
+
onClick,
|
|
27
|
+
tooltipProps,
|
|
28
|
+
menuAriaLabel = 'New chat',
|
|
29
|
+
innerRef,
|
|
30
|
+
tooltipContent = 'New chat',
|
|
31
|
+
isCompact,
|
|
32
|
+
...props
|
|
33
|
+
}: ChatbotHeaderNewChatButtonProps) => (
|
|
34
|
+
<div className={`pf-chatbot__menu${className ? ` ${className}` : ''}`}>
|
|
35
|
+
<Tooltip
|
|
36
|
+
content={tooltipContent}
|
|
37
|
+
position="bottom"
|
|
38
|
+
// prevents VO announcements of both aria label and tooltip
|
|
39
|
+
aria="none"
|
|
40
|
+
{...tooltipProps}
|
|
41
|
+
>
|
|
42
|
+
<Button
|
|
43
|
+
className={`pf-chatbot__button--toggle-menu ${isCompact ? 'pf-m-compact' : ''}`}
|
|
44
|
+
variant="plain"
|
|
45
|
+
onClick={onClick}
|
|
46
|
+
aria-label={menuAriaLabel}
|
|
47
|
+
ref={innerRef}
|
|
48
|
+
icon={
|
|
49
|
+
<Icon size={isCompact ? 'lg' : 'xl'} isInline>
|
|
50
|
+
<PenToSquareIcon />
|
|
51
|
+
</Icon>
|
|
52
|
+
}
|
|
53
|
+
size={isCompact ? 'sm' : undefined}
|
|
54
|
+
{...props}
|
|
55
|
+
/>
|
|
56
|
+
</Tooltip>
|
|
57
|
+
</div>
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
export const ChatbotHeaderNewChatButton = forwardRef(
|
|
61
|
+
(props: ChatbotHeaderNewChatButtonProps, ref: Ref<HTMLButtonElement>) => (
|
|
62
|
+
<ChatbotHeaderNewChatButtonBase innerRef={ref} {...props} />
|
|
63
|
+
)
|
|
64
|
+
);
|
|
@@ -6,12 +6,12 @@
|
|
|
6
6
|
inset-block-end: var(--pf-t--global--spacer--md);
|
|
7
7
|
inset-inline-end: var(--pf-t--global--spacer--md);
|
|
8
8
|
background-color: var(--pf-t--global--background--color--inverse--default);
|
|
9
|
-
--pf-v6-c-button__icon--Color: var(--pf-t--
|
|
9
|
+
--pf-v6-c-button__icon--Color: var(--pf-t--global--icon--color--inverse);
|
|
10
10
|
padding: var(--pf-t--global--spacer--md);
|
|
11
11
|
|
|
12
12
|
&:hover,
|
|
13
13
|
&:focus {
|
|
14
|
-
background-color: var(--pf-t--
|
|
14
|
+
background-color: var(--pf-t--color--gray--70);
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
.pf-v6-c-button__icon {
|
|
@@ -48,19 +48,16 @@ exports[`FileDetails should render file details 1`] = `
|
|
|
48
48
|
<span
|
|
49
49
|
class="pf-chatbot__code-fileName"
|
|
50
50
|
>
|
|
51
|
-
<
|
|
52
|
-
|
|
51
|
+
<span
|
|
52
|
+
class="pf-v6-c-truncate"
|
|
53
|
+
tabindex="0"
|
|
53
54
|
>
|
|
54
55
|
<span
|
|
55
|
-
class="pf-v6-c-
|
|
56
|
+
class="pf-v6-c-truncate__start"
|
|
56
57
|
>
|
|
57
|
-
|
|
58
|
-
class="pf-v6-c-truncate__start"
|
|
59
|
-
>
|
|
60
|
-
test
|
|
61
|
-
</span>
|
|
58
|
+
test
|
|
62
59
|
</span>
|
|
63
|
-
</
|
|
60
|
+
</span>
|
|
64
61
|
</span>
|
|
65
62
|
</div>
|
|
66
63
|
<div
|
|
@@ -60,19 +60,16 @@ exports[`FileDetailsLabel should render file details label 1`] = `
|
|
|
60
60
|
<span
|
|
61
61
|
class="pf-chatbot__code-fileName"
|
|
62
62
|
>
|
|
63
|
-
<
|
|
64
|
-
|
|
63
|
+
<span
|
|
64
|
+
class="pf-v6-c-truncate"
|
|
65
|
+
tabindex="0"
|
|
65
66
|
>
|
|
66
67
|
<span
|
|
67
|
-
class="pf-v6-c-
|
|
68
|
+
class="pf-v6-c-truncate__start"
|
|
68
69
|
>
|
|
69
|
-
|
|
70
|
-
class="pf-v6-c-truncate__start"
|
|
71
|
-
>
|
|
72
|
-
test
|
|
73
|
-
</span>
|
|
70
|
+
test
|
|
74
71
|
</span>
|
|
75
|
-
</
|
|
72
|
+
</span>
|
|
76
73
|
</span>
|
|
77
74
|
</div>
|
|
78
75
|
<div
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { MultipleFileUpload, MultipleFileUploadMain } from '@patternfly/react-core';
|
|
2
2
|
import type { FunctionComponent } from 'react';
|
|
3
3
|
import { useState } from 'react';
|
|
4
4
|
import { ChatbotDisplayMode } from '../Chatbot';
|
|
5
5
|
import { UploadIcon } from '@patternfly/react-icons';
|
|
6
|
-
import { Accept, FileError, FileRejection } from 'react-dropzone
|
|
6
|
+
import { Accept, DropEvent, FileError, FileRejection } from 'react-dropzone';
|
|
7
7
|
|
|
8
8
|
export interface FileDropZoneProps {
|
|
9
9
|
/** Content displayed when the drop zone is not currently in use */
|
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--xl);
|
|
9
9
|
|
|
10
10
|
// Avatar
|
|
11
11
|
// --------------------------------------------------------------------------
|
|
@@ -48,20 +48,22 @@
|
|
|
48
48
|
|
|
49
49
|
// Author name
|
|
50
50
|
.pf-chatbot__message-name {
|
|
51
|
-
font-family: var(
|
|
51
|
+
font-family: var(
|
|
52
|
+
--pf-v6-c-content--heading--FontFamily,
|
|
53
|
+
redhatdisplayvf,
|
|
54
|
+
redhatdisplay,
|
|
55
|
+
helvetica,
|
|
56
|
+
arial,
|
|
57
|
+
sans-serif
|
|
58
|
+
);
|
|
52
59
|
font-weight: 600;
|
|
53
60
|
font-size: var(--pf-t--global--font--size--sm);
|
|
54
61
|
}
|
|
55
62
|
|
|
56
63
|
// Badge
|
|
57
64
|
.pf-v6-c-label {
|
|
58
|
-
--pf-v6-c-label--m-outline--BorderColor: var(--pf-t--global--border--color--on-secondary);
|
|
59
65
|
--pf-v6-c-label--FontSize: var(--pf-t--global--font--size--xs);
|
|
60
66
|
font-weight: var(--pf-t--global--font--weight--body--bold);
|
|
61
|
-
|
|
62
|
-
.pf-v6-c-label__content {
|
|
63
|
-
--pf-v6-c-label--Color: var(--pf-t--global--border--color--on-secondary);
|
|
64
|
-
}
|
|
65
67
|
}
|
|
66
68
|
|
|
67
69
|
// Timestamp
|
|
@@ -12,6 +12,7 @@ const ALL_ACTIONS = [
|
|
|
12
12
|
{ label: /Good response/i },
|
|
13
13
|
{ label: /Bad response/i },
|
|
14
14
|
{ label: /Copy/i },
|
|
15
|
+
{ label: /Edit/i },
|
|
15
16
|
{ label: /Share/i },
|
|
16
17
|
{ label: /Listen/i }
|
|
17
18
|
];
|
|
@@ -426,6 +427,8 @@ describe('Message', () => {
|
|
|
426
427
|
// eslint-disable-next-line no-console
|
|
427
428
|
copy: { onClick: () => console.log('Copy') },
|
|
428
429
|
// eslint-disable-next-line no-console
|
|
430
|
+
edit: { onClick: () => console.log('Edit') },
|
|
431
|
+
// eslint-disable-next-line no-console
|
|
429
432
|
share: { onClick: () => console.log('Share') },
|
|
430
433
|
// eslint-disable-next-line no-console
|
|
431
434
|
download: { onClick: () => console.log('Download') },
|
|
@@ -454,6 +457,8 @@ describe('Message', () => {
|
|
|
454
457
|
// eslint-disable-next-line no-console
|
|
455
458
|
copy: { onClick: () => console.log('Copy') },
|
|
456
459
|
// eslint-disable-next-line no-console
|
|
460
|
+
edit: { onClick: () => console.log('Edit') },
|
|
461
|
+
// eslint-disable-next-line no-console
|
|
457
462
|
share: { onClick: () => console.log('Share') },
|
|
458
463
|
// eslint-disable-next-line no-console
|
|
459
464
|
download: { onClick: () => console.log('Download') },
|
|
@@ -467,6 +472,36 @@ describe('Message', () => {
|
|
|
467
472
|
expect(screen.queryByRole('button', { name: label })).toBeFalsy();
|
|
468
473
|
});
|
|
469
474
|
});
|
|
475
|
+
it('should not show actions if isEditable is true', async () => {
|
|
476
|
+
render(
|
|
477
|
+
<Message
|
|
478
|
+
avatar="./img"
|
|
479
|
+
role="bot"
|
|
480
|
+
name="Bot"
|
|
481
|
+
content="Hi"
|
|
482
|
+
isEditable
|
|
483
|
+
actions={{
|
|
484
|
+
// eslint-disable-next-line no-console
|
|
485
|
+
positive: { onClick: () => console.log('Good response') },
|
|
486
|
+
// eslint-disable-next-line no-console
|
|
487
|
+
negative: { onClick: () => console.log('Bad response') },
|
|
488
|
+
// eslint-disable-next-line no-console
|
|
489
|
+
copy: { onClick: () => console.log('Copy') },
|
|
490
|
+
// eslint-disable-next-line no-console
|
|
491
|
+
edit: { onClick: () => console.log('Edit') },
|
|
492
|
+
// eslint-disable-next-line no-console
|
|
493
|
+
share: { onClick: () => console.log('Share') },
|
|
494
|
+
// eslint-disable-next-line no-console
|
|
495
|
+
download: { onClick: () => console.log('Download') },
|
|
496
|
+
// eslint-disable-next-line no-console
|
|
497
|
+
listen: { onClick: () => console.log('Listen') }
|
|
498
|
+
}}
|
|
499
|
+
/>
|
|
500
|
+
);
|
|
501
|
+
ALL_ACTIONS.forEach(({ label }) => {
|
|
502
|
+
expect(screen.queryByRole('button', { name: label })).toBeFalsy();
|
|
503
|
+
});
|
|
504
|
+
});
|
|
470
505
|
it('should render unordered lists correctly', () => {
|
|
471
506
|
render(<Message avatar="./img" role="user" name="User" content={UNORDERED_LIST} />);
|
|
472
507
|
expect(screen.getByText('Here is an unordered list:')).toBeTruthy();
|
|
@@ -927,4 +962,23 @@ describe('Message', () => {
|
|
|
927
962
|
const form = container.querySelector('form');
|
|
928
963
|
expect(form).toHaveClass('test');
|
|
929
964
|
});
|
|
965
|
+
it('should be able to disable markdown parsing', () => {
|
|
966
|
+
render(<Message avatar="./img" role="user" name="User" content={CODE_MESSAGE} isMarkdownDisabled />);
|
|
967
|
+
// this is looking for markdown syntax that is ordinarily stripped
|
|
968
|
+
expect(screen.getByText(/~~~yaml/i)).toBeTruthy();
|
|
969
|
+
});
|
|
970
|
+
it('should be able to pass props to react-markdown, such as disabling tags', () => {
|
|
971
|
+
render(
|
|
972
|
+
<Message
|
|
973
|
+
avatar="./img"
|
|
974
|
+
role="user"
|
|
975
|
+
name="User"
|
|
976
|
+
content={CODE_MESSAGE}
|
|
977
|
+
reactMarkdownProps={{ disallowedElements: ['code'] }}
|
|
978
|
+
/>
|
|
979
|
+
);
|
|
980
|
+
expect(screen.getByText('Here is some YAML code:')).toBeTruthy();
|
|
981
|
+
// code block isn't rendering
|
|
982
|
+
expect(screen.queryByRole('button', { name: 'Copy code' })).toBeFalsy();
|
|
983
|
+
});
|
|
930
984
|
});
|
package/src/Message/Message.tsx
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// ============================================================================
|
|
4
4
|
import { forwardRef, ReactNode, useEffect, useState } from 'react';
|
|
5
5
|
import type { FunctionComponent, HTMLProps, MouseEvent as ReactMouseEvent, Ref } from 'react';
|
|
6
|
-
import Markdown from 'react-markdown';
|
|
6
|
+
import Markdown, { Options } from 'react-markdown';
|
|
7
7
|
import remarkGfm from 'remark-gfm';
|
|
8
8
|
import {
|
|
9
9
|
AlertProps,
|
|
@@ -44,7 +44,7 @@ import ImageMessage from './ImageMessage/ImageMessage';
|
|
|
44
44
|
import rehypeUnwrapImages from 'rehype-unwrap-images';
|
|
45
45
|
import rehypeExternalLinks from 'rehype-external-links';
|
|
46
46
|
import rehypeSanitize from 'rehype-sanitize';
|
|
47
|
-
import { PluggableList } from '
|
|
47
|
+
import { PluggableList } from 'unified';
|
|
48
48
|
import LinkMessage from './LinkMessage/LinkMessage';
|
|
49
49
|
import ErrorMessage from './ErrorMessage/ErrorMessage';
|
|
50
50
|
import MessageInput from './MessageInput';
|
|
@@ -99,7 +99,7 @@ export interface MessageProps extends Omit<HTMLProps<HTMLDivElement>, 'role'> {
|
|
|
99
99
|
isLoading?: boolean;
|
|
100
100
|
/** Array of attachments attached to a message */
|
|
101
101
|
attachments?: MessageAttachment[];
|
|
102
|
-
/** Props for message actions, such as feedback (positive or negative), copy button, share, and listen */
|
|
102
|
+
/** Props for message actions, such as feedback (positive or negative), copy button, edit message, share, and listen */
|
|
103
103
|
actions?: {
|
|
104
104
|
[key: string]: ActionProps;
|
|
105
105
|
};
|
|
@@ -179,10 +179,16 @@ export interface MessageProps extends Omit<HTMLProps<HTMLDivElement>, 'role'> {
|
|
|
179
179
|
onEditUpdate?: (event: ReactMouseEvent<HTMLButtonElement, MouseEvent>) => void;
|
|
180
180
|
/** Callback functionf or when edit cancel update button is clicked */
|
|
181
181
|
onEditCancel?: (event: ReactMouseEvent<HTMLButtonElement, MouseEvent>) => void;
|
|
182
|
+
/** Ref applied to editable message input */
|
|
183
|
+
inputRef?: Ref<HTMLTextAreaElement>;
|
|
182
184
|
/** Props for edit form */
|
|
183
185
|
editFormProps?: FormProps;
|
|
184
186
|
/** Sets message to compact styling. */
|
|
185
187
|
isCompact?: boolean;
|
|
188
|
+
/** Disables markdown parsing for message, allowing only text input */
|
|
189
|
+
isMarkdownDisabled?: boolean;
|
|
190
|
+
/** Allows passing additional props down to markdown parser react-markdown, such as allowedElements and disallowedElements. See https://github.com/remarkjs/react-markdown?tab=readme-ov-file#options for options */
|
|
191
|
+
reactMarkdownProps?: Options;
|
|
186
192
|
}
|
|
187
193
|
|
|
188
194
|
export const MessageBase: FunctionComponent<MessageProps> = ({
|
|
@@ -219,8 +225,11 @@ export const MessageBase: FunctionComponent<MessageProps> = ({
|
|
|
219
225
|
cancelWord = 'Cancel',
|
|
220
226
|
onEditUpdate,
|
|
221
227
|
onEditCancel,
|
|
228
|
+
inputRef,
|
|
222
229
|
editFormProps,
|
|
223
230
|
isCompact,
|
|
231
|
+
isMarkdownDisabled,
|
|
232
|
+
reactMarkdownProps,
|
|
224
233
|
...props
|
|
225
234
|
}: MessageProps) => {
|
|
226
235
|
const [messageText, setMessageText] = useState(content);
|
|
@@ -247,6 +256,60 @@ export const MessageBase: FunctionComponent<MessageProps> = ({
|
|
|
247
256
|
const date = new Date();
|
|
248
257
|
const dateString = timestamp ?? `${date.toLocaleDateString()} ${date.toLocaleTimeString()}`;
|
|
249
258
|
|
|
259
|
+
const handleMarkdown = () => {
|
|
260
|
+
if (isMarkdownDisabled) {
|
|
261
|
+
return (
|
|
262
|
+
<TextMessage component={ContentVariants.p} {...props}>
|
|
263
|
+
{messageText}
|
|
264
|
+
</TextMessage>
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
return (
|
|
268
|
+
<Markdown
|
|
269
|
+
components={{
|
|
270
|
+
p: (props) => <TextMessage component={ContentVariants.p} {...props} />,
|
|
271
|
+
code: ({ children, ...props }) => (
|
|
272
|
+
<CodeBlockMessage {...props} {...codeBlockProps}>
|
|
273
|
+
{children}
|
|
274
|
+
</CodeBlockMessage>
|
|
275
|
+
),
|
|
276
|
+
h1: (props) => <TextMessage component={ContentVariants.h1} {...props} />,
|
|
277
|
+
h2: (props) => <TextMessage component={ContentVariants.h2} {...props} />,
|
|
278
|
+
h3: (props) => <TextMessage component={ContentVariants.h3} {...props} />,
|
|
279
|
+
h4: (props) => <TextMessage component={ContentVariants.h4} {...props} />,
|
|
280
|
+
h5: (props) => <TextMessage component={ContentVariants.h5} {...props} />,
|
|
281
|
+
h6: (props) => <TextMessage component={ContentVariants.h6} {...props} />,
|
|
282
|
+
blockquote: (props) => <TextMessage component={ContentVariants.blockquote} {...props} />,
|
|
283
|
+
ul: (props) => <UnorderedListMessage {...props} />,
|
|
284
|
+
ol: (props) => <OrderedListMessage {...props} />,
|
|
285
|
+
li: (props) => <ListItemMessage {...props} />,
|
|
286
|
+
table: (props) => <TableMessage {...props} {...tableProps} />,
|
|
287
|
+
tbody: (props) => <TbodyMessage {...props} />,
|
|
288
|
+
thead: (props) => <TheadMessage {...props} />,
|
|
289
|
+
tr: (props) => <TrMessage {...props} />,
|
|
290
|
+
td: (props) => {
|
|
291
|
+
// Conflicts with Td type
|
|
292
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
293
|
+
const { width, ...rest } = props;
|
|
294
|
+
return <TdMessage {...rest} />;
|
|
295
|
+
},
|
|
296
|
+
th: (props) => <ThMessage {...props} />,
|
|
297
|
+
img: (props) => <ImageMessage {...props} />,
|
|
298
|
+
a: (props) => (
|
|
299
|
+
<LinkMessage href={props.href} rel={props.rel} target={props.target} {...linkProps}>
|
|
300
|
+
{props.children}
|
|
301
|
+
</LinkMessage>
|
|
302
|
+
)
|
|
303
|
+
}}
|
|
304
|
+
remarkPlugins={[remarkGfm]}
|
|
305
|
+
rehypePlugins={rehypePlugins}
|
|
306
|
+
{...reactMarkdownProps}
|
|
307
|
+
>
|
|
308
|
+
{messageText}
|
|
309
|
+
</Markdown>
|
|
310
|
+
);
|
|
311
|
+
};
|
|
312
|
+
|
|
250
313
|
const renderMessage = () => {
|
|
251
314
|
if (isLoading) {
|
|
252
315
|
return <MessageLoading loadingWord={loadingWord} />;
|
|
@@ -256,7 +319,7 @@ export const MessageBase: FunctionComponent<MessageProps> = ({
|
|
|
256
319
|
<>
|
|
257
320
|
{beforeMainContent && <>{beforeMainContent}</>}
|
|
258
321
|
<MessageInput
|
|
259
|
-
content={
|
|
322
|
+
content={messageText}
|
|
260
323
|
editPlaceholder={editPlaceholder}
|
|
261
324
|
updateWord={updateWord}
|
|
262
325
|
cancelWord={cancelWord}
|
|
@@ -265,6 +328,7 @@ export const MessageBase: FunctionComponent<MessageProps> = ({
|
|
|
265
328
|
setMessageText(value);
|
|
266
329
|
}}
|
|
267
330
|
onEditCancel={onEditCancel}
|
|
331
|
+
inputRef={inputRef}
|
|
268
332
|
{...editFormProps}
|
|
269
333
|
/>
|
|
270
334
|
</>
|
|
@@ -273,51 +337,7 @@ export const MessageBase: FunctionComponent<MessageProps> = ({
|
|
|
273
337
|
return (
|
|
274
338
|
<>
|
|
275
339
|
{beforeMainContent && <>{beforeMainContent}</>}
|
|
276
|
-
{error ? (
|
|
277
|
-
<ErrorMessage {...error} />
|
|
278
|
-
) : (
|
|
279
|
-
<Markdown
|
|
280
|
-
components={{
|
|
281
|
-
p: (props) => <TextMessage component={ContentVariants.p} {...props} />,
|
|
282
|
-
code: ({ children, ...props }) => (
|
|
283
|
-
<CodeBlockMessage {...props} {...codeBlockProps}>
|
|
284
|
-
{children}
|
|
285
|
-
</CodeBlockMessage>
|
|
286
|
-
),
|
|
287
|
-
h1: (props) => <TextMessage component={ContentVariants.h1} {...props} />,
|
|
288
|
-
h2: (props) => <TextMessage component={ContentVariants.h2} {...props} />,
|
|
289
|
-
h3: (props) => <TextMessage component={ContentVariants.h3} {...props} />,
|
|
290
|
-
h4: (props) => <TextMessage component={ContentVariants.h4} {...props} />,
|
|
291
|
-
h5: (props) => <TextMessage component={ContentVariants.h5} {...props} />,
|
|
292
|
-
h6: (props) => <TextMessage component={ContentVariants.h6} {...props} />,
|
|
293
|
-
blockquote: (props) => <TextMessage component={ContentVariants.blockquote} {...props} />,
|
|
294
|
-
ul: (props) => <UnorderedListMessage {...props} />,
|
|
295
|
-
ol: (props) => <OrderedListMessage {...props} />,
|
|
296
|
-
li: (props) => <ListItemMessage {...props} />,
|
|
297
|
-
table: (props) => <TableMessage {...props} {...tableProps} />,
|
|
298
|
-
tbody: (props) => <TbodyMessage {...props} />,
|
|
299
|
-
thead: (props) => <TheadMessage {...props} />,
|
|
300
|
-
tr: (props) => <TrMessage {...props} />,
|
|
301
|
-
td: (props) => {
|
|
302
|
-
// Conflicts with Td type
|
|
303
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
304
|
-
const { width, ...rest } = props;
|
|
305
|
-
return <TdMessage {...rest} />;
|
|
306
|
-
},
|
|
307
|
-
th: (props) => <ThMessage {...props} />,
|
|
308
|
-
img: (props) => <ImageMessage {...props} />,
|
|
309
|
-
a: (props) => (
|
|
310
|
-
<LinkMessage href={props.href} rel={props.rel} target={props.target} {...linkProps}>
|
|
311
|
-
{props.children}
|
|
312
|
-
</LinkMessage>
|
|
313
|
-
)
|
|
314
|
-
}}
|
|
315
|
-
remarkPlugins={[remarkGfm]}
|
|
316
|
-
rehypePlugins={rehypePlugins}
|
|
317
|
-
>
|
|
318
|
-
{messageText}
|
|
319
|
-
</Markdown>
|
|
320
|
-
)}
|
|
340
|
+
{error ? <ErrorMessage {...error} /> : handleMarkdown()}
|
|
321
341
|
</>
|
|
322
342
|
);
|
|
323
343
|
};
|
|
@@ -369,7 +389,7 @@ export const MessageBase: FunctionComponent<MessageProps> = ({
|
|
|
369
389
|
isCompact={isCompact}
|
|
370
390
|
/>
|
|
371
391
|
)}
|
|
372
|
-
{!isLoading && actions && <ResponseActions actions={actions} />}
|
|
392
|
+
{!isLoading && !isEditable && actions && <ResponseActions actions={actions} />}
|
|
373
393
|
{userFeedbackForm && <UserFeedback {...userFeedbackForm} timestamp={dateString} isCompact={isCompact} />}
|
|
374
394
|
{userFeedbackComplete && (
|
|
375
395
|
<UserFeedbackComplete {...userFeedbackComplete} timestamp={dateString} isCompact={isCompact} />
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// ============================================================================
|
|
2
2
|
// Chatbot Main - Message Input
|
|
3
3
|
// ============================================================================
|
|
4
|
-
import type { FormEvent, FunctionComponent } from 'react';
|
|
4
|
+
import type { FormEvent, FunctionComponent, Ref } from 'react';
|
|
5
5
|
import { useState } from 'react';
|
|
6
6
|
import { ActionGroup, Button, Form, FormProps, TextArea } from '@patternfly/react-core';
|
|
7
7
|
|
|
@@ -16,6 +16,8 @@ export interface MessageInputProps extends FormProps {
|
|
|
16
16
|
onEditUpdate?: (event: React.MouseEvent<HTMLButtonElement, MouseEvent>, value: string) => void;
|
|
17
17
|
/** Callback functionf or when edit cancel update button is clicked */
|
|
18
18
|
onEditCancel?: (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;
|
|
19
|
+
/** Ref applied to editable message input */
|
|
20
|
+
inputRef?: Ref<HTMLTextAreaElement>;
|
|
19
21
|
/** Message text */
|
|
20
22
|
content?: string;
|
|
21
23
|
}
|
|
@@ -26,6 +28,7 @@ const MessageInput: FunctionComponent<MessageInputProps> = ({
|
|
|
26
28
|
cancelWord = 'Cancel',
|
|
27
29
|
onEditUpdate,
|
|
28
30
|
onEditCancel,
|
|
31
|
+
inputRef,
|
|
29
32
|
content,
|
|
30
33
|
...props
|
|
31
34
|
}: MessageInputProps) => {
|
|
@@ -43,6 +46,7 @@ const MessageInput: FunctionComponent<MessageInputProps> = ({
|
|
|
43
46
|
onChange={onChange}
|
|
44
47
|
aria-label={editPlaceholder}
|
|
45
48
|
autoResize
|
|
49
|
+
ref={inputRef}
|
|
46
50
|
/>
|
|
47
51
|
<ActionGroup className="pf-chatbot__message-edit-buttons">
|
|
48
52
|
<Button variant="primary" onClick={(event) => onEditUpdate && onEditUpdate(event, messageText)}>
|
|
@@ -6,8 +6,8 @@ import type { Ref, FunctionComponent } from 'react';
|
|
|
6
6
|
import { forwardRef } from 'react';
|
|
7
7
|
|
|
8
8
|
// Import PatternFly components
|
|
9
|
-
import { Button, ButtonProps,
|
|
10
|
-
import { Accept, DropzoneOptions, FileError, FileRejection, useDropzone } from 'react-dropzone';
|
|
9
|
+
import { Button, ButtonProps, Icon, Tooltip, TooltipProps } from '@patternfly/react-core';
|
|
10
|
+
import { Accept, DropEvent, DropzoneOptions, FileError, FileRejection, useDropzone } from 'react-dropzone';
|
|
11
11
|
import { PaperclipIcon } from '@patternfly/react-icons/dist/esm/icons/paperclip-icon';
|
|
12
12
|
|
|
13
13
|
export interface AttachButtonProps extends ButtonProps {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ChangeEvent, FunctionComponent, KeyboardEvent as ReactKeyboardEvent, Ref } from 'react';
|
|
2
2
|
import { forwardRef, useCallback, useEffect, useRef, useState } from 'react';
|
|
3
|
-
import { Accept, DropzoneOptions, FileError, FileRejection } from 'react-dropzone
|
|
4
|
-
import { ButtonProps,
|
|
3
|
+
import { Accept, DropEvent, DropzoneOptions, FileError, FileRejection } from 'react-dropzone';
|
|
4
|
+
import { ButtonProps, TextArea, TextAreaProps, TooltipProps } from '@patternfly/react-core';
|
|
5
5
|
|
|
6
6
|
// Import Chatbot components
|
|
7
7
|
import SendButton from './SendButton';
|
|
@@ -13,11 +13,11 @@
|
|
|
13
13
|
|
|
14
14
|
&:hover,
|
|
15
15
|
&:focus {
|
|
16
|
-
background-color:
|
|
16
|
+
background-color: rgba(146, 197, 249, 0.25); // --pf-t--global--color--nonstatus--blue--default @ 25%;
|
|
17
17
|
color: var(--pf-t--global--color--brand--hover);
|
|
18
18
|
|
|
19
19
|
.pf-v6-c-button__icon {
|
|
20
|
-
color: var(--pf-t--
|
|
20
|
+
color: var(--pf-t--global--color--brand--hover);
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
23
|
}
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
|
|
38
38
|
.pf-v6-c-button.pf-chatbot__button--send:hover,
|
|
39
39
|
.pf-v6-c-button.pf-chatbot__button--send:focus {
|
|
40
|
-
background-color:
|
|
40
|
+
background-color: rgba(146, 197, 249, 0.25); // --pf-t--global--color--nonstatus--blue--default @ 25%;
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
43
|
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
border-radius: var(--pf-t--global--border--radius--pill) !important;
|
|
15
15
|
--pf-v6-c-button--MinWidth: 2rem !important;
|
|
16
16
|
background-color: var(--pf-t--global--background--color--primary--default) !important;
|
|
17
|
-
border: 1px solid var(--pf-t--
|
|
17
|
+
border: 1px solid var(--pf-t--global--border--color--default) !important;
|
|
18
18
|
box-shadow: var(--pf-t--global--box-shadow--sm);
|
|
19
19
|
color: var(--pf-t--global--icon--color--subtle) !important;
|
|
20
20
|
transform: translate3d(-50%, 0, 0) !important;
|