@patternfly/chatbot 6.3.1 → 6.4.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/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/MessageBar/MessageBar.js +19 -4
- 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/MessageBar/MessageBar.js +19 -4
- 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 +15 -1
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/UserMessage.tsx +39 -7
- 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 +25 -5
- 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
|
@@ -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';
|
|
@@ -141,6 +141,7 @@ export const MessageBarBase: FunctionComponent<MessageBarProps> = ({
|
|
|
141
141
|
const [message, setMessage] = useState<string | number>(value ?? '');
|
|
142
142
|
const [isListeningMessage, setIsListeningMessage] = useState<boolean>(false);
|
|
143
143
|
const [hasSentMessage, setHasSentMessage] = useState(false);
|
|
144
|
+
const [isComposing, setIsComposing] = useState(false);
|
|
144
145
|
const inputRef = useRef<HTMLTextAreaElement>(null);
|
|
145
146
|
const textareaRef = (innerRef as React.RefObject<HTMLTextAreaElement>) ?? inputRef;
|
|
146
147
|
const attachButtonRef = useRef<HTMLButtonElement>(null);
|
|
@@ -285,21 +286,38 @@ export const MessageBarBase: FunctionComponent<MessageBarProps> = ({
|
|
|
285
286
|
|
|
286
287
|
const handleKeyDown = useCallback(
|
|
287
288
|
(event: ReactKeyboardEvent) => {
|
|
288
|
-
|
|
289
|
+
// Japanese and other languages may use IME for character input.
|
|
290
|
+
// In these cases, enter is used to select the final input, so we need to check for composition end instead.
|
|
291
|
+
// See more info at https://www.stum.de/2016/06/24/handling-ime-events-in-javascript/
|
|
292
|
+
// Chrome, Edge, and Firefox seem to work well with just the compose event.
|
|
293
|
+
// Safari is a little bit special. We need to handle 229 as well in this case.
|
|
294
|
+
const nativeEvent = event.nativeEvent as KeyboardEvent;
|
|
295
|
+
const isCompositionKey = nativeEvent.which === 229;
|
|
296
|
+
const isCurrentlyComposing = isComposing || isCompositionKey;
|
|
297
|
+
|
|
298
|
+
if (event.key === 'Enter' && !isCurrentlyComposing && !event.shiftKey) {
|
|
289
299
|
event.preventDefault();
|
|
290
300
|
if (!isSendButtonDisabled && !hasStopButton) {
|
|
291
301
|
handleSend(message);
|
|
292
302
|
}
|
|
293
303
|
}
|
|
294
|
-
if (event.key === 'Enter' && event.shiftKey) {
|
|
304
|
+
if (event.key === 'Enter' && !isCurrentlyComposing && event.shiftKey) {
|
|
295
305
|
if (textareaRef.current) {
|
|
296
306
|
handleNewLine(textareaRef.current);
|
|
297
307
|
}
|
|
298
308
|
}
|
|
299
309
|
},
|
|
300
|
-
[isSendButtonDisabled, hasStopButton, handleSend, message]
|
|
310
|
+
[isSendButtonDisabled, hasStopButton, handleSend, message, isComposing]
|
|
301
311
|
);
|
|
302
312
|
|
|
313
|
+
const handleCompositionStart = useCallback(() => {
|
|
314
|
+
setIsComposing(true);
|
|
315
|
+
}, []);
|
|
316
|
+
|
|
317
|
+
const handleCompositionEnd = useCallback(() => {
|
|
318
|
+
setIsComposing(false);
|
|
319
|
+
}, []);
|
|
320
|
+
|
|
303
321
|
const handleAttachMenuToggle = () => {
|
|
304
322
|
attachMenuProps?.setIsAttachMenuOpen && attachMenuProps?.setIsAttachMenuOpen(!attachMenuProps?.isAttachMenuOpen);
|
|
305
323
|
attachMenuProps?.onAttachMenuToggleClick();
|
|
@@ -402,6 +420,8 @@ export const MessageBarBase: FunctionComponent<MessageBarProps> = ({
|
|
|
402
420
|
placeholder={isListeningMessage ? listeningText : placeholder}
|
|
403
421
|
ref={textareaRef}
|
|
404
422
|
onKeyDown={handleKeyDown}
|
|
423
|
+
onCompositionStart={handleCompositionStart}
|
|
424
|
+
onCompositionEnd={handleCompositionEnd}
|
|
405
425
|
{...props}
|
|
406
426
|
/>
|
|
407
427
|
</div>
|
|
@@ -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;
|
|
@@ -6,20 +6,20 @@ import userEvent from '@testing-library/user-event';
|
|
|
6
6
|
describe('JumpButton', () => {
|
|
7
7
|
it('should render top button correctly', () => {
|
|
8
8
|
render(<JumpButton position="top" onClick={jest.fn()} />);
|
|
9
|
-
expect(screen.getByRole('button', { name: /
|
|
9
|
+
expect(screen.getByRole('button', { name: /Back to top/i })).toBeTruthy();
|
|
10
10
|
});
|
|
11
11
|
it('should render bottom button correctly', () => {
|
|
12
12
|
render(<JumpButton position="bottom" onClick={jest.fn()} />);
|
|
13
|
-
expect(screen.getByRole('button', { name: /
|
|
13
|
+
expect(screen.getByRole('button', { name: /Back to bottom/i })).toBeTruthy();
|
|
14
14
|
});
|
|
15
15
|
it('should call onClick appropriately', async () => {
|
|
16
16
|
const spy = jest.fn();
|
|
17
17
|
render(<JumpButton position="bottom" onClick={spy} />);
|
|
18
|
-
await userEvent.click(screen.getByRole('button', { name: /
|
|
18
|
+
await userEvent.click(screen.getByRole('button', { name: /Back to bottom/i }));
|
|
19
19
|
expect(spy).toHaveBeenCalledTimes(1);
|
|
20
20
|
});
|
|
21
21
|
it('should be hidden if isHidden prop is used', async () => {
|
|
22
22
|
render(<JumpButton position="bottom" onClick={jest.fn()} isHidden />);
|
|
23
|
-
expect(screen.queryByRole('button', { name: /
|
|
23
|
+
expect(screen.queryByRole('button', { name: /Back to bottom/i })).toBeFalsy();
|
|
24
24
|
});
|
|
25
25
|
});
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import type { FunctionComponent } from 'react';
|
|
5
5
|
|
|
6
6
|
// Import PatternFly components
|
|
7
|
-
import { Button, Tooltip, Icon } from '@patternfly/react-core';
|
|
7
|
+
import { Button, Tooltip, Icon, TooltipProps, ButtonProps } from '@patternfly/react-core';
|
|
8
8
|
|
|
9
9
|
import { ArrowUpIcon } from '@patternfly/react-icons/dist/esm/icons/arrow-up-icon';
|
|
10
10
|
import { ArrowDownIcon } from '@patternfly/react-icons/dist/esm/icons/arrow-down-icon';
|
|
@@ -16,16 +16,32 @@ export interface JumpButtonProps {
|
|
|
16
16
|
onClick: () => void;
|
|
17
17
|
/** Flag to change the visibilty of the button */
|
|
18
18
|
isHidden?: boolean;
|
|
19
|
+
/** Additional props passed to jump buttons */
|
|
20
|
+
jumpButtonProps?: ButtonProps;
|
|
21
|
+
/** Additional props passed to tooltip */
|
|
22
|
+
jumpButtonTooltipProps?: TooltipProps;
|
|
19
23
|
}
|
|
20
24
|
|
|
21
|
-
const JumpButton: FunctionComponent<JumpButtonProps> = ({
|
|
25
|
+
const JumpButton: FunctionComponent<JumpButtonProps> = ({
|
|
26
|
+
position,
|
|
27
|
+
isHidden,
|
|
28
|
+
onClick,
|
|
29
|
+
jumpButtonProps,
|
|
30
|
+
jumpButtonTooltipProps
|
|
31
|
+
}: JumpButtonProps) =>
|
|
22
32
|
isHidden ? null : (
|
|
23
|
-
<Tooltip
|
|
33
|
+
<Tooltip
|
|
34
|
+
id={`pf-chatbot__tooltip--jump-${position}`}
|
|
35
|
+
content={`Back to ${position}`}
|
|
36
|
+
position="top"
|
|
37
|
+
{...jumpButtonTooltipProps}
|
|
38
|
+
>
|
|
24
39
|
<Button
|
|
25
40
|
variant="plain"
|
|
26
41
|
className={`pf-chatbot__jump pf-chatbot__jump--${position}`}
|
|
27
|
-
aria-label={`
|
|
42
|
+
aria-label={`Back to ${position}`}
|
|
28
43
|
onClick={onClick}
|
|
44
|
+
{...jumpButtonProps}
|
|
29
45
|
>
|
|
30
46
|
<Icon iconSize="lg" isInline>
|
|
31
47
|
{position === 'top' ? <ArrowUpIcon /> : <ArrowDownIcon />}
|
|
@@ -61,7 +61,7 @@ describe('MessageBox', () => {
|
|
|
61
61
|
});
|
|
62
62
|
|
|
63
63
|
await waitFor(() => {
|
|
64
|
-
userEvent.click(screen.getByRole('button', { name: /
|
|
64
|
+
userEvent.click(screen.getByRole('button', { name: /Back to bottom/i }));
|
|
65
65
|
expect(spy).toHaveBeenCalled();
|
|
66
66
|
});
|
|
67
67
|
});
|
|
@@ -85,7 +85,7 @@ describe('MessageBox', () => {
|
|
|
85
85
|
region.dispatchEvent(new Event('scroll'));
|
|
86
86
|
});
|
|
87
87
|
await waitFor(() => {
|
|
88
|
-
userEvent.click(screen.getByRole('button', { name: /
|
|
88
|
+
userEvent.click(screen.getByRole('button', { name: /Back to top/i }));
|
|
89
89
|
expect(spy).toHaveBeenCalled();
|
|
90
90
|
});
|
|
91
91
|
});
|
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
WheelEventHandler
|
|
19
19
|
} from 'react';
|
|
20
20
|
import JumpButton from './JumpButton';
|
|
21
|
+
import { ButtonProps, TooltipProps } from '@patternfly/react-core';
|
|
21
22
|
|
|
22
23
|
export interface MessageBoxProps extends HTMLProps<HTMLDivElement> {
|
|
23
24
|
/** Content that can be announced, such as a new message, for screen readers */
|
|
@@ -38,6 +39,14 @@ export interface MessageBoxProps extends HTMLProps<HTMLDivElement> {
|
|
|
38
39
|
onScrollToBottomClick?: () => void;
|
|
39
40
|
/** Flag to enable automatic scrolling when new messages are added */
|
|
40
41
|
enableSmartScroll?: boolean;
|
|
42
|
+
/** Props passed to top jump button */
|
|
43
|
+
jumpButtonTopProps?: ButtonProps;
|
|
44
|
+
/** Props passed to bottom jump button */
|
|
45
|
+
jumpButtonBottomProps?: ButtonProps;
|
|
46
|
+
/** Props passed to top jump button tooltip */
|
|
47
|
+
jumpButtonTopTooltipProps?: TooltipProps;
|
|
48
|
+
/** Props passed to top jump button tooltip */
|
|
49
|
+
jumpButtonBottomTooltipProps?: TooltipProps;
|
|
41
50
|
}
|
|
42
51
|
|
|
43
52
|
export interface MessageBoxHandle extends HTMLDivElement {
|
|
@@ -60,6 +69,10 @@ export const MessageBox = forwardRef(
|
|
|
60
69
|
onScrollToTopClick,
|
|
61
70
|
onScrollToBottomClick,
|
|
62
71
|
enableSmartScroll = false,
|
|
72
|
+
jumpButtonTopProps,
|
|
73
|
+
jumpButtonBottomProps,
|
|
74
|
+
jumpButtonBottomTooltipProps,
|
|
75
|
+
jumpButtonTopTooltipProps,
|
|
63
76
|
...props
|
|
64
77
|
}: MessageBoxProps,
|
|
65
78
|
ref: ForwardedRef<MessageBoxHandle | null>
|
|
@@ -305,12 +318,18 @@ export const MessageBox = forwardRef(
|
|
|
305
318
|
|
|
306
319
|
return (
|
|
307
320
|
<>
|
|
308
|
-
<JumpButton
|
|
321
|
+
<JumpButton
|
|
322
|
+
position="top"
|
|
323
|
+
isHidden={isOverflowing && atTop}
|
|
324
|
+
onClick={scrollToTop}
|
|
325
|
+
jumpButtonProps={jumpButtonTopProps}
|
|
326
|
+
jumpButtonTooltipProps={jumpButtonTopTooltipProps}
|
|
327
|
+
/>
|
|
309
328
|
<div
|
|
310
329
|
role="region"
|
|
311
330
|
tabIndex={0}
|
|
312
331
|
aria-label={ariaLabel}
|
|
313
|
-
className={`pf-chatbot__messagebox ${position === 'bottom'
|
|
332
|
+
className={`pf-chatbot__messagebox ${position === 'bottom' ? 'pf-chatbot__messagebox--bottom' : ''} ${className ?? ''}`}
|
|
314
333
|
ref={messageBoxRef}
|
|
315
334
|
{...props}
|
|
316
335
|
{...(enableSmartScroll ? { ...smartScrollHandlers } : {})}
|
|
@@ -324,6 +343,8 @@ export const MessageBox = forwardRef(
|
|
|
324
343
|
position="bottom"
|
|
325
344
|
isHidden={isOverflowing && atBottom}
|
|
326
345
|
onClick={() => scrollToBottom({ resumeSmartScroll: true })}
|
|
346
|
+
jumpButtonProps={jumpButtonBottomProps}
|
|
347
|
+
jumpButtonTooltipProps={jumpButtonBottomTooltipProps}
|
|
327
348
|
/>
|
|
328
349
|
</>
|
|
329
350
|
);
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// Chatbot Main - Message Divider
|
|
3
|
+
// ============================================================================
|
|
4
|
+
.pf-chatbot__message-divider {
|
|
5
|
+
display: grid;
|
|
6
|
+
padding-block-end: var(--pf-t--global--spacer--xl);
|
|
7
|
+
|
|
8
|
+
.pf-v6-c-divider,
|
|
9
|
+
.pf-v6-c-label {
|
|
10
|
+
grid-row: 1 / 1;
|
|
11
|
+
grid-column: 1 / 1;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.pf-v6-c-label {
|
|
15
|
+
--pf-v6-c-label--BackgroundColor: var(--pf-t--global--background--color--tertiary--default);
|
|
16
|
+
--pf-v6-c-label--BorderColor: var(--pf-t--global--border--color--default);
|
|
17
|
+
--pf-v6-c-label--PaddingInlineStart: var(--pf-t--global--spacer--action--horizontal--compact);
|
|
18
|
+
--pf-v6-c-label--PaddingInlineEnd: var(--pf-t--global--spacer--action--horizontal--compact);
|
|
19
|
+
|
|
20
|
+
.pf-v6-c-label__text {
|
|
21
|
+
font-weight: var(--pf-t--global--font--weight--body--bold);
|
|
22
|
+
text-align: center;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
&.pf-m-divider {
|
|
27
|
+
.pf-v6-c-label {
|
|
28
|
+
--pf-v6-c-label--BackgroundColor: var(--pf-t--global--background--color--secondary--default);
|
|
29
|
+
--pf-v6-c-label--MaxWidth: 75%;
|
|
30
|
+
|
|
31
|
+
justify-self: center;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.pf-v6-c-divider {
|
|
35
|
+
align-self: center;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
&.pf-m-wrap {
|
|
40
|
+
.pf-v6-c-label,
|
|
41
|
+
.pf-v6-c-label__text {
|
|
42
|
+
white-space: normal;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { render, screen } from '@testing-library/react';
|
|
2
|
+
import '@testing-library/jest-dom';
|
|
3
|
+
import MessageDivider from './MessageDivider';
|
|
4
|
+
|
|
5
|
+
describe('MessageDivider', () => {
|
|
6
|
+
beforeEach(() => {
|
|
7
|
+
jest.clearAllMocks();
|
|
8
|
+
});
|
|
9
|
+
it('should render default correctly with variant = date and content = new Date().toLocaleDateString()', () => {
|
|
10
|
+
render(<MessageDivider data-testid="message-divider" />);
|
|
11
|
+
expect(screen.getByText(new Date().toLocaleDateString())).toBeInTheDocument();
|
|
12
|
+
expect(screen.getByTestId('message-divider')).toHaveClass('pf-m-divider');
|
|
13
|
+
});
|
|
14
|
+
it('should render inset variant correctly', () => {
|
|
15
|
+
render(<MessageDivider variant="inset" content="test" data-testid="message-divider" />);
|
|
16
|
+
expect(screen.getByText('test')).toBeInTheDocument();
|
|
17
|
+
expect(screen.getByTestId('message-divider')).toHaveClass('pf-m-divider');
|
|
18
|
+
});
|
|
19
|
+
it('should render fullWidth variant correctly', () => {
|
|
20
|
+
render(<MessageDivider variant="fullWidth" content="test" data-testid="message-divider" />);
|
|
21
|
+
expect(screen.getByText('test')).toBeInTheDocument();
|
|
22
|
+
expect(screen.getByTestId('message-divider')).not.toHaveClass('pf-m-divider');
|
|
23
|
+
});
|
|
24
|
+
});
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// Chatbot Main - Message Divider
|
|
3
|
+
// ============================================================================
|
|
4
|
+
import type { FunctionComponent } from 'react';
|
|
5
|
+
import { Divider, Label } from '@patternfly/react-core';
|
|
6
|
+
|
|
7
|
+
export interface MessageDividerProps {
|
|
8
|
+
/** Variant of the divider */
|
|
9
|
+
variant?: 'inset' | 'fullWidth';
|
|
10
|
+
/** Content of the message divider */
|
|
11
|
+
content?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const MessageDivider: FunctionComponent<MessageDividerProps> = ({
|
|
15
|
+
variant = 'inset',
|
|
16
|
+
content = new Date().toLocaleDateString(),
|
|
17
|
+
...props
|
|
18
|
+
}: MessageDividerProps) => {
|
|
19
|
+
if (variant === 'inset') {
|
|
20
|
+
return (
|
|
21
|
+
<div className="pf-chatbot__message-divider pf-m-divider pf-m-wrap" {...props}>
|
|
22
|
+
<Divider />
|
|
23
|
+
<Label variant="outline">{content}</Label>
|
|
24
|
+
</div>
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<div className="pf-chatbot__message-divider pf-m-wrap" {...props}>
|
|
30
|
+
<Label>{content}</Label>
|
|
31
|
+
</div>
|
|
32
|
+
);
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export default MessageDivider;
|
|
@@ -9,6 +9,7 @@ const ALL_ACTIONS = [
|
|
|
9
9
|
{ type: 'positive', label: 'Good response', clickedLabel: 'Response recorded' },
|
|
10
10
|
{ type: 'negative', label: 'Bad response', clickedLabel: 'Response recorded' },
|
|
11
11
|
{ type: 'copy', label: 'Copy', clickedLabel: 'Copied' },
|
|
12
|
+
{ type: 'edit', label: 'Edit', clickedLabel: 'Editing' },
|
|
12
13
|
{ type: 'share', label: 'Share', clickedLabel: 'Shared' },
|
|
13
14
|
{ type: 'listen', label: 'Listen', clickedLabel: 'Listening' }
|
|
14
15
|
];
|
|
@@ -44,6 +45,7 @@ const ALL_ACTIONS_DATA_TEST = [
|
|
|
44
45
|
{ type: 'positive', label: 'Good response', dataTestId: 'positive' },
|
|
45
46
|
{ type: 'negative', label: 'Bad response', dataTestId: 'negative' },
|
|
46
47
|
{ type: 'copy', label: 'Copy', dataTestId: 'copy' },
|
|
48
|
+
{ type: 'edit', label: 'Edit', dataTestId: 'edit' },
|
|
47
49
|
{ type: 'share', label: 'Share', dataTestId: 'share' },
|
|
48
50
|
{ type: 'download', label: 'Download', dataTestId: 'download' },
|
|
49
51
|
{ type: 'listen', label: 'Listen', dataTestId: 'listen' }
|
|
@@ -60,6 +62,7 @@ describe('ResponseActions', () => {
|
|
|
60
62
|
positive: { onClick: jest.fn() },
|
|
61
63
|
negative: { onClick: jest.fn() },
|
|
62
64
|
copy: { onClick: jest.fn() },
|
|
65
|
+
edit: { onClick: jest.fn() },
|
|
63
66
|
share: { onClick: jest.fn() },
|
|
64
67
|
download: { onClick: jest.fn() },
|
|
65
68
|
listen: { onClick: jest.fn() }
|
|
@@ -69,10 +72,11 @@ describe('ResponseActions', () => {
|
|
|
69
72
|
const goodBtn = screen.getByRole('button', { name: 'Good response' });
|
|
70
73
|
const badBtn = screen.getByRole('button', { name: 'Bad response' });
|
|
71
74
|
const copyBtn = screen.getByRole('button', { name: 'Copy' });
|
|
75
|
+
const editBtn = screen.getByRole('button', { name: 'Edit' });
|
|
72
76
|
const shareBtn = screen.getByRole('button', { name: 'Share' });
|
|
73
77
|
const downloadBtn = screen.getByRole('button', { name: 'Download' });
|
|
74
78
|
const listenBtn = screen.getByRole('button', { name: 'Listen' });
|
|
75
|
-
const buttons = [goodBtn, badBtn, copyBtn, shareBtn, downloadBtn, listenBtn];
|
|
79
|
+
const buttons = [goodBtn, badBtn, copyBtn, editBtn, shareBtn, downloadBtn, listenBtn];
|
|
76
80
|
buttons.forEach((button) => {
|
|
77
81
|
expect(button).toBeTruthy();
|
|
78
82
|
});
|
|
@@ -265,6 +269,7 @@ describe('ResponseActions', () => {
|
|
|
265
269
|
{ type: 'positive', ariaLabel: 'Thumbs up' },
|
|
266
270
|
{ type: 'negative', ariaLabel: 'Thumbs down' },
|
|
267
271
|
{ type: 'copy', ariaLabel: 'Copy the message' },
|
|
272
|
+
{ type: 'edit', ariaLabel: 'Edit this message' },
|
|
268
273
|
{ type: 'share', ariaLabel: 'Share it with friends' },
|
|
269
274
|
{ type: 'download', ariaLabel: 'Download your cool message' },
|
|
270
275
|
{ type: 'listen', ariaLabel: 'Listen up' }
|
|
@@ -6,7 +6,8 @@ import {
|
|
|
6
6
|
OutlinedThumbsUpIcon,
|
|
7
7
|
OutlinedThumbsDownIcon,
|
|
8
8
|
OutlinedCopyIcon,
|
|
9
|
-
DownloadIcon
|
|
9
|
+
DownloadIcon,
|
|
10
|
+
PencilAltIcon
|
|
10
11
|
} from '@patternfly/react-icons';
|
|
11
12
|
import ResponseActionButton from './ResponseActionButton';
|
|
12
13
|
import { ButtonProps, TooltipProps } from '@patternfly/react-core';
|
|
@@ -50,6 +51,7 @@ export interface ResponseActionProps {
|
|
|
50
51
|
share?: ActionProps;
|
|
51
52
|
download?: ActionProps;
|
|
52
53
|
listen?: ActionProps;
|
|
54
|
+
edit?: ActionProps;
|
|
53
55
|
};
|
|
54
56
|
}
|
|
55
57
|
|
|
@@ -58,7 +60,7 @@ export const ResponseActions: FunctionComponent<ResponseActionProps> = ({ action
|
|
|
58
60
|
const [clickStatePersisted, setClickStatePersisted] = useState<boolean>(false);
|
|
59
61
|
useEffect(() => {
|
|
60
62
|
// Define the order of precedence for checking initial `isClicked`
|
|
61
|
-
const actionPrecedence = ['positive', 'negative', 'copy', 'share', 'download', 'listen'];
|
|
63
|
+
const actionPrecedence = ['positive', 'negative', 'copy', 'edit', 'share', 'download', 'listen'];
|
|
62
64
|
let initialActive: string | undefined;
|
|
63
65
|
|
|
64
66
|
// Check predefined actions first based on precedence
|
|
@@ -83,7 +85,7 @@ export const ResponseActions: FunctionComponent<ResponseActionProps> = ({ action
|
|
|
83
85
|
setActiveButton(initialActive);
|
|
84
86
|
}, [actions]);
|
|
85
87
|
|
|
86
|
-
const { positive, negative, copy, share, download, listen, ...additionalActions } = actions;
|
|
88
|
+
const { positive, negative, copy, edit, share, download, listen, ...additionalActions } = actions;
|
|
87
89
|
const responseActions = useRef<HTMLDivElement>(null);
|
|
88
90
|
|
|
89
91
|
useEffect(() => {
|
|
@@ -165,6 +167,24 @@ export const ResponseActions: FunctionComponent<ResponseActionProps> = ({ action
|
|
|
165
167
|
aria-controls={copy['aria-controls']}
|
|
166
168
|
></ResponseActionButton>
|
|
167
169
|
)}
|
|
170
|
+
{edit && (
|
|
171
|
+
<ResponseActionButton
|
|
172
|
+
{...edit}
|
|
173
|
+
ariaLabel={edit.ariaLabel ?? 'Edit'}
|
|
174
|
+
clickedAriaLabel={edit.ariaLabel ?? 'Editing'}
|
|
175
|
+
onClick={(e) => handleClick(e, 'edit', edit.onClick)}
|
|
176
|
+
className={edit.className}
|
|
177
|
+
isDisabled={edit.isDisabled}
|
|
178
|
+
tooltipContent={edit.tooltipContent ?? 'Edit '}
|
|
179
|
+
clickedTooltipContent={edit.clickedTooltipContent ?? 'Editing'}
|
|
180
|
+
tooltipProps={edit.tooltipProps}
|
|
181
|
+
icon={<PencilAltIcon />}
|
|
182
|
+
isClicked={activeButton === 'edit'}
|
|
183
|
+
ref={edit.ref}
|
|
184
|
+
aria-expanded={edit['aria-expanded']}
|
|
185
|
+
aria-controls={edit['aria-controls']}
|
|
186
|
+
></ResponseActionButton>
|
|
187
|
+
)}
|
|
168
188
|
{share && (
|
|
169
189
|
<ResponseActionButton
|
|
170
190
|
{...share}
|
|
@@ -219,6 +239,7 @@ export const ResponseActions: FunctionComponent<ResponseActionProps> = ({ action
|
|
|
219
239
|
aria-controls={listen['aria-controls']}
|
|
220
240
|
></ResponseActionButton>
|
|
221
241
|
)}
|
|
242
|
+
|
|
222
243
|
{Object.keys(additionalActions).map((action) => (
|
|
223
244
|
<ResponseActionButton
|
|
224
245
|
{...additionalActions[action]}
|
package/src/index.ts
CHANGED
|
@@ -63,6 +63,9 @@ export * from './MessageBar';
|
|
|
63
63
|
export { default as MessageBox } from './MessageBox';
|
|
64
64
|
export * from './MessageBox';
|
|
65
65
|
|
|
66
|
+
export { default as MessageDivider } from './MessageDivider';
|
|
67
|
+
export * from './MessageDivider';
|
|
68
|
+
|
|
66
69
|
export { default as PreviewAttachment } from './PreviewAttachment';
|
|
67
70
|
export * from './PreviewAttachment';
|
|
68
71
|
|
package/src/main.scss
CHANGED
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
@import './Message/UserFeedback/UserFeedback';
|
|
27
27
|
@import './MessageBar/MessageBar';
|
|
28
28
|
@import './MessageBox/MessageBox';
|
|
29
|
+
@import './MessageDivider/MessageDivider';
|
|
29
30
|
@import './MessageBox/JumpButton';
|
|
30
31
|
@import './ResponseActions/ResponseActions';
|
|
31
32
|
@import './Settings/Settings';
|
|
@@ -33,58 +34,6 @@
|
|
|
33
34
|
@import './SourceDetailsMenuItem/SourceDetailsMenuItem';
|
|
34
35
|
@import './TermsOfUse/TermsOfUse';
|
|
35
36
|
|
|
36
|
-
:where(:root) {
|
|
37
|
-
// ============================================================================
|
|
38
|
-
// Chatbot Custom Default Tokens
|
|
39
|
-
// ============================================================================
|
|
40
|
-
|
|
41
|
-
--pf-t--chatbot--heading--font-family: var(
|
|
42
|
-
--pf-v6-c-content--heading--FontFamily,
|
|
43
|
-
redhatdisplayvf,
|
|
44
|
-
redhatdisplay,
|
|
45
|
-
helvetica,
|
|
46
|
-
arial,
|
|
47
|
-
sans-serif
|
|
48
|
-
);
|
|
49
|
-
|
|
50
|
-
--pf-t--chatbot--illustration--fill: var(--pf-t--color--red--50);
|
|
51
|
-
--pf-t--chatbot--code--background: var(--pf-t--color--gray--20);
|
|
52
|
-
|
|
53
|
-
--pf-t--chatbot-toggle--background--hover: var(--pf-t--color--gray--70);
|
|
54
|
-
|
|
55
|
-
--pf-t--chatbot--blue-icon--background--color--hover: rgba(
|
|
56
|
-
146,
|
|
57
|
-
197,
|
|
58
|
-
249,
|
|
59
|
-
0.25
|
|
60
|
-
); // --pf-t--global--color--nonstatus--blue--default @ 25%
|
|
61
|
-
--pf-t--chatbot--blue-icon--fill--hover: var(--pf-t--global--color--brand--hover);
|
|
62
|
-
|
|
63
|
-
// ============================================================================
|
|
64
|
-
// Chatbot Default tokens using PF semantic tokens
|
|
65
|
-
// ============================================================================
|
|
66
|
-
--pf-t--chatbot-toggle--color: var(--pf-t--global--icon--color--inverse);
|
|
67
|
-
--pf-t--chatbot--background: var(--pf-t--global--background--color--secondary--default);
|
|
68
|
-
--pf-t--chatbot--border: var(--pf-t--global--border--color--default);
|
|
69
|
-
|
|
70
|
-
--pf-t--chatbot--icon--fill--active: var(--pf-t--global--text--color--regular);
|
|
71
|
-
|
|
72
|
-
--pf-t--chatbot--blue-icon--fill: var(--pf-t--global--color--brand--default);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// ============================================================================
|
|
76
|
-
// Chatbot Custom Dark Theme Tokens
|
|
77
|
-
// ============================================================================
|
|
78
|
-
:where(.pf-v6-theme-dark) {
|
|
79
|
-
--pf-t--chatbot--illustration--fill: var(--pf-t--color--white);
|
|
80
|
-
--pf-t--chatbot--code--background: var(--pf-t--color--gray--60);
|
|
81
|
-
|
|
82
|
-
--pf-t--chatbot-toggle--background--hover: var(--pf-t--color--gray--20);
|
|
83
|
-
|
|
84
|
-
--pf-t--chatbot--blue-icon--background--color--hover: var(--pf-t--global--color--brand--hover);
|
|
85
|
-
--pf-t--chatbot--blue-icon--fill--hover: var(--pf-t--global--icon--color--inverse);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
37
|
.ws-full-page-utils {
|
|
89
38
|
left: 0 !important;
|
|
90
39
|
right: auto !important;
|